From cb33d1ada6b17d572230e11d8ee2443e3250360a Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 21 May 2021 11:03:06 +0200 Subject: [PATCH 001/235] Initial implementation for "break into non-overlapping pieces". --- src/verbs.cpp | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/src/verbs.cpp b/src/verbs.cpp index d84b856f24..d4b6e55bf4 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -25,6 +25,7 @@ #include #include +#include // Note that gtkmm headers must be included before gtk+ C headers // in all files. The same applies for glibmm/glib etc. @@ -49,6 +50,7 @@ #include "text-chemistry.h" #include "display/curve.h" +#include "display/control/canvas-item-drawing.h" #include "extension/effect.h" @@ -63,10 +65,15 @@ #include "object/sp-guide.h" #include "object/sp-namedview.h" #include "object/sp-object.h" +#include "object/sp-path.h" #include "path/path-offset.h" #include "path/path-outline.h" #include "path/path-simplify.h" +#include "path/path-boolop.h" + +#include "livarot/LivarotDefs.h" +#include "livarot/Shape.h" #include "ui/dialog/align-and-distribute.h" #include "ui/dialog/clonetiler.h" @@ -94,6 +101,8 @@ #include "ui/tools/select-tool.h" #include "ui/widget/canvas.h" // Canvas area +#include "svg/svg.h" + using Inkscape::DocumentUndo; using Inkscape::UI::Dialog::ActionAlign; using Inkscape::UI::Dialog::DialogContainer; @@ -1104,6 +1113,142 @@ void EditVerb::perform(SPAction *action, void *data) } // end of sp_verb_action_edit_perform() +typedef FillRule FillRuleFlatten; + +static void +sp_flatten(Geom::PathVector &pathvector, FillRuleFlatten fillkind) +{ + // TODO refactor this function. it's duplicated in multiple places. + Path *orig = new Path; + orig->LoadPathVector(pathvector); + Shape *theShape = new Shape; + Shape *theRes = new Shape; + orig->ConvertWithBackData (1.0); + orig->Fill (theShape, 0); + theRes->ConvertToShape (theShape, FillRule(fillkind)); + Path *originaux[1]; + originaux[0] = orig; + Path *res = new Path; + theRes->ConvertToForme (res, 1, originaux, true); + + delete theShape; + delete theRes; + char *res_d = res->svg_dump_path (); + delete res; + delete orig; + pathvector = sp_svg_read_pathv(res_d); +} + +Geom::PathVector get_path_data(SPObject* object) +{ + Geom::PathVector result; + if (SPGroup *group = dynamic_cast(object)) { + std::vector items = sp_item_group_item_list(group); + for (SPItem *item : items) { + Geom::PathVector sub_result = get_path_data(item); + result.insert(result.end(), sub_result.begin(), sub_result.end()); + } + } + else if (SPShape *shape = dynamic_cast(object)) { + result = shape->curve()->get_pathvector(); + } else if (SPPath *path = dynamic_cast(object)) { + result = path->curve()->get_pathvector(); + } + + return result; +} + +Geom::PathVector get_paths_from_object_list(const SPObjectRange& objects) +{ + Geom::PathVector result; + + for (auto object : objects) { + Geom::PathVector path_data = get_path_data(object); + result.insert(result.end(), path_data.begin(), path_data.end()); + } + + return result; +} + +SPColor get_color_on_canvas_at_point(const Geom::IntPoint &point, SPDesktop *desktop) +{ + auto pick_area = Geom::IntRect::from_xywh(point, point); + + Inkscape::CanvasItemDrawing *canvas_item_drawing = desktop->getCanvasDrawing(); + Inkscape::Drawing *drawing = canvas_item_drawing->get_drawing(); + + double R, G, B, A; + drawing->average_color(pick_area, R, G, B, A); + + // FIXME change how the result is being constructed. + return SPColor(SPColor(R, G, B).toRGBA32(A)); +} + +void construct_broken_apart(const Geom::PathVector& result_paths, Inkscape::Selection *selection /*, bool fill_gaps = true */) +{ + // is this comment accurate? this seems like it doesn't fill the gaps by default. + // TODO in the future, if fill_gaps is false, don't construct if there is + // no object from the original selection lies in the center point of the + // currently being processed path. possibly by using something like: + // "if (object.GeometricBounds().include(center))" or something similar. + // It might even be a separate function since if the gaps will be filled, + // there is no reason to pass the originally selected objects. This way, + // you'll have two functions, one that takes the selection and one that's not. + + auto desktop = selection->desktop(); + auto document = desktop->getDocument()->getReprDoc(); + auto item = selection->xmlNodes().front(); + auto parent = item->parent(); + auto pos = item->position(); + + for (Geom::Path path : result_paths) { + Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); + + repr->setAttribute("d", sp_svg_write_path(path)); + + // FIXME is this the desired point? + Geom::Point center = path.boundsFast()->midpoint(); + + // FIXME change the way it's being done. + auto color = get_color_on_canvas_at_point( + { + static_cast(center.x()), + static_cast(center.y()) + }, + desktop + ); + + // FIXME would this work? + repr->setAttribute("fill", color.toString()); + + parent->addChildAtPos(repr, pos); + } +} + +void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill_gaps = true) +{ + // FIXME The colors of the resulted objects is missed up. + // FIXME The functions should be in a different place. + // FIXME It should have its own widget. + // FIXME it this doesn't work for some types of objects (like shapes inside each other). + // FIXME this way doesn't seem to work nicely with round objects. + // probably the more you're zoomed in the better the results? + + Geom::PathVector a = get_paths_from_object_list(selection->objects()); + Geom::PathVector b(a); + + sp_flatten(a, fill_oddEven); + sp_flatten(b, fill_nonZero); + + // FIXME should a be duplicated here? does sp_pathvector_boolop modify the given paths? + Geom::PathVector a_duplicated(a); + Geom::PathVector diff = sp_pathvector_boolop(a_duplicated, b, bool_op_diff, fill_oddEven, fill_nonZero); // FIXME make sure of the fills parameters. + + construct_broken_apart(a, selection); + construct_broken_apart(diff, selection); +} + + /** * Decode the verb code and take appropriate action. */ @@ -1254,6 +1399,11 @@ void SelectionVerb::perform(SPAction *action, void *data) sp_selected_path_offset_screen(dt, 10); break; case SP_VERB_SELECTION_INSET: + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); + break_into_non_overlapping_pieces(selection); + break; + case 23232323: selection->removeLPESRecursive(true); selection->unlinkRecursive(true); sp_selected_path_inset(dt); -- GitLab From fa13778d9a26ac096a1e386573a257b0e2bc0501 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 22 May 2021 06:14:46 +0200 Subject: [PATCH 002/235] some uncomplete code. --- src/verbs.cpp | 374 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 316 insertions(+), 58 deletions(-) diff --git a/src/verbs.cpp b/src/verbs.cpp index d4b6e55bf4..206e26a8c4 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -33,50 +33,46 @@ #include #include +#include +#include +#include #include "desktop.h" - +#include "display/control/canvas-item-drawing.h" +#include "display/curve.h" #include "document.h" +#include "extension/effect.h" #include "file.h" #include "gradient-drag.h" #include "help.h" +#include "helper/action.h" #include "inkscape.h" #include "layer-fns.h" #include "layer-manager.h" -#include "message-stack.h" -#include "path-chemistry.h" -#include "selection-chemistry.h" -#include "seltrans.h" -#include "text-chemistry.h" - -#include "display/curve.h" -#include "display/control/canvas-item-drawing.h" - -#include "extension/effect.h" - -#include "helper/action.h" - +#include "livarot/LivarotDefs.h" +#include "livarot/Shape.h" #include "live_effects/effect.h" #include "live_effects/lpe-powerclip.h" #include "live_effects/lpe-powermask.h" - +#include "message-stack.h" #include "object/sp-defs.h" #include "object/sp-flowtext.h" #include "object/sp-guide.h" #include "object/sp-namedview.h" #include "object/sp-object.h" #include "object/sp-path.h" - +#include "path-chemistry.h" +#include "path/path-boolop.h" #include "path/path-offset.h" #include "path/path-outline.h" #include "path/path-simplify.h" -#include "path/path-boolop.h" - -#include "livarot/LivarotDefs.h" -#include "livarot/Shape.h" - +#include "selection-chemistry.h" +#include "seltrans.h" +#include "svg/svg.h" +#include "text-chemistry.h" #include "ui/dialog/align-and-distribute.h" #include "ui/dialog/clonetiler.h" +#include "ui/dialog/dialog-container.h" #include "ui/dialog/document-properties.h" #include "ui/dialog/glyphs.h" #include "ui/dialog/icon-preview.h" @@ -89,7 +85,6 @@ #include "ui/dialog/swatches.h" #include "ui/dialog/symbols.h" #include "ui/icon-names.h" -#include "ui/dialog/dialog-container.h" #include "ui/interface.h" #include "ui/shape-editor.h" #include "ui/shortcuts.h" @@ -99,9 +94,7 @@ #include "ui/tools/pen-tool.h" #include "ui/tools/pencil-tool.h" #include "ui/tools/select-tool.h" -#include "ui/widget/canvas.h" // Canvas area - -#include "svg/svg.h" +#include "ui/widget/canvas.h" // Canvas area using Inkscape::DocumentUndo; using Inkscape::UI::Dialog::ActionAlign; @@ -1170,7 +1163,15 @@ Geom::PathVector get_paths_from_object_list(const SPObjectRange& objects) return result; } -SPColor get_color_on_canvas_at_point(const Geom::IntPoint &point, SPDesktop *desktop) +struct RGBA +{ + double R; + double G; + double B; + double A; +}; + +RGBA get_color_on_canvas_at_point(const Geom::IntPoint &point, SPDesktop *desktop) { auto pick_area = Geom::IntRect::from_xywh(point, point); @@ -1180,8 +1181,60 @@ SPColor get_color_on_canvas_at_point(const Geom::IntPoint &point, SPDesktop *des double R, G, B, A; drawing->average_color(pick_area, R, G, B, A); - // FIXME change how the result is being constructed. - return SPColor(SPColor(R, G, B).toRGBA32(A)); + return {R, G, B, A}; +} + +template +XML::Node* create_path_and_get_node(const SomePath& path, Inkscape::Selection *selection) +{ + auto desktop = selection->desktop(); + auto document = desktop->getDocument()->getReprDoc(); + auto item = selection->xmlNodes().front(); + auto parent = item->parent(); + auto pos = item->position(); + + Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); + repr->setAttribute("d", sp_svg_write_path(path)); + return repr; +} + +template +void draw_on_canvas_colored(const SomePath& path, Inkscape::Selection *selection, guint32 rgba32) +{ + auto desktop = selection->desktop(); + auto document = desktop->getDocument()->getReprDoc(); + auto item = selection->xmlNodes().front(); + auto parent = item->parent(); + auto pos = item->position(); + + Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); + repr->setAttribute("d", sp_svg_write_path(path)); + + // FIXME would this work? + char color_string[32]; + sp_svg_write_color(color_string, sizeof(color_string), rgba32); + repr->setAttribute("fill", color_string); + + parent->addChildAtPos(repr, pos); +} + +template +void draw_on_canvas(const SomePath& path, Inkscape::Selection *selection) +{ + // FIXME is this the desired point? + Geom::Point center = path.boundsFast()->midpoint(); + + // FIXME change the way it's being done. + auto color = get_color_on_canvas_at_point( + { + static_cast(center.x()), + static_cast(center.y()) + }, + selection->desktop() + ); + + auto rgba32 = SP_RGBA32_F_COMPOSE(color.R, color.G, color.B, color.A); + return draw_on_canvas_colored(path, selection, rgba32); } void construct_broken_apart(const Geom::PathVector& result_paths, Inkscape::Selection *selection /*, bool fill_gaps = true */) @@ -1195,37 +1248,188 @@ void construct_broken_apart(const Geom::PathVector& result_paths, Inkscape::Sele // there is no reason to pass the originally selected objects. This way, // you'll have two functions, one that takes the selection and one that's not. - auto desktop = selection->desktop(); - auto document = desktop->getDocument()->getReprDoc(); - auto item = selection->xmlNodes().front(); - auto parent = item->parent(); - auto pos = item->position(); for (Geom::Path path : result_paths) { - Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); + draw_on_canvas(path, selection); + } +} + +Geom::PathVector get_bounds_path_vector(const Geom::OptRect &bounds, double offset) +{ + auto x1 = bounds->left() - offset; + auto x2 = bounds->right() + offset; + auto y1 = bounds->bottom() + offset; + auto y2 = bounds->top() - offset; + Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); + return { Geom::Path(res_bounds) }; +} + +Geom::PathVector get_bounds_path_vector(const Geom::PathVector &paths, double offset) +{ + auto bounds = paths.boundsExact(); + return get_bounds_path_vector(bounds, offset); +} + +std::vector get_4_points(const Geom::OptRect &rect) { + return { rect->top(), rect->bottom(), rect->left(), rect->right() }; +} + +bool is_equal_double_vector(const std::vector &a, const std::vector b, double epsilon) +{ + int size = std::min(a.size(), b.size()); + for (int i = 0; i < size; i++) { + if (std::abs(a[i] - b[i]) > epsilon) { + return false; + } + } + return true; +} + +void remove_path_with_bounds(Geom::PathVector& paths, Geom::PathVector bounds) +{ + for (int i = 0; i < paths.size(); i++) { + auto a = paths.at(i).boundsFast(); + auto b = bounds.boundsFast(); + auto a_points = get_4_points(a); + auto b_points = get_4_points(b); + for (int i = 0; i < 4; i++) { + std::cout << a_points[i] << ", " << b_points[i] << '\n'; + } + if (is_equal_double_vector(a_points, b_points, 0.1)) + { + paths.erase(paths.begin() + i); + i--; + std::cout << "Deleted\n"; + } + std::cout << '\n'; + } +} + +double get_threshold(Geom::PathVector const &path, double threshold) +{ + auto maybe_box = path.boundsFast(); + if (!maybe_box) + return threshold; + Geom::Rect box = *maybe_box; + double diagonal = Geom::distance( + Geom::Point(box[Geom::X].min(), box[Geom::Y].min()), + Geom::Point(box[Geom::X].max(), box[Geom::Y].max()) + ); + return threshold * (diagonal / 100); +} - repr->setAttribute("d", sp_svg_write_path(path)); +// derived from Path_for_item +Path * +Path_for_pathvector(Geom::PathVector const &epathv) +{ + /*std::cout << "converting to Livarot path" << std::endl; + + Geom::SVGPathWriter wr; + wr.feed(epathv); + std::cout << wr.str() << std::endl;*/ + + Path *dest = new Path; + dest->LoadPathVector(epathv); + return dest; +} + +// cut operations PathVectors A,B -> PathVector result. +// This is derived from sp_pathvector_boolop +// take the source paths from the file, do the operation, delete the originals and add the results into a vector of +// cutted pieces +// fra,fra are fill_rules for PathVectors a,b +std::vector +sp_pathvector_cutboolop(Geom::PathVector const &pathva, Geom::PathVector const &pathvb, fill_typ fra, fill_typ frb) +{ + std::vector result; + // extract the livarot Paths from the source objects + // also get the winding rule specified in the style + int nbOriginaux = 2; + std::vector originaux(nbOriginaux); + std::vector origWind(nbOriginaux); + origWind[0]=fra; + origWind[1]=frb; + Geom::PathVector patht; + // Livarot's outline of arcs is broken. So convert the path to linear and cubics only, for which the outline is created correctly. + originaux[0] = Path_for_pathvector(pathv_to_linear_and_cubic_beziers( pathva)); + originaux[1] = Path_for_pathvector(pathv_to_linear_and_cubic_beziers( pathvb)); + + // some temporary instances, first + Shape *theShapeA = new Shape; + Shape *theShapeB = new Shape; + Shape *theShape = new Shape; + Path *res = new Path; + res->SetBackData(false); + + + // cuts= sort of a bastard boolean operation, thus not the axact same modus operandi + // technically, the cut path is not necessarily a polygon (thus has no winding rule) + // it is just uncrossed, and cleaned from duplicate edges and points + // then it's fed to Booleen() which will uncross it against the other path + // then comes the trick: each edge of the cut path is duplicated (one in each direction), + // thus making a polygon. the weight of the edges of the cut are all 0, but + // the Booleen need to invert the ones inside the source polygon (for the subsequent + // ConvertToForme) + + // the cut path needs to have the highest pathID in the back data + // that's how the Booleen() function knows it's an edge of the cut + { + Path* swap=originaux[0];originaux[0]=originaux[1];originaux[1]=swap; + int swai=origWind[0];origWind[0]=origWind[1];origWind[1]=(fill_typ)swai; + } + originaux[0]->ConvertWithBackData(get_threshold(pathva, 0.1)); + + originaux[0]->Fill(theShape, 0); + + theShapeA->ConvertToShape(theShape, origWind[0]); + + originaux[1]->ConvertWithBackData(get_threshold(pathvb, 0.1)); - // FIXME is this the desired point? - Geom::Point center = path.boundsFast()->midpoint(); + if ((originaux[1]->pts.size() == 2) && originaux[1]->pts[0].isMoveTo && !originaux[1]->pts[1].isMoveTo) + originaux[1]->Fill(theShape, 1,false,true,false); // see LP Bug 177956 + else + originaux[1]->Fill(theShape, 1,false,false,false); //do not closeIfNeeded - // FIXME change the way it's being done. - auto color = get_color_on_canvas_at_point( - { - static_cast(center.x()), - static_cast(center.y()) - }, - desktop - ); + theShapeB->ConvertToShape(theShape, fill_justDont); // fill_justDont doesn't computes winding numbers - // FIXME would this work? - repr->setAttribute("fill", color.toString()); + // les elements arrivent en ordre inverse dans la liste + theShape->Booleen(theShapeB, theShapeA, bool_op_cut, 1); - parent->addChildAtPos(repr, pos); + + int* nesting=nullptr; + int* conts=nullptr; + int nbNest=0; + int nbRP=0; + Path** resPath; + theShape->ConvertToFormeNested(res, nbOriginaux, &originaux[0], 1, nbNest, nesting, conts); + delete theShape; + delete theShapeA; + delete theShapeB; + // cut operation is a bit wicked: you need to keep holes + // that's why you needed the nesting + // ConvertToFormeNested() dumped all the subpath in a single Path "res", so we need + // to get the path for each part of the polygon. that's why you need the nesting info: + // to know in which subpath to add a subpath + resPath=res->SubPathsWithNesting(nbRP, true, nbNest, nesting, conts); + + // cleaning + if ( conts ) free(conts); + if ( nesting ) free(nesting); + + delete originaux[0]; + delete originaux[1]; + for (int i=0;isvg_dump_path(); + Geom::PathVector outres = Geom::parse_svg_path(result_str); + result.push_back(outres); + g_free(result_str); } + delete res; + return result; } -void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill_gaps = true) + +void break_into_non_overlapping_pieces1(Inkscape::Selection *selection, bool fill_gaps) { // FIXME The colors of the resulted objects is missed up. // FIXME The functions should be in a different place. @@ -1234,20 +1438,74 @@ void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill // FIXME this way doesn't seem to work nicely with round objects. // probably the more you're zoomed in the better the results? + auto red = SP_RGBA32_F_COMPOSE(255, 0, 0, 1); + auto green = SP_RGBA32_F_COMPOSE(0, 255, 0, 1); + auto blue = SP_RGBA32_F_COMPOSE(0, 0, 255, 1); + + double expansion_offset = 20; // The choice here is arbitrary. change it if found a better number. Geom::PathVector a = get_paths_from_object_list(selection->objects()); - Geom::PathVector b(a); + Geom::PathVector bounds_expanded = get_bounds_path_vector(a, expansion_offset); + //draw_on_canvas(bounds_expanded, selection); + //draw_on_canvas(a, selection); +// auto s = create_path_and_get_node({bounds_expanded, a}, selection); +// for (auto i : s->xmlNodes()) { +// std::cout << i << '\n'; +// std::cout << i->content() << '\n'; +// } +// auto item = s->xmlNodes().front(); +// auto parent = item->parent(); +// auto pos = item->position(); +// s->pathCut(); +// for (auto node : s->xmlNodes()) { +// parent->addChildAtPos(node, pos); +// } + + auto result = sp_pathvector_cutboolop(a, bounds_expanded, fill_nonZero, fill_nonZero); + // first 2 items are not relevant. Note: second element is all of the objects merged. + // if you want a union, just take the second item, don't calculate it again. + // result.erase(result.begin(), result.begin()+2); + +// draw_on_canvas(a, selection); +// draw_on_canvas(bounds_expanded, selection); + // discard first path. + for (int i = 1; i < result.size(); i++) { + draw_on_canvas(result[i], selection); + } +} - sp_flatten(a, fill_oddEven); - sp_flatten(b, fill_nonZero); +void break_into_non_overlapping_pieces2(Inkscape::Selection *selection, bool fill_gaps) +{ + double expansion_offset = 20; // The choice here is arbitrary. change it if found a better number. + Geom::PathVector a = get_paths_from_object_list(selection->objects()); + Geom::PathVector bounds_expanded = get_bounds_path_vector(a, expansion_offset); + auto item = selection->xmlNodes().front(); + auto pos = item->position(); + auto parent = item->parent(); + auto repr = parent->document()->createElement("svg:path"); + repr->setAttribute("d", sp_svg_write_path(bounds_expanded)); + parent->addChildAtPos(repr, pos); - // FIXME should a be duplicated here? does sp_pathvector_boolop modify the given paths? - Geom::PathVector a_duplicated(a); - Geom::PathVector diff = sp_pathvector_boolop(a_duplicated, b, bool_op_diff, fill_oddEven, fill_nonZero); // FIXME make sure of the fills parameters. + selection->combine(); + selection->add(repr); + selection->pathCut(); - construct_broken_apart(a, selection); - construct_broken_apart(diff, selection); + auto back = selection->xmlNodes().back(); + back->parent()->removeChild(back); } +void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool is_destructive, bool fill_gaps = true) +{ + break_into_non_overlapping_pieces1(selection, fill_gaps); + + // FIXME not working. leaves the first object. this is probably becuase + // how you draw on the canvas. fix it later. + if (is_destructive) { + auto nodes = selection->xmlNodes(); + for (auto node : nodes) { + node->parent()->removeChild(node); + } + } +} /** * Decode the verb code and take appropriate action. @@ -1401,7 +1659,7 @@ void SelectionVerb::perform(SPAction *action, void *data) case SP_VERB_SELECTION_INSET: selection->removeLPESRecursive(true); selection->unlinkRecursive(true); - break_into_non_overlapping_pieces(selection); + break_into_non_overlapping_pieces(selection, true, true); break; case 23232323: selection->removeLPESRecursive(true); -- GitLab From 4e0043a14a7f57e94c2d06258e4588956507be79 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 22 May 2021 08:03:10 +0200 Subject: [PATCH 003/235] some other missing around --- src/verbs.cpp | 124 +++++++------------------------------------------- 1 file changed, 16 insertions(+), 108 deletions(-) diff --git a/src/verbs.cpp b/src/verbs.cpp index 206e26a8c4..f10b6f9bde 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1106,32 +1106,6 @@ void EditVerb::perform(SPAction *action, void *data) } // end of sp_verb_action_edit_perform() -typedef FillRule FillRuleFlatten; - -static void -sp_flatten(Geom::PathVector &pathvector, FillRuleFlatten fillkind) -{ - // TODO refactor this function. it's duplicated in multiple places. - Path *orig = new Path; - orig->LoadPathVector(pathvector); - Shape *theShape = new Shape; - Shape *theRes = new Shape; - orig->ConvertWithBackData (1.0); - orig->Fill (theShape, 0); - theRes->ConvertToShape (theShape, FillRule(fillkind)); - Path *originaux[1]; - originaux[0] = orig; - Path *res = new Path; - theRes->ConvertToForme (res, 1, originaux, true); - - delete theShape; - delete theRes; - char *res_d = res->svg_dump_path (); - delete res; - delete orig; - pathvector = sp_svg_read_pathv(res_d); -} - Geom::PathVector get_path_data(SPObject* object) { Geom::PathVector result; @@ -1184,20 +1158,6 @@ RGBA get_color_on_canvas_at_point(const Geom::IntPoint &point, SPDesktop *deskto return {R, G, B, A}; } -template -XML::Node* create_path_and_get_node(const SomePath& path, Inkscape::Selection *selection) -{ - auto desktop = selection->desktop(); - auto document = desktop->getDocument()->getReprDoc(); - auto item = selection->xmlNodes().front(); - auto parent = item->parent(); - auto pos = item->position(); - - Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); - repr->setAttribute("d", sp_svg_write_path(path)); - return repr; -} - template void draw_on_canvas_colored(const SomePath& path, Inkscape::Selection *selection, guint32 rgba32) { @@ -1270,41 +1230,6 @@ Geom::PathVector get_bounds_path_vector(const Geom::PathVector &paths, double of return get_bounds_path_vector(bounds, offset); } -std::vector get_4_points(const Geom::OptRect &rect) { - return { rect->top(), rect->bottom(), rect->left(), rect->right() }; -} - -bool is_equal_double_vector(const std::vector &a, const std::vector b, double epsilon) -{ - int size = std::min(a.size(), b.size()); - for (int i = 0; i < size; i++) { - if (std::abs(a[i] - b[i]) > epsilon) { - return false; - } - } - return true; -} - -void remove_path_with_bounds(Geom::PathVector& paths, Geom::PathVector bounds) -{ - for (int i = 0; i < paths.size(); i++) { - auto a = paths.at(i).boundsFast(); - auto b = bounds.boundsFast(); - auto a_points = get_4_points(a); - auto b_points = get_4_points(b); - for (int i = 0; i < 4; i++) { - std::cout << a_points[i] << ", " << b_points[i] << '\n'; - } - if (is_equal_double_vector(a_points, b_points, 0.1)) - { - paths.erase(paths.begin() + i); - i--; - std::cout << "Deleted\n"; - } - std::cout << '\n'; - } -} - double get_threshold(Geom::PathVector const &path, double threshold) { auto maybe_box = path.boundsFast(); @@ -1438,43 +1363,25 @@ void break_into_non_overlapping_pieces1(Inkscape::Selection *selection, bool fil // FIXME this way doesn't seem to work nicely with round objects. // probably the more you're zoomed in the better the results? - auto red = SP_RGBA32_F_COMPOSE(255, 0, 0, 1); - auto green = SP_RGBA32_F_COMPOSE(0, 255, 0, 1); - auto blue = SP_RGBA32_F_COMPOSE(0, 0, 255, 1); + selection->removeLPESRecursive(true); double expansion_offset = 20; // The choice here is arbitrary. change it if found a better number. Geom::PathVector a = get_paths_from_object_list(selection->objects()); Geom::PathVector bounds_expanded = get_bounds_path_vector(a, expansion_offset); - //draw_on_canvas(bounds_expanded, selection); - //draw_on_canvas(a, selection); -// auto s = create_path_and_get_node({bounds_expanded, a}, selection); -// for (auto i : s->xmlNodes()) { -// std::cout << i << '\n'; -// std::cout << i->content() << '\n'; -// } -// auto item = s->xmlNodes().front(); -// auto parent = item->parent(); -// auto pos = item->position(); -// s->pathCut(); -// for (auto node : s->xmlNodes()) { -// parent->addChildAtPos(node, pos); -// } - - auto result = sp_pathvector_cutboolop(a, bounds_expanded, fill_nonZero, fill_nonZero); - // first 2 items are not relevant. Note: second element is all of the objects merged. - // if you want a union, just take the second item, don't calculate it again. - // result.erase(result.begin(), result.begin()+2); - -// draw_on_canvas(a, selection); -// draw_on_canvas(bounds_expanded, selection); - // discard first path. - for (int i = 1; i < result.size(); i++) { - draw_on_canvas(result[i], selection); - } + + auto result = sp_pathvector_cutboolop(a, bounds_expanded, fill_nonZero, fill_nonZero); + + // discard first path. + for (int i = 1; i < result.size(); i++) { + draw_on_canvas(result[i], selection); + } } void break_into_non_overlapping_pieces2(Inkscape::Selection *selection, bool fill_gaps) { + selection->combine(); + selection->removeLPESRecursive(true); + double expansion_offset = 20; // The choice here is arbitrary. change it if found a better number. Geom::PathVector a = get_paths_from_object_list(selection->objects()); Geom::PathVector bounds_expanded = get_bounds_path_vector(a, expansion_offset); @@ -1485,7 +1392,6 @@ void break_into_non_overlapping_pieces2(Inkscape::Selection *selection, bool fil repr->setAttribute("d", sp_svg_write_path(bounds_expanded)); parent->addChildAtPos(repr, pos); - selection->combine(); selection->add(repr); selection->pathCut(); @@ -1495,14 +1401,17 @@ void break_into_non_overlapping_pieces2(Inkscape::Selection *selection, bool fil void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool is_destructive, bool fill_gaps = true) { + // FIXME both 1 and 2 crashes when applied on 4 nested rectangles. break_into_non_overlapping_pieces1(selection, fill_gaps); - // FIXME not working. leaves the first object. this is probably becuase + // FIXME not working. leaves some object. this is probably becuase // how you draw on the canvas. fix it later. if (is_destructive) { auto nodes = selection->xmlNodes(); - for (auto node : nodes) { + while (!nodes.empty()) { + auto node = nodes.back(); node->parent()->removeChild(node); + nodes.pop_back(); } } } @@ -1657,7 +1566,6 @@ void SelectionVerb::perform(SPAction *action, void *data) sp_selected_path_offset_screen(dt, 10); break; case SP_VERB_SELECTION_INSET: - selection->removeLPESRecursive(true); selection->unlinkRecursive(true); break_into_non_overlapping_pieces(selection, true, true); break; -- GitLab From deec7b11ace923a163af5319ae1cba432e23e283 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 22 May 2021 10:19:17 +0200 Subject: [PATCH 004/235] Mostly working break-into-non-overlapping-pieces. can be destructive or not can fill the gaps or not a little buggy but works most of the times. --- src/verbs.cpp | 309 +++++++++++++++++++++++--------------------------- 1 file changed, 143 insertions(+), 166 deletions(-) diff --git a/src/verbs.cpp b/src/verbs.cpp index f10b6f9bde..6e9a9cfb3a 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1106,130 +1106,6 @@ void EditVerb::perform(SPAction *action, void *data) } // end of sp_verb_action_edit_perform() -Geom::PathVector get_path_data(SPObject* object) -{ - Geom::PathVector result; - if (SPGroup *group = dynamic_cast(object)) { - std::vector items = sp_item_group_item_list(group); - for (SPItem *item : items) { - Geom::PathVector sub_result = get_path_data(item); - result.insert(result.end(), sub_result.begin(), sub_result.end()); - } - } - else if (SPShape *shape = dynamic_cast(object)) { - result = shape->curve()->get_pathvector(); - } else if (SPPath *path = dynamic_cast(object)) { - result = path->curve()->get_pathvector(); - } - - return result; -} - -Geom::PathVector get_paths_from_object_list(const SPObjectRange& objects) -{ - Geom::PathVector result; - - for (auto object : objects) { - Geom::PathVector path_data = get_path_data(object); - result.insert(result.end(), path_data.begin(), path_data.end()); - } - - return result; -} - -struct RGBA -{ - double R; - double G; - double B; - double A; -}; - -RGBA get_color_on_canvas_at_point(const Geom::IntPoint &point, SPDesktop *desktop) -{ - auto pick_area = Geom::IntRect::from_xywh(point, point); - - Inkscape::CanvasItemDrawing *canvas_item_drawing = desktop->getCanvasDrawing(); - Inkscape::Drawing *drawing = canvas_item_drawing->get_drawing(); - - double R, G, B, A; - drawing->average_color(pick_area, R, G, B, A); - - return {R, G, B, A}; -} - -template -void draw_on_canvas_colored(const SomePath& path, Inkscape::Selection *selection, guint32 rgba32) -{ - auto desktop = selection->desktop(); - auto document = desktop->getDocument()->getReprDoc(); - auto item = selection->xmlNodes().front(); - auto parent = item->parent(); - auto pos = item->position(); - - Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); - repr->setAttribute("d", sp_svg_write_path(path)); - - // FIXME would this work? - char color_string[32]; - sp_svg_write_color(color_string, sizeof(color_string), rgba32); - repr->setAttribute("fill", color_string); - - parent->addChildAtPos(repr, pos); -} - -template -void draw_on_canvas(const SomePath& path, Inkscape::Selection *selection) -{ - // FIXME is this the desired point? - Geom::Point center = path.boundsFast()->midpoint(); - - // FIXME change the way it's being done. - auto color = get_color_on_canvas_at_point( - { - static_cast(center.x()), - static_cast(center.y()) - }, - selection->desktop() - ); - - auto rgba32 = SP_RGBA32_F_COMPOSE(color.R, color.G, color.B, color.A); - return draw_on_canvas_colored(path, selection, rgba32); -} - -void construct_broken_apart(const Geom::PathVector& result_paths, Inkscape::Selection *selection /*, bool fill_gaps = true */) -{ - // is this comment accurate? this seems like it doesn't fill the gaps by default. - // TODO in the future, if fill_gaps is false, don't construct if there is - // no object from the original selection lies in the center point of the - // currently being processed path. possibly by using something like: - // "if (object.GeometricBounds().include(center))" or something similar. - // It might even be a separate function since if the gaps will be filled, - // there is no reason to pass the originally selected objects. This way, - // you'll have two functions, one that takes the selection and one that's not. - - - for (Geom::Path path : result_paths) { - draw_on_canvas(path, selection); - } -} - -Geom::PathVector get_bounds_path_vector(const Geom::OptRect &bounds, double offset) -{ - auto x1 = bounds->left() - offset; - auto x2 = bounds->right() + offset; - auto y1 = bounds->bottom() + offset; - auto y2 = bounds->top() - offset; - Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); - return { Geom::Path(res_bounds) }; -} - -Geom::PathVector get_bounds_path_vector(const Geom::PathVector &paths, double offset) -{ - auto bounds = paths.boundsExact(); - return get_bounds_path_vector(bounds, offset); -} - double get_threshold(Geom::PathVector const &path, double threshold) { auto maybe_box = path.boundsFast(); @@ -1353,66 +1229,166 @@ sp_pathvector_cutboolop(Geom::PathVector const &pathva, Geom::PathVector const & return result; } - -void break_into_non_overlapping_pieces1(Inkscape::Selection *selection, bool fill_gaps) +Geom::PathVector get_path_data(SPObject* object) { - // FIXME The colors of the resulted objects is missed up. - // FIXME The functions should be in a different place. - // FIXME It should have its own widget. - // FIXME it this doesn't work for some types of objects (like shapes inside each other). - // FIXME this way doesn't seem to work nicely with round objects. - // probably the more you're zoomed in the better the results? - - selection->removeLPESRecursive(true); + Geom::PathVector result; + if (SPGroup *group = dynamic_cast(object)) { + std::vector items = sp_item_group_item_list(group); + for (SPItem *item : items) { + Geom::PathVector sub_result = get_path_data(item); + result.insert(result.end(), sub_result.begin(), sub_result.end()); + } + } + else if (SPShape *shape = dynamic_cast(object)) { + result = shape->curve()->get_pathvector(); + } else if (SPPath *path = dynamic_cast(object)) { + result = path->curve()->get_pathvector(); + } - double expansion_offset = 20; // The choice here is arbitrary. change it if found a better number. - Geom::PathVector a = get_paths_from_object_list(selection->objects()); - Geom::PathVector bounds_expanded = get_bounds_path_vector(a, expansion_offset); + return result; +} - auto result = sp_pathvector_cutboolop(a, bounds_expanded, fill_nonZero, fill_nonZero); +Geom::PathVector get_paths_from_object_list(const SPObjectRange& objects) +{ + Geom::PathVector result; - // discard first path. - for (int i = 1; i < result.size(); i++) { - draw_on_canvas(result[i], selection); + for (auto object : objects) { + Geom::PathVector path_data = get_path_data(object); + result.insert(result.end(), path_data.begin(), path_data.end()); } + + return result; } -void break_into_non_overlapping_pieces2(Inkscape::Selection *selection, bool fill_gaps) +SPItem* getAnItemAtPointFromSelection(Geom::Point& point, Inkscape::Selection *selection) { - selection->combine(); - selection->removeLPESRecursive(true); +// auto desktop = selection->desktop(); +// return desktop->getItemAtPoint(center, false); + + // FIXME figure out how to use the above method. + // this is not very accurate since it test whether + // the point lies in the bounding "rectangle" of + // the item no matter how it's shaped. also slow. + + // FIXME remove these statements. + + for (auto item : selection->items()) { + std::cout << "looking now at: "; + std::cout << "x1: " << item->visualBounds()->left() << ' '; + std::cout << "x2: " << item->visualBounds()->right() << ' '; + std::cout << "y1: " << item->visualBounds()->bottom() << ' '; + std::cout << "y2: " << item->visualBounds()->top() << "\n"; + if (item->visualBounds().contains(point)) { + std::cout << "Last one is a match!\n\n"; + return item; + } + } - double expansion_offset = 20; // The choice here is arbitrary. change it if found a better number. - Geom::PathVector a = get_paths_from_object_list(selection->objects()); - Geom::PathVector bounds_expanded = get_bounds_path_vector(a, expansion_offset); + return nullptr; +} + +template +void draw_on_canvas(const SomePath& path, Inkscape::Selection *selection, bool fill_gaps) +{ + // FIXME draw elements correctly. figure out what parent to use. auto item = selection->xmlNodes().front(); - auto pos = item->position(); auto parent = item->parent(); - auto repr = parent->document()->createElement("svg:path"); - repr->setAttribute("d", sp_svg_write_path(bounds_expanded)); + auto pos = item->position(); + + // FIXME is this the desired point? + Geom::Point center = path.boundsFast()->midpoint(); + auto original_item = getAnItemAtPointFromSelection(center, selection); + + // TODO delete this debugging statements. + if (!original_item) { + std::cout << "No item found at " << "x: " << center.x() << ", y: " << center.y() << ".\n"; + if (!fill_gaps) { + return; + } + } else { + std::cout << "Item found at " << "x: " << center.x() << ", y: " << center.y() << ".\n"; + } + + Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); + repr->setAttribute("d", sp_svg_write_path(path)); + + if (original_item) { + gchar *style = g_strdup(original_item->getRepr()->attribute("style")); + auto str = sp_svg_write_path(path); + + repr->setAttribute("style", style); + repr->setAttribute("d", str); + } + parent->addChildAtPos(repr, pos); +} - selection->add(repr); - selection->pathCut(); +void construct_broken_apart(const Geom::PathVector& result_paths, Inkscape::Selection *selection, bool fill_gaps = true) +{ + for (Geom::Path path : result_paths) { + draw_on_canvas(path, selection, fill_gaps); + } +} - auto back = selection->xmlNodes().back(); - back->parent()->removeChild(back); +Geom::PathVector get_bounds_path_vector(const Geom::OptRect &bounds, double offset) +{ + auto x1 = bounds->left() - offset; + auto x2 = bounds->right() + offset; + auto y1 = bounds->bottom() + offset; + auto y2 = bounds->top() - offset; + Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); + return { Geom::Path(res_bounds) }; +} + +Geom::PathVector get_bounds_path_vector(const Geom::PathVector &paths, double offset) +{ + // FIXME boundsFast or boundsExact? probably it's ok with boundsFast since + // most of the time an offset will be given (if not all?). + auto bounds = paths.boundsFast(); + return get_bounds_path_vector(bounds, offset); } -void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool is_destructive, bool fill_gaps = true) +void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill_gaps, bool is_destructive) { - // FIXME both 1 and 2 crashes when applied on 4 nested rectangles. - break_into_non_overlapping_pieces1(selection, fill_gaps); + // FIXME crashes when applied on 4 nested rectangles and some other set of objects. + // FIXME The functions should be in a different place. + // FIXME It should have its own widget. + // FIXME this way doesn't seem to work nicely with round objects. + // probably the more you're zoomed in the better the results? + + auto desktop = selection->desktop(); + + if(desktop){ + // set "busy" cursor + desktop->setWaitingCursor(); + // disable redrawing during the break-apart operation for remarkable speedup for large paths + desktop->getCanvas()->set_drawing_disabled(true); + } + + double expansion_offset = 5; // The choice here is arbitrary. change it if found a better number. + Geom::PathVector a = get_paths_from_object_list(selection->objects()); + Geom::PathVector bounds_expanded = get_bounds_path_vector(a, expansion_offset); + + auto result = sp_pathvector_cutboolop(a, bounds_expanded, fill_nonZero, fill_nonZero); + + // discard first path. + for (int i = 1; i < result.size(); i++) { + draw_on_canvas(result[i], selection, fill_gaps); + } + std::cout << '\n'; - // FIXME not working. leaves some object. this is probably becuase - // how you draw on the canvas. fix it later. if (is_destructive) { - auto nodes = selection->xmlNodes(); - while (!nodes.empty()) { - auto node = nodes.back(); - node->parent()->removeChild(node); - nodes.pop_back(); - } + selection->deleteItems(); + } else { + // the constructed parts should be above it. + // FIXME is it right to make it at the bottom + // of everything, not only the constructed parts? + selection->lowerToBottom(true); + } + + if (desktop) { + desktop->getCanvas()->set_drawing_disabled(false); + desktop->clearWaitingCursor(); } } @@ -1566,8 +1542,9 @@ void SelectionVerb::perform(SPAction *action, void *data) sp_selected_path_offset_screen(dt, 10); break; case SP_VERB_SELECTION_INSET: + selection->removeLPESRecursive(true); selection->unlinkRecursive(true); - break_into_non_overlapping_pieces(selection, true, true); + break_into_non_overlapping_pieces(selection, true, false); break; case 23232323: selection->removeLPESRecursive(true); -- GitLab From 39f49fe43bd142ac1254e526b64e51d51dfc2dd6 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 22 May 2021 11:12:28 +0200 Subject: [PATCH 005/235] A fix for the crashing that happens credits: @jabiertxof --- src/livarot/ShapeMisc.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/livarot/ShapeMisc.cpp b/src/livarot/ShapeMisc.cpp index b76ea5875b..6a72ec99c9 100644 --- a/src/livarot/ShapeMisc.cpp +++ b/src/livarot/ShapeMisc.cpp @@ -389,15 +389,20 @@ Shape::ConvertToFormeNested (Path * dest, int nbP, Path * *orig, int /*wildPath* break; } { - int askTo = pData[fi].askForWindingB; - if (askTo < 0 || askTo >= numberOfEdges() ) { - parentContour=-1; + int askTo = 0; + if (pData.size()<= fi) { + parentContour=-1; } else { - if (getEdge(askTo).prevS >= 0) { - parentContour = GPOINTER_TO_INT(swdData[askTo].misc); - parentContour-=1; // pour compenser le decalage + askTo = pData[fi].askForWindingB; + if (askTo < 0 || askTo >= numberOfEdges() ) { + parentContour=-1; + } else { + if (getEdge(askTo).prevS >= 0) { + parentContour = GPOINTER_TO_INT(swdData[askTo].misc); + parentContour-=1; // pour compenser le decalage + } + childEdge = getPoint(fi % numberOfPoints()).incidentEdge[FIRST]; } - childEdge = getPoint(fi % numberOfPoints()).incidentEdge[FIRST]; } } lastPtUsed = fi + 1; -- GitLab From 9b490d7eb0a0af39cdcabd3924f69293e89ddaa9 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 24 May 2021 22:59:43 +0200 Subject: [PATCH 006/235] some crap written when was trying to figure out how stuff works (didn't figure out completely yet). may need some of it again after deleting it. commiting it. --- src/verbs.cpp | 548 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 502 insertions(+), 46 deletions(-) diff --git a/src/verbs.cpp b/src/verbs.cpp index 6e9a9cfb3a..431ac75e50 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -26,6 +26,7 @@ #include #include #include +#include // Note that gtkmm headers must be included before gtk+ C headers // in all files. The same applies for glibmm/glib etc. @@ -1229,6 +1230,11 @@ sp_pathvector_cutboolop(Geom::PathVector const &pathva, Geom::PathVector const & return result; } +std::string point_coords(const Geom::Point& point) +{ + return "(" + std::to_string(point.x()) + ", " + std::to_string(point.y()) + ")"; +} + Geom::PathVector get_path_data(SPObject* object) { Geom::PathVector result; @@ -1248,7 +1254,8 @@ Geom::PathVector get_path_data(SPObject* object) return result; } -Geom::PathVector get_paths_from_object_list(const SPObjectRange& objects) +template +Geom::PathVector get_paths_from_object_list(const ObjectList& objects) { Geom::PathVector result; @@ -1260,73 +1267,279 @@ Geom::PathVector get_paths_from_object_list(const SPObjectRange& objects) return result; } -SPItem* getAnItemAtPointFromSelection(Geom::Point& point, Inkscape::Selection *selection) +Geom::PathVector get_circle_bounds_path(const Geom::Point ¢er, double radius) { -// auto desktop = selection->desktop(); -// return desktop->getItemAtPoint(center, false); - - // FIXME figure out how to use the above method. - // this is not very accurate since it test whether - // the point lies in the bounding "rectangle" of - // the item no matter how it's shaped. also slow. - - // FIXME remove these statements. + auto x1 = center.x() - radius; + auto x2 = center.x() + radius; + auto y1 = center.y() + radius; + auto y2 = center.y() - radius; + Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); + return { Geom::Path(res_bounds) }; +} - for (auto item : selection->items()) { - std::cout << "looking now at: "; +//SPItem* getAnItemAtPointFromSelection(Geom::Point& point, Inkscape::Selection *selection) +//{ +//// auto desktop = selection->desktop(); +//// return desktop->getItemAtPoint(center, false); +// +// // FIXME figure out how to use the above method. +// // this is not very accurate since it test whether +// // the point lies in the bounding "rectangle" of +// // the item no matter how it's shaped. also slow. +// +// const double epsilon = 0.01; +// Geom::PathVector current_point = Geom::PathVector(get_circle_bounds_path(point, epsilon)); +//// Geom::PathVector current_point { Geom::Path(point) }; +// +// // TODO delete these debugging statements. +// +// for (auto item : selection->items()) { +// std::cout << "looking now at: "; +// std::cout << "x1: " << item->visualBounds()->left() << ' '; +// std::cout << "x2: " << item->visualBounds()->right() << ' '; +// std::cout << "y1: " << item->visualBounds()->bottom() << ' '; +// std::cout << "y2: " << item->visualBounds()->top() << "\n"; +//// if (item->visualBounds().contains(point)) { +// Geom::PathVector paths = get_path_data(item); +// // Geom::PathVector intersection = sp_pathvector_boolop(paths, current_point, bool_op_inters, fill_nonZero, fill_nonZero); +// if (!paths.intersect(current_point, epsilon).empty()) { +// std::cout << "Last one is a match!\n\n"; +// return item; +// } +// } +// +// return nullptr; +//} + +template // Geom::Path or Geom::PathVector +void draw_on_canvas_and_copy_style_from_selection(const Path& path, Inkscape::Selection *selection, bool fill_gaps); + +template // Geom::Path or Geom::PathVector +void draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Node *parent, int pos); + +//bool is_valid_intersection(const Geom::PathVector &intersection, const Geom::PathVector &original_paths, Inkscape::Selection *selection, bool draw) +//{ +// Geom::PathVector diff = sp_pathvector_boolop(original_paths, intersection, bool_op_diff, fill_nonZero, fill_nonZero); +// if (draw) { +// XML::Node *parent = selection->xmlNodes().front()->parent(); +// draw_on_canvas(diff, nullptr, parent, 0); +// } +// return diff != original_paths; +//} + +//SPItem* getAnItemThatIntersectsFromSelection(const Geom::PathVector &paths, Inkscape::Selection *selection, double epsilon = 0.01) +//{ +// for (SPItem* item : selection->items()) { +// auto item_paths = get_path_data(item); +// auto intersection = sp_pathvector_boolop(paths, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); +// if (!intersection.empty() && is_valid_intersection(intersection, paths)) { +//// auto x = paths.boundsFast()->midpoint().x(); +//// auto y = paths.boundsFast()->midpoint().y(); +//// if (x > 110 && x < 111 && y > 192 && y < 193) { +//// draw_on_canvas(intersection, selection, true); +//// } +// return item; +// } +// } +// return nullptr; +//} + +SPItem* getAnItemThatIntersectsFromSelection(const Geom::PathVector &p1, const Geom::PathVector &p2, Inkscape::Selection *selection, double epsilon = 0.01) +{ + for (SPItem* item : selection->items()) { +#ifdef dbg + std::cout << "\nlooking now at: "; std::cout << "x1: " << item->visualBounds()->left() << ' '; std::cout << "x2: " << item->visualBounds()->right() << ' '; std::cout << "y1: " << item->visualBounds()->bottom() << ' '; - std::cout << "y2: " << item->visualBounds()->top() << "\n"; - if (item->visualBounds().contains(point)) { - std::cout << "Last one is a match!\n\n"; + std::cout << "y2: " << item->visualBounds()->top() << "\n\n"; +#endif + auto item_paths = get_path_data(item); + auto inters1 = sp_pathvector_boolop(p1, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); + auto inters2 = sp_pathvector_boolop(p2, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); +// auto x = paths.boundsFast()->midpoint().x(); +// auto y = paths.boundsFast()->midpoint().y(); +// bool draw = x > 110 && x < 111 && y > 192 && y < 193; + if (!inters1.empty() && !inters2.empty()) { // && is_valid_intersection(intersection, paths, selection, draw)) { +// if (draw) { +// draw_on_canvas_and_copy_style_from_selection(intersection, selection, true); +// } return item; } } - return nullptr; } -template -void draw_on_canvas(const SomePath& path, Inkscape::Selection *selection, bool fill_gaps) +SPItem* getAnItemThatIntersectsFromSelection(const Geom::Path &p1, const Geom::Path &p2, Inkscape::Selection *selection, double epsilon = 0.01) +{ + return getAnItemThatIntersectsFromSelection(Geom::PathVector(p1), Geom::PathVector(p2), selection, epsilon); +} + +SPItem* getAnItemThatIntersectsFromSelection(const Geom::Point &p1, const Geom::Point &p2, Inkscape::Selection *selection, double epsilon = 0.015) +{ + // FIXME fix it later. make sure that there is no problem in passing a path with only one point. + // it would be better if there was no epsilon. just is the point inside the object or not. +// Geom::Rect r1(p1, p1); +// Geom::Rect r2(p2, p2); +// return getAnItemThatIntersectsFromSelection(Geom::Path(r1), Geom::Path(r2), selection, epsilon); + auto r1 = get_circle_bounds_path(p1, epsilon); + auto r2 = get_circle_bounds_path(p2, epsilon); + return getAnItemThatIntersectsFromSelection(r1, r2, selection, epsilon); +} + +template +std::vector get_path_points(const Path &path) +{ + auto result = path.nodes(); +#ifdef dbg + std::cout << "\nPoints:\n"; + for (auto &p : result) { + std::cout << "(" << p.x() << ", " << p.y() << ")\n"; + } +#endif + return result; +} + +bool point_is_lower(const Geom::Point &a, const Geom::Point &b) { + return a.y() > b.y(); +} + +template +std::pair get_lowest_two_points_of_path(const Path &path) +{ + Geom::Point low, lowest; + auto points = get_path_points(path); + lowest = points.back(); + for (int i = 1; i < points.size(); i++) { + auto &point = points[i]; + if (point_is_lower(point, lowest)) { + low = lowest; + lowest = point; + } else if (point_is_lower(point, low)) { + low = point; + } + } + + // FIXME should check if 2 points exist or + // should expect the number of points to be at least 2? + return { lowest, low }; +} + +template +Geom::Point get_point_between_the_lowest_two_points_of_path(const Path &path) { + auto points = get_lowest_two_points_of_path(path); + double x1 = points.first.x(); + double x2 = points.second.x(); + double y1 = points.first.y(); + double y2 = points.second.y(); +#ifdef dbg + std::cout << "Lowest 2 points are:\n"; + std::cout << x1 << ", " << y1 << '\n'; + std::cout << x2 << ", " << y2 << "\n\n"; + std::cout << "The result: " << double(x1 + x2) / 2 << ", " << double(y1 + y2) / 2 << "\n\n"; +#endif + return Geom::Point { double(x1 + x2) / 2, double(y1 + y2) / 2 }; +} + +template +std::pair get_min_max_points(const Path &path) +{ + auto points = get_path_points(path); + Geom::Point min = points[0]; + Geom::Point max = points[0]; + for (int i = 1; i < points.size(); i++) { + if (points[i].y() < max.y()) { + max = points[i]; + } + if (points[i].y() > min.y()) { + min = points[i]; + } + } + return {min, max}; +} + +template // Geom::Path or Geom::PathVector +void draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Node *parent, int pos) +{ + Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); + repr->setAttribute("d", sp_svg_write_path(path)); + + if (to_copy_from) { + gchar *style = g_strdup(to_copy_from->getRepr()->attribute("style")); + repr->setAttribute("style", style); + } + + auto str = sp_svg_write_path(path); + repr->setAttribute("d", str); + + parent->addChildAtPos(repr, pos); +} + +template // Geom::Path or Geom::PathVector +void draw_on_canvas_and_copy_style_from_selection(const Path& path, Inkscape::Selection *selection, bool fill_gaps) +{ + // TODO later, query the exact color at the + // mid-pint and set the color of the constructed object to it. + // FIXME draw elements correctly. figure out what parent to use. auto item = selection->xmlNodes().front(); auto parent = item->parent(); auto pos = item->position(); - // FIXME is this the desired point? - Geom::Point center = path.boundsFast()->midpoint(); - auto original_item = getAnItemAtPointFromSelection(center, selection); + // shouldn't use midpoint of the bounds for locating a shape since it might not be on the shape. + // shouldn't use the bottom 2 points. not useful when having curves. + // not useful if the bottom 2 points forms an edge in the shape. + // shouldn't calculate intersection because the gaps intersects at the edges. + Geom::Point center_point = path.boundsFast()->midpoint(); + +// auto lowest_points = get_lowest_two_points_of_path(path); +// // auto point = get_point_between_the_lowest_two_points_of_path(path); +// +// double epsilon = 0.1; +// Geom::Point point(lowest_points.first.x(), lowest_points.first.y() - epsilon); + auto extreme_points = get_min_max_points(path); + auto p1 = extreme_points.first; + auto p2 = extreme_points.second; + auto original_item = getAnItemThatIntersectsFromSelection(p1, p2, selection); + +#ifdef dbg + // TODO delete these debugging statements. + auto bounds = path.boundsFast(); + auto x1 = bounds->left(); + auto x2 = bounds->right(); + auto y1 = bounds->bottom(); + auto y2 = bounds->top(); + std::cout << "Looking at an item with the bounds of (" << x1 << + ", " << y1 << ") and (" << x2 << ", " << y2 << "), a center point of (" << center_point.x() << ", " << center_point.y() << ").\n"; + + std::cout << "Points of search: (" << p1.x() << ", " << p1.y() << ") and (" << p2.x() << ", " << p2.y() << ").\n"; +#endif - // TODO delete this debugging statements. if (!original_item) { - std::cout << "No item found at " << "x: " << center.x() << ", y: " << center.y() << ".\n"; +#ifdef dbg + std::cout << "No item found.\n\n"; +#endif if (!fill_gaps) { return; } } else { - std::cout << "Item found at " << "x: " << center.x() << ", y: " << center.y() << ".\n"; - } - - Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); - repr->setAttribute("d", sp_svg_write_path(path)); - - if (original_item) { - gchar *style = g_strdup(original_item->getRepr()->attribute("style")); - auto str = sp_svg_write_path(path); - - repr->setAttribute("style", style); - repr->setAttribute("d", str); +#ifdef dbg + std::cout << "Found an item with Dimensions: "; + std::cout << "x1: " << original_item->geometricBounds()->left() << ' '; + std::cout << "x2: " << original_item->geometricBounds()->right() << ' '; + std::cout << "y1: " << original_item->geometricBounds()->bottom() << ' '; + std::cout << "y2: " << original_item->geometricBounds()->top() << "\n\n"; +#endif } - parent->addChildAtPos(repr, pos); + draw_on_canvas(path, original_item, parent, pos); } void construct_broken_apart(const Geom::PathVector& result_paths, Inkscape::Selection *selection, bool fill_gaps = true) { for (Geom::Path path : result_paths) { - draw_on_canvas(path, selection, fill_gaps); + draw_on_canvas_and_copy_style_from_selection(path, selection, fill_gaps); } } @@ -1348,9 +1561,224 @@ Geom::PathVector get_bounds_path_vector(const Geom::PathVector &paths, double of return get_bounds_path_vector(bounds, offset); } -void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill_gaps, bool is_destructive) +double points_distance(const Geom::Point &a, const Geom::Point &b) { + auto x1 = a.x(), x2 = b.x(); + auto y1 = a.y(), y2 = b.y(); + return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); +} + +bool points_are_close(const Geom::Point& a, const Geom::Point& b, double epsilon) { + if (a == b) { + return false; + } + auto dist = points_distance(a, b); +#ifdef dbg + std::cout << "Comparing " << point_coords(a) << " with " << point_coords(b) << " - with a distance of " << dist << ".\n"; +#endif + return dist <= epsilon; +} +// +//Geom::BezierCurve* get_curve(const Geom::Curve &curve) { +// // FIXME do we have a memory leak here? +// return dynamic_cast(curve.duplicate()); +//} +// +//void align_paths(std::vector &paths, double epsilon) +//{ +// // FIXME very very slow function. +// std::vector visited_points; +// +// for (auto & path_vec : paths) { +// for (int path_idx = 0; path_idx < path_vec.size(); path_idx++) { +// Geom::Path &path = path_vec[path_idx]; +// for (int curve_idx = 0; curve_idx < path.size(); curve_idx++) { +// Geom::BezierCurve *new_curve = get_curve(path[curve_idx]); +// if (!new_curve) { +// continue; +// } +// auto points = new_curve->controlPoints(); +// for (int point_idx = 0; point_idx < points.size(); point_idx++) { +// auto &point = points[point_idx]; +// for (Geom::Point &visited_point : visited_points) { +// if (points_are_close(visited_point, point, epsilon)) { +// new_curve->setPoint(point_idx, visited_point); +// break; +// } +// } +// visited_points.push_back(point); +// } +// std::cout << "Reached 1\n"; +// // assuming the two points are not close, can be pushed. +// path.replace(path.begin() + path_idx, *new_curve); +// Geom::Curve *next_curve; +// if (curve_idx == path.size() - 1) { +// next_curve = get_curve(path[0]); +// } else { +// next_curve = get_curve(path[curve_idx + 1]); +// } +// next_curve->setInitial(new_curve->finalPoint()); +// if (curve_idx == path.size() - 1) { +// path.replace(path.begin(), *next_curve); +// } else { +// path.replace(path.begin() + curve_idx + 1, *next_curve); +// } +// +// std::cout << "Reached 2\n"; +// } +// assert(path.initialPoint() == path.finalPoint()); +// assert(path.initialCurve() == path.finalCurve()); +// std::cout << "Done path " << path_idx << ".\n"; +// } +// } +//} + +Geom::Point get_closest(const Geom::Point &point, const std::vector points) +{ + double least_dist = 100000000; + const Geom::Point *result = nullptr; + for (auto &dist : points) { + auto dist_val = points_distance(point, dist); + if (dist_val < least_dist) { + least_dist = dist_val; + result = &dist; + } + } + return *result; +} + +bool visit_point_not_close(const Geom::Point &point, std::vector &visited_points, double epsilon) +{ + if (!visited_points.empty()) { + for (auto &visited_point : visited_points) { + if (points_are_close(point, visited_point, epsilon)) { + return false; + } + } + } + visited_points.push_back(point); + return true; +} + +std::vector get_vec_pathvec_nodes(const std::vector &paths, double epsilon) +{ + std::vector result; + for (auto &path_vec : paths) { + for (auto &path : path_vec) { + auto nodes = path.nodes(); + for (auto &point : nodes) { + visit_point_not_close(point, result, epsilon); + } + } + } + return result; +} + +std::vector get_points_that_intersect_with_selection(const std::vector &points, Inkscape::Selection *selection, double epsilon) +{ + std::vector result; + for (auto &point : points) { + if (getAnItemThatIntersectsFromSelection(point, point, selection, epsilon)) { + result.push_back(point); + } + } + return result; +} + +void align_paths(std::vector &paths, const std::vector &points, double epsilon, Inkscape::Selection *selection) { - // FIXME crashes when applied on 4 nested rectangles and some other set of objects. + int processed_points = 0; + int replaced_points = 0; + for (auto &path_vec : paths) { + for (auto &path : path_vec) { + std::vector curves; + for (int curve_idx = 0; curve_idx < path.size(); curve_idx++) { + +// Geom::Curve *curve_ptr = path.at(curve_idx).duplicate(); +// auto &curve = *curve_ptr; + + + Geom::Curve *curve_ptr = path.at(curve_idx).duplicate(); + auto &curve = *curve_ptr; + auto initial = curve.initialPoint(); + auto closest = get_closest(initial, points); + curve.setInitial(closest); + // std::cout << "Replaced " << point_coords(initial) << " with " << point_coords(closest) << ".\n"; + if (curve_idx) { + curves[curve_idx-1]->setFinal(closest); + } + curves.push_back(curve_ptr); + + // auto &curve = path.at(curve_idx); + + // draw_on_canvas_and_copy_style_from_selection(Geom::Path(curve.boundsExact()), selection, true); + +// auto initial = curve.initialPoint(); +// if (!visit_point_not_close(initial, points, epsilon)) { +// +// std::cout << "Reached 1\n"; +// auto closest = get_closest(initial, points); +// curve.setInitial(closest); +// +// replaced_points++; +// std::cout << "Replaced " << point_coords(initial) << " with " << point_coords(closest) << ".\n"; +// +// std::cout << "Reached 2\n"; +// path.replace(path.begin() + curve_idx, curve); +// +//// int prev_idx = (curve_idx - 1 + path.size()) % path.size(); +//// auto prev_curve_ptr = path.at(prev_idx).duplicate(); +//// auto& prev_curve = *prev_curve_ptr; +//// prev_curve.setFinal(closest); +//// path.replace(path.begin() + prev_idx, prev_curve); +// +// } + + processed_points++; + +// auto final = curve.finalPoint(); +// if (!visit_point_not_close(final, points, epsilon)) { +// auto closest = get_closest(final, points); +// curve.setFinal(closest); +// std::cout << "Replaced " << point_coords(final) << " with " << point_coords(closest) << ".\n"; +// replaced_points++; +// } +// processed_points++; + + + } + + curves.back()->setFinal(curves[0]->initialPoint()); + + Geom::Path p; + + for (auto &curve : curves) { + //draw_on_canvas_and_copy_style_from_selection(Geom::Path(curve->boundsExact()), selection, true); + p.append(curve); + } + + path = p; + } + } + + int node_count = 0; + for (auto &path : paths) { + node_count += path.nodes().size(); + } + + std::cout << "Node count: " << node_count << ".\n"; + std::cout << "Processed " << processed_points << " points in " << __FUNCTION__ << ".\n"; + std::cout << "Replaced " << replaced_points << " points in " << __FUNCTION__ << ".\n"; +} + +void align_paths(std::vector &paths, double epsilon, Inkscape::Selection *selection) +{ + std::vector points = get_vec_pathvec_nodes(paths, epsilon); + align_paths(paths, points, epsilon, selection); +} + +void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill_gaps, bool is_destructive, bool put_originals_at_bottom) +{ + // TODO add an option for splitting the shape and the stroke. // FIXME The functions should be in a different place. // FIXME It should have its own widget. // FIXME this way doesn't seem to work nicely with round objects. @@ -1358,6 +1786,10 @@ void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill auto desktop = selection->desktop(); + // FIXME should the strokes be converted to paths or + // should they get removed or should nothing happen to them? + // selection->strokesToPaths(); + if(desktop){ // set "busy" cursor desktop->setWaitingCursor(); @@ -1370,19 +1802,21 @@ void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill Geom::PathVector bounds_expanded = get_bounds_path_vector(a, expansion_offset); auto result = sp_pathvector_cutboolop(a, bounds_expanded, fill_nonZero, fill_nonZero); + double epsilon = 5; // The choice here is arbitrary. change it if found a better number. + align_paths(result, epsilon, selection); + + std::cout << "Size of the cut object: " << result.size() - 1 << "\n\n"; // discard first path. for (int i = 1; i < result.size(); i++) { - draw_on_canvas(result[i], selection, fill_gaps); + draw_on_canvas_and_copy_style_from_selection(result[i], selection, true); +// draw_on_canvas_and_copy_style_from_selection(result[i], selection, fill_gaps); } std::cout << '\n'; if (is_destructive) { selection->deleteItems(); - } else { - // the constructed parts should be above it. - // FIXME is it right to make it at the bottom - // of everything, not only the constructed parts? + } else if (put_originals_at_bottom) { selection->lowerToBottom(true); } @@ -1392,6 +1826,23 @@ void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill } } +void change_shape_of_one_to_the_other(Inkscape::Selection *selection) +{ + auto objects = selection->objects(); + std::cout << objects.size() << '\n'; + if (objects.size() == 2) { + auto obj = objects.begin(); + std::vector a_list { *obj }; + obj++; + std::vector b_list { *obj }; + Geom::PathVector a = get_paths_from_object_list(a_list); + Geom::PathVector b = get_paths_from_object_list(b_list); + std::vector a_vec {a}; + align_paths(a_vec, get_path_points(b), 0.01, selection); + draw_on_canvas_and_copy_style_from_selection(a_vec.back(), selection, true); + } +} + /** * Decode the verb code and take appropriate action. */ @@ -1435,6 +1886,11 @@ void SelectionVerb::perform(SPAction *action, void *data) selection->pathCut(); break; case SP_VERB_SELECTION_SLICE: + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); + change_shape_of_one_to_the_other(selection); + break; + case 22332233: selection->removeLPESRecursive(true); selection->unlinkRecursive(true); selection->pathSlice(); @@ -1544,7 +2000,7 @@ void SelectionVerb::perform(SPAction *action, void *data) case SP_VERB_SELECTION_INSET: selection->removeLPESRecursive(true); selection->unlinkRecursive(true); - break_into_non_overlapping_pieces(selection, true, false); + break_into_non_overlapping_pieces(selection, false, true, true); break; case 23232323: selection->removeLPESRecursive(true); -- GitLab From 8c2278922e6cb59edeb7fa9747f6b832011d3a4c Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 25 May 2021 11:39:34 +0200 Subject: [PATCH 007/235] another junk code. --- src/verbs.cpp | 169 +++++++++++++++++++++++--------------------------- 1 file changed, 79 insertions(+), 90 deletions(-) diff --git a/src/verbs.cpp b/src/verbs.cpp index 431ac75e50..3a66524f5a 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -1230,9 +1231,11 @@ sp_pathvector_cutboolop(Geom::PathVector const &pathva, Geom::PathVector const & return result; } -std::string point_coords(const Geom::Point& point) +std::string point_coords(const Geom::Point& point, int precision = 10) { - return "(" + std::to_string(point.x()) + ", " + std::to_string(point.y()) + ")"; + std::ostringstream oss; + oss << std::fixed << std::setprecision(precision) << "(" << point.x() << ", " << point.y() << ")"; + return oss.str(); } Geom::PathVector get_path_data(SPObject* object) @@ -1261,9 +1264,13 @@ Geom::PathVector get_paths_from_object_list(const ObjectList& objects) for (auto object : objects) { Geom::PathVector path_data = get_path_data(object); +// std::cout << "Path data size: " << path_data.size() << '\n'; +// std::cout << "Path data nodes count: " << path_data.nodes().size() << '\n'; result.insert(result.end(), path_data.begin(), path_data.end()); } +// std::cout << "Result size: " << result.size() << '\n'; +// std::cout << "Result nodes count: " << result.nodes().size() << '\n'; return result; } @@ -1569,12 +1576,12 @@ double points_distance(const Geom::Point &a, const Geom::Point &b) { bool points_are_close(const Geom::Point& a, const Geom::Point& b, double epsilon) { if (a == b) { - return false; + return true; } auto dist = points_distance(a, b); -#ifdef dbg + #ifdef dbg std::cout << "Comparing " << point_coords(a) << " with " << point_coords(b) << " - with a distance of " << dist << ".\n"; -#endif + #endif return dist <= epsilon; } // @@ -1684,96 +1691,57 @@ std::vector get_points_that_intersect_with_selection(const std::vec return result; } -void align_paths(std::vector &paths, const std::vector &points, double epsilon, Inkscape::Selection *selection) +void move_paths_nodes_to_closest_points(Geom::PathVector &paths, const std::vector &points, double curve_epsilon = 0.1) { - int processed_points = 0; - int replaced_points = 0; - for (auto &path_vec : paths) { - for (auto &path : path_vec) { - std::vector curves; - for (int curve_idx = 0; curve_idx < path.size(); curve_idx++) { - -// Geom::Curve *curve_ptr = path.at(curve_idx).duplicate(); -// auto &curve = *curve_ptr; - - - Geom::Curve *curve_ptr = path.at(curve_idx).duplicate(); - auto &curve = *curve_ptr; - auto initial = curve.initialPoint(); - auto closest = get_closest(initial, points); - curve.setInitial(closest); - // std::cout << "Replaced " << point_coords(initial) << " with " << point_coords(closest) << ".\n"; - if (curve_idx) { - curves[curve_idx-1]->setFinal(closest); - } - curves.push_back(curve_ptr); - - // auto &curve = path.at(curve_idx); - - // draw_on_canvas_and_copy_style_from_selection(Geom::Path(curve.boundsExact()), selection, true); - -// auto initial = curve.initialPoint(); -// if (!visit_point_not_close(initial, points, epsilon)) { -// -// std::cout << "Reached 1\n"; -// auto closest = get_closest(initial, points); -// curve.setInitial(closest); -// -// replaced_points++; -// std::cout << "Replaced " << point_coords(initial) << " with " << point_coords(closest) << ".\n"; -// -// std::cout << "Reached 2\n"; -// path.replace(path.begin() + curve_idx, curve); -// -//// int prev_idx = (curve_idx - 1 + path.size()) % path.size(); -//// auto prev_curve_ptr = path.at(prev_idx).duplicate(); -//// auto& prev_curve = *prev_curve_ptr; -//// prev_curve.setFinal(closest); -//// path.replace(path.begin() + prev_idx, prev_curve); -// -// } + // not considering edge cases, efficiency, nothing. just want it to work for now. - processed_points++; + for (auto &path : paths) { -// auto final = curve.finalPoint(); -// if (!visit_point_not_close(final, points, epsilon)) { -// auto closest = get_closest(final, points); -// curve.setFinal(closest); -// std::cout << "Replaced " << point_coords(final) << " with " << point_coords(closest) << ".\n"; -// replaced_points++; -// } -// processed_points++; + std::vector curves; + for (int curve_idx = 0; curve_idx < path.size(); curve_idx++) { + // FIXME are we leaking memory here? should it be deleted after being used? + Geom::Curve *curve_ptr = path.at(curve_idx).duplicate(); + auto &curve = *curve_ptr; + Geom::Point initial = curve.initialPoint(); + Geom::Point closest = get_closest(initial, points); + curve.setInitial(closest); + if (curve_idx) { + curves[curve_idx-1]->setFinal(closest); } + curves.push_back(curve_ptr); + + } - curves.back()->setFinal(curves[0]->initialPoint()); + curves.back()->setFinal(curves[0]->initialPoint()); - Geom::Path p; + // FIXME ideally would love to change the curves in the path directly + // but that doesn't work... the program crashes saying that the path + // is not contiguous. This implies that this way is wrong anyways. fix it. - for (auto &curve : curves) { - //draw_on_canvas_and_copy_style_from_selection(Geom::Path(curve->boundsExact()), selection, true); + Geom::Path p; + for (auto &curve : curves) { + if (!points_are_close(curve->initialPoint(), curve->finalPoint(), curve_epsilon)) { p.append(curve); } - - path = p; } - } - int node_count = 0; - for (auto &path : paths) { - node_count += path.nodes().size(); + path = p; } +} - std::cout << "Node count: " << node_count << ".\n"; - std::cout << "Processed " << processed_points << " points in " << __FUNCTION__ << ".\n"; - std::cout << "Replaced " << replaced_points << " points in " << __FUNCTION__ << ".\n"; +void move_paths_nodes_to_closest_points(std::vector &paths, const std::vector &points) +{ + for (auto &path_vec : paths) { + move_paths_nodes_to_closest_points(path_vec, points); + } } -void align_paths(std::vector &paths, double epsilon, Inkscape::Selection *selection) +void move_paths_nodes_to_closest_points(std::vector &paths, double epsilon, Inkscape::Selection *selection) { std::vector points = get_vec_pathvec_nodes(paths, epsilon); - align_paths(paths, points, epsilon, selection); + move_paths_nodes_to_closest_points(paths, points); } void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill_gaps, bool is_destructive, bool put_originals_at_bottom) @@ -1802,10 +1770,10 @@ void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill Geom::PathVector bounds_expanded = get_bounds_path_vector(a, expansion_offset); auto result = sp_pathvector_cutboolop(a, bounds_expanded, fill_nonZero, fill_nonZero); - double epsilon = 5; // The choice here is arbitrary. change it if found a better number. - align_paths(result, epsilon, selection); + double epsilon = 1; // The choice here is arbitrary. change it if found a better number. + move_paths_nodes_to_closest_points(result, epsilon, selection); - std::cout << "Size of the cut object: " << result.size() - 1 << "\n\n"; +// std::cout << "Size of the cut object: " << result.size() - 1 << "\n\n"; // discard first path. for (int i = 1; i < result.size(); i++) { @@ -1829,18 +1797,39 @@ void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill void change_shape_of_one_to_the_other(Inkscape::Selection *selection) { auto objects = selection->objects(); - std::cout << objects.size() << '\n'; - if (objects.size() == 2) { - auto obj = objects.begin(); - std::vector a_list { *obj }; - obj++; - std::vector b_list { *obj }; - Geom::PathVector a = get_paths_from_object_list(a_list); - Geom::PathVector b = get_paths_from_object_list(b_list); - std::vector a_vec {a}; - align_paths(a_vec, get_path_points(b), 0.01, selection); - draw_on_canvas_and_copy_style_from_selection(a_vec.back(), selection, true); + int shape_num = 1; +// std::map map; + for (auto obj : objects) { + auto list = std::vector{ obj }; + auto pathvec = get_paths_from_object_list(list); + auto points = get_vec_pathvec_nodes({pathvec}, 0); + std::sort(points.begin(), points.end()); + std::cout << "Nodes of shape " << shape_num++ << " sorted : \n"; + for (auto point : points) { +// map[point]++; + std::cout << point_coords(point) << '\n'; + } + std::cout << "\n"; } +// std::cout << "\n\nPoints that are common between shapes:\n"; +// for (auto x : map) { +// if (x.second > 1) { +// std::cout << point_coords(x.first) << " (exists " << x.second << " times).\n"; +// } +// } + +// auto objects = selection->objects(); +// std::cout << objects.size() << '\n'; +// if (objects.size() == 2) { +// auto obj = objects.begin(); +// std::vector a_list { *obj }; obj++; +// std::vector b_list { *obj }; +// Geom::PathVector a = get_paths_from_object_list(a_list); +// Geom::PathVector b = get_paths_from_object_list(b_list); +// auto b_points = get_vec_pathvec_nodes({b}, 5); +// move_paths_nodes_to_closest_points(a, b_points); +// draw_on_canvas_and_copy_style_from_selection(a, selection, true); +// } } /** -- GitLab From 31a101fd57cd273062d668ba281143ecb8826099 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 26 May 2021 05:42:01 +0200 Subject: [PATCH 008/235] Some useful code, figured out a way to remove the noisy paths when unioning (by removing paths with small areas), yet this is not complete since "Union" scales the nodes up for some reason and adds an attribute "transform" with a value of "scale(xxx)". to be solved later. --- src/verbs.cpp | 372 +++++++++++++++++--------------------------------- 1 file changed, 124 insertions(+), 248 deletions(-) diff --git a/src/verbs.cpp b/src/verbs.cpp index 3a66524f5a..2347c460a3 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1254,6 +1254,15 @@ Geom::PathVector get_path_data(SPObject* object) result = path->curve()->get_pathvector(); } +// std::cout << "Just After converting:\n"; +// for (auto path : result) { +// for (auto node : path.nodes()) { +// std::cout << point_coords(node) << ' '; +// } +// std::cout << '\n'; +// } +// std::cout << "\n\n"; + return result; } @@ -1353,6 +1362,7 @@ void draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Node *par SPItem* getAnItemThatIntersectsFromSelection(const Geom::PathVector &p1, const Geom::PathVector &p2, Inkscape::Selection *selection, double epsilon = 0.01) { + // FIXME change this stupid way. for (SPItem* item : selection->items()) { #ifdef dbg std::cout << "\nlooking now at: "; @@ -1407,48 +1417,6 @@ std::vector get_path_points(const Path &path) return result; } -bool point_is_lower(const Geom::Point &a, const Geom::Point &b) { - return a.y() > b.y(); -} - -template -std::pair get_lowest_two_points_of_path(const Path &path) -{ - Geom::Point low, lowest; - auto points = get_path_points(path); - lowest = points.back(); - for (int i = 1; i < points.size(); i++) { - auto &point = points[i]; - if (point_is_lower(point, lowest)) { - low = lowest; - lowest = point; - } else if (point_is_lower(point, low)) { - low = point; - } - } - - // FIXME should check if 2 points exist or - // should expect the number of points to be at least 2? - return { lowest, low }; -} - -template -Geom::Point get_point_between_the_lowest_two_points_of_path(const Path &path) -{ - auto points = get_lowest_two_points_of_path(path); - double x1 = points.first.x(); - double x2 = points.second.x(); - double y1 = points.first.y(); - double y2 = points.second.y(); -#ifdef dbg - std::cout << "Lowest 2 points are:\n"; - std::cout << x1 << ", " << y1 << '\n'; - std::cout << x2 << ", " << y2 << "\n\n"; - std::cout << "The result: " << double(x1 + x2) / 2 << ", " << double(y1 + y2) / 2 << "\n\n"; -#endif - return Geom::Point { double(x1 + x2) / 2, double(y1 + y2) / 2 }; -} - template std::pair get_min_max_points(const Path &path) { @@ -1494,25 +1462,21 @@ void draw_on_canvas_and_copy_style_from_selection(const Path& path, Inkscape::Se auto parent = item->parent(); auto pos = item->position(); + // FIXME find a way to get a point inside the shape. // shouldn't use midpoint of the bounds for locating a shape since it might not be on the shape. // shouldn't use the bottom 2 points. not useful when having curves. // not useful if the bottom 2 points forms an edge in the shape. // shouldn't calculate intersection because the gaps intersects at the edges. Geom::Point center_point = path.boundsFast()->midpoint(); -// auto lowest_points = get_lowest_two_points_of_path(path); -// // auto point = get_point_between_the_lowest_two_points_of_path(path); -// -// double epsilon = 0.1; -// Geom::Point point(lowest_points.first.x(), lowest_points.first.y() - epsilon); auto extreme_points = get_min_max_points(path); auto p1 = extreme_points.first; auto p2 = extreme_points.second; auto original_item = getAnItemThatIntersectsFromSelection(p1, p2, selection); - +// #define dbg #ifdef dbg // TODO delete these debugging statements. - auto bounds = path.boundsFast(); + auto bounds = path.boundsExact(); auto x1 = bounds->left(); auto x2 = bounds->right(); auto y1 = bounds->bottom(); @@ -1579,170 +1543,8 @@ bool points_are_close(const Geom::Point& a, const Geom::Point& b, double epsilon return true; } auto dist = points_distance(a, b); - #ifdef dbg - std::cout << "Comparing " << point_coords(a) << " with " << point_coords(b) << " - with a distance of " << dist << ".\n"; - #endif return dist <= epsilon; } -// -//Geom::BezierCurve* get_curve(const Geom::Curve &curve) { -// // FIXME do we have a memory leak here? -// return dynamic_cast(curve.duplicate()); -//} -// -//void align_paths(std::vector &paths, double epsilon) -//{ -// // FIXME very very slow function. -// std::vector visited_points; -// -// for (auto & path_vec : paths) { -// for (int path_idx = 0; path_idx < path_vec.size(); path_idx++) { -// Geom::Path &path = path_vec[path_idx]; -// for (int curve_idx = 0; curve_idx < path.size(); curve_idx++) { -// Geom::BezierCurve *new_curve = get_curve(path[curve_idx]); -// if (!new_curve) { -// continue; -// } -// auto points = new_curve->controlPoints(); -// for (int point_idx = 0; point_idx < points.size(); point_idx++) { -// auto &point = points[point_idx]; -// for (Geom::Point &visited_point : visited_points) { -// if (points_are_close(visited_point, point, epsilon)) { -// new_curve->setPoint(point_idx, visited_point); -// break; -// } -// } -// visited_points.push_back(point); -// } -// std::cout << "Reached 1\n"; -// // assuming the two points are not close, can be pushed. -// path.replace(path.begin() + path_idx, *new_curve); -// Geom::Curve *next_curve; -// if (curve_idx == path.size() - 1) { -// next_curve = get_curve(path[0]); -// } else { -// next_curve = get_curve(path[curve_idx + 1]); -// } -// next_curve->setInitial(new_curve->finalPoint()); -// if (curve_idx == path.size() - 1) { -// path.replace(path.begin(), *next_curve); -// } else { -// path.replace(path.begin() + curve_idx + 1, *next_curve); -// } -// -// std::cout << "Reached 2\n"; -// } -// assert(path.initialPoint() == path.finalPoint()); -// assert(path.initialCurve() == path.finalCurve()); -// std::cout << "Done path " << path_idx << ".\n"; -// } -// } -//} - -Geom::Point get_closest(const Geom::Point &point, const std::vector points) -{ - double least_dist = 100000000; - const Geom::Point *result = nullptr; - for (auto &dist : points) { - auto dist_val = points_distance(point, dist); - if (dist_val < least_dist) { - least_dist = dist_val; - result = &dist; - } - } - return *result; -} - -bool visit_point_not_close(const Geom::Point &point, std::vector &visited_points, double epsilon) -{ - if (!visited_points.empty()) { - for (auto &visited_point : visited_points) { - if (points_are_close(point, visited_point, epsilon)) { - return false; - } - } - } - visited_points.push_back(point); - return true; -} - -std::vector get_vec_pathvec_nodes(const std::vector &paths, double epsilon) -{ - std::vector result; - for (auto &path_vec : paths) { - for (auto &path : path_vec) { - auto nodes = path.nodes(); - for (auto &point : nodes) { - visit_point_not_close(point, result, epsilon); - } - } - } - return result; -} - -std::vector get_points_that_intersect_with_selection(const std::vector &points, Inkscape::Selection *selection, double epsilon) -{ - std::vector result; - for (auto &point : points) { - if (getAnItemThatIntersectsFromSelection(point, point, selection, epsilon)) { - result.push_back(point); - } - } - return result; -} - -void move_paths_nodes_to_closest_points(Geom::PathVector &paths, const std::vector &points, double curve_epsilon = 0.1) -{ - // not considering edge cases, efficiency, nothing. just want it to work for now. - - for (auto &path : paths) { - - std::vector curves; - - for (int curve_idx = 0; curve_idx < path.size(); curve_idx++) { - - // FIXME are we leaking memory here? should it be deleted after being used? - Geom::Curve *curve_ptr = path.at(curve_idx).duplicate(); - auto &curve = *curve_ptr; - Geom::Point initial = curve.initialPoint(); - Geom::Point closest = get_closest(initial, points); - curve.setInitial(closest); - if (curve_idx) { - curves[curve_idx-1]->setFinal(closest); - } - curves.push_back(curve_ptr); - - } - - curves.back()->setFinal(curves[0]->initialPoint()); - - // FIXME ideally would love to change the curves in the path directly - // but that doesn't work... the program crashes saying that the path - // is not contiguous. This implies that this way is wrong anyways. fix it. - - Geom::Path p; - for (auto &curve : curves) { - if (!points_are_close(curve->initialPoint(), curve->finalPoint(), curve_epsilon)) { - p.append(curve); - } - } - - path = p; - } -} - -void move_paths_nodes_to_closest_points(std::vector &paths, const std::vector &points) -{ - for (auto &path_vec : paths) { - move_paths_nodes_to_closest_points(path_vec, points); - } -} - -void move_paths_nodes_to_closest_points(std::vector &paths, double epsilon, Inkscape::Selection *selection) -{ - std::vector points = get_vec_pathvec_nodes(paths, epsilon); - move_paths_nodes_to_closest_points(paths, points); -} void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill_gaps, bool is_destructive, bool put_originals_at_bottom) { @@ -1770,10 +1572,6 @@ void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill Geom::PathVector bounds_expanded = get_bounds_path_vector(a, expansion_offset); auto result = sp_pathvector_cutboolop(a, bounds_expanded, fill_nonZero, fill_nonZero); - double epsilon = 1; // The choice here is arbitrary. change it if found a better number. - move_paths_nodes_to_closest_points(result, epsilon, selection); - -// std::cout << "Size of the cut object: " << result.size() - 1 << "\n\n"; // discard first path. for (int i = 1; i < result.size(); i++) { @@ -1794,42 +1592,115 @@ void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill } } -void change_shape_of_one_to_the_other(Inkscape::Selection *selection) -{ - auto objects = selection->objects(); - int shape_num = 1; -// std::map map; - for (auto obj : objects) { - auto list = std::vector{ obj }; - auto pathvec = get_paths_from_object_list(list); - auto points = get_vec_pathvec_nodes({pathvec}, 0); - std::sort(points.begin(), points.end()); - std::cout << "Nodes of shape " << shape_num++ << " sorted : \n"; - for (auto point : points) { -// map[point]++; - std::cout << point_coords(point) << '\n'; +double path_area_non_self_intersecting(const std::vector &nodes) +{ + // This function doesn't take curves into consideration. + double area = 0; + int j = nodes.size() - 1; + for (int i = 1; i < nodes.size(); i++) { + auto x1 = nodes[i].x(); + auto x2 = nodes[j].x(); + auto y1 = nodes[i].y(); + auto y2 = nodes[j].y(); + area += (x1 + x2) * (y1 - y2); + j = i; + } + area /= 2; + return std::abs(area); +} + +Geom::PathVector remove_paths_with_small_area_from_pathvec(const Geom::PathVector &paths, double minimum_area) +{ + // FIXME instead of creating a new PathVector, may draw or discard here. + + // This function is used to remove the "noise" + // that has almost 0 area. it's not for removing + // any path with small areas. It assumes that the + // paths to remove are almost lines with no curves. + + Geom::PathVector result; + for (auto &path : paths) { + auto nodes = path.nodes(); + auto area = path_area_non_self_intersecting(nodes); + if (area >= minimum_area) { + result.push_back(path); +#ifdef dbg + std::cout << "Accepted "; + } else { + + std::cout << "Rejected "; +#endif + } +#ifdef dbg + std::cout << "a path with an area of " << area << ".\n"; + for (auto &node : nodes) { + std::cout << point_coords(node) << ' '; + } + std::cout << "\n\n"; +#endif + } +#ifdef dbg + std::cout << "\n\n"; +#endif + + return result; +} + +void print_coords_sorted_from_selection(Inkscape::Selection *selection) +{ + auto cmp = [](const Geom::Point& a, const Geom::Point &b) { + return a.x() > b.x() || (a.x() == b.x() && a.y() > b.y()); + }; + + auto items = selection->items(); + + int item_num = 1; + for (auto item : items) { + std::cout << "Item " << item_num++ << ":\n"; + auto paths = get_path_data(item); + auto nodes = paths.nodes(); + std::sort(nodes.begin(), nodes.end(), cmp); + for (auto node : nodes) { + std::cout << point_coords(node, 1) << ' '; } - std::cout << "\n"; + std::cout << "\n\n"; } -// std::cout << "\n\nPoints that are common between shapes:\n"; -// for (auto x : map) { -// if (x.second > 1) { -// std::cout << point_coords(x.first) << " (exists " << x.second << " times).\n"; + +} + +void draw_selection_without_small_area_paths(Inkscape::Selection *selection) +{ + auto items = selection->items(); + for (auto item : items) { + auto paths = get_path_data(item); + auto nodes = paths.nodes(); +// for (auto node : nodes) { +// std::cout << point_coords(node) << ' '; // } -// } +// std::cout << "\n\n"; + double minimum_area = 1; // FIXME make the minimum area dependent on the selection and not fixed. + paths = remove_paths_with_small_area_from_pathvec(paths, minimum_area); + draw_on_canvas_and_copy_style_from_selection(paths, selection, true); + } + selection->deleteItems(); +} -// auto objects = selection->objects(); -// std::cout << objects.size() << '\n'; -// if (objects.size() == 2) { -// auto obj = objects.begin(); -// std::vector a_list { *obj }; obj++; -// std::vector b_list { *obj }; -// Geom::PathVector a = get_paths_from_object_list(a_list); -// Geom::PathVector b = get_paths_from_object_list(b_list); -// auto b_points = get_vec_pathvec_nodes({b}, 5); -// move_paths_nodes_to_closest_points(a, b_points); -// draw_on_canvas_and_copy_style_from_selection(a, selection, true); -// } +void behavior_for_cut_path(Inkscape::Selection *selection) +{ + draw_selection_without_small_area_paths(selection); +} + +std::vector get_seleted_items_scale_applied(Inkscape::Selection *selection) +{ + std::vector result; + + auto items = selection->items(); + for (auto item : items) { + auto paths = get_path_data(item); + auto scale = item->getAttribute("transform"); + // to be continued... + } + return std::vector(); } /** @@ -1852,7 +1723,12 @@ void SelectionVerb::perform(SPAction *action, void *data) case SP_VERB_SELECTION_UNION: selection->removeLPESRecursive(true); selection->unlinkRecursive(true); + std::cout << "Before union:\n"; + print_coords_sorted_from_selection(selection); + std::cout << "----------------------------\n\n"; selection->pathUnion(); + std::cout << "After union:\n"; + print_coords_sorted_from_selection(selection); break; case SP_VERB_SELECTION_INTERSECT: selection->removeLPESRecursive(true); @@ -1877,7 +1753,7 @@ void SelectionVerb::perform(SPAction *action, void *data) case SP_VERB_SELECTION_SLICE: selection->removeLPESRecursive(true); selection->unlinkRecursive(true); - change_shape_of_one_to_the_other(selection); + behavior_for_cut_path(selection); break; case 22332233: selection->removeLPESRecursive(true); -- GitLab From 86f25b595501fee0fa63c0fed3b30e879d2c5034 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 27 May 2021 14:32:33 +0200 Subject: [PATCH 009/235] break into non-overlapping pieces with most of the functionality implemented. functions need to be moved. --- src/verbs.cpp | 387 +++++++++++++++++--------------------------------- 1 file changed, 133 insertions(+), 254 deletions(-) diff --git a/src/verbs.cpp b/src/verbs.cpp index 2347c460a3..649ac4fb45 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -63,6 +63,8 @@ #include "object/sp-namedview.h" #include "object/sp-object.h" #include "object/sp-path.h" +#include "object/sp-text.h" +#include "object/sp-polygon.h" #include "path-chemistry.h" #include "path/path-boolop.h" #include "path/path-offset.h" @@ -1238,48 +1240,40 @@ std::string point_coords(const Geom::Point& point, int precision = 10) return oss.str(); } -Geom::PathVector get_path_data(SPObject* object) +Geom::PathVector get_path_data(SPItem *item) { Geom::PathVector result; - if (SPGroup *group = dynamic_cast(object)) { + if (SPGroup *group = dynamic_cast(item)) { std::vector items = sp_item_group_item_list(group); for (SPItem *item : items) { Geom::PathVector sub_result = get_path_data(item); result.insert(result.end(), sub_result.begin(), sub_result.end()); } } - else if (SPShape *shape = dynamic_cast(object)) { + else if (SPShape *shape = dynamic_cast(item)) { result = shape->curve()->get_pathvector(); - } else if (SPPath *path = dynamic_cast(object)) { + } else if (SPPath *path = dynamic_cast(item)) { result = path->curve()->get_pathvector(); } -// std::cout << "Just After converting:\n"; -// for (auto path : result) { -// for (auto node : path.nodes()) { -// std::cout << point_coords(node) << ' '; -// } -// std::cout << '\n'; -// } -// std::cout << "\n\n"; + // TODO you might want to handle the case for texts? + // anyways, for now objects are converted to paths + // before passing it to this function. but may be + // a good idea to put a case for it anyways. return result; } -template -Geom::PathVector get_paths_from_object_list(const ObjectList& objects) +template +Geom::PathVector get_paths_from_item_list(const ItemList& items) { Geom::PathVector result; - for (auto object : objects) { - Geom::PathVector path_data = get_path_data(object); -// std::cout << "Path data size: " << path_data.size() << '\n'; -// std::cout << "Path data nodes count: " << path_data.nodes().size() << '\n'; + for (auto item : items) { + Geom::PathVector path_data = get_path_data(item); result.insert(result.end(), path_data.begin(), path_data.end()); } -// std::cout << "Result size: " << result.size() << '\n'; -// std::cout << "Result nodes count: " << result.nodes().size() << '\n'; return result; } @@ -1293,94 +1287,20 @@ Geom::PathVector get_circle_bounds_path(const Geom::Point ¢er, double radius return { Geom::Path(res_bounds) }; } -//SPItem* getAnItemAtPointFromSelection(Geom::Point& point, Inkscape::Selection *selection) -//{ -//// auto desktop = selection->desktop(); -//// return desktop->getItemAtPoint(center, false); -// -// // FIXME figure out how to use the above method. -// // this is not very accurate since it test whether -// // the point lies in the bounding "rectangle" of -// // the item no matter how it's shaped. also slow. -// -// const double epsilon = 0.01; -// Geom::PathVector current_point = Geom::PathVector(get_circle_bounds_path(point, epsilon)); -//// Geom::PathVector current_point { Geom::Path(point) }; -// -// // TODO delete these debugging statements. -// -// for (auto item : selection->items()) { -// std::cout << "looking now at: "; -// std::cout << "x1: " << item->visualBounds()->left() << ' '; -// std::cout << "x2: " << item->visualBounds()->right() << ' '; -// std::cout << "y1: " << item->visualBounds()->bottom() << ' '; -// std::cout << "y2: " << item->visualBounds()->top() << "\n"; -//// if (item->visualBounds().contains(point)) { -// Geom::PathVector paths = get_path_data(item); -// // Geom::PathVector intersection = sp_pathvector_boolop(paths, current_point, bool_op_inters, fill_nonZero, fill_nonZero); -// if (!paths.intersect(current_point, epsilon).empty()) { -// std::cout << "Last one is a match!\n\n"; -// return item; -// } -// } -// -// return nullptr; -//} - template // Geom::Path or Geom::PathVector -void draw_on_canvas_and_copy_style_from_selection(const Path& path, Inkscape::Selection *selection, bool fill_gaps); +XML::Node* draw_on_canvas_and_copy_style_from_selection(const Path& path, Inkscape::Selection *selection, bool fill_gaps); template // Geom::Path or Geom::PathVector -void draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Node *parent, int pos); - -//bool is_valid_intersection(const Geom::PathVector &intersection, const Geom::PathVector &original_paths, Inkscape::Selection *selection, bool draw) -//{ -// Geom::PathVector diff = sp_pathvector_boolop(original_paths, intersection, bool_op_diff, fill_nonZero, fill_nonZero); -// if (draw) { -// XML::Node *parent = selection->xmlNodes().front()->parent(); -// draw_on_canvas(diff, nullptr, parent, 0); -// } -// return diff != original_paths; -//} - -//SPItem* getAnItemThatIntersectsFromSelection(const Geom::PathVector &paths, Inkscape::Selection *selection, double epsilon = 0.01) -//{ -// for (SPItem* item : selection->items()) { -// auto item_paths = get_path_data(item); -// auto intersection = sp_pathvector_boolop(paths, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); -// if (!intersection.empty() && is_valid_intersection(intersection, paths)) { -//// auto x = paths.boundsFast()->midpoint().x(); -//// auto y = paths.boundsFast()->midpoint().y(); -//// if (x > 110 && x < 111 && y > 192 && y < 193) { -//// draw_on_canvas(intersection, selection, true); -//// } -// return item; -// } -// } -// return nullptr; -//} +XML::Node* draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Node *parent, int pos); SPItem* getAnItemThatIntersectsFromSelection(const Geom::PathVector &p1, const Geom::PathVector &p2, Inkscape::Selection *selection, double epsilon = 0.01) { // FIXME change this stupid way. for (SPItem* item : selection->items()) { -#ifdef dbg - std::cout << "\nlooking now at: "; - std::cout << "x1: " << item->visualBounds()->left() << ' '; - std::cout << "x2: " << item->visualBounds()->right() << ' '; - std::cout << "y1: " << item->visualBounds()->bottom() << ' '; - std::cout << "y2: " << item->visualBounds()->top() << "\n\n"; -#endif auto item_paths = get_path_data(item); auto inters1 = sp_pathvector_boolop(p1, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); auto inters2 = sp_pathvector_boolop(p2, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); -// auto x = paths.boundsFast()->midpoint().x(); -// auto y = paths.boundsFast()->midpoint().y(); -// bool draw = x > 110 && x < 111 && y > 192 && y < 193; if (!inters1.empty() && !inters2.empty()) { // && is_valid_intersection(intersection, paths, selection, draw)) { -// if (draw) { -// draw_on_canvas_and_copy_style_from_selection(intersection, selection, true); -// } return item; } } @@ -1404,23 +1324,10 @@ SPItem* getAnItemThatIntersectsFromSelection(const Geom::Point &p1, const Geom:: return getAnItemThatIntersectsFromSelection(r1, r2, selection, epsilon); } -template -std::vector get_path_points(const Path &path) -{ - auto result = path.nodes(); -#ifdef dbg - std::cout << "\nPoints:\n"; - for (auto &p : result) { - std::cout << "(" << p.x() << ", " << p.y() << ")\n"; - } -#endif - return result; -} - template std::pair get_min_max_points(const Path &path) { - auto points = get_path_points(path); + auto points = path.nodes(); Geom::Point min = points[0]; Geom::Point max = points[0]; for (int i = 1; i < points.size(); i++) { @@ -1435,7 +1342,7 @@ std::pair get_min_max_points(const Path &path) } template // Geom::Path or Geom::PathVector -void draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Node *parent, int pos) +XML::Node* draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Node *parent, int pos) { Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); repr->setAttribute("d", sp_svg_write_path(path)); @@ -1449,69 +1356,37 @@ void draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Node *par repr->setAttribute("d", str); parent->addChildAtPos(repr, pos); + + return repr; } template // Geom::Path or Geom::PathVector -void draw_on_canvas_and_copy_style_from_selection(const Path& path, Inkscape::Selection *selection, bool fill_gaps) +XML::Node* draw_on_canvas_and_copy_style_from_selection(const Path& path, Inkscape::Selection *selection, bool fill_gaps) { - // TODO later, query the exact color at the - // mid-pint and set the color of the constructed object to it. - - // FIXME draw elements correctly. figure out what parent to use. + // TODO make this in a better way. auto item = selection->xmlNodes().front(); auto parent = item->parent(); auto pos = item->position(); - // FIXME find a way to get a point inside the shape. - // shouldn't use midpoint of the bounds for locating a shape since it might not be on the shape. + // FIXME find a way to get a point inside the fill of the shape. + // shouldn't use midpoint of the bounds for locating a shape since + // it might not be on the shape. // shouldn't use the bottom 2 points. not useful when having curves. - // not useful if the bottom 2 points forms an edge in the shape. - // shouldn't calculate intersection because the gaps intersects at the edges. - Geom::Point center_point = path.boundsFast()->midpoint(); - + // not useful if the bottom 2 points forms an edge in the shape. + // shouldn't calculate intersection with a point on the edge because + // the gaps intersects at the edges. + // you should only use 1 point, a point inside the fill of the shape. auto extreme_points = get_min_max_points(path); auto p1 = extreme_points.first; auto p2 = extreme_points.second; auto original_item = getAnItemThatIntersectsFromSelection(p1, p2, selection); -// #define dbg -#ifdef dbg - // TODO delete these debugging statements. - auto bounds = path.boundsExact(); - auto x1 = bounds->left(); - auto x2 = bounds->right(); - auto y1 = bounds->bottom(); - auto y2 = bounds->top(); - std::cout << "Looking at an item with the bounds of (" << x1 << - ", " << y1 << ") and (" << x2 << ", " << y2 << "), a center point of (" << center_point.x() << ", " << center_point.y() << ").\n"; - - std::cout << "Points of search: (" << p1.x() << ", " << p1.y() << ") and (" << p2.x() << ", " << p2.y() << ").\n"; -#endif - if (!original_item) { -#ifdef dbg - std::cout << "No item found.\n\n"; -#endif if (!fill_gaps) { - return; + return nullptr; } - } else { -#ifdef dbg - std::cout << "Found an item with Dimensions: "; - std::cout << "x1: " << original_item->geometricBounds()->left() << ' '; - std::cout << "x2: " << original_item->geometricBounds()->right() << ' '; - std::cout << "y1: " << original_item->geometricBounds()->bottom() << ' '; - std::cout << "y2: " << original_item->geometricBounds()->top() << "\n\n"; -#endif } - draw_on_canvas(path, original_item, parent, pos); -} - -void construct_broken_apart(const Geom::PathVector& result_paths, Inkscape::Selection *selection, bool fill_gaps = true) -{ - for (Geom::Path path : result_paths) { - draw_on_canvas_and_copy_style_from_selection(path, selection, fill_gaps); - } + return draw_on_canvas(path, original_item, parent, pos); } Geom::PathVector get_bounds_path_vector(const Geom::OptRect &bounds, double offset) @@ -1546,57 +1421,120 @@ bool points_are_close(const Geom::Point& a, const Geom::Point& b, double epsilon return dist <= epsilon; } -void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill_gaps, bool is_destructive, bool put_originals_at_bottom) +void set_desktop_busy(SPDesktop *desktop) { - // TODO add an option for splitting the shape and the stroke. - // FIXME The functions should be in a different place. - // FIXME It should have its own widget. - // FIXME this way doesn't seem to work nicely with round objects. - // probably the more you're zoomed in the better the results? - - auto desktop = selection->desktop(); - - // FIXME should the strokes be converted to paths or - // should they get removed or should nothing happen to them? - // selection->strokesToPaths(); - - if(desktop){ + if(desktop) { // set "busy" cursor desktop->setWaitingCursor(); // disable redrawing during the break-apart operation for remarkable speedup for large paths desktop->getCanvas()->set_drawing_disabled(true); } +} + +void unset_desktop_busy(SPDesktop *desktop) +{ + if (desktop) { + desktop->getCanvas()->set_drawing_disabled(false); + desktop->clearWaitingCursor(); + } +} + +std::vector draw_and_get_non_overlapping_pieces(Inkscape::Selection *selection) +{ + // TODO this way doesn't seem to work nicely with round objects + // and produces a lot of unnecessary nodes. may improve it so that + // nodes are only at points of intersection and have curves. double expansion_offset = 5; // The choice here is arbitrary. change it if found a better number. - Geom::PathVector a = get_paths_from_object_list(selection->objects()); + Geom::PathVector a = get_paths_from_item_list(selection->items()); Geom::PathVector bounds_expanded = get_bounds_path_vector(a, expansion_offset); auto result = sp_pathvector_cutboolop(a, bounds_expanded, fill_nonZero, fill_nonZero); + std::vector xml_nodes; // discard first path. for (int i = 1; i < result.size(); i++) { - draw_on_canvas_and_copy_style_from_selection(result[i], selection, true); -// draw_on_canvas_and_copy_style_from_selection(result[i], selection, fill_gaps); + auto node = draw_on_canvas_and_copy_style_from_selection(result[i], selection, true); + xml_nodes.push_back(node); + } + + return xml_nodes; +} + +void the_temporary_fix_for_the_transform_bug(Inkscape::Selection *selection) +{ + // FIXME this is to get rid of the bug where + // a transform attribute is added after some + // operations like union. for some reason the + // attribute disappears when the result object + // is moved. + selection->move(0, 0); +} + +void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill_gaps = false, + bool is_destructive = true, bool put_originals_at_bottom = false, bool strokes_to_paths = false) +{ + // FIXME The functions should be in a different place. + // FIXME It should have its own widget. + + auto desktop = selection->desktop(); + + set_desktop_busy(desktop); + + // Ideally shouldn't be converting to paths? + selection->toCurves(); + the_temporary_fix_for_the_transform_bug(selection); + + if (strokes_to_paths) { + selection->strokesToPaths(); } - std::cout << '\n'; + auto xml_nodes = draw_and_get_non_overlapping_pieces(selection); + + // if destructive, no need to put at the bottom. if (is_destructive) { selection->deleteItems(); } else if (put_originals_at_bottom) { selection->lowerToBottom(true); } - if (desktop) { - desktop->getCanvas()->set_drawing_disabled(false); - desktop->clearWaitingCursor(); + // TODO probably there is a better way of doing this? + for (auto node : xml_nodes) { + selection->add(node); } + + unset_desktop_busy(desktop); } -double path_area_non_self_intersecting(const std::vector &nodes) +void print_coords_sorted_from_selection(Inkscape::Selection *selection) +{ + auto cmp = [](const Geom::Point& a, const Geom::Point &b) { + return a.x() > b.x() || (a.x() == b.x() && a.y() > b.y()); + }; + + auto items = selection->items(); + + int item_num = 1; + for (auto item : items) { + std::cout << "Item " << item_num++ << ":\n"; + auto paths = get_path_data(item); + auto nodes = paths.nodes(); + std::sort(nodes.begin(), nodes.end(), cmp); + for (auto node : nodes) { + std::cout << point_coords(node, 1) << ' '; + } + std::cout << "\n\n"; + } + +} + +double path_area_non_self_intersecting_no_curves(const Geom::Path &path) { // This function doesn't take curves into consideration. - double area = 0; + auto nodes = path.nodes(); int j = nodes.size() - 1; + + double area = 0; for (int i = 1; i < nodes.size(); i++) { auto x1 = nodes[i].x(); auto x2 = nodes[j].x(); @@ -1606,101 +1544,45 @@ double path_area_non_self_intersecting(const std::vector &nodes) j = i; } area /= 2; + return std::abs(area); } Geom::PathVector remove_paths_with_small_area_from_pathvec(const Geom::PathVector &paths, double minimum_area) { - // FIXME instead of creating a new PathVector, may draw or discard here. - - // This function is used to remove the "noise" - // that has almost 0 area. it's not for removing - // any path with small areas. It assumes that the - // paths to remove are almost lines with no curves. + // FIXME may want to edit this PathVector instead of creating a new one? + // FIXME take curves into consideration. Guess this will only work with + // the current way of breaking selection into non-overlapping pieces + // since it produces a paths with no curves. Other than that, this + // function shouldn't be used until fixed. Geom::PathVector result; for (auto &path : paths) { - auto nodes = path.nodes(); - auto area = path_area_non_self_intersecting(nodes); + auto area = path_area_non_self_intersecting_no_curves(path); if (area >= minimum_area) { result.push_back(path); -#ifdef dbg - std::cout << "Accepted "; - } else { - - std::cout << "Rejected "; -#endif } -#ifdef dbg - std::cout << "a path with an area of " << area << ".\n"; - for (auto &node : nodes) { - std::cout << point_coords(node) << ' '; - } - std::cout << "\n\n"; -#endif } -#ifdef dbg - std::cout << "\n\n"; -#endif return result; } -void print_coords_sorted_from_selection(Inkscape::Selection *selection) +void selection_remove_paths_with_small_are(Inkscape::Selection *selection) { - auto cmp = [](const Geom::Point& a, const Geom::Point &b) { - return a.x() > b.x() || (a.x() == b.x() && a.y() > b.y()); - }; - auto items = selection->items(); - - int item_num = 1; for (auto item : items) { - std::cout << "Item " << item_num++ << ":\n"; auto paths = get_path_data(item); auto nodes = paths.nodes(); - std::sort(nodes.begin(), nodes.end(), cmp); - for (auto node : nodes) { - std::cout << point_coords(node, 1) << ' '; - } - std::cout << "\n\n"; + // FIXME figure out whether to make the minimum area dependent on the selection or fixed. + double minimum_area = 1; + auto filtered_paths = remove_paths_with_small_area_from_pathvec(paths, minimum_area); + item->setAttribute("d", sp_svg_write_path(filtered_paths)); } - -} - -void draw_selection_without_small_area_paths(Inkscape::Selection *selection) -{ - auto items = selection->items(); - for (auto item : items) { - auto paths = get_path_data(item); - auto nodes = paths.nodes(); -// for (auto node : nodes) { -// std::cout << point_coords(node) << ' '; -// } -// std::cout << "\n\n"; - double minimum_area = 1; // FIXME make the minimum area dependent on the selection and not fixed. - paths = remove_paths_with_small_area_from_pathvec(paths, minimum_area); - draw_on_canvas_and_copy_style_from_selection(paths, selection, true); - } - selection->deleteItems(); } void behavior_for_cut_path(Inkscape::Selection *selection) { - draw_selection_without_small_area_paths(selection); -} - -std::vector get_seleted_items_scale_applied(Inkscape::Selection *selection) -{ - std::vector result; - - auto items = selection->items(); - for (auto item : items) { - auto paths = get_path_data(item); - auto scale = item->getAttribute("transform"); - // to be continued... - } - return std::vector(); + selection_remove_paths_with_small_are(selection); } /** @@ -1723,12 +1605,8 @@ void SelectionVerb::perform(SPAction *action, void *data) case SP_VERB_SELECTION_UNION: selection->removeLPESRecursive(true); selection->unlinkRecursive(true); - std::cout << "Before union:\n"; - print_coords_sorted_from_selection(selection); - std::cout << "----------------------------\n\n"; selection->pathUnion(); - std::cout << "After union:\n"; - print_coords_sorted_from_selection(selection); + the_temporary_fix_for_the_transform_bug(selection); break; case SP_VERB_SELECTION_INTERSECT: selection->removeLPESRecursive(true); @@ -1865,7 +1743,8 @@ void SelectionVerb::perform(SPAction *action, void *data) case SP_VERB_SELECTION_INSET: selection->removeLPESRecursive(true); selection->unlinkRecursive(true); - break_into_non_overlapping_pieces(selection, false, true, true); + // TODO make this a single history item. + break_into_non_overlapping_pieces(selection, false, true, true, false); break; case 23232323: selection->removeLPESRecursive(true); -- GitLab From ca340d81a34a245e2e78884b3b8d0a85b97cdfa3 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 27 May 2021 17:02:46 +0200 Subject: [PATCH 010/235] moved the code to places where I think it should be in (may change again). --- src/object/object-set.cpp | 352 ++++++++++++++++++++++++++++ src/object/object-set.h | 5 + src/path/path-boolop.cpp | 95 ++++++++ src/path/path-boolop.h | 1 + src/verbs.cpp | 480 +------------------------------------- 5 files changed, 456 insertions(+), 477 deletions(-) diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 43863956c8..5b3f397fbf 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -15,11 +15,19 @@ #include #include #include +#include +#include +#include #include +#include #include "box3d.h" +#include "display/curve.h" +#include "object/sp-path.h" +#include "object/sp-shape.h" #include "persp3d.h" #include "preferences.h" +#include "ui/widget/canvas.h" namespace Inkscape { @@ -436,6 +444,350 @@ void ObjectSet::_remove3DBoxesRecursively(SPObject *obj) { } } +std::string point_coords(const Geom::Point& point, int precision = 10) +{ + std::ostringstream oss; + oss << std::fixed << std::setprecision(precision) << "(" << point.x() << ", " << point.y() << ")"; + return oss.str(); +} + +Geom::PathVector get_path_data(SPItem *item) +{ + Geom::PathVector result; + if (SPGroup *group = dynamic_cast(item)) { + std::vector items = sp_item_group_item_list(group); + for (SPItem *item : items) { + Geom::PathVector sub_result = get_path_data(item); + result.insert(result.end(), sub_result.begin(), sub_result.end()); + } + } + else if (SPShape *shape = dynamic_cast(item)) { + result = shape->curve()->get_pathvector(); + } else if (SPPath *path = dynamic_cast(item)) { + result = path->curve()->get_pathvector(); + } + + // TODO you might want to handle the case for texts? + // anyways, for now objects are converted to paths + // before passing it to this function. but may be + // a good idea to put a case for it anyways. + + return result; +} + +template +Geom::PathVector get_paths_from_item_list(const ItemList& items) +{ + Geom::PathVector result; + + for (auto item : items) { + Geom::PathVector path_data = get_path_data(item); + result.insert(result.end(), path_data.begin(), path_data.end()); + } + + return result; +} + +Geom::PathVector get_circle_bounds_path(const Geom::Point ¢er, double radius) +{ + auto x1 = center.x() - radius; + auto x2 = center.x() + radius; + auto y1 = center.y() + radius; + auto y2 = center.y() - radius; + Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); + return { Geom::Path(res_bounds) }; +} + +template // Geom::Path or Geom::PathVector +XML::Node* draw_on_canvas_and_copy_style_from_set(const Path& path, ObjectSet *set, bool fill_gaps); + +template // Geom::Path or Geom::PathVector +XML::Node* draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Node *parent, int pos); + +SPItem* getAnItemThatIntersectsFromSet(const Geom::PathVector &p1, const Geom::PathVector &p2, ObjectSet *set, double epsilon = 0.01) +{ + // FIXME change this stupid way. + for (SPItem* item : set->items()) { + auto item_paths = get_path_data(item); + auto inters1 = sp_pathvector_boolop(p1, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); + auto inters2 = sp_pathvector_boolop(p2, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); + if (!inters1.empty() && !inters2.empty()) { // && is_valid_intersection(intersection, paths, set, draw)) { + return item; + } + } + return nullptr; +} + +SPItem* getAnItemThatIntersectsFromSet(const Geom::Path &p1, const Geom::Path &p2, ObjectSet *set, double epsilon = 0.01) +{ + return getAnItemThatIntersectsFromSet(Geom::PathVector(p1), Geom::PathVector(p2), set, epsilon); +} + +SPItem* getAnItemThatIntersectsFromSet(const Geom::Point &p1, const Geom::Point &p2, ObjectSet *set, double epsilon = 0.015) +{ + // FIXME fix it later. make sure that there is no problem in passing a path with only one point. + // it would be better if there was no epsilon. just is the point inside the object or not. +// Geom::Rect r1(p1, p1); +// Geom::Rect r2(p2, p2); +// return getAnItemThatIntersectsFromSet(Geom::Path(r1), Geom::Path(r2), set, epsilon); + auto r1 = get_circle_bounds_path(p1, epsilon); + auto r2 = get_circle_bounds_path(p2, epsilon); + return getAnItemThatIntersectsFromSet(r1, r2, set, epsilon); +} + +template +std::pair get_min_max_points(const Path &path) +{ + auto points = path.nodes(); + Geom::Point min = points[0]; + Geom::Point max = points[0]; + for (int i = 1; i < points.size(); i++) { + if (points[i].y() < max.y()) { + max = points[i]; + } + if (points[i].y() > min.y()) { + min = points[i]; + } + } + return {min, max}; +} + +template // Geom::Path or Geom::PathVector +XML::Node* draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Node *parent, int pos) +{ + Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); + repr->setAttribute("d", sp_svg_write_path(path)); + + if (to_copy_from) { + gchar *style = g_strdup(to_copy_from->getRepr()->attribute("style")); + repr->setAttribute("style", style); + } + + auto str = sp_svg_write_path(path); + repr->setAttribute("d", str); + + parent->addChildAtPos(repr, pos); + + return repr; +} + +template // Geom::Path or Geom::PathVector +XML::Node* draw_on_canvas_and_copy_style_from_set(const Path& path, ObjectSet *set, bool fill_gaps) +{ + // TODO make this in a better way. + auto item = set->xmlNodes().front(); + auto parent = item->parent(); + auto pos = item->position(); + + // FIXME find a way to get a point inside the fill of the shape. + // shouldn't use midpoint of the bounds for locating a shape since + // it might not be on the shape. + // shouldn't use the bottom 2 points. not useful when having curves. + // not useful if the bottom 2 points forms an edge in the shape. + // shouldn't calculate intersection with a point on the edge because + // the gaps intersects at the edges. + // you should only use 1 point, a point inside the fill of the shape. + auto extreme_points = get_min_max_points(path); + auto p1 = extreme_points.first; + auto p2 = extreme_points.second; + auto original_item = getAnItemThatIntersectsFromSet(p1, p2, set); + if (!original_item) { + if (!fill_gaps) { + return nullptr; + } + } + + return draw_on_canvas(path, original_item, parent, pos); +} + +Geom::PathVector get_bounds_path_vector(const Geom::OptRect &bounds, double offset) +{ + auto x1 = bounds->left() - offset; + auto x2 = bounds->right() + offset; + auto y1 = bounds->bottom() + offset; + auto y2 = bounds->top() - offset; + Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); + return { Geom::Path(res_bounds) }; +} + +Geom::PathVector get_bounds_path_vector(const Geom::PathVector &paths, double offset) +{ + // FIXME boundsFast or boundsExact? probably it's ok with boundsFast since + // most of the time an offset will be given (if not all?). + auto bounds = paths.boundsFast(); + return get_bounds_path_vector(bounds, offset); +} + +double points_distance(const Geom::Point &a, const Geom::Point &b) { + auto x1 = a.x(), x2 = b.x(); + auto y1 = a.y(), y2 = b.y(); + return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); +} + +bool points_are_close(const Geom::Point& a, const Geom::Point& b, double epsilon) { + if (a == b) { + return true; + } + auto dist = points_distance(a, b); + return dist <= epsilon; +} + +void set_desktop_busy(SPDesktop *desktop) +{ + if(desktop) { + // set "busy" cursor + desktop->setWaitingCursor(); + // disable redrawing during the break-apart operation for remarkable speedup for large paths + desktop->getCanvas()->set_drawing_disabled(true); + } +} + +void unset_desktop_busy(SPDesktop *desktop) +{ + if (desktop) { + desktop->getCanvas()->set_drawing_disabled(false); + desktop->clearWaitingCursor(); + } +} + +std::vector draw_and_get_non_overlapping_pieces(ObjectSet *set) +{ + // TODO this way doesn't seem to work nicely with round objects + // and produces a lot of unnecessary nodes. may improve it so that + // nodes are only at points of intersection and have curves. + + double expansion_offset = 5; // The choice here is arbitrary. change it if found a better number. + Geom::PathVector a = get_paths_from_item_list(set->items()); + Geom::PathVector bounds_expanded = get_bounds_path_vector(a, expansion_offset); + + auto result = sp_pathvector_cutboolop(a, bounds_expanded, fill_nonZero, fill_nonZero); + + std::vector xml_nodes; + // discard first path. + for (int i = 1; i < result.size(); i++) { + auto node = draw_on_canvas_and_copy_style_from_set(result[i], set, true); + xml_nodes.push_back(node); + } + + return xml_nodes; +} + +void ObjectSet::the_temporary_fix_for_the_transform_bug() +{ + // FIXME this is to get rid of the bug where + // a transform attribute is added after some + // operations like union. for some reason the + // attribute disappears when the result object + // is moved. + move(0, 0); +} + +void ObjectSet::break_into_non_overlapping_pieces(bool fill_gaps, bool is_destructive, + bool put_originals_at_bottom, bool strokes_to_paths) +{ + // FIXME The functions should be in a different place. + // FIXME It should have its own widget. + + set_desktop_busy(desktop()); + + // Ideally shouldn't be converting to paths? + toCurves(); + the_temporary_fix_for_the_transform_bug(); + + if (strokes_to_paths) { + strokesToPaths(); + } + + auto xml_nodes = draw_and_get_non_overlapping_pieces(this); + + // if destructive, no need to put at the bottom. + if (is_destructive) { + deleteItems(); + } else if (put_originals_at_bottom) { + lowerToBottom(true); + } + + // TODO probably there is a better way of doing this? + for (auto node : xml_nodes) { + add(node); + } + + unset_desktop_busy(desktop()); +} + +void print_coords_sorted_from_set(ObjectSet *set) +{ + auto cmp = [](const Geom::Point& a, const Geom::Point &b) { + return a.x() > b.x() || (a.x() == b.x() && a.y() > b.y()); + }; + + auto items = set->items(); + + int item_num = 1; + for (auto item : items) { + std::cout << "Item " << item_num++ << ":\n"; + auto paths = get_path_data(item); + auto nodes = paths.nodes(); + std::sort(nodes.begin(), nodes.end(), cmp); + for (auto node : nodes) { + std::cout << point_coords(node, 1) << ' '; + } + std::cout << "\n\n"; + } + +} + +double path_area_non_self_intersecting_no_curves(const Geom::Path &path) +{ + // This function doesn't take curves into consideration. + auto nodes = path.nodes(); + int j = nodes.size() - 1; + + double area = 0; + for (int i = 1; i < nodes.size(); i++) { + auto x1 = nodes[i].x(); + auto x2 = nodes[j].x(); + auto y1 = nodes[i].y(); + auto y2 = nodes[j].y(); + area += (x1 + x2) * (y1 - y2); + j = i; + } + area /= 2; + + return std::abs(area); +} + +Geom::PathVector remove_paths_with_small_area_from_pathvec(const Geom::PathVector &paths, double minimum_area) +{ + // FIXME may want to edit this PathVector instead of creating a new one? + // FIXME take curves into consideration. Guess this will only work with + // the current way of breaking selection into non-overlapping pieces + // since it produces a paths with no curves. Other than that, this + // function shouldn't be used until fixed. + + Geom::PathVector result; + for (auto &path : paths) { + auto area = path_area_non_self_intersecting_no_curves(path); + if (area >= minimum_area) { + result.push_back(path); + } + } + + return result; +} + +void ObjectSet::remove_paths_with_small_area() +{ + for (auto item : items()) { + auto paths = get_path_data(item); + auto nodes = paths.nodes(); + // FIXME figure out whether to make the minimum area dependent on the selection or fixed. + double minimum_area = 1; + auto filtered_paths = remove_paths_with_small_area_from_pathvec(paths, minimum_area); + item->setAttribute("d", sp_svg_write_path(filtered_paths)); + } +} + } // namespace Inkscape /* diff --git a/src/object/object-set.h b/src/object/object-set.h index f421e46dfc..c0c02fa93e 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -468,6 +468,11 @@ public: void swapFillStroke(); void fillBetweenMany(); + void remove_paths_with_small_area(); + void the_temporary_fix_for_the_transform_bug(); + void break_into_non_overlapping_pieces(bool fill_gaps = false, bool is_destructive = true, + bool put_originals_at_bottom = false, bool strokes_to_paths = false); + protected: virtual void _connectSignals(SPObject* object) {}; virtual void _releaseSignals(SPObject* object) {}; diff --git a/src/path/path-boolop.cpp b/src/path/path-boolop.cpp index 5464957d2c..830469746d 100644 --- a/src/path/path-boolop.cpp +++ b/src/path/path-boolop.cpp @@ -850,6 +850,101 @@ BoolOpErrors Inkscape::ObjectSet::pathBoolOp(bool_op bop, const bool skip_undo, return DONE; } +// cut operations PathVectors A,B -> PathVector result. +// This is derived from sp_pathvector_boolop +// take the source paths from the file, do the operation, delete the originals and add the results into a vector of +// cutted pieces +// fra,fra are fill_rules for PathVectors a,b +std::vector +sp_pathvector_cutboolop(Geom::PathVector const &pathva, Geom::PathVector const &pathvb, fill_typ fra, fill_typ frb) +{ + std::vector result; + // extract the livarot Paths from the source objects + // also get the winding rule specified in the style + int nbOriginaux = 2; + std::vector originaux(nbOriginaux); + std::vector origWind(nbOriginaux); + origWind[0]=fra; + origWind[1]=frb; + Geom::PathVector patht; + // Livarot's outline of arcs is broken. So convert the path to linear and cubics only, for which the outline is created correctly. + originaux[0] = Path_for_pathvector(pathv_to_linear_and_cubic_beziers( pathva)); + originaux[1] = Path_for_pathvector(pathv_to_linear_and_cubic_beziers( pathvb)); + + // some temporary instances, first + Shape *theShapeA = new Shape; + Shape *theShapeB = new Shape; + Shape *theShape = new Shape; + Path *res = new Path; + res->SetBackData(false); + + + // cuts= sort of a bastard boolean operation, thus not the axact same modus operandi + // technically, the cut path is not necessarily a polygon (thus has no winding rule) + // it is just uncrossed, and cleaned from duplicate edges and points + // then it's fed to Booleen() which will uncross it against the other path + // then comes the trick: each edge of the cut path is duplicated (one in each direction), + // thus making a polygon. the weight of the edges of the cut are all 0, but + // the Booleen need to invert the ones inside the source polygon (for the subsequent + // ConvertToForme) + + // the cut path needs to have the highest pathID in the back data + // that's how the Booleen() function knows it's an edge of the cut + { + Path* swap=originaux[0];originaux[0]=originaux[1];originaux[1]=swap; + int swai=origWind[0];origWind[0]=origWind[1];origWind[1]=(fill_typ)swai; + } + originaux[0]->ConvertWithBackData(get_threshold(pathva, 0.1)); + + originaux[0]->Fill(theShape, 0); + + theShapeA->ConvertToShape(theShape, origWind[0]); + + originaux[1]->ConvertWithBackData(get_threshold(pathvb, 0.1)); + + if ((originaux[1]->pts.size() == 2) && originaux[1]->pts[0].isMoveTo && !originaux[1]->pts[1].isMoveTo) + originaux[1]->Fill(theShape, 1,false,true,false); // see LP Bug 177956 + else + originaux[1]->Fill(theShape, 1,false,false,false); //do not closeIfNeeded + + theShapeB->ConvertToShape(theShape, fill_justDont); // fill_justDont doesn't computes winding numbers + + // les elements arrivent en ordre inverse dans la liste + theShape->Booleen(theShapeB, theShapeA, bool_op_cut, 1); + + + int* nesting=nullptr; + int* conts=nullptr; + int nbNest=0; + int nbRP=0; + Path** resPath; + theShape->ConvertToFormeNested(res, nbOriginaux, &originaux[0], 1, nbNest, nesting, conts); + delete theShape; + delete theShapeA; + delete theShapeB; + // cut operation is a bit wicked: you need to keep holes + // that's why you needed the nesting + // ConvertToFormeNested() dumped all the subpath in a single Path "res", so we need + // to get the path for each part of the polygon. that's why you need the nesting info: + // to know in which subpath to add a subpath + resPath=res->SubPathsWithNesting(nbRP, true, nbNest, nesting, conts); + + // cleaning + if ( conts ) free(conts); + if ( nesting ) free(nesting); + + delete originaux[0]; + delete originaux[1]; + for (int i=0;isvg_dump_path(); + Geom::PathVector outres = Geom::parse_svg_path(result_str); + result.push_back(outres); + g_free(result_str); + } + delete res; + return result; +} + /* Local Variables: mode:c++ diff --git a/src/path/path-boolop.h b/src/path/path-boolop.h index 59c53f7925..2e9b5bfbad 100644 --- a/src/path/path-boolop.h +++ b/src/path/path-boolop.h @@ -15,6 +15,7 @@ #include "object/object-set.h" // bool_op Geom::PathVector sp_pathvector_boolop(Geom::PathVector const &pathva, Geom::PathVector const &pathvb, bool_op bop, FillRule fra, FillRule frb); +std::vector sp_pathvector_cutboolop(Geom::PathVector const &pathva, Geom::PathVector const &pathvb, fill_typ fra, fill_typ frb); #endif // PATH_BOOLOP_H diff --git a/src/verbs.cpp b/src/verbs.cpp index 649ac4fb45..1c71cc1302 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1110,480 +1110,6 @@ void EditVerb::perform(SPAction *action, void *data) } // end of sp_verb_action_edit_perform() -double get_threshold(Geom::PathVector const &path, double threshold) -{ - auto maybe_box = path.boundsFast(); - if (!maybe_box) - return threshold; - Geom::Rect box = *maybe_box; - double diagonal = Geom::distance( - Geom::Point(box[Geom::X].min(), box[Geom::Y].min()), - Geom::Point(box[Geom::X].max(), box[Geom::Y].max()) - ); - return threshold * (diagonal / 100); -} - -// derived from Path_for_item -Path * -Path_for_pathvector(Geom::PathVector const &epathv) -{ - /*std::cout << "converting to Livarot path" << std::endl; - - Geom::SVGPathWriter wr; - wr.feed(epathv); - std::cout << wr.str() << std::endl;*/ - - Path *dest = new Path; - dest->LoadPathVector(epathv); - return dest; -} - -// cut operations PathVectors A,B -> PathVector result. -// This is derived from sp_pathvector_boolop -// take the source paths from the file, do the operation, delete the originals and add the results into a vector of -// cutted pieces -// fra,fra are fill_rules for PathVectors a,b -std::vector -sp_pathvector_cutboolop(Geom::PathVector const &pathva, Geom::PathVector const &pathvb, fill_typ fra, fill_typ frb) -{ - std::vector result; - // extract the livarot Paths from the source objects - // also get the winding rule specified in the style - int nbOriginaux = 2; - std::vector originaux(nbOriginaux); - std::vector origWind(nbOriginaux); - origWind[0]=fra; - origWind[1]=frb; - Geom::PathVector patht; - // Livarot's outline of arcs is broken. So convert the path to linear and cubics only, for which the outline is created correctly. - originaux[0] = Path_for_pathvector(pathv_to_linear_and_cubic_beziers( pathva)); - originaux[1] = Path_for_pathvector(pathv_to_linear_and_cubic_beziers( pathvb)); - - // some temporary instances, first - Shape *theShapeA = new Shape; - Shape *theShapeB = new Shape; - Shape *theShape = new Shape; - Path *res = new Path; - res->SetBackData(false); - - - // cuts= sort of a bastard boolean operation, thus not the axact same modus operandi - // technically, the cut path is not necessarily a polygon (thus has no winding rule) - // it is just uncrossed, and cleaned from duplicate edges and points - // then it's fed to Booleen() which will uncross it against the other path - // then comes the trick: each edge of the cut path is duplicated (one in each direction), - // thus making a polygon. the weight of the edges of the cut are all 0, but - // the Booleen need to invert the ones inside the source polygon (for the subsequent - // ConvertToForme) - - // the cut path needs to have the highest pathID in the back data - // that's how the Booleen() function knows it's an edge of the cut - { - Path* swap=originaux[0];originaux[0]=originaux[1];originaux[1]=swap; - int swai=origWind[0];origWind[0]=origWind[1];origWind[1]=(fill_typ)swai; - } - originaux[0]->ConvertWithBackData(get_threshold(pathva, 0.1)); - - originaux[0]->Fill(theShape, 0); - - theShapeA->ConvertToShape(theShape, origWind[0]); - - originaux[1]->ConvertWithBackData(get_threshold(pathvb, 0.1)); - - if ((originaux[1]->pts.size() == 2) && originaux[1]->pts[0].isMoveTo && !originaux[1]->pts[1].isMoveTo) - originaux[1]->Fill(theShape, 1,false,true,false); // see LP Bug 177956 - else - originaux[1]->Fill(theShape, 1,false,false,false); //do not closeIfNeeded - - theShapeB->ConvertToShape(theShape, fill_justDont); // fill_justDont doesn't computes winding numbers - - // les elements arrivent en ordre inverse dans la liste - theShape->Booleen(theShapeB, theShapeA, bool_op_cut, 1); - - - int* nesting=nullptr; - int* conts=nullptr; - int nbNest=0; - int nbRP=0; - Path** resPath; - theShape->ConvertToFormeNested(res, nbOriginaux, &originaux[0], 1, nbNest, nesting, conts); - delete theShape; - delete theShapeA; - delete theShapeB; - // cut operation is a bit wicked: you need to keep holes - // that's why you needed the nesting - // ConvertToFormeNested() dumped all the subpath in a single Path "res", so we need - // to get the path for each part of the polygon. that's why you need the nesting info: - // to know in which subpath to add a subpath - resPath=res->SubPathsWithNesting(nbRP, true, nbNest, nesting, conts); - - // cleaning - if ( conts ) free(conts); - if ( nesting ) free(nesting); - - delete originaux[0]; - delete originaux[1]; - for (int i=0;isvg_dump_path(); - Geom::PathVector outres = Geom::parse_svg_path(result_str); - result.push_back(outres); - g_free(result_str); - } - delete res; - return result; -} - -std::string point_coords(const Geom::Point& point, int precision = 10) -{ - std::ostringstream oss; - oss << std::fixed << std::setprecision(precision) << "(" << point.x() << ", " << point.y() << ")"; - return oss.str(); -} - -Geom::PathVector get_path_data(SPItem *item) -{ - Geom::PathVector result; - if (SPGroup *group = dynamic_cast(item)) { - std::vector items = sp_item_group_item_list(group); - for (SPItem *item : items) { - Geom::PathVector sub_result = get_path_data(item); - result.insert(result.end(), sub_result.begin(), sub_result.end()); - } - } - else if (SPShape *shape = dynamic_cast(item)) { - result = shape->curve()->get_pathvector(); - } else if (SPPath *path = dynamic_cast(item)) { - result = path->curve()->get_pathvector(); - } - - // TODO you might want to handle the case for texts? - // anyways, for now objects are converted to paths - // before passing it to this function. but may be - // a good idea to put a case for it anyways. - - return result; -} - -template -Geom::PathVector get_paths_from_item_list(const ItemList& items) -{ - Geom::PathVector result; - - for (auto item : items) { - Geom::PathVector path_data = get_path_data(item); - result.insert(result.end(), path_data.begin(), path_data.end()); - } - - return result; -} - -Geom::PathVector get_circle_bounds_path(const Geom::Point ¢er, double radius) -{ - auto x1 = center.x() - radius; - auto x2 = center.x() + radius; - auto y1 = center.y() + radius; - auto y2 = center.y() - radius; - Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); - return { Geom::Path(res_bounds) }; -} - -template // Geom::Path or Geom::PathVector -XML::Node* draw_on_canvas_and_copy_style_from_selection(const Path& path, Inkscape::Selection *selection, bool fill_gaps); - -template // Geom::Path or Geom::PathVector -XML::Node* draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Node *parent, int pos); - -SPItem* getAnItemThatIntersectsFromSelection(const Geom::PathVector &p1, const Geom::PathVector &p2, Inkscape::Selection *selection, double epsilon = 0.01) -{ - // FIXME change this stupid way. - for (SPItem* item : selection->items()) { - auto item_paths = get_path_data(item); - auto inters1 = sp_pathvector_boolop(p1, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); - auto inters2 = sp_pathvector_boolop(p2, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); - if (!inters1.empty() && !inters2.empty()) { // && is_valid_intersection(intersection, paths, selection, draw)) { - return item; - } - } - return nullptr; -} - -SPItem* getAnItemThatIntersectsFromSelection(const Geom::Path &p1, const Geom::Path &p2, Inkscape::Selection *selection, double epsilon = 0.01) -{ - return getAnItemThatIntersectsFromSelection(Geom::PathVector(p1), Geom::PathVector(p2), selection, epsilon); -} - -SPItem* getAnItemThatIntersectsFromSelection(const Geom::Point &p1, const Geom::Point &p2, Inkscape::Selection *selection, double epsilon = 0.015) -{ - // FIXME fix it later. make sure that there is no problem in passing a path with only one point. - // it would be better if there was no epsilon. just is the point inside the object or not. -// Geom::Rect r1(p1, p1); -// Geom::Rect r2(p2, p2); -// return getAnItemThatIntersectsFromSelection(Geom::Path(r1), Geom::Path(r2), selection, epsilon); - auto r1 = get_circle_bounds_path(p1, epsilon); - auto r2 = get_circle_bounds_path(p2, epsilon); - return getAnItemThatIntersectsFromSelection(r1, r2, selection, epsilon); -} - -template -std::pair get_min_max_points(const Path &path) -{ - auto points = path.nodes(); - Geom::Point min = points[0]; - Geom::Point max = points[0]; - for (int i = 1; i < points.size(); i++) { - if (points[i].y() < max.y()) { - max = points[i]; - } - if (points[i].y() > min.y()) { - min = points[i]; - } - } - return {min, max}; -} - -template // Geom::Path or Geom::PathVector -XML::Node* draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Node *parent, int pos) -{ - Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); - repr->setAttribute("d", sp_svg_write_path(path)); - - if (to_copy_from) { - gchar *style = g_strdup(to_copy_from->getRepr()->attribute("style")); - repr->setAttribute("style", style); - } - - auto str = sp_svg_write_path(path); - repr->setAttribute("d", str); - - parent->addChildAtPos(repr, pos); - - return repr; -} - -template // Geom::Path or Geom::PathVector -XML::Node* draw_on_canvas_and_copy_style_from_selection(const Path& path, Inkscape::Selection *selection, bool fill_gaps) -{ - // TODO make this in a better way. - auto item = selection->xmlNodes().front(); - auto parent = item->parent(); - auto pos = item->position(); - - // FIXME find a way to get a point inside the fill of the shape. - // shouldn't use midpoint of the bounds for locating a shape since - // it might not be on the shape. - // shouldn't use the bottom 2 points. not useful when having curves. - // not useful if the bottom 2 points forms an edge in the shape. - // shouldn't calculate intersection with a point on the edge because - // the gaps intersects at the edges. - // you should only use 1 point, a point inside the fill of the shape. - auto extreme_points = get_min_max_points(path); - auto p1 = extreme_points.first; - auto p2 = extreme_points.second; - auto original_item = getAnItemThatIntersectsFromSelection(p1, p2, selection); - if (!original_item) { - if (!fill_gaps) { - return nullptr; - } - } - - return draw_on_canvas(path, original_item, parent, pos); -} - -Geom::PathVector get_bounds_path_vector(const Geom::OptRect &bounds, double offset) -{ - auto x1 = bounds->left() - offset; - auto x2 = bounds->right() + offset; - auto y1 = bounds->bottom() + offset; - auto y2 = bounds->top() - offset; - Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); - return { Geom::Path(res_bounds) }; -} - -Geom::PathVector get_bounds_path_vector(const Geom::PathVector &paths, double offset) -{ - // FIXME boundsFast or boundsExact? probably it's ok with boundsFast since - // most of the time an offset will be given (if not all?). - auto bounds = paths.boundsFast(); - return get_bounds_path_vector(bounds, offset); -} - -double points_distance(const Geom::Point &a, const Geom::Point &b) { - auto x1 = a.x(), x2 = b.x(); - auto y1 = a.y(), y2 = b.y(); - return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); -} - -bool points_are_close(const Geom::Point& a, const Geom::Point& b, double epsilon) { - if (a == b) { - return true; - } - auto dist = points_distance(a, b); - return dist <= epsilon; -} - -void set_desktop_busy(SPDesktop *desktop) -{ - if(desktop) { - // set "busy" cursor - desktop->setWaitingCursor(); - // disable redrawing during the break-apart operation for remarkable speedup for large paths - desktop->getCanvas()->set_drawing_disabled(true); - } -} - -void unset_desktop_busy(SPDesktop *desktop) -{ - if (desktop) { - desktop->getCanvas()->set_drawing_disabled(false); - desktop->clearWaitingCursor(); - } -} - -std::vector draw_and_get_non_overlapping_pieces(Inkscape::Selection *selection) -{ - // TODO this way doesn't seem to work nicely with round objects - // and produces a lot of unnecessary nodes. may improve it so that - // nodes are only at points of intersection and have curves. - - double expansion_offset = 5; // The choice here is arbitrary. change it if found a better number. - Geom::PathVector a = get_paths_from_item_list(selection->items()); - Geom::PathVector bounds_expanded = get_bounds_path_vector(a, expansion_offset); - - auto result = sp_pathvector_cutboolop(a, bounds_expanded, fill_nonZero, fill_nonZero); - - std::vector xml_nodes; - // discard first path. - for (int i = 1; i < result.size(); i++) { - auto node = draw_on_canvas_and_copy_style_from_selection(result[i], selection, true); - xml_nodes.push_back(node); - } - - return xml_nodes; -} - -void the_temporary_fix_for_the_transform_bug(Inkscape::Selection *selection) -{ - // FIXME this is to get rid of the bug where - // a transform attribute is added after some - // operations like union. for some reason the - // attribute disappears when the result object - // is moved. - selection->move(0, 0); -} - -void break_into_non_overlapping_pieces(Inkscape::Selection *selection, bool fill_gaps = false, - bool is_destructive = true, bool put_originals_at_bottom = false, bool strokes_to_paths = false) -{ - // FIXME The functions should be in a different place. - // FIXME It should have its own widget. - - auto desktop = selection->desktop(); - - set_desktop_busy(desktop); - - // Ideally shouldn't be converting to paths? - selection->toCurves(); - the_temporary_fix_for_the_transform_bug(selection); - - if (strokes_to_paths) { - selection->strokesToPaths(); - } - - auto xml_nodes = draw_and_get_non_overlapping_pieces(selection); - - // if destructive, no need to put at the bottom. - if (is_destructive) { - selection->deleteItems(); - } else if (put_originals_at_bottom) { - selection->lowerToBottom(true); - } - - // TODO probably there is a better way of doing this? - for (auto node : xml_nodes) { - selection->add(node); - } - - unset_desktop_busy(desktop); -} - -void print_coords_sorted_from_selection(Inkscape::Selection *selection) -{ - auto cmp = [](const Geom::Point& a, const Geom::Point &b) { - return a.x() > b.x() || (a.x() == b.x() && a.y() > b.y()); - }; - - auto items = selection->items(); - - int item_num = 1; - for (auto item : items) { - std::cout << "Item " << item_num++ << ":\n"; - auto paths = get_path_data(item); - auto nodes = paths.nodes(); - std::sort(nodes.begin(), nodes.end(), cmp); - for (auto node : nodes) { - std::cout << point_coords(node, 1) << ' '; - } - std::cout << "\n\n"; - } - -} - -double path_area_non_self_intersecting_no_curves(const Geom::Path &path) -{ - // This function doesn't take curves into consideration. - auto nodes = path.nodes(); - int j = nodes.size() - 1; - - double area = 0; - for (int i = 1; i < nodes.size(); i++) { - auto x1 = nodes[i].x(); - auto x2 = nodes[j].x(); - auto y1 = nodes[i].y(); - auto y2 = nodes[j].y(); - area += (x1 + x2) * (y1 - y2); - j = i; - } - area /= 2; - - return std::abs(area); -} - -Geom::PathVector remove_paths_with_small_area_from_pathvec(const Geom::PathVector &paths, double minimum_area) -{ - // FIXME may want to edit this PathVector instead of creating a new one? - // FIXME take curves into consideration. Guess this will only work with - // the current way of breaking selection into non-overlapping pieces - // since it produces a paths with no curves. Other than that, this - // function shouldn't be used until fixed. - - Geom::PathVector result; - for (auto &path : paths) { - auto area = path_area_non_self_intersecting_no_curves(path); - if (area >= minimum_area) { - result.push_back(path); - } - } - - return result; -} - -void selection_remove_paths_with_small_are(Inkscape::Selection *selection) -{ - auto items = selection->items(); - for (auto item : items) { - auto paths = get_path_data(item); - auto nodes = paths.nodes(); - // FIXME figure out whether to make the minimum area dependent on the selection or fixed. - double minimum_area = 1; - auto filtered_paths = remove_paths_with_small_area_from_pathvec(paths, minimum_area); - item->setAttribute("d", sp_svg_write_path(filtered_paths)); - } -} - -void behavior_for_cut_path(Inkscape::Selection *selection) -{ - selection_remove_paths_with_small_are(selection); -} /** * Decode the verb code and take appropriate action. @@ -1606,7 +1132,7 @@ void SelectionVerb::perform(SPAction *action, void *data) selection->removeLPESRecursive(true); selection->unlinkRecursive(true); selection->pathUnion(); - the_temporary_fix_for_the_transform_bug(selection); + selection->the_temporary_fix_for_the_transform_bug(); break; case SP_VERB_SELECTION_INTERSECT: selection->removeLPESRecursive(true); @@ -1631,7 +1157,7 @@ void SelectionVerb::perform(SPAction *action, void *data) case SP_VERB_SELECTION_SLICE: selection->removeLPESRecursive(true); selection->unlinkRecursive(true); - behavior_for_cut_path(selection); + selection->remove_paths_with_small_area(); break; case 22332233: selection->removeLPESRecursive(true); @@ -1744,7 +1270,7 @@ void SelectionVerb::perform(SPAction *action, void *data) selection->removeLPESRecursive(true); selection->unlinkRecursive(true); // TODO make this a single history item. - break_into_non_overlapping_pieces(selection, false, true, true, false); + selection->break_into_non_overlapping_pieces(false, true, true, false); break; case 23232323: selection->removeLPESRecursive(true); -- GitLab From 4fb18096525884cff901adb6f6593bbde75a09ee Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 30 May 2021 12:51:48 +0200 Subject: [PATCH 011/235] first usable version of breaking into non-overlapping pieces. still need a lot of cleaning and optimization. --- src/object/object-set.cpp | 212 +++++++++++++++++++++++++++-------- src/object/object-set.h | 1 + src/ui/tools/select-tool.cpp | 6 + src/verbs.cpp | 3 +- 4 files changed, 174 insertions(+), 48 deletions(-) diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 5b3f397fbf..41d0d5c794 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -21,6 +21,10 @@ #include #include +#include "helper/geom-pathstroke.h" +#include "document.h" +#include "xml/repr.h" + #include "box3d.h" #include "display/curve.h" #include "object/sp-path.h" @@ -475,6 +479,21 @@ Geom::PathVector get_path_data(SPItem *item) return result; } +std::vector get_items_sorted_from_set_from_bottom_to_top(ObjectSet *set) +{ + auto _items = set->items(); + std::vector items(_items.begin(), _items.end()); + std::sort(items.begin(), items.end(), sp_item_repr_compare_position_bool); + return items; +} + +std::vector get_items_sorted_from_set_from_top_to_bottom(ObjectSet *set) +{ + auto items = get_items_sorted_from_set_from_bottom_to_top(set); + std::reverse(items.begin(), items.end()); + return items; +} + template Geom::PathVector get_paths_from_item_list(const ItemList& items) { @@ -499,58 +518,80 @@ Geom::PathVector get_circle_bounds_path(const Geom::Point ¢er, double radius } template // Geom::Path or Geom::PathVector -XML::Node* draw_on_canvas_and_copy_style_from_set(const Path& path, ObjectSet *set, bool fill_gaps); +XML::Node* draw_on_canvas_and_copy_style_from_set(const Geom::Path& path, ObjectSet *set, bool fill_holes); template // Geom::Path or Geom::PathVector -XML::Node* draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Node *parent, int pos); +XML::Node* draw_on_canvas(const Geom::Path& path, const SPItem *to_copy_from, XML::Node *parent, int pos); -SPItem* getAnItemThatIntersectsFromSet(const Geom::PathVector &p1, const Geom::PathVector &p2, ObjectSet *set, double epsilon = 0.01) +SPItem* getAnItemThatIntersectsFromSet(const Geom::PathVector &path, ObjectSet *set) { // FIXME change this stupid way. - for (SPItem* item : set->items()) { + // TODO may want to start with the item on the top first or should it be dependent on the order of selection? + auto items = get_items_sorted_from_set_from_top_to_bottom(set); + for (SPItem* item : items) { auto item_paths = get_path_data(item); - auto inters1 = sp_pathvector_boolop(p1, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); - auto inters2 = sp_pathvector_boolop(p2, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); - if (!inters1.empty() && !inters2.empty()) { // && is_valid_intersection(intersection, paths, set, draw)) { + auto inters = sp_pathvector_boolop(path, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); + if (!inters.empty()) { return item; } } return nullptr; } -SPItem* getAnItemThatIntersectsFromSet(const Geom::Path &p1, const Geom::Path &p2, ObjectSet *set, double epsilon = 0.01) +SPItem* getAnItemThatIntersectsFromSet(const Geom::Path &p1, ObjectSet *set, double epsilon) { - return getAnItemThatIntersectsFromSet(Geom::PathVector(p1), Geom::PathVector(p2), set, epsilon); + return getAnItemThatIntersectsFromSet(Geom::PathVector(p1), set); } -SPItem* getAnItemThatIntersectsFromSet(const Geom::Point &p1, const Geom::Point &p2, ObjectSet *set, double epsilon = 0.015) +SPItem* getAnItemThatIntersectsFromSet(const Geom::Point &point, ObjectSet *set, double epsilon) { // FIXME fix it later. make sure that there is no problem in passing a path with only one point. // it would be better if there was no epsilon. just is the point inside the object or not. -// Geom::Rect r1(p1, p1); -// Geom::Rect r2(p2, p2); -// return getAnItemThatIntersectsFromSet(Geom::Path(r1), Geom::Path(r2), set, epsilon); - auto r1 = get_circle_bounds_path(p1, epsilon); - auto r2 = get_circle_bounds_path(p2, epsilon); - return getAnItemThatIntersectsFromSet(r1, r2, set, epsilon); +// Geom::Rect rect(point, point); +// return getAnItemThatIntersectsFromSet(Geom::Path(rect), set, epsilon); + auto r = get_circle_bounds_path(point, epsilon); + return getAnItemThatIntersectsFromSet(r, set); } -template -std::pair get_min_max_points(const Path &path) -{ - auto points = path.nodes(); - Geom::Point min = points[0]; - Geom::Point max = points[0]; - for (int i = 1; i < points.size(); i++) { - if (points[i].y() < max.y()) { - max = points[i]; - } - if (points[i].y() > min.y()) { - min = points[i]; +template +void draw_sp_items(const SPItemList &items, ObjectSet *set) { + for (auto item : items) { + auto paths = get_path_data(item); + draw_on_canvas_and_copy_style_from_set(paths, set, true); + } +} + +SPItem* getAnItemThatIntersectsFromSet(const Geom::Point &point, ObjectSet *set) +{ + auto document = set->document(); + auto dkey = set->desktop()->dkey; + std::vector result_items = document->getItemsAtPoints(dkey, {point}); + std::cout << result_items.size() << '\n'; + for (auto item1 : result_items) { + for (auto item2 : set->items()) { + if (item1 == item2) { + return item1; + } } } - return {min, max}; + return nullptr; } +// +//SPItem* getAnItemThatIntersectsFromSet(const Geom::PathVector &paths, ObjectSet *set) +//{ +// auto document = set->document(); +// auto dkey = set->desktop()->dkey; +// std::vector result_items = document->getItemsAtPoints(dkey, paths.nodes()); +// std::cout << result_items.size() << '\n'; +// for (auto item1 : result_items) { +// for (auto item2 : set->items()) { +// if (item1 == item2) { +// return item1; +// } +// } +// } +// return nullptr; +//} template // Geom::Path or Geom::PathVector XML::Node* draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Node *parent, int pos) @@ -571,30 +612,79 @@ XML::Node* draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Nod return repr; } -template // Geom::Path or Geom::PathVector -XML::Node* draw_on_canvas_and_copy_style_from_set(const Path& path, ObjectSet *set, bool fill_gaps) +Geom::Path get_smallest_path(const Geom::PathVector& paths) +{ + double smallest_area = 1000000000; + const Geom::Path* result = nullptr; + for (auto &path : paths) { + double area = path.boundsFast()->area(); + if (area < smallest_area) { + result = &path; + smallest_area = area; + } + } + assert(result != nullptr); + return *result; +} + +Geom::Path get_biggest_path(const Geom::PathVector& paths) +{ + double biggest_area = 0; + const Geom::Path* result = nullptr; + for (auto &path : paths) { + double area = path.boundsFast()->area(); + if (area > biggest_area) { + result = &path; + biggest_area = area; + } + } + assert(result != nullptr); + return *result; +} + +Geom::PathVector get_outline(const Geom::PathVector& paths, double width = 1) +{ + // FIXME should the width/miter be fixed? + double miter = 1; // TODO figure out what miter is. + Geom::Path path = get_biggest_path(paths); + return outline(path, width, miter); +} + +Geom::Path get_path_inside_fill(const Geom::PathVector &paths) +{ + double width = 0.5; // FIXME chose the value wisely. + auto outline = get_outline(paths, width); + return get_smallest_path(outline); +} + +Geom::Point get_point_inside_fill(const Geom::PathVector &paths) +{ + // FIXME should we actually return any point? + return get_path_inside_fill(paths).initialPoint(); +} + +template +XML::Node* draw_on_canvas_and_copy_style_from_set(const Path& path, ObjectSet *set, bool fill_holes) { // TODO make this in a better way. auto item = set->xmlNodes().front(); auto parent = item->parent(); auto pos = item->position(); - // FIXME find a way to get a point inside the fill of the shape. + // FIXME find the best way to get a point inside the fill of the shape. // shouldn't use midpoint of the bounds for locating a shape since // it might not be on the shape. // shouldn't use the bottom 2 points. not useful when having curves. // not useful if the bottom 2 points forms an edge in the shape. // shouldn't calculate intersection with a point on the edge because - // the gaps intersects at the edges. + // the holes intersects at the edges. // you should only use 1 point, a point inside the fill of the shape. - auto extreme_points = get_min_max_points(path); - auto p1 = extreme_points.first; - auto p2 = extreme_points.second; - auto original_item = getAnItemThatIntersectsFromSet(p1, p2, set); - if (!original_item) { - if (!fill_gaps) { - return nullptr; - } + Geom::Point point = get_point_inside_fill(path); + double epsilon = 0.1; // FIXME choose the value wisely. + SPItem* original_item = getAnItemThatIntersectsFromSet(point, set, epsilon); +// SPItem* original_item = getAnItemThatIntersectsFromSet(point, set); + if (!original_item && !fill_holes) { + return nullptr; } return draw_on_canvas(path, original_item, parent, pos); @@ -650,13 +740,14 @@ void unset_desktop_busy(SPDesktop *desktop) } } -std::vector draw_and_get_non_overlapping_pieces(ObjectSet *set) +std::vector draw_and_get_non_overlapping_pieces(ObjectSet *set, bool fill_holes) { // TODO this way doesn't seem to work nicely with round objects // and produces a lot of unnecessary nodes. may improve it so that // nodes are only at points of intersection and have curves. - double expansion_offset = 5; // The choice here is arbitrary. change it if found a better number. + // FIXME make sure of the choice of the number here. + double expansion_offset = 1; // The choice here is arbitrary. change it if found a better number. Geom::PathVector a = get_paths_from_item_list(set->items()); Geom::PathVector bounds_expanded = get_bounds_path_vector(a, expansion_offset); @@ -665,7 +756,7 @@ std::vector draw_and_get_non_overlapping_pieces(ObjectSet *set) std::vector xml_nodes; // discard first path. for (int i = 1; i < result.size(); i++) { - auto node = draw_on_canvas_and_copy_style_from_set(result[i], set, true); + auto node = draw_on_canvas_and_copy_style_from_set(result[i], set, fill_holes); xml_nodes.push_back(node); } @@ -682,11 +773,17 @@ void ObjectSet::the_temporary_fix_for_the_transform_bug() move(0, 0); } -void ObjectSet::break_into_non_overlapping_pieces(bool fill_gaps, bool is_destructive, +void ObjectSet::break_into_non_overlapping_pieces(bool fill_holes, bool is_destructive, bool put_originals_at_bottom, bool strokes_to_paths) { - // FIXME The functions should be in a different place. + // FIXME The functions may be in a different place. // FIXME It should have its own widget. + // FIXME get rid of all fixed numbers so that it works + // with shapes of any size. + // TODO (for swipe-union thing) may want to copy the style of + // the first object in the selection instead of letting it be + // random (prabably the it copies the style of the biggest + // object). may also let the user decide. set_desktop_busy(desktop()); @@ -698,7 +795,7 @@ void ObjectSet::break_into_non_overlapping_pieces(bool fill_gaps, bool is_destru strokesToPaths(); } - auto xml_nodes = draw_and_get_non_overlapping_pieces(this); + auto xml_nodes = draw_and_get_non_overlapping_pieces(this, fill_holes); // if destructive, no need to put at the bottom. if (is_destructive) { @@ -788,6 +885,27 @@ void ObjectSet::remove_paths_with_small_area() } } +void draw_outline(const Geom::PathVector &paths, ObjectSet *set) +{ + auto outline = get_outline(paths); + auto path = get_smallest_path(outline); + draw_on_canvas_and_copy_style_from_set(path, set, true); +} + +void ObjectSet::additional_method() +{ +// for (auto item : items()) { +// draw_outline(get_path_data(item), this); +// } + for (auto item : items()) { + auto paths = get_path_data(item); + auto point = get_point_inside_fill(paths); + std::vector result_items = document()->getItemsAtPoints(desktop()->dkey, {point}); + std::cout << result_items.size() << '\n'; + draw_sp_items(result_items, this); + } +} + } // namespace Inkscape /* diff --git a/src/object/object-set.h b/src/object/object-set.h index c0c02fa93e..7d2050a9c0 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -472,6 +472,7 @@ public: void the_temporary_fix_for_the_transform_bug(); void break_into_non_overlapping_pieces(bool fill_gaps = false, bool is_destructive = true, bool put_originals_at_bottom = false, bool strokes_to_paths = false); + void additional_method(); protected: virtual void _connectSignals(SPObject* object) {}; diff --git a/src/ui/tools/select-tool.cpp b/src/ui/tools/select-tool.cpp index 92fb23c4e2..4455e7cd3c 100644 --- a/src/ui/tools/select-tool.cpp +++ b/src/ui/tools/select-tool.cpp @@ -719,6 +719,12 @@ bool SelectTool::root_handler(GdkEvent* event) { } else { // without shift, simply select anew selection->setList (items); + if(Modifier::get(Modifiers::Type::SELECT_TOUCH_PATH)->active(event->button.state)) { + selection->pathUnion(); + selection->the_temporary_fix_for_the_transform_bug(); + selection->remove_paths_with_small_area(); + selection->clear(); + } } } else { // it was just a click, or a too small rubberband diff --git a/src/verbs.cpp b/src/verbs.cpp index 1c71cc1302..aefe3005de 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1157,7 +1157,8 @@ void SelectionVerb::perform(SPAction *action, void *data) case SP_VERB_SELECTION_SLICE: selection->removeLPESRecursive(true); selection->unlinkRecursive(true); - selection->remove_paths_with_small_area(); + // selection->remove_paths_with_small_area(); + selection->additional_method(); break; case 22332233: selection->removeLPESRecursive(true); -- GitLab From 36efcc194b190b5a2f949271746a719001169a6e Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 30 May 2021 14:34:51 +0200 Subject: [PATCH 012/235] extracted code for breaking into non-overlapping pieces into a class called 'NonOverlappingPathsBuilder'. --- src/object/object-set.cpp | 602 ++++++++++++++++++-------------------- src/object/object-set.h | 2 +- 2 files changed, 289 insertions(+), 315 deletions(-) diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 41d0d5c794..5aadfd0afa 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -479,80 +479,6 @@ Geom::PathVector get_path_data(SPItem *item) return result; } -std::vector get_items_sorted_from_set_from_bottom_to_top(ObjectSet *set) -{ - auto _items = set->items(); - std::vector items(_items.begin(), _items.end()); - std::sort(items.begin(), items.end(), sp_item_repr_compare_position_bool); - return items; -} - -std::vector get_items_sorted_from_set_from_top_to_bottom(ObjectSet *set) -{ - auto items = get_items_sorted_from_set_from_bottom_to_top(set); - std::reverse(items.begin(), items.end()); - return items; -} - -template -Geom::PathVector get_paths_from_item_list(const ItemList& items) -{ - Geom::PathVector result; - - for (auto item : items) { - Geom::PathVector path_data = get_path_data(item); - result.insert(result.end(), path_data.begin(), path_data.end()); - } - - return result; -} - -Geom::PathVector get_circle_bounds_path(const Geom::Point ¢er, double radius) -{ - auto x1 = center.x() - radius; - auto x2 = center.x() + radius; - auto y1 = center.y() + radius; - auto y2 = center.y() - radius; - Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); - return { Geom::Path(res_bounds) }; -} - -template // Geom::Path or Geom::PathVector -XML::Node* draw_on_canvas_and_copy_style_from_set(const Geom::Path& path, ObjectSet *set, bool fill_holes); - -template // Geom::Path or Geom::PathVector -XML::Node* draw_on_canvas(const Geom::Path& path, const SPItem *to_copy_from, XML::Node *parent, int pos); - -SPItem* getAnItemThatIntersectsFromSet(const Geom::PathVector &path, ObjectSet *set) -{ - // FIXME change this stupid way. - // TODO may want to start with the item on the top first or should it be dependent on the order of selection? - auto items = get_items_sorted_from_set_from_top_to_bottom(set); - for (SPItem* item : items) { - auto item_paths = get_path_data(item); - auto inters = sp_pathvector_boolop(path, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); - if (!inters.empty()) { - return item; - } - } - return nullptr; -} - -SPItem* getAnItemThatIntersectsFromSet(const Geom::Path &p1, ObjectSet *set, double epsilon) -{ - return getAnItemThatIntersectsFromSet(Geom::PathVector(p1), set); -} - -SPItem* getAnItemThatIntersectsFromSet(const Geom::Point &point, ObjectSet *set, double epsilon) -{ - // FIXME fix it later. make sure that there is no problem in passing a path with only one point. - // it would be better if there was no epsilon. just is the point inside the object or not. -// Geom::Rect rect(point, point); -// return getAnItemThatIntersectsFromSet(Geom::Path(rect), set, epsilon); - auto r = get_circle_bounds_path(point, epsilon); - return getAnItemThatIntersectsFromSet(r, set); -} - template void draw_sp_items(const SPItemList &items, ObjectSet *set) { for (auto item : items) { @@ -576,192 +502,6 @@ SPItem* getAnItemThatIntersectsFromSet(const Geom::Point &point, ObjectSet *set) } return nullptr; } -// -//SPItem* getAnItemThatIntersectsFromSet(const Geom::PathVector &paths, ObjectSet *set) -//{ -// auto document = set->document(); -// auto dkey = set->desktop()->dkey; -// std::vector result_items = document->getItemsAtPoints(dkey, paths.nodes()); -// std::cout << result_items.size() << '\n'; -// for (auto item1 : result_items) { -// for (auto item2 : set->items()) { -// if (item1 == item2) { -// return item1; -// } -// } -// } -// return nullptr; -//} - -template // Geom::Path or Geom::PathVector -XML::Node* draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Node *parent, int pos) -{ - Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); - repr->setAttribute("d", sp_svg_write_path(path)); - - if (to_copy_from) { - gchar *style = g_strdup(to_copy_from->getRepr()->attribute("style")); - repr->setAttribute("style", style); - } - - auto str = sp_svg_write_path(path); - repr->setAttribute("d", str); - - parent->addChildAtPos(repr, pos); - - return repr; -} - -Geom::Path get_smallest_path(const Geom::PathVector& paths) -{ - double smallest_area = 1000000000; - const Geom::Path* result = nullptr; - for (auto &path : paths) { - double area = path.boundsFast()->area(); - if (area < smallest_area) { - result = &path; - smallest_area = area; - } - } - assert(result != nullptr); - return *result; -} - -Geom::Path get_biggest_path(const Geom::PathVector& paths) -{ - double biggest_area = 0; - const Geom::Path* result = nullptr; - for (auto &path : paths) { - double area = path.boundsFast()->area(); - if (area > biggest_area) { - result = &path; - biggest_area = area; - } - } - assert(result != nullptr); - return *result; -} - -Geom::PathVector get_outline(const Geom::PathVector& paths, double width = 1) -{ - // FIXME should the width/miter be fixed? - double miter = 1; // TODO figure out what miter is. - Geom::Path path = get_biggest_path(paths); - return outline(path, width, miter); -} - -Geom::Path get_path_inside_fill(const Geom::PathVector &paths) -{ - double width = 0.5; // FIXME chose the value wisely. - auto outline = get_outline(paths, width); - return get_smallest_path(outline); -} - -Geom::Point get_point_inside_fill(const Geom::PathVector &paths) -{ - // FIXME should we actually return any point? - return get_path_inside_fill(paths).initialPoint(); -} - -template -XML::Node* draw_on_canvas_and_copy_style_from_set(const Path& path, ObjectSet *set, bool fill_holes) -{ - // TODO make this in a better way. - auto item = set->xmlNodes().front(); - auto parent = item->parent(); - auto pos = item->position(); - - // FIXME find the best way to get a point inside the fill of the shape. - // shouldn't use midpoint of the bounds for locating a shape since - // it might not be on the shape. - // shouldn't use the bottom 2 points. not useful when having curves. - // not useful if the bottom 2 points forms an edge in the shape. - // shouldn't calculate intersection with a point on the edge because - // the holes intersects at the edges. - // you should only use 1 point, a point inside the fill of the shape. - Geom::Point point = get_point_inside_fill(path); - double epsilon = 0.1; // FIXME choose the value wisely. - SPItem* original_item = getAnItemThatIntersectsFromSet(point, set, epsilon); -// SPItem* original_item = getAnItemThatIntersectsFromSet(point, set); - if (!original_item && !fill_holes) { - return nullptr; - } - - return draw_on_canvas(path, original_item, parent, pos); -} - -Geom::PathVector get_bounds_path_vector(const Geom::OptRect &bounds, double offset) -{ - auto x1 = bounds->left() - offset; - auto x2 = bounds->right() + offset; - auto y1 = bounds->bottom() + offset; - auto y2 = bounds->top() - offset; - Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); - return { Geom::Path(res_bounds) }; -} - -Geom::PathVector get_bounds_path_vector(const Geom::PathVector &paths, double offset) -{ - // FIXME boundsFast or boundsExact? probably it's ok with boundsFast since - // most of the time an offset will be given (if not all?). - auto bounds = paths.boundsFast(); - return get_bounds_path_vector(bounds, offset); -} - -double points_distance(const Geom::Point &a, const Geom::Point &b) { - auto x1 = a.x(), x2 = b.x(); - auto y1 = a.y(), y2 = b.y(); - return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); -} - -bool points_are_close(const Geom::Point& a, const Geom::Point& b, double epsilon) { - if (a == b) { - return true; - } - auto dist = points_distance(a, b); - return dist <= epsilon; -} - -void set_desktop_busy(SPDesktop *desktop) -{ - if(desktop) { - // set "busy" cursor - desktop->setWaitingCursor(); - // disable redrawing during the break-apart operation for remarkable speedup for large paths - desktop->getCanvas()->set_drawing_disabled(true); - } -} - -void unset_desktop_busy(SPDesktop *desktop) -{ - if (desktop) { - desktop->getCanvas()->set_drawing_disabled(false); - desktop->clearWaitingCursor(); - } -} - -std::vector draw_and_get_non_overlapping_pieces(ObjectSet *set, bool fill_holes) -{ - // TODO this way doesn't seem to work nicely with round objects - // and produces a lot of unnecessary nodes. may improve it so that - // nodes are only at points of intersection and have curves. - - // FIXME make sure of the choice of the number here. - double expansion_offset = 1; // The choice here is arbitrary. change it if found a better number. - Geom::PathVector a = get_paths_from_item_list(set->items()); - Geom::PathVector bounds_expanded = get_bounds_path_vector(a, expansion_offset); - - auto result = sp_pathvector_cutboolop(a, bounds_expanded, fill_nonZero, fill_nonZero); - - std::vector xml_nodes; - // discard first path. - for (int i = 1; i < result.size(); i++) { - auto node = draw_on_canvas_and_copy_style_from_set(result[i], set, fill_holes); - xml_nodes.push_back(node); - } - - return xml_nodes; -} void ObjectSet::the_temporary_fix_for_the_transform_bug() { @@ -773,45 +513,6 @@ void ObjectSet::the_temporary_fix_for_the_transform_bug() move(0, 0); } -void ObjectSet::break_into_non_overlapping_pieces(bool fill_holes, bool is_destructive, - bool put_originals_at_bottom, bool strokes_to_paths) -{ - // FIXME The functions may be in a different place. - // FIXME It should have its own widget. - // FIXME get rid of all fixed numbers so that it works - // with shapes of any size. - // TODO (for swipe-union thing) may want to copy the style of - // the first object in the selection instead of letting it be - // random (prabably the it copies the style of the biggest - // object). may also let the user decide. - - set_desktop_busy(desktop()); - - // Ideally shouldn't be converting to paths? - toCurves(); - the_temporary_fix_for_the_transform_bug(); - - if (strokes_to_paths) { - strokesToPaths(); - } - - auto xml_nodes = draw_and_get_non_overlapping_pieces(this, fill_holes); - - // if destructive, no need to put at the bottom. - if (is_destructive) { - deleteItems(); - } else if (put_originals_at_bottom) { - lowerToBottom(true); - } - - // TODO probably there is a better way of doing this? - for (auto node : xml_nodes) { - add(node); - } - - unset_desktop_busy(desktop()); -} - void print_coords_sorted_from_set(ObjectSet *set) { auto cmp = [](const Geom::Point& a, const Geom::Point &b) { @@ -877,7 +578,6 @@ void ObjectSet::remove_paths_with_small_area() { for (auto item : items()) { auto paths = get_path_data(item); - auto nodes = paths.nodes(); // FIXME figure out whether to make the minimum area dependent on the selection or fixed. double minimum_area = 1; auto filtered_paths = remove_paths_with_small_area_from_pathvec(paths, minimum_area); @@ -885,25 +585,299 @@ void ObjectSet::remove_paths_with_small_area() } } -void draw_outline(const Geom::PathVector &paths, ObjectSet *set) +void ObjectSet::additional_method() { - auto outline = get_outline(paths); - auto path = get_smallest_path(outline); - draw_on_canvas_and_copy_style_from_set(path, set, true); + // called when clicked on "Cut Path" + remove_paths_with_small_area(); } -void ObjectSet::additional_method() +class NonOverlappingPathsBuilder { -// for (auto item : items()) { -// draw_outline(get_path_data(item), this); -// } - for (auto item : items()) { - auto paths = get_path_data(item); - auto point = get_point_inside_fill(paths); - std::vector result_items = document()->getItemsAtPoints(desktop()->dkey, {point}); - std::cout << result_items.size() << '\n'; - draw_sp_items(result_items, this); + double point_tolerance = 0.1; + double outline_width = 1; + double outline_miter = 1; // TODO figure out what miter is. + double expansion_offset = 1; + bool fill_holes = false; + bool is_destructive = true; + bool put_originals_at_bottom = true; // won't matter if is_destructive is true. + bool strokes_to_paths = false; + bool add_result_to_set = true; + + ObjectSet* set; + std::vector items_sorted; + std::vector non_overlapping_result; + +public: + + NonOverlappingPathsBuilder(ObjectSet *set) : set(set) + { + set_items_sorted(); + } + + void build() + { + // FIXME The functions may be in a different place. + // FIXME It should have its own widget. + // FIXME get rid of all fixed numbers so that it works + // with shapes of any size. + // TODO (for swipe-union thing) may want to copy the style of + // the first object in the selection instead of letting it be + // random (probably the it copies the style of the biggest + // object). may also let the user decide. + + set_desktop_busy(); + + // Ideally shouldn't be converting to paths? + set->toCurves(); + set->the_temporary_fix_for_the_transform_bug(); + + if (strokes_to_paths) { + set->strokesToPaths(); + } + + construct_non_overlapping_pieces(); + + // if destructive, no need to put at the bottom. + if (is_destructive) { + set->deleteItems(); + } else if (put_originals_at_bottom) { + set->lowerToBottom(true); // FIXME should skip_undo be true? + } + + if (add_result_to_set) { + // TODO probably there is a better way of doing this? + for (auto node : non_overlapping_result) { + set->add(node); + } + } + + unset_desktop_busy(); + } + + void setPointTolerance(double pointTolerance) { point_tolerance = pointTolerance; } + void setOutlineWidth(double outlineWidth) { outline_width = outlineWidth; } + void setOutlineMiter(double outlineMiter) { outline_miter = outlineMiter; } + void setExpansionOffset(double expansionOffset) { expansion_offset = expansionOffset; } + void setFillHoles(bool fillHoles) { fill_holes = fillHoles; } + void setIsDestructive(bool isDestructive) { is_destructive = isDestructive; } + void setPutOriginalsAtBottom(bool putOriginalsAtBottom) { put_originals_at_bottom = putOriginalsAtBottom; } + void setStrokesToPaths(bool strokesToPaths) { strokes_to_paths = strokesToPaths; } + void setAddResultToSet(bool addResultToSet) { add_result_to_set = addResultToSet; } + +private: + + SPDesktop* desktop() { + return set->desktop(); + } + + void set_desktop_busy() + { + if(desktop()) { + // set "busy" cursor + desktop()->setWaitingCursor(); + // disable redrawing during the break-apart operation for remarkable speedup for large paths + desktop()->getCanvas()->set_drawing_disabled(true); + } + } + + void unset_desktop_busy() + { + if (desktop()) { + desktop()->getCanvas()->set_drawing_disabled(false); + desktop()->clearWaitingCursor(); + } + } + + template + Geom::PathVector get_paths_from_item_list(const ItemList& items) + { + Geom::PathVector result; + + for (auto item : items) { + Geom::PathVector path_data = get_path_data(item); + result.insert(result.end(), path_data.begin(), path_data.end()); + } + + return result; + } + + Geom::Path get_circle_bounds_path(const Geom::Point ¢er, double radius) + { + auto x1 = center.x() - radius; + auto x2 = center.x() + radius; + auto y1 = center.y() + radius; + auto y2 = center.y() - radius; + Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); + return Geom::Path(res_bounds); } + + SPItem* getTopItemThatIntersectsWithPaths(const Geom::PathVector &path) + { + // FIXME change this stupid way. + for (SPItem* item : items_sorted) { + auto item_paths = get_path_data(item); + auto inters = sp_pathvector_boolop(path, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); + if (!inters.empty()) { + return item; + } + } + return nullptr; + } + + SPItem* getTopItemThatIntersectsWithPath(const Geom::Path &path) + { + return getTopItemThatIntersectsWithPaths(Geom::PathVector(path)); + } + + SPItem* getTopItemThatIntersectsWithPoint(const Geom::Point &point) + { + auto path = get_circle_bounds_path(point, point_tolerance); + return getTopItemThatIntersectsWithPath(path); + } + + Geom::PathVector get_bounds_path_vector(const Geom::OptRect &bounds) + { + auto x1 = bounds->left() - expansion_offset; + auto x2 = bounds->right() + expansion_offset; + auto y1 = bounds->bottom() + expansion_offset; + auto y2 = bounds->top() - expansion_offset; + Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); + return { Geom::Path(res_bounds) }; + } + + Geom::PathVector get_bounds_path_vector(const Geom::PathVector &paths) + { + // FIXME boundsFast or boundsExact? probably it's ok with boundsFast since + // most of the time an offset will be given (if not all?). + auto bounds = paths.boundsFast(); + return get_bounds_path_vector(bounds); + } + + void construct_non_overlapping_pieces() + { + // TODO this way doesn't seem to work nicely with round objects + // and produces a lot of unnecessary nodes. may improve it so that + // nodes are only at points of intersection and have curves. + + // FIXME make sure of the choice of the number here. + Geom::PathVector a = get_paths_from_item_list(set->items()); + Geom::PathVector bounds_expanded = get_bounds_path_vector(a); + + auto result = sp_pathvector_cutboolop(a, bounds_expanded, fill_nonZero, fill_nonZero); + + non_overlapping_result.clear(); + // discard first path. + for (int i = 1; i < result.size(); i++) { + auto node = draw_on_canvas(result[i]); + non_overlapping_result.push_back(node); + } + } + + double path_area(const Geom::Path& path) + { + return path.boundsFast()->area(); + } + + template + Geom::Path get_the_most_path_by_area(const Geom::PathVector& paths) + { + const Geom::Path* result = &paths.front(); + double current_area = path_area(*result); + for (auto &path : paths) { + double area = path_area(path); + if (cmp(area, current_area)) { + result = &path; + current_area = area; + } + } + assert(result != nullptr); + return *result; + } + + Geom::PathVector get_outline(const Geom::PathVector& paths) + { + // returns the outline for the biggest path. + auto double_greater = [](double a, double b) { + return a < b; + }; + Geom::Path path = get_the_most_path_by_area(paths); + return outline(path, outline_width, outline_miter); + } + + Geom::Path get_path_inside_fill(const Geom::PathVector &paths) + { + // the smaller path should be inside the fill (if outline_width is not way too big). + auto double_less = [](double a, double b) { + return a < b; + }; + auto outline = get_outline(paths); + return get_the_most_path_by_area(outline); + } + + Geom::Point get_point_inside_fill(const Geom::PathVector &paths) + { + // FIXME should we actually return any point? + return get_path_inside_fill(paths).initialPoint(); + } + + template // Geom::Path or Geom::PathVector + XML::Node* draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Node *parent, int pos) + { + Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); + repr->setAttribute("d", sp_svg_write_path(path)); + + if (to_copy_from) { + gchar *style = g_strdup(to_copy_from->getRepr()->attribute("style")); + repr->setAttribute("style", style); + } + + auto str = sp_svg_write_path(path); + repr->setAttribute("d", str); + + parent->addChildAtPos(repr, pos); + + return repr; + } + + template // Geom::Path or Geom::PathVector + XML::Node* draw_on_canvas(const Path& path) + { + // TODO make this in a better way. + auto item = set->xmlNodes().front(); + auto parent = item->parent(); + auto pos = item->position(); + + // FIXME find the best way to get a point inside the fill of the shape. + Geom::Point point = get_point_inside_fill(path); + SPItem* original_item = getTopItemThatIntersectsWithPoint(point); + if (!original_item && !fill_holes) { + return nullptr; + } + + return draw_on_canvas(path, original_item, parent, pos); + } + + void set_items_sorted() + { + // the top item in the document is the first in the list. + auto items = set->items(); + items_sorted = std::vector(items.begin(), items.end()); + auto cmp = [](SPItem *a, SPItem *b) { + return !sp_item_repr_compare_position_bool(a, b); + }; + std::sort(items_sorted.begin(), items_sorted.end(), cmp); + } +}; + +void ObjectSet::break_into_non_overlapping_pieces(bool fill_holes, bool is_destructive, + bool put_originals_at_bottom, bool strokes_to_paths) +{ + NonOverlappingPathsBuilder builder(this); + builder.setFillHoles(fill_holes); + builder.setIsDestructive(is_destructive); + builder.setPutOriginalsAtBottom(put_originals_at_bottom); + builder.setStrokesToPaths(strokes_to_paths); + builder.build(); } } // namespace Inkscape diff --git a/src/object/object-set.h b/src/object/object-set.h index 7d2050a9c0..53abaae326 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -470,7 +470,7 @@ public: void remove_paths_with_small_area(); void the_temporary_fix_for_the_transform_bug(); - void break_into_non_overlapping_pieces(bool fill_gaps = false, bool is_destructive = true, + void break_into_non_overlapping_pieces(bool fill_holes = false, bool is_destructive = true, bool put_originals_at_bottom = false, bool strokes_to_paths = false); void additional_method(); -- GitLab From d19f8f15fb789c80288ef3548f30d891e3dffd97 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 30 May 2021 15:59:49 +0200 Subject: [PATCH 013/235] Added NonOverlappingPathsBuilder class into a separate header and source files. --- src/helper/CMakeLists.txt | 2 + src/helper/NonOverlappingPathsBuilder.cpp | 263 ++++++++++++++++++ src/helper/NonOverlappingPathsBuilder.h | 90 +++++++ src/object/object-set.cpp | 309 +--------------------- src/object/sp-item.cpp | 28 ++ src/object/sp-item.h | 2 + src/verbs.cpp | 1 - 7 files changed, 388 insertions(+), 307 deletions(-) create mode 100644 src/helper/NonOverlappingPathsBuilder.cpp create mode 100644 src/helper/NonOverlappingPathsBuilder.h diff --git a/src/helper/CMakeLists.txt b/src/helper/CMakeLists.txt index 60e42ac671..0e6601a84e 100644 --- a/src/helper/CMakeLists.txt +++ b/src/helper/CMakeLists.txt @@ -18,6 +18,7 @@ set(helper_SRC geom-pathvectorsatellites.cpp geom-satellite.cpp gettext.cpp + NonOverlappingPathsBuilder.cpp pixbuf-ops.cpp png-write.cpp stock-items.cpp @@ -40,6 +41,7 @@ set(helper_SRC geom.h gettext.h mathfns.h + NonOverlappingPathsBuilder.h pixbuf-ops.h png-write.h stock-items.h diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp new file mode 100644 index 0000000000..7cee0eced5 --- /dev/null +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Builder class that construct non-overlapping paths given an ObjectSet. + * + * + *//* + * Authors: + * Osama Ahmad + * + * Copyright (C) 2021 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "NonOverlappingPathsBuilder.h" + +#include +#include +#include +#include "ui/widget/canvas.h" +#include "geom-pathstroke.h" + +namespace Inkscape { + +void NonOverlappingPathsBuilder::build() +{ + // FIXME The functions may be in a different place. + // FIXME It should have its own widget. + // FIXME get rid of all fixed numbers so that it works + // with shapes of any size. + // TODO (for swipe-union thing) may want to copy the style of + // the first object in the selection instead of letting it be + // random (probably the it copies the style of the biggest + // object). may also let the user decide. + + set_desktop_busy(); + + // Ideally shouldn't be converting to paths? + set->toCurves(); + set->the_temporary_fix_for_the_transform_bug(); + + if (strokes_to_paths) { + set->strokesToPaths(); + } + + construct_non_overlapping_pieces(); + + // if destructive, no need to put at the bottom. + if (is_destructive) { + set->deleteItems(); + } else if (put_originals_at_bottom) { + set->lowerToBottom(true); // FIXME should skip_undo be true? + } + + if (add_result_to_set) { + // TODO probably there is a better way of doing this? + for (auto node : non_overlapping_result) { + set->add(node); + } + } + + unset_desktop_busy(); +} + +SPDesktop *NonOverlappingPathsBuilder::desktop() +{ + return set->desktop(); +} + +void NonOverlappingPathsBuilder::set_desktop_busy() +{ + if (desktop()) { + // set "busy" cursor + desktop()->setWaitingCursor(); + // disable redrawing during the break-apart operation for remarkable speedup for large paths + desktop()->getCanvas()->set_drawing_disabled(true); + } +} + +void NonOverlappingPathsBuilder::unset_desktop_busy() +{ + if (desktop()) { + desktop()->getCanvas()->set_drawing_disabled(false); + desktop()->clearWaitingCursor(); + } +} + +template +Geom::PathVector NonOverlappingPathsBuilder::get_paths_from_item_list(const ItemList &items) +{ + Geom::PathVector result; + + for (auto item : items) { + Geom::PathVector path_data = item->get_pathvector(); + result.insert(result.end(), path_data.begin(), path_data.end()); + } + + return result; +} + +Geom::Path NonOverlappingPathsBuilder::get_circle_bounds_path(const Geom::Point ¢er, double radius) +{ + auto x1 = center.x() - radius; + auto x2 = center.x() + radius; + auto y1 = center.y() + radius; + auto y2 = center.y() - radius; + Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); + return Geom::Path(res_bounds); +} + +SPItem *NonOverlappingPathsBuilder::getTopItemThatIntersectsWithPaths(const Geom::PathVector &path) +{ + // FIXME change this stupid way. + for (SPItem *item : items_sorted) { + auto item_paths = item->get_pathvector(); + auto inters = sp_pathvector_boolop(path, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); + if (!inters.empty()) { + return item; + } + } + return nullptr; +} + +SPItem *NonOverlappingPathsBuilder::getTopItemThatIntersectsWithPath(const Geom::Path &path) +{ + return getTopItemThatIntersectsWithPaths(Geom::PathVector(path)); +} + +SPItem *NonOverlappingPathsBuilder::getTopItemThatIntersectsWithPoint(const Geom::Point &point) +{ + auto path = get_circle_bounds_path(point, point_tolerance); + return getTopItemThatIntersectsWithPath(path); +} + +Geom::PathVector NonOverlappingPathsBuilder::get_bounds_path_vector(const Geom::OptRect &bounds) +{ + auto x1 = bounds->left() - expansion_offset; + auto x2 = bounds->right() + expansion_offset; + auto y1 = bounds->bottom() + expansion_offset; + auto y2 = bounds->top() - expansion_offset; + Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); + return {Geom::Path(res_bounds)}; +} + +Geom::PathVector NonOverlappingPathsBuilder::get_bounds_path_vector(const Geom::PathVector &paths) +{ + // FIXME boundsFast or boundsExact? probably it's ok with boundsFast since + // most of the time an offset will be given (if not all?). + auto bounds = paths.boundsFast(); + return get_bounds_path_vector(bounds); +} + +void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() +{ + // TODO this way doesn't seem to work nicely with round objects + // and produces a lot of unnecessary nodes. may improve it so that + // nodes are only at points of intersection and have curves. + + // FIXME make sure of the choice of the number here. + Geom::PathVector a = get_paths_from_item_list(set->items()); + Geom::PathVector bounds_expanded = get_bounds_path_vector(a); + + auto result = sp_pathvector_cutboolop(a, bounds_expanded, fill_nonZero, fill_nonZero); + + non_overlapping_result.clear(); + // discard first path. + for (int i = 1; i < result.size(); i++) { + auto node = draw_on_canvas(result[i]); + non_overlapping_result.push_back(node); + } +} + +double NonOverlappingPathsBuilder::path_area(const Geom::Path &path) +{ + return path.boundsFast()->area(); +} + +bool double_greater(double a, double b) { return a > b; }; +bool double_less (double a, double b) { return a < b; }; + +template +Geom::Path NonOverlappingPathsBuilder::get_the_most_path_by_area(const Geom::PathVector &paths) +{ + const Geom::Path *result = &paths.front(); + double current_area = path_area(*result); + for (auto &path : paths) { + double area = path_area(path); + if (cmp(area, current_area)) { + result = &path; + current_area = area; + } + } + assert(result != nullptr); + return *result; +} + +Geom::PathVector NonOverlappingPathsBuilder::get_outline(const Geom::PathVector &paths) +{ + // returns the outline for the biggest path. + Geom::Path path = get_the_most_path_by_area(paths); + return outline(path, outline_width, outline_miter); +} + +Geom::Path NonOverlappingPathsBuilder::get_path_inside_fill(const Geom::PathVector &paths) +{ + // the smaller path should be inside the fill (if outline_width is not way too big). + auto outline = get_outline(paths); + return get_the_most_path_by_area(outline); +} + +Geom::Point NonOverlappingPathsBuilder::get_point_inside_fill(const Geom::PathVector &paths) +{ + // FIXME should we actually return any point? + return get_path_inside_fill(paths).initialPoint(); +} + +template // Geom::Path or Geom::PathVector +XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, const SPItem *to_copy_from, XML::Node *parent, + int pos) +{ + Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); + repr->setAttribute("d", sp_svg_write_path(path)); + + if (to_copy_from) { + gchar *style = g_strdup(to_copy_from->getRepr()->attribute("style")); + repr->setAttribute("style", style); + } + + auto str = sp_svg_write_path(path); + repr->setAttribute("d", str); + + parent->addChildAtPos(repr, pos); + + return repr; +} + +template // Geom::Path or Geom::PathVector +XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path) +{ + // TODO make this in a better way. + auto item = set->xmlNodes().front(); + auto parent = item->parent(); + auto pos = item->position(); + + // FIXME find the best way to get a point inside the fill of the shape. + Geom::Point point = get_point_inside_fill(path); + SPItem *original_item = getTopItemThatIntersectsWithPoint(point); + if (!original_item && !fill_holes) { + return nullptr; + } + + return draw_on_canvas(path, original_item, parent, pos); +} + +void NonOverlappingPathsBuilder::set_items_sorted() +{ + // the top item in the document is the first in the list. + auto items = set->items(); + items_sorted = std::vector(items.begin(), items.end()); + auto cmp = [](SPItem *a, SPItem *b) { return !sp_item_repr_compare_position_bool(a, b); }; + std::sort(items_sorted.begin(), items_sorted.end(), cmp); +} + +} \ No newline at end of file diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h new file mode 100644 index 0000000000..2b65770bb8 --- /dev/null +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Builder class that construct non-overlapping paths given an ObjectSet. + * + * + *//* + * Authors: + * Osama Ahmad + * + * Copyright (C) 2021 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#pragma once + +#include "object/object-set.h" +#include "xml/node.h" +#include "object/sp-item.h" + +namespace Inkscape { + +class NonOverlappingPathsBuilder +{ + // TODO figure out the best values for now. later, get rid of all fixed values. + double point_tolerance = 0.1; + double outline_width = 1; + double outline_miter = 1; // TODO figure out what miter is. + double expansion_offset = 1; + bool fill_holes = false; + bool is_destructive = true; + bool put_originals_at_bottom = true; // won't matter if is_destructive is true. + bool strokes_to_paths = false; + bool add_result_to_set = true; + + ObjectSet *set; + std::vector items_sorted; + std::vector non_overlapping_result; + +public: + NonOverlappingPathsBuilder(ObjectSet *set) + : set(set) + { + set_items_sorted(); + } + + void build(); + + void setPointTolerance(double pointTolerance) { point_tolerance = pointTolerance; } + void setOutlineWidth(double outlineWidth) { outline_width = outlineWidth; } + void setOutlineMiter(double outlineMiter) { outline_miter = outlineMiter; } + void setExpansionOffset(double expansionOffset) { expansion_offset = expansionOffset; } + void setFillHoles(bool fillHoles) { fill_holes = fillHoles; } + void setIsDestructive(bool isDestructive) { is_destructive = isDestructive; } + void setPutOriginalsAtBottom(bool putOriginalsAtBottom) { put_originals_at_bottom = putOriginalsAtBottom; } + void setStrokesToPaths(bool strokesToPaths) { strokes_to_paths = strokesToPaths; } + void setAddResultToSet(bool addResultToSet) { add_result_to_set = addResultToSet; } + +private: + void set_items_sorted(); + + void construct_non_overlapping_pieces(); + + SPDesktop *desktop(); + void set_desktop_busy(); + void unset_desktop_busy(); + Geom::Path get_circle_bounds_path(const Geom::Point ¢er, double radius); + SPItem *getTopItemThatIntersectsWithPaths(const Geom::PathVector &path); + SPItem *getTopItemThatIntersectsWithPath(const Geom::Path &path); + SPItem *getTopItemThatIntersectsWithPoint(const Geom::Point &point); + Geom::PathVector get_bounds_path_vector(const Geom::OptRect &bounds); + Geom::PathVector get_bounds_path_vector(const Geom::PathVector &paths); + double path_area(const Geom::Path &path); + Geom::PathVector get_outline(const Geom::PathVector &paths); + Geom::Path get_path_inside_fill(const Geom::PathVector &paths); + Geom::Point get_point_inside_fill(const Geom::PathVector &paths); + + template + Geom::PathVector get_paths_from_item_list(const ItemList &items); + + template + Geom::Path get_the_most_path_by_area(const Geom::PathVector &paths); + + template // Geom::Path or Geom::PathVector + XML::Node *draw_on_canvas(const Path &path, const SPItem *to_copy_from, XML::Node *parent, int pos); + + template // Geom::Path or Geom::PathVector + XML::Node *draw_on_canvas(const Path &path); +}; + +} \ No newline at end of file diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 5aadfd0afa..8db21ffabb 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -16,12 +16,11 @@ #include #include #include -#include -#include #include #include #include "helper/geom-pathstroke.h" +#include "helper/NonOverlappingPathsBuilder.h" #include "document.h" #include "xml/repr.h" @@ -455,30 +454,6 @@ std::string point_coords(const Geom::Point& point, int precision = 10) return oss.str(); } -Geom::PathVector get_path_data(SPItem *item) -{ - Geom::PathVector result; - if (SPGroup *group = dynamic_cast(item)) { - std::vector items = sp_item_group_item_list(group); - for (SPItem *item : items) { - Geom::PathVector sub_result = get_path_data(item); - result.insert(result.end(), sub_result.begin(), sub_result.end()); - } - } - else if (SPShape *shape = dynamic_cast(item)) { - result = shape->curve()->get_pathvector(); - } else if (SPPath *path = dynamic_cast(item)) { - result = path->curve()->get_pathvector(); - } - - // TODO you might want to handle the case for texts? - // anyways, for now objects are converted to paths - // before passing it to this function. but may be - // a good idea to put a case for it anyways. - - return result; -} - template void draw_sp_items(const SPItemList &items, ObjectSet *set) { for (auto item : items) { @@ -524,7 +499,7 @@ void print_coords_sorted_from_set(ObjectSet *set) int item_num = 1; for (auto item : items) { std::cout << "Item " << item_num++ << ":\n"; - auto paths = get_path_data(item); + auto paths = item->get_pathvector(); auto nodes = paths.nodes(); std::sort(nodes.begin(), nodes.end(), cmp); for (auto node : nodes) { @@ -577,7 +552,7 @@ Geom::PathVector remove_paths_with_small_area_from_pathvec(const Geom::PathVecto void ObjectSet::remove_paths_with_small_area() { for (auto item : items()) { - auto paths = get_path_data(item); + auto paths = item->get_pathvector(); // FIXME figure out whether to make the minimum area dependent on the selection or fixed. double minimum_area = 1; auto filtered_paths = remove_paths_with_small_area_from_pathvec(paths, minimum_area); @@ -591,284 +566,6 @@ void ObjectSet::additional_method() remove_paths_with_small_area(); } -class NonOverlappingPathsBuilder -{ - double point_tolerance = 0.1; - double outline_width = 1; - double outline_miter = 1; // TODO figure out what miter is. - double expansion_offset = 1; - bool fill_holes = false; - bool is_destructive = true; - bool put_originals_at_bottom = true; // won't matter if is_destructive is true. - bool strokes_to_paths = false; - bool add_result_to_set = true; - - ObjectSet* set; - std::vector items_sorted; - std::vector non_overlapping_result; - -public: - - NonOverlappingPathsBuilder(ObjectSet *set) : set(set) - { - set_items_sorted(); - } - - void build() - { - // FIXME The functions may be in a different place. - // FIXME It should have its own widget. - // FIXME get rid of all fixed numbers so that it works - // with shapes of any size. - // TODO (for swipe-union thing) may want to copy the style of - // the first object in the selection instead of letting it be - // random (probably the it copies the style of the biggest - // object). may also let the user decide. - - set_desktop_busy(); - - // Ideally shouldn't be converting to paths? - set->toCurves(); - set->the_temporary_fix_for_the_transform_bug(); - - if (strokes_to_paths) { - set->strokesToPaths(); - } - - construct_non_overlapping_pieces(); - - // if destructive, no need to put at the bottom. - if (is_destructive) { - set->deleteItems(); - } else if (put_originals_at_bottom) { - set->lowerToBottom(true); // FIXME should skip_undo be true? - } - - if (add_result_to_set) { - // TODO probably there is a better way of doing this? - for (auto node : non_overlapping_result) { - set->add(node); - } - } - - unset_desktop_busy(); - } - - void setPointTolerance(double pointTolerance) { point_tolerance = pointTolerance; } - void setOutlineWidth(double outlineWidth) { outline_width = outlineWidth; } - void setOutlineMiter(double outlineMiter) { outline_miter = outlineMiter; } - void setExpansionOffset(double expansionOffset) { expansion_offset = expansionOffset; } - void setFillHoles(bool fillHoles) { fill_holes = fillHoles; } - void setIsDestructive(bool isDestructive) { is_destructive = isDestructive; } - void setPutOriginalsAtBottom(bool putOriginalsAtBottom) { put_originals_at_bottom = putOriginalsAtBottom; } - void setStrokesToPaths(bool strokesToPaths) { strokes_to_paths = strokesToPaths; } - void setAddResultToSet(bool addResultToSet) { add_result_to_set = addResultToSet; } - -private: - - SPDesktop* desktop() { - return set->desktop(); - } - - void set_desktop_busy() - { - if(desktop()) { - // set "busy" cursor - desktop()->setWaitingCursor(); - // disable redrawing during the break-apart operation for remarkable speedup for large paths - desktop()->getCanvas()->set_drawing_disabled(true); - } - } - - void unset_desktop_busy() - { - if (desktop()) { - desktop()->getCanvas()->set_drawing_disabled(false); - desktop()->clearWaitingCursor(); - } - } - - template - Geom::PathVector get_paths_from_item_list(const ItemList& items) - { - Geom::PathVector result; - - for (auto item : items) { - Geom::PathVector path_data = get_path_data(item); - result.insert(result.end(), path_data.begin(), path_data.end()); - } - - return result; - } - - Geom::Path get_circle_bounds_path(const Geom::Point ¢er, double radius) - { - auto x1 = center.x() - radius; - auto x2 = center.x() + radius; - auto y1 = center.y() + radius; - auto y2 = center.y() - radius; - Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); - return Geom::Path(res_bounds); - } - - SPItem* getTopItemThatIntersectsWithPaths(const Geom::PathVector &path) - { - // FIXME change this stupid way. - for (SPItem* item : items_sorted) { - auto item_paths = get_path_data(item); - auto inters = sp_pathvector_boolop(path, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); - if (!inters.empty()) { - return item; - } - } - return nullptr; - } - - SPItem* getTopItemThatIntersectsWithPath(const Geom::Path &path) - { - return getTopItemThatIntersectsWithPaths(Geom::PathVector(path)); - } - - SPItem* getTopItemThatIntersectsWithPoint(const Geom::Point &point) - { - auto path = get_circle_bounds_path(point, point_tolerance); - return getTopItemThatIntersectsWithPath(path); - } - - Geom::PathVector get_bounds_path_vector(const Geom::OptRect &bounds) - { - auto x1 = bounds->left() - expansion_offset; - auto x2 = bounds->right() + expansion_offset; - auto y1 = bounds->bottom() + expansion_offset; - auto y2 = bounds->top() - expansion_offset; - Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); - return { Geom::Path(res_bounds) }; - } - - Geom::PathVector get_bounds_path_vector(const Geom::PathVector &paths) - { - // FIXME boundsFast or boundsExact? probably it's ok with boundsFast since - // most of the time an offset will be given (if not all?). - auto bounds = paths.boundsFast(); - return get_bounds_path_vector(bounds); - } - - void construct_non_overlapping_pieces() - { - // TODO this way doesn't seem to work nicely with round objects - // and produces a lot of unnecessary nodes. may improve it so that - // nodes are only at points of intersection and have curves. - - // FIXME make sure of the choice of the number here. - Geom::PathVector a = get_paths_from_item_list(set->items()); - Geom::PathVector bounds_expanded = get_bounds_path_vector(a); - - auto result = sp_pathvector_cutboolop(a, bounds_expanded, fill_nonZero, fill_nonZero); - - non_overlapping_result.clear(); - // discard first path. - for (int i = 1; i < result.size(); i++) { - auto node = draw_on_canvas(result[i]); - non_overlapping_result.push_back(node); - } - } - - double path_area(const Geom::Path& path) - { - return path.boundsFast()->area(); - } - - template - Geom::Path get_the_most_path_by_area(const Geom::PathVector& paths) - { - const Geom::Path* result = &paths.front(); - double current_area = path_area(*result); - for (auto &path : paths) { - double area = path_area(path); - if (cmp(area, current_area)) { - result = &path; - current_area = area; - } - } - assert(result != nullptr); - return *result; - } - - Geom::PathVector get_outline(const Geom::PathVector& paths) - { - // returns the outline for the biggest path. - auto double_greater = [](double a, double b) { - return a < b; - }; - Geom::Path path = get_the_most_path_by_area(paths); - return outline(path, outline_width, outline_miter); - } - - Geom::Path get_path_inside_fill(const Geom::PathVector &paths) - { - // the smaller path should be inside the fill (if outline_width is not way too big). - auto double_less = [](double a, double b) { - return a < b; - }; - auto outline = get_outline(paths); - return get_the_most_path_by_area(outline); - } - - Geom::Point get_point_inside_fill(const Geom::PathVector &paths) - { - // FIXME should we actually return any point? - return get_path_inside_fill(paths).initialPoint(); - } - - template // Geom::Path or Geom::PathVector - XML::Node* draw_on_canvas(const Path& path, const SPItem *to_copy_from, XML::Node *parent, int pos) - { - Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); - repr->setAttribute("d", sp_svg_write_path(path)); - - if (to_copy_from) { - gchar *style = g_strdup(to_copy_from->getRepr()->attribute("style")); - repr->setAttribute("style", style); - } - - auto str = sp_svg_write_path(path); - repr->setAttribute("d", str); - - parent->addChildAtPos(repr, pos); - - return repr; - } - - template // Geom::Path or Geom::PathVector - XML::Node* draw_on_canvas(const Path& path) - { - // TODO make this in a better way. - auto item = set->xmlNodes().front(); - auto parent = item->parent(); - auto pos = item->position(); - - // FIXME find the best way to get a point inside the fill of the shape. - Geom::Point point = get_point_inside_fill(path); - SPItem* original_item = getTopItemThatIntersectsWithPoint(point); - if (!original_item && !fill_holes) { - return nullptr; - } - - return draw_on_canvas(path, original_item, parent, pos); - } - - void set_items_sorted() - { - // the top item in the document is the first in the list. - auto items = set->items(); - items_sorted = std::vector(items.begin(), items.end()); - auto cmp = [](SPItem *a, SPItem *b) { - return !sp_item_repr_compare_position_bool(a, b); - }; - std::sort(items_sorted.begin(), items_sorted.end(), cmp); - } -}; - void ObjectSet::break_into_non_overlapping_pieces(bool fill_holes, bool is_destructive, bool put_originals_at_bottom, bool strokes_to_paths) { diff --git a/src/object/sp-item.cpp b/src/object/sp-item.cpp index 211626150d..bfdda874db 100644 --- a/src/object/sp-item.cpp +++ b/src/object/sp-item.cpp @@ -56,6 +56,9 @@ #include "live_effects/effect.h" #include "live_effects/lpeobject-reference.h" +#include "display/curve.h" +#include "object/sp-path.h" + #include "util/units.h" #define noSP_ITEM_DEBUG_IDLE @@ -136,6 +139,31 @@ bool SPItem::isVisibleAndUnlocked(unsigned display_key) const { return (!isHidden(display_key) && !isLocked()); } +Geom::PathVector SPItem::get_pathvector() +{ + Geom::PathVector result; + if (SPGroup *group = dynamic_cast(this)) { + std::vector items = sp_item_group_item_list(group); + for (SPItem *item : items) { + Geom::PathVector sub_result = item->get_pathvector(); + result.insert(result.end(), sub_result.begin(), sub_result.end()); + } + } + else if (SPShape *shape = dynamic_cast(this)) { + result = shape->curve()->get_pathvector(); + } else if (SPPath *path = dynamic_cast(this)) { + result = path->curve()->get_pathvector(); + } + + // TODO you might want to handle the case for texts? + // anyways, for now objects are converted to paths + // before passing it to this function. but may be + // a good idea to put a case for it anyways. + + return result; +} + + bool SPItem::isLocked() const { for (SPObject const *o = this; o != nullptr; o = o->parent) { SPItem const *item = dynamic_cast(o); diff --git a/src/object/sp-item.h b/src/object/sp-item.h index dc6797971d..2466b1a733 100644 --- a/src/object/sp-item.h +++ b/src/object/sp-item.h @@ -171,6 +171,8 @@ public: sigc::signal _transformed_signal; + Geom::PathVector get_pathvector(); + bool isLocked() const; void setLocked(bool lock); diff --git a/src/verbs.cpp b/src/verbs.cpp index aefe3005de..7638710859 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #include #include "desktop.h" -- GitLab From 409e810cd5543cd4057d4ebd24a8311be81794e7 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 31 May 2021 04:01:31 +0200 Subject: [PATCH 014/235] Changed the way for getting the top item at a given point. Also, discovered a bug, the program crashes when breaking 3 intersecting circles. --- src/helper/NonOverlappingPathsBuilder.cpp | 98 ++++++++++------------- src/helper/NonOverlappingPathsBuilder.h | 32 ++++---- 2 files changed, 58 insertions(+), 72 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 7cee0eced5..da204b1ea3 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -23,9 +23,8 @@ namespace Inkscape { void NonOverlappingPathsBuilder::build() { - // FIXME The functions may be in a different place. - // FIXME It should have its own widget. - // FIXME get rid of all fixed numbers so that it works + // TODO It should have its own widget. + // TODO get rid of all fixed numbers so that it works // with shapes of any size. // TODO (for swipe-union thing) may want to copy the style of // the first object in the selection instead of letting it be @@ -48,12 +47,12 @@ void NonOverlappingPathsBuilder::build() if (is_destructive) { set->deleteItems(); } else if (put_originals_at_bottom) { - set->lowerToBottom(true); // FIXME should skip_undo be true? + set->lowerToBottom(true); // TODO should skip_undo be true? } if (add_result_to_set) { // TODO probably there is a better way of doing this? - for (auto node : non_overlapping_result) { + for (auto node : result_nodes) { set->add(node); } } @@ -84,53 +83,29 @@ void NonOverlappingPathsBuilder::unset_desktop_busy() } } -template -Geom::PathVector NonOverlappingPathsBuilder::get_paths_from_item_list(const ItemList &items) +Geom::PathVector NonOverlappingPathsBuilder::get_all_paths_combined() { Geom::PathVector result; - for (auto item : items) { - Geom::PathVector path_data = item->get_pathvector(); - result.insert(result.end(), path_data.begin(), path_data.end()); + for (auto &path : original_paths_sorted) { + result.insert(result.end(), path.begin(), path.end()); } return result; } -Geom::Path NonOverlappingPathsBuilder::get_circle_bounds_path(const Geom::Point ¢er, double radius) +SPItem *NonOverlappingPathsBuilder::get_top_item_at_point(const Geom::Point &point) { - auto x1 = center.x() - radius; - auto x2 = center.x() + radius; - auto y1 = center.y() + radius; - auto y2 = center.y() - radius; - Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); - return Geom::Path(res_bounds); -} - -SPItem *NonOverlappingPathsBuilder::getTopItemThatIntersectsWithPaths(const Geom::PathVector &path) -{ - // FIXME change this stupid way. - for (SPItem *item : items_sorted) { - auto item_paths = item->get_pathvector(); - auto inters = sp_pathvector_boolop(path, item_paths, bool_op_inters, fill_nonZero, fill_nonZero); - if (!inters.empty()) { - return item; + for (int i = 0; i < original_paths_sorted.size(); i++) { + auto &path = original_paths_sorted[i]; + // TODO make sure this works correctly. + if (path.winding(point)) { + return original_items_sorted[i]; } } return nullptr; } -SPItem *NonOverlappingPathsBuilder::getTopItemThatIntersectsWithPath(const Geom::Path &path) -{ - return getTopItemThatIntersectsWithPaths(Geom::PathVector(path)); -} - -SPItem *NonOverlappingPathsBuilder::getTopItemThatIntersectsWithPoint(const Geom::Point &point) -{ - auto path = get_circle_bounds_path(point, point_tolerance); - return getTopItemThatIntersectsWithPath(path); -} - Geom::PathVector NonOverlappingPathsBuilder::get_bounds_path_vector(const Geom::OptRect &bounds) { auto x1 = bounds->left() - expansion_offset; @@ -143,8 +118,6 @@ Geom::PathVector NonOverlappingPathsBuilder::get_bounds_path_vector(const Geom:: Geom::PathVector NonOverlappingPathsBuilder::get_bounds_path_vector(const Geom::PathVector &paths) { - // FIXME boundsFast or boundsExact? probably it's ok with boundsFast since - // most of the time an offset will be given (if not all?). auto bounds = paths.boundsFast(); return get_bounds_path_vector(bounds); } @@ -155,17 +128,23 @@ void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() // and produces a lot of unnecessary nodes. may improve it so that // nodes are only at points of intersection and have curves. - // FIXME make sure of the choice of the number here. - Geom::PathVector a = get_paths_from_item_list(set->items()); + Geom::PathVector a = get_all_paths_combined(); Geom::PathVector bounds_expanded = get_bounds_path_vector(a); - auto result = sp_pathvector_cutboolop(a, bounds_expanded, fill_nonZero, fill_nonZero); + std::vector result = sp_pathvector_cutboolop( + a, + bounds_expanded, + fill_nonZero, + fill_nonZero + ); + + int n = result.size(); + result_nodes.clear(); + result_nodes.resize(n); - non_overlapping_result.clear(); // discard first path. - for (int i = 1; i < result.size(); i++) { - auto node = draw_on_canvas(result[i]); - non_overlapping_result.push_back(node); + for (int i = 1; i < n; i++) { + result_nodes[i] = draw_on_canvas(result[i]); } } @@ -209,13 +188,13 @@ Geom::Path NonOverlappingPathsBuilder::get_path_inside_fill(const Geom::PathVect Geom::Point NonOverlappingPathsBuilder::get_point_inside_fill(const Geom::PathVector &paths) { - // FIXME should we actually return any point? + // TODO should we actually return any point? return get_path_inside_fill(paths).initialPoint(); } template // Geom::Path or Geom::PathVector -XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, const SPItem *to_copy_from, XML::Node *parent, - int pos) +XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, + const SPItem *to_copy_from, XML::Node *parent, int pos) { Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); repr->setAttribute("d", sp_svg_write_path(path)); @@ -241,9 +220,9 @@ XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path) auto parent = item->parent(); auto pos = item->position(); - // FIXME find the best way to get a point inside the fill of the shape. + // TODO find the best way to get a point inside the fill of the shape. Geom::Point point = get_point_inside_fill(path); - SPItem *original_item = getTopItemThatIntersectsWithPoint(point); + SPItem *original_item = get_top_item_at_point(point); if (!original_item && !fill_holes) { return nullptr; } @@ -251,13 +230,24 @@ XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path) return draw_on_canvas(path, original_item, parent, pos); } +void NonOverlappingPathsBuilder::set_path_from_sorted_items() +{ + int n = original_items_sorted.size(); + original_paths_sorted.resize(n); + for (int i = 0; i < n; i++) { + original_paths_sorted[i] = original_items_sorted[i]->get_pathvector(); + } +} + void NonOverlappingPathsBuilder::set_items_sorted() { // the top item in the document is the first in the list. auto items = set->items(); - items_sorted = std::vector(items.begin(), items.end()); + original_items_sorted = std::vector(items.begin(), items.end()); auto cmp = [](SPItem *a, SPItem *b) { return !sp_item_repr_compare_position_bool(a, b); }; - std::sort(items_sorted.begin(), items_sorted.end(), cmp); + std::sort(original_items_sorted.begin(), original_items_sorted.end(), cmp); + + set_path_from_sorted_items(); } } \ No newline at end of file diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index 2b65770bb8..a2b1c5507a 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -22,8 +22,7 @@ namespace Inkscape { class NonOverlappingPathsBuilder { // TODO figure out the best values for now. later, get rid of all fixed values. - double point_tolerance = 0.1; - double outline_width = 1; + double outline_width = 0.05; double outline_miter = 1; // TODO figure out what miter is. double expansion_offset = 1; bool fill_holes = false; @@ -33,8 +32,9 @@ class NonOverlappingPathsBuilder bool add_result_to_set = true; ObjectSet *set; - std::vector items_sorted; - std::vector non_overlapping_result; + std::vector original_items_sorted; + std::vector original_paths_sorted; + std::vector result_nodes; public: NonOverlappingPathsBuilder(ObjectSet *set) @@ -45,7 +45,6 @@ public: void build(); - void setPointTolerance(double pointTolerance) { point_tolerance = pointTolerance; } void setOutlineWidth(double outlineWidth) { outline_width = outlineWidth; } void setOutlineMiter(double outlineMiter) { outline_miter = outlineMiter; } void setExpansionOffset(double expansionOffset) { expansion_offset = expansionOffset; } @@ -56,35 +55,32 @@ public: void setAddResultToSet(bool addResultToSet) { add_result_to_set = addResultToSet; } private: + void set_items_sorted(); + void set_path_from_sorted_items(); void construct_non_overlapping_pieces(); + template // Geom::Path or Geom::PathVector + XML::Node *draw_on_canvas(const Path &path, const SPItem *to_copy_from, XML::Node *parent, int pos); + + template // Geom::Path or Geom::PathVector + XML::Node *draw_on_canvas(const Path &path); + SPDesktop *desktop(); void set_desktop_busy(); void unset_desktop_busy(); - Geom::Path get_circle_bounds_path(const Geom::Point ¢er, double radius); - SPItem *getTopItemThatIntersectsWithPaths(const Geom::PathVector &path); - SPItem *getTopItemThatIntersectsWithPath(const Geom::Path &path); - SPItem *getTopItemThatIntersectsWithPoint(const Geom::Point &point); + SPItem *get_top_item_at_point(const Geom::Point &point); Geom::PathVector get_bounds_path_vector(const Geom::OptRect &bounds); Geom::PathVector get_bounds_path_vector(const Geom::PathVector &paths); double path_area(const Geom::Path &path); Geom::PathVector get_outline(const Geom::PathVector &paths); Geom::Path get_path_inside_fill(const Geom::PathVector &paths); Geom::Point get_point_inside_fill(const Geom::PathVector &paths); - - template - Geom::PathVector get_paths_from_item_list(const ItemList &items); + Geom::PathVector get_all_paths_combined(); template Geom::Path get_the_most_path_by_area(const Geom::PathVector &paths); - - template // Geom::Path or Geom::PathVector - XML::Node *draw_on_canvas(const Path &path, const SPItem *to_copy_from, XML::Node *parent, int pos); - - template // Geom::Path or Geom::PathVector - XML::Node *draw_on_canvas(const Path &path); }; } \ No newline at end of file -- GitLab From 50736ff4ca68f23110589d1c47afb0e0772bf8eb Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 31 May 2021 05:26:07 +0200 Subject: [PATCH 015/235] fixed the bug where the program was crashing when breaking 3 intersecting circles apart. set_sorted_items was being called only for one time. this is incorrect. the function should be called whenever the items of the set changes. --- src/helper/NonOverlappingPathsBuilder.cpp | 1 + src/helper/NonOverlappingPathsBuilder.h | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index da204b1ea3..50907ad99e 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -41,6 +41,7 @@ void NonOverlappingPathsBuilder::build() set->strokesToPaths(); } + set_items_sorted(); construct_non_overlapping_pieces(); // if destructive, no need to put at the bottom. diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index a2b1c5507a..0a74de93d2 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -38,10 +38,7 @@ class NonOverlappingPathsBuilder public: NonOverlappingPathsBuilder(ObjectSet *set) - : set(set) - { - set_items_sorted(); - } + : set(set) {} void build(); -- GitLab From 4a970996c83320433ec42d327ce8bb6f7a6f8fcd Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 31 May 2021 06:18:50 +0200 Subject: [PATCH 016/235] added a method for getting the top item that intersects with multiple points. --- src/helper/NonOverlappingPathsBuilder.cpp | 18 +++++++++++++++--- src/helper/NonOverlappingPathsBuilder.h | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 50907ad99e..fa7360b6b1 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -95,18 +95,30 @@ Geom::PathVector NonOverlappingPathsBuilder::get_all_paths_combined() return result; } -SPItem *NonOverlappingPathsBuilder::get_top_item_at_point(const Geom::Point &point) +SPItem *NonOverlappingPathsBuilder::get_top_item_at_points(const std::vector &points) { for (int i = 0; i < original_paths_sorted.size(); i++) { auto &path = original_paths_sorted[i]; - // TODO make sure this works correctly. - if (path.winding(point)) { + bool is_valid = true; + for (auto &point : points) { + if (!path.winding(point)) { + is_valid = false; + break; + } + } + if (is_valid) { return original_items_sorted[i]; } } return nullptr; } +SPItem *NonOverlappingPathsBuilder::get_top_item_at_point(const Geom::Point &point) +{ + // TODO is the version with multiple points even used? + return get_top_item_at_points({ point }); +} + Geom::PathVector NonOverlappingPathsBuilder::get_bounds_path_vector(const Geom::OptRect &bounds) { auto x1 = bounds->left() - expansion_offset; diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index 0a74de93d2..1bc1e853fd 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -68,6 +68,7 @@ private: void set_desktop_busy(); void unset_desktop_busy(); SPItem *get_top_item_at_point(const Geom::Point &point); + SPItem *get_top_item_at_points(const std::vector &points); Geom::PathVector get_bounds_path_vector(const Geom::OptRect &bounds); Geom::PathVector get_bounds_path_vector(const Geom::PathVector &paths); double path_area(const Geom::Path &path); -- GitLab From 42beb9f094d6087857e7fbe3d44343c50765ac63 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 31 May 2021 07:24:15 +0200 Subject: [PATCH 017/235] Improved the option to put the result above the original items and removed the option for converting strokes to paths. The option for converting strokes to paths didn't seem to work very well, thus removed. The option for putting the result on top of the original items was just putting the originals at the bottom and not just below the result. This is not desirable since if the selected items are on top of another item, it's not a good idea to have the originals be below that item. --- src/helper/NonOverlappingPathsBuilder.cpp | 45 +++++++++++++---------- src/helper/NonOverlappingPathsBuilder.h | 16 ++++---- src/object/object-set.cpp | 6 +-- src/object/object-set.h | 2 +- src/verbs.cpp | 2 +- 5 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index fa7360b6b1..22f85b7f27 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -37,18 +37,11 @@ void NonOverlappingPathsBuilder::build() set->toCurves(); set->the_temporary_fix_for_the_transform_bug(); - if (strokes_to_paths) { - set->strokesToPaths(); - } - set_items_sorted(); construct_non_overlapping_pieces(); - // if destructive, no need to put at the bottom. if (is_destructive) { set->deleteItems(); - } else if (put_originals_at_bottom) { - set->lowerToBottom(true); // TODO should skip_undo be true? } if (add_result_to_set) { @@ -95,7 +88,7 @@ Geom::PathVector NonOverlappingPathsBuilder::get_all_paths_combined() return result; } -SPItem *NonOverlappingPathsBuilder::get_top_item_at_points(const std::vector &points) +SPItem *NonOverlappingPathsBuilder::get_top_item_that_intersects_with_points(const std::vector &points) { for (int i = 0; i < original_paths_sorted.size(); i++) { auto &path = original_paths_sorted[i]; @@ -116,7 +109,7 @@ SPItem *NonOverlappingPathsBuilder::get_top_item_at_points(const std::vectorarea(); } @@ -173,9 +168,9 @@ template Geom::Path NonOverlappingPathsBuilder::get_the_most_path_by_area(const Geom::PathVector &paths) { const Geom::Path *result = &paths.front(); - double current_area = path_area(*result); + double current_area = path_bounding_area(*result); for (auto &path : paths) { - double area = path_area(path); + double area = path_bounding_area(path); if (cmp(area, current_area)) { result = &path; current_area = area; @@ -201,13 +196,17 @@ Geom::Path NonOverlappingPathsBuilder::get_path_inside_fill(const Geom::PathVect Geom::Point NonOverlappingPathsBuilder::get_point_inside_fill(const Geom::PathVector &paths) { + // FIXME this way doesn't work well for all sizes and shapes. + // you have to figure out a different way. for now, might be fine. // TODO should we actually return any point? return get_path_inside_fill(paths).initialPoint(); } +// } ************************************************************************************************ + template // Geom::Path or Geom::PathVector XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, - const SPItem *to_copy_from, XML::Node *parent, int pos) + const SPItem *to_copy_from, XML::Node *parent, XML::Node *after) { Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); repr->setAttribute("d", sp_svg_write_path(path)); @@ -220,7 +219,7 @@ XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, auto str = sp_svg_write_path(path); repr->setAttribute("d", str); - parent->addChildAtPos(repr, pos); + parent->addChild(repr, after); return repr; } @@ -228,10 +227,16 @@ XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, template // Geom::Path or Geom::PathVector XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path) { - // TODO make this in a better way. - auto item = set->xmlNodes().front(); + XML::Node *item = set->topRepr(); auto parent = item->parent(); - auto pos = item->position(); + XML::Node *after; + + if (put_above_originals) { + // front() is the top item. + after = original_items_sorted.front()->getRepr(); + } else { + after = original_items_sorted.back()->getRepr()->prev(); + } // TODO find the best way to get a point inside the fill of the shape. Geom::Point point = get_point_inside_fill(path); @@ -240,10 +245,10 @@ XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path) return nullptr; } - return draw_on_canvas(path, original_item, parent, pos); + return draw_on_canvas(path, original_item, parent, after); } -void NonOverlappingPathsBuilder::set_path_from_sorted_items() +void NonOverlappingPathsBuilder::set_paths_from_sorted_items() { int n = original_items_sorted.size(); original_paths_sorted.resize(n); @@ -260,7 +265,7 @@ void NonOverlappingPathsBuilder::set_items_sorted() auto cmp = [](SPItem *a, SPItem *b) { return !sp_item_repr_compare_position_bool(a, b); }; std::sort(original_items_sorted.begin(), original_items_sorted.end(), cmp); - set_path_from_sorted_items(); + set_paths_from_sorted_items(); } } \ No newline at end of file diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index 1bc1e853fd..86fc5a4728 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -24,11 +24,10 @@ class NonOverlappingPathsBuilder // TODO figure out the best values for now. later, get rid of all fixed values. double outline_width = 0.05; double outline_miter = 1; // TODO figure out what miter is. - double expansion_offset = 1; + double expansion_offset = 1; // the choice here is arbitrary. bool fill_holes = false; bool is_destructive = true; - bool put_originals_at_bottom = true; // won't matter if is_destructive is true. - bool strokes_to_paths = false; + bool put_above_originals = true; // won't matter if is_destructive is true. bool add_result_to_set = true; ObjectSet *set; @@ -47,19 +46,18 @@ public: void setExpansionOffset(double expansionOffset) { expansion_offset = expansionOffset; } void setFillHoles(bool fillHoles) { fill_holes = fillHoles; } void setIsDestructive(bool isDestructive) { is_destructive = isDestructive; } - void setPutOriginalsAtBottom(bool putOriginalsAtBottom) { put_originals_at_bottom = putOriginalsAtBottom; } - void setStrokesToPaths(bool strokesToPaths) { strokes_to_paths = strokesToPaths; } + void setPutOriginalsAtBottom(bool putOriginalsAtBottom) { put_above_originals = putOriginalsAtBottom; } void setAddResultToSet(bool addResultToSet) { add_result_to_set = addResultToSet; } private: void set_items_sorted(); - void set_path_from_sorted_items(); + void set_paths_from_sorted_items(); void construct_non_overlapping_pieces(); template // Geom::Path or Geom::PathVector - XML::Node *draw_on_canvas(const Path &path, const SPItem *to_copy_from, XML::Node *parent, int pos); + XML::Node *draw_on_canvas(const Path &path, const SPItem *to_copy_from, XML::Node *parent, XML::Node *after); template // Geom::Path or Geom::PathVector XML::Node *draw_on_canvas(const Path &path); @@ -68,10 +66,10 @@ private: void set_desktop_busy(); void unset_desktop_busy(); SPItem *get_top_item_at_point(const Geom::Point &point); - SPItem *get_top_item_at_points(const std::vector &points); + SPItem *get_top_item_that_intersects_with_points(const std::vector &points); Geom::PathVector get_bounds_path_vector(const Geom::OptRect &bounds); Geom::PathVector get_bounds_path_vector(const Geom::PathVector &paths); - double path_area(const Geom::Path &path); + double path_bounding_area(const Geom::Path &path); Geom::PathVector get_outline(const Geom::PathVector &paths); Geom::Path get_path_inside_fill(const Geom::PathVector &paths); Geom::Point get_point_inside_fill(const Geom::PathVector &paths); diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 8db21ffabb..35c89b25a2 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -566,14 +566,12 @@ void ObjectSet::additional_method() remove_paths_with_small_area(); } -void ObjectSet::break_into_non_overlapping_pieces(bool fill_holes, bool is_destructive, - bool put_originals_at_bottom, bool strokes_to_paths) +void ObjectSet::break_into_non_overlapping_pieces(bool fill_holes, bool is_destructive, bool put_above_originals) { NonOverlappingPathsBuilder builder(this); builder.setFillHoles(fill_holes); builder.setIsDestructive(is_destructive); - builder.setPutOriginalsAtBottom(put_originals_at_bottom); - builder.setStrokesToPaths(strokes_to_paths); + builder.setPutOriginalsAtBottom(put_above_originals); builder.build(); } diff --git a/src/object/object-set.h b/src/object/object-set.h index 53abaae326..fc492adb11 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -471,7 +471,7 @@ public: void remove_paths_with_small_area(); void the_temporary_fix_for_the_transform_bug(); void break_into_non_overlapping_pieces(bool fill_holes = false, bool is_destructive = true, - bool put_originals_at_bottom = false, bool strokes_to_paths = false); + bool put_above_originals = false); void additional_method(); protected: diff --git a/src/verbs.cpp b/src/verbs.cpp index 7638710859..789fa7f41a 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1270,7 +1270,7 @@ void SelectionVerb::perform(SPAction *action, void *data) selection->removeLPESRecursive(true); selection->unlinkRecursive(true); // TODO make this a single history item. - selection->break_into_non_overlapping_pieces(false, true, true, false); + selection->break_into_non_overlapping_pieces(false, true, true); break; case 23232323: selection->removeLPESRecursive(true); -- GitLab From 2b17f3e69c2daa6fd88693ab81d8f1b43694d49f Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 1 Jun 2021 12:18:18 +0200 Subject: [PATCH 018/235] tried to use an algorithm that relies on binary search for getting a point inside the fill of a path vector. didn't work for now. --- src/helper/NonOverlappingPathsBuilder.cpp | 97 +++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 22f85b7f27..0e68cacd1a 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -134,6 +134,9 @@ void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() // and produces a lot of unnecessary nodes. may improve it so that // nodes are only at points of intersection and have curves. + // FIXME doesn't work well with very small round shapes. the result + // paths are wiggly. when applied on bigger shapes, works fine. + Geom::PathVector a = get_all_paths_combined(); Geom::PathVector bounds_expanded = get_bounds_path_vector(a); @@ -202,6 +205,100 @@ Geom::Point NonOverlappingPathsBuilder::get_point_inside_fill(const Geom::PathVe return get_path_inside_fill(paths).initialPoint(); } +template +Geom::Point get_bottom_point(const Path &path) +{ + auto nodes = path.nodes(); + Geom::Point *result = &nodes.front(); + + for (Geom::Point &node : nodes) { + // bigger y means below. + if (node.y() > result->y()) { + result = &node; + } + } + return *result; +} + +struct ResultCoordinates +{ + double x; + double top_y; + double bottom_y; +}; + +template +ResultCoordinates get_two_points_for_binary_search(Path &path) +{ + // returns 2 points with the same x component. + // the first point is the lowest point on the + // path and is guaranteed to be on the path. + // the second might not be on the path. + // the second point might be slightly above or below + // the top point. this is not an issue since the binary + // search will be moving towards the bottom point anyways. + + auto bounds = path.boundsFast(); + Geom::Point bottom_point = get_bottom_point(path); + + ResultCoordinates result; + + result.x = bottom_point.x(); + result.bottom_y = bottom_point.y(); + result.top_y = bounds->top(); + + std::cout << "Bounds: "; + std::cout << "Top: " << bounds->top() << ", "; + std::cout << "Bottom: " << bounds->bottom() << ", "; + std::cout << "Left: " << bounds->left() << ", "; + std::cout << "Right: " << bounds->right() << "\n"; + + std::cout << "X: " << result.x << ", "; + std::cout << "Top Y: " << result.top_y << ", "; + std::cout << "Bottom Y: " << result.bottom_y << "\n"; + + return result; +} + +Geom::Point get_point_inside_fill(const Geom::PathVector &paths) +{ + // TODO To be continued. didn't work for a case of a rectanle. + // the top and the bottom point will be on the same line and + // the returned point will not be inside the fill. + + // goes towards the first point. + + auto points = get_two_points_for_binary_search(paths); + double &bottom_y = points.bottom_y; + double &top_y = points.top_y; + double &x = points.x; + + // TODO figure out a solution for this. + // this is to avoid iterating infinitely. This way might + // not work if the bottom point is on a line. in such case + // will just return the point that is on the line. + int maximum_iterations = 1000; + + while (maximum_iterations--) + { + double mid_y = (top_y + bottom_y) / 2; + + std::cout << "Bottom Y: " << bottom_y << ", " << "Top Y: " << top_y << ", " << "Mid Y: " << mid_y << '\n'; + + Geom::Point mid_point(x, mid_y); + if (paths.winding(mid_point)) { + std::cout << "Returned.\n"; + return mid_point; + } else { + std::cout << "Not in fill.\n"; + top_y = mid_y; + } + } + + std::cout << "Returned the initial point...\n"; + return paths.initialPoint(); +} + // } ************************************************************************************************ template // Geom::Path or Geom::PathVector -- GitLab From 11fecd39ff7cbe454d4007214e7d5e5f55e2049c Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 1 Jun 2021 13:17:47 +0200 Subject: [PATCH 019/235] another unsuccessful try for getting a point inside the fill. was generating random points inside the coordinates. --- src/helper/NonOverlappingPathsBuilder.cpp | 39 +++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 0e68cacd1a..faed0be871 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -18,6 +18,7 @@ #include #include "ui/widget/canvas.h" #include "geom-pathstroke.h" +#include namespace Inkscape { @@ -205,6 +206,44 @@ Geom::Point NonOverlappingPathsBuilder::get_point_inside_fill(const Geom::PathVe return get_path_inside_fill(paths).initialPoint(); } +//Geom::Point NonOverlappingPathsBuilder::get_point_inside_fill(const Geom::PathVector &paths) +//{ +// auto bounds = paths.boundsFast(); +// double top = bounds->top(); +// double bottom = bounds->bottom(); +// double left = bounds->left(); +// double right = bounds->right(); +// +// std::random_device x_rd; //Will be used to obtain a seed for the random number engine +// std::mt19937 x_gen(x_rd()); //Standard mersenne_twister_engine seeded with rd() +// std::uniform_real_distribution<> x_dis(top, bottom); +// +// std::random_device y_rd; //Will be used to obtain a seed for the random number engine +// std::mt19937 y_gen(y_rd()); //Standard mersenne_twister_engine seeded with rd() +// std::uniform_real_distribution<> y_dis(left, right); +// +// std::cout << "Bounds: "; +// std::cout << "Top: " << bounds->top() << ", "; +// std::cout << "Bottom: " << bounds->bottom() << ", "; +// std::cout << "Left: " << bounds->left() << ", "; +// std::cout << "Right: " << bounds->right() << "\n"; +// +// int maximum_iterations = 1000000; +// while (maximum_iterations--) { +// double x = x_dis(x_gen); +// double y = y_dis(y_gen); +//// std::cout << "X: " << x << ", Y: " << y << "\n"; +// Geom::Point result(x, y); +// if (paths.winding(result)) { +//// std::cout << "\n\n---------- End ----------\n\n"; +// return result; +// } +// } +// +// std::cout << "Returned the initial point...\n"; +// return paths.initialPoint(); +//} + template Geom::Point get_bottom_point(const Path &path) { -- GitLab From 589e7838e864eab31f1b938d83da70ecb6d2ecd5 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 7 Jun 2021 03:54:40 +0200 Subject: [PATCH 020/235] Another unsuccessful trial for implementing the breaking into non-overlapping functionality. Was trying to make a division for each item from the original selection with a bigger bounding rectangle. this way, the nodes of the broken shapes and the original shapes will match. unfortunately not and it's not guaranteed. --- src/helper/NonOverlappingPathsBuilder.cpp | 288 ++++++++-------------- src/helper/NonOverlappingPathsBuilder.h | 10 +- 2 files changed, 112 insertions(+), 186 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index faed0be871..bcafc974b4 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -89,6 +89,52 @@ Geom::PathVector NonOverlappingPathsBuilder::get_all_paths_combined() return result; } +bool points_equal(const Geom::Point &a, const Geom::Point &b, double epsilon = 0.001) { +// std::cout << "Diff: " << abs(a.x() - b.x()) << ", " << abs(a.y() - b.y()) << '\n'; +// std::cout << "X1: " << a.x() << ", "; +// std::cout << "X2: " << b.x() << ", "; +// std::cout << "Y1: " << a.y() << ", "; +// std::cout << "Y2: " << b.y() << "\n"; + return abs(a.x() - b.x()) < epsilon && abs(a.y() - b.y()) < epsilon; +} + +bool point_on_edge(const Geom::PathVector &paths, const Geom::Point &point) +{ + auto nodes = paths.nodes(); +// std::cout << "Nodes count: " << nodes.size() << '\n'; + int idx = 1; + for (auto &node : nodes) { +// std::cout << "Point " << idx++ << ":\n"; + if (points_equal(point, node)) { + std::cout << "Match of " << node << " and " << point << ".\n"; + return true; + } + } +// std::cout << "No match at all\n"; + return false; +} + +//SPItem *NonOverlappingPathsBuilder::get_top_item_with_points_on_edges(const std::vector &points) +//{ +// for (int i = 0; i < original_paths_sorted.size(); i++) { +// auto &path = original_paths_sorted[i]; +// bool is_valid = true; +// int pt_idx = 1; +// for (auto &point : points) { +// std::cout << "Path " << i + 1 << ", Point " << pt_idx++ << ":\n"; +// if (!point_on_edge(point, path)) { +// std::cout << "Path no match with points...\n"; +// is_valid = false; +// break; +// } +// } +// if (is_valid) { +// return original_items_sorted[i]; +// } +// } +// return nullptr; +//} + SPItem *NonOverlappingPathsBuilder::get_top_item_that_intersects_with_points(const std::vector &points) { for (int i = 0; i < original_paths_sorted.size(); i++) { @@ -158,206 +204,64 @@ void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() } } -// this part should be replaces with a better way of returning a point inside a fill { ************** - -double NonOverlappingPathsBuilder::path_bounding_area(const Geom::Path &path) +template // Geom::Path or Geom::PathVector +XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, + const SPItem *to_copy_from, XML::Node *parent, XML::Node *after) { - return path.boundsFast()->area(); -} - -bool double_greater(double a, double b) { return a > b; }; -bool double_less (double a, double b) { return a < b; }; + Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); + repr->setAttribute("d", sp_svg_write_path(path)); -template -Geom::Path NonOverlappingPathsBuilder::get_the_most_path_by_area(const Geom::PathVector &paths) -{ - const Geom::Path *result = &paths.front(); - double current_area = path_bounding_area(*result); - for (auto &path : paths) { - double area = path_bounding_area(path); - if (cmp(area, current_area)) { - result = &path; - current_area = area; - } + if (to_copy_from) { + gchar *style = g_strdup(to_copy_from->getRepr()->attribute("style")); + repr->setAttribute("style", style); } - assert(result != nullptr); - return *result; -} - -Geom::PathVector NonOverlappingPathsBuilder::get_outline(const Geom::PathVector &paths) -{ - // returns the outline for the biggest path. - Geom::Path path = get_the_most_path_by_area(paths); - return outline(path, outline_width, outline_miter); -} - -Geom::Path NonOverlappingPathsBuilder::get_path_inside_fill(const Geom::PathVector &paths) -{ - // the smaller path should be inside the fill (if outline_width is not way too big). - auto outline = get_outline(paths); - return get_the_most_path_by_area(outline); -} - -Geom::Point NonOverlappingPathsBuilder::get_point_inside_fill(const Geom::PathVector &paths) -{ - // FIXME this way doesn't work well for all sizes and shapes. - // you have to figure out a different way. for now, might be fine. - // TODO should we actually return any point? - return get_path_inside_fill(paths).initialPoint(); -} -//Geom::Point NonOverlappingPathsBuilder::get_point_inside_fill(const Geom::PathVector &paths) -//{ -// auto bounds = paths.boundsFast(); -// double top = bounds->top(); -// double bottom = bounds->bottom(); -// double left = bounds->left(); -// double right = bounds->right(); -// -// std::random_device x_rd; //Will be used to obtain a seed for the random number engine -// std::mt19937 x_gen(x_rd()); //Standard mersenne_twister_engine seeded with rd() -// std::uniform_real_distribution<> x_dis(top, bottom); -// -// std::random_device y_rd; //Will be used to obtain a seed for the random number engine -// std::mt19937 y_gen(y_rd()); //Standard mersenne_twister_engine seeded with rd() -// std::uniform_real_distribution<> y_dis(left, right); -// -// std::cout << "Bounds: "; -// std::cout << "Top: " << bounds->top() << ", "; -// std::cout << "Bottom: " << bounds->bottom() << ", "; -// std::cout << "Left: " << bounds->left() << ", "; -// std::cout << "Right: " << bounds->right() << "\n"; -// -// int maximum_iterations = 1000000; -// while (maximum_iterations--) { -// double x = x_dis(x_gen); -// double y = y_dis(y_gen); -//// std::cout << "X: " << x << ", Y: " << y << "\n"; -// Geom::Point result(x, y); -// if (paths.winding(result)) { -//// std::cout << "\n\n---------- End ----------\n\n"; -// return result; -// } -// } -// -// std::cout << "Returned the initial point...\n"; -// return paths.initialPoint(); -//} + auto str = sp_svg_write_path(path); + repr->setAttribute("d", str); -template -Geom::Point get_bottom_point(const Path &path) -{ - auto nodes = path.nodes(); - Geom::Point *result = &nodes.front(); + parent->addChild(repr, after); - for (Geom::Point &node : nodes) { - // bigger y means below. - if (node.y() > result->y()) { - result = &node; - } - } - return *result; + return repr; } -struct ResultCoordinates -{ - double x; - double top_y; - double bottom_y; -}; - -template -ResultCoordinates get_two_points_for_binary_search(Path &path) -{ - // returns 2 points with the same x component. - // the first point is the lowest point on the - // path and is guaranteed to be on the path. - // the second might not be on the path. - // the second point might be slightly above or below - // the top point. this is not an issue since the binary - // search will be moving towards the bottom point anyways. - +void print_bounds_and_nodes_count(const Geom::PathVector &path) { auto bounds = path.boundsFast(); - Geom::Point bottom_point = get_bottom_point(path); - - ResultCoordinates result; - - result.x = bottom_point.x(); - result.bottom_y = bottom_point.y(); - result.top_y = bounds->top(); - + std::cout << "Nodes count: " << path.nodes().size() << ", "; std::cout << "Bounds: "; std::cout << "Top: " << bounds->top() << ", "; std::cout << "Bottom: " << bounds->bottom() << ", "; std::cout << "Left: " << bounds->left() << ", "; std::cout << "Right: " << bounds->right() << "\n"; - - std::cout << "X: " << result.x << ", "; - std::cout << "Top Y: " << result.top_y << ", "; - std::cout << "Bottom Y: " << result.bottom_y << "\n"; - - return result; } -Geom::Point get_point_inside_fill(const Geom::PathVector &paths) +template +bool point_in_path(const Path& path, Geom::Point point) { - // TODO To be continued. didn't work for a case of a rectanle. - // the top and the bottom point will be on the same line and - // the returned point will not be inside the fill. - - // goes towards the first point. - - auto points = get_two_points_for_binary_search(paths); - double &bottom_y = points.bottom_y; - double &top_y = points.top_y; - double &x = points.x; - - // TODO figure out a solution for this. - // this is to avoid iterating infinitely. This way might - // not work if the bottom point is on a line. in such case - // will just return the point that is on the line. - int maximum_iterations = 1000; - - while (maximum_iterations--) - { - double mid_y = (top_y + bottom_y) / 2; - - std::cout << "Bottom Y: " << bottom_y << ", " << "Top Y: " << top_y << ", " << "Mid Y: " << mid_y << '\n'; - - Geom::Point mid_point(x, mid_y); - if (paths.winding(mid_point)) { - std::cout << "Returned.\n"; - return mid_point; - } else { - std::cout << "Not in fill.\n"; - top_y = mid_y; - } - } - - std::cout << "Returned the initial point...\n"; - return paths.initialPoint(); + return path.winding(point) || point_on_edge(path, point); } -// } ************************************************************************************************ - -template // Geom::Path or Geom::PathVector -XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, - const SPItem *to_copy_from, XML::Node *parent, XML::Node *after) +template +SPItem *NonOverlappingPathsBuilder::get_top_item_that_intersects(const Path &path) { - Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); - repr->setAttribute("d", sp_svg_write_path(path)); - - if (to_copy_from) { - gchar *style = g_strdup(to_copy_from->getRepr()->attribute("style")); - repr->setAttribute("style", style); - } + print_bounds_and_nodes_count(path); + auto nodes = path.nodes(); + for (int i = 0; i < paths_for_matching.size(); i++) { - auto str = sp_svg_write_path(path); - repr->setAttribute("d", str); + std::cout << "In path #" << i << '\n'; - parent->addChild(repr, after); + bool found = true; + for (auto &node : nodes) { + if (!point_in_path(path, node)) { + found = false; + break; + } + } - return repr; + if (found) { + return original_items_sorted[i]; + } + } + return nullptr; } template // Geom::Path or Geom::PathVector @@ -374,9 +278,7 @@ XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path) after = original_items_sorted.back()->getRepr()->prev(); } - // TODO find the best way to get a point inside the fill of the shape. - Geom::Point point = get_point_inside_fill(path); - SPItem *original_item = get_top_item_at_point(point); + SPItem *original_item = get_top_item_that_intersects(path); if (!original_item && !fill_holes) { return nullptr; } @@ -402,6 +304,32 @@ void NonOverlappingPathsBuilder::set_items_sorted() std::sort(original_items_sorted.begin(), original_items_sorted.end(), cmp); set_paths_from_sorted_items(); + construct_paths_for_matching(); +} + +void NonOverlappingPathsBuilder::construct_paths_for_matching() +{ + int n = original_items_sorted.size(); + paths_for_matching.resize(n); + for (int i = 0; i < n; i++) { + auto path = original_items_sorted[i]->get_pathvector(); + auto bounds = get_bounds_path_vector(path); + + std::vector result = sp_pathvector_cutboolop( + path, + bounds, + fill_nonZero, + fill_nonZero + ); + + for (int j = 1; j < result.size(); j++) { + paths_for_matching[i].( + paths_for_matching.end(), + result[j].begin(), + result[j].end() + ); + } + } } } \ No newline at end of file diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index 86fc5a4728..07221310c5 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -34,6 +34,7 @@ class NonOverlappingPathsBuilder std::vector original_items_sorted; std::vector original_paths_sorted; std::vector result_nodes; + std::vector paths_for_matching; public: NonOverlappingPathsBuilder(ObjectSet *set) @@ -53,6 +54,7 @@ private: void set_items_sorted(); void set_paths_from_sorted_items(); + void construct_paths_for_matching(); void construct_non_overlapping_pieces(); @@ -69,14 +71,10 @@ private: SPItem *get_top_item_that_intersects_with_points(const std::vector &points); Geom::PathVector get_bounds_path_vector(const Geom::OptRect &bounds); Geom::PathVector get_bounds_path_vector(const Geom::PathVector &paths); - double path_bounding_area(const Geom::Path &path); - Geom::PathVector get_outline(const Geom::PathVector &paths); - Geom::Path get_path_inside_fill(const Geom::PathVector &paths); - Geom::Point get_point_inside_fill(const Geom::PathVector &paths); Geom::PathVector get_all_paths_combined(); - template - Geom::Path get_the_most_path_by_area(const Geom::PathVector &paths); + template + SPItem *get_top_item_that_intersects(const Path &path); }; } \ No newline at end of file -- GitLab From 795c01f2f514063d2cbea53c36d8f1093189ca4a Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 7 Jun 2021 06:49:42 +0200 Subject: [PATCH 021/235] another unsuccessful trial... tried to replace each 2 intersecting shapes with their differences and their intersection. not working properly due to approximations. result shapes (3 shapes that are not intersecting) are not very accurate and might intersect again. one solution is to try to remove very small results. not gonna do it now. --- src/helper/NonOverlappingPathsBuilder.cpp | 109 ++++++++++++++++------ src/object/object-set.cpp | 2 +- 2 files changed, 79 insertions(+), 32 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index bcafc974b4..62c06442f8 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -175,32 +175,78 @@ Geom::PathVector NonOverlappingPathsBuilder::get_bounds_path_vector(const Geom:: return get_bounds_path_vector(bounds); } -void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() +static double path_area_non_self_intersecting_no_curves(const Geom::Path &path) +{ + // This function doesn't take curves into consideration. + auto nodes = path.nodes(); + int j = nodes.size() - 1; + + double area = 0; + for (int i = 1; i < nodes.size(); i++) { + auto x1 = nodes[i].x(); + auto x2 = nodes[j].x(); + auto y1 = nodes[i].y(); + auto y2 = nodes[j].y(); + area += (x1 + x2) * (y1 - y2); + j = i; + } + area /= 2; + + return std::abs(area); +} + +static Geom::PathVector remove_paths_with_small_area_from_pathvec(const Geom::PathVector &paths, double minimum_area) { - // TODO this way doesn't seem to work nicely with round objects - // and produces a lot of unnecessary nodes. may improve it so that - // nodes are only at points of intersection and have curves. + // FIXME may want to edit this PathVector instead of creating a new one? + // FIXME take curves into consideration. Guess this will only work with + // the current way of breaking selection into non-overlapping pieces + // since it produces a paths with no curves. Other than that, this + // function shouldn't be used until fixed. - // FIXME doesn't work well with very small round shapes. the result - // paths are wiggly. when applied on bigger shapes, works fine. + Geom::PathVector result; + for (auto &path : paths) { + auto area = path_area_non_self_intersecting_no_curves(path); + if (area >= minimum_area) { + result.push_back(path); + } + } - Geom::PathVector a = get_all_paths_combined(); - Geom::PathVector bounds_expanded = get_bounds_path_vector(a); + return result; +} - std::vector result = sp_pathvector_cutboolop( - a, - bounds_expanded, - fill_nonZero, - fill_nonZero - ); +void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() +{ + for (int i = 0; i < original_paths_sorted.size(); i++) { + for (int j = i + 1; j < original_paths_sorted.size(); j++) { + auto &a = original_paths_sorted[i]; + auto &b = original_paths_sorted[j]; + auto intersection = sp_pathvector_boolop(b, a, bool_op_inters, fill_nonZero, fill_nonZero); + intersection = remove_paths_with_small_area_from_pathvec(intersection, 0.05); + if (!intersection.empty()) { + auto diff1 = sp_pathvector_boolop(a, b, bool_op_diff, fill_nonZero, fill_nonZero); + diff1 = remove_paths_with_small_area_from_pathvec(diff1, 0.05); + auto diff2 = sp_pathvector_boolop(b, a, bool_op_diff, fill_nonZero, fill_nonZero); + diff2 = remove_paths_with_small_area_from_pathvec(diff2, 0.05); + original_paths_sorted.erase(original_paths_sorted.begin() + i); + original_paths_sorted.erase(original_paths_sorted.begin() + j); + original_paths_sorted.push_back(intersection); + if (!diff1.empty()) { + original_paths_sorted.push_back(diff1); + } + if (!diff2.empty()) { + original_paths_sorted.push_back(diff2); + } + j--; + } + } + } - int n = result.size(); + int n = original_paths_sorted.size(); result_nodes.clear(); result_nodes.resize(n); - // discard first path. - for (int i = 1; i < n; i++) { - result_nodes[i] = draw_on_canvas(result[i]); + for (int i = 0; i < n; i++) { + result_nodes[i] = draw_on_canvas(original_paths_sorted[i]); } } @@ -278,12 +324,13 @@ XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path) after = original_items_sorted.back()->getRepr()->prev(); } - SPItem *original_item = get_top_item_that_intersects(path); - if (!original_item && !fill_holes) { - return nullptr; - } +// SPItem *original_item = get_top_item_that_intersects(path); +// if (!original_item && !fill_holes) { +// return nullptr; +// } - return draw_on_canvas(path, original_item, parent, after); +// return draw_on_canvas(path, original_item, parent, after); + return draw_on_canvas(path, nullptr, parent, after); } void NonOverlappingPathsBuilder::set_paths_from_sorted_items() @@ -321,14 +368,14 @@ void NonOverlappingPathsBuilder::construct_paths_for_matching() fill_nonZero, fill_nonZero ); - - for (int j = 1; j < result.size(); j++) { - paths_for_matching[i].( - paths_for_matching.end(), - result[j].begin(), - result[j].end() - ); - } +// +// for (int j = 1; j < result.size(); j++) { +// paths_for_matching[i].( +// paths_for_matching.end(), +// result[j].begin(), +// result[j].end() +// ); +// } } } diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 35c89b25a2..fcab37eaa2 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -530,7 +530,7 @@ double path_area_non_self_intersecting_no_curves(const Geom::Path &path) return std::abs(area); } -Geom::PathVector remove_paths_with_small_area_from_pathvec(const Geom::PathVector &paths, double minimum_area) +static Geom::PathVector remove_paths_with_small_area_from_pathvec(const Geom::PathVector &paths, double minimum_area) { // FIXME may want to edit this PathVector instead of creating a new one? // FIXME take curves into consideration. Guess this will only work with -- GitLab From 5a5f7e99728a4abbb3839381de31ea6101eda222 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 13 Jun 2021 09:50:29 +0200 Subject: [PATCH 022/235] a trial to do it by replacing each pair by (diff - intersection - diff). There is a bug that doesn't make it work correctly. This is just a checkpoint for later. --- src/helper/NonOverlappingPathsBuilder.cpp | 329 +++++++++++++++++++--- src/helper/NonOverlappingPathsBuilder.h | 2 +- 2 files changed, 285 insertions(+), 46 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 62c06442f8..4f3411c12e 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -89,32 +89,33 @@ Geom::PathVector NonOverlappingPathsBuilder::get_all_paths_combined() return result; } -bool points_equal(const Geom::Point &a, const Geom::Point &b, double epsilon = 0.001) { -// std::cout << "Diff: " << abs(a.x() - b.x()) << ", " << abs(a.y() - b.y()) << '\n'; -// std::cout << "X1: " << a.x() << ", "; -// std::cout << "X2: " << b.x() << ", "; -// std::cout << "Y1: " << a.y() << ", "; -// std::cout << "Y2: " << b.y() << "\n"; +bool points_equal(const Geom::Point &a, const Geom::Point &b, double epsilon = 0.001) +{ + // std::cout << "Diff: " << abs(a.x() - b.x()) << ", " << abs(a.y() - b.y()) << '\n'; + // std::cout << "X1: " << a.x() << ", "; + // std::cout << "X2: " << b.x() << ", "; + // std::cout << "Y1: " << a.y() << ", "; + // std::cout << "Y2: " << b.y() << "\n"; return abs(a.x() - b.x()) < epsilon && abs(a.y() - b.y()) < epsilon; } bool point_on_edge(const Geom::PathVector &paths, const Geom::Point &point) { auto nodes = paths.nodes(); -// std::cout << "Nodes count: " << nodes.size() << '\n'; - int idx = 1; + // std::cout << "Nodes count: " << nodes.size() << '\n'; + // int idx = 1; for (auto &node : nodes) { -// std::cout << "Point " << idx++ << ":\n"; + // std::cout << "Point " << idx++ << ":\n"; if (points_equal(point, node)) { std::cout << "Match of " << node << " and " << point << ".\n"; return true; } } -// std::cout << "No match at all\n"; + // std::cout << "No match at all\n"; return false; } -//SPItem *NonOverlappingPathsBuilder::get_top_item_with_points_on_edges(const std::vector &points) +// SPItem *NonOverlappingPathsBuilder::get_top_item_with_points_on_edges(const std::vector &points) //{ // for (int i = 0; i < original_paths_sorted.size(); i++) { // auto &path = original_paths_sorted[i]; @@ -195,6 +196,16 @@ static double path_area_non_self_intersecting_no_curves(const Geom::Path &path) return std::abs(area); } +void print_bounds_and_nodes_count(const Geom::PathVector &path) { + auto bounds = path.boundsFast(); + std::cout << "Nodes count: " << path.nodes().size() << ", "; + std::cout << "Bounds: "; + std::cout << "Top: " << bounds->top() << ", "; + std::cout << "Bottom: " << bounds->bottom() << ", "; + std::cout << "Left: " << bounds->left() << ", "; + std::cout << "Right: " << bounds->right() << "\n"; +} + static Geom::PathVector remove_paths_with_small_area_from_pathvec(const Geom::PathVector &paths, double minimum_area) { // FIXME may want to edit this PathVector instead of creating a new one? @@ -208,45 +219,283 @@ static Geom::PathVector remove_paths_with_small_area_from_pathvec(const Geom::Pa auto area = path_area_non_self_intersecting_no_curves(path); if (area >= minimum_area) { result.push_back(path); + } else { +// std::cout << "Deleted a path with an area of " << area << ".\n"; + // print_bounds_and_nodes_count(path); + } + } + + return result; +} + +static Geom::PathVector remove_lines_from_pathvec(const Geom::PathVector &paths) +{ + Geom::PathVector result; + for (auto &path : paths) { + if (path.nodes().size() > 2) { + result.push_back(path); + } + } + + return result; +} + +// removes a line that is part of the path. a line that starts at point x +// and keeps extending as a line (possibly multiple nodes are involved) +// and ends at the same point (or close). +//static Geom::Path remove_loop_lines_from_path(const Geom::Path &path) +//{ +// // FIXME not working as expected... +// Geom::Path result; +// result.setStitching(true); +// std::vector curves; +// for (auto &curve : path) { +// curves.push_back(curve.duplicate()); +// } +// int n = curves.size(); +// for (int i = 1; i < n; i++) { +// for (int j = n - 2; j > i; j--) { +// if (points_equal(curves[i]->initialPoint(), curves[j]->finalPoint(), 0.001)) { +// Geom::Path temp; +// temp.setStitching(true); +// for (int x = i; x <= j; x++) { +// temp.append(curves[i]->duplicate()); +// } +// if (path_area_non_self_intersecting_no_curves(temp) < 0.1) { +// result.append(curves[i]->duplicate()); +// i = j; +// break; +// } +// } +// } +// result.append(curves[i]->duplicate()); +// } +// +// for (Geom::Curve *curve : curves) { +// delete curve; +// } +// +// return result; +//} +// +//static Geom::PathVector remove_loop_lines_from_pathvec(const Geom::PathVector &paths) +//{ +// Geom::PathVector result; +// for (auto &path : paths) { +// auto new_path = remove_loop_lines_from_path(path); +// if (!path.empty()) { +// result.push_back(new_path); +// } +// } +// return result; +//} + +double get_bounding_area(const Geom::PathVector &path) +{ + return path.boundsFast()->area(); +} + +template +bool is_intersecting(const Path1 &a, const Path2 &b) { + for (auto &node : b.nodes()) { + if (a.winding(node)) { + return true; + } + } + for (auto &node : a.nodes()) { + if (b.winding(node)) { + return true; + } + } + return false; +} + +std::vector break_apart_non_touching(Geom::PathVector &paths) +{ + std::vector visited(paths.size()); + std::vector result; + + int n = paths.size(); + for (int i = n - 1; i >= 0; i--) { + if (visited[i]) { + continue; + } + result.emplace_back(paths[i]); + visited[i] = true; + auto &curr = result.back(); + for (int j = 0; j < n; j++) { + if (visited[j]) { + continue; + } + if (is_intersecting(curr, paths[j])) { + visited[j] = true; + curr.push_back(paths[j]); + } } } return result; } + +class PathHelper +{ + static int counter; + + void put_operations(const PathHelper &from, PathHelper &to) + { + to.operations.insert(from.operations.begin(), from.operations.end()); + } + + void clean(Geom::PathVector &paths) + { + double max_area = 0; + for (auto &path : paths) { + max_area = std::max(max_area, path_area_non_self_intersecting_no_curves(path)); + } + paths = remove_lines_from_pathvec(paths); + // paths = remove_loop_lines_from_pathvec(paths); + paths = remove_paths_with_small_area_from_pathvec(paths, max_area/100); +// paths = remove_paths_with_small_area_from_pathvec(paths, 0.5); + } + +public: + Geom::PathVector paths; + SPItem *item; + std::set operations; + + bool part_of_operation(int operation) { return operations.find(operation) != operations.end(); } + + bool share_an_operation_with(const PathHelper &path) + { + for (auto operation : path.operations) { + if (part_of_operation(operation)) { + return true; + } + } + return false; + } + + std::vector break_into_non_overlapping_pieces(const PathHelper &path) + { + // FIXME you can't rely on the current area calculation method. make it work with curves. + assert(!share_an_operation_with(path)); + + PathHelper intersection; + intersection.paths = sp_pathvector_boolop(paths, path.paths, bool_op_inters, fill_nonZero, fill_nonZero); + clean(intersection.paths); + + if (intersection.paths.empty()) { + return {}; + } + + intersection.item = sp_item_repr_compare_position_bool(path.item, this->item) ? this->item : path.item; + put_operations(*this, intersection); + put_operations(path, intersection); + intersection.operations.insert(counter); + + PathHelper diff1; + diff1.item = path.item; + diff1.paths = sp_pathvector_boolop(paths, path.paths, bool_op_diff, fill_nonZero, fill_nonZero); + put_operations(path, diff1); + diff1.operations.insert(counter); + clean(diff1.paths); + + PathHelper diff2; + diff2.item = this->item; + diff2.paths = sp_pathvector_boolop(path.paths, paths, bool_op_diff, fill_nonZero, fill_nonZero); + put_operations(*this, diff2); + diff2.operations.insert(counter); + clean(diff2.paths); + + counter++; + + std::vector result = {intersection, diff1, diff2}; + + for (int i = 0; i < result.size(); i++) { + if (result[i].paths.empty()) { + result.erase(result.begin() + i); + i--; + } + } + + std::vector break_apart_non_touching(std::vector &paths); + + return break_apart_non_touching(result); + } +}; + +std::vector break_apart_non_touching(std::vector &paths) +{ + std::vector result; + for (auto &path : paths) { + auto broken = break_apart_non_touching(path.paths); + for (auto &broken_path : broken) { + result.push_back({broken_path, path.item, path.operations}); + } + } + return result; +} + +int PathHelper::counter = 0; + void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() { + std::vector paths; for (int i = 0; i < original_paths_sorted.size(); i++) { - for (int j = i + 1; j < original_paths_sorted.size(); j++) { - auto &a = original_paths_sorted[i]; - auto &b = original_paths_sorted[j]; - auto intersection = sp_pathvector_boolop(b, a, bool_op_inters, fill_nonZero, fill_nonZero); - intersection = remove_paths_with_small_area_from_pathvec(intersection, 0.05); - if (!intersection.empty()) { - auto diff1 = sp_pathvector_boolop(a, b, bool_op_diff, fill_nonZero, fill_nonZero); - diff1 = remove_paths_with_small_area_from_pathvec(diff1, 0.05); - auto diff2 = sp_pathvector_boolop(b, a, bool_op_diff, fill_nonZero, fill_nonZero); - diff2 = remove_paths_with_small_area_from_pathvec(diff2, 0.05); - original_paths_sorted.erase(original_paths_sorted.begin() + i); - original_paths_sorted.erase(original_paths_sorted.begin() + j); - original_paths_sorted.push_back(intersection); - if (!diff1.empty()) { - original_paths_sorted.push_back(diff1); - } - if (!diff2.empty()) { - original_paths_sorted.push_back(diff2); + paths.push_back({original_paths_sorted[i], original_items_sorted[i], {}}); + } + + for (int i = 0; i < paths.size(); i++) { + for (int j = 0; j < paths.size(); j++) { + + if (i == j) { + continue; + } + + std::cout << "Path 1:\n"; + print_bounds_and_nodes_count(paths[i].paths); + + std::cout << "Path 2:\n"; + print_bounds_and_nodes_count(paths[j].paths); + + if (paths[i].share_an_operation_with(paths[j])) { + std::cout << "Share operation/s.\n\n"; + continue; + } + + auto broken = paths[i].break_into_non_overlapping_pieces(paths[j]); + + if (broken.empty()) { + std::cout << "Don't intersect.\n\n"; + continue; + } + + std::cout << "Operations:\n"; + for (int i = 0; i < broken.size(); i++) { + std::cout << i << ": "; + for (int op : broken[i].operations) { + std::cout << op << ' '; } - j--; + std::cout << '\n'; } + std::cout << "Broken.\n\n"; + + // j must be deleted before i since i is smaller. + paths.erase(paths.begin() + j); + paths.erase(paths.begin() + i); + j--; // i is not being incremented in the next iteration. shouldn't be decremented. + + paths.insert(paths.end(), broken.begin(), broken.end()); } } - int n = original_paths_sorted.size(); + int n = paths.size(); result_nodes.clear(); result_nodes.resize(n); for (int i = 0; i < n; i++) { - result_nodes[i] = draw_on_canvas(original_paths_sorted[i]); + result_nodes[i] = draw_on_canvas(paths[i].paths, paths[i].item); } } @@ -270,16 +519,6 @@ XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, return repr; } -void print_bounds_and_nodes_count(const Geom::PathVector &path) { - auto bounds = path.boundsFast(); - std::cout << "Nodes count: " << path.nodes().size() << ", "; - std::cout << "Bounds: "; - std::cout << "Top: " << bounds->top() << ", "; - std::cout << "Bottom: " << bounds->bottom() << ", "; - std::cout << "Left: " << bounds->left() << ", "; - std::cout << "Right: " << bounds->right() << "\n"; -} - template bool point_in_path(const Path& path, Geom::Point point) { @@ -289,7 +528,7 @@ bool point_in_path(const Path& path, Geom::Point point) template SPItem *NonOverlappingPathsBuilder::get_top_item_that_intersects(const Path &path) { - print_bounds_and_nodes_count(path); + // print_bounds_and_nodes_count(path); auto nodes = path.nodes(); for (int i = 0; i < paths_for_matching.size(); i++) { @@ -311,7 +550,7 @@ SPItem *NonOverlappingPathsBuilder::get_top_item_that_intersects(const Path &pat } template // Geom::Path or Geom::PathVector -XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path) +XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, SPItem *to_copy_from) { XML::Node *item = set->topRepr(); auto parent = item->parent(); @@ -330,7 +569,7 @@ XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path) // } // return draw_on_canvas(path, original_item, parent, after); - return draw_on_canvas(path, nullptr, parent, after); + return draw_on_canvas(path, to_copy_from, parent, after); } void NonOverlappingPathsBuilder::set_paths_from_sorted_items() diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index 07221310c5..b1f5bbcc10 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -62,7 +62,7 @@ private: XML::Node *draw_on_canvas(const Path &path, const SPItem *to_copy_from, XML::Node *parent, XML::Node *after); template // Geom::Path or Geom::PathVector - XML::Node *draw_on_canvas(const Path &path); + XML::Node *draw_on_canvas(const Path &path, SPItem *to_copy_from = nullptr); SPDesktop *desktop(); void set_desktop_busy(); -- GitLab From 34a8046dfabcba70d21e8300e56ee4306a004f54 Mon Sep 17 00:00:00 2001 From: SushantAA Date: Mon, 7 Jun 2021 01:30:15 +0530 Subject: [PATCH 023/235] Aux Select : UI basic setup --- share/ui/toolbar-select.ui | 151 +++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/share/ui/toolbar-select.ui b/share/ui/toolbar-select.ui index e69de29bb2..21a180d744 100644 --- a/share/ui/toolbar-select.ui +++ b/share/ui/toolbar-select.ui @@ -0,0 +1,151 @@ + + + + + + + + + + True + + + + True + + + + + + True + app.object-rotate-90-cw + edit-select-all + Rotate 90° CW + + + + + True + app.object-rotate-90-ccw + edit-select-all-layers + Rotate 90° CCW + + + + + True + app.object-rotate-90-cw + edit-select-none + Rotate 90° CW + + + + + True + app.object-rotate-90-ccwdfsdf + selection-touch + Rotate 90° CCWsdf + + + + + + True + + + + + + True + app.object-rotate-90-ccw + object-rotate-left + Rotate 90° CCW + + + + + True + app.object-rotate-90-cw + object-rotate-right + Rotate 90° CW + + + + + + True + + + + + + True + app.object-flip-horizontal + object-flip-horizontal + Flip _Horizontal + + + + + True + app.object-flip-vertical + object-flip-vertical + Flip _Vertical + + + + + + True + + + + + + True + app.selection-top + selection-top + Raise to _Top + + + + + True + app.selection-raise + selection-raise + _Raise + + + + + True + app.selection-lower + selection-lower + _Lower + + + + + True + app.selection-bottom + selection-bottom + Lower to _Bottom + + + + + + True + + + + + + True + True + + + + + + -- GitLab From 8c15bfd7f961d02b555ee63fec4774ba26f6ea25 Mon Sep 17 00:00:00 2001 From: SushantAA Date: Mon, 7 Jun 2021 01:57:52 +0530 Subject: [PATCH 024/235] Menubar Object and Command Palette hint squash commit Converting menubar object to Gio::Actions and Command Palette hints --- share/keys/inkscape.xml | 84 ++++++--- share/ui/menu-object.ui | 206 +++++++++++++++++++++++ src/CMakeLists.txt | 11 +- src/actions/actions-edit.cpp | 65 +++++++ src/actions/actions-edit.h | 9 + src/actions/actions-file.cpp | 11 +- src/actions/actions-hide-lock.cpp | 66 ++++++++ src/actions/actions-hide-lock.h | 9 + src/actions/actions-hint-data.cpp | 30 ++++ src/actions/actions-hint-data.h | 26 +++ src/actions/actions-object-align.cpp | 11 +- src/actions/actions-object.cpp | 146 ++++++++++++++-- src/actions/actions-output.cpp | 36 +++- src/actions/actions-selection-object.cpp | 98 +++++++++++ src/actions/actions-selection-object.h | 8 + src/actions/actions-selection.cpp | 26 +-- src/actions/actions-transform.cpp | 12 +- src/inkscape-application.cpp | 20 ++- src/inkscape-application.h | 5 +- src/inkscape-window.cpp | 5 + src/ui/desktop/menubar.cpp | 31 ++-- src/ui/dialog/command-palette.cpp | 22 ++- src/ui/dialog/dialog-container.cpp | 7 +- 23 files changed, 855 insertions(+), 89 deletions(-) create mode 100644 share/ui/menu-object.ui create mode 100644 src/actions/actions-edit.cpp create mode 100644 src/actions/actions-edit.h create mode 100644 src/actions/actions-hide-lock.cpp create mode 100644 src/actions/actions-hide-lock.h create mode 100644 src/actions/actions-hint-data.cpp create mode 100644 src/actions/actions-hint-data.h create mode 100644 src/actions/actions-selection-object.cpp create mode 100644 src/actions/actions-selection-object.h diff --git a/share/keys/inkscape.xml b/share/keys/inkscape.xml index 5b05572e51..5023fb8139 100644 --- a/share/keys/inkscape.xml +++ b/share/keys/inkscape.xml @@ -188,21 +188,21 @@ override) the bindings in the main default.xml. - + - - + - - + - - + - - + @@ -222,30 +222,30 @@ override) the bindings in the main default.xml. - - + - - + - + - + - + - + - - + @@ -387,7 +387,7 @@ override) the bindings in the main default.xml. - + @@ -395,19 +395,19 @@ override) the bindings in the main default.xml. - + - + - + - + @@ -530,4 +530,34 @@ override) the bindings in the main default.xml. - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/share/ui/menu-object.ui b/share/ui/menu-object.ui new file mode 100644 index 0000000000..788b4f514c --- /dev/null +++ b/share/ui/menu-object.ui @@ -0,0 +1,206 @@ + + + + + + + + + Object... + win.dialog-open + Objects + + + + + _Fill and Stroke... + win.dialog-open + FillStroke + + + + _Object Properties... + win.dialog-open + ObjectProperties + + + + S_ymbols... + win.dialog-open + Symbols + + + + _Paint Servers... + win.dialog-open + PaintServers + + + + _Selectors and CSS... + win.dialog-open + Objects + + + + + + _Group + app.select-object-group + + + _Ungroup + app.select-object-ungroup + + + _Pop Selected Objects out of Group + app.select-object-ungroup-pop + + + + Cli_p +
+ + _Set + app.object-set + + + _Set Inverse (LPE) + app.object-set-inverse + + + _Release + app.object-release + +
+
+ + + Mas_k +
+ + _Set + app.object-set-mask + + + _Set Inverse (LPE) + app.object-set-inverse-mask + + + _Release + app.object-release-mask + +
+
+ + + + Patter_n +
+ + Objects to Patter_n + win.object-to-pattern + + + Pattern to _Objects + win.pattern-to-object + +
+
+ + + Objects to _Marker + win.object-to-marker + + + Objects to Gu_ides + win.object-to-guides + + + + + Raise to _Top + app.selection-top + selection-top + + + + _Raise + app.selection-raise + selection-raise + + + + _Lower + app.selection-lower + selection-lower + + + + Lower to _Bottom + app.selection-bottom + selection-bottom + + + + + + Rotate 90° CW + app.object-rotate-90-cw + object-rotate-right + + + Rotate 90° CCW + app.object-rotate-90-ccw + object-rotate-left + + + Flip _Horizontal + app.object-flip-horizontal + object-flip-horizontal + + + Flip _Vertical + app.object-flip-vertical + object-flip-vertical + + + + + + Unhide All + win.unhide-all + + + + Unlock All + win.unlock-all + + + + + Transfor_m... + win.dialog-open + Transform + + + + _Align and Distribute... + win.dialog-open + AlignDistribute + + + + _Arrange... + win.dialog-open + Arrange + + + + ------object action end--------- + + + +
+ + \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cbb16235b7..9a19394cb7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -201,12 +201,19 @@ set(inkscape_SRC actions/actions-extra-data.h actions/actions-extra-data.cpp + actions/actions-hint-data.h + actions/actions-hint-data.cpp + actions/actions-canvas-mode.h actions/actions-canvas-mode.cpp actions/actions-canvas-snapping.h actions/actions-canvas-snapping.cpp actions/actions-canvas-transform.h actions/actions-canvas-transform.cpp + actions/actions-hide-lock.h + actions/actions-hide-lock.cpp + actions/actions-edit.h + actions/actions-edit.cpp actions/actions-base.h actions/actions-dialogs.cpp actions/actions-dialogs.h @@ -221,6 +228,8 @@ set(inkscape_SRC actions/actions-object-align.cpp actions/actions-output.h actions/actions-output.cpp + actions/actions-selection-object.h + actions/actions-selection-object.cpp actions/actions-selection.h actions/actions-selection.cpp actions/actions-tools.h @@ -405,4 +414,4 @@ if(WIN32) endif() if(BUILD_SHARED_LIBS) install(TARGETS inkscape_base LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/inkscape") -endif() +endif() \ No newline at end of file diff --git a/src/actions/actions-edit.cpp b/src/actions/actions-edit.cpp new file mode 100644 index 0000000000..6204b4c78f --- /dev/null +++ b/src/actions/actions-edit.cpp @@ -0,0 +1,65 @@ +#include + +#include // Not ! To eventually allow a headless version! +#include + +#include "actions-edit.h" +#include "inkscape-application.h" +#include "inkscape-window.h" +#include "desktop.h" + +void +object_to_pattern(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->tile(); +} + +void +pattern_to_object(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->untile(); +} + +void +object_to_marker(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->toMarker(); +} + +void +object_to_guides(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->toGuides(); +} + +std::vector> raw_data_edit = +{ + // clang-format off + {"win.object-to-pattern", N_("Objects to Pattern"), "Edit", N_("Convert selection to a rectangle with tiled pattern fill") }, + {"win.pattern-to-object", N_("Pattern to Objects"), "Edit", N_("Extract objects from a tiled pattern fill") }, + {"win.object-to-marker", N_("Objects to Marker"), "Edit", N_("Convert selection to a line marker") }, + {"win.object-to-guides", N_("Objects to Guides"), "Edit", N_("Convert selected objects to a collection of guidelines aligned with their edges") } + // clang-format on +}; + +void +add_actions_edit(InkscapeWindow* win) +{ + // clang-format off + win->add_action( "object-to-pattern", sigc::bind(sigc::ptr_fun(&object_to_pattern), win)); + win->add_action( "pattern-to-object", sigc::bind(sigc::ptr_fun(&pattern_to_object), win)); + win->add_action( "object-to-marker", sigc::bind(sigc::ptr_fun(&object_to_marker), win)); + win->add_action( "object-to-guides", sigc::bind(sigc::ptr_fun(&object_to_guides), win)); + // clang-format on + + auto app = InkscapeApplication::instance(); + if (!app) { + std::cerr << "add_actions_edit: no app!" << std::endl; + return; + } + app->get_action_extra_data().add_data(raw_data_edit); +} \ No newline at end of file diff --git a/src/actions/actions-edit.h b/src/actions/actions-edit.h new file mode 100644 index 0000000000..4a1825dfda --- /dev/null +++ b/src/actions/actions-edit.h @@ -0,0 +1,9 @@ +#ifndef INK_ACTIONS_EDIT_H +#define INK_ACTIONS_EDIT_H + +class InkscapeWindow; +class InkscapeApplication; + +void add_actions_edit(InkscapeWindow* win); + +#endif // INK_ACTIONS_EDIT_H \ No newline at end of file diff --git a/src/actions/actions-file.cpp b/src/actions/actions-file.cpp index 79c6d74137..1d9d135f60 100644 --- a/src/actions/actions-file.cpp +++ b/src/actions/actions-file.cpp @@ -103,6 +103,14 @@ std::vector> raw_data_file = // clang-format on }; +std::vector> hint_data_file = +{ + // clang-format off + {"app.file-open", N_("Give String input for File name")}, + {"app.file-new", N_("Give String input for File name")} + // clang-format on +}; + void add_actions_file(InkscapeApplication* app) { @@ -126,6 +134,7 @@ add_actions_file(InkscapeApplication* app) #endif app->get_action_extra_data().add_data(raw_data_file); + app->get_action_hint_data().add_data(hint_data_file); } @@ -138,4 +147,4 @@ add_actions_file(InkscapeApplication* app) fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : \ No newline at end of file diff --git a/src/actions/actions-hide-lock.cpp b/src/actions/actions-hide-lock.cpp new file mode 100644 index 0000000000..4e536a8d9f --- /dev/null +++ b/src/actions/actions-hide-lock.cpp @@ -0,0 +1,66 @@ +#include + +#include // Not ! To eventually allow a headless version! +#include + +#include "actions-hide-lock.h" +#include "inkscape-application.h" +#include "document-undo.h" +#include "inkscape-window.h" +#include "desktop.h" +#include "selection-chemistry.h" + +enum{ + UNLOCK_ALL, + UNHIDE_ALL + /* + Currently not in use but later it might be + + UNLOCK_ALL_IN_ALL_LAYERS, + UNHIDE_ALL_IN_ALL_LAYERS + */ +}; + +void +hide_lock_unhide_all(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + SPDocument *doc = dt->getDocument(); + unhide_all(dt); + Inkscape::DocumentUndo::done(doc, UNHIDE_ALL, _("Unhide all objects in the current layer")); +} + +void +hide_lock_unlock_all(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + SPDocument *doc = dt->getDocument(); + unlock_all(dt); + Inkscape::DocumentUndo::done(doc, UNLOCK_ALL, _("Unlock all objects in the current layer")); +} + +std::vector> raw_data_hide_lock = +{ + // clang-format off + {"win.unhide-all", N_("Unhide All"), "Hide and Lock", N_("Unhide all objects in the current layer") }, + {"win.unlock-all", N_("Unlock All"), "Hide and Lock", N_("Unlock all objects in the current layer") } + // clang-format on +}; + +void +add_actions_hide_lock(InkscapeWindow* win) +{ + + // clang-format off + win->add_action( "unhide-all", sigc::bind(sigc::ptr_fun(&hide_lock_unhide_all), win)); + win->add_action( "unlock-all", sigc::bind(sigc::ptr_fun(&hide_lock_unlock_all), win)); + // clang-format on + + + auto app = InkscapeApplication::instance(); + if (!app) { + std::cerr << "add_actions_hide_lock: no app!" << std::endl; + return; + } + app->get_action_extra_data().add_data(raw_data_hide_lock); +} \ No newline at end of file diff --git a/src/actions/actions-hide-lock.h b/src/actions/actions-hide-lock.h new file mode 100644 index 0000000000..2ec85c9365 --- /dev/null +++ b/src/actions/actions-hide-lock.h @@ -0,0 +1,9 @@ +#ifndef INK_ACTIONS_HIDE_LOCK_H +#define INK_ACTIONS_HIDE_LOCK_H + +class InkscapeWindow; +class InkscapeApplication; + +void add_actions_hide_lock(InkscapeWindow* win); + +#endif // INK_ACTIONS_HIDE_LOCK_H \ No newline at end of file diff --git a/src/actions/actions-hint-data.cpp b/src/actions/actions-hint-data.cpp new file mode 100644 index 0000000000..b206d937fa --- /dev/null +++ b/src/actions/actions-hint-data.cpp @@ -0,0 +1,30 @@ +#include "actions-hint-data.h" +#include + +std::vector +InkActionHintData::get_actions() +{ + std::vector action_names; + for (auto hint : data) { + action_names.emplace_back(hint.first); + } + return action_names; +} + +Glib::ustring +InkActionHintData::get_tooltip_hint_for_action(Glib::ustring const &action_name, bool translated) { + + Glib::ustring value; + auto search = data.find(action_name); + if (search != data.end()) { + value = translated ? _(search->second.c_str()) : search->second; + } + return value; +} + +void +InkActionHintData::add_data(std::vector> &hint_data) { + for (auto hint : hint_data) { + data.emplace(hint[0], hint[1]); // action name , hint + } +} \ No newline at end of file diff --git a/src/actions/actions-hint-data.h b/src/actions/actions-hint-data.h new file mode 100644 index 0000000000..1312879ad7 --- /dev/null +++ b/src/actions/actions-hint-data.h @@ -0,0 +1,26 @@ +#ifndef INK_ACTIONS_HINT_DATA_H +#define INK_ACTIONS_HINT_DATA_H + +#include +#include +#include +#include +#include +#include + +class InkActionHintData +{ +public: + InkActionHintData() = default ; + + std::vector get_actions(); + + void add_data(std::vector> &raw_data); + + Glib::ustring get_tooltip_hint_for_action(Glib::ustring const &action_name, bool translated = true); + +private: + std::map data; +}; + +#endif // INK_ACTIONS_HINT_DATA_H \ No newline at end of file diff --git a/src/actions/actions-object-align.cpp b/src/actions/actions-object-align.cpp index 8b873293e4..f3a901a73a 100644 --- a/src/actions/actions-object-align.cpp +++ b/src/actions/actions-object-align.cpp @@ -360,6 +360,14 @@ std::vector> raw_data_object_align = // clang-format on }; +std::vector> hint_data_object_align = +{ + // clang-format off + {"app.object-align", N_("Give String input for Relativity Alignment")}, + {"app.object-distribute", N_("Give String input for Distribution (hgap, left, hcenter, right, vgap, top, vcenter, bottom)")} + // clang-format on +}; + void add_actions_object_align(InkscapeApplication* app) { @@ -378,6 +386,7 @@ add_actions_object_align(InkscapeApplication* app) #endif app->get_action_extra_data().add_data(raw_data_object_align); + app->get_action_hint_data().add_data(hint_data_object_align); } @@ -390,4 +399,4 @@ add_actions_object_align(InkscapeApplication* app) fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : \ No newline at end of file diff --git a/src/actions/actions-object.cpp b/src/actions/actions-object.cpp index e547901f62..f8a0363eae 100644 --- a/src/actions/actions-object.cpp +++ b/src/actions/actions-object.cpp @@ -21,7 +21,17 @@ #include "inkscape.h" // Inkscape::Application #include "selection.h" // Selection #include "path/path-simplify.h" - +#include "live_effects/lpe-powerclip.h" +#include "live_effects/lpe-powermask.h" + +enum{ + OBJECT_SET_INVERSE_CLIPPATH, + OBJECT_UNSET_CLIPPATH, + OBJECT_SET_INVERSE_MASK, + OBJECT_UNSET_MASK, + OBJECT_FLIP_HORIZONTAL, + OBJECT_FLIP_VERTICAL +}; // No sanity checking is done... should probably add. void @@ -98,6 +108,86 @@ object_unlink_clones(InkscapeApplication *app) selection->unlink(); } +void +set_clip(InkscapeApplication *app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + selection->setMask(true, false); +} + +void +object_set_inverse(InkscapeApplication *app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + selection->setMask(true, false); + Inkscape::LivePathEffect::sp_inverse_powerclip(app->get_active_selection()); + Inkscape::DocumentUndo::done(app->get_active_document(), OBJECT_SET_INVERSE_CLIPPATH, _("_Set Inverse (LPE)")); +} + +void +object_release(InkscapeApplication *app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + Inkscape::LivePathEffect::sp_remove_powerclip(app->get_active_selection()); + selection->unsetMask(true); + Inkscape::DocumentUndo::done(app->get_active_document(),OBJECT_UNSET_CLIPPATH , _("Release clipping path")); +} + +void +set_mask(InkscapeApplication *app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + selection->setMask(false, false); +} + +void +object_set_inverse_mask(InkscapeApplication *app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + selection->setMask(false, false); + Inkscape::LivePathEffect::sp_inverse_powermask(app->get_active_selection()); + Inkscape::DocumentUndo::done(app->get_active_document(), OBJECT_SET_INVERSE_MASK, _("_Set Inverse (LPE)")); +} + +void +object_release_mask(InkscapeApplication *app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + Inkscape::LivePathEffect::sp_remove_powermask(app->get_active_selection()); + selection->unsetMask(false); + Inkscape::DocumentUndo::done(app->get_active_document(),OBJECT_UNSET_MASK , _("Release mask")); +} + +void +object_rotate_90_cw(InkscapeApplication *app){ + Inkscape::Selection *selection = app->get_active_selection(); + selection->rotate90(false); +} + +void +object_rotate_90_ccw(InkscapeApplication *app){ + Inkscape::Selection *selection = app->get_active_selection(); + selection->rotate90(true); +} + +void +object_flip_horizontal(InkscapeApplication *app){ + Inkscape::Selection *selection = app->get_active_selection(); + Geom::Point center; + center = *selection->center(); + selection->setScaleRelative(center, Geom::Scale(-1.0, 1.0)); + Inkscape::DocumentUndo::done(app->get_active_document(), OBJECT_FLIP_HORIZONTAL,_("Flip horizontally")); +} + +void +object_flip_vertical(InkscapeApplication *app){ + Inkscape::Selection *selection = app->get_active_selection(); + Geom::Point center; + center = *selection->center(); + selection->setScaleRelative(center, Geom::Scale(1.0, -1.0)); + Inkscape::DocumentUndo::done(app->get_active_document(), OBJECT_FLIP_VERTICAL,_("Flip vertically")); +} + void object_to_path(InkscapeApplication *app) @@ -137,16 +227,36 @@ object_simplify_path(InkscapeApplication *app) selection->simplifyPaths(); } - std::vector> raw_data_object = { // clang-format off - {"app.object-set-attribute", N_("Set Attribute"), "Object", N_("Set or update an attribute of selected objects; usage: object-set-attribute:attribute name, attribute value;")}, - {"app.object-set-property", N_("Set Property"), "Object", N_("Set or update a property on selected objects; usage: object-set-property:property name, property value;")}, - {"app.object-unlink-clones", N_("Unlink Clones"), "Object", N_("Unlink clones and symbols") }, - {"app.object-to-path", N_("Object To Path"), "Object", N_("Convert shapes to paths") }, - {"app.object-stroke-to-path", N_("Stroke to Path"), "Object", N_("Convert strokes to paths") }, - {"app.object-simplify-path", N_("Simplify Path"), "Object", N_("Simplify paths, reducing node counts") } + {"app.object-set-attribute", N_("Set Attribute"), "Object", N_("Set or update an attribute of selected objects; usage: object-set-attribute:attribute name, attribute value;") }, + {"app.object-set-property", N_("Set Property"), "Object", N_("Set or update a property on selected objects; usage: object-set-property:property name, property value;") }, + {"app.object-unlink-clones", N_("Unlink Clones"), "Object", N_("Unlink clones and symbols") }, + {"app.object-to-path", N_("Object To Path"), "Object", N_("Convert shapes to paths") }, + {"app.object-stroke-to-path", N_("Stroke to Path"), "Object", N_("Convert strokes to paths") }, + {"app.object-simplify-path", N_("Simplify Path"), "Object", N_("Simplify paths, reducing node counts") }, + /* Clip */ + {"app.object-set", N_("Object Clip Set"), "Object", N_("Apply clipping path to selection (using the topmost object as clipping path)") }, + {"app.object-set-inverse", N_("Object Clip Set Inverse"), "Object", N_("Apply inverse clipping path to selection (using the topmost object as clipping path)") }, + {"app.object-release", N_("Object Clip Release"), "Object", N_("Remove clipping path from selection") }, + /* Mask */ + {"app.object-set-mask", N_("Object Mask Set"), "Object", N_("Apply mask to selection (using the topmost object as mask)") }, + {"app.object-set-inverse-mask", N_("Object Mask Set Inverse"), "Object", N_("Set Inverse (LPE)") }, + {"app.object-release-mask", N_("Object Mask Release"), "Object", N_("Remove mask from selection") }, + /* Rotate */ + {"app.object-rotate-90-cw", N_("Object Rotate 90"), "Object", N_("Rotate selection 90° clockwise") }, + {"app.object-rotate-90-ccw", N_("Object Rotate 90 CCW"), "Object", N_("Rotate selection 90° counter-clockwise") }, + {"app.object-flip-horizontal", N_("Object Flip Horizontal"), "Object", N_("Flip selected objects horizontally") }, + {"app.object-flip-vertical", N_("Object Flip Vertical"), "Object", N_("Flip selected objects vertically") } + // clang-format on +}; + +std::vector> hint_data_object = +{ + // clang-format off + {"app.object-set-attribute", N_("Give two String input for Attribute Name, Attribute Value") }, + {"app.object-set-property", N_("Give two String input for Property Name, Property Value") } // clang-format on }; @@ -164,17 +274,31 @@ add_actions_object(InkscapeApplication* app) #if GLIB_CHECK_VERSION(2, 52, 0) // clang-format off - gapp->add_action_with_parameter( "object-set-attribute", String, sigc::bind(sigc::ptr_fun(&object_set_attribute), app)); - gapp->add_action_with_parameter( "object-set-property", String, sigc::bind(sigc::ptr_fun(&object_set_property), app)); + gapp->add_action_with_parameter( "object-set-attribute", String, sigc::bind(sigc::ptr_fun(&object_set_attribute), app)); + gapp->add_action_with_parameter( "object-set-property", String, sigc::bind(sigc::ptr_fun(&object_set_property), app)); gapp->add_action( "object-unlink-clones", sigc::bind(sigc::ptr_fun(&object_unlink_clones), app)); gapp->add_action( "object-to-path", sigc::bind(sigc::ptr_fun(&object_to_path), app)); gapp->add_action( "object-stroke-to-path", sigc::bind(sigc::ptr_fun(&object_stroke_to_path), app)); gapp->add_action( "object-simplify-path", sigc::bind(sigc::ptr_fun(&object_simplify_path), app)); + /* Clip */ + gapp->add_action( "object-set", sigc::bind(sigc::ptr_fun(&set_clip), app)); + gapp->add_action( "object-set-inverse", sigc::bind(sigc::ptr_fun(&object_set_inverse), app)); + gapp->add_action( "object-release", sigc::bind(sigc::ptr_fun(&object_release), app)); + /* Mask */ + gapp->add_action( "object-set-mask", sigc::bind(sigc::ptr_fun(&set_mask), app)); + gapp->add_action( "object-set-inverse-mask", sigc::bind(sigc::ptr_fun(&object_set_inverse_mask), app)); + gapp->add_action( "object-release-mask", sigc::bind(sigc::ptr_fun(&object_release_mask), app)); + /* Rotate */ + gapp->add_action( "object-rotate-90-cw", sigc::bind(sigc::ptr_fun(&object_rotate_90_cw), app)); + gapp->add_action( "object-rotate-90-ccw", sigc::bind(sigc::ptr_fun(&object_rotate_90_cw), app)); + gapp->add_action( "object-flip-horizontal", sigc::bind(sigc::ptr_fun(&object_flip_horizontal), app)); + gapp->add_action( "object-flip-vertical", sigc::bind(sigc::ptr_fun(&object_flip_vertical), app)); // clang-format on #endif app->get_action_extra_data().add_data(raw_data_object); + app->get_action_hint_data().add_data(hint_data_object); } @@ -187,4 +311,4 @@ add_actions_object(InkscapeApplication* app) fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : \ No newline at end of file diff --git a/src/actions/actions-output.cpp b/src/actions/actions-output.cpp index f2128e2400..acc80396f1 100644 --- a/src/actions/actions-output.cpp +++ b/src/actions/actions-output.cpp @@ -253,6 +253,39 @@ std::vector> raw_data_output = // clang-format on }; +std::vector> hint_data_output = +{ + // clang-format off + {"app.export-type", N_("Give String input for Type") }, + {"app.export-filename", N_("Give String input for File Name") }, + {"app.export-overwrite", N_("Give input 0/1 for No/Yes to Export Overwrite") }, + + {"app.export-area", N_("Give String input for Area") }, + {"app.export-area-drawing", N_("Give input 0/1 for No/Yes to Export Area Drawing") }, + {"app.export-area-page", N_("Give input 0/1 for No/Yes to Export Area Page") }, + {"app.export-margin", N_("Give Integer input for Margin") }, + {"app.export-area-snap", N_("Give input 0/1 for No/Yes to Export Area Snap") }, + {"app.export-width", N_("Give Integer input for Width") }, + {"app.export-height", N_("Give Integer input for Height") }, + + {"app.export-id", N_("Give String input for Export ID") }, + {"app.export-id-only", N_("Give input 0/1 for No/Yes to Export ID Only") }, + + {"app.export-plain-svg", N_("Give input 0/1 for No/Yes to Export Plain SVG") }, + {"app.export-dpi", N_("Give input 0/1 for No/Yes to Export DPI") }, + {"app.export-ignore-filters", N_("Give input 0/1 for No/Yes to Export Ignore Filters") }, + {"app.export-text-to-path", N_("Give input 0/1 for No/Yes to Export Text to Path") }, + {"app.export-ps-level", N_("Give Integer input for PS Level") }, + {"app.export-pdf-version", N_("Give String input for PDF Version") }, + {"app.export-latex", N_("Give input 0/1 for No/Yes to Export LaTeX") }, + {"app.export-use-hints", N_("Give input 0/1 for No/Yes to Export Use Hints") }, + {"app.export-background", N_("Give String input Background") }, + {"app.export-background-opacity", N_("Give input 0/1 for No/Yes to Background Opacity") }, + {"app.export-png-color-mode", N_("Give String input PNG Color Mode") } + // clang-format on +}; + + void add_actions_output(InkscapeApplication* app) { @@ -303,6 +336,7 @@ add_actions_output(InkscapeApplication* app) #endif app->get_action_extra_data().add_data(raw_data_output); + app->get_action_hint_data().add_data(hint_data_output); } @@ -315,4 +349,4 @@ add_actions_output(InkscapeApplication* app) fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : \ No newline at end of file diff --git a/src/actions/actions-selection-object.cpp b/src/actions/actions-selection-object.cpp new file mode 100644 index 0000000000..391b75668b --- /dev/null +++ b/src/actions/actions-selection-object.cpp @@ -0,0 +1,98 @@ +#include + +#include // Not ! To eventually allow a headless version! +#include + +#include "actions-selection-object.h" +#include "actions-helper.h" +#include "inkscape-application.h" + +#include "inkscape.h" // Inkscape::Application +#include "selection.h" // Selection + +#include "object/sp-root.h" // select_all: document->getRoot(); +#include "object/sp-item-group.h" // select_all + +void +select_object_group(InkscapeApplication* app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + selection->group(); +} + +void +select_object_ungroup(InkscapeApplication* app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + selection->ungroup(); +} + +void +select_object_ungroup_pop(InkscapeApplication* app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + selection->popFromGroup(); +} + +void +selection_top(InkscapeApplication* app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + selection->raiseToTop(); +} + +void +selection_raise(InkscapeApplication* app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + selection->raise(); +} + +void +selection_lower(InkscapeApplication* app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + selection->lower(); +} + +void +selection_bottom(InkscapeApplication* app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + selection->lowerToBottom(); +} + +// SHOULD REALLY BE DOC ACTIONS +std::vector> raw_data_selection_object = +{ + /* Group */ + {"app.select-object-group", N_("Group"), "Select", N_("Group selected objects") }, + {"app.select-object-ungroup", N_("Ungroup"), "Select", N_("Ungroup selected objects") }, + {"app.select-object-ungroup-pop", N_("Pop Selected Objects out of Group"), "Select", N_("Pop selected objects out of group") }, + /* Rise and Lower */ + {"app.selection-top", N_("Raise to Top"), "Select", N_("Raise selection to top") }, + {"app.selection-raise", N_("Raise"), "Select", N_("Raise selection one step") }, + {"app.selection-lower", N_("Lower"), "Select", N_("Lower selection one step") }, + {"app.selection-bottom", N_("Lower to Bottom"), "Select", N_("Lower selection to bottom") } + // clang-format on +}; + +void +add_actions_selection_object(InkscapeApplication* app) +{ + auto *gapp = app->gio_app(); + + // clang-format off + /* Group */ + gapp->add_action( "select-object-group", sigc::bind(sigc::ptr_fun(&select_object_group), app) ); + gapp->add_action( "select-object-ungroup", sigc::bind(sigc::ptr_fun(&select_object_ungroup), app) ); + gapp->add_action( "select-object-ungroup-pop", sigc::bind(sigc::ptr_fun(&select_object_ungroup_pop), app) ); + /* Rise and Lower */ + gapp->add_action( "selection-top", sigc::bind(sigc::ptr_fun(&selection_top), app) ); + gapp->add_action( "selection-raise", sigc::bind(sigc::ptr_fun(&selection_raise), app) ); + gapp->add_action( "selection-lower", sigc::bind(sigc::ptr_fun(&selection_lower), app) ); + gapp->add_action( "selection-bottom", sigc::bind(sigc::ptr_fun(&selection_bottom), app) ); + // clang-format on + + app->get_action_extra_data().add_data(raw_data_selection_object); +} \ No newline at end of file diff --git a/src/actions/actions-selection-object.h b/src/actions/actions-selection-object.h new file mode 100644 index 0000000000..2357002d9b --- /dev/null +++ b/src/actions/actions-selection-object.h @@ -0,0 +1,8 @@ +#ifndef INK_ACTIONS_SELECTION_OBJECT_H +#define INK_ACTIONS_SELECTION_OBJECT_H + +class InkscapeApplication; + +void add_actions_selection_object(InkscapeApplication* app); + +#endif // INK_ACTIONS_SELECTION_OBJECT_H \ No newline at end of file diff --git a/src/actions/actions-selection.cpp b/src/actions/actions-selection.cpp index 50281d8e00..03e61d10fc 100644 --- a/src/actions/actions-selection.cpp +++ b/src/actions/actions-selection.cpp @@ -235,18 +235,18 @@ select_list(InkscapeApplication* app) // SHOULD REALLY BE DOC ACTIONS std::vector> raw_data_selection = { - // clang-format off - {"app.select-clear", N_("Clear Selection"), "Select", N_("Clear selection") }, - {"app.select", N_("Select"), "Select", N_("Select by ID (deprecated)") }, - {"app.unselect", N_("Deselect"), "Select", N_("Deselect by ID (deprecated)") }, - {"app.select-by-id", N_("Select by ID"), "Select", N_("Select by ID") }, - {"app.unselect-by-id", N_("Deselect by ID"), "Select", N_("Deselect by ID") }, - {"app.select-by-class", N_("Select by Class"), "Select", N_("Select by class") }, - {"app.select-by-element", N_("Select by Element"), "Select", N_("Select by SVG element (e.g. 'rect')") }, - {"app.select-by-selector", N_("Select by Selector"), "Select", N_("Select by CSS selector") }, - {"app.select-all", N_("Select All"), "Select", N_("Select all; options: 'all' (every object including groups), 'layers', 'no-layers' (top level objects in layers), 'groups' (all groups including layers), 'no-groups' (all objects other than groups and layers, default)")}, - {"app.select-invert", N_("Invert Selection"), "Select", N_("Invert selection; options: 'all', 'layers', 'no-layers', 'groups', 'no-groups' (default)")}, - {"app.select-list", N_("List Selection"), "Select", N_("Print a list of objects in current selection") } + // clang-format offs + {"app.select-clear", N_("Clear Selection"), "Select", N_("Clear selection") }, + {"app.select", N_("Select"), "Select", N_("Select by ID (deprecated)") }, + {"app.unselect", N_("Deselect"), "Select", N_("Deselect by ID (deprecated)") }, + {"app.select-by-id", N_("Select by ID"), "Select", N_("Select by ID") }, + {"app.unselect-by-id", N_("Deselect by ID"), "Select", N_("Deselect by ID") }, + {"app.select-by-class", N_("Select by Class"), "Select", N_("Select by class") }, + {"app.select-by-element", N_("Select by Element"), "Select", N_("Select by SVG element (e.g. 'rect')") }, + {"app.select-by-selector", N_("Select by Selector"), "Select", N_("Select by CSS selector") }, + {"app.select-all", N_("Select All"), "Select", N_("Select all; options: 'all' (every object including groups), 'layers', 'no-layers' (top level objects in layers), 'groups' (all groups including layers), 'no-groups' (all objects other than groups and layers, default)")}, + {"app.select-invert", N_("Invert Selection"), "Select", N_("Invert selection; options: 'all', 'layers', 'no-layers', 'groups', 'no-groups' (default)")}, + {"app.select-list", N_("List Selection"), "Select", N_("Print a list of objects in current selection") } // clang-format on }; @@ -282,4 +282,4 @@ add_actions_selection(InkscapeApplication* app) fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : \ No newline at end of file diff --git a/src/actions/actions-transform.cpp b/src/actions/actions-transform.cpp index 81a9ca7a79..379c755e1b 100644 --- a/src/actions/actions-transform.cpp +++ b/src/actions/actions-transform.cpp @@ -94,6 +94,15 @@ std::vector> raw_data_transform = // clang-format on }; +std::vector> hint_data_transform = +{ + // clang-format off + {"app.transform-translate", N_("Give two String input for translation") }, + {"app.transform-rotate", N_("Give Double input for angle of Rotation") }, + {"app.transform-scale", N_("Give Double input for Scale") } + // clang-format on +}; + void add_actions_transform(InkscapeApplication* app) { @@ -117,6 +126,7 @@ add_actions_transform(InkscapeApplication* app) #endif app->get_action_extra_data().add_data(raw_data_transform); + app->get_action_hint_data().add_data(hint_data_transform); } @@ -129,4 +139,4 @@ add_actions_transform(InkscapeApplication* app) fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : \ No newline at end of file diff --git a/src/inkscape-application.cpp b/src/inkscape-application.cpp index 93f89139a5..e9539445fc 100644 --- a/src/inkscape-application.cpp +++ b/src/inkscape-application.cpp @@ -53,6 +53,7 @@ #include "actions/actions-object.h" // Actions #include "actions/actions-object-align.h" // Actions #include "actions/actions-output.h" // Actions +#include "actions/actions-selection-object.h" // Actions #include "actions/actions-selection.h" // Actions #include "actions/actions-transform.h" // Actions #include "actions/actions-window.h" // Actions @@ -584,14 +585,15 @@ InkscapeApplication::InkscapeApplication() // Glib::set_application_name(N_("Inkscape - A Vector Drawing Program")); // After gettext() init. // ======================== Actions ========================= - add_actions_base(this); // actions that are GUI independent - add_actions_file(this); // actions for file handling - add_actions_object(this); // actions for object manipulation - add_actions_object_align(this); // actions for object alignment - add_actions_output(this); // actions for file export - add_actions_selection(this); // actions for object selection - add_actions_transform(this); // actions for transforming selected objects - add_actions_window(this); // actions for windows + add_actions_base(this); // actions that are GUI independent + add_actions_file(this); // actions for file handling + add_actions_object(this); // actions for object manipulation + add_actions_object_align(this); // actions for object alignment + add_actions_output(this); // actions for file export + add_actions_selection(this); // actions for object selection + add_actions_selection_object(this); // actions for selected objects + add_actions_transform(this); // actions for transforming selected objects + add_actions_window(this); // actions for windows // ====================== Command Line ====================== @@ -1618,4 +1620,4 @@ int InkscapeApplication::get_number_of_windows() const { fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : \ No newline at end of file diff --git a/src/inkscape-application.h b/src/inkscape-application.h index 210da9d6ef..41f7c38bb5 100644 --- a/src/inkscape-application.h +++ b/src/inkscape-application.h @@ -26,6 +26,7 @@ #include "selection.h" #include "actions/actions-extra-data.h" +#include "actions/actions-hint-data.h" #include "helper/action.h" #include "io/file-export-cmd.h" // File export (non-verb) @@ -111,6 +112,7 @@ public: /****** Actions *******/ InkActionExtraData& get_action_extra_data() { return _action_extra_data; } + InkActionHintData& get_action_hint_data() { return _action_hint_data; } /******* Debug ********/ void dump(); @@ -150,7 +152,8 @@ protected: action_vector_t _command_line_actions; // Extra data associated with actions (Label, Section, Tooltip/Help). - InkActionExtraData _action_extra_data; + InkActionExtraData _action_extra_data; + InkActionHintData _action_hint_data; void on_activate(); void on_open(const Gio::Application::type_vec_files &files, const Glib::ustring &hint); diff --git a/src/inkscape-window.cpp b/src/inkscape-window.cpp index 64e91e15e1..40c8c2cfb6 100644 --- a/src/inkscape-window.cpp +++ b/src/inkscape-window.cpp @@ -25,6 +25,8 @@ #include "actions/actions-canvas-mode.h" #include "actions/actions-canvas-transform.h" #include "actions/actions-dialogs.h" +#include "actions/actions-hide-lock.h" +#include "actions/actions-edit.h" #include "actions/actions-tools.h" #include "object/sp-namedview.h" // TODO Remove need for this! @@ -91,6 +93,9 @@ InkscapeWindow::InkscapeWindow(SPDocument* document) // =================== Actions =================== // After canvas has been constructed.. move to canvas proper. add_actions_canvas_transform(this); // Actions to transform canvas view. + add_actions_dialogs(this); // Actions to transform dialog. + add_actions_hide_lock(this); // Actions to transform dialog. + add_actions_edit(this); // Actions to transform dialog. add_actions_canvas_mode(this); // Actions to change canvas display mode. add_actions_dialogs(this); // Actions to open dialogs. add_actions_tools(this); // Actions to switch between tools. diff --git a/src/ui/desktop/menubar.cpp b/src/ui/desktop/menubar.cpp index 7620ac0400..d6e5497e48 100644 --- a/src/ui/desktop/menubar.cpp +++ b/src/ui/desktop/menubar.cpp @@ -454,25 +454,31 @@ build_menu(Gtk::MenuShell* menu, Inkscape::XML::Node* xml, Inkscape::UI::View::V Gtk::MenuItem* menuitem = Gtk::manage(new Gtk::MenuItem(_(name), true)); menuitem->set_name(name); - // TEMP - if (strcmp(name, "_View") == 0) { - // Add from menu-view.ui first. + std::string filename = ""; + std::string menuname = ""; + auto refBuilder = Gtk::Builder::create(); - auto refBuilder = Gtk::Builder::create(); + if (strcmp(name, "_View") == 0) { + filename = Inkscape::IO::Resource::get_filename(Inkscape::IO::Resource::UIS, "menu-view.ui"); + menuname = "view-menu"; + } + else if (strcmp(name, "_Object") == 0) { + filename = Inkscape::IO::Resource::get_filename(Inkscape::IO::Resource::UIS, "menu-object.ui"); + menuname = "object-menu"; + } + + if(filename!=""){ try { - std::string filename = - Inkscape::IO::Resource::get_filename(Inkscape::IO::Resource::UIS, "menu-view.ui"); refBuilder->add_from_file(filename); } catch (const Glib::Error& err) { std::cerr << "build_menu: failed to load View menu from: " - << "menu-view.ui: " + << filename <<": " << err.what() << std::endl; } - - auto object = refBuilder->get_object("view-menu"); + auto object = refBuilder->get_object(menuname); auto gmenu = Glib::RefPtr::cast_dynamic(object); if (!gmenu) { std::cerr << "build_menu: failed to build View menu!" << std::endl; @@ -484,10 +490,9 @@ build_menu(Gtk::MenuShell* menu, Inkscape::XML::Node* xml, Inkscape::UI::View::V // Rest of View menu from menus.xml build_menu(submenu, menu_ptr->firstChild(), view, show_icons_curr); } - continue; - - } else { + } + else { Gtk::Menu* submenu = Gtk::manage(new Gtk::Menu()); build_menu(submenu, menu_ptr->firstChild(), view, show_icons_curr); @@ -672,4 +677,4 @@ build_menubar(Inkscape::UI::View::View* view) fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : \ No newline at end of file diff --git a/src/ui/dialog/command-palette.cpp b/src/ui/dialog/command-palette.cpp index 447a64761b..e6279ffbbb 100644 --- a/src/ui/dialog/command-palette.cpp +++ b/src/ui/dialog/command-palette.cpp @@ -44,6 +44,7 @@ #include #include +// #include "actions/actions-hint-data.h" #include "actions/actions-extra-data.h" #include "file.h" #include "gc-anchored.h" @@ -617,9 +618,22 @@ bool CommandPalette::ask_action_parameter(const ActionPtrName &action_ptr_name) default: break; } + + const auto app = InkscapeApplication::instance(); + InkActionHintData &action_hint_data = app->get_action_hint_data(); + auto action_hint = action_hint_data.get_tooltip_hint_for_action(action_ptr_name.second, false); + + // Indicate user about what to enter FIXME Dialog generation - _CPFilter->set_placeholder_text("Enter a " + type_string + "..."); - _CPFilter->set_tooltip_text("Enter a " + type_string + "..."); + if(action_hint.length()){ + _CPFilter->set_placeholder_text(action_hint); + _CPFilter->set_tooltip_text(action_hint); + }else{ + _CPFilter->set_placeholder_text("Enter a " + type_string + "..."); + _CPFilter->set_tooltip_text("Enter a " + type_string + "..."); + } + + return true; } @@ -1454,7 +1468,7 @@ void CPHistoryXML::add_action_parameter(const std::string &full_action_name, con if (full_action_name == action_iter->attribute("name")) { // If the last parameter was the same don't do anything, inner text is also a node hence 2 times last // child - if (action_iter->lastChild()->lastChild()->content() == param) { + if (action_iter->lastChild()->lastChild() && action_iter->lastChild()->lastChild()->content() == param) { Inkscape::GC::release(parameter_node); return; } @@ -1572,4 +1586,4 @@ std::optional CPHistoryXML::_get_operation_type(Inkscape::XML::Node } // namespace Dialog } // namespace UI -} // namespace Inkscape +} // namespace Inkscape \ No newline at end of file diff --git a/src/ui/dialog/dialog-container.cpp b/src/ui/dialog/dialog-container.cpp index a5dfc63589..430475db6c 100644 --- a/src/ui/dialog/dialog-container.cpp +++ b/src/ui/dialog/dialog-container.cpp @@ -307,7 +307,6 @@ void DialogContainer::new_dialog(const Glib::ustring& dialog_type ) } } - DialogBase* DialogContainer::find_existing_dialog(const Glib::ustring& dialog_type) { DialogBase *existing_dialog = get_dialog(dialog_type); if (!existing_dialog) { @@ -387,7 +386,6 @@ void DialogContainer::new_dialog(const Glib::ustring& dialog_type, DialogNoteboo last_column = create_column(); columns->append(last_column); } - // Look to see if first widget in column is notebook, if not add one. notebook = dynamic_cast(last_column->get_first_widget()); if (!notebook) { @@ -395,10 +393,8 @@ void DialogContainer::new_dialog(const Glib::ustring& dialog_type, DialogNoteboo last_column->prepend(notebook); } } - // Add dialog notebook->add_page(*dialog, *tab, dialog->get_name()); - if (auto panel = dynamic_cast(notebook->get_parent())) { // if panel is collapsed, show it now, or else new dialog will be mysteriously missing panel->show(); @@ -568,7 +564,6 @@ DialogWindow *DialogContainer::create_new_floating_dialog(const Glib::ustring& d } return nullptr; } - // check if this dialog *was* open and floating; if so recreate its window if (auto state = DialogManager::singleton().find_dialog_state(dialog_type)) { if (recreate_dialogs_from_state(state.get())) { @@ -1180,4 +1175,4 @@ void DialogContainer::column_empty(DialogMultipaned *column) fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : \ No newline at end of file -- GitLab From b7a3a878f223407b664d01e8e9575093ded15bf6 Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Wed, 9 Jun 2021 13:07:32 +0530 Subject: [PATCH 025/235] Replacing verbs from menubar object Commenting object menubar --- share/ui/menu-object.ui | 33 +++++++++++---------------------- share/ui/menus.xml | 4 ++-- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/share/ui/menu-object.ui b/share/ui/menu-object.ui index 788b4f514c..59d086f84b 100644 --- a/share/ui/menu-object.ui +++ b/share/ui/menu-object.ui @@ -107,35 +107,33 @@ + - - Objects to _Marker - win.object-to-marker - - - Objects to Gu_ides - win.object-to-guides - + + Objects to _Marker + win.object-to-marker + + + Objects to Gu_ides + win.object-to-guides + Raise to _Top app.selection-top selection-top - - + _Raise app.selection-raise selection-raise - - + _Lower app.selection-lower selection-lower - Lower to _Bottom app.selection-bottom @@ -166,12 +164,10 @@ - Unhide All win.unhide-all - Unlock All win.unlock-all @@ -183,24 +179,17 @@ win.dialog-open Transform - _Align and Distribute... win.dialog-open AlignDistribute - _Arrange... win.dialog-open Arrange - - ------object action end--------- - - - \ No newline at end of file diff --git a/share/ui/menus.xml b/share/ui/menus.xml index a7d376767d..939cf8f5a8 100644 --- a/share/ui/menus.xml +++ b/share/ui/menus.xml @@ -196,7 +196,7 @@ - + -- GitLab From 86abdeb6829593c7948c7e0518eb90e65f191df9 Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Wed, 9 Jun 2021 14:22:19 +0530 Subject: [PATCH 026/235] Line addition in XML and adding spaces --- share/keys/inkscape.xml | 60 +++--------------------------- share/ui/menu-object.ui | 43 +++++++++++++-------- src/actions/actions-edit.h | 1 - src/ui/dialog/command-palette.cpp | 6 +-- src/ui/dialog/dialog-container.cpp | 1 + 5 files changed, 37 insertions(+), 74 deletions(-) diff --git a/share/keys/inkscape.xml b/share/keys/inkscape.xml index 5023fb8139..3618a553e9 100644 --- a/share/keys/inkscape.xml +++ b/share/keys/inkscape.xml @@ -188,22 +188,6 @@ override) the bindings in the main default.xml. - - - - - - - - - - @@ -222,31 +206,10 @@ override) the bindings in the main default.xml. - - - - - - - - - - - - - - @@ -383,32 +346,22 @@ override) the bindings in the main default.xml. - + - - - - - - - - - - @@ -533,12 +486,11 @@ override) the bindings in the main default.xml. - - - - - - + + + + + diff --git a/share/ui/menu-object.ui b/share/ui/menu-object.ui index 59d086f84b..539c6c559d 100644 --- a/share/ui/menu-object.ui +++ b/share/ui/menu-object.ui @@ -4,14 +4,13 @@ - +
Object... win.dialog-open Objects - _Fill and Stroke... win.dialog-open @@ -39,11 +38,12 @@ _Selectors and CSS... win.dialog-open - Objects + Selectors - +
- + +
_Group app.select-object-group @@ -56,7 +56,9 @@ _Pop Selected Objects out of Group app.select-object-ungroup-pop - +
+ +
Cli_p
@@ -74,6 +76,7 @@
+
Mas_k @@ -92,7 +95,7 @@ - + Patter_n @@ -107,8 +110,9 @@ - - + + +
Objects to _Marker win.object-to-marker @@ -117,8 +121,9 @@ Objects to Gu_ides win.object-to-guides - - +
+ +
Raise to _Top app.selection-top @@ -139,9 +144,10 @@ app.selection-bottom selection-bottom - +
- + +
Rotate 90° CW app.object-rotate-90-cw @@ -162,8 +168,10 @@ app.object-flip-vertical object-flip-vertical +
+
Unhide All win.unhide-all @@ -172,8 +180,10 @@ Unlock All win.unlock-all - - +
+ + +
Transfor_m... win.dialog-open @@ -189,7 +199,8 @@ win.dialog-open Arrange - +
+
\ No newline at end of file diff --git a/src/actions/actions-edit.h b/src/actions/actions-edit.h index 4a1825dfda..7639385513 100644 --- a/src/actions/actions-edit.h +++ b/src/actions/actions-edit.h @@ -2,7 +2,6 @@ #define INK_ACTIONS_EDIT_H class InkscapeWindow; -class InkscapeApplication; void add_actions_edit(InkscapeWindow* win); diff --git a/src/ui/dialog/command-palette.cpp b/src/ui/dialog/command-palette.cpp index e6279ffbbb..14ab8bc557 100644 --- a/src/ui/dialog/command-palette.cpp +++ b/src/ui/dialog/command-palette.cpp @@ -620,15 +620,15 @@ bool CommandPalette::ask_action_parameter(const ActionPtrName &action_ptr_name) } const auto app = InkscapeApplication::instance(); - InkActionHintData &action_hint_data = app->get_action_hint_data(); + InkActionHintData &action_hint_data = app->get_action_hint_data(); auto action_hint = action_hint_data.get_tooltip_hint_for_action(action_ptr_name.second, false); // Indicate user about what to enter FIXME Dialog generation - if(action_hint.length()){ + if (action_hint.length()) { _CPFilter->set_placeholder_text(action_hint); _CPFilter->set_tooltip_text(action_hint); - }else{ + } else { _CPFilter->set_placeholder_text("Enter a " + type_string + "..."); _CPFilter->set_tooltip_text("Enter a " + type_string + "..."); } diff --git a/src/ui/dialog/dialog-container.cpp b/src/ui/dialog/dialog-container.cpp index 430475db6c..071667a65b 100644 --- a/src/ui/dialog/dialog-container.cpp +++ b/src/ui/dialog/dialog-container.cpp @@ -307,6 +307,7 @@ void DialogContainer::new_dialog(const Glib::ustring& dialog_type ) } } + DialogBase* DialogContainer::find_existing_dialog(const Glib::ustring& dialog_type) { DialogBase *existing_dialog = get_dialog(dialog_type); if (!existing_dialog) { -- GitLab From c9393c065558a71f671271b8b18504011b1fc050 Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Wed, 9 Jun 2021 16:24:47 +0530 Subject: [PATCH 027/235] Replacing Undo and commenting converted verbs --- src/actions/actions-hide-lock.cpp | 15 +--- src/actions/actions-object.cpp | 26 ++---- src/verbs.cpp | 138 +++++++++++++++--------------- 3 files changed, 80 insertions(+), 99 deletions(-) diff --git a/src/actions/actions-hide-lock.cpp b/src/actions/actions-hide-lock.cpp index 4e536a8d9f..7ec05488af 100644 --- a/src/actions/actions-hide-lock.cpp +++ b/src/actions/actions-hide-lock.cpp @@ -10,24 +10,13 @@ #include "desktop.h" #include "selection-chemistry.h" -enum{ - UNLOCK_ALL, - UNHIDE_ALL - /* - Currently not in use but later it might be - - UNLOCK_ALL_IN_ALL_LAYERS, - UNHIDE_ALL_IN_ALL_LAYERS - */ -}; - void hide_lock_unhide_all(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); SPDocument *doc = dt->getDocument(); unhide_all(dt); - Inkscape::DocumentUndo::done(doc, UNHIDE_ALL, _("Unhide all objects in the current layer")); + Inkscape::DocumentUndo::done(doc, _("Unhide all objects in the current layer"), nullptr); } void @@ -36,7 +25,7 @@ hide_lock_unlock_all(InkscapeWindow* win) SPDesktop* dt = win->get_desktop(); SPDocument *doc = dt->getDocument(); unlock_all(dt); - Inkscape::DocumentUndo::done(doc, UNLOCK_ALL, _("Unlock all objects in the current layer")); + Inkscape::DocumentUndo::done(doc, _("Unlock all objects in the current layer"), nullptr); } std::vector> raw_data_hide_lock = diff --git a/src/actions/actions-object.cpp b/src/actions/actions-object.cpp index f8a0363eae..03060fc668 100644 --- a/src/actions/actions-object.cpp +++ b/src/actions/actions-object.cpp @@ -23,15 +23,7 @@ #include "path/path-simplify.h" #include "live_effects/lpe-powerclip.h" #include "live_effects/lpe-powermask.h" - -enum{ - OBJECT_SET_INVERSE_CLIPPATH, - OBJECT_UNSET_CLIPPATH, - OBJECT_SET_INVERSE_MASK, - OBJECT_UNSET_MASK, - OBJECT_FLIP_HORIZONTAL, - OBJECT_FLIP_VERTICAL -}; +#include "ui/icon-names.h" // No sanity checking is done... should probably add. void @@ -59,7 +51,7 @@ object_set_attribute(const Glib::VariantBase& value, InkscapeApplication *app) } // Needed to update repr (is this the best way?). - Inkscape::DocumentUndo::done(app->get_active_document(), 0, "ActionObjectSetAttribute"); + Inkscape::DocumentUndo::done(app->get_active_document(), "ActionObjectSetAttribute", nullptr); } @@ -92,7 +84,7 @@ object_set_property(const Glib::VariantBase& value, InkscapeApplication *app) } // Needed to update repr (is this the best way?). - Inkscape::DocumentUndo::done(app->get_active_document(), 0, "ActionObjectSetProperty"); + Inkscape::DocumentUndo::done(app->get_active_document(), "ActionObjectSetProperty", nullptr); } @@ -121,7 +113,7 @@ object_set_inverse(InkscapeApplication *app) Inkscape::Selection *selection = app->get_active_selection(); selection->setMask(true, false); Inkscape::LivePathEffect::sp_inverse_powerclip(app->get_active_selection()); - Inkscape::DocumentUndo::done(app->get_active_document(), OBJECT_SET_INVERSE_CLIPPATH, _("_Set Inverse (LPE)")); + Inkscape::DocumentUndo::done(app->get_active_document(), _("_Set Inverse (LPE)"), nullptr); } void @@ -130,7 +122,7 @@ object_release(InkscapeApplication *app) Inkscape::Selection *selection = app->get_active_selection(); Inkscape::LivePathEffect::sp_remove_powerclip(app->get_active_selection()); selection->unsetMask(true); - Inkscape::DocumentUndo::done(app->get_active_document(),OBJECT_UNSET_CLIPPATH , _("Release clipping path")); + Inkscape::DocumentUndo::done(app->get_active_document(), _("Release clipping path"), nullptr); } void @@ -146,7 +138,7 @@ object_set_inverse_mask(InkscapeApplication *app) Inkscape::Selection *selection = app->get_active_selection(); selection->setMask(false, false); Inkscape::LivePathEffect::sp_inverse_powermask(app->get_active_selection()); - Inkscape::DocumentUndo::done(app->get_active_document(), OBJECT_SET_INVERSE_MASK, _("_Set Inverse (LPE)")); + Inkscape::DocumentUndo::done(app->get_active_document(), _("_Set Inverse (LPE)"), nullptr); } void @@ -155,7 +147,7 @@ object_release_mask(InkscapeApplication *app) Inkscape::Selection *selection = app->get_active_selection(); Inkscape::LivePathEffect::sp_remove_powermask(app->get_active_selection()); selection->unsetMask(false); - Inkscape::DocumentUndo::done(app->get_active_document(),OBJECT_UNSET_MASK , _("Release mask")); + Inkscape::DocumentUndo::done(app->get_active_document(), _("Release mask"), nullptr); } void @@ -176,7 +168,7 @@ object_flip_horizontal(InkscapeApplication *app){ Geom::Point center; center = *selection->center(); selection->setScaleRelative(center, Geom::Scale(-1.0, 1.0)); - Inkscape::DocumentUndo::done(app->get_active_document(), OBJECT_FLIP_HORIZONTAL,_("Flip horizontally")); + Inkscape::DocumentUndo::done(app->get_active_document(), _("Flip horizontally"), INKSCAPE_ICON("object-flip-horizontal")); } void @@ -185,7 +177,7 @@ object_flip_vertical(InkscapeApplication *app){ Geom::Point center; center = *selection->center(); selection->setScaleRelative(center, Geom::Scale(1.0, -1.0)); - Inkscape::DocumentUndo::done(app->get_active_document(), OBJECT_FLIP_VERTICAL,_("Flip vertically")); + Inkscape::DocumentUndo::done(app->get_active_document(), _("Flip vertically"), INKSCAPE_ICON("object-flip-vertical")); } diff --git a/src/verbs.cpp b/src/verbs.cpp index 86d89826ce..2fa9dcf6e2 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1027,7 +1027,7 @@ void EditVerb::perform(SPAction *action, void *data) case SP_VERB_EDIT_CLONE_ORIGINAL_PATH_LPE: dt->selection->cloneOriginalPathLPE(); break; - case SP_VERB_EDIT_SELECTION_2_MARKER: + /* case SP_VERB_EDIT_SELECTION_2_MARKER: dt->selection->toMarker(); break; case SP_VERB_EDIT_SELECTION_2_GUIDES: @@ -1038,7 +1038,7 @@ void EditVerb::perform(SPAction *action, void *data) break; case SP_VERB_EDIT_UNTILE: dt->selection->untile(); - break; + break; */ case SP_VERB_EDIT_SYMBOL: dt->selection->toSymbol(); break; @@ -1185,7 +1185,7 @@ void SelectionVerb::perform(SPAction *action, void *data) selection->scaleTimes(0.5); break; } - case SP_VERB_SELECTION_TO_FRONT: + /* case SP_VERB_SELECTION_TO_FRONT: selection->raiseToTop(); break; case SP_VERB_SELECTION_TO_BACK: @@ -1196,14 +1196,14 @@ void SelectionVerb::perform(SPAction *action, void *data) break; case SP_VERB_SELECTION_LOWER: selection->lower(); - break; + break; */ case SP_VERB_SELECTION_STACK_UP: selection->stackUp(); break; case SP_VERB_SELECTION_STACK_DOWN: selection->stackDown(); break; - case SP_VERB_SELECTION_GROUP: + /* case SP_VERB_SELECTION_GROUP: selection->group(); break; case SP_VERB_SELECTION_UNGROUP: @@ -1211,7 +1211,7 @@ void SelectionVerb::perform(SPAction *action, void *data) break; case SP_VERB_SELECTION_UNGROUP_POP_SELECTION: selection->popFromGroup(); - break; + break; */ case SP_VERB_SELECTION_FILL_BETWEEN_MANY: selection->fillBetweenMany(); break; @@ -1308,9 +1308,9 @@ void SelectionVerb::perform(SPAction *action, void *data) case SP_VERB_SELECTION_BREAK_APART: selection->breakApart(); break; - case SP_VERB_SELECTION_ARRANGE: + /* case SP_VERB_SELECTION_ARRANGE: container->new_dialog(SP_VERB_SELECTION_ARRANGE); - break; + break; */ default: break; } @@ -1601,12 +1601,12 @@ void ObjectVerb::perform( SPAction *action, void *data) } switch (reinterpret_cast(data)) { - case SP_VERB_OBJECT_ROTATE_90_CW: - sel->rotate90(false); - break; - case SP_VERB_OBJECT_ROTATE_90_CCW: - sel->rotate90(true); - break; + // case SP_VERB_OBJECT_ROTATE_90_CW: + // sel->rotate90(false); + // break; + // case SP_VERB_OBJECT_ROTATE_90_CCW: + // sel->rotate90(true); + // break; case SP_VERB_OBJECT_FLATTEN: sel->removeTransform(); break; @@ -1622,7 +1622,7 @@ void ObjectVerb::perform( SPAction *action, void *data) case SP_VERB_OBJECT_FLOWTEXT_TO_TEXT: flowtext_to_text(); break; - case SP_VERB_OBJECT_FLIP_HORIZONTAL: + /* case SP_VERB_OBJECT_FLIP_HORIZONTAL: sel->setScaleRelative(center, Geom::Scale(-1.0, 1.0)); DocumentUndo::done(dt->getDocument(), SP_VERB_OBJECT_FLIP_HORIZONTAL, _("Flip horizontally")); @@ -1632,18 +1632,18 @@ void ObjectVerb::perform( SPAction *action, void *data) DocumentUndo::done(dt->getDocument(), SP_VERB_OBJECT_FLIP_VERTICAL, _("Flip vertically")); break; - case SP_VERB_OBJECT_SET_MASK: + case SP_VERB_OBJECT_SET_MASK: sel->setMask(false, false); - break; + break; case SP_VERB_OBJECT_SET_INVERSE_MASK: sel->setMask(false, false); Inkscape::LivePathEffect::sp_inverse_powermask(sp_action_get_selection(action)); DocumentUndo::done(dt->getDocument(), SP_VERB_OBJECT_SET_INVERSE_MASK, _("_Set Inverse (LPE)")); - break; + break; */ case SP_VERB_OBJECT_EDIT_MASK: sel->editMask(false); break; - case SP_VERB_OBJECT_UNSET_MASK: + /* case SP_VERB_OBJECT_UNSET_MASK: Inkscape::LivePathEffect::sp_remove_powermask(sp_action_get_selection(action)); sel->unsetMask(false); DocumentUndo::done(dt->getDocument(), SP_VERB_OBJECT_UNSET_MASK, _("Release mask")); @@ -1655,19 +1655,19 @@ void ObjectVerb::perform( SPAction *action, void *data) sel->setMask(true, false); Inkscape::LivePathEffect::sp_inverse_powerclip(sp_action_get_selection(action)); DocumentUndo::done(dt->getDocument(), SP_VERB_OBJECT_SET_INVERSE_CLIPPATH, _("_Set Inverse (LPE)")); - break; + break; */ case SP_VERB_OBJECT_CREATE_CLIP_GROUP: sel->setClipGroup(); break; case SP_VERB_OBJECT_EDIT_CLIPPATH: sel->editMask(true); break; - case SP_VERB_OBJECT_UNSET_CLIPPATH: + /* case SP_VERB_OBJECT_UNSET_CLIPPATH: Inkscape::LivePathEffect::sp_remove_powerclip(sp_action_get_selection(action)); sel->unsetMask(true); DocumentUndo::done(dt->getDocument(), SP_VERB_OBJECT_UNSET_CLIPPATH, _("Release clipping path")); - break; + break; */ default: break; } @@ -1894,16 +1894,16 @@ void DialogVerb::perform(SPAction *action, void *data) case SP_VERB_DIALOG_PROTOTYPE: #endif case SP_VERB_DIALOG_DOCPROPERTIES: - case SP_VERB_DIALOG_FILL_STROKE: + // case SP_VERB_DIALOG_FILL_STROKE: case SP_VERB_DIALOG_GLYPHS: case SP_VERB_DIALOG_SWATCHES: - case SP_VERB_DIALOG_SYMBOLS: + /* case SP_VERB_DIALOG_SYMBOLS: case SP_VERB_DIALOG_PAINT: case SP_VERB_DIALOG_TRANSFORM: - case SP_VERB_DIALOG_ALIGN_DISTRIBUTE: + case SP_VERB_DIALOG_ALIGN_DISTRIBUTE: */ case SP_VERB_DIALOG_TEXT: case SP_VERB_DIALOG_XML_EDITOR: - case SP_VERB_DIALOG_SELECTORS: + // case SP_VERB_DIALOG_SELECTORS: case SP_VERB_DIALOG_FIND: #if WITH_GSPELL case SP_VERB_DIALOG_SPELLCHECK: @@ -1912,11 +1912,11 @@ void DialogVerb::perform(SPAction *action, void *data) case SP_VERB_DIALOG_UNDO_HISTORY: case SP_VERB_DIALOG_CLONETILER: case SP_VERB_DIALOG_ATTR: - case SP_VERB_DIALOG_ITEM: + // case SP_VERB_DIALOG_ITEM: case SP_VERB_DIALOG_INPUT: case SP_VERB_DIALOG_EXPORT: case SP_VERB_DIALOG_LAYERS: - case SP_VERB_DIALOG_OBJECTS: + // case SP_VERB_DIALOG_OBJECTS: case SP_VERB_DIALOG_LIVE_PATH_EFFECT: case SP_VERB_DIALOG_FILTER_EFFECTS: case SP_VERB_DIALOG_SVG_FONTS: @@ -2225,18 +2225,18 @@ void LockAndHideVerb::perform(SPAction *action, void *data) if (!doc) return; switch (reinterpret_cast(data)) { - case SP_VERB_UNLOCK_ALL: + /* case SP_VERB_UNLOCK_ALL: unlock_all(dt); DocumentUndo::done(doc, SP_VERB_UNLOCK_ALL, _("Unlock all objects in the current layer")); - break; + break; */ case SP_VERB_UNLOCK_ALL_IN_ALL_LAYERS: unlock_all_in_all_layers(dt); DocumentUndo::done(doc, SP_VERB_UNLOCK_ALL_IN_ALL_LAYERS, _("Unlock all objects in all layers")); break; - case SP_VERB_UNHIDE_ALL: + /* case SP_VERB_UNHIDE_ALL: unhide_all(dt); DocumentUndo::done(doc, SP_VERB_UNHIDE_ALL, _("Unhide all objects in the current layer")); - break; + break; */ case SP_VERB_UNHIDE_ALL_IN_ALL_LAYERS: unhide_all_in_all_layers(dt); DocumentUndo::done(doc, SP_VERB_UNHIDE_ALL_IN_ALL_LAYERS, _("Unhide all objects in all layers")); @@ -2348,14 +2348,14 @@ Verb *Verb::_base_verbs[] = { new EditVerb(SP_VERB_EDIT_CLONE_ORIGINAL_PATH_LPE, "EditCloneOriginalPathLPE", N_("Clone original path (LPE)"), N_("Creates a new path, applies the Clone original LPE, and refers it to the selected path"), INKSCAPE_ICON("edit-clone-link-lpe")), - new EditVerb(SP_VERB_EDIT_SELECTION_2_MARKER, "ObjectsToMarker", N_("Objects to _Marker"), + /* new EditVerb(SP_VERB_EDIT_SELECTION_2_MARKER, "ObjectsToMarker", N_("Objects to _Marker"), N_("Convert selection to a line marker"), nullptr), new EditVerb(SP_VERB_EDIT_SELECTION_2_GUIDES, "ObjectsToGuides", N_("Objects to Gu_ides"), N_("Convert selected objects to a collection of guidelines aligned with their edges"), nullptr), new EditVerb(SP_VERB_EDIT_TILE, "ObjectsToPattern", N_("Objects to Patter_n"), N_("Convert selection to a rectangle with tiled pattern fill"), nullptr), new EditVerb(SP_VERB_EDIT_UNTILE, "ObjectsFromPattern", N_("Pattern to _Objects"), - N_("Extract objects from a tiled pattern fill"), nullptr), + N_("Extract objects from a tiled pattern fill"), nullptr), */ new EditVerb(SP_VERB_EDIT_SYMBOL, "ObjectsToSymbol", N_("Group to Symbol"), N_("Convert group to a symbol"), nullptr), new EditVerb(SP_VERB_EDIT_UNSYMBOL, "ObjectsFromSymbol", N_("Symbol to Group"), N_("Extract group from a symbol"), @@ -2406,27 +2406,27 @@ Verb *Verb::_base_verbs[] = { N_("Swap fill and stroke of an object"), nullptr), // Selection - new SelectionVerb(SP_VERB_SELECTION_TO_FRONT, "SelectionToFront", N_("Raise to _Top"), N_("Raise selection to top"), + /* new SelectionVerb(SP_VERB_SELECTION_TO_FRONT, "SelectionToFront", N_("Raise to _Top"), N_("Raise selection to top"), INKSCAPE_ICON("selection-top")), new SelectionVerb(SP_VERB_SELECTION_TO_BACK, "SelectionToBack", N_("Lower to _Bottom"), N_("Lower selection to bottom"), INKSCAPE_ICON("selection-bottom")), new SelectionVerb(SP_VERB_SELECTION_RAISE, "SelectionRaise", N_("_Raise"), N_("Raise selection one step"), INKSCAPE_ICON("selection-raise")), new SelectionVerb(SP_VERB_SELECTION_LOWER, "SelectionLower", N_("_Lower"), N_("Lower selection one step"), - INKSCAPE_ICON("selection-lower")), + INKSCAPE_ICON("selection-lower")), */ new SelectionVerb(SP_VERB_SELECTION_STACK_UP, "SelectionStackUp", N_("_Stack up"), N_("Stack selection one step up"), INKSCAPE_ICON("layer-raise")), new SelectionVerb(SP_VERB_SELECTION_STACK_DOWN, "SelectionStackDown", N_("_Stack down"), N_("Stack selection one step down"), INKSCAPE_ICON("layer-lower")), - new SelectionVerb(SP_VERB_SELECTION_GROUP, "SelectionGroup", N_("_Group"), N_("Group selected objects"), + /* new SelectionVerb(SP_VERB_SELECTION_GROUP, "SelectionGroup", N_("_Group"), N_("Group selected objects"), INKSCAPE_ICON("object-group")), new SelectionVerb(SP_VERB_SELECTION_UNGROUP, "SelectionUnGroup", N_("_Ungroup"), N_("Ungroup selected groups"), INKSCAPE_ICON("object-ungroup")), new SelectionVerb(SP_VERB_SELECTION_UNGROUP_POP_SELECTION, "SelectionUnGroupPopSelection", N_("_Pop Selected Objects out of Group"), N_("Pop selected objects out of group"), - INKSCAPE_ICON("object-ungroup-pop-selection")), + INKSCAPE_ICON("object-ungroup-pop-selection")), */ new SelectionVerb(SP_VERB_SELECTION_TEXTTOPATH, "SelectionTextToPath", N_("_Put on Path"), N_("Put text on path"), INKSCAPE_ICON("text-put-on-path")), @@ -2509,8 +2509,8 @@ Verb *Verb::_base_verbs[] = { // Advanced tutorial for more info new SelectionVerb(SP_VERB_SELECTION_BREAK_APART, "SelectionBreakApart", N_("Break _Apart"), N_("Break selected paths into subpaths"), INKSCAPE_ICON("path-break-apart")), - new SelectionVerb(SP_VERB_SELECTION_ARRANGE, "DialogArrange", N_("_Arrange..."), - N_("Arrange selected objects in a table or circle"), INKSCAPE_ICON("dialog-rows-and-columns")), + /* new SelectionVerb(SP_VERB_SELECTION_ARRANGE, "DialogArrange", N_("_Arrange..."), + N_("Arrange selected objects in a table or circle"), INKSCAPE_ICON("dialog-rows-and-columns")), */ new SelectionVerb(SP_VERB_SELECTION_FILL_BETWEEN_MANY, "SelectionFillBetweenMany", N_("Fill between paths"), N_("Create a fill object using the selected paths"), nullptr), // Layer @@ -2555,14 +2555,14 @@ Verb *Verb::_base_verbs[] = { N_("Toggle visibility of current layer"), nullptr), // Object - new ObjectVerb(SP_VERB_OBJECT_ROTATE_90_CW, "ObjectRotate90", N_("Rotate _90\xc2\xb0 CW"), - // This is shared between tooltips and statusbar, so they - // must use UTF-8, not HTML entities for special characters. - N_("Rotate selection 90\xc2\xb0 clockwise"), INKSCAPE_ICON("object-rotate-right")), - new ObjectVerb(SP_VERB_OBJECT_ROTATE_90_CCW, "ObjectRotate90CCW", N_("Rotate 9_0\xc2\xb0 CCW"), - // This is shared between tooltips and statusbar, so they - // must use UTF-8, not HTML entities for special characters. - N_("Rotate selection 90\xc2\xb0 counter-clockwise"), INKSCAPE_ICON("object-rotate-left")), + // new ObjectVerb(SP_VERB_OBJECT_ROTATE_90_CW, "ObjectRotate90", N_("Rotate _90\xc2\xb0 CW"), + // // This is shared between tooltips and statusbar, so they + // // must use UTF-8, not HTML entities for special characters. + // N_("Rotate selection 90\xc2\xb0 clockwise"), INKSCAPE_ICON("object-rotate-right")), + // new ObjectVerb(SP_VERB_OBJECT_ROTATE_90_CCW, "ObjectRotate90CCW", N_("Rotate 9_0\xc2\xb0 CCW"), + // // This is shared between tooltips and statusbar, so they + // // must use UTF-8, not HTML entities for special characters. + // N_("Rotate selection 90\xc2\xb0 counter-clockwise"), INKSCAPE_ICON("object-rotate-left")), new ObjectVerb(SP_VERB_OBJECT_FLATTEN, "ObjectRemoveTransform", N_("Remove _Transformations"), N_("Remove transformations from object"), nullptr), new ObjectVerb(SP_VERB_OBJECT_TO_CURVE, "ObjectToPath", N_("_Object to Path"), @@ -2578,28 +2578,28 @@ Verb *Verb::_base_verbs[] = { new ObjectVerb(SP_VERB_OBJECT_FLOWTEXT_TO_TEXT, "ObjectFlowtextToText", N_("_Convert to Text"), N_("Convert flowed text to regular text object (preserves appearance)"), INKSCAPE_ICON("text-convert-to-regular")), - new ObjectVerb(SP_VERB_OBJECT_FLIP_HORIZONTAL, "ObjectFlipHorizontally", N_("Flip _Horizontal"), + /* new ObjectVerb(SP_VERB_OBJECT_FLIP_HORIZONTAL, "ObjectFlipHorizontally", N_("Flip _Horizontal"), N_("Flip selected objects horizontally"), INKSCAPE_ICON("object-flip-horizontal")), new ObjectVerb(SP_VERB_OBJECT_FLIP_VERTICAL, "ObjectFlipVertically", N_("Flip _Vertical"), N_("Flip selected objects vertically"), INKSCAPE_ICON("object-flip-vertical")), - new ObjectVerb(SP_VERB_OBJECT_SET_MASK, "ObjectSetMask", N_("_Set"), + new ObjectVerb(SP_VERB_OBJECT_SET_MASK, "ObjectSetMask", N_("_Set"), N_("Apply mask to selection (using the topmost object as mask)"), nullptr), new ObjectVerb(SP_VERB_OBJECT_SET_INVERSE_MASK, "ObjectSetInverseMask", N_("_Set Inverse (LPE)"), - N_("Apply inverse mask to selection (using the topmost object as mask)"), nullptr), + N_("Apply inverse mask to selection (using the topmost object as mask)"), nullptr), */ new ObjectVerb(SP_VERB_OBJECT_EDIT_MASK, "ObjectEditMask", N_("_Edit"), N_("Edit mask"), INKSCAPE_ICON("path-mask-edit")), - new ObjectVerb(SP_VERB_OBJECT_UNSET_MASK, "ObjectUnSetMask", N_("_Release"), N_("Remove mask from selection"), + /* new ObjectVerb(SP_VERB_OBJECT_UNSET_MASK, "ObjectUnSetMask", N_("_Release"), N_("Remove mask from selection"), nullptr), new ObjectVerb(SP_VERB_OBJECT_SET_CLIPPATH, "ObjectSetClipPath", N_("_Set"), N_("Apply clipping path to selection (using the topmost object as clipping path)"), nullptr), new ObjectVerb(SP_VERB_OBJECT_SET_INVERSE_CLIPPATH, "ObjectSetInverseClipPath", N_("_Set Inverse (LPE)"), - N_("Apply inverse clipping path to selection (using the topmost object as clipping path)"), nullptr), + N_("Apply inverse clipping path to selection (using the topmost object as clipping path)"), nullptr), */ new ObjectVerb(SP_VERB_OBJECT_CREATE_CLIP_GROUP, "ObjectCreateClipGroup", N_("Create Cl_ip Group"), N_("Creates a clip group using the selected objects as a base"), nullptr), new ObjectVerb(SP_VERB_OBJECT_EDIT_CLIPPATH, "ObjectEditClipPath", N_("_Edit"), N_("Edit clipping path"), + /* new ObjectVerb(SP_VERB_OBJECT_UNSET_CLIPPATH, "ObjectUnSetClipPath", N_("_Release"), + N_("Remove clipping path from selection"), nullptr), */ INKSCAPE_ICON("path-clip-edit")), - new ObjectVerb(SP_VERB_OBJECT_UNSET_CLIPPATH, "ObjectUnSetClipPath", N_("_Release"), - N_("Remove clipping path from selection"), nullptr), // Tools new ContextVerb(SP_VERB_CONTEXT_SELECT, "ToolSelector", NC_("ContextVerb", "Select"), N_("Select and transform objects"), INKSCAPE_ICON("tool-pointer")), @@ -2699,9 +2699,9 @@ Verb *Verb::_base_verbs[] = { new DialogVerb(SP_VERB_DIALOG_DOCPROPERTIES, "DialogDocumentProperties", N_("_Document Properties..."), N_("Edit properties of this document (to be saved with the document)"), INKSCAPE_ICON("document-properties")), - new DialogVerb(SP_VERB_DIALOG_FILL_STROKE, "DialogFillStroke", N_("_Fill and Stroke..."), + /* new DialogVerb(SP_VERB_DIALOG_FILL_STROKE, "DialogFillStroke", N_("_Fill and Stroke..."), N_("Edit objects' colors, gradients, arrowheads, and other fill and stroke properties..."), - INKSCAPE_ICON("dialog-fill-and-stroke")), + INKSCAPE_ICON("dialog-fill-and-stroke")), */ // FIXME: Probably better to either use something from the icon naming spec or ship our own "select-font" icon // Technically what we show are unicode code points and not glyphs. The actual glyphs shown are determined by the // shaping engines. @@ -2711,7 +2711,7 @@ Verb *Verb::_base_verbs[] = { // TRANSLATORS: "Swatches" means: color samples new DialogVerb(SP_VERB_DIALOG_SWATCHES, "DialogSwatches", N_("S_watches..."), N_("Select colors from a swatches palette"), INKSCAPE_ICON("swatches")), - new DialogVerb(SP_VERB_DIALOG_SYMBOLS, "DialogSymbols", N_("S_ymbols..."), + /* new DialogVerb(SP_VERB_DIALOG_SYMBOLS, "DialogSymbols", N_("S_ymbols..."), N_("Select symbol from a symbols palette"), INKSCAPE_ICON("symbols")), new DialogVerb(SP_VERB_DIALOG_PAINT, "DialogPaintServers", N_("_Paint Servers..."), // FIXME missing Inkscape Paint Server Icon @@ -2719,7 +2719,7 @@ Verb *Verb::_base_verbs[] = { new DialogVerb(SP_VERB_DIALOG_TRANSFORM, "DialogTransform", N_("Transfor_m..."), N_("Precisely control objects' transformations"), INKSCAPE_ICON("dialog-transform")), new DialogVerb(SP_VERB_DIALOG_ALIGN_DISTRIBUTE, "DialogAlignDistribute", N_("_Align and Distribute..."), - N_("Align and distribute objects"), INKSCAPE_ICON("dialog-align-and-distribute")), + N_("Align and distribute objects"), INKSCAPE_ICON("dialog-align-and-distribute")), */ new DialogVerb(SP_VERB_DIALOG_UNDO_HISTORY, "DialogUndoHistory", N_("Undo _History..."), N_("Undo History"), INKSCAPE_ICON("edit-undo-history")), new DialogVerb(SP_VERB_DIALOG_TEXT, "DialogText", N_("_Text and Font..."), @@ -2727,8 +2727,8 @@ Verb *Verb::_base_verbs[] = { INKSCAPE_ICON("dialog-text-and-font")), new DialogVerb(SP_VERB_DIALOG_XML_EDITOR, "DialogXMLEditor", N_("_XML Editor..."), N_("View and edit the XML tree of the document"), INKSCAPE_ICON("dialog-xml-editor")), - new DialogVerb(SP_VERB_DIALOG_SELECTORS, "DialogSelectors", N_("_Selectors and CSS..."), - N_("View and edit CSS selectors and styles"), INKSCAPE_ICON("dialog-selectors")), + /* new DialogVerb(SP_VERB_DIALOG_SELECTORS, "DialogSelectors", N_("_Selectors and CSS..."), + N_("View and edit CSS selectors and styles"), INKSCAPE_ICON("dialog-selectors")), */ new DialogVerb(SP_VERB_DIALOG_FIND, "DialogFind", N_("_Find/Replace..."), N_("Find objects in document"), INKSCAPE_ICON("edit-find")), #if WITH_GSPELL @@ -2746,16 +2746,16 @@ Verb *Verb::_base_verbs[] = { N_("Edit the object attributes..."), INKSCAPE_ICON("dialog-object-properties")), new DialogVerb(SP_VERB_DIALOG_ATTR_XML, "DialogAttrDialog", N_("_Object attributes..."), N_("Edit the object attributes..."), INKSCAPE_ICON("dialog-object-properties")), - new DialogVerb(SP_VERB_DIALOG_ITEM, "DialogObjectProperties", N_("_Object Properties..."), + /* new DialogVerb(SP_VERB_DIALOG_ITEM, "DialogObjectProperties", N_("_Object Properties..."), N_("Edit the ID, locked and visible status, and other object properties"), - INKSCAPE_ICON("dialog-object-properties")), + INKSCAPE_ICON("dialog-object-properties")), */ new DialogVerb(SP_VERB_DIALOG_INPUT, "DialogInput", N_("_Input Devices..."), N_("Configure extended input devices, such as a graphics tablet"), INKSCAPE_ICON("dialog-input-devices")), new DialogVerb(SP_VERB_DIALOG_LAYERS, "DialogLayers", N_("Layer_s..."), N_("View Layers"), INKSCAPE_ICON("dialog-layers")), - new DialogVerb(SP_VERB_DIALOG_OBJECTS, "DialogObjects", N_("Object_s..."), N_("View Objects"), - INKSCAPE_ICON("dialog-objects")), + /* new DialogVerb(SP_VERB_DIALOG_OBJECTS, "DialogObjects", N_("Object_s..."), N_("View Objects"), + INKSCAPE_ICON("dialog-objects")), */ new DialogVerb(SP_VERB_DIALOG_STYLE, "DialogStyle", N_("Style Dialog..."), N_("View Style Dialog"), nullptr), 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")), @@ -2827,12 +2827,12 @@ Verb *Verb::_base_verbs[] = { N_("_Resize Page to Selection"), N_("Fit the page to the current selection or the drawing if there is no selection"), nullptr), // LockAndHide - new LockAndHideVerb(SP_VERB_UNLOCK_ALL, "UnlockAll", N_("Unlock All"), - N_("Unlock all objects in the current layer"), nullptr), + /* new LockAndHideVerb(SP_VERB_UNLOCK_ALL, "UnlockAll", N_("Unlock All"), + N_("Unlock all objects in the current layer"), nullptr), */ new LockAndHideVerb(SP_VERB_UNLOCK_ALL_IN_ALL_LAYERS, "UnlockAllInAllLayers", N_("Unlock All in All Layers"), N_("Unlock all objects in all layers"), nullptr), - new LockAndHideVerb(SP_VERB_UNHIDE_ALL, "UnhideAll", N_("Unhide All"), - N_("Unhide all objects in the current layer"), nullptr), + /* new LockAndHideVerb(SP_VERB_UNHIDE_ALL, "UnhideAll", N_("Unhide All"), + N_("Unhide all objects in the current layer"), nullptr), */ new LockAndHideVerb(SP_VERB_UNHIDE_ALL_IN_ALL_LAYERS, "UnhideAllInAllLayers", N_("Unhide All in All Layers"), N_("Unhide all objects in all layers"), nullptr), // Color Management -- GitLab From 09dc4e678294fd7931660bd1e70d15acc9d27647 Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Fri, 11 Jun 2021 14:00:07 +0530 Subject: [PATCH 028/235] Menubar Edit : Basic setup Add ui file and some actions --- share/ui/menu-edit.ui | 109 +++++++++++++++++++++++++++ share/ui/toolbar-select.ui | 116 ++++++++++++++++++++++++++++- src/actions/actions-edit.cpp | 129 +++++++++++++++++++++++++++++++- src/ui/desktop/menubar.cpp | 4 + src/verbs.cpp | 138 +++++++++++++++++------------------ 5 files changed, 424 insertions(+), 72 deletions(-) create mode 100644 share/ui/menu-edit.ui diff --git a/share/ui/menu-edit.ui b/share/ui/menu-edit.ui new file mode 100644 index 0000000000..2d3b7710d1 --- /dev/null +++ b/share/ui/menu-edit.ui @@ -0,0 +1,109 @@ + + + + + + + +
+ + _Undo + win.undo + edit-undo + + + _Redo + win.redo + edit-redo + + + Undo _History... + win.dialog-open + UndoHistory + edit-undo-history + +
+ + + Cu_t + win.cut + edit-cut + + + _Copy + win.copy + edit-copy + + + _Paste + win.paste + edit-paste + + + Paste _In Place + win.paste-in-place + edit-paste-in-place + + + Paste _Style + win.paste-style + edit-paste-style + + + + Paste Si_ze +
+ + Paste Si_ze + win.paste-size + edit-paste-size + + + Paste _Width + win.paste-width + edit-paste-width + + + Paste _Height + win.paste-height + edit-paste-height + + + Paste Size Separately + win.paste-size-separately + edit-paste-size-separately + + + Paste Width Separately + win.paste-width-separately + edit-paste-width-separately + + + Paste Height Separately + win.paste-height-separately + edit-paste-height-separately + +
+
+ + + + _Find/Replace... + win.dialog-open + Find + edit-find + + + + Duplic_ate + win.duplicate + edit-duplicate + + + + ------- action end ------- + + +
+ +
\ No newline at end of file diff --git a/share/ui/toolbar-select.ui b/share/ui/toolbar-select.ui index 21a180d744..58b8375ae8 100644 --- a/share/ui/toolbar-select.ui +++ b/share/ui/toolbar-select.ui @@ -125,7 +125,7 @@ - + True app.selection-bottom selection-bottom @@ -139,13 +139,125 @@ + + - + + True + X + + + + + + X True True + 3 + True + True + + + False + True + 40 + + + + + + True + Y + + + True + True + 2 + True + True + + + False + True + 50 + + + + + + True + W + + + + + + True + True + 1 + True + True + + + False + True + 60 + + + + + + True + app.selection-top + object-unlocked + Raise to _Top + + + + + + True + H + + + + + + True + True + 1 + True + True + + + False + True + 60 + + + + + + True + False + 0 + + mm + px + lm + + + + + diff --git a/src/actions/actions-edit.cpp b/src/actions/actions-edit.cpp index 6204b4c78f..b3b06595aa 100644 --- a/src/actions/actions-edit.cpp +++ b/src/actions/actions-edit.cpp @@ -7,6 +7,7 @@ #include "inkscape-application.h" #include "inkscape-window.h" #include "desktop.h" +#include "selection-chemistry.h" void object_to_pattern(InkscapeWindow* win) @@ -36,13 +37,125 @@ object_to_guides(InkscapeWindow* win) dt->selection->toGuides(); } +void +undo(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + sp_undo(dt, dt->getDocument()); +} + +void +redo(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + sp_redo(dt, dt->getDocument()); +} + +void +cut(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->cut(); +} + +void +copy(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->copy(); +} + +void +paste(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + sp_selection_paste(dt, false); +} + +void +paste_in_place(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + sp_selection_paste(dt, true); +} + +void +paste_style(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->pasteStyle(); +} + +void +paste_size(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->pasteSize(true,true); +} + +void +paste_width(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->pasteSize(true, false); +} + +void +paste_height(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->pasteSize(false, true); +} + +void +paste_size_separately(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->pasteSizeSeparately(true, true); +} + +void +paste_width_separately(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->pasteSizeSeparately(true, false); +} + +void +paste_height_separately(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->pasteSizeSeparately(false, true); +} + +void +duplicate(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->duplicate(); +} + std::vector> raw_data_edit = { // clang-format off {"win.object-to-pattern", N_("Objects to Pattern"), "Edit", N_("Convert selection to a rectangle with tiled pattern fill") }, {"win.pattern-to-object", N_("Pattern to Objects"), "Edit", N_("Extract objects from a tiled pattern fill") }, {"win.object-to-marker", N_("Objects to Marker"), "Edit", N_("Convert selection to a line marker") }, - {"win.object-to-guides", N_("Objects to Guides"), "Edit", N_("Convert selected objects to a collection of guidelines aligned with their edges") } + {"win.object-to-guides", N_("Objects to Guides"), "Edit", N_("Convert selected objects to a collection of guidelines aligned with their edges") }, + {"win.undo", N_("_Undo"), "Edit", N_("Undo last action") }, + {"win.redo", N_("_Redo"), "Edit", N_("Do again the last undone action") }, + {"win.cut", N_("Cu_t"), "Edit", N_("Cut selection to clipboard") }, + {"win.copy", N_("_Copy"), "Edit", N_("Copy selection to clipboard") }, + {"win.paste", N_("_Paste"), "Edit", N_("Paste objects from clipboard to mouse point, or paste text") }, + {"win.paste-in-place", N_("Paste _In Place"), "Edit", N_("Paste objects from clipboard to mouse point, or paste text") }, + {"win.paste-style", N_("Paste _Style"), "Edit", N_("Apply the style of the copied object to selection") }, + {"win.paste-size", N_("Paste Si_ze"), "Edit", N_("Scale selection to match the size of the copied object") }, + {"win.paste-width", N_("Paste _Width"), "Edit", N_("Scale selection horizontally to match the width of the copied object") }, + {"win.paste-height", N_("Paste _Height"), "Edit", N_("Scale selection vertically to match the height of the copied object") }, + {"win.paste-size-separately", N_("Paste Size Separately"), "Edit", N_("Scale each selected object to match the size of the copied object") }, + {"win.paste-width-separately", N_("Paste Width Separately"), "Edit", N_("Scale each selected object horizontally to match the width of the copied object") }, + {"win.paste-height-separately", N_("Paste Height Separately"), "Edit", N_("Scale each selected object vertically to match the height of the copied object") }, + {"win.duplicate", N_("Duplic_ate"), "Edit", N_("Duplicate Selected Objects") } // clang-format on }; @@ -54,6 +167,20 @@ add_actions_edit(InkscapeWindow* win) win->add_action( "pattern-to-object", sigc::bind(sigc::ptr_fun(&pattern_to_object), win)); win->add_action( "object-to-marker", sigc::bind(sigc::ptr_fun(&object_to_marker), win)); win->add_action( "object-to-guides", sigc::bind(sigc::ptr_fun(&object_to_guides), win)); + win->add_action( "undo", sigc::bind(sigc::ptr_fun(&undo), win)); + win->add_action( "redo", sigc::bind(sigc::ptr_fun(&redo), win)); + win->add_action( "cut", sigc::bind(sigc::ptr_fun(&cut), win)); + win->add_action( "copy", sigc::bind(sigc::ptr_fun(©), win)); + win->add_action( "paste", sigc::bind(sigc::ptr_fun(&paste), win)); + win->add_action( "paste-in-place", sigc::bind(sigc::ptr_fun(&paste_in_place), win)); + win->add_action( "paste-style", sigc::bind(sigc::ptr_fun(&paste_style), win)); + win->add_action( "paste-size", sigc::bind(sigc::ptr_fun(&paste_size), win)); + win->add_action( "paste-width", sigc::bind(sigc::ptr_fun(&paste_width), win)); + win->add_action( "paste-height", sigc::bind(sigc::ptr_fun(&paste_height), win)); + win->add_action( "paste-size-separately", sigc::bind(sigc::ptr_fun(&paste_size_separately), win)); + win->add_action( "paste-width-separately", sigc::bind(sigc::ptr_fun(&paste_width_separately), win)); + win->add_action( "paste-height-separately", sigc::bind(sigc::ptr_fun(&paste_height_separately), win)); + win->add_action( "duplicate", sigc::bind(sigc::ptr_fun(&duplicate), win)); // clang-format on auto app = InkscapeApplication::instance(); diff --git a/src/ui/desktop/menubar.cpp b/src/ui/desktop/menubar.cpp index d6e5497e48..2825c46b2d 100644 --- a/src/ui/desktop/menubar.cpp +++ b/src/ui/desktop/menubar.cpp @@ -466,6 +466,10 @@ build_menu(Gtk::MenuShell* menu, Inkscape::XML::Node* xml, Inkscape::UI::View::V filename = Inkscape::IO::Resource::get_filename(Inkscape::IO::Resource::UIS, "menu-object.ui"); menuname = "object-menu"; } + else if (strcmp(name, "_Edit") == 0) { + filename = Inkscape::IO::Resource::get_filename(Inkscape::IO::Resource::UIS, "menu-edit.ui"); + menuname = "edit-menu"; + } if(filename!=""){ try diff --git a/src/verbs.cpp b/src/verbs.cpp index 2fa9dcf6e2..86d89826ce 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1027,7 +1027,7 @@ void EditVerb::perform(SPAction *action, void *data) case SP_VERB_EDIT_CLONE_ORIGINAL_PATH_LPE: dt->selection->cloneOriginalPathLPE(); break; - /* case SP_VERB_EDIT_SELECTION_2_MARKER: + case SP_VERB_EDIT_SELECTION_2_MARKER: dt->selection->toMarker(); break; case SP_VERB_EDIT_SELECTION_2_GUIDES: @@ -1038,7 +1038,7 @@ void EditVerb::perform(SPAction *action, void *data) break; case SP_VERB_EDIT_UNTILE: dt->selection->untile(); - break; */ + break; case SP_VERB_EDIT_SYMBOL: dt->selection->toSymbol(); break; @@ -1185,7 +1185,7 @@ void SelectionVerb::perform(SPAction *action, void *data) selection->scaleTimes(0.5); break; } - /* case SP_VERB_SELECTION_TO_FRONT: + case SP_VERB_SELECTION_TO_FRONT: selection->raiseToTop(); break; case SP_VERB_SELECTION_TO_BACK: @@ -1196,14 +1196,14 @@ void SelectionVerb::perform(SPAction *action, void *data) break; case SP_VERB_SELECTION_LOWER: selection->lower(); - break; */ + break; case SP_VERB_SELECTION_STACK_UP: selection->stackUp(); break; case SP_VERB_SELECTION_STACK_DOWN: selection->stackDown(); break; - /* case SP_VERB_SELECTION_GROUP: + case SP_VERB_SELECTION_GROUP: selection->group(); break; case SP_VERB_SELECTION_UNGROUP: @@ -1211,7 +1211,7 @@ void SelectionVerb::perform(SPAction *action, void *data) break; case SP_VERB_SELECTION_UNGROUP_POP_SELECTION: selection->popFromGroup(); - break; */ + break; case SP_VERB_SELECTION_FILL_BETWEEN_MANY: selection->fillBetweenMany(); break; @@ -1308,9 +1308,9 @@ void SelectionVerb::perform(SPAction *action, void *data) case SP_VERB_SELECTION_BREAK_APART: selection->breakApart(); break; - /* case SP_VERB_SELECTION_ARRANGE: + case SP_VERB_SELECTION_ARRANGE: container->new_dialog(SP_VERB_SELECTION_ARRANGE); - break; */ + break; default: break; } @@ -1601,12 +1601,12 @@ void ObjectVerb::perform( SPAction *action, void *data) } switch (reinterpret_cast(data)) { - // case SP_VERB_OBJECT_ROTATE_90_CW: - // sel->rotate90(false); - // break; - // case SP_VERB_OBJECT_ROTATE_90_CCW: - // sel->rotate90(true); - // break; + case SP_VERB_OBJECT_ROTATE_90_CW: + sel->rotate90(false); + break; + case SP_VERB_OBJECT_ROTATE_90_CCW: + sel->rotate90(true); + break; case SP_VERB_OBJECT_FLATTEN: sel->removeTransform(); break; @@ -1622,7 +1622,7 @@ void ObjectVerb::perform( SPAction *action, void *data) case SP_VERB_OBJECT_FLOWTEXT_TO_TEXT: flowtext_to_text(); break; - /* case SP_VERB_OBJECT_FLIP_HORIZONTAL: + case SP_VERB_OBJECT_FLIP_HORIZONTAL: sel->setScaleRelative(center, Geom::Scale(-1.0, 1.0)); DocumentUndo::done(dt->getDocument(), SP_VERB_OBJECT_FLIP_HORIZONTAL, _("Flip horizontally")); @@ -1632,18 +1632,18 @@ void ObjectVerb::perform( SPAction *action, void *data) DocumentUndo::done(dt->getDocument(), SP_VERB_OBJECT_FLIP_VERTICAL, _("Flip vertically")); break; - case SP_VERB_OBJECT_SET_MASK: + case SP_VERB_OBJECT_SET_MASK: sel->setMask(false, false); - break; + break; case SP_VERB_OBJECT_SET_INVERSE_MASK: sel->setMask(false, false); Inkscape::LivePathEffect::sp_inverse_powermask(sp_action_get_selection(action)); DocumentUndo::done(dt->getDocument(), SP_VERB_OBJECT_SET_INVERSE_MASK, _("_Set Inverse (LPE)")); - break; */ + break; case SP_VERB_OBJECT_EDIT_MASK: sel->editMask(false); break; - /* case SP_VERB_OBJECT_UNSET_MASK: + case SP_VERB_OBJECT_UNSET_MASK: Inkscape::LivePathEffect::sp_remove_powermask(sp_action_get_selection(action)); sel->unsetMask(false); DocumentUndo::done(dt->getDocument(), SP_VERB_OBJECT_UNSET_MASK, _("Release mask")); @@ -1655,19 +1655,19 @@ void ObjectVerb::perform( SPAction *action, void *data) sel->setMask(true, false); Inkscape::LivePathEffect::sp_inverse_powerclip(sp_action_get_selection(action)); DocumentUndo::done(dt->getDocument(), SP_VERB_OBJECT_SET_INVERSE_CLIPPATH, _("_Set Inverse (LPE)")); - break; */ + break; case SP_VERB_OBJECT_CREATE_CLIP_GROUP: sel->setClipGroup(); break; case SP_VERB_OBJECT_EDIT_CLIPPATH: sel->editMask(true); break; - /* case SP_VERB_OBJECT_UNSET_CLIPPATH: + case SP_VERB_OBJECT_UNSET_CLIPPATH: Inkscape::LivePathEffect::sp_remove_powerclip(sp_action_get_selection(action)); sel->unsetMask(true); DocumentUndo::done(dt->getDocument(), SP_VERB_OBJECT_UNSET_CLIPPATH, _("Release clipping path")); - break; */ + break; default: break; } @@ -1894,16 +1894,16 @@ void DialogVerb::perform(SPAction *action, void *data) case SP_VERB_DIALOG_PROTOTYPE: #endif case SP_VERB_DIALOG_DOCPROPERTIES: - // case SP_VERB_DIALOG_FILL_STROKE: + case SP_VERB_DIALOG_FILL_STROKE: case SP_VERB_DIALOG_GLYPHS: case SP_VERB_DIALOG_SWATCHES: - /* case SP_VERB_DIALOG_SYMBOLS: + case SP_VERB_DIALOG_SYMBOLS: case SP_VERB_DIALOG_PAINT: case SP_VERB_DIALOG_TRANSFORM: - case SP_VERB_DIALOG_ALIGN_DISTRIBUTE: */ + case SP_VERB_DIALOG_ALIGN_DISTRIBUTE: case SP_VERB_DIALOG_TEXT: case SP_VERB_DIALOG_XML_EDITOR: - // case SP_VERB_DIALOG_SELECTORS: + case SP_VERB_DIALOG_SELECTORS: case SP_VERB_DIALOG_FIND: #if WITH_GSPELL case SP_VERB_DIALOG_SPELLCHECK: @@ -1912,11 +1912,11 @@ void DialogVerb::perform(SPAction *action, void *data) case SP_VERB_DIALOG_UNDO_HISTORY: case SP_VERB_DIALOG_CLONETILER: case SP_VERB_DIALOG_ATTR: - // case SP_VERB_DIALOG_ITEM: + case SP_VERB_DIALOG_ITEM: case SP_VERB_DIALOG_INPUT: case SP_VERB_DIALOG_EXPORT: case SP_VERB_DIALOG_LAYERS: - // case SP_VERB_DIALOG_OBJECTS: + case SP_VERB_DIALOG_OBJECTS: case SP_VERB_DIALOG_LIVE_PATH_EFFECT: case SP_VERB_DIALOG_FILTER_EFFECTS: case SP_VERB_DIALOG_SVG_FONTS: @@ -2225,18 +2225,18 @@ void LockAndHideVerb::perform(SPAction *action, void *data) if (!doc) return; switch (reinterpret_cast(data)) { - /* case SP_VERB_UNLOCK_ALL: + case SP_VERB_UNLOCK_ALL: unlock_all(dt); DocumentUndo::done(doc, SP_VERB_UNLOCK_ALL, _("Unlock all objects in the current layer")); - break; */ + break; case SP_VERB_UNLOCK_ALL_IN_ALL_LAYERS: unlock_all_in_all_layers(dt); DocumentUndo::done(doc, SP_VERB_UNLOCK_ALL_IN_ALL_LAYERS, _("Unlock all objects in all layers")); break; - /* case SP_VERB_UNHIDE_ALL: + case SP_VERB_UNHIDE_ALL: unhide_all(dt); DocumentUndo::done(doc, SP_VERB_UNHIDE_ALL, _("Unhide all objects in the current layer")); - break; */ + break; case SP_VERB_UNHIDE_ALL_IN_ALL_LAYERS: unhide_all_in_all_layers(dt); DocumentUndo::done(doc, SP_VERB_UNHIDE_ALL_IN_ALL_LAYERS, _("Unhide all objects in all layers")); @@ -2348,14 +2348,14 @@ Verb *Verb::_base_verbs[] = { new EditVerb(SP_VERB_EDIT_CLONE_ORIGINAL_PATH_LPE, "EditCloneOriginalPathLPE", N_("Clone original path (LPE)"), N_("Creates a new path, applies the Clone original LPE, and refers it to the selected path"), INKSCAPE_ICON("edit-clone-link-lpe")), - /* new EditVerb(SP_VERB_EDIT_SELECTION_2_MARKER, "ObjectsToMarker", N_("Objects to _Marker"), + new EditVerb(SP_VERB_EDIT_SELECTION_2_MARKER, "ObjectsToMarker", N_("Objects to _Marker"), N_("Convert selection to a line marker"), nullptr), new EditVerb(SP_VERB_EDIT_SELECTION_2_GUIDES, "ObjectsToGuides", N_("Objects to Gu_ides"), N_("Convert selected objects to a collection of guidelines aligned with their edges"), nullptr), new EditVerb(SP_VERB_EDIT_TILE, "ObjectsToPattern", N_("Objects to Patter_n"), N_("Convert selection to a rectangle with tiled pattern fill"), nullptr), new EditVerb(SP_VERB_EDIT_UNTILE, "ObjectsFromPattern", N_("Pattern to _Objects"), - N_("Extract objects from a tiled pattern fill"), nullptr), */ + N_("Extract objects from a tiled pattern fill"), nullptr), new EditVerb(SP_VERB_EDIT_SYMBOL, "ObjectsToSymbol", N_("Group to Symbol"), N_("Convert group to a symbol"), nullptr), new EditVerb(SP_VERB_EDIT_UNSYMBOL, "ObjectsFromSymbol", N_("Symbol to Group"), N_("Extract group from a symbol"), @@ -2406,27 +2406,27 @@ Verb *Verb::_base_verbs[] = { N_("Swap fill and stroke of an object"), nullptr), // Selection - /* new SelectionVerb(SP_VERB_SELECTION_TO_FRONT, "SelectionToFront", N_("Raise to _Top"), N_("Raise selection to top"), + new SelectionVerb(SP_VERB_SELECTION_TO_FRONT, "SelectionToFront", N_("Raise to _Top"), N_("Raise selection to top"), INKSCAPE_ICON("selection-top")), new SelectionVerb(SP_VERB_SELECTION_TO_BACK, "SelectionToBack", N_("Lower to _Bottom"), N_("Lower selection to bottom"), INKSCAPE_ICON("selection-bottom")), new SelectionVerb(SP_VERB_SELECTION_RAISE, "SelectionRaise", N_("_Raise"), N_("Raise selection one step"), INKSCAPE_ICON("selection-raise")), new SelectionVerb(SP_VERB_SELECTION_LOWER, "SelectionLower", N_("_Lower"), N_("Lower selection one step"), - INKSCAPE_ICON("selection-lower")), */ + INKSCAPE_ICON("selection-lower")), new SelectionVerb(SP_VERB_SELECTION_STACK_UP, "SelectionStackUp", N_("_Stack up"), N_("Stack selection one step up"), INKSCAPE_ICON("layer-raise")), new SelectionVerb(SP_VERB_SELECTION_STACK_DOWN, "SelectionStackDown", N_("_Stack down"), N_("Stack selection one step down"), INKSCAPE_ICON("layer-lower")), - /* new SelectionVerb(SP_VERB_SELECTION_GROUP, "SelectionGroup", N_("_Group"), N_("Group selected objects"), + new SelectionVerb(SP_VERB_SELECTION_GROUP, "SelectionGroup", N_("_Group"), N_("Group selected objects"), INKSCAPE_ICON("object-group")), new SelectionVerb(SP_VERB_SELECTION_UNGROUP, "SelectionUnGroup", N_("_Ungroup"), N_("Ungroup selected groups"), INKSCAPE_ICON("object-ungroup")), new SelectionVerb(SP_VERB_SELECTION_UNGROUP_POP_SELECTION, "SelectionUnGroupPopSelection", N_("_Pop Selected Objects out of Group"), N_("Pop selected objects out of group"), - INKSCAPE_ICON("object-ungroup-pop-selection")), */ + INKSCAPE_ICON("object-ungroup-pop-selection")), new SelectionVerb(SP_VERB_SELECTION_TEXTTOPATH, "SelectionTextToPath", N_("_Put on Path"), N_("Put text on path"), INKSCAPE_ICON("text-put-on-path")), @@ -2509,8 +2509,8 @@ Verb *Verb::_base_verbs[] = { // Advanced tutorial for more info new SelectionVerb(SP_VERB_SELECTION_BREAK_APART, "SelectionBreakApart", N_("Break _Apart"), N_("Break selected paths into subpaths"), INKSCAPE_ICON("path-break-apart")), - /* new SelectionVerb(SP_VERB_SELECTION_ARRANGE, "DialogArrange", N_("_Arrange..."), - N_("Arrange selected objects in a table or circle"), INKSCAPE_ICON("dialog-rows-and-columns")), */ + new SelectionVerb(SP_VERB_SELECTION_ARRANGE, "DialogArrange", N_("_Arrange..."), + N_("Arrange selected objects in a table or circle"), INKSCAPE_ICON("dialog-rows-and-columns")), new SelectionVerb(SP_VERB_SELECTION_FILL_BETWEEN_MANY, "SelectionFillBetweenMany", N_("Fill between paths"), N_("Create a fill object using the selected paths"), nullptr), // Layer @@ -2555,14 +2555,14 @@ Verb *Verb::_base_verbs[] = { N_("Toggle visibility of current layer"), nullptr), // Object - // new ObjectVerb(SP_VERB_OBJECT_ROTATE_90_CW, "ObjectRotate90", N_("Rotate _90\xc2\xb0 CW"), - // // This is shared between tooltips and statusbar, so they - // // must use UTF-8, not HTML entities for special characters. - // N_("Rotate selection 90\xc2\xb0 clockwise"), INKSCAPE_ICON("object-rotate-right")), - // new ObjectVerb(SP_VERB_OBJECT_ROTATE_90_CCW, "ObjectRotate90CCW", N_("Rotate 9_0\xc2\xb0 CCW"), - // // This is shared between tooltips and statusbar, so they - // // must use UTF-8, not HTML entities for special characters. - // N_("Rotate selection 90\xc2\xb0 counter-clockwise"), INKSCAPE_ICON("object-rotate-left")), + new ObjectVerb(SP_VERB_OBJECT_ROTATE_90_CW, "ObjectRotate90", N_("Rotate _90\xc2\xb0 CW"), + // This is shared between tooltips and statusbar, so they + // must use UTF-8, not HTML entities for special characters. + N_("Rotate selection 90\xc2\xb0 clockwise"), INKSCAPE_ICON("object-rotate-right")), + new ObjectVerb(SP_VERB_OBJECT_ROTATE_90_CCW, "ObjectRotate90CCW", N_("Rotate 9_0\xc2\xb0 CCW"), + // This is shared between tooltips and statusbar, so they + // must use UTF-8, not HTML entities for special characters. + N_("Rotate selection 90\xc2\xb0 counter-clockwise"), INKSCAPE_ICON("object-rotate-left")), new ObjectVerb(SP_VERB_OBJECT_FLATTEN, "ObjectRemoveTransform", N_("Remove _Transformations"), N_("Remove transformations from object"), nullptr), new ObjectVerb(SP_VERB_OBJECT_TO_CURVE, "ObjectToPath", N_("_Object to Path"), @@ -2578,28 +2578,28 @@ Verb *Verb::_base_verbs[] = { new ObjectVerb(SP_VERB_OBJECT_FLOWTEXT_TO_TEXT, "ObjectFlowtextToText", N_("_Convert to Text"), N_("Convert flowed text to regular text object (preserves appearance)"), INKSCAPE_ICON("text-convert-to-regular")), - /* new ObjectVerb(SP_VERB_OBJECT_FLIP_HORIZONTAL, "ObjectFlipHorizontally", N_("Flip _Horizontal"), + new ObjectVerb(SP_VERB_OBJECT_FLIP_HORIZONTAL, "ObjectFlipHorizontally", N_("Flip _Horizontal"), N_("Flip selected objects horizontally"), INKSCAPE_ICON("object-flip-horizontal")), new ObjectVerb(SP_VERB_OBJECT_FLIP_VERTICAL, "ObjectFlipVertically", N_("Flip _Vertical"), N_("Flip selected objects vertically"), INKSCAPE_ICON("object-flip-vertical")), - new ObjectVerb(SP_VERB_OBJECT_SET_MASK, "ObjectSetMask", N_("_Set"), + new ObjectVerb(SP_VERB_OBJECT_SET_MASK, "ObjectSetMask", N_("_Set"), N_("Apply mask to selection (using the topmost object as mask)"), nullptr), new ObjectVerb(SP_VERB_OBJECT_SET_INVERSE_MASK, "ObjectSetInverseMask", N_("_Set Inverse (LPE)"), - N_("Apply inverse mask to selection (using the topmost object as mask)"), nullptr), */ + N_("Apply inverse mask to selection (using the topmost object as mask)"), nullptr), new ObjectVerb(SP_VERB_OBJECT_EDIT_MASK, "ObjectEditMask", N_("_Edit"), N_("Edit mask"), INKSCAPE_ICON("path-mask-edit")), - /* new ObjectVerb(SP_VERB_OBJECT_UNSET_MASK, "ObjectUnSetMask", N_("_Release"), N_("Remove mask from selection"), + new ObjectVerb(SP_VERB_OBJECT_UNSET_MASK, "ObjectUnSetMask", N_("_Release"), N_("Remove mask from selection"), nullptr), new ObjectVerb(SP_VERB_OBJECT_SET_CLIPPATH, "ObjectSetClipPath", N_("_Set"), N_("Apply clipping path to selection (using the topmost object as clipping path)"), nullptr), new ObjectVerb(SP_VERB_OBJECT_SET_INVERSE_CLIPPATH, "ObjectSetInverseClipPath", N_("_Set Inverse (LPE)"), - N_("Apply inverse clipping path to selection (using the topmost object as clipping path)"), nullptr), */ + N_("Apply inverse clipping path to selection (using the topmost object as clipping path)"), nullptr), new ObjectVerb(SP_VERB_OBJECT_CREATE_CLIP_GROUP, "ObjectCreateClipGroup", N_("Create Cl_ip Group"), N_("Creates a clip group using the selected objects as a base"), nullptr), new ObjectVerb(SP_VERB_OBJECT_EDIT_CLIPPATH, "ObjectEditClipPath", N_("_Edit"), N_("Edit clipping path"), - /* new ObjectVerb(SP_VERB_OBJECT_UNSET_CLIPPATH, "ObjectUnSetClipPath", N_("_Release"), - N_("Remove clipping path from selection"), nullptr), */ INKSCAPE_ICON("path-clip-edit")), + new ObjectVerb(SP_VERB_OBJECT_UNSET_CLIPPATH, "ObjectUnSetClipPath", N_("_Release"), + N_("Remove clipping path from selection"), nullptr), // Tools new ContextVerb(SP_VERB_CONTEXT_SELECT, "ToolSelector", NC_("ContextVerb", "Select"), N_("Select and transform objects"), INKSCAPE_ICON("tool-pointer")), @@ -2699,9 +2699,9 @@ Verb *Verb::_base_verbs[] = { new DialogVerb(SP_VERB_DIALOG_DOCPROPERTIES, "DialogDocumentProperties", N_("_Document Properties..."), N_("Edit properties of this document (to be saved with the document)"), INKSCAPE_ICON("document-properties")), - /* new DialogVerb(SP_VERB_DIALOG_FILL_STROKE, "DialogFillStroke", N_("_Fill and Stroke..."), + new DialogVerb(SP_VERB_DIALOG_FILL_STROKE, "DialogFillStroke", N_("_Fill and Stroke..."), N_("Edit objects' colors, gradients, arrowheads, and other fill and stroke properties..."), - INKSCAPE_ICON("dialog-fill-and-stroke")), */ + INKSCAPE_ICON("dialog-fill-and-stroke")), // FIXME: Probably better to either use something from the icon naming spec or ship our own "select-font" icon // Technically what we show are unicode code points and not glyphs. The actual glyphs shown are determined by the // shaping engines. @@ -2711,7 +2711,7 @@ Verb *Verb::_base_verbs[] = { // TRANSLATORS: "Swatches" means: color samples new DialogVerb(SP_VERB_DIALOG_SWATCHES, "DialogSwatches", N_("S_watches..."), N_("Select colors from a swatches palette"), INKSCAPE_ICON("swatches")), - /* new DialogVerb(SP_VERB_DIALOG_SYMBOLS, "DialogSymbols", N_("S_ymbols..."), + new DialogVerb(SP_VERB_DIALOG_SYMBOLS, "DialogSymbols", N_("S_ymbols..."), N_("Select symbol from a symbols palette"), INKSCAPE_ICON("symbols")), new DialogVerb(SP_VERB_DIALOG_PAINT, "DialogPaintServers", N_("_Paint Servers..."), // FIXME missing Inkscape Paint Server Icon @@ -2719,7 +2719,7 @@ Verb *Verb::_base_verbs[] = { new DialogVerb(SP_VERB_DIALOG_TRANSFORM, "DialogTransform", N_("Transfor_m..."), N_("Precisely control objects' transformations"), INKSCAPE_ICON("dialog-transform")), new DialogVerb(SP_VERB_DIALOG_ALIGN_DISTRIBUTE, "DialogAlignDistribute", N_("_Align and Distribute..."), - N_("Align and distribute objects"), INKSCAPE_ICON("dialog-align-and-distribute")), */ + N_("Align and distribute objects"), INKSCAPE_ICON("dialog-align-and-distribute")), new DialogVerb(SP_VERB_DIALOG_UNDO_HISTORY, "DialogUndoHistory", N_("Undo _History..."), N_("Undo History"), INKSCAPE_ICON("edit-undo-history")), new DialogVerb(SP_VERB_DIALOG_TEXT, "DialogText", N_("_Text and Font..."), @@ -2727,8 +2727,8 @@ Verb *Verb::_base_verbs[] = { INKSCAPE_ICON("dialog-text-and-font")), new DialogVerb(SP_VERB_DIALOG_XML_EDITOR, "DialogXMLEditor", N_("_XML Editor..."), N_("View and edit the XML tree of the document"), INKSCAPE_ICON("dialog-xml-editor")), - /* new DialogVerb(SP_VERB_DIALOG_SELECTORS, "DialogSelectors", N_("_Selectors and CSS..."), - N_("View and edit CSS selectors and styles"), INKSCAPE_ICON("dialog-selectors")), */ + new DialogVerb(SP_VERB_DIALOG_SELECTORS, "DialogSelectors", N_("_Selectors and CSS..."), + N_("View and edit CSS selectors and styles"), INKSCAPE_ICON("dialog-selectors")), new DialogVerb(SP_VERB_DIALOG_FIND, "DialogFind", N_("_Find/Replace..."), N_("Find objects in document"), INKSCAPE_ICON("edit-find")), #if WITH_GSPELL @@ -2746,16 +2746,16 @@ Verb *Verb::_base_verbs[] = { N_("Edit the object attributes..."), INKSCAPE_ICON("dialog-object-properties")), new DialogVerb(SP_VERB_DIALOG_ATTR_XML, "DialogAttrDialog", N_("_Object attributes..."), N_("Edit the object attributes..."), INKSCAPE_ICON("dialog-object-properties")), - /* new DialogVerb(SP_VERB_DIALOG_ITEM, "DialogObjectProperties", N_("_Object Properties..."), + new DialogVerb(SP_VERB_DIALOG_ITEM, "DialogObjectProperties", N_("_Object Properties..."), N_("Edit the ID, locked and visible status, and other object properties"), - INKSCAPE_ICON("dialog-object-properties")), */ + INKSCAPE_ICON("dialog-object-properties")), new DialogVerb(SP_VERB_DIALOG_INPUT, "DialogInput", N_("_Input Devices..."), N_("Configure extended input devices, such as a graphics tablet"), INKSCAPE_ICON("dialog-input-devices")), new DialogVerb(SP_VERB_DIALOG_LAYERS, "DialogLayers", N_("Layer_s..."), N_("View Layers"), INKSCAPE_ICON("dialog-layers")), - /* new DialogVerb(SP_VERB_DIALOG_OBJECTS, "DialogObjects", N_("Object_s..."), N_("View Objects"), - INKSCAPE_ICON("dialog-objects")), */ + new DialogVerb(SP_VERB_DIALOG_OBJECTS, "DialogObjects", N_("Object_s..."), N_("View Objects"), + INKSCAPE_ICON("dialog-objects")), new DialogVerb(SP_VERB_DIALOG_STYLE, "DialogStyle", N_("Style Dialog..."), N_("View Style Dialog"), nullptr), 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")), @@ -2827,12 +2827,12 @@ Verb *Verb::_base_verbs[] = { N_("_Resize Page to Selection"), N_("Fit the page to the current selection or the drawing if there is no selection"), nullptr), // LockAndHide - /* new LockAndHideVerb(SP_VERB_UNLOCK_ALL, "UnlockAll", N_("Unlock All"), - N_("Unlock all objects in the current layer"), nullptr), */ + new LockAndHideVerb(SP_VERB_UNLOCK_ALL, "UnlockAll", N_("Unlock All"), + N_("Unlock all objects in the current layer"), nullptr), new LockAndHideVerb(SP_VERB_UNLOCK_ALL_IN_ALL_LAYERS, "UnlockAllInAllLayers", N_("Unlock All in All Layers"), N_("Unlock all objects in all layers"), nullptr), - /* new LockAndHideVerb(SP_VERB_UNHIDE_ALL, "UnhideAll", N_("Unhide All"), - N_("Unhide all objects in the current layer"), nullptr), */ + new LockAndHideVerb(SP_VERB_UNHIDE_ALL, "UnhideAll", N_("Unhide All"), + N_("Unhide all objects in the current layer"), nullptr), new LockAndHideVerb(SP_VERB_UNHIDE_ALL_IN_ALL_LAYERS, "UnhideAllInAllLayers", N_("Unhide All in All Layers"), N_("Unhide all objects in all layers"), nullptr), // Color Management -- GitLab From b6032c23787643a87f3fce76b6d30d9d17adbb4d Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Sat, 12 Jun 2021 15:13:50 +0530 Subject: [PATCH 029/235] Menubar Edit : Basic setup complete Basic setup of all the functions is added . state addition is left --- share/ui/menu-edit.ui | 215 +++++++++++++++++++--- share/ui/menu-object.ui | 2 +- src/CMakeLists.txt | 7 + src/actions/actions-dialogs.cpp | 4 + src/actions/actions-edit.cpp | 174 ++++++++++++++++- src/actions/actions-fit-canvas.cpp | 60 ++++++ src/actions/actions-fit-canvas.h | 8 + src/actions/actions-selection-desktop.cpp | 39 ++++ src/actions/actions-selection-desktop.h | 8 + src/inkscape-window.cpp | 16 +- 10 files changed, 501 insertions(+), 32 deletions(-) create mode 100644 src/actions/actions-fit-canvas.cpp create mode 100644 src/actions/actions-fit-canvas.h create mode 100644 src/actions/actions-selection-desktop.cpp create mode 100644 src/actions/actions-selection-desktop.h diff --git a/share/ui/menu-edit.ui b/share/ui/menu-edit.ui index 2d3b7710d1..36eb079adb 100644 --- a/share/ui/menu-edit.ui +++ b/share/ui/menu-edit.ui @@ -9,6 +9,7 @@ _Undo win.undo + False edit-undo @@ -23,7 +24,7 @@ edit-undo-history - +
Cu_t win.cut @@ -49,14 +50,139 @@ win.paste-style edit-paste-style +
+ + + Paste Si_ze +
+ + Paste Si_ze + win.paste-size + edit-paste-size + + + Paste _Width + win.paste-width + edit-paste-width + + + Paste _Height + win.paste-height + edit-paste-height + + + Paste Size Separately + win.paste-size-separately + edit-paste-size-separately + + + Paste Width Separately + win.paste-width-separately + edit-paste-width-separately + + + Paste Height Separately + win.paste-height-separately + edit-paste-height-separately + +
+
+ +
+ + _Find/Replace... + win.dialog-open + Find + edit-find + +
+ +
+ + Duplic_ate + win.duplicate + edit-duplicate + +
- Paste Si_ze + Clo_ne
- Paste Si_ze - win.paste-size - edit-paste-size + Create Clo_ne + win.clone + edit-clone + + + Create Tiled Clones... + win.dialog-open + Clonetiler + dialog-tile-clones + + + Unlin_k Clone + win.clone-unlink + edit-clone-unlink + + + Unlink Clones _recursively + win.clone-unlink-recursively + edit-clone-unlink + + + Relink to Copied + win.clone-link + edit-clone-link + + + Select _Original + win.select-original + edit-select-original + + + Clone original path (LPE) + win.clone-link-lpe + edit-clone-link-lpe + +
+
+ +
+ + Make a _Bitmap Copy + win.selection-make-bitmap-copy + selection-make-bitmap-copy + +
+ +
+ + _Delete + win.delete + edit-delete + +
+ +
+ + Select Al_l + win.select-all + edit-select-all + + + Select All in All La_yers + win.select-all-layers + edit-select-all-layers + +
+ + + Select Sa_me +
+ + Fill _and Stroke + win.select-same-fill-and-stroke + edit-select-same-fill-and-stroke Paste _Width @@ -64,41 +190,82 @@ edit-paste-width - Paste _Height - win.paste-height - edit-paste-height + _Fill Color + win.select-same-fill + edit-select-same-fill + + + Stroke St_yle + win.select-same-stroke-style + edit-select-same-stroke-style - Paste Size Separately - win.paste-size-separately - edit-paste-size-separately + _Object Type + win.select-same-object-type + edit-select-same-object-type +
+
+ +
- Paste Width Separately - win.paste-width-separately - edit-paste-width-separately + In_vert Selection + win.select-invert + edit-select-invert - Paste Height Separately - win.paste-height-separately - edit-paste-height-separately + D_eselect + win.select-none + edit-select-none
- +
+ + _Resize Page to Selection + win.fit-canvas-to-selection-or-drawing + +
+
- _Find/Replace... + Create _Guides Around the Page + win.create-guides-around-page + + + Lock All Guides + win.lock-all-guides + + + Delete All Guides + win.delete-all-guides + +
+ +
+ + _XML Editor... win.dialog-open - Find - edit-find + XMLEditor + dialog-xml-editor +
+
- Duplic_ate - win.duplicate - edit-duplicate + _Input Devices... + win.dialog-open + Input + dialog-input-devices + + P_references + win.dialog-open + Preferences + preferences-system + +
+ ------- action end ------- diff --git a/share/ui/menu-object.ui b/share/ui/menu-object.ui index 539c6c559d..d5d151d5bf 100644 --- a/share/ui/menu-object.ui +++ b/share/ui/menu-object.ui @@ -38,7 +38,7 @@ _Selectors and CSS... win.dialog-open - Selectors + Preferences diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9a19394cb7..6864905050 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -230,6 +230,13 @@ set(inkscape_SRC actions/actions-output.cpp actions/actions-selection-object.h actions/actions-selection-object.cpp + + actions/actions-selection-desktop.h + actions/actions-selection-desktop.cpp + + actions/actions-fit-canvas.h + actions/actions-fit-canvas.cpp + actions/actions-selection.h actions/actions-selection.cpp actions/actions-tools.h diff --git a/src/actions/actions-dialogs.cpp b/src/actions/actions-dialogs.cpp index 6b13a8cb14..ddee20469b 100644 --- a/src/actions/actions-dialogs.cpp +++ b/src/actions/actions-dialogs.cpp @@ -76,6 +76,10 @@ std::vector> raw_data_dialogs = void dialog_open(const Glib::VariantBase& value, InkscapeWindow *win) { + for(auto x:dialog_data){ + std::cout<<'|'< s = Glib::VariantBase::cast_dynamic >(value); auto dialog = s.get(); diff --git a/src/actions/actions-edit.cpp b/src/actions/actions-edit.cpp index b3b06595aa..6a77ed5d75 100644 --- a/src/actions/actions-edit.cpp +++ b/src/actions/actions-edit.cpp @@ -8,6 +8,7 @@ #include "inkscape-window.h" #include "desktop.h" #include "selection-chemistry.h" +#include "object/sp-guide.h" void object_to_pattern(InkscapeWindow* win) @@ -135,6 +136,139 @@ duplicate(InkscapeWindow* win) dt->selection->duplicate(); } +void +clone(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->clone(); +} + +void +clone_unlink(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->unlink(); +} + +void +clone_unlink_recursively(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->unlinkRecursive(false, true); +} + +void +clone_link(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->relink(); +} + +void +select_original(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->cloneOriginal(); +} + +void +clone_link_lpe(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->cloneOriginalPathLPE(); +} + +void +edit_delete(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->deleteItems(); +} + +void +select_all(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + Inkscape::SelectionHelper::selectAll(dt); +} + +void +select_all_layers(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + Inkscape::SelectionHelper::selectAllInAll(dt); +} + +void +select_same_fill_and_stroke(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + Inkscape::SelectionHelper::selectSameFillStroke(dt); +} + +void +select_same_fill(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + Inkscape::SelectionHelper::selectSameFillColor(dt); +} + +void +select_same_stroke_color(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + Inkscape::SelectionHelper::selectSameStrokeColor(dt); +} + +void +select_same_stroke_style(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + Inkscape::SelectionHelper::selectSameStrokeStyle(dt); +} + +void +select_same_object_type(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + Inkscape::SelectionHelper::selectSameObjectType(dt); +} + +void +select_invert(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + Inkscape::SelectionHelper::invert(dt); +} + +void +select_none(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + Inkscape::SelectionHelper::selectNone(dt); +} + +void +create_guides_around_page(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + sp_guide_create_guides_around_page(dt); +} + +void +lock_all_guides(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->toggleGuidesLock(); +} + +void +delete_all_guides(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + sp_guide_delete_all_guides(dt); +} + std::vector> raw_data_edit = { // clang-format off @@ -155,7 +289,26 @@ std::vector> raw_data_edit = {"win.paste-size-separately", N_("Paste Size Separately"), "Edit", N_("Scale each selected object to match the size of the copied object") }, {"win.paste-width-separately", N_("Paste Width Separately"), "Edit", N_("Scale each selected object horizontally to match the width of the copied object") }, {"win.paste-height-separately", N_("Paste Height Separately"), "Edit", N_("Scale each selected object vertically to match the height of the copied object") }, - {"win.duplicate", N_("Duplic_ate"), "Edit", N_("Duplicate Selected Objects") } + {"win.duplicate", N_("Duplic_ate"), "Edit", N_("Duplicate Selected Objects") }, + {"win.clone", N_("Create Clo_ne"), "Edit", N_("Create a clone (a copy linked to the original) of selected object") }, + {"win.clone-unlink", N_("Unlin_k Clone"), "Edit", N_("Cut the selected clones' links to the originals, turning them into standalone objects") }, + {"win.clone-unlink-recursively", N_("Unlink Clones _recursively"), "Edit", N_("Unlink all clones in the selection, even if they are in groups.") } + ,{"win.clone-link", N_("Relink to Copied"), "Edit", N_("Relink the selected clones to the object currently on the clipboard") } + ,{"win.select-original", N_("Select _Original"), "Edit", N_("Select the object to which the selected clone is linked") } + ,{"win.clone-link-lpe", N_("Clone original path (LPE)"), "Edit", N_("Creates a new path, applies the Clone original LPE, and refers it to the selected path") } + ,{"win.delete", N_("_Delete"), "Edit", N_("Delete selection") } + ,{"win.select-all", N_("Select Al_l"), "Edit", N_("Select all objects or all nodes") } + ,{"win.select-all-layers", N_("Select All in All La_yers"), "Edit", N_("Select all objects in all visible and unlocked layers") } + ,{"win.select-same-fill-and-stroke", N_("Fill _and Stroke"), "Edit", N_("Select all objects with the same fill and stroke as the selected objects") } + ,{"win.select-same-fill", N_("_Fill Color"), "Edit", N_("Select all objects with the same fill as the selected objects") } + ,{"win.select-same-stroke-color", N_("_Stroke Color"), "Edit", N_("Select all objects with the same stroke as the selected objects") } + ,{"win.select-same-stroke-style", N_("Stroke St_yle"), "Edit", N_("Select all objects with the same stroke style (width, dash, markers) as the selected objects") } + ,{"win.select-same-object-type", N_("_Object Type"), "Edit", N_("Select all objects with the same object type (rect, arc, text, path, bitmap etc) as the selected objects") } + ,{"win.select-invert", N_("In_vert Selection"), "Edit", N_("Invert selection (unselect what is selected and select everything else)") } + ,{"win.select-none", N_("D_eselect"), "Edit", N_("Deselect any selected objects or nodes") } + ,{"win.create-guides-around-page", N_("Create _Guides Around the Page"), "Edit", N_("Create four guides aligned with the page borders") } + ,{"win.lock-all-guides", N_("Lock All Guides"), "Edit", N_("Toggle lock of all guides in the document") } + ,{"win.delete-all-guides", N_("Delete All Guides"), "Edit", N_("Delete all the guides in the document") } // clang-format on }; @@ -181,6 +334,25 @@ add_actions_edit(InkscapeWindow* win) win->add_action( "paste-width-separately", sigc::bind(sigc::ptr_fun(&paste_width_separately), win)); win->add_action( "paste-height-separately", sigc::bind(sigc::ptr_fun(&paste_height_separately), win)); win->add_action( "duplicate", sigc::bind(sigc::ptr_fun(&duplicate), win)); + win->add_action( "clone", sigc::bind(sigc::ptr_fun(&clone), win)); + win->add_action( "clone-unlink", sigc::bind(sigc::ptr_fun(&clone_unlink), win)); + win->add_action( "clone-unlink-recursively", sigc::bind(sigc::ptr_fun(&clone_unlink_recursively), win)); + win->add_action( "clone-link", sigc::bind(sigc::ptr_fun(&clone_link), win)); + win->add_action( "select-original", sigc::bind(sigc::ptr_fun(&select_original), win)); + win->add_action( "clone-link-lpe", sigc::bind(sigc::ptr_fun(&clone_link_lpe), win)); + win->add_action( "delete", sigc::bind(sigc::ptr_fun(&edit_delete), win)); + win->add_action( "select-all", sigc::bind(sigc::ptr_fun(&select_all), win)); + win->add_action( "select-all-layers", sigc::bind(sigc::ptr_fun(&select_all_layers), win)); + win->add_action( "select-same-fill-and-stroke", sigc::bind(sigc::ptr_fun(&select_same_fill_and_stroke), win)); + win->add_action( "select-same-fill", sigc::bind(sigc::ptr_fun(&select_same_fill), win)); + win->add_action( "select-same-stroke-color", sigc::bind(sigc::ptr_fun(&select_same_stroke_color), win)); + win->add_action( "select-same-stroke-style", sigc::bind(sigc::ptr_fun(&select_same_stroke_style), win)); + win->add_action( "select-same-object-type", sigc::bind(sigc::ptr_fun(&select_same_object_type), win)); + win->add_action( "select-invert", sigc::bind(sigc::ptr_fun(&select_invert), win)); + win->add_action( "select-none", sigc::bind(sigc::ptr_fun(&select_none), win)); + win->add_action( "create-guides-around-page", sigc::bind(sigc::ptr_fun(&create_guides_around_page), win)); + win->add_action( "lock-all-guides", sigc::bind(sigc::ptr_fun(&lock_all_guides), win)); + win->add_action( "delete-all-guides", sigc::bind(sigc::ptr_fun(&delete_all_guides), win)); // clang-format on auto app = InkscapeApplication::instance(); diff --git a/src/actions/actions-fit-canvas.cpp b/src/actions/actions-fit-canvas.cpp new file mode 100644 index 0000000000..c96edfe6b3 --- /dev/null +++ b/src/actions/actions-fit-canvas.cpp @@ -0,0 +1,60 @@ +#include + +#include // Not ! To eventually allow a headless version! +#include + +#include "actions-fit-canvas.h" +#include "inkscape-application.h" +#include "inkscape-window.h" +#include "desktop.h" +#include "selection-chemistry.h" + +void +fit_canvas_to_selection(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->fitCanvas(true); +} + +void +fit_canvas_drawing(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + if (fit_canvas_to_drawing(dt->getDocument())) { + Inkscape::DocumentUndo::done(dt->getDocument(), _("Fit Page to Drawing"), nullptr); + } +} + +void +canvas_to_selection_or_drawing(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + fit_canvas_to_selection_or_drawing(dt); +} + +std::vector> raw_fit_canvas_data = +{ + // clang-format off + {"win.fit-canvas-to-selection", N_("Fit Page to Selection"), "Selection Desktop", N_("Fit the page to the current selection") }, + {"win.fit-canvas-to-drawing", N_("Fit Page to Drawing"), "Selection Desktop", N_("Fit the page to the drawing") }, + {"win.fit-canvas-to-selection-or-drawing", N_("_Resize Page to Selection"), "Selection Desktop", N_("Fit the page to the current selection or the drawing if there is no selection") } + // clang-format on +}; + +void +add_actions_fit_canvas(InkscapeWindow* win) +{ + std::cout<<"add_actions_fit_canvas\n"; + // clang-format off + win->add_action( "fit-canvas-to-selection", sigc::bind(sigc::ptr_fun(&fit_canvas_to_selection), win)); + win->add_action( "fit-canvas-to-drawing", sigc::bind(sigc::ptr_fun(&fit_canvas_drawing), win)); + win->add_action( "fit-canvas-to-selection-or-drawing", sigc::bind(sigc::ptr_fun(&canvas_to_selection_or_drawing), win)); + // clang-format on + + auto app = InkscapeApplication::instance(); + if (!app) { + std::cerr << "add_actions_edit: no app!" << std::endl; + return; + } + app->get_action_extra_data().add_data(raw_fit_canvas_data); +} \ No newline at end of file diff --git a/src/actions/actions-fit-canvas.h b/src/actions/actions-fit-canvas.h new file mode 100644 index 0000000000..dbf7af29cc --- /dev/null +++ b/src/actions/actions-fit-canvas.h @@ -0,0 +1,8 @@ +#ifndef INK_ACTIONS_FIT_CANVAS_H +#define INK_ACTIONS_FIT_CANVAS_H + +class InkscapeWindow; + +void add_actions_fit_canvas(InkscapeWindow* win); + +#endif // INK_ACTIONS_FIT_CANVAS_H \ No newline at end of file diff --git a/src/actions/actions-selection-desktop.cpp b/src/actions/actions-selection-desktop.cpp new file mode 100644 index 0000000000..4a480ff72d --- /dev/null +++ b/src/actions/actions-selection-desktop.cpp @@ -0,0 +1,39 @@ +#include + +#include // Not ! To eventually allow a headless version! +#include + +#include "actions-selection-desktop.h" +#include "inkscape-application.h" +#include "inkscape-window.h" +#include "desktop.h" +#include "document-undo.h" + +void +selection_make_bitmap_copy(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->createBitmapCopy(); +} + +std::vector> raw_selection_dekstop_data = +{ + // clang-format off + {"win.selection-make-bitmap-copy", N_("Make a _Bitmap Copy"), "Selection Desktop", N_("Export selection to a bitmap and insert it into document") } + // clang-format on +}; + +void +add_actions_select_desktop(InkscapeWindow* win) +{ + // clang-format off + win->add_action( "selection-make-bitmap-copy", sigc::bind(sigc::ptr_fun(&selection_make_bitmap_copy), win)); + // clang-format on + + auto app = InkscapeApplication::instance(); + if (!app) { + std::cerr << "add_actions_edit: no app!" << std::endl; + return; + } + app->get_action_extra_data().add_data(raw_selection_dekstop_data); +} \ No newline at end of file diff --git a/src/actions/actions-selection-desktop.h b/src/actions/actions-selection-desktop.h new file mode 100644 index 0000000000..c066ddc1ff --- /dev/null +++ b/src/actions/actions-selection-desktop.h @@ -0,0 +1,8 @@ +#ifndef INK_ACTIONS_SELECTION_DESKTOP_H +#define INK_ACTIONS_SELECTION_DESKTOP_H + +class InkscapeWindow; + +void add_actions_select_desktop(InkscapeWindow* win); + +#endif // INK_ACTIONS_SELECTION_DESKTOP_H \ No newline at end of file diff --git a/src/inkscape-window.cpp b/src/inkscape-window.cpp index 40c8c2cfb6..9712ad9520 100644 --- a/src/inkscape-window.cpp +++ b/src/inkscape-window.cpp @@ -27,6 +27,8 @@ #include "actions/actions-dialogs.h" #include "actions/actions-hide-lock.h" #include "actions/actions-edit.h" +#include "actions/actions-selection-desktop.h" +#include "actions/actions-fit-canvas.h" #include "actions/actions-tools.h" #include "object/sp-namedview.h" // TODO Remove need for this! @@ -92,13 +94,15 @@ InkscapeWindow::InkscapeWindow(SPDocument* document) // =================== Actions =================== // After canvas has been constructed.. move to canvas proper. - add_actions_canvas_transform(this); // Actions to transform canvas view. + add_actions_canvas_transform(this); // Actions to transform canvas view. add_actions_dialogs(this); // Actions to transform dialog. - add_actions_hide_lock(this); // Actions to transform dialog. - add_actions_edit(this); // Actions to transform dialog. - add_actions_canvas_mode(this); // Actions to change canvas display mode. - add_actions_dialogs(this); // Actions to open dialogs. - add_actions_tools(this); // Actions to switch between tools. + add_actions_hide_lock(this); // Actions to transform dialog. + add_actions_edit(this); // Actions to transform dialog. + add_actions_select_desktop(this); // Actions with desktop selection + add_actions_fit_canvas(this); // Actions to fit canvas + add_actions_canvas_mode(this); // Actions to change canvas display mode. + add_actions_dialogs(this); // Actions to open dialogs. + add_actions_tools(this); // Actions to switch between tools. // ========== Drag and Drop of Documents ========= ink_drag_setup(_desktop_widget); -- GitLab From 2203f5e023ccb441d991a8e899fd7b9d715cb5e3 Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Sun, 13 Jun 2021 01:18:24 +0530 Subject: [PATCH 030/235] Menubar Edit : Refactor left Undo Redo label change --- share/ui/menu-edit.ui | 5 +- src/actions/actions-dialogs.cpp | 4 - src/actions/actions-edit.cpp | 165 ++++++++++++++++++-------------- 3 files changed, 95 insertions(+), 79 deletions(-) diff --git a/share/ui/menu-edit.ui b/share/ui/menu-edit.ui index 36eb079adb..a99b4f8fbe 100644 --- a/share/ui/menu-edit.ui +++ b/share/ui/menu-edit.ui @@ -9,7 +9,6 @@ _Undo win.undo - False edit-undo @@ -232,10 +231,12 @@ Create _Guides Around the Page win.create-guides-around-page + - Lock All Guides + Lock All Guides win.lock-all-guides + Delete All Guides win.delete-all-guides diff --git a/src/actions/actions-dialogs.cpp b/src/actions/actions-dialogs.cpp index ddee20469b..6b13a8cb14 100644 --- a/src/actions/actions-dialogs.cpp +++ b/src/actions/actions-dialogs.cpp @@ -76,10 +76,6 @@ std::vector> raw_data_dialogs = void dialog_open(const Glib::VariantBase& value, InkscapeWindow *win) { - for(auto x:dialog_data){ - std::cout<<'|'< s = Glib::VariantBase::cast_dynamic >(value); auto dialog = s.get(); diff --git a/src/actions/actions-edit.cpp b/src/actions/actions-edit.cpp index 6a77ed5d75..9efe97e5ad 100644 --- a/src/actions/actions-edit.cpp +++ b/src/actions/actions-edit.cpp @@ -256,8 +256,25 @@ create_guides_around_page(InkscapeWindow* win) } void -lock_all_guides(InkscapeWindow* win) +lock_all_guides(InkscapeWindow *win) { + auto action = win->lookup_action("lock-all-guides"); + if (!action) { + std::cerr << "lock_all_guides: action missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "lock_all_guides: action not SimpleAction!" << std::endl; + return; + } + + bool state = false; + saction->get_state(state); + state = !state; + saction->change_state(state); + SPDesktop* dt = win->get_desktop(); dt->toggleGuidesLock(); } @@ -272,87 +289,89 @@ delete_all_guides(InkscapeWindow* win) std::vector> raw_data_edit = { // clang-format off - {"win.object-to-pattern", N_("Objects to Pattern"), "Edit", N_("Convert selection to a rectangle with tiled pattern fill") }, - {"win.pattern-to-object", N_("Pattern to Objects"), "Edit", N_("Extract objects from a tiled pattern fill") }, - {"win.object-to-marker", N_("Objects to Marker"), "Edit", N_("Convert selection to a line marker") }, - {"win.object-to-guides", N_("Objects to Guides"), "Edit", N_("Convert selected objects to a collection of guidelines aligned with their edges") }, - {"win.undo", N_("_Undo"), "Edit", N_("Undo last action") }, - {"win.redo", N_("_Redo"), "Edit", N_("Do again the last undone action") }, - {"win.cut", N_("Cu_t"), "Edit", N_("Cut selection to clipboard") }, - {"win.copy", N_("_Copy"), "Edit", N_("Copy selection to clipboard") }, - {"win.paste", N_("_Paste"), "Edit", N_("Paste objects from clipboard to mouse point, or paste text") }, - {"win.paste-in-place", N_("Paste _In Place"), "Edit", N_("Paste objects from clipboard to mouse point, or paste text") }, - {"win.paste-style", N_("Paste _Style"), "Edit", N_("Apply the style of the copied object to selection") }, - {"win.paste-size", N_("Paste Si_ze"), "Edit", N_("Scale selection to match the size of the copied object") }, - {"win.paste-width", N_("Paste _Width"), "Edit", N_("Scale selection horizontally to match the width of the copied object") }, - {"win.paste-height", N_("Paste _Height"), "Edit", N_("Scale selection vertically to match the height of the copied object") }, - {"win.paste-size-separately", N_("Paste Size Separately"), "Edit", N_("Scale each selected object to match the size of the copied object") }, - {"win.paste-width-separately", N_("Paste Width Separately"), "Edit", N_("Scale each selected object horizontally to match the width of the copied object") }, - {"win.paste-height-separately", N_("Paste Height Separately"), "Edit", N_("Scale each selected object vertically to match the height of the copied object") }, - {"win.duplicate", N_("Duplic_ate"), "Edit", N_("Duplicate Selected Objects") }, - {"win.clone", N_("Create Clo_ne"), "Edit", N_("Create a clone (a copy linked to the original) of selected object") }, - {"win.clone-unlink", N_("Unlin_k Clone"), "Edit", N_("Cut the selected clones' links to the originals, turning them into standalone objects") }, - {"win.clone-unlink-recursively", N_("Unlink Clones _recursively"), "Edit", N_("Unlink all clones in the selection, even if they are in groups.") } - ,{"win.clone-link", N_("Relink to Copied"), "Edit", N_("Relink the selected clones to the object currently on the clipboard") } - ,{"win.select-original", N_("Select _Original"), "Edit", N_("Select the object to which the selected clone is linked") } - ,{"win.clone-link-lpe", N_("Clone original path (LPE)"), "Edit", N_("Creates a new path, applies the Clone original LPE, and refers it to the selected path") } - ,{"win.delete", N_("_Delete"), "Edit", N_("Delete selection") } - ,{"win.select-all", N_("Select Al_l"), "Edit", N_("Select all objects or all nodes") } - ,{"win.select-all-layers", N_("Select All in All La_yers"), "Edit", N_("Select all objects in all visible and unlocked layers") } - ,{"win.select-same-fill-and-stroke", N_("Fill _and Stroke"), "Edit", N_("Select all objects with the same fill and stroke as the selected objects") } - ,{"win.select-same-fill", N_("_Fill Color"), "Edit", N_("Select all objects with the same fill as the selected objects") } - ,{"win.select-same-stroke-color", N_("_Stroke Color"), "Edit", N_("Select all objects with the same stroke as the selected objects") } - ,{"win.select-same-stroke-style", N_("Stroke St_yle"), "Edit", N_("Select all objects with the same stroke style (width, dash, markers) as the selected objects") } - ,{"win.select-same-object-type", N_("_Object Type"), "Edit", N_("Select all objects with the same object type (rect, arc, text, path, bitmap etc) as the selected objects") } - ,{"win.select-invert", N_("In_vert Selection"), "Edit", N_("Invert selection (unselect what is selected and select everything else)") } - ,{"win.select-none", N_("D_eselect"), "Edit", N_("Deselect any selected objects or nodes") } - ,{"win.create-guides-around-page", N_("Create _Guides Around the Page"), "Edit", N_("Create four guides aligned with the page borders") } - ,{"win.lock-all-guides", N_("Lock All Guides"), "Edit", N_("Toggle lock of all guides in the document") } - ,{"win.delete-all-guides", N_("Delete All Guides"), "Edit", N_("Delete all the guides in the document") } + {"win.object-to-pattern", N_("Objects to Pattern"), "Edit", N_("Convert selection to a rectangle with tiled pattern fill")}, + {"win.pattern-to-object", N_("Pattern to Objects"), "Edit", N_("Extract objects from a tiled pattern fill")}, + {"win.object-to-marker", N_("Objects to Marker"), "Edit", N_("Convert selection to a line marker")}, + {"win.object-to-guides", N_("Objects to Guides"), "Edit", N_("Convert selected objects to a collection of guidelines aligned with their edges")}, + {"win.undo", N_("_Undo"), "Edit", N_("Undo last action")}, + {"win.redo", N_("_Redo"), "Edit", N_("Do again the last undone action")}, + {"win.cut", N_("Cu_t"), "Edit", N_("Cut selection to clipboard")}, + {"win.copy", N_("_Copy"), "Edit", N_("Copy selection to clipboard")}, + {"win.paste", N_("_Paste"), "Edit", N_("Paste objects from clipboard to mouse point, or paste text")}, + {"win.paste-in-place", N_("Paste _In Place"), "Edit", N_("Paste objects from clipboard to mouse point, or paste text")}, + {"win.paste-style", N_("Paste _Style"), "Edit", N_("Apply the style of the copied object to selection")}, + {"win.paste-size", N_("Paste Si_ze"), "Edit", N_("Scale selection to match the size of the copied object")}, + {"win.paste-width", N_("Paste _Width"), "Edit", N_("Scale selection horizontally to match the width of the copied object")}, + {"win.paste-height", N_("Paste _Height"), "Edit", N_("Scale selection vertically to match the height of the copied object")}, + {"win.paste-size-separately", N_("Paste Size Separately"), "Edit", N_("Scale each selected object to match the size of the copied object")}, + {"win.paste-width-separately", N_("Paste Width Separately"), "Edit", N_("Scale each selected object horizontally to match the width of the copied object")}, + {"win.paste-height-separately", N_("Paste Height Separately"), "Edit", N_("Scale each selected object vertically to match the height of the copied object")}, + {"win.duplicate", N_("Duplic_ate"), "Edit", N_("Duplicate Selected Objects")}, + {"win.clone", N_("Create Clo_ne"), "Edit", N_("Create a clone (a copy linked to the original) of selected object")}, + {"win.clone-unlink", N_("Unlin_k Clone"), "Edit", N_("Cut the selected clones' links to the originals, turning them into standalone objects")}, + {"win.clone-unlink-recursively", N_("Unlink Clones _recursively"), "Edit", N_("Unlink all clones in the selection, even if they are in groups.")}, + {"win.clone-link", N_("Relink to Copied"), "Edit", N_("Relink the selected clones to the object currently on the clipboard")}, + {"win.select-original", N_("Select _Original"), "Edit", N_("Select the object to which the selected clone is linked")}, + {"win.clone-link-lpe", N_("Clone original path (LPE)"), "Edit", N_("Creates a new path, applies the Clone original LPE, and refers it to the selected path")}, + {"win.delete", N_("_Delete"), "Edit", N_("Delete selection")}, + {"win.select-all", N_("Select Al_l"), "Edit", N_("Select all objects or all nodes")}, + {"win.select-all-layers", N_("Select All in All La_yers"), "Edit", N_("Select all objects in all visible and unlocked layers")}, + {"win.select-same-fill-and-stroke", N_("Fill _and Stroke"), "Edit", N_("Select all objects with the same fill and stroke as the selected objects")}, + {"win.select-same-fill", N_("_Fill Color"), "Edit", N_("Select all objects with the same fill as the selected objects")}, + {"win.select-same-stroke-color", N_("_Stroke Color"), "Edit", N_("Select all objects with the same stroke as the selected objects")}, + {"win.select-same-stroke-style", N_("Stroke St_yle"), "Edit", N_("Select all objects with the same stroke style (width, dash, markers) as the selected objects")}, + {"win.select-same-object-type", N_("_Object Type"), "Edit", N_("Select all objects with the same object type (rect, arc, text, path, bitmap etc) as the selected objects")}, + {"win.select-invert", N_("In_vert Selection"), "Edit", N_("Invert selection (unselect what is selected and select everything else)")}, + {"win.select-none", N_("D_eselect"), "Edit", N_("Deselect any selected objects or nodes")}, + {"win.create-guides-around-page", N_("Create _Guides Around the Page"), "Edit", N_("Create four guides aligned with the page borders")}, + {"win.lock-all-guides", N_("Lock All Guides"), "Edit", N_("Toggle lock of all guides in the document")}, + {"win.delete-all-guides", N_("Delete All Guides"), "Edit", N_("Delete all the guides in the document")} // clang-format on }; void add_actions_edit(InkscapeWindow* win) { + Glib::VariantType String(Glib::VARIANT_TYPE_STRING); + // clang-format off - win->add_action( "object-to-pattern", sigc::bind(sigc::ptr_fun(&object_to_pattern), win)); - win->add_action( "pattern-to-object", sigc::bind(sigc::ptr_fun(&pattern_to_object), win)); - win->add_action( "object-to-marker", sigc::bind(sigc::ptr_fun(&object_to_marker), win)); - win->add_action( "object-to-guides", sigc::bind(sigc::ptr_fun(&object_to_guides), win)); - win->add_action( "undo", sigc::bind(sigc::ptr_fun(&undo), win)); - win->add_action( "redo", sigc::bind(sigc::ptr_fun(&redo), win)); - win->add_action( "cut", sigc::bind(sigc::ptr_fun(&cut), win)); - win->add_action( "copy", sigc::bind(sigc::ptr_fun(©), win)); - win->add_action( "paste", sigc::bind(sigc::ptr_fun(&paste), win)); + win->add_action( "object-to-pattern", sigc::bind(sigc::ptr_fun(&object_to_pattern), win)); + win->add_action( "pattern-to-object", sigc::bind(sigc::ptr_fun(&pattern_to_object), win)); + win->add_action( "object-to-marker", sigc::bind(sigc::ptr_fun(&object_to_marker), win)); + win->add_action( "object-to-guides", sigc::bind(sigc::ptr_fun(&object_to_guides), win)); + win->add_action( "undo", sigc::bind(sigc::ptr_fun(&undo), win)); + win->add_action( "redo", sigc::bind(sigc::ptr_fun(&redo), win)); + win->add_action( "cut", sigc::bind(sigc::ptr_fun(&cut), win)); + win->add_action( "copy", sigc::bind(sigc::ptr_fun(©), win)); + win->add_action( "paste", sigc::bind(sigc::ptr_fun(&paste), win)); win->add_action( "paste-in-place", sigc::bind(sigc::ptr_fun(&paste_in_place), win)); - win->add_action( "paste-style", sigc::bind(sigc::ptr_fun(&paste_style), win)); - win->add_action( "paste-size", sigc::bind(sigc::ptr_fun(&paste_size), win)); - win->add_action( "paste-width", sigc::bind(sigc::ptr_fun(&paste_width), win)); - win->add_action( "paste-height", sigc::bind(sigc::ptr_fun(&paste_height), win)); - win->add_action( "paste-size-separately", sigc::bind(sigc::ptr_fun(&paste_size_separately), win)); - win->add_action( "paste-width-separately", sigc::bind(sigc::ptr_fun(&paste_width_separately), win)); - win->add_action( "paste-height-separately", sigc::bind(sigc::ptr_fun(&paste_height_separately), win)); - win->add_action( "duplicate", sigc::bind(sigc::ptr_fun(&duplicate), win)); - win->add_action( "clone", sigc::bind(sigc::ptr_fun(&clone), win)); - win->add_action( "clone-unlink", sigc::bind(sigc::ptr_fun(&clone_unlink), win)); - win->add_action( "clone-unlink-recursively", sigc::bind(sigc::ptr_fun(&clone_unlink_recursively), win)); - win->add_action( "clone-link", sigc::bind(sigc::ptr_fun(&clone_link), win)); - win->add_action( "select-original", sigc::bind(sigc::ptr_fun(&select_original), win)); + win->add_action( "paste-style", sigc::bind(sigc::ptr_fun(&paste_style), win)); + win->add_action( "paste-size", sigc::bind(sigc::ptr_fun(&paste_size), win)); + win->add_action( "paste-width", sigc::bind(sigc::ptr_fun(&paste_width), win)); + win->add_action( "paste-height", sigc::bind(sigc::ptr_fun(&paste_height), win)); + win->add_action( "paste-size-separately", sigc::bind(sigc::ptr_fun(&paste_size_separately), win)); + win->add_action( "paste-width-separately", sigc::bind(sigc::ptr_fun(&paste_width_separately), win)); + win->add_action( "paste-height-separately", sigc::bind(sigc::ptr_fun(&paste_height_separately), win)); + win->add_action( "duplicate", sigc::bind(sigc::ptr_fun(&duplicate), win)); + win->add_action( "clone", sigc::bind(sigc::ptr_fun(&clone), win)); + win->add_action( "clone-unlink", sigc::bind(sigc::ptr_fun(&clone_unlink), win)); + win->add_action( "clone-unlink-recursively", sigc::bind(sigc::ptr_fun(&clone_unlink_recursively), win)); + win->add_action( "clone-link", sigc::bind(sigc::ptr_fun(&clone_link), win)); + win->add_action( "select-original", sigc::bind(sigc::ptr_fun(&select_original), win)); win->add_action( "clone-link-lpe", sigc::bind(sigc::ptr_fun(&clone_link_lpe), win)); - win->add_action( "delete", sigc::bind(sigc::ptr_fun(&edit_delete), win)); - win->add_action( "select-all", sigc::bind(sigc::ptr_fun(&select_all), win)); - win->add_action( "select-all-layers", sigc::bind(sigc::ptr_fun(&select_all_layers), win)); - win->add_action( "select-same-fill-and-stroke", sigc::bind(sigc::ptr_fun(&select_same_fill_and_stroke), win)); - win->add_action( "select-same-fill", sigc::bind(sigc::ptr_fun(&select_same_fill), win)); - win->add_action( "select-same-stroke-color", sigc::bind(sigc::ptr_fun(&select_same_stroke_color), win)); - win->add_action( "select-same-stroke-style", sigc::bind(sigc::ptr_fun(&select_same_stroke_style), win)); - win->add_action( "select-same-object-type", sigc::bind(sigc::ptr_fun(&select_same_object_type), win)); - win->add_action( "select-invert", sigc::bind(sigc::ptr_fun(&select_invert), win)); - win->add_action( "select-none", sigc::bind(sigc::ptr_fun(&select_none), win)); - win->add_action( "create-guides-around-page", sigc::bind(sigc::ptr_fun(&create_guides_around_page), win)); - win->add_action( "lock-all-guides", sigc::bind(sigc::ptr_fun(&lock_all_guides), win)); - win->add_action( "delete-all-guides", sigc::bind(sigc::ptr_fun(&delete_all_guides), win)); + win->add_action( "delete", sigc::bind(sigc::ptr_fun(&edit_delete), win)); + win->add_action( "select-all", sigc::bind(sigc::ptr_fun(&select_all), win)); + win->add_action( "select-all-layers", sigc::bind(sigc::ptr_fun(&select_all_layers), win)); + win->add_action( "select-same-fill-and-stroke", sigc::bind(sigc::ptr_fun(&select_same_fill_and_stroke), win)); + win->add_action( "select-same-fill", sigc::bind(sigc::ptr_fun(&select_same_fill), win)); + win->add_action( "select-same-stroke-color", sigc::bind(sigc::ptr_fun(&select_same_stroke_color), win)); + win->add_action( "select-same-stroke-style", sigc::bind(sigc::ptr_fun(&select_same_stroke_style), win)); + win->add_action( "select-same-object-type", sigc::bind(sigc::ptr_fun(&select_same_object_type), win)); + win->add_action( "select-invert", sigc::bind(sigc::ptr_fun(&select_invert), win)); + win->add_action( "select-none", sigc::bind(sigc::ptr_fun(&select_none), win)); + win->add_action( "create-guides-around-page", sigc::bind(sigc::ptr_fun(&create_guides_around_page), win)); + win->add_action_bool( "lock-all-guides", sigc::bind(sigc::ptr_fun(&lock_all_guides), win)); + win->add_action( "delete-all-guides", sigc::bind(sigc::ptr_fun(&delete_all_guides), win)); // clang-format on auto app = InkscapeApplication::instance(); -- GitLab From 33a6f89f27a3d5d323339a660c8ed7b04cc755a9 Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Mon, 14 Jun 2021 15:25:18 +0530 Subject: [PATCH 031/235] Menubar Edit : Complete --- share/keys/inkscape.xml | 79 +++-- share/ui/menu-edit.ui | 524 ++++++++++++++--------------- share/ui/menus.xml | 4 +- src/actions/actions-fit-canvas.cpp | 15 +- 4 files changed, 317 insertions(+), 305 deletions(-) diff --git a/share/keys/inkscape.xml b/share/keys/inkscape.xml index 3618a553e9..c2e2e5c12a 100644 --- a/share/keys/inkscape.xml +++ b/share/keys/inkscape.xml @@ -112,8 +112,8 @@ override) the bindings in the main default.xml. - - + @@ -140,9 +140,9 @@ override) the bindings in the main default.xml. - + - + - + @@ -178,13 +178,13 @@ override) the bindings in the main default.xml. - + - + @@ -206,7 +206,7 @@ override) the bindings in the main default.xml. - + @@ -344,19 +344,19 @@ override) the bindings in the main default.xml. - + + --> - - + @@ -370,10 +370,10 @@ override) the bindings in the main default.xml. - + - + @@ -492,6 +492,12 @@ override) the bindings in the main default.xml. + + + + + + @@ -512,4 +518,37 @@ override) the bindings in the main default.xml. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/share/ui/menu-edit.ui b/share/ui/menu-edit.ui index a99b4f8fbe..8a81119b91 100644 --- a/share/ui/menu-edit.ui +++ b/share/ui/menu-edit.ui @@ -1,277 +1,251 @@ - - - - - -
- - _Undo - win.undo - edit-undo - - - _Redo - win.redo - edit-redo - - - Undo _History... - win.dialog-open - UndoHistory - edit-undo-history - -
-
- - Cu_t - win.cut - edit-cut - - - _Copy - win.copy - edit-copy - - - _Paste - win.paste - edit-paste - - - Paste _In Place - win.paste-in-place - edit-paste-in-place - - - Paste _Style - win.paste-style - edit-paste-style - -
- - - Paste Si_ze -
- - Paste Si_ze - win.paste-size - edit-paste-size - - - Paste _Width - win.paste-width - edit-paste-width - - - Paste _Height - win.paste-height - edit-paste-height - - - Paste Size Separately - win.paste-size-separately - edit-paste-size-separately - - - Paste Width Separately - win.paste-width-separately - edit-paste-width-separately - - - Paste Height Separately - win.paste-height-separately - edit-paste-height-separately - -
-
- -
- - _Find/Replace... - win.dialog-open - Find - edit-find - -
- -
- - Duplic_ate - win.duplicate - edit-duplicate - -
- - - Clo_ne -
- - Create Clo_ne - win.clone - edit-clone - - - Create Tiled Clones... - win.dialog-open - Clonetiler - dialog-tile-clones - - - Unlin_k Clone - win.clone-unlink - edit-clone-unlink - - - Unlink Clones _recursively - win.clone-unlink-recursively - edit-clone-unlink - - - Relink to Copied - win.clone-link - edit-clone-link - - - Select _Original - win.select-original - edit-select-original - - - Clone original path (LPE) - win.clone-link-lpe - edit-clone-link-lpe - -
-
- -
- - Make a _Bitmap Copy - win.selection-make-bitmap-copy - selection-make-bitmap-copy - -
- -
- - _Delete - win.delete - edit-delete - -
- -
- - Select Al_l - win.select-all - edit-select-all - - - Select All in All La_yers - win.select-all-layers - edit-select-all-layers - -
- - - Select Sa_me -
- - Fill _and Stroke - win.select-same-fill-and-stroke - edit-select-same-fill-and-stroke - - - Paste _Width - win.paste-width - edit-paste-width - - - _Fill Color - win.select-same-fill - edit-select-same-fill - - - Stroke St_yle - win.select-same-stroke-style - edit-select-same-stroke-style - - - _Object Type - win.select-same-object-type - edit-select-same-object-type - -
-
- -
- - In_vert Selection - win.select-invert - edit-select-invert - - - D_eselect - win.select-none - edit-select-none - -
- -
- - _Resize Page to Selection - win.fit-canvas-to-selection-or-drawing - -
- -
- - Create _Guides Around the Page - win.create-guides-around-page - - - - Lock All Guides - win.lock-all-guides - - - - Delete All Guides - win.delete-all-guides - -
- -
- - _XML Editor... - win.dialog-open - XMLEditor - dialog-xml-editor - -
- -
- - _Input Devices... - win.dialog-open - Input - dialog-input-devices - - - P_references - win.dialog-open - Preferences - preferences-system - -
- - - - ------- action end ------- - - -
- -
\ No newline at end of file + +
+ + _Undo + win.undo + edit-undo + + + _Redo + win.redo + edit-redo + + + Undo _History... + win.dialog-open + UndoHistory + edit-undo-history + +
+
+ + Cu_t + win.cut + edit-cut + + + _Copy + win.copy + edit-copy + + + _Paste + win.paste + edit-paste + + + Paste _In Place + win.paste-in-place + edit-paste-in-place + + + Paste _Style + win.paste-style + edit-paste-style + +
+ + Paste Si_ze +
+ + Paste Si_ze + win.paste-size + edit-paste-size + + + Paste _Width + win.paste-width + edit-paste-width + + + Paste _Height + win.paste-height + edit-paste-height + + + Paste Size Separately + win.paste-size-separately + edit-paste-size-separately + + + Paste Width Separately + win.paste-width-separately + edit-paste-width-separately + + + Paste Height Separately + win.paste-height-separately + edit-paste-height-separately + +
+
+
+ + _Find/Replace... + win.dialog-open + Find + edit-find + +
+
+ + Duplic_ate + win.duplicate + edit-duplicate + +
+ + Clo_ne +
+ + Create Clo_ne + win.clone + edit-clone + + + Create Tiled Clones... + win.dialog-open + Clonetiler + dialog-tile-clones + + + Unlin_k Clone + win.clone-unlink + edit-clone-unlink + + + Unlink Clones _recursively + win.clone-unlink-recursively + edit-clone-unlink + + + Relink to Copied + win.clone-link + edit-clone-link + + + Select _Original + win.select-original + edit-select-original + + + Clone original path (LPE) + win.clone-link-lpe + edit-clone-link-lpe + +
+
+
+ + Make a _Bitmap Copy + win.selection-make-bitmap-copy + selection-make-bitmap-copy + +
+
+ + _Delete + win.delete + edit-delete + +
+
+ + Select Al_l + win.select-all + edit-select-all + + + Select All in All La_yers + win.select-all-layers + edit-select-all-layers + +
+ + Select Sa_me +
+ + Fill _and Stroke + win.select-same-fill-and-stroke + edit-select-same-fill-and-stroke + + + Paste _Width + win.paste-width + edit-paste-width + + + _Fill Color + win.select-same-fill + edit-select-same-fill + + + Stroke St_yle + win.select-same-stroke-style + edit-select-same-stroke-style + + + _Object Type + win.select-same-object-type + edit-select-same-object-type + +
+
+
+ + In_vert Selection + win.select-invert + edit-select-invert + + + D_eselect + win.select-none + edit-select-none + +
+
+ + _Resize Page to Selection + win.fit-canvas-to-selection-or-drawing + +
+
+ + Create _Guides Around the Page + win.create-guides-around-page + + + Lock All Guides + win.lock-all-guides + + + Delete All Guides + win.delete-all-guides + +
+
+ + _XML Editor... + win.dialog-open + XMLEditor + dialog-xml-editor + +
+
+ + _Input Devices... + win.dialog-open + Input + dialog-input-devices + + + P_references + win.dialog-open + Preferences + preferences-system + +
+
+ diff --git a/share/ui/menus.xml b/share/ui/menus.xml index 939cf8f5a8..d0fd7abbc7 100644 --- a/share/ui/menus.xml +++ b/share/ui/menus.xml @@ -27,7 +27,7 @@ - + diff --git a/src/actions/actions-fit-canvas.cpp b/src/actions/actions-fit-canvas.cpp index c96edfe6b3..a426bcffb9 100644 --- a/src/actions/actions-fit-canvas.cpp +++ b/src/actions/actions-fit-canvas.cpp @@ -35,25 +35,24 @@ canvas_to_selection_or_drawing(InkscapeWindow* win) std::vector> raw_fit_canvas_data = { // clang-format off - {"win.fit-canvas-to-selection", N_("Fit Page to Selection"), "Selection Desktop", N_("Fit the page to the current selection") }, - {"win.fit-canvas-to-drawing", N_("Fit Page to Drawing"), "Selection Desktop", N_("Fit the page to the drawing") }, - {"win.fit-canvas-to-selection-or-drawing", N_("_Resize Page to Selection"), "Selection Desktop", N_("Fit the page to the current selection or the drawing if there is no selection") } + {"win.fit-canvas-to-selection", N_("Fit Page to Selection"), "Selection Desktop", N_("Fit the page to the current selection")}, + {"win.fit-canvas-to-drawing", N_("Fit Page to Drawing"), "Selection Desktop", N_("Fit the page to the drawing")}, + {"win.fit-canvas-to-selection-or-drawing", N_("_Resize Page to Selection"), "Selection Desktop", N_("Fit the page to the current selection or the drawing if there is no selection")} // clang-format on }; void add_actions_fit_canvas(InkscapeWindow* win) { - std::cout<<"add_actions_fit_canvas\n"; // clang-format off - win->add_action( "fit-canvas-to-selection", sigc::bind(sigc::ptr_fun(&fit_canvas_to_selection), win)); - win->add_action( "fit-canvas-to-drawing", sigc::bind(sigc::ptr_fun(&fit_canvas_drawing), win)); - win->add_action( "fit-canvas-to-selection-or-drawing", sigc::bind(sigc::ptr_fun(&canvas_to_selection_or_drawing), win)); + win->add_action( "fit-canvas-to-selection", sigc::bind(sigc::ptr_fun(&fit_canvas_to_selection), win)); + win->add_action( "fit-canvas-to-drawing", sigc::bind(sigc::ptr_fun(&fit_canvas_drawing), win)); + win->add_action( "fit-canvas-to-selection-or-drawing", sigc::bind(sigc::ptr_fun(&canvas_to_selection_or_drawing), win)); // clang-format on auto app = InkscapeApplication::instance(); if (!app) { - std::cerr << "add_actions_edit: no app!" << std::endl; + std::cerr << "add_actions_fit_canvas: no app!" << std::endl; return; } app->get_action_extra_data().add_data(raw_fit_canvas_data); -- GitLab From 9feec6cd8771b3da2d105d0d0bc966d2db2d4652 Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Mon, 14 Jun 2021 15:32:37 +0530 Subject: [PATCH 032/235] Menubar Edit : Alignment Fix --- src/CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6864905050..ee693e8204 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -230,13 +230,10 @@ set(inkscape_SRC actions/actions-output.cpp actions/actions-selection-object.h actions/actions-selection-object.cpp - actions/actions-selection-desktop.h actions/actions-selection-desktop.cpp - actions/actions-fit-canvas.h actions/actions-fit-canvas.cpp - actions/actions-selection.h actions/actions-selection.cpp actions/actions-tools.h -- GitLab From 6be7106824e3deca17542467c37be8bcbbe3624a Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Tue, 15 Jun 2021 18:07:42 +0530 Subject: [PATCH 033/235] Over All Refactoring + Menubar Path : Start Menubar Path : key addition left --- share/ui/menu-edit.ui | 24 +- share/ui/menu-object.ui | 309 +++++++++++----------- share/ui/menu-path.ui | 134 ++++++++++ src/actions/actions-edit.cpp | 72 +++-- src/actions/actions-object.cpp | 38 ++- src/actions/actions-selection-desktop.cpp | 74 +++++- src/actions/actions-selection-object.cpp | 33 ++- src/actions/actions-selection.cpp | 150 +++++++++-- src/actions/actions-transform.cpp | 14 +- src/ui/desktop/menubar.cpp | 4 + 10 files changed, 588 insertions(+), 264 deletions(-) create mode 100644 share/ui/menu-path.ui diff --git a/share/ui/menu-edit.ui b/share/ui/menu-edit.ui index 8a81119b91..51dcaf3810 100644 --- a/share/ui/menu-edit.ui +++ b/share/ui/menu-edit.ui @@ -1,7 +1,9 @@ -
+ + +
_Undo win.undo @@ -19,6 +21,7 @@ edit-undo-history
+
Cu_t @@ -46,8 +49,9 @@ edit-paste-style
+ - Paste Si_ze + Paste Si_ze
Paste Si_ze @@ -81,6 +85,7 @@
+
_Find/Replace... @@ -96,8 +101,9 @@ edit-duplicate
+ - Clo_ne + Clo_ne
Create Clo_ne @@ -137,6 +143,7 @@
+
Make a _Bitmap Copy @@ -144,6 +151,7 @@ selection-make-bitmap-copy
+
_Delete @@ -151,6 +159,7 @@ edit-delete
+
Select Al_l @@ -163,8 +172,9 @@ edit-select-all-layers
+ - Select Sa_me + Select Sa_me
Fill _and Stroke @@ -193,6 +203,7 @@
+
In_vert Selection @@ -205,12 +216,14 @@ edit-select-none
+
_Resize Page to Selection win.fit-canvas-to-selection-or-drawing
+
Create _Guides Around the Page @@ -225,6 +238,7 @@ win.delete-all-guides
+
_XML Editor... @@ -233,6 +247,7 @@ dialog-xml-editor
+
_Input Devices... @@ -247,5 +262,6 @@ preferences-system
+
diff --git a/share/ui/menu-object.ui b/share/ui/menu-object.ui index d5d151d5bf..253a14e78a 100644 --- a/share/ui/menu-object.ui +++ b/share/ui/menu-object.ui @@ -1,82 +1,75 @@ - - -
- - Object... - win.dialog-open - Objects - - - _Fill and Stroke... - win.dialog-open - FillStroke - - - - _Object Properties... - win.dialog-open - ObjectProperties - - - - S_ymbols... - win.dialog-open - Symbols - - - - _Paint Servers... - win.dialog-open - PaintServers - - - - _Selectors and CSS... - win.dialog-open - Preferences - -
+
+ + Object... + win.dialog-open + Objects + + + _Fill and Stroke... + win.dialog-open + FillStroke + + + _Object Properties... + win.dialog-open + ObjectProperties + + + S_ymbols... + win.dialog-open + Symbols + + + _Paint Servers... + win.dialog-open + PaintServers + + + _Selectors and CSS... + win.dialog-open + Preferences + +
- -
- - _Group - app.select-object-group - - - _Ungroup - app.select-object-ungroup - - - _Pop Selected Objects out of Group - app.select-object-ungroup-pop - -
- -
- - Cli_p -
- - _Set - app.object-set - - - _Set Inverse (LPE) - app.object-set-inverse - - - _Release - app.object-release - -
-
-
+
+ + _Group + app.select-object-group + + + _Ungroup + app.select-object-ungroup + + + _Pop Selected Objects out of Group + app.select-object-ungroup-pop + +
+ +
+ + Cli_p +
+ + _Set + app.object-set + + + _Set Inverse (LPE) + app.object-set-inverse + + + _Release + app.object-release + +
+
+
Mas_k @@ -96,7 +89,6 @@
- Patter_n
@@ -111,96 +103,91 @@
- -
- - Objects to _Marker - win.object-to-marker - - - Objects to Gu_ides - win.object-to-guides - -
- -
- - Raise to _Top - app.selection-top - selection-top - - - _Raise - app.selection-raise - selection-raise - - - _Lower - app.selection-lower - selection-lower - - - Lower to _Bottom - app.selection-bottom - selection-bottom - -
+
+ + Objects to _Marker + win.object-to-marker + + + Objects to Gu_ides + win.object-to-guides + +
- -
- - Rotate 90° CW - app.object-rotate-90-cw - object-rotate-right - - - Rotate 90° CCW - app.object-rotate-90-ccw - object-rotate-left - - - Flip _Horizontal - app.object-flip-horizontal - object-flip-horizontal - - - Flip _Vertical - app.object-flip-vertical - object-flip-vertical - -
+
+ + Raise to _Top + app.selection-top + selection-top + + + _Raise + app.selection-raise + selection-raise + + + _Lower + app.selection-lower + selection-lower + + + Lower to _Bottom + app.selection-bottom + selection-bottom + +
- -
- - Unhide All - win.unhide-all - - - Unlock All - win.unlock-all - -
+
+ + Rotate 90° CW + app.object-rotate-90-cw + object-rotate-right + + + Rotate 90° CCW + app.object-rotate-90-ccw + object-rotate-left + + + Flip _Horizontal + app.object-flip-horizontal + object-flip-horizontal + + + Flip _Vertical + app.object-flip-vertical + object-flip-vertical + +
+ +
+ + Unhide All + win.unhide-all + + + Unlock All + win.unlock-all + +
- -
- - Transfor_m... - win.dialog-open - Transform - - - _Align and Distribute... - win.dialog-open - AlignDistribute - - - _Arrange... - win.dialog-open - Arrange - -
+
+ + Transfor_m... + win.dialog-open + Transform + + + _Align and Distribute... + win.dialog-open + AlignDistribute + + + _Arrange... + win.dialog-open + Arrange + +
-
\ No newline at end of file diff --git a/share/ui/menu-path.ui b/share/ui/menu-path.ui new file mode 100644 index 0000000000..b866ab0142 --- /dev/null +++ b/share/ui/menu-path.ui @@ -0,0 +1,134 @@ + + + + + +
+ + _Object to Path + app.object-to-path + object-to-path + + + _Stroke to Path + app.object-stroke-to-path + stroke-to-path + + + _Trace Bitmap... + win.object-bitmap-trace + bitmap-trace + +
+ +
+ + _Union + app.select-path-union + path-union + + + _Difference + app.select-path-difference + path-difference + + + _Intersection + app.select-path-intersection + path-intersection + + + E_xclusion + app.select-path-exclusion + path-exclusion + + + Di_vision + app.select-path-division + path-division + + + Cut _Path + app.select-path-cut + path-cut + +
+ +
+ + _Combine + app.select-path-combine + path-combine + + + Break _Apart + app.select-path-break-apart + path-break-apart + +
+ +
+ + I_nset + win.select-path-inset + path-inset + + + Outs_et + win.select-path-outset + path-outset + + + D_ynamic Offset + win.select-path-offset-dynamic + path-offset-dynamic + + + _Linked Offset + win.select-path-offset-linked + path-offset-linked + +
+ +
+ + Fill between paths + app.select-fill-between-paths + +
+ +
+ + Si_mplify + app.select-path-simplify + path-simplify + + + _Reverse + win.select-path-reverse + path-reverse + +
+ +
+ + Path E_ffects... + win.dialog-open + LivePathEffect + dialog-path-effects + + + Paste Path _Effect + win.paste-path-effect + + + Remove Path _Effect + win.remove-path-effect + +
+ + + --- action end --- + +
+
diff --git a/src/actions/actions-edit.cpp b/src/actions/actions-edit.cpp index 9efe97e5ad..18b8058e94 100644 --- a/src/actions/actions-edit.cpp +++ b/src/actions/actions-edit.cpp @@ -286,6 +286,20 @@ delete_all_guides(InkscapeWindow* win) sp_guide_delete_all_guides(dt); } +void +paste_path_effect(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->pastePathEffect(); +} + +void +remove_path_effect(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->removeLPE(); +} + std::vector> raw_data_edit = { // clang-format off @@ -293,39 +307,41 @@ std::vector> raw_data_edit = {"win.pattern-to-object", N_("Pattern to Objects"), "Edit", N_("Extract objects from a tiled pattern fill")}, {"win.object-to-marker", N_("Objects to Marker"), "Edit", N_("Convert selection to a line marker")}, {"win.object-to-guides", N_("Objects to Guides"), "Edit", N_("Convert selected objects to a collection of guidelines aligned with their edges")}, - {"win.undo", N_("_Undo"), "Edit", N_("Undo last action")}, - {"win.redo", N_("_Redo"), "Edit", N_("Do again the last undone action")}, - {"win.cut", N_("Cu_t"), "Edit", N_("Cut selection to clipboard")}, - {"win.copy", N_("_Copy"), "Edit", N_("Copy selection to clipboard")}, - {"win.paste", N_("_Paste"), "Edit", N_("Paste objects from clipboard to mouse point, or paste text")}, - {"win.paste-in-place", N_("Paste _In Place"), "Edit", N_("Paste objects from clipboard to mouse point, or paste text")}, - {"win.paste-style", N_("Paste _Style"), "Edit", N_("Apply the style of the copied object to selection")}, - {"win.paste-size", N_("Paste Si_ze"), "Edit", N_("Scale selection to match the size of the copied object")}, - {"win.paste-width", N_("Paste _Width"), "Edit", N_("Scale selection horizontally to match the width of the copied object")}, - {"win.paste-height", N_("Paste _Height"), "Edit", N_("Scale selection vertically to match the height of the copied object")}, + {"win.undo", N_("Undo"), "Edit", N_("Undo last action")}, + {"win.redo", N_("Redo"), "Edit", N_("Do again the last undone action")}, + {"win.cut", N_("Cut"), "Edit", N_("Cut selection to clipboard")}, + {"win.copy", N_("Copy"), "Edit", N_("Copy selection to clipboard")}, + {"win.paste", N_("Paste"), "Edit", N_("Paste objects from clipboard to mouse point, or paste text")}, + {"win.paste-in-place", N_("Paste In Place"), "Edit", N_("Paste objects from clipboard to mouse point, or paste text")}, + {"win.paste-style", N_("Paste Style"), "Edit", N_("Apply the style of the copied object to selection")}, + {"win.paste-size", N_("Paste Size"), "Edit", N_("Scale selection to match the size of the copied object")}, + {"win.paste-width", N_("Paste Width"), "Edit", N_("Scale selection horizontally to match the width of the copied object")}, + {"win.paste-height", N_("Paste Height"), "Edit", N_("Scale selection vertically to match the height of the copied object")}, {"win.paste-size-separately", N_("Paste Size Separately"), "Edit", N_("Scale each selected object to match the size of the copied object")}, {"win.paste-width-separately", N_("Paste Width Separately"), "Edit", N_("Scale each selected object horizontally to match the width of the copied object")}, {"win.paste-height-separately", N_("Paste Height Separately"), "Edit", N_("Scale each selected object vertically to match the height of the copied object")}, - {"win.duplicate", N_("Duplic_ate"), "Edit", N_("Duplicate Selected Objects")}, - {"win.clone", N_("Create Clo_ne"), "Edit", N_("Create a clone (a copy linked to the original) of selected object")}, - {"win.clone-unlink", N_("Unlin_k Clone"), "Edit", N_("Cut the selected clones' links to the originals, turning them into standalone objects")}, - {"win.clone-unlink-recursively", N_("Unlink Clones _recursively"), "Edit", N_("Unlink all clones in the selection, even if they are in groups.")}, + {"win.duplicate", N_("Duplicate"), "Edit", N_("Duplicate Selected Objects")}, + {"win.clone", N_("Create Clone"), "Edit", N_("Create a clone (a copy linked to the original) of selected object")}, + {"win.clone-unlink", N_("Unlink Clone"), "Edit", N_("Cut the selected clones' links to the originals, turning them into standalone objects")}, + {"win.clone-unlink-recursively", N_("Unlink Clones recursively"), "Edit", N_("Unlink all clones in the selection, even if they are in groups.")}, {"win.clone-link", N_("Relink to Copied"), "Edit", N_("Relink the selected clones to the object currently on the clipboard")}, - {"win.select-original", N_("Select _Original"), "Edit", N_("Select the object to which the selected clone is linked")}, + {"win.select-original", N_("Select Original"), "Edit", N_("Select the object to which the selected clone is linked")}, {"win.clone-link-lpe", N_("Clone original path (LPE)"), "Edit", N_("Creates a new path, applies the Clone original LPE, and refers it to the selected path")}, - {"win.delete", N_("_Delete"), "Edit", N_("Delete selection")}, - {"win.select-all", N_("Select Al_l"), "Edit", N_("Select all objects or all nodes")}, - {"win.select-all-layers", N_("Select All in All La_yers"), "Edit", N_("Select all objects in all visible and unlocked layers")}, - {"win.select-same-fill-and-stroke", N_("Fill _and Stroke"), "Edit", N_("Select all objects with the same fill and stroke as the selected objects")}, - {"win.select-same-fill", N_("_Fill Color"), "Edit", N_("Select all objects with the same fill as the selected objects")}, - {"win.select-same-stroke-color", N_("_Stroke Color"), "Edit", N_("Select all objects with the same stroke as the selected objects")}, - {"win.select-same-stroke-style", N_("Stroke St_yle"), "Edit", N_("Select all objects with the same stroke style (width, dash, markers) as the selected objects")}, - {"win.select-same-object-type", N_("_Object Type"), "Edit", N_("Select all objects with the same object type (rect, arc, text, path, bitmap etc) as the selected objects")}, - {"win.select-invert", N_("In_vert Selection"), "Edit", N_("Invert selection (unselect what is selected and select everything else)")}, - {"win.select-none", N_("D_eselect"), "Edit", N_("Deselect any selected objects or nodes")}, - {"win.create-guides-around-page", N_("Create _Guides Around the Page"), "Edit", N_("Create four guides aligned with the page borders")}, + {"win.delete", N_("Delete"), "Edit", N_("Delete selection")}, + {"win.select-all", N_("Select All"), "Edit", N_("Select all objects or all nodes")}, + {"win.select-all-layers", N_("Select All in All Layers"), "Edit", N_("Select all objects in all visible and unlocked layers")}, + {"win.select-same-fill-and-stroke", N_("Fill and Stroke"), "Edit", N_("Select all objects with the same fill and stroke as the selected objects")}, + {"win.select-same-fill", N_("Fill Color"), "Edit", N_("Select all objects with the same fill as the selected objects")}, + {"win.select-same-stroke-color", N_("Stroke Color"), "Edit", N_("Select all objects with the same stroke as the selected objects")}, + {"win.select-same-stroke-style", N_("Stroke Style"), "Edit", N_("Select all objects with the same stroke style (width, dash, markers) as the selected objects")}, + {"win.select-same-object-type", N_("Object Type"), "Edit", N_("Select all objects with the same object type (rect, arc, text, path, bitmap etc) as the selected objects")}, + {"win.select-invert", N_("Invert Selection"), "Edit", N_("Invert selection (unselect what is selected and select everything else)")}, + {"win.select-none", N_("Deselect"), "Edit", N_("Deselect any selected objects or nodes")}, + {"win.create-guides-around-page", N_("Create Guides Around the Page"), "Edit", N_("Create four guides aligned with the page borders")}, {"win.lock-all-guides", N_("Lock All Guides"), "Edit", N_("Toggle lock of all guides in the document")}, - {"win.delete-all-guides", N_("Delete All Guides"), "Edit", N_("Delete all the guides in the document")} + {"win.delete-all-guides", N_("Delete All Guides"), "Edit", N_("Delete all the guides in the document")}, + {"win.paste-path-effect", N_("Paste Path Effect"), "Edit", N_("Apply the path effect of the copied object to selection")}, + {"win.remove-path-effect", N_("Remove Path Effect"), "Edit", N_("Remove any path effects from selected objects")} // clang-format on }; @@ -372,6 +388,8 @@ add_actions_edit(InkscapeWindow* win) win->add_action( "create-guides-around-page", sigc::bind(sigc::ptr_fun(&create_guides_around_page), win)); win->add_action_bool( "lock-all-guides", sigc::bind(sigc::ptr_fun(&lock_all_guides), win)); win->add_action( "delete-all-guides", sigc::bind(sigc::ptr_fun(&delete_all_guides), win)); + win->add_action( "paste-path-effect", sigc::bind(sigc::ptr_fun(&paste_path_effect), win)); + win->add_action( "remove-path-effect", sigc::bind(sigc::ptr_fun(&remove_path_effect), win)); // clang-format on auto app = InkscapeApplication::instance(); diff --git a/src/actions/actions-object.cpp b/src/actions/actions-object.cpp index 03060fc668..9c3bafe600 100644 --- a/src/actions/actions-object.cpp +++ b/src/actions/actions-object.cpp @@ -222,25 +222,22 @@ object_simplify_path(InkscapeApplication *app) std::vector> raw_data_object = { // clang-format off - {"app.object-set-attribute", N_("Set Attribute"), "Object", N_("Set or update an attribute of selected objects; usage: object-set-attribute:attribute name, attribute value;") }, - {"app.object-set-property", N_("Set Property"), "Object", N_("Set or update a property on selected objects; usage: object-set-property:property name, property value;") }, - {"app.object-unlink-clones", N_("Unlink Clones"), "Object", N_("Unlink clones and symbols") }, - {"app.object-to-path", N_("Object To Path"), "Object", N_("Convert shapes to paths") }, - {"app.object-stroke-to-path", N_("Stroke to Path"), "Object", N_("Convert strokes to paths") }, - {"app.object-simplify-path", N_("Simplify Path"), "Object", N_("Simplify paths, reducing node counts") }, - /* Clip */ - {"app.object-set", N_("Object Clip Set"), "Object", N_("Apply clipping path to selection (using the topmost object as clipping path)") }, - {"app.object-set-inverse", N_("Object Clip Set Inverse"), "Object", N_("Apply inverse clipping path to selection (using the topmost object as clipping path)") }, - {"app.object-release", N_("Object Clip Release"), "Object", N_("Remove clipping path from selection") }, - /* Mask */ - {"app.object-set-mask", N_("Object Mask Set"), "Object", N_("Apply mask to selection (using the topmost object as mask)") }, - {"app.object-set-inverse-mask", N_("Object Mask Set Inverse"), "Object", N_("Set Inverse (LPE)") }, - {"app.object-release-mask", N_("Object Mask Release"), "Object", N_("Remove mask from selection") }, - /* Rotate */ - {"app.object-rotate-90-cw", N_("Object Rotate 90"), "Object", N_("Rotate selection 90° clockwise") }, - {"app.object-rotate-90-ccw", N_("Object Rotate 90 CCW"), "Object", N_("Rotate selection 90° counter-clockwise") }, - {"app.object-flip-horizontal", N_("Object Flip Horizontal"), "Object", N_("Flip selected objects horizontally") }, - {"app.object-flip-vertical", N_("Object Flip Vertical"), "Object", N_("Flip selected objects vertically") } + {"app.object-set-attribute", N_("Set Attribute"), "Object", N_("Set or update an attribute of selected objects; usage: object-set-attribute:attribute name, attribute value;")}, + {"app.object-set-property", N_("Set Property"), "Object", N_("Set or update a property on selected objects; usage: object-set-property:property name, property value;")}, + {"app.object-unlink-clones", N_("Unlink Clones"), "Object", N_("Unlink clones and symbols")}, + {"app.object-to-path", N_("Object To Path"), "Object", N_("Convert shapes to paths")}, + {"app.object-stroke-to-path", N_("Stroke to Path"), "Object", N_("Convert strokes to paths")}, + {"app.object-simplify-path", N_("Simplify Path"), "Object", N_("Simplify paths, reducing node counts")}, + {"app.object-set", N_("Object Clip Set"), "Object", N_("Apply clipping path to selection (using the topmost object as clipping path)")}, + {"app.object-set-inverse", N_("Object Clip Set Inverse"), "Object", N_("Apply inverse clipping path to selection (using the topmost object as clipping path)")}, + {"app.object-release", N_("Object Clip Release"), "Object", N_("Remove clipping path from selection")}, + {"app.object-set-mask", N_("Object Mask Set"), "Object", N_("Apply mask to selection (using the topmost object as mask)")}, + {"app.object-set-inverse-mask", N_("Object Mask Set Inverse"), "Object", N_("Set Inverse (LPE)")}, + {"app.object-release-mask", N_("Object Mask Release"), "Object", N_("Remove mask from selection")}, + {"app.object-rotate-90-cw", N_("Object Rotate 90"), "Object", N_("Rotate selection 90° clockwise")}, + {"app.object-rotate-90-ccw", N_("Object Rotate 90 CCW"), "Object", N_("Rotate selection 90° counter-clockwise")}, + {"app.object-flip-horizontal", N_("Object Flip Horizontal"), "Object", N_("Flip selected objects horizontally")}, + {"app.object-flip-vertical", N_("Object Flip Vertical"), "Object", N_("Flip selected objects vertically")} // clang-format on }; @@ -272,15 +269,12 @@ add_actions_object(InkscapeApplication* app) gapp->add_action( "object-to-path", sigc::bind(sigc::ptr_fun(&object_to_path), app)); gapp->add_action( "object-stroke-to-path", sigc::bind(sigc::ptr_fun(&object_stroke_to_path), app)); gapp->add_action( "object-simplify-path", sigc::bind(sigc::ptr_fun(&object_simplify_path), app)); - /* Clip */ gapp->add_action( "object-set", sigc::bind(sigc::ptr_fun(&set_clip), app)); gapp->add_action( "object-set-inverse", sigc::bind(sigc::ptr_fun(&object_set_inverse), app)); gapp->add_action( "object-release", sigc::bind(sigc::ptr_fun(&object_release), app)); - /* Mask */ gapp->add_action( "object-set-mask", sigc::bind(sigc::ptr_fun(&set_mask), app)); gapp->add_action( "object-set-inverse-mask", sigc::bind(sigc::ptr_fun(&object_set_inverse_mask), app)); gapp->add_action( "object-release-mask", sigc::bind(sigc::ptr_fun(&object_release_mask), app)); - /* Rotate */ gapp->add_action( "object-rotate-90-cw", sigc::bind(sigc::ptr_fun(&object_rotate_90_cw), app)); gapp->add_action( "object-rotate-90-ccw", sigc::bind(sigc::ptr_fun(&object_rotate_90_cw), app)); gapp->add_action( "object-flip-horizontal", sigc::bind(sigc::ptr_fun(&object_flip_horizontal), app)); diff --git a/src/actions/actions-selection-desktop.cpp b/src/actions/actions-selection-desktop.cpp index 4a480ff72d..866cb08e2c 100644 --- a/src/actions/actions-selection-desktop.cpp +++ b/src/actions/actions-selection-desktop.cpp @@ -8,6 +8,10 @@ #include "inkscape-window.h" #include "desktop.h" #include "document-undo.h" +#include "ui/dialog/dialog-container.h" +#include "path/path-offset.h" +#include "actions/actions-tools.h" +#include "selection-chemistry.h" void selection_make_bitmap_copy(InkscapeWindow* win) @@ -16,10 +20,70 @@ selection_make_bitmap_copy(InkscapeWindow* win) dt->selection->createBitmapCopy(); } +void +object_bitmap_trace(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + Inkscape::UI::Dialog::DialogContainer *container = dt->getContainer(); + container->new_dialog("Trace"); +} + +void +select_path_inset(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->removeLPESRecursive(true); + dt->selection->unlinkRecursive(true); + sp_selected_path_inset(dt); +} + +void +select_path_outset(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->removeLPESRecursive(true); + dt->selection->unlinkRecursive(true); + sp_selected_path_offset(dt); +} + +void +select_path_offset_dynamic(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->removeLPESRecursive(true); + dt->selection->unlinkRecursive(true); + sp_selected_path_create_offset_object_zero(dt); + set_active_tool(dt,"Node"); +} + +void +select_path_offset_linked(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + dt->selection->removeLPESRecursive(true); + dt->selection->unlinkRecursive(true); + sp_selected_path_create_updating_offset_object_zero(dt); + set_active_tool(dt, "Node"); +} + +void +select_path_reverse(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + Inkscape::SelectionHelper::reverse(dt); +} + std::vector> raw_selection_dekstop_data = { // clang-format off - {"win.selection-make-bitmap-copy", N_("Make a _Bitmap Copy"), "Selection Desktop", N_("Export selection to a bitmap and insert it into document") } + {"win.selection-make-bitmap-copy", N_("Make a Bitmap Copy"), "Selection Desktop", N_("Export selection to a bitmap and insert it into document")}, + {"win.object-bitmap-trace", N_("Trace Bitmap..."), "Selection Desktop", N_("Create one or more paths from a bitmap by tracing it")}, + {"win.select-path-inset", N_("Inset"), "Selection Desktop", N_("Inset selected paths")}, + {"win.select-path-outset", N_("Outset"), "Selection Desktop", N_("Outset selected paths")}, + {"win.select-path-offset-dynamic", N_("Dynamic Offset"), "Selection Desktop", N_("Create a dynamic offset object")}, + {"win.select-path-offset-linked", N_("Linked Offset"), "Selection Desktop", N_("Create a dynamic offset object linked to the original path")}, + {"win.select-path-reverse", N_("Reverse"), "Selection Desktop", N_("Reverse the direction of selected paths (useful for flipping markers)")} + // clang-format on }; @@ -27,7 +91,13 @@ void add_actions_select_desktop(InkscapeWindow* win) { // clang-format off - win->add_action( "selection-make-bitmap-copy", sigc::bind(sigc::ptr_fun(&selection_make_bitmap_copy), win)); + win->add_action( "selection-make-bitmap-copy", sigc::bind(sigc::ptr_fun(&selection_make_bitmap_copy), win)); + win->add_action( "object-bitmap-trace", sigc::bind(sigc::ptr_fun(&object_bitmap_trace), win)); + win->add_action( "select-path-inset", sigc::bind(sigc::ptr_fun(&select_path_inset),win)); + win->add_action( "select-path-outset", sigc::bind(sigc::ptr_fun(&select_path_outset),win)); + win->add_action( "select-path-offset-dynamic", sigc::bind(sigc::ptr_fun(&select_path_offset_dynamic),win)); + win->add_action( "select-path-offset-linked", sigc::bind(sigc::ptr_fun(&select_path_offset_linked),win)); + win->add_action( "select-path-reverse", sigc::bind(sigc::ptr_fun(&select_path_reverse),win)); // clang-format on auto app = InkscapeApplication::instance(); diff --git a/src/actions/actions-selection-object.cpp b/src/actions/actions-selection-object.cpp index 391b75668b..3db6dc3598 100644 --- a/src/actions/actions-selection-object.cpp +++ b/src/actions/actions-selection-object.cpp @@ -65,15 +65,14 @@ selection_bottom(InkscapeApplication* app) // SHOULD REALLY BE DOC ACTIONS std::vector> raw_data_selection_object = { - /* Group */ - {"app.select-object-group", N_("Group"), "Select", N_("Group selected objects") }, - {"app.select-object-ungroup", N_("Ungroup"), "Select", N_("Ungroup selected objects") }, - {"app.select-object-ungroup-pop", N_("Pop Selected Objects out of Group"), "Select", N_("Pop selected objects out of group") }, - /* Rise and Lower */ - {"app.selection-top", N_("Raise to Top"), "Select", N_("Raise selection to top") }, - {"app.selection-raise", N_("Raise"), "Select", N_("Raise selection one step") }, - {"app.selection-lower", N_("Lower"), "Select", N_("Lower selection one step") }, - {"app.selection-bottom", N_("Lower to Bottom"), "Select", N_("Lower selection to bottom") } + // clang-format off + { "app.select-object-group", N_("Group"), "Select", N_("Group selected objects")}, + { "app.select-object-ungroup", N_("Ungroup"), "Select", N_("Ungroup selected objects")}, + { "app.select-object-ungroup-pop", N_("Pop Selected Objects out of Group"), "Select", N_("Pop selected objects out of group")}, + { "app.selection-top", N_("Raise to Top"), "Select", N_("Raise selection to top")}, + { "app.selection-raise", N_("Raise"), "Select", N_("Raise selection one step")}, + { "app.selection-lower", N_("Lower"), "Select", N_("Lower selection one step")}, + { "app.selection-bottom", N_("Lower to Bottom"), "Select", N_("Lower selection to bottom")} // clang-format on }; @@ -83,15 +82,13 @@ add_actions_selection_object(InkscapeApplication* app) auto *gapp = app->gio_app(); // clang-format off - /* Group */ - gapp->add_action( "select-object-group", sigc::bind(sigc::ptr_fun(&select_object_group), app) ); - gapp->add_action( "select-object-ungroup", sigc::bind(sigc::ptr_fun(&select_object_ungroup), app) ); - gapp->add_action( "select-object-ungroup-pop", sigc::bind(sigc::ptr_fun(&select_object_ungroup_pop), app) ); - /* Rise and Lower */ - gapp->add_action( "selection-top", sigc::bind(sigc::ptr_fun(&selection_top), app) ); - gapp->add_action( "selection-raise", sigc::bind(sigc::ptr_fun(&selection_raise), app) ); - gapp->add_action( "selection-lower", sigc::bind(sigc::ptr_fun(&selection_lower), app) ); - gapp->add_action( "selection-bottom", sigc::bind(sigc::ptr_fun(&selection_bottom), app) ); + gapp->add_action( "select-object-group", sigc::bind(sigc::ptr_fun(&select_object_group), app)); + gapp->add_action( "select-object-ungroup", sigc::bind(sigc::ptr_fun(&select_object_ungroup), app)); + gapp->add_action( "select-object-ungroup-pop", sigc::bind(sigc::ptr_fun(&select_object_ungroup_pop), app)); + gapp->add_action( "selection-top", sigc::bind(sigc::ptr_fun(&selection_top), app)); + gapp->add_action( "selection-raise", sigc::bind(sigc::ptr_fun(&selection_raise), app)); + gapp->add_action( "selection-lower", sigc::bind(sigc::ptr_fun(&selection_lower), app)); + gapp->add_action( "selection-bottom", sigc::bind(sigc::ptr_fun(&selection_bottom), app)); // clang-format on app->get_action_extra_data().add_data(raw_data_selection_object); diff --git a/src/actions/actions-selection.cpp b/src/actions/actions-selection.cpp index 03e61d10fc..0e32dfa707 100644 --- a/src/actions/actions-selection.cpp +++ b/src/actions/actions-selection.cpp @@ -232,21 +232,115 @@ select_list(InkscapeApplication* app) } } +void +object_path_union(InkscapeApplication *app) +{ + auto selection = app->get_active_selection(); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); + selection->pathUnion(); +} + +void +select_path_difference(InkscapeApplication *app) +{ + auto selection = app->get_active_selection(); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); + selection->pathDiff(); + +} + +void +select_path_intersection(InkscapeApplication *app) +{ + auto selection = app->get_active_selection(); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); + selection->pathIntersect(); +} + +void +select_path_exclusion(InkscapeApplication *app) +{ + auto selection = app->get_active_selection(); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); + selection->pathSymDiff(); +} + +void +select_path_division(InkscapeApplication *app) +{ + auto selection = app->get_active_selection(); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); + selection->pathCut(); +} + +void +select_path_cut(InkscapeApplication *app) +{ + auto selection = app->get_active_selection(); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); + selection->pathSlice(); +} + +void +select_path_combine(InkscapeApplication *app) +{ + auto selection = app->get_active_selection(); + selection->unlinkRecursive(true); + selection->combine(); +} + +void +select_path_break_apart(InkscapeApplication *app) +{ + auto selection = app->get_active_selection(); + selection->breakApart(); +} + +void +fill_between_paths(InkscapeApplication *app) +{ + auto selection = app->get_active_selection(); + selection->fillBetweenMany(); +} + +void +select_path_simplify(InkscapeApplication *app) +{ + auto selection = app->get_active_selection(); + selection->simplifyPaths(); +} + // SHOULD REALLY BE DOC ACTIONS std::vector> raw_data_selection = { // clang-format offs - {"app.select-clear", N_("Clear Selection"), "Select", N_("Clear selection") }, - {"app.select", N_("Select"), "Select", N_("Select by ID (deprecated)") }, - {"app.unselect", N_("Deselect"), "Select", N_("Deselect by ID (deprecated)") }, - {"app.select-by-id", N_("Select by ID"), "Select", N_("Select by ID") }, - {"app.unselect-by-id", N_("Deselect by ID"), "Select", N_("Deselect by ID") }, - {"app.select-by-class", N_("Select by Class"), "Select", N_("Select by class") }, - {"app.select-by-element", N_("Select by Element"), "Select", N_("Select by SVG element (e.g. 'rect')") }, - {"app.select-by-selector", N_("Select by Selector"), "Select", N_("Select by CSS selector") }, - {"app.select-all", N_("Select All"), "Select", N_("Select all; options: 'all' (every object including groups), 'layers', 'no-layers' (top level objects in layers), 'groups' (all groups including layers), 'no-groups' (all objects other than groups and layers, default)")}, - {"app.select-invert", N_("Invert Selection"), "Select", N_("Invert selection; options: 'all', 'layers', 'no-layers', 'groups', 'no-groups' (default)")}, - {"app.select-list", N_("List Selection"), "Select", N_("Print a list of objects in current selection") } + {"app.select-clear", N_("Clear Selection"), "Select", N_("Clear selection")}, + {"app.select", N_("Select"), "Select", N_("Select by ID (deprecated)")}, + {"app.unselect", N_("Deselect"), "Select", N_("Deselect by ID (deprecated)")}, + {"app.select-by-id", N_("Select by ID"), "Select", N_("Select by ID")}, + {"app.unselect-by-id", N_("Deselect by ID"), "Select", N_("Deselect by ID")}, + {"app.select-by-class", N_("Select by Class"), "Select", N_("Select by class")}, + {"app.select-by-element", N_("Select by Element"), "Select", N_("Select by SVG element (e.g. 'rect')")}, + {"app.select-by-selector", N_("Select by Selector"), "Select", N_("Select by CSS selector")}, + {"app.select-all", N_("Select All Objects"), "Select", N_("Select all; options: 'all' (every object including groups), 'layers', 'no-layers' (top level objects in layers), 'groups' (all groups including layers), 'no-groups' (all objects other than groups and layers, default)")}, + {"app.select-invert", N_("Invert Selection"), "Select", N_("Invert selection; options: 'all', 'layers', 'no-layers', 'groups', 'no-groups' (default)")}, + {"app.select-list", N_("List Selection"), "Select", N_("Print a list of objects in current selection")}, + {"app.select-path-union", N_("Union"), "Select", N_("Create union of selected paths")}, + {"app.select-path-difference", N_("Difference"), "Select", N_("Create difference of selected paths (bottom minus top)")}, + {"app.select-path-intersection", N_("Intersection"), "Select", N_("Create intersection of selected paths")}, + {"app.select-path-exclusion", N_("Exclusion"), "Select", N_("Create exclusive OR of selected paths (those parts that belong to only one path)")}, + {"app.select-path-division", N_("Division"), "Select", N_("Cut the bottom path into pieces")}, + {"app.select-path-cut", N_("Cut Path"), "Select", N_("Cut the bottom path's stroke into pieces, removing fill")}, + {"app.select-path-combine", N_("Combine"), "Select", N_("Combine several paths into one")}, + {"app.select-path-break-apart", N_("Break Apart"), "Select", N_("Break selected paths into subpaths")}, + {"app.select-fill-between-paths", N_("Fill between paths"), "Select", N_("Create a fill object using the selected paths")}, + {"app.select-path-simplify", N_("Simplify"), "Select", N_("Simplify selected paths (remove extra nodes)")} // clang-format on }; @@ -256,18 +350,28 @@ add_actions_selection(InkscapeApplication* app) auto *gapp = app->gio_app(); // clang-format off - gapp->add_action( "select-clear", sigc::bind(sigc::ptr_fun(&select_clear), app) ); - gapp->add_action_radio_string( "select", sigc::bind(sigc::ptr_fun(&select_by_id), app), "null"); // Backwards compatible. - gapp->add_action_radio_string( "unselect", sigc::bind(sigc::ptr_fun(&unselect_by_id), app), "null"); // Match select. - gapp->add_action_radio_string( "select-by-id", sigc::bind(sigc::ptr_fun(&select_by_id), app), "null"); - gapp->add_action_radio_string( "unselect-by-id", sigc::bind(sigc::ptr_fun(&unselect_by_id), app), "null"); - gapp->add_action_radio_string( "select-by-class", sigc::bind(sigc::ptr_fun(&select_by_class), app), "null"); - gapp->add_action_radio_string( "select-by-element", sigc::bind(sigc::ptr_fun(&select_by_element), app), "null"); - gapp->add_action_radio_string( "select-by-selector", sigc::bind(sigc::ptr_fun(&select_by_selector), app), "null"); - gapp->add_action_radio_string( "select-all", sigc::bind(sigc::ptr_fun(&select_all), app), "null"); - gapp->add_action_radio_string( "select-invert", sigc::bind(sigc::ptr_fun(&select_invert), app), "null"); - gapp->add_action( "select-list", sigc::bind(sigc::ptr_fun(&select_list), app) ); - // clang-format on + gapp->add_action( "select-clear", sigc::bind(sigc::ptr_fun(&select_clear), app) ); + gapp->add_action_radio_string( "select", sigc::bind(sigc::ptr_fun(&select_by_id), app), "null"); // Backwards compatible. + gapp->add_action_radio_string( "unselect", sigc::bind(sigc::ptr_fun(&unselect_by_id), app), "null"); // Match select. + gapp->add_action_radio_string( "select-by-id", sigc::bind(sigc::ptr_fun(&select_by_id), app), "null"); + gapp->add_action_radio_string( "unselect-by-id", sigc::bind(sigc::ptr_fun(&unselect_by_id), app), "null"); + gapp->add_action_radio_string( "select-by-class", sigc::bind(sigc::ptr_fun(&select_by_class), app), "null"); + gapp->add_action_radio_string( "select-by-element", sigc::bind(sigc::ptr_fun(&select_by_element), app), "null"); + gapp->add_action_radio_string( "select-by-selector", sigc::bind(sigc::ptr_fun(&select_by_selector), app), "null"); + gapp->add_action_radio_string( "select-all", sigc::bind(sigc::ptr_fun(&select_all), app), "null"); + gapp->add_action_radio_string( "select-invert", sigc::bind(sigc::ptr_fun(&select_invert), app), "null"); + gapp->add_action( "select-list", sigc::bind(sigc::ptr_fun(&select_list), app) ); + gapp->add_action( "select-path-union", sigc::bind(sigc::ptr_fun(&object_path_union), app)); + gapp->add_action( "select-path-difference", sigc::bind(sigc::ptr_fun(&select_path_difference), app)); + gapp->add_action( "select-path-intersection", sigc::bind(sigc::ptr_fun(&select_path_intersection), app)); + gapp->add_action( "select-path-exclusion", sigc::bind(sigc::ptr_fun(&select_path_exclusion), app)); + gapp->add_action( "select-path-division", sigc::bind(sigc::ptr_fun(&select_path_division), app)); + gapp->add_action( "select-path-cut", sigc::bind(sigc::ptr_fun(&select_path_cut), app)); + gapp->add_action( "select-path-combine", sigc::bind(sigc::ptr_fun(&select_path_combine), app)); + gapp->add_action( "select-path-break-apart", sigc::bind(sigc::ptr_fun(&select_path_break_apart), app)); + gapp->add_action( "select-fill-between-paths", sigc::bind(sigc::ptr_fun(&fill_between_paths), app)); + gapp->add_action( "select-path-simplify", sigc::bind(sigc::ptr_fun(&select_path_simplify), app)); + // clangt on app->get_action_extra_data().add_data(raw_data_selection); } diff --git a/src/actions/actions-transform.cpp b/src/actions/actions-transform.cpp index 379c755e1b..34511f5efe 100644 --- a/src/actions/actions-transform.cpp +++ b/src/actions/actions-transform.cpp @@ -87,19 +87,19 @@ transform_remove(InkscapeApplication *app) std::vector> raw_data_transform = { // clang-format off - {"app.transform-translate", N_("Translate"), "Transform", N_("Translate selected objects (dx,dy)") }, - {"app.transform-rotate", N_("Rotate"), "Transform", N_("Rotate selected objects by degrees") }, - {"app.transform-scale", N_("Scale"), "Transform", N_("Scale selected objects by scale factor") }, - {"app.transform-remove", N_("Remove Transforms"), "Transform", N_("Remove any transforms from selected objects") } + {"app.transform-translate", N_("Translate"), "Transform", N_("Translate selected objects (dx,dy)")}, + {"app.transform-rotate", N_("Rotate"), "Transform", N_("Rotate selected objects by degrees")}, + {"app.transform-scale", N_("Scale"), "Transform", N_("Scale selected objects by scale factor")}, + {"app.transform-remove", N_("Remove Transforms"), "Transform", N_("Remove any transforms from selected objects")} // clang-format on }; std::vector> hint_data_transform = { // clang-format off - {"app.transform-translate", N_("Give two String input for translation") }, - {"app.transform-rotate", N_("Give Double input for angle of Rotation") }, - {"app.transform-scale", N_("Give Double input for Scale") } + {"app.transform-translate", N_("Give two String input for translation")}, + {"app.transform-rotate", N_("Give Double input for angle of Rotation")}, + {"app.transform-scale", N_("Give Double input for Scale")} // clang-format on }; diff --git a/src/ui/desktop/menubar.cpp b/src/ui/desktop/menubar.cpp index 2825c46b2d..1bb006af1e 100644 --- a/src/ui/desktop/menubar.cpp +++ b/src/ui/desktop/menubar.cpp @@ -470,6 +470,10 @@ build_menu(Gtk::MenuShell* menu, Inkscape::XML::Node* xml, Inkscape::UI::View::V filename = Inkscape::IO::Resource::get_filename(Inkscape::IO::Resource::UIS, "menu-edit.ui"); menuname = "edit-menu"; } + else if (strcmp(name, "_Path") == 0) { + filename = Inkscape::IO::Resource::get_filename(Inkscape::IO::Resource::UIS, "menu-path.ui"); + menuname = "path-menu"; + } if(filename!=""){ try -- GitLab From d538e5ddc57ad62f8b9a38280836412eda0d8203 Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Tue, 15 Jun 2021 19:06:37 +0530 Subject: [PATCH 034/235] Menu Path : Key Add Key Add and Refactoring --- share/keys/inkscape.xml | 48 ++++++++++++++++++++++++++--------------- share/ui/menu-path.ui | 5 +---- share/ui/menus.xml | 4 ++-- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/share/keys/inkscape.xml b/share/keys/inkscape.xml index c2e2e5c12a..916322a24a 100644 --- a/share/keys/inkscape.xml +++ b/share/keys/inkscape.xml @@ -208,7 +208,7 @@ override) the bindings in the main default.xml. - + @@ -219,7 +219,7 @@ override) the bindings in the main default.xml. - + + @@ -254,7 +254,7 @@ override) the bindings in the main default.xml. - + --> @@ -262,10 +262,10 @@ override) the bindings in the main default.xml. - + @@ -277,9 +277,9 @@ override) the bindings in the main default.xml. - + @@ -362,13 +362,13 @@ override) the bindings in the main default.xml. - + @@ -485,7 +485,6 @@ override) the bindings in the main default.xml. - @@ -493,33 +492,29 @@ override) the bindings in the main default.xml. - + + - - - - - @@ -551,4 +546,23 @@ override) the bindings in the main default.xml. + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/share/ui/menu-path.ui b/share/ui/menu-path.ui index b866ab0142..ced95ed645 100644 --- a/share/ui/menu-path.ui +++ b/share/ui/menu-path.ui @@ -2,7 +2,7 @@ - +
_Object to Path @@ -127,8 +127,5 @@
- - --- action end --- -
diff --git a/share/ui/menus.xml b/share/ui/menus.xml index d0fd7abbc7..b3252f1f0d 100644 --- a/share/ui/menus.xml +++ b/share/ui/menus.xml @@ -244,7 +244,7 @@
- + -- GitLab From b10d5e5a33d143336d3d9199202dfb09d71907a1 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 16 Jun 2021 21:41:23 +0200 Subject: [PATCH 035/235] some checkpoing commit before deleting much code and rewriting the code again using bool operations. --- src/helper/NonOverlappingPathsBuilder.cpp | 254 ++++++++++++++++------ 1 file changed, 183 insertions(+), 71 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 4f3411c12e..11436248d0 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -19,6 +19,7 @@ #include "ui/widget/canvas.h" #include "geom-pathstroke.h" #include +#include <2geom/intersection-graph.h> namespace Inkscape { @@ -378,11 +379,10 @@ public: std::vector break_into_non_overlapping_pieces(const PathHelper &path) { - // FIXME you can't rely on the current area calculation method. make it work with curves. - assert(!share_an_operation_with(path)); + Geom::PathIntersectionGraph pig(paths, path.paths, Geom::EPSILON); PathHelper intersection; - intersection.paths = sp_pathvector_boolop(paths, path.paths, bool_op_inters, fill_nonZero, fill_nonZero); + intersection.paths = pig.getIntersection(); clean(intersection.paths); if (intersection.paths.empty()) { @@ -390,23 +390,14 @@ public: } intersection.item = sp_item_repr_compare_position_bool(path.item, this->item) ? this->item : path.item; - put_operations(*this, intersection); - put_operations(path, intersection); - intersection.operations.insert(counter); PathHelper diff1; diff1.item = path.item; - diff1.paths = sp_pathvector_boolop(paths, path.paths, bool_op_diff, fill_nonZero, fill_nonZero); - put_operations(path, diff1); - diff1.operations.insert(counter); - clean(diff1.paths); + diff1.paths = pig.getAminusB(); PathHelper diff2; diff2.item = this->item; - diff2.paths = sp_pathvector_boolop(path.paths, paths, bool_op_diff, fill_nonZero, fill_nonZero); - put_operations(*this, diff2); - diff2.operations.insert(counter); - clean(diff2.paths); + diff2.paths = pig.getBminusA(); counter++; @@ -423,8 +414,60 @@ public: return break_apart_non_touching(result); } + +// std::vector break_into_non_overlapping_pieces(const PathHelper &path) +// { +// // FIXME you can't rely on the current area calculation method. make it work with curves. +// assert(!share_an_operation_with(path)); +// +// Geom::PathIntersectionGraph pig(paths, path.paths, Geom::EPSILON); +// +// PathHelper intersection; +// intersection.paths = pig.getIntersection(); +// clean(intersection.paths); +// +// if (intersection.paths.empty()) { +// return {}; +// } +// +// intersection.item = sp_item_repr_compare_position_bool(path.item, this->item) ? this->item : path.item; +// put_operations(*this, intersection); +// put_operations(path, intersection); +// intersection.operations.insert(counter); +// +// PathHelper diff1; +// diff1.item = path.item; +// diff1.paths = pig.getAminusB(); +// put_operations(path, diff1); +// diff1.operations.insert(counter); +// clean(diff1.paths); +// +// PathHelper diff2; +// diff2.item = this->item; +// diff2.paths = pig.getBminusA(); +// put_operations(*this, diff2); +// diff2.operations.insert(counter); +// clean(diff2.paths); +// +// counter++; +// +// std::vector result = {intersection, diff1, diff2}; +// +// for (int i = 0; i < result.size(); i++) { +// if (result[i].paths.empty()) { +// result.erase(result.begin() + i); +// i--; +// } +// } +// +// std::vector break_apart_non_touching(std::vector &paths); +// +// return break_apart_non_touching(result); +// } }; +int PathHelper::counter = 0; + std::vector break_apart_non_touching(std::vector &paths) { std::vector result; @@ -437,67 +480,136 @@ std::vector break_apart_non_touching(std::vector &paths) return result; } -int PathHelper::counter = 0; - void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() { - std::vector paths; - for (int i = 0; i < original_paths_sorted.size(); i++) { - paths.push_back({original_paths_sorted[i], original_items_sorted[i], {}}); - } - - for (int i = 0; i < paths.size(); i++) { - for (int j = 0; j < paths.size(); j++) { - - if (i == j) { - continue; - } - - std::cout << "Path 1:\n"; - print_bounds_and_nodes_count(paths[i].paths); - - std::cout << "Path 2:\n"; - print_bounds_and_nodes_count(paths[j].paths); - - if (paths[i].share_an_operation_with(paths[j])) { - std::cout << "Share operation/s.\n\n"; - continue; - } - - auto broken = paths[i].break_into_non_overlapping_pieces(paths[j]); - - if (broken.empty()) { - std::cout << "Don't intersect.\n\n"; - continue; - } - - std::cout << "Operations:\n"; - for (int i = 0; i < broken.size(); i++) { - std::cout << i << ": "; - for (int op : broken[i].operations) { - std::cout << op << ' '; - } - std::cout << '\n'; - } - std::cout << "Broken.\n\n"; - - // j must be deleted before i since i is smaller. - paths.erase(paths.begin() + j); - paths.erase(paths.begin() + i); - j--; // i is not being incremented in the next iteration. shouldn't be decremented. - - paths.insert(paths.end(), broken.begin(), broken.end()); - } - } + auto path1 = original_paths_sorted[0]; + auto path2 = original_paths_sorted[1]; + +// Geom::PathIntersectionGraph pig(path1, path2, Geom::EPSILON); + +// auto intersection = pig.getIntersection(); + auto intersection = sp_pathvector_boolop(path1, path2, bool_op_inters, fill_nonZero, fill_nonZero); + if (intersection.empty()) { return; } +// auto diff1 = pig.getAminusB(); +// auto diff2 = pig.getBminusA(); + auto diff1 = sp_pathvector_boolop(path1, path2, bool_op_diff, fill_nonZero, fill_nonZero); + auto diff2 = sp_pathvector_boolop(path2, path1, bool_op_diff, fill_nonZero, fill_nonZero); + + result_nodes.resize(3); + result_nodes[0] = draw_on_canvas(diff1, original_items_sorted[0]); + result_nodes[1] = draw_on_canvas(intersection, nullptr); // nullptr means no style will be copied. just a black shape. + result_nodes[2] = draw_on_canvas(diff2, original_items_sorted[1]); +} - int n = paths.size(); - result_nodes.clear(); - result_nodes.resize(n); +//void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() +//{ +// std::vector paths; +// for (int i = 0; i < original_paths_sorted.size(); i++) { +// paths.push_back({original_paths_sorted[i], original_items_sorted[i], {}}); +// } +// +// for (int i = 0; i < paths.size(); i++) { +// for (int j = 0; j < paths.size(); j++) { +// +// if (i == j) { +// continue; +// } +// +// std::cout << "Path 1:\n"; +// print_bounds_and_nodes_count(paths[i].paths); +// +// std::cout << "Path 2:\n"; +// print_bounds_and_nodes_count(paths[j].paths); +// +// if (paths[i].share_an_operation_with(paths[j])) { +// std::cout << "Share operation/s.\n\n"; +// continue; +// } +// +// auto broken = paths[i].break_into_non_overlapping_pieces(paths[j]); +// +// if (broken.empty()) { +// std::cout << "Don't intersect.\n\n"; +// continue; +// } +// +// // j must be deleted before i since i is smaller. +// paths.erase(paths.begin() + j); +// paths.erase(paths.begin() + i); +// j--; // i is not being incremented in the next iteration. shouldn't be decremented. +// +// paths.insert(paths.end(), broken.begin(), broken.end()); +// } +// } +// +// int n = paths.size(); +// result_nodes.clear(); +// result_nodes.resize(n); +// +// for (int i = 0; i < n; i++) { +// result_nodes[i] = draw_on_canvas(paths[i].paths, paths[i].item); +// } +//} - for (int i = 0; i < n; i++) { - result_nodes[i] = draw_on_canvas(paths[i].paths, paths[i].item); - } -} +//void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() +//{ +// std::vector paths; +// for (int i = 0; i < original_paths_sorted.size(); i++) { +// paths.push_back({original_paths_sorted[i], original_items_sorted[i], {}}); +// } +// +// for (int i = 0; i < paths.size(); i++) { +// for (int j = 0; j < paths.size(); j++) { +// +// if (i == j) { +// continue; +// } +// +// std::cout << "Path 1:\n"; +// print_bounds_and_nodes_count(paths[i].paths); +// +// std::cout << "Path 2:\n"; +// print_bounds_and_nodes_count(paths[j].paths); +// +// if (paths[i].share_an_operation_with(paths[j])) { +// std::cout << "Share operation/s.\n\n"; +// continue; +// } +// +// auto broken = paths[i].break_into_non_overlapping_pieces(paths[j]); +// +// if (broken.empty()) { +// std::cout << "Don't intersect.\n\n"; +// continue; +// } +// +// std::cout << "Operations:\n"; +// for (int i = 0; i < broken.size(); i++) { +// std::cout << i << ": "; +// for (int op : broken[i].operations) { +// std::cout << op << ' '; +// } +// std::cout << '\n'; +// } +// std::cout << "Broken.\n\n"; +// +// // j must be deleted before i since i is smaller. +// paths.erase(paths.begin() + j); +// paths.erase(paths.begin() + i); +// j--; // i is not being incremented in the next iteration. shouldn't be decremented. +// +// paths.insert(paths.end(), broken.begin(), broken.end()); +// } +// } +// +// int n = paths.size(); +// result_nodes.clear(); +// result_nodes.resize(n); +// +// for (int i = 0; i < n; i++) { +// result_nodes[i] = draw_on_canvas(paths[i].paths, paths[i].item); +// } +//} template // Geom::Path or Geom::PathVector XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, -- GitLab From cbe36c9cd03aa78212111ff7785501d3446cf690 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 17 Jun 2021 02:32:17 +0200 Subject: [PATCH 036/235] A working breaking function! yet needs some cleanups for the results. Found the bug that was causing the result to be wrong. yet, the result contains some noise which needs to be removed. --- src/helper/NonOverlappingPathsBuilder.cpp | 570 +++++----------------- src/helper/NonOverlappingPathsBuilder.h | 40 +- 2 files changed, 134 insertions(+), 476 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 11436248d0..30889c583a 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -19,6 +19,7 @@ #include "ui/widget/canvas.h" #include "geom-pathstroke.h" #include +#include #include <2geom/intersection-graph.h> namespace Inkscape { @@ -39,7 +40,7 @@ void NonOverlappingPathsBuilder::build() set->toCurves(); set->the_temporary_fix_for_the_transform_bug(); - set_items_sorted(); + set_parameters(); construct_non_overlapping_pieces(); if (is_destructive) { @@ -56,6 +57,34 @@ void NonOverlappingPathsBuilder::build() unset_desktop_busy(); } +SPItem* NonOverlappingPathsBuilder::get_most_item(std::function cmp) +{ + SPItem *result = items.front(); + for (auto item : items) { + if (cmp(item, result)) { + result = item; + } + } + return result; +} + +void NonOverlappingPathsBuilder::set_parameters() +{ + auto _items = set->items(); + items = std::vector(_items.begin(), _items.end()); + XML::Node *item = set->topRepr(); + parent = item->parent(); + + if (put_above_originals) { + auto cmp = [](SPItem *a, SPItem *b) { return sp_item_repr_compare_position_bool(a, b); }; + after = get_most_item(cmp)->getRepr(); + } else { + auto cmp = [](SPItem *a, SPItem *b) { return !sp_item_repr_compare_position_bool(a, b); }; + after = get_most_item(cmp)->getRepr()->prev(); + } + +} + SPDesktop *NonOverlappingPathsBuilder::desktop() { return set->desktop(); @@ -79,104 +108,6 @@ void NonOverlappingPathsBuilder::unset_desktop_busy() } } -Geom::PathVector NonOverlappingPathsBuilder::get_all_paths_combined() -{ - Geom::PathVector result; - - for (auto &path : original_paths_sorted) { - result.insert(result.end(), path.begin(), path.end()); - } - - return result; -} - -bool points_equal(const Geom::Point &a, const Geom::Point &b, double epsilon = 0.001) -{ - // std::cout << "Diff: " << abs(a.x() - b.x()) << ", " << abs(a.y() - b.y()) << '\n'; - // std::cout << "X1: " << a.x() << ", "; - // std::cout << "X2: " << b.x() << ", "; - // std::cout << "Y1: " << a.y() << ", "; - // std::cout << "Y2: " << b.y() << "\n"; - return abs(a.x() - b.x()) < epsilon && abs(a.y() - b.y()) < epsilon; -} - -bool point_on_edge(const Geom::PathVector &paths, const Geom::Point &point) -{ - auto nodes = paths.nodes(); - // std::cout << "Nodes count: " << nodes.size() << '\n'; - // int idx = 1; - for (auto &node : nodes) { - // std::cout << "Point " << idx++ << ":\n"; - if (points_equal(point, node)) { - std::cout << "Match of " << node << " and " << point << ".\n"; - return true; - } - } - // std::cout << "No match at all\n"; - return false; -} - -// SPItem *NonOverlappingPathsBuilder::get_top_item_with_points_on_edges(const std::vector &points) -//{ -// for (int i = 0; i < original_paths_sorted.size(); i++) { -// auto &path = original_paths_sorted[i]; -// bool is_valid = true; -// int pt_idx = 1; -// for (auto &point : points) { -// std::cout << "Path " << i + 1 << ", Point " << pt_idx++ << ":\n"; -// if (!point_on_edge(point, path)) { -// std::cout << "Path no match with points...\n"; -// is_valid = false; -// break; -// } -// } -// if (is_valid) { -// return original_items_sorted[i]; -// } -// } -// return nullptr; -//} - -SPItem *NonOverlappingPathsBuilder::get_top_item_that_intersects_with_points(const std::vector &points) -{ - for (int i = 0; i < original_paths_sorted.size(); i++) { - auto &path = original_paths_sorted[i]; - bool is_valid = true; - for (auto &point : points) { - if (!path.winding(point)) { - is_valid = false; - break; - } - } - if (is_valid) { - return original_items_sorted[i]; - } - } - return nullptr; -} - -SPItem *NonOverlappingPathsBuilder::get_top_item_at_point(const Geom::Point &point) -{ - // TODO is the version with multiple points even used? - return get_top_item_that_intersects_with_points({point}); -} - -Geom::PathVector NonOverlappingPathsBuilder::get_bounds_path_vector(const Geom::OptRect &bounds) -{ - auto x1 = bounds->left() - expansion_offset; - auto x2 = bounds->right() + expansion_offset; - auto y1 = bounds->bottom() + expansion_offset; - auto y2 = bounds->top() - expansion_offset; - Geom::Rect res_bounds(Geom::Point(x1, y1), Geom::Point(x2, y2)); - return {Geom::Path(res_bounds)}; -} - -Geom::PathVector NonOverlappingPathsBuilder::get_bounds_path_vector(const Geom::PathVector &paths) -{ - auto bounds = paths.boundsFast(); - return get_bounds_path_vector(bounds); -} - static double path_area_non_self_intersecting_no_curves(const Geom::Path &path) { // This function doesn't take curves into consideration. @@ -241,61 +172,6 @@ static Geom::PathVector remove_lines_from_pathvec(const Geom::PathVector &paths) return result; } -// removes a line that is part of the path. a line that starts at point x -// and keeps extending as a line (possibly multiple nodes are involved) -// and ends at the same point (or close). -//static Geom::Path remove_loop_lines_from_path(const Geom::Path &path) -//{ -// // FIXME not working as expected... -// Geom::Path result; -// result.setStitching(true); -// std::vector curves; -// for (auto &curve : path) { -// curves.push_back(curve.duplicate()); -// } -// int n = curves.size(); -// for (int i = 1; i < n; i++) { -// for (int j = n - 2; j > i; j--) { -// if (points_equal(curves[i]->initialPoint(), curves[j]->finalPoint(), 0.001)) { -// Geom::Path temp; -// temp.setStitching(true); -// for (int x = i; x <= j; x++) { -// temp.append(curves[i]->duplicate()); -// } -// if (path_area_non_self_intersecting_no_curves(temp) < 0.1) { -// result.append(curves[i]->duplicate()); -// i = j; -// break; -// } -// } -// } -// result.append(curves[i]->duplicate()); -// } -// -// for (Geom::Curve *curve : curves) { -// delete curve; -// } -// -// return result; -//} -// -//static Geom::PathVector remove_loop_lines_from_pathvec(const Geom::PathVector &paths) -//{ -// Geom::PathVector result; -// for (auto &path : paths) { -// auto new_path = remove_loop_lines_from_path(path); -// if (!path.empty()) { -// result.push_back(new_path); -// } -// } -// return result; -//} - -double get_bounding_area(const Geom::PathVector &path) -{ - return path.boundsFast()->area(); -} - template bool is_intersecting(const Path1 &a, const Path2 &b) { for (auto &node : b.nodes()) { @@ -343,61 +219,79 @@ class PathHelper { static int counter; - void put_operations(const PathHelper &from, PathHelper &to) + void clean() { - to.operations.insert(from.operations.begin(), from.operations.end()); - } - - void clean(Geom::PathVector &paths) - { - double max_area = 0; - for (auto &path : paths) { - max_area = std::max(max_area, path_area_non_self_intersecting_no_curves(path)); - } paths = remove_lines_from_pathvec(paths); // paths = remove_loop_lines_from_pathvec(paths); - paths = remove_paths_with_small_area_from_pathvec(paths, max_area/100); -// paths = remove_paths_with_small_area_from_pathvec(paths, 0.5); + // paths = remove_paths_with_small_area_from_pathvec(paths, 0.5); + // double max_area = 0; + // for (auto &path : paths) { + // max_area = std::max(max_area, path_area_non_self_intersecting_no_curves(path)); + // } + // paths = remove_paths_with_small_area_from_pathvec(paths, max_area/100); } public: + Geom::PathVector paths; SPItem *item; std::set operations; + PathHelper() {} + + PathHelper(Geom::PathVector paths, SPItem *item, std::set operations) + : paths(std::move(paths)) + , item(item) + , operations(std::move(operations)) + {} + + PathHelper(Geom::PathVector paths, SPItem *item, std::set operations1, const std::set &operations2) + : paths(std::move(paths)) + , item(item) + , operations(std::move(operations1)) + { + operations.insert(operations2.begin(), operations2.end()); + } + bool part_of_operation(int operation) { return operations.find(operation) != operations.end(); } - bool share_an_operation_with(const PathHelper &path) + int get_common_operation(const PathHelper &path) { for (auto operation : path.operations) { if (part_of_operation(operation)) { - return true; + return operation; } } - return false; + return -1; + } + + void prepare_for_current_operation() + { + operations.insert(counter); + clean(); } std::vector break_into_non_overlapping_pieces(const PathHelper &path) { - Geom::PathIntersectionGraph pig(paths, path.paths, Geom::EPSILON); + // FIXME you can't rely on the current area calculation method. make it work with curves. + assert(get_common_operation(path) == -1); - PathHelper intersection; - intersection.paths = pig.getIntersection(); - clean(intersection.paths); + auto intersection_paths = sp_pathvector_boolop(paths, path.paths, bool_op_inters, fill_nonZero, fill_nonZero); + auto intersection_item = sp_item_repr_compare_position_bool(path.item, this->item) ? this->item : path.item; + PathHelper intersection(intersection_paths, intersection_item, operations, path.operations); + intersection.prepare_for_current_operation(); if (intersection.paths.empty()) { return {}; } - intersection.item = sp_item_repr_compare_position_bool(path.item, this->item) ? this->item : path.item; + auto diff1_paths = sp_pathvector_boolop(paths, path.paths, bool_op_diff, fill_nonZero, fill_nonZero); + PathHelper diff1(diff1_paths, path.item, path.operations); + diff1.prepare_for_current_operation(); - PathHelper diff1; - diff1.item = path.item; - diff1.paths = pig.getAminusB(); - - PathHelper diff2; - diff2.item = this->item; - diff2.paths = pig.getBminusA(); + auto diff2_paths = sp_pathvector_boolop(path.paths, paths, bool_op_diff, fill_nonZero, fill_nonZero); + PathHelper diff2(diff2_paths, item, operations); + diff2.prepare_for_current_operation(); counter++; @@ -414,56 +308,6 @@ public: return break_apart_non_touching(result); } - -// std::vector break_into_non_overlapping_pieces(const PathHelper &path) -// { -// // FIXME you can't rely on the current area calculation method. make it work with curves. -// assert(!share_an_operation_with(path)); -// -// Geom::PathIntersectionGraph pig(paths, path.paths, Geom::EPSILON); -// -// PathHelper intersection; -// intersection.paths = pig.getIntersection(); -// clean(intersection.paths); -// -// if (intersection.paths.empty()) { -// return {}; -// } -// -// intersection.item = sp_item_repr_compare_position_bool(path.item, this->item) ? this->item : path.item; -// put_operations(*this, intersection); -// put_operations(path, intersection); -// intersection.operations.insert(counter); -// -// PathHelper diff1; -// diff1.item = path.item; -// diff1.paths = pig.getAminusB(); -// put_operations(path, diff1); -// diff1.operations.insert(counter); -// clean(diff1.paths); -// -// PathHelper diff2; -// diff2.item = this->item; -// diff2.paths = pig.getBminusA(); -// put_operations(*this, diff2); -// diff2.operations.insert(counter); -// clean(diff2.paths); -// -// counter++; -// -// std::vector result = {intersection, diff1, diff2}; -// -// for (int i = 0; i < result.size(); i++) { -// if (result[i].paths.empty()) { -// result.erase(result.begin() + i); -// i--; -// } -// } -// -// std::vector break_apart_non_touching(std::vector &paths); -// -// return break_apart_non_touching(result); -// } }; int PathHelper::counter = 0; @@ -474,146 +318,14 @@ std::vector break_apart_non_touching(std::vector &paths) for (auto &path : paths) { auto broken = break_apart_non_touching(path.paths); for (auto &broken_path : broken) { - result.push_back({broken_path, path.item, path.operations}); + result.emplace_back(broken_path, path.item, path.operations); } } return result; } -void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() -{ - auto path1 = original_paths_sorted[0]; - auto path2 = original_paths_sorted[1]; - -// Geom::PathIntersectionGraph pig(path1, path2, Geom::EPSILON); - -// auto intersection = pig.getIntersection(); - auto intersection = sp_pathvector_boolop(path1, path2, bool_op_inters, fill_nonZero, fill_nonZero); - if (intersection.empty()) { return; } -// auto diff1 = pig.getAminusB(); -// auto diff2 = pig.getBminusA(); - auto diff1 = sp_pathvector_boolop(path1, path2, bool_op_diff, fill_nonZero, fill_nonZero); - auto diff2 = sp_pathvector_boolop(path2, path1, bool_op_diff, fill_nonZero, fill_nonZero); - - result_nodes.resize(3); - result_nodes[0] = draw_on_canvas(diff1, original_items_sorted[0]); - result_nodes[1] = draw_on_canvas(intersection, nullptr); // nullptr means no style will be copied. just a black shape. - result_nodes[2] = draw_on_canvas(diff2, original_items_sorted[1]); -} - -//void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() -//{ -// std::vector paths; -// for (int i = 0; i < original_paths_sorted.size(); i++) { -// paths.push_back({original_paths_sorted[i], original_items_sorted[i], {}}); -// } -// -// for (int i = 0; i < paths.size(); i++) { -// for (int j = 0; j < paths.size(); j++) { -// -// if (i == j) { -// continue; -// } -// -// std::cout << "Path 1:\n"; -// print_bounds_and_nodes_count(paths[i].paths); -// -// std::cout << "Path 2:\n"; -// print_bounds_and_nodes_count(paths[j].paths); -// -// if (paths[i].share_an_operation_with(paths[j])) { -// std::cout << "Share operation/s.\n\n"; -// continue; -// } -// -// auto broken = paths[i].break_into_non_overlapping_pieces(paths[j]); -// -// if (broken.empty()) { -// std::cout << "Don't intersect.\n\n"; -// continue; -// } -// -// // j must be deleted before i since i is smaller. -// paths.erase(paths.begin() + j); -// paths.erase(paths.begin() + i); -// j--; // i is not being incremented in the next iteration. shouldn't be decremented. -// -// paths.insert(paths.end(), broken.begin(), broken.end()); -// } -// } -// -// int n = paths.size(); -// result_nodes.clear(); -// result_nodes.resize(n); -// -// for (int i = 0; i < n; i++) { -// result_nodes[i] = draw_on_canvas(paths[i].paths, paths[i].item); -// } -//} - -//void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() -//{ -// std::vector paths; -// for (int i = 0; i < original_paths_sorted.size(); i++) { -// paths.push_back({original_paths_sorted[i], original_items_sorted[i], {}}); -// } -// -// for (int i = 0; i < paths.size(); i++) { -// for (int j = 0; j < paths.size(); j++) { -// -// if (i == j) { -// continue; -// } -// -// std::cout << "Path 1:\n"; -// print_bounds_and_nodes_count(paths[i].paths); -// -// std::cout << "Path 2:\n"; -// print_bounds_and_nodes_count(paths[j].paths); -// -// if (paths[i].share_an_operation_with(paths[j])) { -// std::cout << "Share operation/s.\n\n"; -// continue; -// } -// -// auto broken = paths[i].break_into_non_overlapping_pieces(paths[j]); -// -// if (broken.empty()) { -// std::cout << "Don't intersect.\n\n"; -// continue; -// } -// -// std::cout << "Operations:\n"; -// for (int i = 0; i < broken.size(); i++) { -// std::cout << i << ": "; -// for (int op : broken[i].operations) { -// std::cout << op << ' '; -// } -// std::cout << '\n'; -// } -// std::cout << "Broken.\n\n"; -// -// // j must be deleted before i since i is smaller. -// paths.erase(paths.begin() + j); -// paths.erase(paths.begin() + i); -// j--; // i is not being incremented in the next iteration. shouldn't be decremented. -// -// paths.insert(paths.end(), broken.begin(), broken.end()); -// } -// } -// -// int n = paths.size(); -// result_nodes.clear(); -// result_nodes.resize(n); -// -// for (int i = 0; i < n; i++) { -// result_nodes[i] = draw_on_canvas(paths[i].paths, paths[i].item); -// } -//} - template // Geom::Path or Geom::PathVector -XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, - const SPItem *to_copy_from, XML::Node *parent, XML::Node *after) +XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, const SPItem *to_copy_from) { Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); repr->setAttribute("d", sp_svg_write_path(path)); @@ -631,102 +343,66 @@ XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, return repr; } -template -bool point_in_path(const Path& path, Geom::Point point) +void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() { - return path.winding(point) || point_on_edge(path, point); -} + std::vector result; + { + int n = items.size(); + result.resize(n); + for (int i = 0; i < n; i++) { + result[i] = PathHelper(items[i]->get_pathvector(), items[i], {}); + } + } -template -SPItem *NonOverlappingPathsBuilder::get_top_item_that_intersects(const Path &path) -{ - // print_bounds_and_nodes_count(path); - auto nodes = path.nodes(); - for (int i = 0; i < paths_for_matching.size(); i++) { + for (int i = 0; i < result.size(); i++) { + for (int j = 0; j < result.size(); j++) { + if (i == j) { continue; } - std::cout << "In path #" << i << '\n'; + std::cout << "Path i (" << i << "): "; + print_bounds_and_nodes_count(result[i].paths); - bool found = true; - for (auto &node : nodes) { - if (!point_in_path(path, node)) { - found = false; - break; - } - } + std::cout << "Path j (" << j << "): "; + print_bounds_and_nodes_count(result[j].paths); - if (found) { - return original_items_sorted[i]; - } - } - return nullptr; -} + int common_operation = result[i].get_common_operation(result[j]); + if (common_operation != -1) { + std::cout << "Share the operation #" << common_operation << ".\n\n"; + continue; + } -template // Geom::Path or Geom::PathVector -XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, SPItem *to_copy_from) -{ - XML::Node *item = set->topRepr(); - auto parent = item->parent(); - XML::Node *after; + auto broken = result[i].break_into_non_overlapping_pieces(result[j]); + if (broken.empty()) { + std::cout << "Don't intersect.\n\n"; + continue; + } - if (put_above_originals) { - // front() is the top item. - after = original_items_sorted.front()->getRepr(); - } else { - after = original_items_sorted.back()->getRepr()->prev(); - } + std::cout << "Broken into " << broken.size() << " pieces:\n"; + for (int i = 0; i < broken.size(); i++) { + std::cout << i + 1 << ": "; + print_bounds_and_nodes_count(broken[i].paths); + } + std::cout << "\n\n"; -// SPItem *original_item = get_top_item_that_intersects(path); -// if (!original_item && !fill_holes) { -// return nullptr; -// } + // the bigger index should be erased first. + int bigger_index = (i > j) ? i : j; + int smaller_index = (i > j) ? j : i; + result.erase(result.begin() + bigger_index); + result.erase(result.begin() + smaller_index); -// return draw_on_canvas(path, original_item, parent, after); - return draw_on_canvas(path, to_copy_from, parent, after); -} + j--; // i is not being incremented in the next iteration. shouldn't be decremented. -void NonOverlappingPathsBuilder::set_paths_from_sorted_items() -{ - int n = original_items_sorted.size(); - original_paths_sorted.resize(n); - for (int i = 0; i < n; i++) { - original_paths_sorted[i] = original_items_sorted[i]->get_pathvector(); + result.insert(result.end(), broken.begin(), broken.end()); + } } -} -void NonOverlappingPathsBuilder::set_items_sorted() -{ - // the top item in the document is the first in the list. - auto items = set->items(); - original_items_sorted = std::vector(items.begin(), items.end()); - auto cmp = [](SPItem *a, SPItem *b) { return !sp_item_repr_compare_position_bool(a, b); }; - std::sort(original_items_sorted.begin(), original_items_sorted.end(), cmp); - - set_paths_from_sorted_items(); - construct_paths_for_matching(); -} + std::cout << "Result shapes dimensions:\n"; -void NonOverlappingPathsBuilder::construct_paths_for_matching() -{ - int n = original_items_sorted.size(); - paths_for_matching.resize(n); + int n = result.size(); + result_nodes.resize(n); for (int i = 0; i < n; i++) { - auto path = original_items_sorted[i]->get_pathvector(); - auto bounds = get_bounds_path_vector(path); - - std::vector result = sp_pathvector_cutboolop( - path, - bounds, - fill_nonZero, - fill_nonZero - ); -// -// for (int j = 1; j < result.size(); j++) { -// paths_for_matching[i].( -// paths_for_matching.end(), -// result[j].begin(), -// result[j].end() -// ); -// } + std::cout << i + 1 << ": "; + print_bounds_and_nodes_count(result[i].paths); + result_nodes[i] = draw_on_canvas(result[i].paths, result[i].item); } } diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index b1f5bbcc10..feed6a1243 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -21,60 +21,42 @@ namespace Inkscape { class NonOverlappingPathsBuilder { - // TODO figure out the best values for now. later, get rid of all fixed values. - double outline_width = 0.05; - double outline_miter = 1; // TODO figure out what miter is. - double expansion_offset = 1; // the choice here is arbitrary. bool fill_holes = false; bool is_destructive = true; bool put_above_originals = true; // won't matter if is_destructive is true. bool add_result_to_set = true; + + XML::Node *parent; + XML::Node *after; + ObjectSet *set; - std::vector original_items_sorted; - std::vector original_paths_sorted; + std::vector items; std::vector result_nodes; - std::vector paths_for_matching; public: - NonOverlappingPathsBuilder(ObjectSet *set) - : set(set) {} + + NonOverlappingPathsBuilder(ObjectSet *set) : set(set) {} void build(); - void setOutlineWidth(double outlineWidth) { outline_width = outlineWidth; } - void setOutlineMiter(double outlineMiter) { outline_miter = outlineMiter; } - void setExpansionOffset(double expansionOffset) { expansion_offset = expansionOffset; } void setFillHoles(bool fillHoles) { fill_holes = fillHoles; } void setIsDestructive(bool isDestructive) { is_destructive = isDestructive; } void setPutOriginalsAtBottom(bool putOriginalsAtBottom) { put_above_originals = putOriginalsAtBottom; } void setAddResultToSet(bool addResultToSet) { add_result_to_set = addResultToSet; } private: - - void set_items_sorted(); - void set_paths_from_sorted_items(); - void construct_paths_for_matching(); - void construct_non_overlapping_pieces(); template // Geom::Path or Geom::PathVector - XML::Node *draw_on_canvas(const Path &path, const SPItem *to_copy_from, XML::Node *parent, XML::Node *after); - - template // Geom::Path or Geom::PathVector - XML::Node *draw_on_canvas(const Path &path, SPItem *to_copy_from = nullptr); + XML::Node *draw_on_canvas(const Path &path, const SPItem *to_copy_from = nullptr); SPDesktop *desktop(); void set_desktop_busy(); void unset_desktop_busy(); - SPItem *get_top_item_at_point(const Geom::Point &point); - SPItem *get_top_item_that_intersects_with_points(const std::vector &points); - Geom::PathVector get_bounds_path_vector(const Geom::OptRect &bounds); - Geom::PathVector get_bounds_path_vector(const Geom::PathVector &paths); - Geom::PathVector get_all_paths_combined(); - - template - SPItem *get_top_item_that_intersects(const Path &path); + + void set_parameters(); + SPItem* get_most_item(std::function cmp); }; } \ No newline at end of file -- GitLab From e72056d99b421955e630f9c950e2b41cedf2273b Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Thu, 17 Jun 2021 21:08:09 +0530 Subject: [PATCH 037/235] Toolbar Node : Start (Only Buttons) SpinButton not added to Toolbar and will be done after some time (not continuously) --- share/keys/inkscape.xml | 2 +- share/ui/menu-path.ui | 2 +- share/ui/toolbar-node.ui | 223 +++++++++++++++++++++++ share/ui/toolbar-select.ui | 128 ++----------- src/CMakeLists.txt | 4 +- src/actions/actions-edit.cpp | 12 +- src/actions/actions-node.cpp | 312 ++++++++++++++++++++++++++++++++ src/actions/actions-node.h | 11 ++ src/actions/actions-object.cpp | 31 +++- src/inkscape-window.cpp | 2 + src/ui/toolbar/node-toolbar.cpp | 27 +++ 11 files changed, 633 insertions(+), 121 deletions(-) create mode 100644 share/ui/toolbar-node.ui create mode 100644 src/actions/actions-node.cpp create mode 100644 src/actions/actions-node.h diff --git a/share/keys/inkscape.xml b/share/keys/inkscape.xml index 916322a24a..f436ee5890 100644 --- a/share/keys/inkscape.xml +++ b/share/keys/inkscape.xml @@ -548,7 +548,7 @@ override) the bindings in the main default.xml. - + diff --git a/share/ui/menu-path.ui b/share/ui/menu-path.ui index ced95ed645..ca8c8f3664 100644 --- a/share/ui/menu-path.ui +++ b/share/ui/menu-path.ui @@ -11,7 +11,7 @@
_Stroke to Path - app.object-stroke-to-path + app.stroke-to-path stroke-to-path diff --git a/share/ui/toolbar-node.ui b/share/ui/toolbar-node.ui new file mode 100644 index 0000000000..3add37d744 --- /dev/null +++ b/share/ui/toolbar-node.ui @@ -0,0 +1,223 @@ + + + + + + + True + + + + + + True + win.node-edit-add + node-add + Insert node + + + + + True + win.node-node-delete + node-delete + Delete node + + + + + + True + + + + + + True + win.node-join + node-join + Join nodes + + + + + True + win.node-break + node-break + Break nodes + + + + + + True + + + + + + True + win.node-join-segment + node-join-segment + Join with segment + + + + + True + win.node-delete-segment + node-delete-segment + Delete segment + + + + + + True + + + + + + True + win.node-type-cusp + node-type-cusp + Node Cusp + + + + + True + win.node-type-smooth + node-type-smooth + Node Smooth + + + + + True + win.node-type-symmetric + node-type-symmetric + Node Smooth + + + + + True + win.node-type-auto-smooth + node-type-auto-smooth + Node Auto + + + + + + True + + + + + + True + win.node-segment-line + node-segment-line + Node Line + + + + + True + win.node-segment-curve + node-segment-curve + Node Curve + + + + + + True + + + + + + True + app.object-to-path + object-to-path + Object To Path + + + + + True + app.stroke-to-path + stroke-to-path + Stroke to Path + + + + + + True + + + + + + True + win.node-path-clip-edit + path-clip-edit + Edit clipping paths + + + + + True + win.node-path-mask-edit + path-mask-edit + Edit masks + + + + + True + win.path-effect-parameter-next + path-effect-parameter-next + Stroke to Path + + + + + + True + + + + + + True + win.node-transform + node-transform + Show Transform Handles + + + + + True + win.show-node-handles + show-node-handles + Show Handles + + + + + True + win.show-path-outline + show-path-outline + Show Outline + + + + + + diff --git a/share/ui/toolbar-select.ui b/share/ui/toolbar-select.ui index 58b8375ae8..81630d9ab0 100644 --- a/share/ui/toolbar-select.ui +++ b/share/ui/toolbar-select.ui @@ -139,125 +139,25 @@
- - - - - True - X - - - - - - X - True - True - 3 - True - True - - - False - True - 40 - - - - - - True - Y - - - - - - True - True - 2 - True - True - - - False - True - 50 - - - - - - True - W - - - - - - True - True - 1 - True - True - - - False - True - 60 - - - - - - True - app.selection-top - object-unlocked - Raise to _Top - - - - - - True - H - - - + + - + True - True - 1 - True - True + + + True + + + True + + + + - - False - True - 60 - - - - True - False - 0 - - mm - px - lm - - - - - diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ee693e8204..364abd045a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -221,7 +221,9 @@ set(inkscape_SRC actions/actions-file.h actions/actions-file.cpp actions/actions-helper.h - actions/actions-helper.cpp + actions/actions-helper.cpp + actions/actions-node.h + actions/actions-node.cpp actions/actions-object.h actions/actions-object.cpp actions/actions-object-align.h diff --git a/src/actions/actions-edit.cpp b/src/actions/actions-edit.cpp index 18b8058e94..f8e02f1752 100644 --- a/src/actions/actions-edit.cpp +++ b/src/actions/actions-edit.cpp @@ -300,6 +300,14 @@ remove_path_effect(InkscapeWindow* win) dt->selection->removeLPE(); } +/* Node toolbar : deactivate when no effect is done ( how to deactivate from c++ ) */ +void +path_effect_parameter_next(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + sp_selection_next_patheffect_param(dt); +} + std::vector> raw_data_edit = { // clang-format off @@ -341,7 +349,8 @@ std::vector> raw_data_edit = {"win.lock-all-guides", N_("Lock All Guides"), "Edit", N_("Toggle lock of all guides in the document")}, {"win.delete-all-guides", N_("Delete All Guides"), "Edit", N_("Delete all the guides in the document")}, {"win.paste-path-effect", N_("Paste Path Effect"), "Edit", N_("Apply the path effect of the copied object to selection")}, - {"win.remove-path-effect", N_("Remove Path Effect"), "Edit", N_("Remove any path effects from selected objects")} + {"win.remove-path-effect", N_("Remove Path Effect"), "Edit", N_("Remove any path effects from selected objects")}, + {"win.path-effect-parameter-next", N_("Remove Path Effect"), "Edit", N_("Remove any path effects from selected objects")} // clang-format on }; @@ -390,6 +399,7 @@ add_actions_edit(InkscapeWindow* win) win->add_action( "delete-all-guides", sigc::bind(sigc::ptr_fun(&delete_all_guides), win)); win->add_action( "paste-path-effect", sigc::bind(sigc::ptr_fun(&paste_path_effect), win)); win->add_action( "remove-path-effect", sigc::bind(sigc::ptr_fun(&remove_path_effect), win)); + win->add_action( "path-effect-parameter-next", sigc::bind(sigc::ptr_fun(&path_effect_parameter_next), win)); // clang-format on auto app = InkscapeApplication::instance(); diff --git a/src/actions/actions-node.cpp b/src/actions/actions-node.cpp new file mode 100644 index 0000000000..42f85e6c1e --- /dev/null +++ b/src/actions/actions-node.cpp @@ -0,0 +1,312 @@ +#include // Not ! To eventually allow a headless version! +#include +#include "actions-node.h" +#include "inkscape-application.h" +#include "inkscape-window.h" +#include "desktop.h" +#include "ui/tool/multi-path-manipulator.h" + +using Inkscape::UI::Tools::NodeTool; +using Inkscape::UI::Tools::ToolBase; + +static NodeTool *get_node_tool() +{ + NodeTool *tool = nullptr; + if (SP_ACTIVE_DESKTOP ) { + ToolBase *ec = SP_ACTIVE_DESKTOP->event_context; + if (INK_IS_NODE_TOOL(ec)) { + tool = static_cast(ec); + } + } + return tool; +} + +void +node_edit_add(InkscapeWindow* win) +{ + NodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->insertNodes(); + } +} + + +void +node_node_delete(InkscapeWindow* win) +{ + NodeTool *nt = get_node_tool(); + if (nt) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + nt->_multipath->deleteNodes(prefs->getBool("/tools/nodes/delete_preserves_shape", true)); + } +} + +void +node_join(InkscapeWindow* win) +{ + NodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->joinNodes(); + } +} + +void +node_break(InkscapeWindow* win) +{ + NodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->breakNodes(); + } +} + +void +node_join_segment(InkscapeWindow* win) +{ + NodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->breakNodes(); + } +} + +void +node_delete_segment(InkscapeWindow* win) +{ + NodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->breakNodes(); + } +} + +void +node_type_cusp(InkscapeWindow* win) +{ + NodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->breakNodes(); + } +} + +void +node_type_smooth(InkscapeWindow* win) +{ + NodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->setNodeType(Inkscape::UI::NODE_SMOOTH); + } +} + +void +node_type_symmetric(InkscapeWindow* win) +{ + NodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->setNodeType(Inkscape::UI::NODE_SYMMETRIC); + } +} + +void +node_type_auto_smooth(InkscapeWindow* win) +{ + NodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->setNodeType(Inkscape::UI::NODE_AUTO); + } +} + +void +node_segment_line(InkscapeWindow* win) +{ + NodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_STRAIGHT); + } +} + +void +node_segment_curve(InkscapeWindow* win) +{ + NodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_CUBIC_BEZIER); + } +} + +void +node_path_clip_edit(InkscapeWindow* win){ + + auto action = win->lookup_action("node-path-clip-edit"); + if (!action) { + std::cerr << "node_path_clip_edit: action missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "node_path_clip_edit: action not SimpleAction!" << std::endl; + return; + } + + bool state = true; + saction->get_state(state); + state = !state; + saction->change_state(state); + + auto prefs = Inkscape::Preferences::get(); + prefs->setBool("/tools/nodes/edit_clipping_paths", state); +} + +void +node_path_mask_edit(InkscapeWindow* win){ + + auto action = win->lookup_action("node-path-mask-edit"); + if (!action) { + std::cerr << "node_path_mask_edit: action missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "node_path_mask_edit: action not SimpleAction!" << std::endl; + return; + } + + bool state = true; + saction->get_state(state); + state = !state; + saction->change_state(state); + + auto prefs = Inkscape::Preferences::get(); + prefs->setBool("/tools/nodes/edit_masks", state); +} + + +void +node_transform(InkscapeWindow* win){ + + auto action = win->lookup_action("node-transform"); + if (!action) { + std::cerr << "node_transform: action missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "node_transform: action not SimpleAction!" << std::endl; + return; + } + + bool state = true; + saction->get_state(state); + state = !state; + saction->change_state(state); + + auto prefs = Inkscape::Preferences::get(); + prefs->setBool("/tools/nodes/show_transform_handles", state); +} + +void +show_node_handles(InkscapeWindow* win){ + + auto action = win->lookup_action("show-node-handles"); + if (!action) { + std::cerr << "show_node_handles: action missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "show_node_handles: action not SimpleAction!" << std::endl; + return; + } + + bool state = true; + saction->get_state(state); + state = !state; + saction->change_state(state); + + auto prefs = Inkscape::Preferences::get(); + prefs->setBool("/tools/nodes/show_handles", state); +} + +void +show_path_outline(InkscapeWindow* win){ + + auto action = win->lookup_action("show-path-outline"); + if (!action) { + std::cerr << "show_path_outline: action missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "show_path_outline: action not SimpleAction!" << std::endl; + return; + } + + bool state = true; + saction->get_state(state); + state = !state; + saction->change_state(state); + + auto prefs = Inkscape::Preferences::get(); + prefs->setBool("/tools/nodes/show_outline", state); +} + + + + +std::vector> raw_node_data = +{ + /* Should be done without desktop ? */ + + // clang-format off + {"win.node-edit-add", N_("Insert node"), "Node Toolbar", N_("Insert new nodes into selected segments")}, + {"win.node-node-delete", N_("Delete node"), "Node Toolbar", N_("Delete selected nodes")}, + {"win.node-join", N_("Join nodes"), "Node Toolbar", N_("Join selected nodes")}, + {"win.node-break", N_("Break nodes"), "Node Toolbar", N_("Break path at selected nodes")}, + {"win.node-join-segment", N_("Join with segment"), "Node Toolbar", N_("Join selected endnodes with a new segment")}, + {"win.node-delete-segment", N_("Delete segment"), "Node Toolbar", N_("Delete segment between two non-endpoint nodes")}, + {"win.node-type-cusp", N_("Node Cusp"), "Node Toolbar", N_("Make selected nodes corner")}, + {"win.node-type-smooth", N_("Node Smooth"), "Node Toolbar", N_("Make selected nodes smooth")}, + {"win.node-type-symmetric", N_("Node Symmetric"), "Node Toolbar", N_("Make selected nodes symmetric")}, + {"win.node-type-auto-smooth", N_("Node Auto"), "Node Toolbar", N_("Make selected nodes auto-smooth")}, + {"win.node-segment-line", N_("Node Line"), "Node Toolbar", N_("Make selected segments lines")}, + {"win.node-segment-curve", N_("Node Curve"), "Node Toolbar", N_("Make selected segments curves")}, + {"win.node-path-clip-edit", N_("Edit clipping paths"), "Node Toolbar", N_("Show clipping path(s) of selected object(s)")}, + {"win.node-path-mask-edit", N_("Edit masks"), "Node Toolbar", N_("Show mask(s) of selected object(s)")}, + {"win.node-transform", N_("Show Transform Handles"), "Node Toolbar", N_("Show transformation handles for selected nodes")}, + {"win.show-node-handles", N_("Show Handles"), "Node Toolbar", N_("Show Bezier handles of selected nodes")}, + {"win.show-path-outline", N_("Show Outline"), "Node Toolbar", N_("Show path outline (without path effects)")} + // clang-format on +}; + +void +add_actions_node(InkscapeWindow* win) +{ + // clang-format off + win->add_action( "node-edit-add", sigc::bind(sigc::ptr_fun(&node_edit_add), win)); + win->add_action( "node-node-delete", sigc::bind(sigc::ptr_fun(&node_node_delete), win)); + win->add_action( "node-join", sigc::bind(sigc::ptr_fun(&node_join), win)); + win->add_action( "node-break", sigc::bind(sigc::ptr_fun(&node_break), win)); + win->add_action( "node-join-segment", sigc::bind(sigc::ptr_fun(&node_join_segment), win)); + win->add_action( "node-delete-segment", sigc::bind(sigc::ptr_fun(&node_delete_segment), win)); + win->add_action( "node-type-cusp", sigc::bind(sigc::ptr_fun(&node_type_cusp), win)); + win->add_action( "node-type-smooth", sigc::bind(sigc::ptr_fun(&node_type_smooth), win)); + win->add_action( "node-type-symmetric", sigc::bind(sigc::ptr_fun(&node_type_symmetric), win)); + win->add_action( "node-type-auto-smooth", sigc::bind(sigc::ptr_fun(&node_type_auto_smooth), win)); + win->add_action( "node-segment-line", sigc::bind(sigc::ptr_fun(&node_segment_line), win)); + win->add_action( "node-segment-curve", sigc::bind(sigc::ptr_fun(&node_segment_curve), win)); + win->add_action_bool( "node-path-clip-edit", sigc::bind(sigc::ptr_fun(&node_path_clip_edit), win)); + win->add_action_bool( "node-path-mask-edit", sigc::bind(sigc::ptr_fun(&node_path_mask_edit), win)); + win->add_action_bool( "node-transform", sigc::bind(sigc::ptr_fun(&node_transform), win)); + win->add_action_bool( "show-node-handles", sigc::bind(sigc::ptr_fun(&show_node_handles), win)); + win->add_action_bool( "show-path-outline", sigc::bind(sigc::ptr_fun(&show_path_outline), win)); + // clang-format on + + auto app = InkscapeApplication::instance(); + if (!app) { + std::cerr << "add_actions_node: no app!" << std::endl; + return; + } + app->get_action_extra_data().add_data(raw_node_data); +} \ No newline at end of file diff --git a/src/actions/actions-node.h b/src/actions/actions-node.h new file mode 100644 index 0000000000..17142d894b --- /dev/null +++ b/src/actions/actions-node.h @@ -0,0 +1,11 @@ +#ifndef INK_ACTIONS_NODE_H +#define INK_ACTIONS_NODE_H + +#include "ui/tools/node-tool.h" +#include "inkscape.h" + +class InkscapeWindow; + +void add_actions_node(InkscapeWindow* win); + +#endif // INK_ACTIONS_NODE_H \ No newline at end of file diff --git a/src/actions/actions-object.cpp b/src/actions/actions-object.cpp index 9c3bafe600..e0056fd217 100644 --- a/src/actions/actions-object.cpp +++ b/src/actions/actions-object.cpp @@ -165,8 +165,20 @@ object_rotate_90_ccw(InkscapeApplication *app){ void object_flip_horizontal(InkscapeApplication *app){ Inkscape::Selection *selection = app->get_active_selection(); + + Geom::OptRect bbox = selection->visualBounds(); + if (!bbox) { + return; + } + Geom::Point center; - center = *selection->center(); + + if (selection->center()) { + center = *selection->center(); + } else { + center = bbox->midpoint(); + } + selection->setScaleRelative(center, Geom::Scale(-1.0, 1.0)); Inkscape::DocumentUndo::done(app->get_active_document(), _("Flip horizontally"), INKSCAPE_ICON("object-flip-horizontal")); } @@ -174,7 +186,20 @@ object_flip_horizontal(InkscapeApplication *app){ void object_flip_vertical(InkscapeApplication *app){ Inkscape::Selection *selection = app->get_active_selection(); + + Geom::OptRect bbox = selection->visualBounds(); + if (!bbox) { + return; + } + Geom::Point center; + + if (selection->center()) { + center = *selection->center(); + } else { + center = bbox->midpoint(); + } + center = *selection->center(); selection->setScaleRelative(center, Geom::Scale(1.0, -1.0)); Inkscape::DocumentUndo::done(app->get_active_document(), _("Flip vertically"), INKSCAPE_ICON("object-flip-vertical")); @@ -226,7 +251,7 @@ std::vector> raw_data_object = {"app.object-set-property", N_("Set Property"), "Object", N_("Set or update a property on selected objects; usage: object-set-property:property name, property value;")}, {"app.object-unlink-clones", N_("Unlink Clones"), "Object", N_("Unlink clones and symbols")}, {"app.object-to-path", N_("Object To Path"), "Object", N_("Convert shapes to paths")}, - {"app.object-stroke-to-path", N_("Stroke to Path"), "Object", N_("Convert strokes to paths")}, + {"app.stroke-to-path", N_("Stroke to Path"), "Object", N_("Convert strokes to paths")}, {"app.object-simplify-path", N_("Simplify Path"), "Object", N_("Simplify paths, reducing node counts")}, {"app.object-set", N_("Object Clip Set"), "Object", N_("Apply clipping path to selection (using the topmost object as clipping path)")}, {"app.object-set-inverse", N_("Object Clip Set Inverse"), "Object", N_("Apply inverse clipping path to selection (using the topmost object as clipping path)")}, @@ -267,7 +292,7 @@ add_actions_object(InkscapeApplication* app) gapp->add_action_with_parameter( "object-set-property", String, sigc::bind(sigc::ptr_fun(&object_set_property), app)); gapp->add_action( "object-unlink-clones", sigc::bind(sigc::ptr_fun(&object_unlink_clones), app)); gapp->add_action( "object-to-path", sigc::bind(sigc::ptr_fun(&object_to_path), app)); - gapp->add_action( "object-stroke-to-path", sigc::bind(sigc::ptr_fun(&object_stroke_to_path), app)); + gapp->add_action( "stroke-to-path", sigc::bind(sigc::ptr_fun(&object_stroke_to_path), app)); gapp->add_action( "object-simplify-path", sigc::bind(sigc::ptr_fun(&object_simplify_path), app)); gapp->add_action( "object-set", sigc::bind(sigc::ptr_fun(&set_clip), app)); gapp->add_action( "object-set-inverse", sigc::bind(sigc::ptr_fun(&object_set_inverse), app)); diff --git a/src/inkscape-window.cpp b/src/inkscape-window.cpp index 9712ad9520..3564aaf618 100644 --- a/src/inkscape-window.cpp +++ b/src/inkscape-window.cpp @@ -28,6 +28,7 @@ #include "actions/actions-hide-lock.h" #include "actions/actions-edit.h" #include "actions/actions-selection-desktop.h" +#include "actions/actions-node.h" #include "actions/actions-fit-canvas.h" #include "actions/actions-tools.h" @@ -99,6 +100,7 @@ InkscapeWindow::InkscapeWindow(SPDocument* document) add_actions_hide_lock(this); // Actions to transform dialog. add_actions_edit(this); // Actions to transform dialog. add_actions_select_desktop(this); // Actions with desktop selection + add_actions_node(this); // Actions node add_actions_fit_canvas(this); // Actions to fit canvas add_actions_canvas_mode(this); // Actions to change canvas display mode. add_actions_dialogs(this); // Actions to open dialogs. diff --git a/src/ui/toolbar/node-toolbar.cpp b/src/ui/toolbar/node-toolbar.cpp index ae1ee8232d..342d1c1711 100644 --- a/src/ui/toolbar/node-toolbar.cpp +++ b/src/ui/toolbar/node-toolbar.cpp @@ -57,6 +57,10 @@ #include "widgets/widget-sizes.h" +#include "io/resource.h" + +using Inkscape::IO::Resource::UIS; + using Inkscape::UI::Widget::UnitTracker; using Inkscape::Util::Unit; using Inkscape::Util::Quantity; @@ -362,6 +366,29 @@ NodeToolbar::NodeToolbar(SPDesktop *desktop) GtkWidget * NodeToolbar::create(SPDesktop *desktop) { + // NODE TOOLBAR : SpinButton Addition Left + + /* Glib::ustring node_toolbar_builder_file = get_filename(UIS, "toolbar-node.ui"); + auto builder = Gtk::Builder::create(); + try + { + builder->add_from_file(node_toolbar_builder_file); + } + catch (const Glib::Error& ex) + { + std::cerr << "NodeToolbar: " << node_toolbar_builder_file << " file not read! " << ex.what() << std::endl; + } + + Gtk::Toolbar* toolbar = nullptr; + builder->get_widget("node-toolbar", toolbar); + if (!toolbar) { + std::cerr << "InkscapeWindow: Failed to load node toolbar!" << std::endl; + return nullptr; + } + + toolbar->reference(); + return GTK_WIDGET(toolbar->gobj()); */ + auto holder = new NodeToolbar(desktop); return GTK_WIDGET(holder->gobj()); } // NodeToolbar::prep() -- GitLab From 68c062750ad8b97b843d940b4e7ced640895dcfa Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Thu, 17 Jun 2021 23:35:17 +0530 Subject: [PATCH 038/235] Toolbar Select : Only Buttons SpinButton Left --- src/ui/toolbar/select-toolbar.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/ui/toolbar/select-toolbar.cpp b/src/ui/toolbar/select-toolbar.cpp index d67c3315f8..0f2da43a30 100644 --- a/src/ui/toolbar/select-toolbar.cpp +++ b/src/ui/toolbar/select-toolbar.cpp @@ -42,6 +42,10 @@ #include "ui/widget/unit-tracker.h" #include "widgets/widget-sizes.h" +#include "io/resource.h" + +using Inkscape::IO::Resource::UIS; + using Inkscape::UI::Widget::UnitTracker; using Inkscape::Util::Unit; @@ -251,6 +255,29 @@ void SelectToolbar::on_unrealize() GtkWidget * SelectToolbar::create(SPDesktop *desktop) { + // Select TOOLBAR : SpinButton Addition Left + + /* Glib::ustring select_toolbar_builder_file = get_filename(UIS, "toolbar-select.ui"); + auto builder = Gtk::Builder::create(); + try + { + builder->add_from_file(select_toolbar_builder_file); + } + catch (const Glib::Error& ex) + { + std::cerr << "SelectToolbar: " << select_toolbar_builder_file << " file not read! " << ex.what() << std::endl; + } + + Gtk::Toolbar* toolbar = nullptr; + builder->get_widget("select-toolbar", toolbar); + if (!toolbar) { + std::cerr << "InkscapeWindow: Failed to load select toolbar!" << std::endl; + return nullptr; + } + + toolbar->reference(); + return GTK_WIDGET(toolbar->gobj()); */ + auto toolbar = new SelectToolbar(desktop); return GTK_WIDGET(toolbar->gobj()); } -- GitLab From 0710e61b1c77ec8e9558f30c85c210c762970e0a Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Fri, 18 Jun 2021 01:35:03 +0530 Subject: [PATCH 039/235] Refactoring + License add --- share/ui/toolbar-node.ui | 39 ++--- share/ui/toolbar-select.ui | 1 - src/actions/actions-edit.cpp | 179 ++++++++++++---------- src/actions/actions-edit.h | 11 ++ src/actions/actions-fit-canvas.cpp | 13 +- src/actions/actions-fit-canvas.h | 11 ++ src/actions/actions-hide-lock.cpp | 19 ++- src/actions/actions-hide-lock.h | 12 +- src/actions/actions-hint-data.cpp | 13 ++ src/actions/actions-hint-data.h | 12 +- src/actions/actions-node.cpp | 154 +++++++++++-------- src/actions/actions-node.h | 18 ++- src/actions/actions-object.cpp | 32 ++-- src/actions/actions-selection-desktop.cpp | 16 +- src/actions/actions-selection-desktop.h | 11 ++ src/actions/actions-selection-object.cpp | 23 ++- src/actions/actions-selection-object.h | 11 ++ src/inkscape-application.cpp | 20 +-- src/inkscape-window.cpp | 15 +- 19 files changed, 384 insertions(+), 226 deletions(-) diff --git a/share/ui/toolbar-node.ui b/share/ui/toolbar-node.ui index 3add37d744..8c4e4c515d 100644 --- a/share/ui/toolbar-node.ui +++ b/share/ui/toolbar-node.ui @@ -11,7 +11,7 @@ True - win.node-edit-add + app.node-edit-add node-add Insert node @@ -19,7 +19,7 @@ True - win.node-node-delete + app.node-node-delete node-delete Delete node @@ -34,7 +34,7 @@ True - win.node-join + app.node-join node-join Join nodes @@ -42,7 +42,7 @@ True - win.node-break + app.node-break node-break Break nodes @@ -57,7 +57,7 @@ True - win.node-join-segment + app.node-join-segment node-join-segment Join with segment @@ -65,7 +65,7 @@ True - win.node-delete-segment + app.node-delete-segment node-delete-segment Delete segment @@ -80,7 +80,7 @@ True - win.node-type-cusp + app.node-type-cusp node-type-cusp Node Cusp @@ -88,7 +88,7 @@ True - win.node-type-smooth + app.node-type-smooth node-type-smooth Node Smooth @@ -96,7 +96,7 @@ True - win.node-type-symmetric + app.node-type-symmetric node-type-symmetric Node Smooth @@ -104,7 +104,7 @@ True - win.node-type-auto-smooth + app.node-type-auto-smooth node-type-auto-smooth Node Auto @@ -119,7 +119,7 @@ True - win.node-segment-line + app.node-segment-line node-segment-line Node Line @@ -127,7 +127,7 @@ True - win.node-segment-curve + app.node-segment-curve node-segment-curve Node Curve @@ -165,7 +165,7 @@ True - win.node-path-clip-edit + app.node-path-clip-edit path-clip-edit Edit clipping paths @@ -173,14 +173,15 @@ True - win.node-path-mask-edit + app.node-path-mask-edit path-mask-edit Edit masks - + True + win.path-effect-parameter-next path-effect-parameter-next Stroke to Path @@ -196,7 +197,7 @@ True - win.node-transform + app.node-transform node-transform Show Transform Handles @@ -204,7 +205,7 @@ True - win.show-node-handles + app.show-node-handles show-node-handles Show Handles @@ -212,7 +213,7 @@ True - win.show-path-outline + app.show-path-outline show-path-outline Show Outline @@ -220,4 +221,4 @@ - + \ No newline at end of file diff --git a/share/ui/toolbar-select.ui b/share/ui/toolbar-select.ui index 81630d9ab0..313a76963c 100644 --- a/share/ui/toolbar-select.ui +++ b/share/ui/toolbar-select.ui @@ -5,7 +5,6 @@ - True diff --git a/src/actions/actions-edit.cpp b/src/actions/actions-edit.cpp index f8e02f1752..628b6b6a09 100644 --- a/src/actions/actions-edit.cpp +++ b/src/actions/actions-edit.cpp @@ -1,4 +1,16 @@ -#include +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * + * Actions for Editing an object + * Contains many actions of Edit Verb + * + * Authors: + * Sushant A A + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ #include // Not ! To eventually allow a headless version! #include @@ -300,7 +312,7 @@ remove_path_effect(InkscapeWindow* win) dt->selection->removeLPE(); } -/* Node toolbar : deactivate when no effect is done ( how to deactivate from c++ ) */ +/* Node toolbar : deactivate button when no effect is done ( Currently not added this feature ) */ void path_effect_parameter_next(InkscapeWindow* win) { @@ -311,95 +323,96 @@ path_effect_parameter_next(InkscapeWindow* win) std::vector> raw_data_edit = { // clang-format off - {"win.object-to-pattern", N_("Objects to Pattern"), "Edit", N_("Convert selection to a rectangle with tiled pattern fill")}, - {"win.pattern-to-object", N_("Pattern to Objects"), "Edit", N_("Extract objects from a tiled pattern fill")}, - {"win.object-to-marker", N_("Objects to Marker"), "Edit", N_("Convert selection to a line marker")}, - {"win.object-to-guides", N_("Objects to Guides"), "Edit", N_("Convert selected objects to a collection of guidelines aligned with their edges")}, - {"win.undo", N_("Undo"), "Edit", N_("Undo last action")}, - {"win.redo", N_("Redo"), "Edit", N_("Do again the last undone action")}, - {"win.cut", N_("Cut"), "Edit", N_("Cut selection to clipboard")}, - {"win.copy", N_("Copy"), "Edit", N_("Copy selection to clipboard")}, - {"win.paste", N_("Paste"), "Edit", N_("Paste objects from clipboard to mouse point, or paste text")}, - {"win.paste-in-place", N_("Paste In Place"), "Edit", N_("Paste objects from clipboard to mouse point, or paste text")}, - {"win.paste-style", N_("Paste Style"), "Edit", N_("Apply the style of the copied object to selection")}, - {"win.paste-size", N_("Paste Size"), "Edit", N_("Scale selection to match the size of the copied object")}, - {"win.paste-width", N_("Paste Width"), "Edit", N_("Scale selection horizontally to match the width of the copied object")}, - {"win.paste-height", N_("Paste Height"), "Edit", N_("Scale selection vertically to match the height of the copied object")}, - {"win.paste-size-separately", N_("Paste Size Separately"), "Edit", N_("Scale each selected object to match the size of the copied object")}, - {"win.paste-width-separately", N_("Paste Width Separately"), "Edit", N_("Scale each selected object horizontally to match the width of the copied object")}, - {"win.paste-height-separately", N_("Paste Height Separately"), "Edit", N_("Scale each selected object vertically to match the height of the copied object")}, - {"win.duplicate", N_("Duplicate"), "Edit", N_("Duplicate Selected Objects")}, - {"win.clone", N_("Create Clone"), "Edit", N_("Create a clone (a copy linked to the original) of selected object")}, - {"win.clone-unlink", N_("Unlink Clone"), "Edit", N_("Cut the selected clones' links to the originals, turning them into standalone objects")}, - {"win.clone-unlink-recursively", N_("Unlink Clones recursively"), "Edit", N_("Unlink all clones in the selection, even if they are in groups.")}, - {"win.clone-link", N_("Relink to Copied"), "Edit", N_("Relink the selected clones to the object currently on the clipboard")}, - {"win.select-original", N_("Select Original"), "Edit", N_("Select the object to which the selected clone is linked")}, - {"win.clone-link-lpe", N_("Clone original path (LPE)"), "Edit", N_("Creates a new path, applies the Clone original LPE, and refers it to the selected path")}, - {"win.delete", N_("Delete"), "Edit", N_("Delete selection")}, - {"win.select-all", N_("Select All"), "Edit", N_("Select all objects or all nodes")}, - {"win.select-all-layers", N_("Select All in All Layers"), "Edit", N_("Select all objects in all visible and unlocked layers")}, - {"win.select-same-fill-and-stroke", N_("Fill and Stroke"), "Edit", N_("Select all objects with the same fill and stroke as the selected objects")}, - {"win.select-same-fill", N_("Fill Color"), "Edit", N_("Select all objects with the same fill as the selected objects")}, - {"win.select-same-stroke-color", N_("Stroke Color"), "Edit", N_("Select all objects with the same stroke as the selected objects")}, - {"win.select-same-stroke-style", N_("Stroke Style"), "Edit", N_("Select all objects with the same stroke style (width, dash, markers) as the selected objects")}, - {"win.select-same-object-type", N_("Object Type"), "Edit", N_("Select all objects with the same object type (rect, arc, text, path, bitmap etc) as the selected objects")}, - {"win.select-invert", N_("Invert Selection"), "Edit", N_("Invert selection (unselect what is selected and select everything else)")}, - {"win.select-none", N_("Deselect"), "Edit", N_("Deselect any selected objects or nodes")}, - {"win.create-guides-around-page", N_("Create Guides Around the Page"), "Edit", N_("Create four guides aligned with the page borders")}, - {"win.lock-all-guides", N_("Lock All Guides"), "Edit", N_("Toggle lock of all guides in the document")}, - {"win.delete-all-guides", N_("Delete All Guides"), "Edit", N_("Delete all the guides in the document")}, - {"win.paste-path-effect", N_("Paste Path Effect"), "Edit", N_("Apply the path effect of the copied object to selection")}, - {"win.remove-path-effect", N_("Remove Path Effect"), "Edit", N_("Remove any path effects from selected objects")}, - {"win.path-effect-parameter-next", N_("Remove Path Effect"), "Edit", N_("Remove any path effects from selected objects")} + {"win.object-to-pattern", N_("Objects to Pattern"), "Edit", N_("Convert selection to a rectangle with tiled pattern fill")}, + {"win.pattern-to-object", N_("Pattern to Objects"), "Edit", N_("Extract objects from a tiled pattern fill")}, + {"win.object-to-marker", N_("Objects to Marker"), "Edit", N_("Convert selection to a line marker")}, + {"win.object-to-guides", N_("Objects to Guides"), "Edit", N_("Convert selected objects to a collection of guidelines aligned with their edges")}, + {"win.undo", N_("Undo"), "Edit", N_("Undo last action")}, + {"win.redo", N_("Redo"), "Edit", N_("Do again the last undone action")}, + {"win.cut", N_("Cut"), "Edit", N_("Cut selection to clipboard")}, + {"win.copy", N_("Copy"), "Edit", N_("Copy selection to clipboard")}, + {"win.paste", N_("Paste"), "Edit", N_("Paste objects from clipboard to mouse point, or paste text")}, + {"win.paste-in-place", N_("Paste In Place"), "Edit", N_("Paste objects from clipboard to mouse point, or paste text")}, + {"win.paste-style", N_("Paste Style"), "Edit", N_("Apply the style of the copied object to selection")}, + {"win.paste-size", N_("Paste Size"), "Edit", N_("Scale selection to match the size of the copied object")}, + {"win.paste-width", N_("Paste Width"), "Edit", N_("Scale selection horizontally to match the width of the copied object")}, + {"win.paste-height", N_("Paste Height"), "Edit", N_("Scale selection vertically to match the height of the copied object")}, + {"win.paste-size-separately", N_("Paste Size Separately"), "Edit", N_("Scale each selected object to match the size of the copied object")}, + {"win.paste-width-separately", N_("Paste Width Separately"), "Edit", N_("Scale each selected object horizontally to match the width of the copied object")}, + {"win.paste-height-separately", N_("Paste Height Separately"), "Edit", N_("Scale each selected object vertically to match the height of the copied object")}, + {"win.duplicate", N_("Duplicate"), "Edit", N_("Duplicate Selected Objects")}, + {"win.clone", N_("Create Clone"), "Edit", N_("Create a clone (a copy linked to the original) of selected object")}, + {"win.clone-unlink", N_("Unlink Clone"), "Edit", N_("Cut the selected clones' links to the originals, turning them into standalone objects")}, + {"win.clone-unlink-recursively", N_("Unlink Clones recursively"), "Edit", N_("Unlink all clones in the selection, even if they are in groups.")}, + {"win.clone-link", N_("Relink to Copied"), "Edit", N_("Relink the selected clones to the object currently on the clipboard")}, + {"win.select-original", N_("Select Original"), "Edit", N_("Select the object to which the selected clone is linked")}, + {"win.clone-link-lpe", N_("Clone original path (LPE)"), "Edit", N_("Creates a new path, applies the Clone original LPE, and refers it to the selected path")}, + {"win.delete", N_("Delete"), "Edit", N_("Delete selection")}, + {"win.select-all", N_("Select All"), "Edit", N_("Select all objects or all nodes")}, + {"win.select-all-layers", N_("Select All in All Layers"), "Edit", N_("Select all objects in all visible and unlocked layers")}, + {"win.select-same-fill-and-stroke", N_("Fill and Stroke"), "Edit", N_("Select all objects with the same fill and stroke as the selected objects")}, + {"win.select-same-fill", N_("Fill Color"), "Edit", N_("Select all objects with the same fill as the selected objects")}, + {"win.select-same-stroke-color", N_("Stroke Color"), "Edit", N_("Select all objects with the same stroke as the selected objects")}, + {"win.select-same-stroke-style", N_("Stroke Style"), "Edit", N_("Select all objects with the same stroke style (width, dash, markers) as the selected objects")}, + {"win.select-same-object-type", N_("Object Type"), "Edit", N_("Select all objects with the same object type (rect, arc, text, path, bitmap etc) as the selected objects")}, + {"win.select-invert", N_("Invert Selection"), "Edit", N_("Invert selection (unselect what is selected and select everything else)")}, + {"win.select-none", N_("Deselect"), "Edit", N_("Deselect any selected objects or nodes")}, + {"win.create-guides-around-page", N_("Create Guides Around the Page"), "Edit", N_("Create four guides aligned with the page borders")}, + {"win.lock-all-guides", N_("Lock All Guides"), "Edit", N_("Toggle lock of all guides in the document")}, + {"win.delete-all-guides", N_("Delete All Guides"), "Edit", N_("Delete all the guides in the document")}, + {"win.paste-path-effect", N_("Paste Path Effect"), "Edit", N_("Apply the path effect of the copied object to selection")}, + {"win.remove-path-effect", N_("Remove Path Effect"), "Edit", N_("Remove any path effects from selected objects")}, + {"win.path-effect-parameter-next", N_("Next path effect parameter"), "Edit", N_("Show next editable path effect parameter")} // clang-format on }; void add_actions_edit(InkscapeWindow* win) { - Glib::VariantType String(Glib::VARIANT_TYPE_STRING); + + /* Should be done in separate function or switch cases ? */ // clang-format off - win->add_action( "object-to-pattern", sigc::bind(sigc::ptr_fun(&object_to_pattern), win)); - win->add_action( "pattern-to-object", sigc::bind(sigc::ptr_fun(&pattern_to_object), win)); - win->add_action( "object-to-marker", sigc::bind(sigc::ptr_fun(&object_to_marker), win)); - win->add_action( "object-to-guides", sigc::bind(sigc::ptr_fun(&object_to_guides), win)); - win->add_action( "undo", sigc::bind(sigc::ptr_fun(&undo), win)); - win->add_action( "redo", sigc::bind(sigc::ptr_fun(&redo), win)); - win->add_action( "cut", sigc::bind(sigc::ptr_fun(&cut), win)); - win->add_action( "copy", sigc::bind(sigc::ptr_fun(©), win)); - win->add_action( "paste", sigc::bind(sigc::ptr_fun(&paste), win)); - win->add_action( "paste-in-place", sigc::bind(sigc::ptr_fun(&paste_in_place), win)); - win->add_action( "paste-style", sigc::bind(sigc::ptr_fun(&paste_style), win)); - win->add_action( "paste-size", sigc::bind(sigc::ptr_fun(&paste_size), win)); - win->add_action( "paste-width", sigc::bind(sigc::ptr_fun(&paste_width), win)); - win->add_action( "paste-height", sigc::bind(sigc::ptr_fun(&paste_height), win)); - win->add_action( "paste-size-separately", sigc::bind(sigc::ptr_fun(&paste_size_separately), win)); - win->add_action( "paste-width-separately", sigc::bind(sigc::ptr_fun(&paste_width_separately), win)); - win->add_action( "paste-height-separately", sigc::bind(sigc::ptr_fun(&paste_height_separately), win)); - win->add_action( "duplicate", sigc::bind(sigc::ptr_fun(&duplicate), win)); - win->add_action( "clone", sigc::bind(sigc::ptr_fun(&clone), win)); - win->add_action( "clone-unlink", sigc::bind(sigc::ptr_fun(&clone_unlink), win)); - win->add_action( "clone-unlink-recursively", sigc::bind(sigc::ptr_fun(&clone_unlink_recursively), win)); - win->add_action( "clone-link", sigc::bind(sigc::ptr_fun(&clone_link), win)); - win->add_action( "select-original", sigc::bind(sigc::ptr_fun(&select_original), win)); - win->add_action( "clone-link-lpe", sigc::bind(sigc::ptr_fun(&clone_link_lpe), win)); - win->add_action( "delete", sigc::bind(sigc::ptr_fun(&edit_delete), win)); - win->add_action( "select-all", sigc::bind(sigc::ptr_fun(&select_all), win)); - win->add_action( "select-all-layers", sigc::bind(sigc::ptr_fun(&select_all_layers), win)); - win->add_action( "select-same-fill-and-stroke", sigc::bind(sigc::ptr_fun(&select_same_fill_and_stroke), win)); - win->add_action( "select-same-fill", sigc::bind(sigc::ptr_fun(&select_same_fill), win)); - win->add_action( "select-same-stroke-color", sigc::bind(sigc::ptr_fun(&select_same_stroke_color), win)); - win->add_action( "select-same-stroke-style", sigc::bind(sigc::ptr_fun(&select_same_stroke_style), win)); - win->add_action( "select-same-object-type", sigc::bind(sigc::ptr_fun(&select_same_object_type), win)); - win->add_action( "select-invert", sigc::bind(sigc::ptr_fun(&select_invert), win)); - win->add_action( "select-none", sigc::bind(sigc::ptr_fun(&select_none), win)); - win->add_action( "create-guides-around-page", sigc::bind(sigc::ptr_fun(&create_guides_around_page), win)); - win->add_action_bool( "lock-all-guides", sigc::bind(sigc::ptr_fun(&lock_all_guides), win)); - win->add_action( "delete-all-guides", sigc::bind(sigc::ptr_fun(&delete_all_guides), win)); - win->add_action( "paste-path-effect", sigc::bind(sigc::ptr_fun(&paste_path_effect), win)); - win->add_action( "remove-path-effect", sigc::bind(sigc::ptr_fun(&remove_path_effect), win)); - win->add_action( "path-effect-parameter-next", sigc::bind(sigc::ptr_fun(&path_effect_parameter_next), win)); + win->add_action( "object-to-pattern", sigc::bind(sigc::ptr_fun(&object_to_pattern), win)); + win->add_action( "pattern-to-object", sigc::bind(sigc::ptr_fun(&pattern_to_object), win)); + win->add_action( "object-to-marker", sigc::bind(sigc::ptr_fun(&object_to_marker), win)); + win->add_action( "object-to-guides", sigc::bind(sigc::ptr_fun(&object_to_guides), win)); + win->add_action( "undo", sigc::bind(sigc::ptr_fun(&undo), win)); + win->add_action( "redo", sigc::bind(sigc::ptr_fun(&redo), win)); + win->add_action( "cut", sigc::bind(sigc::ptr_fun(&cut), win)); + win->add_action( "copy", sigc::bind(sigc::ptr_fun(©), win)); + win->add_action( "paste", sigc::bind(sigc::ptr_fun(&paste), win)); + win->add_action( "paste-in-place", sigc::bind(sigc::ptr_fun(&paste_in_place), win)); + win->add_action( "paste-style", sigc::bind(sigc::ptr_fun(&paste_style), win)); + win->add_action( "paste-size", sigc::bind(sigc::ptr_fun(&paste_size), win)); + win->add_action( "paste-width", sigc::bind(sigc::ptr_fun(&paste_width), win)); + win->add_action( "paste-height", sigc::bind(sigc::ptr_fun(&paste_height), win)); + win->add_action( "paste-size-separately", sigc::bind(sigc::ptr_fun(&paste_size_separately), win)); + win->add_action( "paste-width-separately", sigc::bind(sigc::ptr_fun(&paste_width_separately), win)); + win->add_action( "paste-height-separately", sigc::bind(sigc::ptr_fun(&paste_height_separately), win)); + win->add_action( "duplicate", sigc::bind(sigc::ptr_fun(&duplicate), win)); + win->add_action( "clone", sigc::bind(sigc::ptr_fun(&clone), win)); + win->add_action( "clone-unlink", sigc::bind(sigc::ptr_fun(&clone_unlink), win)); + win->add_action( "clone-unlink-recursively", sigc::bind(sigc::ptr_fun(&clone_unlink_recursively), win)); + win->add_action( "clone-link", sigc::bind(sigc::ptr_fun(&clone_link), win)); + win->add_action( "select-original", sigc::bind(sigc::ptr_fun(&select_original), win)); + win->add_action( "clone-link-lpe", sigc::bind(sigc::ptr_fun(&clone_link_lpe), win)); + win->add_action( "delete", sigc::bind(sigc::ptr_fun(&edit_delete), win)); + win->add_action( "select-all", sigc::bind(sigc::ptr_fun(&select_all), win)); + win->add_action( "select-all-layers", sigc::bind(sigc::ptr_fun(&select_all_layers), win)); + win->add_action( "select-same-fill-and-stroke", sigc::bind(sigc::ptr_fun(&select_same_fill_and_stroke), win)); + win->add_action( "select-same-fill", sigc::bind(sigc::ptr_fun(&select_same_fill), win)); + win->add_action( "select-same-stroke-color", sigc::bind(sigc::ptr_fun(&select_same_stroke_color), win)); + win->add_action( "select-same-stroke-style", sigc::bind(sigc::ptr_fun(&select_same_stroke_style), win)); + win->add_action( "select-same-object-type", sigc::bind(sigc::ptr_fun(&select_same_object_type), win)); + win->add_action( "select-invert", sigc::bind(sigc::ptr_fun(&select_invert), win)); + win->add_action( "select-none", sigc::bind(sigc::ptr_fun(&select_none), win)); + win->add_action( "create-guides-around-page", sigc::bind(sigc::ptr_fun(&create_guides_around_page), win)); + win->add_action_bool( "lock-all-guides", sigc::bind(sigc::ptr_fun(&lock_all_guides), win)); + win->add_action( "delete-all-guides", sigc::bind(sigc::ptr_fun(&delete_all_guides), win)); + win->add_action( "paste-path-effect", sigc::bind(sigc::ptr_fun(&paste_path_effect), win)); + win->add_action( "remove-path-effect", sigc::bind(sigc::ptr_fun(&remove_path_effect), win)); + win->add_action( "path-effect-parameter-next", sigc::bind(sigc::ptr_fun(&path_effect_parameter_next), win)); // clang-format on auto app = InkscapeApplication::instance(); diff --git a/src/actions/actions-edit.h b/src/actions/actions-edit.h index 7639385513..1f4a1c7fd3 100644 --- a/src/actions/actions-edit.h +++ b/src/actions/actions-edit.h @@ -1,3 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * + * Authors: + * Sushant A A + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + #ifndef INK_ACTIONS_EDIT_H #define INK_ACTIONS_EDIT_H diff --git a/src/actions/actions-fit-canvas.cpp b/src/actions/actions-fit-canvas.cpp index a426bcffb9..9e9a0dace7 100644 --- a/src/actions/actions-fit-canvas.cpp +++ b/src/actions/actions-fit-canvas.cpp @@ -1,4 +1,15 @@ -#include +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * + * Actions related to fit canvas + * + * Authors: + * Sushant A A + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ #include // Not ! To eventually allow a headless version! #include diff --git a/src/actions/actions-fit-canvas.h b/src/actions/actions-fit-canvas.h index dbf7af29cc..0d47c0af02 100644 --- a/src/actions/actions-fit-canvas.h +++ b/src/actions/actions-fit-canvas.h @@ -1,3 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * + * Authors: + * Sushant A A + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + #ifndef INK_ACTIONS_FIT_CANVAS_H #define INK_ACTIONS_FIT_CANVAS_H diff --git a/src/actions/actions-hide-lock.cpp b/src/actions/actions-hide-lock.cpp index 7ec05488af..6128f7e668 100644 --- a/src/actions/actions-hide-lock.cpp +++ b/src/actions/actions-hide-lock.cpp @@ -1,13 +1,24 @@ -#include +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * + * Actions related to hide and lock + * + * Authors: + * Sushant A A + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ #include // Not ! To eventually allow a headless version! #include #include "actions-hide-lock.h" #include "inkscape-application.h" -#include "document-undo.h" #include "inkscape-window.h" #include "desktop.h" +#include "document-undo.h" #include "selection-chemistry.h" void @@ -31,8 +42,8 @@ hide_lock_unlock_all(InkscapeWindow* win) std::vector> raw_data_hide_lock = { // clang-format off - {"win.unhide-all", N_("Unhide All"), "Hide and Lock", N_("Unhide all objects in the current layer") }, - {"win.unlock-all", N_("Unlock All"), "Hide and Lock", N_("Unlock all objects in the current layer") } + {"win.unhide-all", N_("Unhide All"), "Hide and Lock", N_("Unhide all objects in the current layer") }, + {"win.unlock-all", N_("Unlock All"), "Hide and Lock", N_("Unlock all objects in the current layer") } // clang-format on }; diff --git a/src/actions/actions-hide-lock.h b/src/actions/actions-hide-lock.h index 2ec85c9365..fc7fb3eb4f 100644 --- a/src/actions/actions-hide-lock.h +++ b/src/actions/actions-hide-lock.h @@ -1,8 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * + * Authors: + * Sushant A A + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + #ifndef INK_ACTIONS_HIDE_LOCK_H #define INK_ACTIONS_HIDE_LOCK_H class InkscapeWindow; -class InkscapeApplication; void add_actions_hide_lock(InkscapeWindow* win); diff --git a/src/actions/actions-hint-data.cpp b/src/actions/actions-hint-data.cpp index b206d937fa..5d41f8bf1d 100644 --- a/src/actions/actions-hint-data.cpp +++ b/src/actions/actions-hint-data.cpp @@ -1,3 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * + * Command Palette input placehoder hint data + * + * Authors: + * Sushant A A + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + #include "actions-hint-data.h" #include diff --git a/src/actions/actions-hint-data.h b/src/actions/actions-hint-data.h index 1312879ad7..8961d394ba 100644 --- a/src/actions/actions-hint-data.h +++ b/src/actions/actions-hint-data.h @@ -1,3 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * + * Authors: + * Sushant A A + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + #ifndef INK_ACTIONS_HINT_DATA_H #define INK_ACTIONS_HINT_DATA_H @@ -6,7 +17,6 @@ #include #include #include -#include class InkActionHintData { diff --git a/src/actions/actions-node.cpp b/src/actions/actions-node.cpp index 42f85e6c1e..4536cc3a89 100644 --- a/src/actions/actions-node.cpp +++ b/src/actions/actions-node.cpp @@ -1,9 +1,23 @@ -#include // Not ! To eventually allow a headless version! +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * + * Actions for Node tool present in Node Toolbar + * + * Authors: + * Sushant A A + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include #include + #include "actions-node.h" #include "inkscape-application.h" -#include "inkscape-window.h" -#include "desktop.h" +#include "ui/tools/node-tool.h" +#include "inkscape.h" #include "ui/tool/multi-path-manipulator.h" using Inkscape::UI::Tools::NodeTool; @@ -22,9 +36,10 @@ static NodeTool *get_node_tool() } void -node_edit_add(InkscapeWindow* win) +node_edit_add(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + if (nt) { nt->_multipath->insertNodes(); } @@ -32,7 +47,7 @@ node_edit_add(InkscapeWindow* win) void -node_node_delete(InkscapeWindow* win) +node_node_delete(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); if (nt) { @@ -42,99 +57,109 @@ node_node_delete(InkscapeWindow* win) } void -node_join(InkscapeWindow* win) +node_join(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + if (nt) { nt->_multipath->joinNodes(); } } void -node_break(InkscapeWindow* win) +node_break(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + if (nt) { nt->_multipath->breakNodes(); } } void -node_join_segment(InkscapeWindow* win) +node_join_segment(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + if (nt) { nt->_multipath->breakNodes(); } } void -node_delete_segment(InkscapeWindow* win) +node_delete_segment(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + if (nt) { nt->_multipath->breakNodes(); } } void -node_type_cusp(InkscapeWindow* win) +node_type_cusp(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + if (nt) { nt->_multipath->breakNodes(); } } void -node_type_smooth(InkscapeWindow* win) +node_type_smooth(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + if (nt) { nt->_multipath->setNodeType(Inkscape::UI::NODE_SMOOTH); } } void -node_type_symmetric(InkscapeWindow* win) +node_type_symmetric(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + if (nt) { nt->_multipath->setNodeType(Inkscape::UI::NODE_SYMMETRIC); } } void -node_type_auto_smooth(InkscapeWindow* win) +node_type_auto_smooth(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + if (nt) { nt->_multipath->setNodeType(Inkscape::UI::NODE_AUTO); } } void -node_segment_line(InkscapeWindow* win) +node_segment_line(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + if (nt) { nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_STRAIGHT); } } void -node_segment_curve(InkscapeWindow* win) +node_segment_curve(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + if (nt) { nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_CUBIC_BEZIER); } } void -node_path_clip_edit(InkscapeWindow* win){ +node_path_clip_edit(InkscapeApplication* app){ - auto action = win->lookup_action("node-path-clip-edit"); + auto action = app->gio_app()->lookup_action("node-path-clip-edit"); if (!action) { std::cerr << "node_path_clip_edit: action missing!" << std::endl; return; @@ -156,9 +181,9 @@ node_path_clip_edit(InkscapeWindow* win){ } void -node_path_mask_edit(InkscapeWindow* win){ +node_path_mask_edit(InkscapeApplication* app){ - auto action = win->lookup_action("node-path-mask-edit"); + auto action = app->gio_app()->lookup_action("node-path-mask-edit"); if (!action) { std::cerr << "node_path_mask_edit: action missing!" << std::endl; return; @@ -181,9 +206,9 @@ node_path_mask_edit(InkscapeWindow* win){ void -node_transform(InkscapeWindow* win){ +node_transform(InkscapeApplication* app){ - auto action = win->lookup_action("node-transform"); + auto action = app->gio_app()->lookup_action("node-transform"); if (!action) { std::cerr << "node_transform: action missing!" << std::endl; return; @@ -205,9 +230,9 @@ node_transform(InkscapeWindow* win){ } void -show_node_handles(InkscapeWindow* win){ +show_node_handles(InkscapeApplication* app){ - auto action = win->lookup_action("show-node-handles"); + auto action = app->gio_app()->lookup_action("show-node-handles"); if (!action) { std::cerr << "show_node_handles: action missing!" << std::endl; return; @@ -229,9 +254,9 @@ show_node_handles(InkscapeWindow* win){ } void -show_path_outline(InkscapeWindow* win){ +show_path_outline(InkscapeApplication* app){ - auto action = win->lookup_action("show-path-outline"); + auto action = app->gio_app()->lookup_action("show-path-outline"); if (!action) { std::cerr << "show_path_outline: action missing!" << std::endl; return; @@ -252,58 +277,55 @@ show_path_outline(InkscapeWindow* win){ prefs->setBool("/tools/nodes/show_outline", state); } - - - std::vector> raw_node_data = { - /* Should be done without desktop ? */ - // clang-format off - {"win.node-edit-add", N_("Insert node"), "Node Toolbar", N_("Insert new nodes into selected segments")}, - {"win.node-node-delete", N_("Delete node"), "Node Toolbar", N_("Delete selected nodes")}, - {"win.node-join", N_("Join nodes"), "Node Toolbar", N_("Join selected nodes")}, - {"win.node-break", N_("Break nodes"), "Node Toolbar", N_("Break path at selected nodes")}, - {"win.node-join-segment", N_("Join with segment"), "Node Toolbar", N_("Join selected endnodes with a new segment")}, - {"win.node-delete-segment", N_("Delete segment"), "Node Toolbar", N_("Delete segment between two non-endpoint nodes")}, - {"win.node-type-cusp", N_("Node Cusp"), "Node Toolbar", N_("Make selected nodes corner")}, - {"win.node-type-smooth", N_("Node Smooth"), "Node Toolbar", N_("Make selected nodes smooth")}, - {"win.node-type-symmetric", N_("Node Symmetric"), "Node Toolbar", N_("Make selected nodes symmetric")}, - {"win.node-type-auto-smooth", N_("Node Auto"), "Node Toolbar", N_("Make selected nodes auto-smooth")}, - {"win.node-segment-line", N_("Node Line"), "Node Toolbar", N_("Make selected segments lines")}, - {"win.node-segment-curve", N_("Node Curve"), "Node Toolbar", N_("Make selected segments curves")}, - {"win.node-path-clip-edit", N_("Edit clipping paths"), "Node Toolbar", N_("Show clipping path(s) of selected object(s)")}, - {"win.node-path-mask-edit", N_("Edit masks"), "Node Toolbar", N_("Show mask(s) of selected object(s)")}, - {"win.node-transform", N_("Show Transform Handles"), "Node Toolbar", N_("Show transformation handles for selected nodes")}, - {"win.show-node-handles", N_("Show Handles"), "Node Toolbar", N_("Show Bezier handles of selected nodes")}, - {"win.show-path-outline", N_("Show Outline"), "Node Toolbar", N_("Show path outline (without path effects)")} + {"app.node-edit-add", N_("Insert node"), "Node Toolbar", N_("Insert new nodes into selected segments")}, + {"app.node-node-delete", N_("Delete node"), "Node Toolbar", N_("Delete selected nodes")}, + {"app.node-join", N_("Join nodes"), "Node Toolbar", N_("Join selected nodes")}, + {"app.node-break", N_("Break nodes"), "Node Toolbar", N_("Break path at selected nodes")}, + {"app.node-join-segment", N_("Join with segment"), "Node Toolbar", N_("Join selected endnodes with a new segment")}, + {"app.node-delete-segment", N_("Delete segment"), "Node Toolbar", N_("Delete segment between two non-endpoint nodes")}, + {"app.node-type-cusp", N_("Node Cusp"), "Node Toolbar", N_("Make selected nodes corner")}, + {"app.node-type-smooth", N_("Node Smooth"), "Node Toolbar", N_("Make selected nodes smooth")}, + {"app.node-type-symmetric", N_("Node Symmetric"), "Node Toolbar", N_("Make selected nodes symmetric")}, + {"app.node-type-auto-smooth", N_("Node Auto"), "Node Toolbar", N_("Make selected nodes auto-smooth")}, + {"app.node-segment-line", N_("Node Line"), "Node Toolbar", N_("Make selected segments lines")}, + {"app.node-segment-curve", N_("Node Curve"), "Node Toolbar", N_("Make selected segments curves")}, + {"app.node-path-clip-edit", N_("Edit clipping paths"), "Node Toolbar", N_("Show clipping path(s) of selected object(s)")}, + {"app.node-path-mask-edit", N_("Edit masks"), "Node Toolbar", N_("Show mask(s) of selected object(s)")}, + {"app.node-transform", N_("Show Transform Handles"), "Node Toolbar", N_("Show transformation handles for selected nodes")}, + {"app.show-node-handles", N_("Show Handles"), "Node Toolbar", N_("Show Bezier handles of selected nodes")}, + {"app.show-path-outline", N_("Show Outline"), "Node Toolbar", N_("Show path outline (without path effects)")} // clang-format on }; void -add_actions_node(InkscapeWindow* win) +add_actions_node(InkscapeApplication* app) { + auto *gapp = app->gio_app(); + // clang-format off - win->add_action( "node-edit-add", sigc::bind(sigc::ptr_fun(&node_edit_add), win)); - win->add_action( "node-node-delete", sigc::bind(sigc::ptr_fun(&node_node_delete), win)); - win->add_action( "node-join", sigc::bind(sigc::ptr_fun(&node_join), win)); - win->add_action( "node-break", sigc::bind(sigc::ptr_fun(&node_break), win)); - win->add_action( "node-join-segment", sigc::bind(sigc::ptr_fun(&node_join_segment), win)); - win->add_action( "node-delete-segment", sigc::bind(sigc::ptr_fun(&node_delete_segment), win)); - win->add_action( "node-type-cusp", sigc::bind(sigc::ptr_fun(&node_type_cusp), win)); - win->add_action( "node-type-smooth", sigc::bind(sigc::ptr_fun(&node_type_smooth), win)); - win->add_action( "node-type-symmetric", sigc::bind(sigc::ptr_fun(&node_type_symmetric), win)); - win->add_action( "node-type-auto-smooth", sigc::bind(sigc::ptr_fun(&node_type_auto_smooth), win)); - win->add_action( "node-segment-line", sigc::bind(sigc::ptr_fun(&node_segment_line), win)); - win->add_action( "node-segment-curve", sigc::bind(sigc::ptr_fun(&node_segment_curve), win)); - win->add_action_bool( "node-path-clip-edit", sigc::bind(sigc::ptr_fun(&node_path_clip_edit), win)); - win->add_action_bool( "node-path-mask-edit", sigc::bind(sigc::ptr_fun(&node_path_mask_edit), win)); - win->add_action_bool( "node-transform", sigc::bind(sigc::ptr_fun(&node_transform), win)); - win->add_action_bool( "show-node-handles", sigc::bind(sigc::ptr_fun(&show_node_handles), win)); - win->add_action_bool( "show-path-outline", sigc::bind(sigc::ptr_fun(&show_path_outline), win)); + gapp->add_action( "node-edit-add", sigc::bind(sigc::ptr_fun(&node_edit_add), app)); + gapp->add_action( "node-node-delete", sigc::bind(sigc::ptr_fun(&node_node_delete), app)); + gapp->add_action( "node-join", sigc::bind(sigc::ptr_fun(&node_join), app)); + gapp->add_action( "node-break", sigc::bind(sigc::ptr_fun(&node_break), app)); + gapp->add_action( "node-join-segment", sigc::bind(sigc::ptr_fun(&node_join_segment), app)); + gapp->add_action( "node-delete-segment", sigc::bind(sigc::ptr_fun(&node_delete_segment), app)); + gapp->add_action( "node-type-cusp", sigc::bind(sigc::ptr_fun(&node_type_cusp), app)); + gapp->add_action( "node-type-smooth", sigc::bind(sigc::ptr_fun(&node_type_smooth), app)); + gapp->add_action( "node-type-symmetric", sigc::bind(sigc::ptr_fun(&node_type_symmetric), app)); + gapp->add_action( "node-type-auto-smooth", sigc::bind(sigc::ptr_fun(&node_type_auto_smooth), app)); + gapp->add_action( "node-segment-line", sigc::bind(sigc::ptr_fun(&node_segment_line), app)); + gapp->add_action( "node-segment-curve", sigc::bind(sigc::ptr_fun(&node_segment_curve), app)); + gapp->add_action_bool( "node-path-clip-edit", sigc::bind(sigc::ptr_fun(&node_path_clip_edit), app)); + gapp->add_action_bool( "node-path-mask-edit", sigc::bind(sigc::ptr_fun(node_path_mask_edit), app)); + gapp->add_action_bool( "node-transform", sigc::bind(sigc::ptr_fun(&node_transform), app)); + gapp->add_action_bool( "show-node-handles", sigc::bind(sigc::ptr_fun(&show_node_handles), app)); + gapp->add_action_bool( "show-path-outline", sigc::bind(sigc::ptr_fun(&show_path_outline), app)); // clang-format on - auto app = InkscapeApplication::instance(); + // auto app = InkscapeApplication::instance(); if (!app) { std::cerr << "add_actions_node: no app!" << std::endl; return; diff --git a/src/actions/actions-node.h b/src/actions/actions-node.h index 17142d894b..fce15ee9d8 100644 --- a/src/actions/actions-node.h +++ b/src/actions/actions-node.h @@ -1,11 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * + * Authors: + * Sushant A A + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + #ifndef INK_ACTIONS_NODE_H #define INK_ACTIONS_NODE_H -#include "ui/tools/node-tool.h" -#include "inkscape.h" - -class InkscapeWindow; +class InkscapeApplication; -void add_actions_node(InkscapeWindow* win); +void add_actions_node(InkscapeApplication* app); #endif // INK_ACTIONS_NODE_H \ No newline at end of file diff --git a/src/actions/actions-object.cpp b/src/actions/actions-object.cpp index e0056fd217..a18b79e412 100644 --- a/src/actions/actions-object.cpp +++ b/src/actions/actions-object.cpp @@ -288,22 +288,22 @@ add_actions_object(InkscapeApplication* app) #if GLIB_CHECK_VERSION(2, 52, 0) // clang-format off - gapp->add_action_with_parameter( "object-set-attribute", String, sigc::bind(sigc::ptr_fun(&object_set_attribute), app)); - gapp->add_action_with_parameter( "object-set-property", String, sigc::bind(sigc::ptr_fun(&object_set_property), app)); - gapp->add_action( "object-unlink-clones", sigc::bind(sigc::ptr_fun(&object_unlink_clones), app)); - gapp->add_action( "object-to-path", sigc::bind(sigc::ptr_fun(&object_to_path), app)); - gapp->add_action( "stroke-to-path", sigc::bind(sigc::ptr_fun(&object_stroke_to_path), app)); - gapp->add_action( "object-simplify-path", sigc::bind(sigc::ptr_fun(&object_simplify_path), app)); - gapp->add_action( "object-set", sigc::bind(sigc::ptr_fun(&set_clip), app)); - gapp->add_action( "object-set-inverse", sigc::bind(sigc::ptr_fun(&object_set_inverse), app)); - gapp->add_action( "object-release", sigc::bind(sigc::ptr_fun(&object_release), app)); - gapp->add_action( "object-set-mask", sigc::bind(sigc::ptr_fun(&set_mask), app)); - gapp->add_action( "object-set-inverse-mask", sigc::bind(sigc::ptr_fun(&object_set_inverse_mask), app)); - gapp->add_action( "object-release-mask", sigc::bind(sigc::ptr_fun(&object_release_mask), app)); - gapp->add_action( "object-rotate-90-cw", sigc::bind(sigc::ptr_fun(&object_rotate_90_cw), app)); - gapp->add_action( "object-rotate-90-ccw", sigc::bind(sigc::ptr_fun(&object_rotate_90_cw), app)); - gapp->add_action( "object-flip-horizontal", sigc::bind(sigc::ptr_fun(&object_flip_horizontal), app)); - gapp->add_action( "object-flip-vertical", sigc::bind(sigc::ptr_fun(&object_flip_vertical), app)); + gapp->add_action_with_parameter( "object-set-attribute", String, sigc::bind(sigc::ptr_fun(&object_set_attribute), app)); + gapp->add_action_with_parameter( "object-set-property", String, sigc::bind(sigc::ptr_fun(&object_set_property), app)); + gapp->add_action( "object-unlink-clones", sigc::bind(sigc::ptr_fun(&object_unlink_clones), app)); + gapp->add_action( "object-to-path", sigc::bind(sigc::ptr_fun(&object_to_path), app)); + gapp->add_action( "stroke-to-path", sigc::bind(sigc::ptr_fun(&object_stroke_to_path), app)); + gapp->add_action( "object-simplify-path", sigc::bind(sigc::ptr_fun(&object_simplify_path), app)); + gapp->add_action( "object-set", sigc::bind(sigc::ptr_fun(&set_clip), app)); + gapp->add_action( "object-set-inverse", sigc::bind(sigc::ptr_fun(&object_set_inverse), app)); + gapp->add_action( "object-release", sigc::bind(sigc::ptr_fun(&object_release), app)); + gapp->add_action( "object-set-mask", sigc::bind(sigc::ptr_fun(&set_mask), app)); + gapp->add_action( "object-set-inverse-mask", sigc::bind(sigc::ptr_fun(&object_set_inverse_mask), app)); + gapp->add_action( "object-release-mask", sigc::bind(sigc::ptr_fun(&object_release_mask), app)); + gapp->add_action( "object-rotate-90-cw", sigc::bind(sigc::ptr_fun(&object_rotate_90_cw), app)); + gapp->add_action( "object-rotate-90-ccw", sigc::bind(sigc::ptr_fun(&object_rotate_90_cw), app)); + gapp->add_action( "object-flip-horizontal", sigc::bind(sigc::ptr_fun(&object_flip_horizontal), app)); + gapp->add_action( "object-flip-vertical", sigc::bind(sigc::ptr_fun(&object_flip_vertical), app)); // clang-format on #endif diff --git a/src/actions/actions-selection-desktop.cpp b/src/actions/actions-selection-desktop.cpp index 866cb08e2c..8426911be6 100644 --- a/src/actions/actions-selection-desktop.cpp +++ b/src/actions/actions-selection-desktop.cpp @@ -1,13 +1,23 @@ -#include +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * + * Actions related to selection wich require desktop + * + * Authors: + * Sushant A A + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ -#include // Not ! To eventually allow a headless version! +#include #include #include "actions-selection-desktop.h" #include "inkscape-application.h" #include "inkscape-window.h" #include "desktop.h" -#include "document-undo.h" #include "ui/dialog/dialog-container.h" #include "path/path-offset.h" #include "actions/actions-tools.h" diff --git a/src/actions/actions-selection-desktop.h b/src/actions/actions-selection-desktop.h index c066ddc1ff..1a2beb40e8 100644 --- a/src/actions/actions-selection-desktop.h +++ b/src/actions/actions-selection-desktop.h @@ -1,3 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * + * Authors: + * Sushant A A + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + #ifndef INK_ACTIONS_SELECTION_DESKTOP_H #define INK_ACTIONS_SELECTION_DESKTOP_H diff --git a/src/actions/actions-selection-object.cpp b/src/actions/actions-selection-object.cpp index 3db6dc3598..851733403b 100644 --- a/src/actions/actions-selection-object.cpp +++ b/src/actions/actions-selection-object.cpp @@ -1,17 +1,24 @@ -#include +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * + * Actions related to selection of objects which don't require desktop + * + * Authors: + * Sushant A A + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ -#include // Not ! To eventually allow a headless version! +#include #include #include "actions-selection-object.h" #include "actions-helper.h" #include "inkscape-application.h" - -#include "inkscape.h" // Inkscape::Application -#include "selection.h" // Selection - -#include "object/sp-root.h" // select_all: document->getRoot(); -#include "object/sp-item-group.h" // select_all +#include "inkscape.h" +#include "selection.h" void select_object_group(InkscapeApplication* app) diff --git a/src/actions/actions-selection-object.h b/src/actions/actions-selection-object.h index 2357002d9b..c67b9d26cb 100644 --- a/src/actions/actions-selection-object.h +++ b/src/actions/actions-selection-object.h @@ -1,3 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * + * Authors: + * Sushant A A + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + #ifndef INK_ACTIONS_SELECTION_OBJECT_H #define INK_ACTIONS_SELECTION_OBJECT_H diff --git a/src/inkscape-application.cpp b/src/inkscape-application.cpp index e9539445fc..163a8b0a7f 100644 --- a/src/inkscape-application.cpp +++ b/src/inkscape-application.cpp @@ -48,15 +48,16 @@ #include "util/units.h" // Redimension window -#include "actions/actions-base.h" // Actions -#include "actions/actions-file.h" // Actions -#include "actions/actions-object.h" // Actions -#include "actions/actions-object-align.h" // Actions -#include "actions/actions-output.h" // Actions -#include "actions/actions-selection-object.h" // Actions -#include "actions/actions-selection.h" // Actions -#include "actions/actions-transform.h" // Actions -#include "actions/actions-window.h" // Actions +#include "actions/actions-base.h" // Actions +#include "actions/actions-file.h" // Actions +#include "actions/actions-node.h" // Actions +#include "actions/actions-object.h" // Actions +#include "actions/actions-object-align.h" // Actions +#include "actions/actions-output.h" // Actions +#include "actions/actions-selection-object.h" // Actions +#include "actions/actions-selection.h" // Actions +#include "actions/actions-transform.h" // Actions +#include "actions/actions-window.h" // Actions #ifdef GDK_WINDOWING_QUARTZ #include @@ -587,6 +588,7 @@ InkscapeApplication::InkscapeApplication() // ======================== Actions ========================= add_actions_base(this); // actions that are GUI independent add_actions_file(this); // actions for file handling + add_actions_node(this); // actions for node add_actions_object(this); // actions for object manipulation add_actions_object_align(this); // actions for object alignment add_actions_output(this); // actions for file export diff --git a/src/inkscape-window.cpp b/src/inkscape-window.cpp index 3564aaf618..0d87300c01 100644 --- a/src/inkscape-window.cpp +++ b/src/inkscape-window.cpp @@ -25,11 +25,10 @@ #include "actions/actions-canvas-mode.h" #include "actions/actions-canvas-transform.h" #include "actions/actions-dialogs.h" -#include "actions/actions-hide-lock.h" #include "actions/actions-edit.h" -#include "actions/actions-selection-desktop.h" -#include "actions/actions-node.h" #include "actions/actions-fit-canvas.h" +#include "actions/actions-hide-lock.h" +#include "actions/actions-selection-desktop.h" #include "actions/actions-tools.h" #include "object/sp-namedview.h" // TODO Remove need for this! @@ -96,14 +95,12 @@ InkscapeWindow::InkscapeWindow(SPDocument* document) // =================== Actions =================== // After canvas has been constructed.. move to canvas proper. add_actions_canvas_transform(this); // Actions to transform canvas view. - add_actions_dialogs(this); // Actions to transform dialog. - add_actions_hide_lock(this); // Actions to transform dialog. - add_actions_edit(this); // Actions to transform dialog. - add_actions_select_desktop(this); // Actions with desktop selection - add_actions_node(this); // Actions node - add_actions_fit_canvas(this); // Actions to fit canvas add_actions_canvas_mode(this); // Actions to change canvas display mode. add_actions_dialogs(this); // Actions to open dialogs. + add_actions_edit(this); // Actions to transform dialog. + add_actions_fit_canvas(this); // Actions to fit canvas + add_actions_hide_lock(this); // Actions to transform dialog. + add_actions_select_desktop(this); // Actions with desktop selection add_actions_tools(this); // Actions to switch between tools. // ========== Drag and Drop of Documents ========= -- GitLab From 25e4917f457eb2fff92d085899371bcfef73dbc1 Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Fri, 18 Jun 2021 14:59:32 +0530 Subject: [PATCH 040/235] Translation fix --- po/POTFILES.src.in | 7 +++++++ po/POTFILES.ui.in | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/po/POTFILES.src.in b/po/POTFILES.src.in index 66adb60f12..0454e51314 100644 --- a/po/POTFILES.src.in +++ b/po/POTFILES.src.in @@ -8,11 +8,18 @@ ../src/actions/actions-canvas-snapping.cpp ../src/actions/actions-canvas-transform.cpp ../src/actions/actions-dialogs.cpp +../src/actions/actions-edit.cpp ../src/actions/actions-extra-data.cpp ../src/actions/actions-file.cpp +../src/actions/actions-fit-canvas.cpp +../src/actions/actions-hide-lock.cpp +../src/actions/actions-hint-data.cpp +../src/actions/actions-node.cpp ../src/actions/actions-object-align.cpp ../src/actions/actions-object.cpp ../src/actions/actions-output.cpp +../src/actions/actions-selection-desktop.cpp +../src/actions/actions-selection-object.cpp ../src/actions/actions-selection.cpp ../src/actions/actions-tools.cpp ../src/actions/actions-transform.cpp diff --git a/po/POTFILES.ui.in b/po/POTFILES.ui.in index 72a7fdd8b1..2e7474e172 100644 --- a/po/POTFILES.ui.in +++ b/po/POTFILES.ui.in @@ -7,7 +7,12 @@ ../share/ui/dialog-trace.glade ../share/ui/inkscape-about.glade ../share/ui/inkscape-start.glade +../share/ui/menu-edit.ui +../share/ui/menu-object.ui +../share/ui/menu-path.ui ../share/ui/menu-view.ui +../share/ui/toolbar-node.ui +../share/ui/toolbar-select.ui ../share/ui/toolbar-snap.ui ../share/ui/toolbar-tool.ui ../share/ui/toolbar-zoom.ui -- GitLab From 5b1b9ecb5fbd00574cdbd3873156487f0bbf6cc3 Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Fri, 18 Jun 2021 17:13:51 +0530 Subject: [PATCH 041/235] Reference Comment Add --- src/actions/actions-edit.cpp | 84 ++++++++++++++++++++++- src/actions/actions-fit-canvas.cpp | 8 ++- src/actions/actions-hide-lock.cpp | 1 - src/actions/actions-hint-data.cpp | 12 +++- src/actions/actions-node.cpp | 34 ++++++++- src/actions/actions-object.cpp | 39 ++++++++--- src/actions/actions-selection-desktop.cpp | 14 ++++ src/actions/actions-selection-object.cpp | 14 ++++ src/ui/dialog/command-palette.cpp | 1 - 9 files changed, 188 insertions(+), 19 deletions(-) diff --git a/src/actions/actions-edit.cpp b/src/actions/actions-edit.cpp index 628b6b6a09..a0250ccbb0 100644 --- a/src/actions/actions-edit.cpp +++ b/src/actions/actions-edit.cpp @@ -26,6 +26,8 @@ void object_to_pattern(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Objects to Pattern dt->selection->tile(); } @@ -33,6 +35,8 @@ void pattern_to_object(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Pattern to Objects dt->selection->untile(); } @@ -40,6 +44,8 @@ void object_to_marker(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Objects to Marker dt->selection->toMarker(); } @@ -47,6 +53,8 @@ void object_to_guides(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Objects to Guides dt->selection->toGuides(); } @@ -54,6 +62,8 @@ void undo(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Undo sp_undo(dt, dt->getDocument()); } @@ -61,6 +71,8 @@ void redo(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Redo sp_redo(dt, dt->getDocument()); } @@ -68,6 +80,8 @@ void cut(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Cut dt->selection->cut(); } @@ -75,6 +89,8 @@ void copy(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Copy dt->selection->copy(); } @@ -82,6 +98,8 @@ void paste(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Paste sp_selection_paste(dt, false); } @@ -89,6 +107,8 @@ void paste_in_place(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Paste In Place sp_selection_paste(dt, true); } @@ -96,6 +116,8 @@ void paste_style(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Paste Style dt->selection->pasteStyle(); } @@ -103,6 +125,8 @@ void paste_size(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Paste Size dt->selection->pasteSize(true,true); } @@ -110,6 +134,8 @@ void paste_width(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Paste Width dt->selection->pasteSize(true, false); } @@ -117,6 +143,8 @@ void paste_height(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Paste Height dt->selection->pasteSize(false, true); } @@ -124,6 +152,8 @@ void paste_size_separately(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Paste Size Separately dt->selection->pasteSizeSeparately(true, true); } @@ -131,6 +161,8 @@ void paste_width_separately(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Paste Width Separately dt->selection->pasteSizeSeparately(true, false); } @@ -138,6 +170,8 @@ void paste_height_separately(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Paste Height Separately dt->selection->pasteSizeSeparately(false, true); } @@ -145,6 +179,8 @@ void duplicate(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Duplicate dt->selection->duplicate(); } @@ -152,6 +188,8 @@ void clone(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Create Clone dt->selection->clone(); } @@ -159,6 +197,8 @@ void clone_unlink(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Unlink Clone dt->selection->unlink(); } @@ -166,6 +206,8 @@ void clone_unlink_recursively(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Unlink Clones recursively dt->selection->unlinkRecursive(false, true); } @@ -173,6 +215,8 @@ void clone_link(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Relink to Copied dt->selection->relink(); } @@ -180,6 +224,8 @@ void select_original(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Select Original dt->selection->cloneOriginal(); } @@ -187,6 +233,8 @@ void clone_link_lpe(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Clone original path (LPE) dt->selection->cloneOriginalPathLPE(); } @@ -194,6 +242,8 @@ void edit_delete(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Delete dt->selection->deleteItems(); } @@ -201,6 +251,8 @@ void select_all(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Select All Inkscape::SelectionHelper::selectAll(dt); } @@ -208,6 +260,8 @@ void select_all_layers(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Select All in All Layers Inkscape::SelectionHelper::selectAllInAll(dt); } @@ -215,6 +269,8 @@ void select_same_fill_and_stroke(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Fill and Stroke Inkscape::SelectionHelper::selectSameFillStroke(dt); } @@ -222,6 +278,8 @@ void select_same_fill(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Fill Color Inkscape::SelectionHelper::selectSameFillColor(dt); } @@ -229,6 +287,8 @@ void select_same_stroke_color(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Stroke Color Inkscape::SelectionHelper::selectSameStrokeColor(dt); } @@ -236,6 +296,8 @@ void select_same_stroke_style(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Stroke Style Inkscape::SelectionHelper::selectSameStrokeStyle(dt); } @@ -243,6 +305,8 @@ void select_same_object_type(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Object Type Inkscape::SelectionHelper::selectSameObjectType(dt); } @@ -250,6 +314,8 @@ void select_invert(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Invert Selection Inkscape::SelectionHelper::invert(dt); } @@ -257,6 +323,8 @@ void select_none(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Deselect Inkscape::SelectionHelper::selectNone(dt); } @@ -264,12 +332,15 @@ void create_guides_around_page(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Create Guides Around the Page sp_guide_create_guides_around_page(dt); } void lock_all_guides(InkscapeWindow *win) { + // Get Action auto action = win->lookup_action("lock-all-guides"); if (!action) { std::cerr << "lock_all_guides: action missing!" << std::endl; @@ -282,12 +353,15 @@ lock_all_guides(InkscapeWindow *win) return; } + // Toggle State bool state = false; saction->get_state(state); state = !state; saction->change_state(state); SPDesktop* dt = win->get_desktop(); + + // Lock All Guides dt->toggleGuidesLock(); } @@ -295,6 +369,8 @@ void delete_all_guides(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Delete All Guides sp_guide_delete_all_guides(dt); } @@ -302,6 +378,8 @@ void paste_path_effect(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Paste Path Effect dt->selection->pastePathEffect(); } @@ -309,6 +387,8 @@ void remove_path_effect(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Remove Path Effect dt->selection->removeLPE(); } @@ -316,7 +396,9 @@ remove_path_effect(InkscapeWindow* win) void path_effect_parameter_next(InkscapeWindow* win) { - SPDesktop* dt = win->get_desktop(); + SPDesktop* dt = win->get_desktop(); + + // Next path effect parameter sp_selection_next_patheffect_param(dt); } diff --git a/src/actions/actions-fit-canvas.cpp b/src/actions/actions-fit-canvas.cpp index 9e9a0dace7..3a9dd6d2e3 100644 --- a/src/actions/actions-fit-canvas.cpp +++ b/src/actions/actions-fit-canvas.cpp @@ -24,6 +24,8 @@ void fit_canvas_to_selection(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Fit Page to Selection dt->selection->fitCanvas(true); } @@ -31,6 +33,8 @@ void fit_canvas_drawing(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Fit Page to Drawing if (fit_canvas_to_drawing(dt->getDocument())) { Inkscape::DocumentUndo::done(dt->getDocument(), _("Fit Page to Drawing"), nullptr); } @@ -40,6 +44,8 @@ void canvas_to_selection_or_drawing(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Resize Page to Selection fit_canvas_to_selection_or_drawing(dt); } @@ -48,7 +54,7 @@ std::vector> raw_fit_canvas_data = // clang-format off {"win.fit-canvas-to-selection", N_("Fit Page to Selection"), "Selection Desktop", N_("Fit the page to the current selection")}, {"win.fit-canvas-to-drawing", N_("Fit Page to Drawing"), "Selection Desktop", N_("Fit the page to the drawing")}, - {"win.fit-canvas-to-selection-or-drawing", N_("_Resize Page to Selection"), "Selection Desktop", N_("Fit the page to the current selection or the drawing if there is no selection")} + {"win.fit-canvas-to-selection-or-drawing", N_("Resize Page to Selection"), "Selection Desktop", N_("Fit the page to the current selection or the drawing if there is no selection")} // clang-format on }; diff --git a/src/actions/actions-hide-lock.cpp b/src/actions/actions-hide-lock.cpp index 6128f7e668..3f4151acb7 100644 --- a/src/actions/actions-hide-lock.cpp +++ b/src/actions/actions-hide-lock.cpp @@ -50,7 +50,6 @@ std::vector> raw_data_hide_lock = void add_actions_hide_lock(InkscapeWindow* win) { - // clang-format off win->add_action( "unhide-all", sigc::bind(sigc::ptr_fun(&hide_lock_unhide_all), win)); win->add_action( "unlock-all", sigc::bind(sigc::ptr_fun(&hide_lock_unlock_all), win)); diff --git a/src/actions/actions-hint-data.cpp b/src/actions/actions-hint-data.cpp index 5d41f8bf1d..f4b242249a 100644 --- a/src/actions/actions-hint-data.cpp +++ b/src/actions/actions-hint-data.cpp @@ -17,6 +17,8 @@ std::vector InkActionHintData::get_actions() { + // To get all the Placeholder hints + std::vector action_names; for (auto hint : data) { action_names.emplace_back(hint.first); @@ -25,7 +27,9 @@ InkActionHintData::get_actions() } Glib::ustring -InkActionHintData::get_tooltip_hint_for_action(Glib::ustring const &action_name, bool translated) { +InkActionHintData::get_tooltip_hint_for_action(Glib::ustring const &action_name, bool translated) +{ + // Hint for a perticular Action Glib::ustring value; auto search = data.find(action_name); @@ -36,8 +40,10 @@ InkActionHintData::get_tooltip_hint_for_action(Glib::ustring const &action_name, } void -InkActionHintData::add_data(std::vector> &hint_data) { +InkActionHintData::add_data(std::vector> &hint_data) +{ for (auto hint : hint_data) { - data.emplace(hint[0], hint[1]); // action name , hint + // Action Name , Hint + data.emplace(hint[0], hint[1]); } } \ No newline at end of file diff --git a/src/actions/actions-node.cpp b/src/actions/actions-node.cpp index 4536cc3a89..44b99b409f 100644 --- a/src/actions/actions-node.cpp +++ b/src/actions/actions-node.cpp @@ -39,7 +39,8 @@ void node_edit_add(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); - + + // Adding a Node if (nt) { nt->_multipath->insertNodes(); } @@ -50,6 +51,8 @@ void node_node_delete(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + + // Deleting a Node if (nt) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); nt->_multipath->deleteNodes(prefs->getBool("/tools/nodes/delete_preserves_shape", true)); @@ -60,7 +63,8 @@ void node_join(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); - + + // Join Nodes if (nt) { nt->_multipath->joinNodes(); } @@ -71,6 +75,7 @@ node_break(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + // Break nodes if (nt) { nt->_multipath->breakNodes(); } @@ -81,6 +86,7 @@ node_join_segment(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + // Join with Line (segment) if (nt) { nt->_multipath->breakNodes(); } @@ -91,6 +97,7 @@ node_delete_segment(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + // Delete segment if (nt) { nt->_multipath->breakNodes(); } @@ -101,6 +108,7 @@ node_type_cusp(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + // Selected Nodes to Corner if (nt) { nt->_multipath->breakNodes(); } @@ -111,6 +119,7 @@ node_type_smooth(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + // Node Smooth if (nt) { nt->_multipath->setNodeType(Inkscape::UI::NODE_SMOOTH); } @@ -121,6 +130,7 @@ node_type_symmetric(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + // Makes selected nodes symmetric if (nt) { nt->_multipath->setNodeType(Inkscape::UI::NODE_SYMMETRIC); } @@ -130,7 +140,8 @@ void node_type_auto_smooth(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); - + + // Node Smooth Auto if (nt) { nt->_multipath->setNodeType(Inkscape::UI::NODE_AUTO); } @@ -141,6 +152,7 @@ node_segment_line(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + // Node selected segments line if (nt) { nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_STRAIGHT); } @@ -151,6 +163,7 @@ node_segment_curve(InkscapeApplication* app) { NodeTool *nt = get_node_tool(); + // Node selected segments curves if (nt) { nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_CUBIC_BEZIER); } @@ -159,6 +172,7 @@ node_segment_curve(InkscapeApplication* app) void node_path_clip_edit(InkscapeApplication* app){ + // Retrieve Action auto action = app->gio_app()->lookup_action("node-path-clip-edit"); if (!action) { std::cerr << "node_path_clip_edit: action missing!" << std::endl; @@ -171,11 +185,13 @@ node_path_clip_edit(InkscapeApplication* app){ return; } + // Toggle State bool state = true; saction->get_state(state); state = !state; saction->change_state(state); + // Set State auto prefs = Inkscape::Preferences::get(); prefs->setBool("/tools/nodes/edit_clipping_paths", state); } @@ -183,6 +199,7 @@ node_path_clip_edit(InkscapeApplication* app){ void node_path_mask_edit(InkscapeApplication* app){ + // Retrieve Action auto action = app->gio_app()->lookup_action("node-path-mask-edit"); if (!action) { std::cerr << "node_path_mask_edit: action missing!" << std::endl; @@ -195,11 +212,13 @@ node_path_mask_edit(InkscapeApplication* app){ return; } + // Toggle State bool state = true; saction->get_state(state); state = !state; saction->change_state(state); + // Set State auto prefs = Inkscape::Preferences::get(); prefs->setBool("/tools/nodes/edit_masks", state); } @@ -208,6 +227,7 @@ node_path_mask_edit(InkscapeApplication* app){ void node_transform(InkscapeApplication* app){ + // Retrieve Action auto action = app->gio_app()->lookup_action("node-transform"); if (!action) { std::cerr << "node_transform: action missing!" << std::endl; @@ -220,11 +240,13 @@ node_transform(InkscapeApplication* app){ return; } + // Toggle State bool state = true; saction->get_state(state); state = !state; saction->change_state(state); + // Set State auto prefs = Inkscape::Preferences::get(); prefs->setBool("/tools/nodes/show_transform_handles", state); } @@ -232,6 +254,7 @@ node_transform(InkscapeApplication* app){ void show_node_handles(InkscapeApplication* app){ + // Retrieve Action auto action = app->gio_app()->lookup_action("show-node-handles"); if (!action) { std::cerr << "show_node_handles: action missing!" << std::endl; @@ -244,11 +267,13 @@ show_node_handles(InkscapeApplication* app){ return; } + // Toggle State bool state = true; saction->get_state(state); state = !state; saction->change_state(state); + // Set State auto prefs = Inkscape::Preferences::get(); prefs->setBool("/tools/nodes/show_handles", state); } @@ -256,6 +281,7 @@ show_node_handles(InkscapeApplication* app){ void show_path_outline(InkscapeApplication* app){ + // Retrieve Action auto action = app->gio_app()->lookup_action("show-path-outline"); if (!action) { std::cerr << "show_path_outline: action missing!" << std::endl; @@ -268,11 +294,13 @@ show_path_outline(InkscapeApplication* app){ return; } + // Toggle State bool state = true; saction->get_state(state); state = !state; saction->change_state(state); + // Set State auto prefs = Inkscape::Preferences::get(); prefs->setBool("/tools/nodes/show_outline", state); } diff --git a/src/actions/actions-object.cpp b/src/actions/actions-object.cpp index a18b79e412..97a3515bf0 100644 --- a/src/actions/actions-object.cpp +++ b/src/actions/actions-object.cpp @@ -104,6 +104,8 @@ void set_clip(InkscapeApplication *app) { Inkscape::Selection *selection = app->get_active_selection(); + + // Object Clip Set selection->setMask(true, false); } @@ -111,6 +113,8 @@ void object_set_inverse(InkscapeApplication *app) { Inkscape::Selection *selection = app->get_active_selection(); + + // Object Clip Set Inverse selection->setMask(true, false); Inkscape::LivePathEffect::sp_inverse_powerclip(app->get_active_selection()); Inkscape::DocumentUndo::done(app->get_active_document(), _("_Set Inverse (LPE)"), nullptr); @@ -120,6 +124,8 @@ void object_release(InkscapeApplication *app) { Inkscape::Selection *selection = app->get_active_selection(); + + // Object Clip Release Inkscape::LivePathEffect::sp_remove_powerclip(app->get_active_selection()); selection->unsetMask(true); Inkscape::DocumentUndo::done(app->get_active_document(), _("Release clipping path"), nullptr); @@ -129,6 +135,8 @@ void set_mask(InkscapeApplication *app) { Inkscape::Selection *selection = app->get_active_selection(); + + // Object Mask Set selection->setMask(false, false); } @@ -136,6 +144,8 @@ void object_set_inverse_mask(InkscapeApplication *app) { Inkscape::Selection *selection = app->get_active_selection(); + + // Object Mask Set Inverse selection->setMask(false, false); Inkscape::LivePathEffect::sp_inverse_powermask(app->get_active_selection()); Inkscape::DocumentUndo::done(app->get_active_document(), _("_Set Inverse (LPE)"), nullptr); @@ -145,25 +155,34 @@ void object_release_mask(InkscapeApplication *app) { Inkscape::Selection *selection = app->get_active_selection(); + + // Object Mask Release Inkscape::LivePathEffect::sp_remove_powermask(app->get_active_selection()); selection->unsetMask(false); Inkscape::DocumentUndo::done(app->get_active_document(), _("Release mask"), nullptr); } void -object_rotate_90_cw(InkscapeApplication *app){ +object_rotate_90_cw(InkscapeApplication *app) +{ Inkscape::Selection *selection = app->get_active_selection(); + + // Object Rotate 90 selection->rotate90(false); } void -object_rotate_90_ccw(InkscapeApplication *app){ +object_rotate_90_ccw(InkscapeApplication *app) +{ Inkscape::Selection *selection = app->get_active_selection(); + + // Object Rotate 90 CCW selection->rotate90(true); } void -object_flip_horizontal(InkscapeApplication *app){ +object_flip_horizontal(InkscapeApplication *app) +{ Inkscape::Selection *selection = app->get_active_selection(); Geom::OptRect bbox = selection->visualBounds(); @@ -171,20 +190,22 @@ object_flip_horizontal(InkscapeApplication *app){ return; } - Geom::Point center; - + // Get center + Geom::Point center; if (selection->center()) { center = *selection->center(); } else { center = bbox->midpoint(); } + // Object Flip Horizontal selection->setScaleRelative(center, Geom::Scale(-1.0, 1.0)); Inkscape::DocumentUndo::done(app->get_active_document(), _("Flip horizontally"), INKSCAPE_ICON("object-flip-horizontal")); } void -object_flip_vertical(InkscapeApplication *app){ +object_flip_vertical(InkscapeApplication *app) +{ Inkscape::Selection *selection = app->get_active_selection(); Geom::OptRect bbox = selection->visualBounds(); @@ -192,15 +213,15 @@ object_flip_vertical(InkscapeApplication *app){ return; } + // Get center Geom::Point center; - if (selection->center()) { center = *selection->center(); } else { center = bbox->midpoint(); } - - center = *selection->center(); + + // Object Flip Vertical selection->setScaleRelative(center, Geom::Scale(1.0, -1.0)); Inkscape::DocumentUndo::done(app->get_active_document(), _("Flip vertically"), INKSCAPE_ICON("object-flip-vertical")); } diff --git a/src/actions/actions-selection-desktop.cpp b/src/actions/actions-selection-desktop.cpp index 8426911be6..ed72f786b2 100644 --- a/src/actions/actions-selection-desktop.cpp +++ b/src/actions/actions-selection-desktop.cpp @@ -27,6 +27,8 @@ void selection_make_bitmap_copy(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Make a Bitmap Copy dt->selection->createBitmapCopy(); } @@ -34,6 +36,8 @@ void object_bitmap_trace(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Trace Bitmap Inkscape::UI::Dialog::DialogContainer *container = dt->getContainer(); container->new_dialog("Trace"); } @@ -42,6 +46,8 @@ void select_path_inset(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Inset selected paths dt->selection->removeLPESRecursive(true); dt->selection->unlinkRecursive(true); sp_selected_path_inset(dt); @@ -51,6 +57,8 @@ void select_path_outset(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Outset selected paths dt->selection->removeLPESRecursive(true); dt->selection->unlinkRecursive(true); sp_selected_path_offset(dt); @@ -60,6 +68,8 @@ void select_path_offset_dynamic(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Dynamic Offset dt->selection->removeLPESRecursive(true); dt->selection->unlinkRecursive(true); sp_selected_path_create_offset_object_zero(dt); @@ -70,6 +80,8 @@ void select_path_offset_linked(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Linked Offset dt->selection->removeLPESRecursive(true); dt->selection->unlinkRecursive(true); sp_selected_path_create_updating_offset_object_zero(dt); @@ -80,6 +92,8 @@ void select_path_reverse(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); + + // Reverse Inkscape::SelectionHelper::reverse(dt); } diff --git a/src/actions/actions-selection-object.cpp b/src/actions/actions-selection-object.cpp index 851733403b..53873c9d61 100644 --- a/src/actions/actions-selection-object.cpp +++ b/src/actions/actions-selection-object.cpp @@ -24,6 +24,8 @@ void select_object_group(InkscapeApplication* app) { Inkscape::Selection *selection = app->get_active_selection(); + + // Group selection->group(); } @@ -31,6 +33,8 @@ void select_object_ungroup(InkscapeApplication* app) { Inkscape::Selection *selection = app->get_active_selection(); + + // Ungroup selection->ungroup(); } @@ -38,6 +42,8 @@ void select_object_ungroup_pop(InkscapeApplication* app) { Inkscape::Selection *selection = app->get_active_selection(); + + // Pop Selected Objects out of Group selection->popFromGroup(); } @@ -45,6 +51,8 @@ void selection_top(InkscapeApplication* app) { Inkscape::Selection *selection = app->get_active_selection(); + + // Raise to Top selection->raiseToTop(); } @@ -52,6 +60,8 @@ void selection_raise(InkscapeApplication* app) { Inkscape::Selection *selection = app->get_active_selection(); + + // Raise selection->raise(); } @@ -59,6 +69,8 @@ void selection_lower(InkscapeApplication* app) { Inkscape::Selection *selection = app->get_active_selection(); + + // Lower selection->lower(); } @@ -66,6 +78,8 @@ void selection_bottom(InkscapeApplication* app) { Inkscape::Selection *selection = app->get_active_selection(); + + // Lower to Bottom selection->lowerToBottom(); } diff --git a/src/ui/dialog/command-palette.cpp b/src/ui/dialog/command-palette.cpp index 14ab8bc557..338caf0f7b 100644 --- a/src/ui/dialog/command-palette.cpp +++ b/src/ui/dialog/command-palette.cpp @@ -44,7 +44,6 @@ #include #include -// #include "actions/actions-hint-data.h" #include "actions/actions-extra-data.h" #include "file.h" #include "gc-anchored.h" -- GitLab From f3476763505c48f186dab3914e212bd32b39cd2e Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 19 Jun 2021 16:12:40 +0200 Subject: [PATCH 042/235] a cleanup for the last code. --- src/helper/NonOverlappingPathsBuilder.cpp | 44 +++++------------------ 1 file changed, 9 insertions(+), 35 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 30889c583a..001cce866f 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -27,8 +27,6 @@ namespace Inkscape { void NonOverlappingPathsBuilder::build() { // TODO It should have its own widget. - // TODO get rid of all fixed numbers so that it works - // with shapes of any size. // TODO (for swipe-union thing) may want to copy the style of // the first object in the selection instead of letting it be // random (probably the it copies the style of the biggest @@ -274,7 +272,7 @@ public: std::vector break_into_non_overlapping_pieces(const PathHelper &path) { // FIXME you can't rely on the current area calculation method. make it work with curves. - assert(get_common_operation(path) == -1); + // assert(get_common_operation(path) == -1); auto intersection_paths = sp_pathvector_boolop(paths, path.paths, bool_op_inters, fill_nonZero, fill_nonZero); auto intersection_item = sp_item_repr_compare_position_bool(path.item, this->item) ? this->item : path.item; @@ -346,42 +344,22 @@ XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, const SP void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() { std::vector result; - { - int n = items.size(); - result.resize(n); - for (int i = 0; i < n; i++) { - result[i] = PathHelper(items[i]->get_pathvector(), items[i], {}); - } + int n = items.size(); + result.resize(n); + + for (int i = 0; i < n; i++) { + result[i] = PathHelper(items[i]->get_pathvector(), items[i], {}); } for (int i = 0; i < result.size(); i++) { for (int j = 0; j < result.size(); j++) { if (i == j) { continue; } - std::cout << "Path i (" << i << "): "; - print_bounds_and_nodes_count(result[i].paths); - - std::cout << "Path j (" << j << "): "; - print_bounds_and_nodes_count(result[j].paths); - int common_operation = result[i].get_common_operation(result[j]); - if (common_operation != -1) { - std::cout << "Share the operation #" << common_operation << ".\n\n"; - continue; - } + if (common_operation != -1) { continue; } auto broken = result[i].break_into_non_overlapping_pieces(result[j]); - if (broken.empty()) { - std::cout << "Don't intersect.\n\n"; - continue; - } - - std::cout << "Broken into " << broken.size() << " pieces:\n"; - for (int i = 0; i < broken.size(); i++) { - std::cout << i + 1 << ": "; - print_bounds_and_nodes_count(broken[i].paths); - } - std::cout << "\n\n"; + if (broken.empty()) { continue; } // the bigger index should be erased first. int bigger_index = (i > j) ? i : j; @@ -395,13 +373,9 @@ void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() } } - std::cout << "Result shapes dimensions:\n"; - - int n = result.size(); + n = result.size(); result_nodes.resize(n); for (int i = 0; i < n; i++) { - std::cout << i + 1 << ": "; - print_bounds_and_nodes_count(result[i].paths); result_nodes[i] = draw_on_canvas(result[i].paths, result[i].item); } } -- GitLab From 4c6fd7b2ce2feb44120db71880f2da199384eab0 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 19 Jun 2021 16:42:16 +0200 Subject: [PATCH 043/235] A temporary fix for a bug that is not found yet. for some reason, if the paths are not sorted such that the first item is the bottom item, some extra shapes gets drawn. just sorting for now. later, will have to fix this bug. --- src/helper/NonOverlappingPathsBuilder.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 001cce866f..2d2d3a4553 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -274,7 +274,10 @@ public: // FIXME you can't rely on the current area calculation method. make it work with curves. // assert(get_common_operation(path) == -1); + // Geom::PathIntersectionGraph pig(paths, path.paths, Geom::EPSILON); + auto intersection_paths = sp_pathvector_boolop(paths, path.paths, bool_op_inters, fill_nonZero, fill_nonZero); + // auto intersection_paths = pig.getIntersection(); auto intersection_item = sp_item_repr_compare_position_bool(path.item, this->item) ? this->item : path.item; PathHelper intersection(intersection_paths, intersection_item, operations, path.operations); intersection.prepare_for_current_operation(); @@ -283,10 +286,12 @@ public: return {}; } + // auto diff1_paths = pig.getAminusB(); auto diff1_paths = sp_pathvector_boolop(paths, path.paths, bool_op_diff, fill_nonZero, fill_nonZero); PathHelper diff1(diff1_paths, path.item, path.operations); diff1.prepare_for_current_operation(); + // auto diff2_paths = pig.getBminusA(); auto diff2_paths = sp_pathvector_boolop(path.paths, paths, bool_op_diff, fill_nonZero, fill_nonZero); PathHelper diff2(diff2_paths, item, operations); diff2.prepare_for_current_operation(); @@ -341,6 +346,14 @@ XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, const SP return repr; } +void sort_pathhelper_vector(std::vector &vector) +{ + auto cmp = [](const PathHelper &a, const PathHelper &b) { + return sp_item_repr_compare_position_bool(a.item, b.item); + }; + std::sort(vector.begin(), vector.end(), cmp); +} + void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() { std::vector result; @@ -351,6 +364,12 @@ void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() result[i] = PathHelper(items[i]->get_pathvector(), items[i], {}); } + // FIXME for some reason, sometimes the output is not correct unless + // the items are sorted such that the lowest item is at the beginning. + // THIS SHOULD NOT BE HERE. FIX THIS BUG. + // It doesn't happen as often. it happened with the 6 intersecting circles. + sort_pathhelper_vector(result); + for (int i = 0; i < result.size(); i++) { for (int j = 0; j < result.size(); j++) { if (i == j) { continue; } -- GitLab From 2fe671cdfd4fd7f1bdcd137ea261dc13bef93f4b Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Sun, 20 Jun 2021 01:42:58 +0530 Subject: [PATCH 044/235] Menubar View (Remaining Part) Have a FIXME related to message display in actions-canvas-mode.cpp --- share/ui/menu-view.ui | 127 +++++++++ share/ui/menus.xml | 6 +- src/CMakeLists.txt | 2 + src/actions/actions-canvas-mode.cpp | 413 +++++++++++++++++++++++++++- src/actions/actions-doc-file.cpp | 60 ++++ src/actions/actions-doc-file.h | 19 ++ src/actions/actions-edit.cpp | 82 +++--- src/desktop.cpp | 25 +- src/desktop.h | 2 +- src/document.cpp | 2 + src/object/sp-namedview.cpp | 7 - src/ui/desktop/menubar.cpp | 70 +---- src/verbs.cpp | 12 +- 13 files changed, 667 insertions(+), 160 deletions(-) create mode 100644 src/actions/actions-doc-file.cpp create mode 100644 src/actions/actions-doc-file.h diff --git a/share/ui/menu-view.ui b/share/ui/menu-view.ui index ebbe4e5416..39f328ae97 100644 --- a/share/ui/menu-view.ui +++ b/share/ui/menu-view.ui @@ -191,6 +191,133 @@ Color Management win.canvas-color-manage + + Page _Grid + win.canvas-show-grid + + + G_uides + win.canvas-show-guides + + + + Sh_ow/Hide + +
+ + _Commands Bar + win.canvas-commands-bar + + + Sn_ap Controls Bar + win.canvas-snap-controls-bar + + + T_ool Controls Bar + win.canvas-tool-control-bar + + + _Toolbox + win.canvas-toolbox + + + _Rulers + win.canvas-rulers + + + Scroll_bars + win.canvas-scroll-bars + + + _Palette + win.canvas-palette + + + _Statusbar + win.canvas-statusbar + +
+
+ +
+ + Show/Hide D_ialogs + win.dialog-toggle + show-dialogs + + + _Command Palette + win.canvas-command-palette + +
+ +
+ + S_watches... + win.dialog-open + Swatches + swatches + + + _Messages... + win.dialog-open + Messages + dialog-messages + +
+ +
+ + P_revious Window + doc.window-previous + window-previous + + + N_ext Window + doc.window-next + window-next + +
+ +
+ + Icon Preview + win.dialog-open + IconPreview + dialog-icon-preview + + + Duplic_ate Window + win.window-new + window-new + +
+ +
+ + _Fullscreen + win.view-fullscreen + view-fullscreen + +
+ +
+ + Default + win.canvas-interface-mode + 0 + + + Custom + win.canvas-interface-mode + 1 + + + Wide + win.canvas-interface-mode + 2 + +
diff --git a/share/ui/menus.xml b/share/ui/menus.xml index b3252f1f0d..26029cd669 100644 --- a/share/ui/menus.xml +++ b/share/ui/menus.xml @@ -140,7 +140,7 @@ - + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 364abd045a..312aeb0468 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -210,6 +210,8 @@ set(inkscape_SRC actions/actions-canvas-snapping.cpp actions/actions-canvas-transform.h actions/actions-canvas-transform.cpp + actions/actions-doc-file.h + actions/actions-doc-file.cpp actions/actions-hide-lock.h actions/actions-hide-lock.cpp actions/actions-edit.h diff --git a/src/actions/actions-canvas-mode.cpp b/src/actions/actions-canvas-mode.cpp index 4eedda9651..c03190e090 100644 --- a/src/actions/actions-canvas-mode.cpp +++ b/src/actions/actions-canvas-mode.cpp @@ -13,6 +13,10 @@ #include // Not ! To eventually allow a headless version! #include +#include "ui/interface.h" +#include "ui/uxmanager.h" +#include "ui/view/view.h" + #include "actions-canvas-mode.h" #include "inkscape-application.h" @@ -253,6 +257,348 @@ canvas_color_manage_toggle(InkscapeWindow *win) canvas->redraw_all(); } +void +canvas_show_grid_toggle(InkscapeWindow *win) +{ + // Get Action + auto action = win->lookup_action("canvas-show-grid"); + if (!action) { + std::cerr << "canvas_show_grid_toggle: action missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "canvas_show_grid_toggle: action not SimpleAction!" << std::endl; + return; + } + + // Toggle State + bool state = false; + saction->get_state(state); + state = !state; + saction->change_state(state); + + // Toggle Action + SPDesktop* dt = win->get_desktop(); + dt->toggleGrids(); +} + +void +canvas_show_guides_toggle(InkscapeWindow *win) +{ + // Get Action + auto action = win->lookup_action("canvas-show-guides"); + if (!action) { + std::cerr << "canvas_show_guides_toggle: action missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "canvas_show_guides_toggle: action not SimpleAction!" << std::endl; + return; + } + + // Toggle State + bool state = false; + saction->get_state(state); + state = !state; + saction->change_state(state); + + // Toggle Action + SPDesktop* dt = win->get_desktop(); + SPDocument* doc = dt->getDocument(); + sp_namedview_toggle_guides(doc, dt->namedview); +} + +void +canvas_commands_bar_toggle(InkscapeWindow *win) +{ + // Get Action + auto action = win->lookup_action("canvas-commands-bar"); + if (!action) { + std::cerr << "canvas_commands_bar_toggle: action missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "canvas_commands_bar_toggle: action not SimpleAction!" << std::endl; + return; + } + + // Toggle State + bool state = false; + saction->get_state(state); + state = !state; + saction->change_state(state); + + // Toggle Action + SPDesktop* dt = win->get_desktop(); + dt->toggleToolbar("commands"); +} + +void +canvas_snap_controls_bar_toggle(InkscapeWindow *win) +{ + // Get Action + auto action = win->lookup_action("canvas-snap-controls-bar"); + if (!action) { + std::cerr << "canvas_snap_controls_bar_toggle: action missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "canvas_snap_controls_bar_toggle: action not SimpleAction!" << std::endl; + return; + } + + // Toggle State + bool state = false; + saction->get_state(state); + state = !state; + saction->change_state(state); + + // Toggle Action + SPDesktop* dt = win->get_desktop(); + dt->toggleToolbar("snaptoolbox"); +} + +void +canvas_tool_control_bar_toggle(InkscapeWindow *win) +{ + // Get Action + auto action = win->lookup_action("canvas-tool-control-bar"); + if (!action) { + std::cerr << "canvas_tool_control_bar_toggle: action missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "canvas_tool_control_bar_toggle: action not SimpleAction!" << std::endl; + return; + } + + // Toggle State + bool state = false; + saction->get_state(state); + state = !state; + saction->change_state(state); + + // Toggle Action + SPDesktop* dt = win->get_desktop(); + dt->toggleToolbar("toppanel"); +} + +void +canvas_toolbox_toggle(InkscapeWindow *win) +{ + // Get Action + auto action = win->lookup_action("canvas-toolbox"); + if (!action) { + std::cerr << "canvas_toolbox_toggle: action missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "canvas_toolbox_toggle: action not SimpleAction!" << std::endl; + return; + } + + // Toggle State + bool state = false; + saction->get_state(state); + state = !state; + saction->change_state(state); + + // Toggle Action + SPDesktop* dt = win->get_desktop(); + dt->toggleToolbar("toolbox"); +} + +void +canvas_rulers_toggle(InkscapeWindow *win) +{ + // Get Action + auto action = win->lookup_action("canvas-rulers"); + if (!action) { + std::cerr << "canvas_rulers_toggle: action missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "canvas_rulers_toggle: action not SimpleAction!" << std::endl; + return; + } + + // Toggle State + bool state = false; + saction->get_state(state); + state = !state; + saction->change_state(state); + + // Toggle Action + SPDesktop* dt = win->get_desktop(); + dt->toggleToolbar("rulers"); +} + +void +canvas_scroll_bars(InkscapeWindow *win) +{ + // Get Action + auto action = win->lookup_action("canvas-scroll-bars"); + if (!action) { + std::cerr << "canvas_scroll_bars: action missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "canvas_scroll_bars: action not SimpleAction!" << std::endl; + return; + } + + // Toggle State + bool state = false; + saction->get_state(state); + state = !state; + saction->change_state(state); + + // Toggle Action + SPDesktop* dt = win->get_desktop(); + dt->toggleToolbar("scrollbars"); +} + +void +canvas_palette_toggle(InkscapeWindow *win) +{ + // Get Action + auto action = win->lookup_action("canvas-palette"); + if (!action) { + std::cerr << "canvas_palette_toggle: action missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "canvas_palette_toggle: action not SimpleAction!" << std::endl; + return; + } + + // Toggle State + bool state = false; + saction->get_state(state); + state = !state; + saction->change_state(state); + + // Toggle Action + SPDesktop* dt = win->get_desktop(); + dt->toggleToolbar("panels"); +} + +void +canvas_statusbar_toggle(InkscapeWindow *win) +{ + // Get Action + auto action = win->lookup_action("canvas-statusbar"); + if (!action) { + std::cerr << "canvas_statusbar_toggle: action missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "canvas_statusbar_toggle: action not SimpleAction!" << std::endl; + return; + } + + // Toggle State + bool state = false; + saction->get_state(state); + state = !state; + saction->change_state(state); + + // Toggle Action + SPDesktop* dt = win->get_desktop(); + dt->toggleToolbar("statusbar"); +} + +void +canvas_command_palette(InkscapeWindow *win) +{ + SPDesktop* dt = win->get_desktop(); + dt->toggleCommandPalette(); +} + +void +window_new(InkscapeWindow *win) +{ + sp_ui_new_view(); +} + +void +view_fullscreen(InkscapeWindow *win) +{ + SPDesktop* dt = win->get_desktop(); + dt->fullscreen(); +} + +void +canvas_interface_mode(int value, InkscapeWindow *win) +{ + if (value < 0 || value >= 3) { + std::cerr << "canvas_interface_mode: value out of bound! : " << value << std::endl; + return; + } + + auto action = win->lookup_action("canvas-interface-mode"); + if (!action) { + std::cerr << "canvas_interface_mode: action 'canvas-interface-mode' missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "canvas_interface_mode: action 'canvas-interface-mode' not SimpleAction!" << std::endl; + return; + } + + // Setting Message + Glib::ustring tip; + if (value == 0) { + tip = _("Default interface setup"); + } + else if (value == 1) { + tip = _("Setup for custom task"); + } + else if (value == 2) { + tip = _("Setup for widescreen work"); + } + + // Change state + saction->change_state(value); + + // Set Interface + SPDesktop* dt = win->get_desktop(); + Inkscape::UI::UXManager::getInstance()->setTask(dt, value); + +#ifdef GDK_WINDOWING_QUARTZ + // call later, crashes during startup if called directly + g_idle_add(sync_menubar, nullptr); +#endif + + // Message FIXME having some error + // dt->tipsMessageContext()->clear(); + // dt->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, gettext( tip.c_str() ) ); + // similar = dt->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, gettext( tool_msg[tool].c_str() ) ); + +} + std::vector> raw_data_canvas_mode = { // clang-format off @@ -269,6 +615,27 @@ std::vector> raw_data_canvas_mode = {"win.canvas-color-mode", N_("Color Mode"), "Canvas Display", N_("Toggle between normal and grayscale modes") }, {"win.canvas-color-manage", N_("Color Managed Mode"), "Canvas Display", N_("Toggle between normal and color managed modes") }, + + {"win.canvas-show-grid", N_("Page Grid"), "Canvas Display", N_("Show or hide the page grid") }, + + {"win.canvas-show-guides", N_("Guides"), "Canvas Display", N_("Show or hide guides (drag from a ruler to create a guide)") }, + {"win.canvas-commands-bar", N_("Commands Bar"), "Canvas Display", N_("Show or hide the Commands bar (under the menu)") }, + {"win.canvas-snap-controls-bar", N_("Snap Controls Bar"), "Canvas Display", N_("Show or hide the snapping controls")}, + {"win.canvas-tool-control-bar", N_("Tool Controls Bar"), "Canvas Display", N_("Show or hide the Tool Controls bar")}, + {"win.canvas-toolbox", N_("Toolbox"), "Canvas Display", N_("Show or hide the main toolbox (on the left)")}, + {"win.canvas-rulers", N_("Rulers"), "Canvas Display", N_("Show or hide the canvas rulers")}, + {"win.canvas-scroll-bars", N_("Scroll bars"), "Canvas Display", N_("Show or hide the canvas scrollbars")}, + {"win.canvas-palette", N_("Palette"), "Canvas Display", N_("Show or hide the color palette")}, + {"win.canvas-statusbar", N_("Statusbar"), "Canvas Display", N_("Show or hide the statusbar (at the bottom of the window)")}, + + {"win.canvas-command-palette", N_("Command Palette"), "Canvas Display", N_("Show or hide the on-canvas command palette")}, + {"win.window-new", N_("Duplicate Window"), "Canvas Display", N_("Open a new window with the same document")}, + {"win.view-fullscreen", N_("Fullscreen"), "Canvas Display", N_("Stretch this document window to full screen")}, + + {"win.canvas-interface-mode(0)", N_("Default"), "Canvas Display", N_("Default interface setup") }, + {"win.canvas-interface-mode(1)", N_("Custom"), "Canvas Display", N_("Setup for custom task") }, + {"win.canvas-interface-mode(2)", N_("Wide"), "Canvas Display", N_("Setup for widescreen work") } + // clang-format on }; @@ -277,11 +644,22 @@ add_actions_canvas_mode(InkscapeWindow* win) { // Sync action with desktop variables. TODO: Remove! auto prefs = Inkscape::Preferences::get(); + SPDesktop* dt = win->get_desktop(); - int display_mode = prefs->getIntLimited("/options/displaymode", 0, 0, 4); // Default, minimum, maximum - bool color_manage = prefs->getBool("/options/displayprofile/enable"); + // Initial States of Actions + int display_mode = prefs->getIntLimited("/options/displaymode", 0, 0, 4); // Default, minimum, maximum + bool color_manage = prefs->getBool("/options/displayprofile/enable"); + bool commands_toggle = prefs->getBool("/window/commands/state", true); + bool snaptoolbox_toggle = prefs->getBool("/window/snaptoolbox/state", true); + bool toppanel_toggle = prefs->getBool("/window/toppanel/state", true); + bool toolbox_toggle = prefs->getBool("/window/toolbox/state", true); + bool panels_toggle = prefs->getBool("/window/panels/state", true); + bool statusbar_toggle = prefs->getBool("/window/statusbar/state", true); + bool scrollbars_toggle = prefs->getBool("/window/scrollbars/state", true); + bool rulers_toggle = prefs->getBool("/window/rulers/state", true); + bool guides_toggle = win->get_desktop()->namedview->getRepr()->getAttributeBoolean("showguides", true); // Should set it true or retrive the state (every time it set to true on restart) + int interface_mode = Inkscape::UI::UXManager::getInstance()->getDefaultTask(dt); - SPDesktop* dt = win->get_desktop(); if (dt) { auto canvas = dt->getCanvas(); canvas->set_render_mode(Inkscape::RenderMode(display_mode)); @@ -291,15 +669,26 @@ add_actions_canvas_mode(InkscapeWindow* win) } // clang-format off - win->add_action_radio_integer ("canvas-display-mode", sigc::bind(sigc::ptr_fun(&canvas_display_mode), win), display_mode); - win->add_action( "canvas-display-mode-cycle", sigc::bind(sigc::ptr_fun(&canvas_display_mode_cycle), win) ); - win->add_action( "canvas-display-mode-toggle", sigc::bind(sigc::ptr_fun(&canvas_display_mode_toggle), win) ); - - win->add_action_radio_integer ("canvas-split-mode", sigc::bind(sigc::ptr_fun(&canvas_split_mode), win), (int)Inkscape::SplitMode::NORMAL); - - win->add_action_bool( "canvas-color-mode", sigc::bind(sigc::ptr_fun(&canvas_color_mode_toggle), win) ); - - win->add_action_bool( "canvas-color-manage", sigc::bind(sigc::ptr_fun(&canvas_color_manage_toggle), win), color_manage); + win->add_action_radio_integer ("canvas-display-mode", sigc::bind(sigc::ptr_fun(&canvas_display_mode), win), display_mode); + win->add_action( "canvas-display-mode-cycle", sigc::bind(sigc::ptr_fun(&canvas_display_mode_cycle), win)); + win->add_action( "canvas-display-mode-toggle", sigc::bind(sigc::ptr_fun(&canvas_display_mode_toggle), win)); + win->add_action_radio_integer ("canvas-split-mode", sigc::bind(sigc::ptr_fun(&canvas_split_mode), win), (int)Inkscape::SplitMode::NORMAL); + win->add_action_bool( "canvas-color-mode", sigc::bind(sigc::ptr_fun(&canvas_color_mode_toggle), win)); + win->add_action_bool( "canvas-color-manage", sigc::bind(sigc::ptr_fun(&canvas_color_manage_toggle), win), color_manage); + win->add_action_bool( "canvas-show-grid", sigc::bind(sigc::ptr_fun(&canvas_show_grid_toggle), win)); + win->add_action_bool( "canvas-show-guides", sigc::bind(sigc::ptr_fun(&canvas_show_guides_toggle), win), guides_toggle); + win->add_action_bool( "canvas-commands-bar", sigc::bind(sigc::ptr_fun(&canvas_commands_bar_toggle), win), commands_toggle); + win->add_action_bool( "canvas-snap-controls-bar", sigc::bind(sigc::ptr_fun(&canvas_snap_controls_bar_toggle), win), snaptoolbox_toggle); + win->add_action_bool( "canvas-tool-control-bar", sigc::bind(sigc::ptr_fun(&canvas_tool_control_bar_toggle), win), toppanel_toggle); + win->add_action_bool( "canvas-toolbox", sigc::bind(sigc::ptr_fun(&canvas_toolbox_toggle), win), toolbox_toggle); + win->add_action_bool( "canvas-rulers", sigc::bind(sigc::ptr_fun(&canvas_rulers_toggle), win), rulers_toggle); + win->add_action_bool( "canvas-scroll-bars", sigc::bind(sigc::ptr_fun(&canvas_scroll_bars), win), scrollbars_toggle); + win->add_action_bool( "canvas-palette", sigc::bind(sigc::ptr_fun(&canvas_palette_toggle), win), panels_toggle); + win->add_action_bool( "canvas-statusbar", sigc::bind(sigc::ptr_fun(&canvas_statusbar_toggle), win), statusbar_toggle); + win->add_action( "canvas-command-palette", sigc::bind(sigc::ptr_fun(&canvas_command_palette), win)); + win->add_action( "window-new", sigc::bind(sigc::ptr_fun(&window_new), win)); + win->add_action( "view-fullscreen", sigc::bind(sigc::ptr_fun(&view_fullscreen), win)); + win->add_action_radio_integer ("canvas-interface-mode", sigc::bind(sigc::ptr_fun(&canvas_interface_mode), win), interface_mode); // clang-format on auto app = InkscapeApplication::instance(); diff --git a/src/actions/actions-doc-file.cpp b/src/actions/actions-doc-file.cpp new file mode 100644 index 0000000000..98d2bb39a3 --- /dev/null +++ b/src/actions/actions-doc-file.cpp @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * + * Actions Related to Files + * + * Authors: + * Sushant A A + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#include + +#include "actions-doc-file.h" +#include "actions/actions-extra-data.h" +#include "inkscape-application.h" + +#include "document.h" +#include "inkscape.h" +#include "object/sp-namedview.h" + +void +window_previous(SPDocument* document) +{ + INKSCAPE.switch_desktops_prev(); +} + +void +window_next(SPDocument* document) +{ + INKSCAPE.switch_desktops_next(); +} + +std::vector> raw_data_doc_file = +{ + {"doc.window-previous", N_("P_revious Window"), "File", N_("Switch to the previous document window")}, + {"doc.window-next", N_("N_ext Window"), "File", N_("Switch to the next document window")} +}; + +void +add_actions_doc_file(SPDocument* document) +{ + std::cout<<"add_actions_doc_file\n"; + + Glib::RefPtr map = document->getActionGroup(); + + map->add_action( "window-previous", sigc::bind(sigc::ptr_fun(&window_previous), document)); + map->add_action( "window-next", sigc::bind(sigc::ptr_fun(&window_next), document)); + + // Check if there is already an application instance (GUI or non-GUI). + auto app = InkscapeApplication::instance(); + if (!app) { + std::cerr << "add_actions_doc_file: no app!" << std::endl; + return; + } + app->get_action_extra_data().add_data(raw_data_doc_file); +} \ No newline at end of file diff --git a/src/actions/actions-doc-file.h b/src/actions/actions-doc-file.h new file mode 100644 index 0000000000..b04d5e3909 --- /dev/null +++ b/src/actions/actions-doc-file.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * + * Authors: + * Sushant A A + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#ifndef INK_ACTIONS_DOC_FILE_H +#define INK_ACTIONS_DOC_FILE_H + +class SPDocument; + +void add_actions_doc_file(SPDocument* document); + +#endif // INK_ACTIONS_DOC_FILE_H \ No newline at end of file diff --git a/src/actions/actions-edit.cpp b/src/actions/actions-edit.cpp index a0250ccbb0..2c9b6123b5 100644 --- a/src/actions/actions-edit.cpp +++ b/src/actions/actions-edit.cpp @@ -12,7 +12,7 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ -#include // Not ! To eventually allow a headless version! +#include #include #include "actions-edit.h" @@ -26,7 +26,7 @@ void object_to_pattern(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Objects to Pattern dt->selection->tile(); } @@ -35,7 +35,7 @@ void pattern_to_object(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Pattern to Objects dt->selection->untile(); } @@ -44,7 +44,7 @@ void object_to_marker(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Objects to Marker dt->selection->toMarker(); } @@ -53,7 +53,7 @@ void object_to_guides(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Objects to Guides dt->selection->toGuides(); } @@ -62,7 +62,7 @@ void undo(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Undo sp_undo(dt, dt->getDocument()); } @@ -71,7 +71,7 @@ void redo(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Redo sp_redo(dt, dt->getDocument()); } @@ -80,7 +80,7 @@ void cut(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Cut dt->selection->cut(); } @@ -89,7 +89,7 @@ void copy(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Copy dt->selection->copy(); } @@ -98,7 +98,7 @@ void paste(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Paste sp_selection_paste(dt, false); } @@ -107,7 +107,7 @@ void paste_in_place(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Paste In Place sp_selection_paste(dt, true); } @@ -116,7 +116,7 @@ void paste_style(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Paste Style dt->selection->pasteStyle(); } @@ -125,7 +125,7 @@ void paste_size(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Paste Size dt->selection->pasteSize(true,true); } @@ -134,7 +134,7 @@ void paste_width(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Paste Width dt->selection->pasteSize(true, false); } @@ -143,7 +143,7 @@ void paste_height(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Paste Height dt->selection->pasteSize(false, true); } @@ -152,7 +152,7 @@ void paste_size_separately(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Paste Size Separately dt->selection->pasteSizeSeparately(true, true); } @@ -161,7 +161,7 @@ void paste_width_separately(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Paste Width Separately dt->selection->pasteSizeSeparately(true, false); } @@ -170,7 +170,7 @@ void paste_height_separately(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Paste Height Separately dt->selection->pasteSizeSeparately(false, true); } @@ -179,7 +179,7 @@ void duplicate(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Duplicate dt->selection->duplicate(); } @@ -188,7 +188,7 @@ void clone(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Create Clone dt->selection->clone(); } @@ -197,7 +197,7 @@ void clone_unlink(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Unlink Clone dt->selection->unlink(); } @@ -206,7 +206,7 @@ void clone_unlink_recursively(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Unlink Clones recursively dt->selection->unlinkRecursive(false, true); } @@ -215,7 +215,7 @@ void clone_link(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Relink to Copied dt->selection->relink(); } @@ -224,7 +224,7 @@ void select_original(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Select Original dt->selection->cloneOriginal(); } @@ -233,7 +233,7 @@ void clone_link_lpe(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Clone original path (LPE) dt->selection->cloneOriginalPathLPE(); } @@ -242,7 +242,7 @@ void edit_delete(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Delete dt->selection->deleteItems(); } @@ -251,7 +251,7 @@ void select_all(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Select All Inkscape::SelectionHelper::selectAll(dt); } @@ -260,7 +260,7 @@ void select_all_layers(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Select All in All Layers Inkscape::SelectionHelper::selectAllInAll(dt); } @@ -269,7 +269,7 @@ void select_same_fill_and_stroke(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Fill and Stroke Inkscape::SelectionHelper::selectSameFillStroke(dt); } @@ -278,7 +278,7 @@ void select_same_fill(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Fill Color Inkscape::SelectionHelper::selectSameFillColor(dt); } @@ -287,7 +287,7 @@ void select_same_stroke_color(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Stroke Color Inkscape::SelectionHelper::selectSameStrokeColor(dt); } @@ -296,7 +296,7 @@ void select_same_stroke_style(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Stroke Style Inkscape::SelectionHelper::selectSameStrokeStyle(dt); } @@ -305,7 +305,7 @@ void select_same_object_type(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Object Type Inkscape::SelectionHelper::selectSameObjectType(dt); } @@ -314,7 +314,7 @@ void select_invert(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Invert Selection Inkscape::SelectionHelper::invert(dt); } @@ -323,7 +323,7 @@ void select_none(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Deselect Inkscape::SelectionHelper::selectNone(dt); } @@ -332,7 +332,7 @@ void create_guides_around_page(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Create Guides Around the Page sp_guide_create_guides_around_page(dt); } @@ -360,7 +360,7 @@ lock_all_guides(InkscapeWindow *win) saction->change_state(state); SPDesktop* dt = win->get_desktop(); - + // Lock All Guides dt->toggleGuidesLock(); } @@ -369,7 +369,7 @@ void delete_all_guides(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Delete All Guides sp_guide_delete_all_guides(dt); } @@ -378,7 +378,7 @@ void paste_path_effect(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Paste Path Effect dt->selection->pastePathEffect(); } @@ -387,7 +387,7 @@ void remove_path_effect(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Remove Path Effect dt->selection->removeLPE(); } @@ -397,7 +397,7 @@ void path_effect_parameter_next(InkscapeWindow* win) { SPDesktop* dt = win->get_desktop(); - + // Next path effect parameter sp_selection_next_patheffect_param(dt); } diff --git a/src/desktop.cpp b/src/desktop.cpp index 11598afe70..f699283de2 100644 --- a/src/desktop.cpp +++ b/src/desktop.cpp @@ -1333,33 +1333,22 @@ void SPDesktop::toggleRulers() { _widget->toggle_rulers(); - Inkscape::Verb *verb = Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS); - if (verb) { - _menu_update.emit(verb->get_code(), getStateFromPref(this, "rulers")); - } } void SPDesktop::toggleScrollbars() { _widget->toggle_scrollbars(); - Inkscape::Verb *verb = Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS); - if (verb) { - _menu_update.emit(verb->get_code(), getStateFromPref(this, "scrollbars")); - } } - -void SPDesktop::toggleToolbar(gchar const *toolbar_name, unsigned int verbenum) +void SPDesktop::toggleToolbar(gchar const *toolbar_name) { Glib::ustring pref_path = getLayoutPrefPath(this) + toolbar_name + "/state"; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); gboolean visible = prefs->getBool(pref_path, true); prefs->setBool(pref_path, !visible); - Inkscape::Verb *verb = Inkscape::Verb::get(verbenum); - if (verb) { - _menu_update.emit(verb->get_code(), getStateFromPref(this, toolbar_name)); - } + layoutWidget(); } @@ -1507,10 +1496,6 @@ void SPDesktop::toggleColorProfAdjust() void SPDesktop::toggleGuidesLock() { sp_namedview_guides_toggle_lock(this->getDocument(), namedview); - Inkscape::Verb *verb = Inkscape::Verb::get(SP_VERB_EDIT_GUIDES_TOGGLE_LOCK); - if (verb) { - _menu_update.emit(verb->get_code(), namedview->lockguides); - } } bool SPDesktop::colorProfAdjustEnabled() @@ -1527,10 +1512,6 @@ void SPDesktop::toggleGrids() namedview->writeNewGrid(this->getDocument(), Inkscape::GRID_RECTANGULAR); showGrids(true); } - Inkscape::Verb *verb = Inkscape::Verb::get(SP_VERB_TOGGLE_GRID); - if (verb) { - _menu_update.emit(verb->get_code(), gridsEnabled()); - } } void SPDesktop::showGrids(bool show, bool dirty_document) diff --git a/src/desktop.h b/src/desktop.h index 4f7db4d419..797c448b07 100644 --- a/src/desktop.h +++ b/src/desktop.h @@ -435,7 +435,7 @@ public: void toggleGrids(); bool gridsEnabled() const { return grids_visible; }; void showGrids(bool show, bool dirty_document = true); - void toggleToolbar(gchar const *toolbar_name, unsigned int verbenum); + void toggleToolbar(gchar const *toolbar_name); bool is_iconified(); bool is_darktheme(); diff --git a/src/document.cpp b/src/document.cpp index c76968a962..1d7778fed6 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -55,6 +55,7 @@ #include "rdf.h" #include "actions/actions-canvas-snapping.h" +#include "actions/actions-doc-file.h" #include "display/drawing.h" @@ -148,6 +149,7 @@ SPDocument::SPDocument() : // Actions action_group = Gio::SimpleActionGroup::create(); add_actions_canvas_snapping(this); + add_actions_doc_file(this); } SPDocument::~SPDocument() { diff --git a/src/object/sp-namedview.cpp b/src/object/sp-namedview.cpp index 8d902e8fe9..506760ccef 100644 --- a/src/object/sp-namedview.cpp +++ b/src/object/sp-namedview.cpp @@ -1002,13 +1002,6 @@ void sp_namedview_toggle_guides(SPDocument *doc, SPNamedView *namedview) DocumentUndo::setUndoSensitive(doc, false); repr->setAttributeBoolean("showguides", v); DocumentUndo::setUndoSensitive(doc, saved); - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - if (desktop) { - Inkscape::Verb *verb = Inkscape::Verb::get(SP_VERB_TOGGLE_GUIDES); - if (verb) { - desktop->_menu_update.emit(verb->get_code(), namedview->getGuides()); - } - } doc->setModifiedSinceSave(); } diff --git a/src/ui/desktop/menubar.cpp b/src/ui/desktop/menubar.cpp index 1bb006af1e..4502ff343e 100644 --- a/src/ui/desktop/menubar.cpp +++ b/src/ui/desktop/menubar.cpp @@ -237,6 +237,8 @@ bool getStateFromPref(SPDesktop *dt, Glib::ustring item) } // I wonder if this can be done without hard coding names. + +// check and remove it after all toggle done in Gio::Actions static void checkitem_update(Gtk::CheckMenuItem* menuitem, SPAction* action) { @@ -317,67 +319,6 @@ build_menu_check_item_from_verb(SPAction* action) return menuitem; } - -// ================= Tasks Submenu ============== -static void -task_activated(SPDesktop* dt, int number) -{ - Inkscape::UI::UXManager::getInstance()->setTask(dt, number); - -#ifdef GDK_WINDOWING_QUARTZ - // call later, crashes during startup if called directly - g_idle_add(sync_menubar, nullptr); -#endif -} - -// Sets tip -static void -select_task(SPDesktop* dt, Glib::ustring tip) -{ - dt->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, tip.c_str()); -} - -// Clears tip -static void -deselect_task(SPDesktop* dt) -{ - dt->tipsMessageContext()->clear(); -} - -static void -add_tasks(Gtk::MenuShell* menu, SPDesktop* dt) -{ - const Glib::ustring data[3][2] = { - { C_("Interface setup", "Default"), _("Default interface setup") }, - { C_("Interface setup", "Custom"), _("Setup for custom task") }, - { C_("Interface setup", "Wide"), _("Setup for widescreen work") } - }; - - int active = Inkscape::UI::UXManager::getInstance()->getDefaultTask(dt); - - Gtk::RadioMenuItem::Group group; - - for (unsigned int i = 0; i < 3; ++i) { - - Gtk::RadioMenuItem* menuitem = Gtk::manage(new Gtk::RadioMenuItem(group, data[i][0])); - if (menuitem) { - if (active == i) { - menuitem->set_active(true); - } - - menuitem->signal_toggled().connect( - sigc::bind(sigc::ptr_fun(&task_activated), dt, i)); - menuitem->signal_select().connect( - sigc::bind(sigc::ptr_fun(&select_task), dt, data[i][1])); - menuitem->signal_deselect().connect( - sigc::bind(sigc::ptr_fun(&deselect_task),dt)); - - menu->append(*menuitem); - } - } -} - - static void sp_recent_open(Gtk::RecentChooser* recentchooser) { @@ -589,13 +530,6 @@ build_menu(Gtk::MenuShell* menu, Inkscape::XML::Node* xml, Inkscape::UI::View::V continue; } - // This is used only for wide-screen vs non-wide-screen displays. - // The code should be rewritten to use actions like everything else here. - if (name == "task-checkboxes") { - add_tasks(menu, static_cast(view)); - continue; - } - if (name == "recent-file-list") { // Filter recent files to those already opened in Inkscape. diff --git a/src/verbs.cpp b/src/verbs.cpp index 86d89826ce..07ae271d72 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1829,22 +1829,22 @@ void ZoomVerb::perform(SPAction *action, void *data) dt->toggleScrollbars(); break; case SP_VERB_TOGGLE_COMMANDS_TOOLBAR: - dt->toggleToolbar("commands", SP_VERB_TOGGLE_COMMANDS_TOOLBAR); + dt->toggleToolbar("commands"); break; case SP_VERB_TOGGLE_SNAP_TOOLBAR: - dt->toggleToolbar("snaptoolbox", SP_VERB_TOGGLE_SNAP_TOOLBAR); + dt->toggleToolbar("snaptoolbox"); break; case SP_VERB_TOGGLE_TOOL_TOOLBAR: - dt->toggleToolbar("toppanel", SP_VERB_TOGGLE_TOOL_TOOLBAR); + dt->toggleToolbar("toppanel"); break; case SP_VERB_TOGGLE_TOOLBOX: - dt->toggleToolbar("toolbox", SP_VERB_TOGGLE_TOOLBOX); + dt->toggleToolbar("toolbox"); break; case SP_VERB_TOGGLE_PALETTE: - dt->toggleToolbar("panels", SP_VERB_TOGGLE_PALETTE); + dt->toggleToolbar("panels"); break; case SP_VERB_TOGGLE_STATUSBAR: - dt->toggleToolbar("statusbar", SP_VERB_TOGGLE_STATUSBAR); + dt->toggleToolbar("statusbar"); break; case SP_VERB_TOGGLE_GUIDES: sp_namedview_toggle_guides(doc, dt->namedview); -- GitLab From c6b896b21c9fed070cd37667d0bcdc6d318f5c68 Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Sun, 20 Jun 2021 15:07:46 +0530 Subject: [PATCH 045/235] Removing Verbs from Selection Chemistry Replacing DocumentUndo::done and removing other verbs --- src/selection-chemistry.cpp | 206 ++++++++++++++---------------------- 1 file changed, 80 insertions(+), 126 deletions(-) diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 99ed5f5302..520e6fbeb8 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -47,7 +47,6 @@ #include "selection.h" #include "text-editing.h" #include "text-chemistry.h" -#include "verbs.h" #include "actions/actions-tools.h" // Switching tools @@ -112,6 +111,7 @@ #include "xml/rebase-hrefs.h" #include "xml/simple-document.h" +#include "ui/icon-names.h" // TODO FIXME: This should be moved into preference repr SPCycleType SP_CYCLING = SP_CYCLE_FOCUS; @@ -390,8 +390,7 @@ void ObjectSet::deleteItems() if (desktop()) { if(dynamic_cast(desktop()->event_context)) { if (Inkscape::UI::Tools::sp_text_delete_selection(desktop()->event_context)) { - DocumentUndo::done(desktop()->getDocument(), SP_VERB_CONTEXT_TEXT, - _("Delete text")); + DocumentUndo::done(desktop()->getDocument(), _("Delete text"), INKSCAPE_ICON("draw-text")); return; } } @@ -426,7 +425,7 @@ void ObjectSet::deleteItems() } if(document()) { - DocumentUndo::done(document(), SP_VERB_EDIT_DELETE, _("Delete")); + DocumentUndo::done(document(), _("Delete"), INKSCAPE_ICON("edit-delete")); } } @@ -611,8 +610,7 @@ void ObjectSet::duplicate(bool suppressDone, bool duplicateLayer) if ( !suppressDone ) { - DocumentUndo::done(document(), SP_VERB_EDIT_DUPLICATE, - _("Duplicate")); + DocumentUndo::done(document(), _("Duplicate"), INKSCAPE_ICON("edit-duplicate")); } if(!duplicateLayer) setReprList(newsel); @@ -640,8 +638,7 @@ void sp_edit_clear_all(Inkscape::Selection *selection) item->deleteObject(); } - DocumentUndo::done(doc, SP_VERB_EDIT_CLEAR_ALL, - _("Delete all")); + DocumentUndo::done(doc, _("Delete all"), nullptr); } /* @@ -830,8 +827,7 @@ Inkscape::XML::Node* ObjectSet::group() { topmost_parent->addChildAtPos(group, topmost + 1); set(doc->getObjectByRepr(group)); - DocumentUndo::done(doc, SP_VERB_SELECTION_GROUP, - C_("Verb", "Group")); + DocumentUndo::done(doc, _("Group"), INKSCAPE_ICON("object-group")); return group; } @@ -864,8 +860,7 @@ void ObjectSet::popFromGroup(){ toLayer(*grandparents.begin(), true); if(document()) - DocumentUndo::done(document(), SP_VERB_SELECTION_UNGROUP_POP_SELECTION, - _("Pop selection from group")); + DocumentUndo::done(document(), _("Pop selection from group"), INKSCAPE_ICON("object-ungroup-pop-selection")); } @@ -943,8 +938,7 @@ void ObjectSet::ungroup(bool skip_undo) ungroup_impl(this); if(document() && !skip_undo) - DocumentUndo::done(document(), SP_VERB_SELECTION_UNGROUP, - _("Ungroup")); + DocumentUndo::done(document(), _("Ungroup"), INKSCAPE_ICON("object-ungroup")); } // TODO replace it with ObjectSet::degroup_list @@ -1067,15 +1061,14 @@ void ObjectSet::raise(bool skip_undo){ } } } - if(document() && !skip_undo) - DocumentUndo::done(document(), SP_VERB_SELECTION_RAISE, - //TRANSLATORS: "Raise" means "to raise an object" in the undo history - C_("Undo action", "Raise")); + if (document() && !skip_undo) { + DocumentUndo::done(document(), C_("Undo action", "Raise"), INKSCAPE_ICON("selection-raise")); + } } void ObjectSet::raiseToTop(bool skip_undo) { - if(isEmpty()){ + if (isEmpty()) { selection_display_message(desktop(), Inkscape::WARNING_MESSAGE, _("Select object(s) to raise.")); return; } @@ -1094,8 +1087,7 @@ void ObjectSet::raiseToTop(bool skip_undo) { repr->setPosition(-1); } if (document() && !skip_undo) { - DocumentUndo::done(document(), SP_VERB_SELECTION_TO_FRONT, - _("Raise to top")); + DocumentUndo::done(document(), _("Raise to top"), INKSCAPE_ICON("selection-top")); } } @@ -1148,9 +1140,7 @@ void ObjectSet::lower(bool skip_undo){ } } if(document() && !skip_undo) - DocumentUndo::done(document(), SP_VERB_SELECTION_LOWER, - //TRANSLATORS: "Lower" means "to lower an object" in the undo history - C_("Undo action", "Lower")); + DocumentUndo::done(document(), C_("Undo action", "Lower"), INKSCAPE_ICON("selection-lower")); } @@ -1187,8 +1177,7 @@ void ObjectSet::lowerToBottom(bool skip_undo){ repr->setPosition(minpos); } if (document() && !skip_undo) { - DocumentUndo::done(document(), SP_VERB_SELECTION_TO_BACK, - _("Lower to bottom")); + DocumentUndo::done(document(), _("Lower to bottom"), INKSCAPE_ICON("selection-bottom")); } } @@ -1211,9 +1200,7 @@ void ObjectSet::stackUp(bool skip_undo) { } if(document() && !skip_undo) - DocumentUndo::done(document(), SP_VERB_SELECTION_STACK_UP, - //TRANSLATORS: undo history: "stack up" means to raise an object of its ordinal position by 1 - C_("Undo action", "stack up")); + DocumentUndo::done(document(), C_("Undo action", "stack up"), INKSCAPE_ICON("layer-raise")); } void ObjectSet::stackDown(bool skip_undo) { @@ -1234,10 +1221,9 @@ void ObjectSet::stackDown(bool skip_undo) { } } - if(document() && !skip_undo) - DocumentUndo::done(document(), SP_VERB_SELECTION_STACK_DOWN, - //TRANSLATORS: undo history: "stack down" means to lower an object of its ordinal position by 1 - C_("Undo action", "stack down")); + if (document() && !skip_undo) { + DocumentUndo::done(document(), C_("Undo action", "stack down"), INKSCAPE_ICON("layer-lower")); + } } void @@ -1330,7 +1316,7 @@ void sp_selection_paste(SPDesktop *desktop, bool in_place) { Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); if (cm->paste(desktop, in_place)) { - DocumentUndo::done(desktop->getDocument(), SP_VERB_EDIT_PASTE, _("Paste")); + DocumentUndo::done(desktop->getDocument(), _("Paste"), INKSCAPE_ICON("edit-paste")); } } @@ -1338,7 +1324,7 @@ void ObjectSet::pasteStyle() { Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); if (cm->pasteStyle(this)) { - DocumentUndo::done(document(), SP_VERB_EDIT_PASTE_STYLE, _("Paste style")); + DocumentUndo::done(document(), _("Paste style"), INKSCAPE_ICON("edit-paste-style")); } } @@ -1346,8 +1332,7 @@ void ObjectSet::pastePathEffect() { Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); if (cm->pastePathEffect(this)) { - DocumentUndo::done(document(), SP_VERB_EDIT_PASTE_LIVEPATHEFFECT, - _("Paste live path effect")); + DocumentUndo::done(document(), _("Paste live path effect"), nullptr); } } @@ -1378,9 +1363,9 @@ void ObjectSet::removeLPE() } - if(document()) - DocumentUndo::done(document(), SP_VERB_EDIT_REMOVE_LIVEPATHEFFECT, - _("Remove live path effect")); + if (document()) { + DocumentUndo::done(document(), _("Remove live path effect"), nullptr); + } } void ObjectSet::removeFilter() @@ -1403,9 +1388,9 @@ void ObjectSet::removeFilter() // depends on desktop items. set_active_tool (d, get_active_tool(d)); } - if(document()) - DocumentUndo::done(document(), SP_VERB_EDIT_REMOVE_FILTER, - _("Remove filter")); + if (document()) { + DocumentUndo::done(document(), _("Remove filter"), nullptr); + } } @@ -1413,8 +1398,7 @@ void ObjectSet::pasteSize(bool apply_x, bool apply_y) { Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); if (cm->pasteSize(this, false, apply_x, apply_y)) { - DocumentUndo::done(document(), SP_VERB_EDIT_PASTE_SIZE, - _("Paste size")); + DocumentUndo::done(document(), _("Paste size"), INKSCAPE_ICON("edit-paste-size")); } } @@ -1422,8 +1406,7 @@ void ObjectSet::pasteSizeSeparately(bool apply_x, bool apply_y) { Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); if (cm->pasteSize(this, true, apply_x, apply_y)) { - DocumentUndo::done(document(), SP_VERB_EDIT_PASTE_SIZE_SEPARATELY, - _("Paste size separately")); + DocumentUndo::done(document(), _("Paste size separately"), INKSCAPE_ICON("edit-paste-size-separately")); } } @@ -1477,8 +1460,7 @@ void ObjectSet::toNextLayer(bool skip_undo) setReprList(copied); if (next) dt->setCurrentLayer(next); if ( !skip_undo ) { - DocumentUndo::done(dt->getDocument(), SP_VERB_LAYER_MOVE_TO_NEXT, - _("Raise to next layer")); + DocumentUndo::done(dt->getDocument(), _("Raise to next layer"), INKSCAPE_ICON("selection-move-to-layer-above")); } } else { no_more = true; @@ -1523,8 +1505,7 @@ void ObjectSet::toPrevLayer(bool skip_undo) setReprList( copied); if (next) dt->setCurrentLayer(next); if ( !skip_undo ) { - DocumentUndo::done(dt->getDocument(), SP_VERB_LAYER_MOVE_TO_PREV, - _("Lower to previous layer")); + DocumentUndo::done(dt->getDocument(), _("Lower to previous layer"), INKSCAPE_ICON("selection-move-to-layer-below")); } } else { no_more = true; @@ -1560,9 +1541,8 @@ void ObjectSet::toLayer(SPObject *moveto, bool skip_undo) setReprList(copied); if (!temp_clip.empty()) temp_clip.clear(); if (moveto && dt) dt->setCurrentLayer(moveto); - if ( !skip_undo ) { - DocumentUndo::done(document(), SP_VERB_LAYER_MOVE_TO, - _("Move selection to layer")); + if (!skip_undo) { + DocumentUndo::done(document(), _("Move selection to layer"), INKSCAPE_ICON("selection-move-to-layer")); } } } @@ -1816,9 +1796,9 @@ void ObjectSet::removeTransform() (*l)->removeAttribute("transform"); } - if(document()) - DocumentUndo::done(document(), SP_VERB_OBJECT_FLATTEN, - _("Remove transform")); + if (document()) { + DocumentUndo::done(document(), _("Remove transform"), nullptr); + } } void ObjectSet::setScaleAbsolute(double x0, double x1,double y0, double y1) @@ -1916,10 +1896,11 @@ void ObjectSet::rotate90(bool ccw) } } - if (document()) + if (document()) { DocumentUndo::done(document(), - ccw ? SP_VERB_OBJECT_ROTATE_90_CCW : SP_VERB_OBJECT_ROTATE_90_CW, - ccw ? _("Rotate 90\xc2\xb0 CCW") : _("Rotate 90\xc2\xb0 CW")); + ccw ? _("Rotate 90\xc2\xb0 CCW") : _("Rotate 90\xc2\xb0 CW"), + ccw ? INKSCAPE_ICON("object-rotate-left") : INKSCAPE_ICON("object-rotate-right")); + } } void ObjectSet::rotate(gdouble const angle_degrees) @@ -1933,13 +1914,11 @@ void ObjectSet::rotate(gdouble const angle_degrees) } rotateRelative(*center_, angle_degrees); - if (document()) + if (document()) { DocumentUndo::maybeDone(document(), - ( ( angle_degrees > 0 ) - ? "selector:rotate:ccw" - : "selector:rotate:cw" ), - SP_VERB_CONTEXT_SELECT, - _("Rotate")); + ( ( angle_degrees > 0 )? "selector:rotate:ccw": "selector:rotate:cw" ), + _("Rotate"), INKSCAPE_ICON("tool-pointer")); + } } /* @@ -2296,11 +2275,8 @@ void ObjectSet::rotateScreen(double angle) rotateRelative(*center_, zangle); DocumentUndo::maybeDone(document(), - ( (angle > 0) - ? "selector:rotate:ccw" - : "selector:rotate:cw" ), - SP_VERB_CONTEXT_SELECT, - _("Rotate by pixels")); + ( (angle > 0) ? "selector:rotate:ccw": "selector:rotate:cw" ), + _("Rotate by pixels"), INKSCAPE_ICON("tool-pointer")); } void ObjectSet::scale(double grow) @@ -2326,11 +2302,8 @@ void ObjectSet::scale(double grow) if (document()) { DocumentUndo::maybeDone(document(), - ( (grow > 0) - ? "selector:scale:larger" - : "selector:scale:smaller" ), - SP_VERB_CONTEXT_SELECT, - _("Scale")); + ( (grow > 0) ? "selector:scale:larger" : "selector:scale:smaller" ), + _("Scale"), INKSCAPE_ICON("tool-pointer")); } } @@ -2354,8 +2327,7 @@ void ObjectSet::scaleTimes(double times) Geom::Point const center_(sel_bbox->midpoint()); setScaleRelative(center_, Geom::Scale(times, times)); - DocumentUndo::done(document(), SP_VERB_CONTEXT_SELECT, - _("Scale by whole factor")); + DocumentUndo::done(document(), _("Scale by whole factor"), INKSCAPE_ICON("tool-pointer")); } void ObjectSet::move(double dx, double dy) @@ -2368,14 +2340,11 @@ void ObjectSet::move(double dx, double dy) if (document()) { if (dx == 0) { - DocumentUndo::maybeDone(document(), "selector:move:vertical", SP_VERB_CONTEXT_SELECT, - _("Move vertically")); + DocumentUndo::maybeDone(document(), "selector:move:vertical", _("Move vertically"), INKSCAPE_ICON("tool-pointer")); } else if (dy == 0) { - DocumentUndo::maybeDone(document(), "selector:move:horizontal", SP_VERB_CONTEXT_SELECT, - _("Move horizontally")); + DocumentUndo::maybeDone(document(), "selector:move:horizontal", _("Move horizontally"), INKSCAPE_ICON("tool-pointer")); } else { - DocumentUndo::done(document(), SP_VERB_CONTEXT_SELECT, - _("Move")); + DocumentUndo::done(document(), _("Move"), INKSCAPE_ICON("tool-pointer")); } } } @@ -2394,14 +2363,11 @@ void ObjectSet::moveScreen(double dx, double dy) SPDocument *doc = document(); if (dx == 0) { - DocumentUndo::maybeDone(doc, "selector:move:vertical", SP_VERB_CONTEXT_SELECT, - _("Move vertically by pixels")); + DocumentUndo::maybeDone(doc, "selector:move:vertical", _("Move vertically by pixels"), INKSCAPE_ICON("tool-pointer")); } else if (dy == 0) { - DocumentUndo::maybeDone(doc, "selector:move:horizontal", SP_VERB_CONTEXT_SELECT, - _("Move horizontally by pixels")); + DocumentUndo::maybeDone(doc, "selector:move:horizontal", _("Move horizontally by pixels"), INKSCAPE_ICON("tool-pointer")); } else { - DocumentUndo::done(doc, SP_VERB_CONTEXT_SELECT, - _("Move")); + DocumentUndo::done(doc, _("Move"), INKSCAPE_ICON("tool-pointer")); } } @@ -2699,8 +2665,7 @@ void ObjectSet::clone() Inkscape::GC::release(clone); } - DocumentUndo::done(document(), SP_VERB_EDIT_CLONE, - C_("Action", "Clone")); + DocumentUndo::done(document(), C_("Action", "Clone"), INKSCAPE_ICON("edit-clone")); setReprList(newsel); } @@ -2739,8 +2704,7 @@ void ObjectSet::relink() if(desktop()) desktop()->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("No clones to relink in the selection.")); } else { - DocumentUndo::done(document(), SP_VERB_EDIT_UNLINK_CLONE, - _("Relink clone")); + DocumentUndo::done(document(), _("Relink clone"), INKSCAPE_ICON("edit-clone-unlink")); } } @@ -2830,8 +2794,7 @@ bool ObjectSet::unlink(const bool skip_undo) } if (!skip_undo) { - DocumentUndo::done(document(), SP_VERB_EDIT_UNLINK_CLONE, - _("Unlink clone")); + DocumentUndo::done(document(), _("Unlink clone"), INKSCAPE_ICON("edit-clone-unlink")); } return unlinked; } @@ -2868,8 +2831,7 @@ bool ObjectSet::unlinkRecursive(const bool skip_undo, const bool force) { desktop()->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("No clones to unlink in the selection.")); } if (!skip_undo) { - DocumentUndo::done(document(), SP_VERB_EDIT_UNLINK_CLONE_RECURSIVE, - _("Unlink clone recursively")); + DocumentUndo::done(document(), _("Unlink clone recursively"), INKSCAPE_ICON("edit-clone-unlink")); } setList(items_); return unlinked; @@ -3061,9 +3023,9 @@ void ObjectSet::cloneOriginalPathLPE(bool allow_transforms) clone_lpeitem->addPathEffect(lpe_id_href, false); } if (multiple) { - DocumentUndo::done(document(), SP_VERB_EDIT_CLONE_ORIGINAL_PATH_LPE, _("Fill between many")); + DocumentUndo::done(document(), _("Fill between many"), INKSCAPE_ICON("edit-clone-link-lpe")); } else { - DocumentUndo::done(document(), SP_VERB_EDIT_CLONE_ORIGINAL_PATH_LPE, _("Clone original")); + DocumentUndo::done(document(), _("Clone original"), INKSCAPE_ICON("edit-clone-link-lpe")); } } } else { @@ -3146,8 +3108,7 @@ void ObjectSet::toMarker(bool apply) - DocumentUndo::done(doc, SP_VERB_EDIT_SELECTION_2_MARKER, - _("Objects to marker")); + DocumentUndo::done(doc, _("Objects to marker"), nullptr); } static void sp_selection_to_guides_recursive(SPItem *item, bool wholegroups) { @@ -3191,7 +3152,7 @@ void ObjectSet::toGuides() sp_selection_delete_impl(items_); } - DocumentUndo::done(doc, SP_VERB_EDIT_SELECTION_2_GUIDES, _("Objects to guides")); + DocumentUndo::done(doc, _("Objects to guides"), nullptr); } /* @@ -3357,7 +3318,7 @@ void ObjectSet::toSymbol() // Clean up Inkscape::GC::release(symbol_repr); - DocumentUndo::done(doc, SP_VERB_EDIT_SYMBOL, _("Group to symbol")); + DocumentUndo::done(doc, _("Group to symbol"), nullptr); } /* @@ -3374,7 +3335,7 @@ void ObjectSet::unSymbol() } } } - DocumentUndo::done(document(), SP_VERB_EDIT_UNSYMBOL, _("unSymbol all selected symbols")); + DocumentUndo::done(document(), _("unSymbol all selected symbols"), nullptr); } void ObjectSet::tile(bool apply) @@ -3474,8 +3435,7 @@ void ObjectSet::tile(bool apply) } - DocumentUndo::done(doc, SP_VERB_EDIT_TILE, - _("Objects to pattern")); + DocumentUndo::done(doc, _("Objects to pattern"), nullptr); } void ObjectSet::untile() @@ -3549,8 +3509,7 @@ void ObjectSet::untile() if(desktop()) desktop()->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("No pattern fills in the selection.")); } else { - DocumentUndo::done(document(), SP_VERB_EDIT_UNTILE, - _("Pattern to objects")); + DocumentUndo::done(document(), _("Pattern to objects"), nullptr); setList(new_select); } } @@ -3724,8 +3683,7 @@ void ObjectSet::createBitmapCopy() delete pb; // Complete undoable transaction - DocumentUndo::done(doc, SP_VERB_SELECTION_CREATE_BITMAP, - _("Create bitmap")); + DocumentUndo::done(doc, _("Create bitmap"), INKSCAPE_ICON("selection-make-bitmap-copy")); } if(desktop()) { @@ -3829,7 +3787,7 @@ void ObjectSet::setClipGroup() Inkscape::GC::release(clone); set(outer); - DocumentUndo::done(doc, SP_VERB_OBJECT_SET_CLIPPATH, _("Create Clip Group")); + DocumentUndo::done(doc, _("Create Clip Group"), nullptr); } /** @@ -4024,9 +3982,9 @@ void ObjectSet::setClipGroup() addList(items_to_select); if (!skip_undo) { if (apply_clip_path) { - DocumentUndo::done(doc, SP_VERB_OBJECT_SET_CLIPPATH, _("Set clipping path")); + DocumentUndo::done(doc, _("Set clipping path"), nullptr); } else { - DocumentUndo::done(doc, SP_VERB_OBJECT_SET_MASK, _("Set mask")); + DocumentUndo::done(doc, _("Set mask"), nullptr); } } } @@ -4157,9 +4115,9 @@ void ObjectSet::unsetMask(const bool apply_clip_path, const bool skip_undo) { addList(items_to_select); if (!skip_undo) { if (apply_clip_path) { - DocumentUndo::done(doc, SP_VERB_OBJECT_UNSET_CLIPPATH, _("Release clipping path")); + DocumentUndo::done(doc, _("Release clipping path"), nullptr); } else { - DocumentUndo::done(doc, SP_VERB_OBJECT_UNSET_MASK, _("Release mask")); + DocumentUndo::done(doc, _("Release mask"), nullptr); } } } @@ -4182,8 +4140,7 @@ bool ObjectSet::fitCanvas(bool with_margins, bool skip_undo) if (bbox) { document()->fitToRect(*bbox, with_margins); if(!skip_undo) - DocumentUndo::done(document(), SP_VERB_FIT_CANVAS_TO_SELECTION, - _("Fit Page to Selection")); + DocumentUndo::done(document(), _("Fit Page to Selection"), nullptr); return true; } else { return false; @@ -4259,8 +4216,7 @@ void ObjectSet::swapFillStroke() sp_repr_css_attr_unref (css); } - DocumentUndo::done(document(), SP_VERB_EDIT_SWAP_FILL_STROKE, - _("Swap fill and stroke of an object")); + DocumentUndo::done(document(), _("Swap fill and stroke of an object"), nullptr); } /** @@ -4334,7 +4290,7 @@ void ObjectSet::fillBetweenMany() clear(); add(fillRepr); - DocumentUndo::done(doc, SP_VERB_SELECTION_FILL_BETWEEN_MANY, _("Create linked fill object between paths")); + DocumentUndo::done(doc, _("Create linked fill object between paths"), nullptr); } /** @@ -4358,11 +4314,10 @@ fit_canvas_to_drawing(SPDocument *doc, bool with_margins) } void -verb_fit_canvas_to_drawing(SPDesktop *desktop) +fit_canvas_to_drawing(SPDesktop *desktop) { if (fit_canvas_to_drawing(desktop->getDocument())) { - DocumentUndo::done(desktop->getDocument(), SP_VERB_FIT_CANVAS_TO_DRAWING, - _("Fit Page to Drawing")); + DocumentUndo::done(desktop->getDocument(), _("Fit Page to Drawing"), nullptr); } } @@ -4382,8 +4337,7 @@ void fit_canvas_to_selection_or_drawing(SPDesktop *desktop) { ? fit_canvas_to_drawing(doc, true) : desktop->selection->fitCanvas(true,true)); if (changed) { - DocumentUndo::done(desktop->getDocument(), SP_VERB_FIT_CANVAS_TO_SELECTION_OR_DRAWING, - _("Fit Page to Selection or Drawing")); + DocumentUndo::done(desktop->getDocument(), _("Fit Page to Selection or Drawing"), nullptr); } }; -- GitLab From bd86e4e4a90c835124fe1ebed6c2bf7de621631b Mon Sep 17 00:00:00 2001 From: "sushant.co19" Date: Sun, 20 Jun 2021 17:25:02 +0530 Subject: [PATCH 046/235] Removing Verb Dependencies in path offset --- share/keys/inkscape.xml | 2 +- share/ui/menu-path.ui | 3 ++- src/actions/actions-doc-file.cpp | 28 +++++++++++++++++++---- src/actions/actions-selection-desktop.cpp | 13 ----------- src/object/sp-guide.cpp | 5 ++-- src/path-chemistry.cpp | 14 ++++-------- src/path/path-offset.cpp | 24 ++++++++++--------- src/selection-chemistry.h | 2 +- src/ui/desktop/menubar.cpp | 4 ++++ src/ui/tool/multi-path-manipulator.cpp | 8 +++---- src/verbs.cpp | 2 +- 11 files changed, 56 insertions(+), 49 deletions(-) diff --git a/share/keys/inkscape.xml b/share/keys/inkscape.xml index f436ee5890..cc30baa72a 100644 --- a/share/keys/inkscape.xml +++ b/share/keys/inkscape.xml @@ -498,6 +498,7 @@ override) the bindings in the main default.xml. + @@ -549,7 +550,6 @@ override) the bindings in the main default.xml. - diff --git a/share/ui/menu-path.ui b/share/ui/menu-path.ui index ca8c8f3664..94ab099886 100644 --- a/share/ui/menu-path.ui +++ b/share/ui/menu-path.ui @@ -16,7 +16,8 @@ _Trace Bitmap... - win.object-bitmap-trace + win.dialog-open + Trace bitmap-trace diff --git a/src/actions/actions-doc-file.cpp b/src/actions/actions-doc-file.cpp index 98d2bb39a3..523152c2e0 100644 --- a/src/actions/actions-doc-file.cpp +++ b/src/actions/actions-doc-file.cpp @@ -22,6 +22,9 @@ #include "inkscape.h" #include "object/sp-namedview.h" +#include "file.h" +#include "ui/dialog/new-from-template.h" + void window_previous(SPDocument* document) { @@ -34,21 +37,36 @@ window_next(SPDocument* document) INKSCAPE.switch_desktops_next(); } +void +document_new(SPDocument* document) +{ + sp_file_new_default(); +} + +void +document_dialog_templates(SPDocument* document) +{ + Inkscape::UI::NewFromTemplate::load_new_from_template(); +} + std::vector> raw_data_doc_file = { - {"doc.window-previous", N_("P_revious Window"), "File", N_("Switch to the previous document window")}, - {"doc.window-next", N_("N_ext Window"), "File", N_("Switch to the next document window")} + {"doc.window-previous", N_("P_revious Window"), "File", N_("Switch to the previous document window")}, + {"doc.window-next", N_("N_ext Window"), "File", N_("Switch to the next document window")}, + {"doc.document-new", N_("_New"), "File", N_("Create new document from the default template")}, + {"doc.document-dialog-templates", N_("New from _Template..."), "File", N_("Create new project from template")} }; void add_actions_doc_file(SPDocument* document) { - std::cout<<"add_actions_doc_file\n"; Glib::RefPtr map = document->getActionGroup(); - map->add_action( "window-previous", sigc::bind(sigc::ptr_fun(&window_previous), document)); - map->add_action( "window-next", sigc::bind(sigc::ptr_fun(&window_next), document)); + map->add_action( "window-previous", sigc::bind(sigc::ptr_fun(&window_previous), document)); + map->add_action( "window-next", sigc::bind(sigc::ptr_fun(&window_next), document)); + map->add_action( "document-new", sigc::bind(sigc::ptr_fun(&document_new), document)); + map->add_action( "document-dialog-templates", sigc::bind(sigc::ptr_fun(&document_dialog_templates), document)); // Check if there is already an application instance (GUI or non-GUI). auto app = InkscapeApplication::instance(); diff --git a/src/actions/actions-selection-desktop.cpp b/src/actions/actions-selection-desktop.cpp index ed72f786b2..0dce9135e7 100644 --- a/src/actions/actions-selection-desktop.cpp +++ b/src/actions/actions-selection-desktop.cpp @@ -32,16 +32,6 @@ selection_make_bitmap_copy(InkscapeWindow* win) dt->selection->createBitmapCopy(); } -void -object_bitmap_trace(InkscapeWindow* win) -{ - SPDesktop* dt = win->get_desktop(); - - // Trace Bitmap - Inkscape::UI::Dialog::DialogContainer *container = dt->getContainer(); - container->new_dialog("Trace"); -} - void select_path_inset(InkscapeWindow* win) { @@ -101,13 +91,11 @@ std::vector> raw_selection_dekstop_data = { // clang-format off {"win.selection-make-bitmap-copy", N_("Make a Bitmap Copy"), "Selection Desktop", N_("Export selection to a bitmap and insert it into document")}, - {"win.object-bitmap-trace", N_("Trace Bitmap..."), "Selection Desktop", N_("Create one or more paths from a bitmap by tracing it")}, {"win.select-path-inset", N_("Inset"), "Selection Desktop", N_("Inset selected paths")}, {"win.select-path-outset", N_("Outset"), "Selection Desktop", N_("Outset selected paths")}, {"win.select-path-offset-dynamic", N_("Dynamic Offset"), "Selection Desktop", N_("Create a dynamic offset object")}, {"win.select-path-offset-linked", N_("Linked Offset"), "Selection Desktop", N_("Create a dynamic offset object linked to the original path")}, {"win.select-path-reverse", N_("Reverse"), "Selection Desktop", N_("Reverse the direction of selected paths (useful for flipping markers)")} - // clang-format on }; @@ -116,7 +104,6 @@ add_actions_select_desktop(InkscapeWindow* win) { // clang-format off win->add_action( "selection-make-bitmap-copy", sigc::bind(sigc::ptr_fun(&selection_make_bitmap_copy), win)); - win->add_action( "object-bitmap-trace", sigc::bind(sigc::ptr_fun(&object_bitmap_trace), win)); win->add_action( "select-path-inset", sigc::bind(sigc::ptr_fun(&select_path_inset),win)); win->add_action( "select-path-outset", sigc::bind(sigc::ptr_fun(&select_path_outset),win)); win->add_action( "select-path-offset-dynamic", sigc::bind(sigc::ptr_fun(&select_path_offset_dynamic),win)); diff --git a/src/object/sp-guide.cpp b/src/object/sp-guide.cpp index 1d1571f493..3319d3146f 100644 --- a/src/object/sp-guide.cpp +++ b/src/object/sp-guide.cpp @@ -26,7 +26,6 @@ #include "document-undo.h" #include "helper-fns.h" #include "inkscape.h" -#include "verbs.h" #include "sp-guide.h" #include "sp-namedview.h" @@ -285,7 +284,7 @@ void sp_guide_create_guides_around_page(SPDesktop *dt) sp_guide_pt_pairs_to_guides(doc, pts); - DocumentUndo::done(doc, SP_VERB_NONE, _("Create Guides Around the Page")); + DocumentUndo::done(doc, _("Create Guides Around the Page"), nullptr); } void sp_guide_delete_all_guides(SPDesktop *dt) @@ -298,7 +297,7 @@ void sp_guide_delete_all_guides(SPDesktop *dt) current = doc->getResourceList("guide"); } - DocumentUndo::done(doc, SP_VERB_NONE, _("Delete All Guides")); + DocumentUndo::done(doc, _("Delete All Guides"), nullptr); } // Actually, create a new guide. diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp index 5f746a1518..eecd81b860 100644 --- a/src/path-chemistry.cpp +++ b/src/path-chemistry.cpp @@ -29,7 +29,6 @@ #include "selection-chemistry.h" #include "selection.h" #include "text-editing.h" -#include "verbs.h" #include "display/curve.h" @@ -41,6 +40,7 @@ #include "style.h" #include "ui/widget/canvas.h" // Disable drawing during ops +#include "ui/icon-names.h" #include "svg/svg.h" @@ -173,8 +173,7 @@ ObjectSet::combine(bool skip_undo) parent->addChildAtPos(repr, position > 0 ? position : 0); if ( !skip_undo ) { - DocumentUndo::done(doc, SP_VERB_SELECTION_COMBINE, - _("Combine")); + DocumentUndo::done(doc, _("Combine"), INKSCAPE_ICON("path-combine")); } set(repr); @@ -276,8 +275,7 @@ ObjectSet::breakApart(bool skip_undo) if (did) { if ( !skip_undo ) { - DocumentUndo::done(document(), SP_VERB_SELECTION_BREAK_APART, - _("Break apart")); + DocumentUndo::done(document(), _("Break apart"), INKSCAPE_ICON("path-break-apart")); } } else { if(desktop()) @@ -315,8 +313,7 @@ void ObjectSet::toCurves(bool skip_undo) desktop()->clearWaitingCursor(); } if (did&& !skip_undo) { - DocumentUndo::done(document(), SP_VERB_OBJECT_TO_CURVE, - _("Object to path")); + DocumentUndo::done(document(), _("Object to path"), INKSCAPE_ICON("object-to-path")); } else { if(desktop()) desktop()->getMessageStack()->flash(Inkscape::ERROR_MESSAGE, _("No objects to convert to path in the selection.")); @@ -611,8 +608,7 @@ ObjectSet::pathReverse() desktop()->clearWaitingCursor(); if (did) { - DocumentUndo::done(document(), SP_VERB_SELECTION_REVERSE, - _("Reverse path")); + DocumentUndo::done(document(), _("Reverse path"), INKSCAPE_ICON("path-reverse")); } else { if(desktop()) desktop()->getMessageStack()->flash(Inkscape::ERROR_MESSAGE, _("No paths to reverse in the selection.")); diff --git a/src/path/path-offset.cpp b/src/path/path-offset.cpp index efca7a4682..533e08d2ed 100644 --- a/src/path/path-offset.cpp +++ b/src/path/path-offset.cpp @@ -35,6 +35,8 @@ #include "object/sp-path.h" #include "object/sp-text.h" +#include "ui/icon-names.h" + #include "style.h" #define MIN_OFFSET 0.01 @@ -183,11 +185,11 @@ void sp_selected_path_create_offset_object(SPDesktop *desktop, int expand, bool { // pas vraiment de points sur le resultat // donc il ne reste rien - DocumentUndo::done(desktop->getDocument(), - (updating ? SP_VERB_SELECTION_LINKED_OFFSET - : SP_VERB_SELECTION_DYNAMIC_OFFSET), + DocumentUndo::done(desktop->getDocument(), (updating ? _("Create linked offset") - : _("Create dynamic offset"))); + : _("Create dynamic offset")), + (updating ? INKSCAPE_ICON("path-offset-linked") + : INKSCAPE_ICON("path-offset-dynamic"))); selection->clear(); delete res; @@ -252,11 +254,11 @@ void sp_selected_path_create_offset_object(SPDesktop *desktop, int expand, bool selection->set(nitem); } - DocumentUndo::done(desktop->getDocument(), - (updating ? SP_VERB_SELECTION_LINKED_OFFSET - : SP_VERB_SELECTION_DYNAMIC_OFFSET), + DocumentUndo::done(desktop->getDocument(), (updating ? _("Create linked offset") - : _("Create dynamic offset"))); + : _("Create dynamic offset")), + (updating ? INKSCAPE_ICON("path-offset-linked") + : INKSCAPE_ICON("path-offset-dynamic"))); delete res; delete orig; @@ -452,9 +454,9 @@ sp_selected_path_do_offset(SPDesktop *desktop, bool expand, double prefOffset) } if (did) { - DocumentUndo::done(desktop->getDocument(), - (expand ? SP_VERB_SELECTION_OFFSET : SP_VERB_SELECTION_INSET), - (expand ? _("Outset path") : _("Inset path"))); + DocumentUndo::done(desktop->getDocument(), + (expand ? _("Outset path") : _("Inset path")), + (expand ? INKSCAPE_ICON("path-outset") : INKSCAPE_ICON("path-inset"))); } else { desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("No paths to inset/outset in the selection.")); return; diff --git a/src/selection-chemistry.h b/src/selection-chemistry.h index 771066b817..b4779db57a 100644 --- a/src/selection-chemistry.h +++ b/src/selection-chemistry.h @@ -102,7 +102,7 @@ void sp_redo (SPDesktop *desktop, SPDocument *doc); void sp_document_get_export_hints (SPDocument * doc, Glib::ustring &filename, float *xdpi, float *ydpi); bool fit_canvas_to_drawing(SPDocument *, bool with_margins = false); -void verb_fit_canvas_to_drawing(SPDesktop *); +void fit_canvas_to_drawing(SPDesktop *); void fit_canvas_to_selection_or_drawing(SPDesktop *); void unlock_all(SPDesktop *dt); diff --git a/src/ui/desktop/menubar.cpp b/src/ui/desktop/menubar.cpp index 4502ff343e..290ffc08ca 100644 --- a/src/ui/desktop/menubar.cpp +++ b/src/ui/desktop/menubar.cpp @@ -415,6 +415,10 @@ build_menu(Gtk::MenuShell* menu, Inkscape::XML::Node* xml, Inkscape::UI::View::V filename = Inkscape::IO::Resource::get_filename(Inkscape::IO::Resource::UIS, "menu-path.ui"); menuname = "path-menu"; } + else if (strcmp(name, "_File") == 0) { + filename = Inkscape::IO::Resource::get_filename(Inkscape::IO::Resource::UIS, "menu-file.ui"); + menuname = "file-menu"; + } if(filename!=""){ try diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp index 06f6de4fa1..9f262ef051 100644 --- a/src/ui/tool/multi-path-manipulator.cpp +++ b/src/ui/tool/multi-path-manipulator.cpp @@ -21,12 +21,12 @@ #include "document-undo.h" #include "message-stack.h" #include "node.h" -#include "verbs.h" #include "live_effects/lpeobject.h" #include "object/sp-path.h" +#include "ui/icon-names.h" #include "ui/tool/control-point-selection.h" #include "ui/tool/event-utils.h" #include "ui/tool/multi-path-manipulator.h" @@ -844,9 +844,9 @@ void MultiPathManipulator::_commit(CommitEvent cps) _selection.signal_update.emit(); invokeForAll(&PathManipulator::writeXML); if (key) { - DocumentUndo::maybeDone(_desktop->getDocument(), key, SP_VERB_CONTEXT_NODE, reason); + DocumentUndo::maybeDone(_desktop->getDocument(), key, reason, INKSCAPE_ICON("tool-node-editor")); } else { - DocumentUndo::done(_desktop->getDocument(), SP_VERB_CONTEXT_NODE, reason); + DocumentUndo::done(_desktop->getDocument(), reason, INKSCAPE_ICON("tool-node-editor")); } signal_coords_changed.emit(); } @@ -855,7 +855,7 @@ void MultiPathManipulator::_commit(CommitEvent cps) void MultiPathManipulator::_done(gchar const *reason, bool alert_LPE) { invokeForAll(&PathManipulator::update, alert_LPE); invokeForAll(&PathManipulator::writeXML); - DocumentUndo::done(_desktop->getDocument(), SP_VERB_CONTEXT_NODE, reason); + DocumentUndo::done(_desktop->getDocument(), reason, INKSCAPE_ICON("tool-node-editor")); signal_coords_changed.emit(); } diff --git a/src/verbs.cpp b/src/verbs.cpp index 07ae271d72..1a398d3bda 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -2163,7 +2163,7 @@ void FitCanvasVerb::perform(SPAction *action, void *data) dt->selection->fitCanvas(true); break; case SP_VERB_FIT_CANVAS_TO_DRAWING: - verb_fit_canvas_to_drawing(dt); + fit_canvas_to_drawing(dt); break; case SP_VERB_FIT_CANVAS_TO_SELECTION_OR_DRAWING: fit_canvas_to_selection_or_drawing(dt); -- GitLab From c301930b544019f9b767de37c58f016e7157d3d1 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 21 Jun 2021 18:03:30 +0200 Subject: [PATCH 047/235] Crated a menu item for the current functionality. Also called it "Fracture". --- .../Tango/scalable/actions/path-fracture.svg | 89 +++++++++++++++++++ .../scalable/actions/path-fracture.svg | 89 +++++++++++++++++++ .../symbolic/actions/path-fracture.svg | 89 +++++++++++++++++++ .../symbolic/actions/path-fracture.svg | 89 +++++++++++++++++++ share/keys/inkscape.xml | 3 +- share/ui/menu-path.ui | 5 ++ src/actions/actions-selection.cpp | 11 +++ 7 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 share/icons/Tango/scalable/actions/path-fracture.svg create mode 100644 share/icons/hicolor/scalable/actions/path-fracture.svg create mode 100644 share/icons/hicolor/symbolic/actions/path-fracture.svg create mode 100644 share/icons/multicolor/symbolic/actions/path-fracture.svg diff --git a/share/icons/Tango/scalable/actions/path-fracture.svg b/share/icons/Tango/scalable/actions/path-fracture.svg new file mode 100644 index 0000000000..f9b572987f --- /dev/null +++ b/share/icons/Tango/scalable/actions/path-fracture.svg @@ -0,0 +1,89 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/scalable/actions/path-fracture.svg b/share/icons/hicolor/scalable/actions/path-fracture.svg new file mode 100644 index 0000000000..f9b572987f --- /dev/null +++ b/share/icons/hicolor/scalable/actions/path-fracture.svg @@ -0,0 +1,89 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/symbolic/actions/path-fracture.svg b/share/icons/hicolor/symbolic/actions/path-fracture.svg new file mode 100644 index 0000000000..f9b572987f --- /dev/null +++ b/share/icons/hicolor/symbolic/actions/path-fracture.svg @@ -0,0 +1,89 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/share/icons/multicolor/symbolic/actions/path-fracture.svg b/share/icons/multicolor/symbolic/actions/path-fracture.svg new file mode 100644 index 0000000000..f9b572987f --- /dev/null +++ b/share/icons/multicolor/symbolic/actions/path-fracture.svg @@ -0,0 +1,89 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/share/keys/inkscape.xml b/share/keys/inkscape.xml index cc30baa72a..26e5014197 100644 --- a/share/keys/inkscape.xml +++ b/share/keys/inkscape.xml @@ -556,6 +556,7 @@ override) the bindings in the main default.xml. + @@ -565,4 +566,4 @@ override) the bindings in the main default.xml. - \ No newline at end of file + diff --git a/share/ui/menu-path.ui b/share/ui/menu-path.ui index 94ab099886..c0bbe36d40 100644 --- a/share/ui/menu-path.ui +++ b/share/ui/menu-path.ui @@ -53,6 +53,11 @@ app.select-path-cut path-cut + + _Fracture + app.select-path-fracture + path-fracture +
diff --git a/src/actions/actions-selection.cpp b/src/actions/actions-selection.cpp index 0e32dfa707..e134107905 100644 --- a/src/actions/actions-selection.cpp +++ b/src/actions/actions-selection.cpp @@ -287,6 +287,15 @@ select_path_cut(InkscapeApplication *app) selection->pathSlice(); } +void +select_path_fracture(InkscapeApplication *app) +{ + auto selection = app->get_active_selection(); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); + selection->break_into_non_overlapping_pieces(); +} + void select_path_combine(InkscapeApplication *app) { @@ -337,6 +346,7 @@ std::vector> raw_data_selection = {"app.select-path-exclusion", N_("Exclusion"), "Select", N_("Create exclusive OR of selected paths (those parts that belong to only one path)")}, {"app.select-path-division", N_("Division"), "Select", N_("Cut the bottom path into pieces")}, {"app.select-path-cut", N_("Cut Path"), "Select", N_("Cut the bottom path's stroke into pieces, removing fill")}, + {"app.select-path-fracture", N_("Fracture"), "Select", N_("Break the selected paths into non-overlapping (fractured) paths")}, {"app.select-path-combine", N_("Combine"), "Select", N_("Combine several paths into one")}, {"app.select-path-break-apart", N_("Break Apart"), "Select", N_("Break selected paths into subpaths")}, {"app.select-fill-between-paths", N_("Fill between paths"), "Select", N_("Create a fill object using the selected paths")}, @@ -367,6 +377,7 @@ add_actions_selection(InkscapeApplication* app) gapp->add_action( "select-path-exclusion", sigc::bind(sigc::ptr_fun(&select_path_exclusion), app)); gapp->add_action( "select-path-division", sigc::bind(sigc::ptr_fun(&select_path_division), app)); gapp->add_action( "select-path-cut", sigc::bind(sigc::ptr_fun(&select_path_cut), app)); + gapp->add_action( "select-path-fracture", sigc::bind(sigc::ptr_fun(&select_path_fracture), app)); gapp->add_action( "select-path-combine", sigc::bind(sigc::ptr_fun(&select_path_combine), app)); gapp->add_action( "select-path-break-apart", sigc::bind(sigc::ptr_fun(&select_path_break_apart), app)); gapp->add_action( "select-fill-between-paths", sigc::bind(sigc::ptr_fun(&fill_between_paths), app)); -- GitLab From c70669587fb0a5d13283a2d6fc13198439bbc338 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 21 Jun 2021 20:43:16 +0200 Subject: [PATCH 048/235] Created a path operation called "Split Non-intersecting Paths". This is very bad, lots of copying and duplicated code... needs refactoring. --- .../actions/path-split-non-intersecting.svg | 135 ++++++++++++++++++ .../actions/path-split-non-intersecting.svg | 135 ++++++++++++++++++ .../actions/path-split-non-intersecting.svg | 135 ++++++++++++++++++ .../actions/path-split-non-intersecting.svg | 135 ++++++++++++++++++ share/keys/inkscape.xml | 1 + share/ui/menu-path.ui | 5 + src/actions/actions-selection.cpp | 9 ++ src/helper/NonOverlappingPathsBuilder.cpp | 4 +- src/object/object-set.cpp | 82 +++++++++++ src/object/object-set.h | 1 + 10 files changed, 640 insertions(+), 2 deletions(-) create mode 100644 share/icons/Tango/scalable/actions/path-split-non-intersecting.svg create mode 100644 share/icons/hicolor/scalable/actions/path-split-non-intersecting.svg create mode 100644 share/icons/hicolor/symbolic/actions/path-split-non-intersecting.svg create mode 100644 share/icons/multicolor/symbolic/actions/path-split-non-intersecting.svg diff --git a/share/icons/Tango/scalable/actions/path-split-non-intersecting.svg b/share/icons/Tango/scalable/actions/path-split-non-intersecting.svg new file mode 100644 index 0000000000..51de27569f --- /dev/null +++ b/share/icons/Tango/scalable/actions/path-split-non-intersecting.svg @@ -0,0 +1,135 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/scalable/actions/path-split-non-intersecting.svg b/share/icons/hicolor/scalable/actions/path-split-non-intersecting.svg new file mode 100644 index 0000000000..51de27569f --- /dev/null +++ b/share/icons/hicolor/scalable/actions/path-split-non-intersecting.svg @@ -0,0 +1,135 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/symbolic/actions/path-split-non-intersecting.svg b/share/icons/hicolor/symbolic/actions/path-split-non-intersecting.svg new file mode 100644 index 0000000000..51de27569f --- /dev/null +++ b/share/icons/hicolor/symbolic/actions/path-split-non-intersecting.svg @@ -0,0 +1,135 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/multicolor/symbolic/actions/path-split-non-intersecting.svg b/share/icons/multicolor/symbolic/actions/path-split-non-intersecting.svg new file mode 100644 index 0000000000..51de27569f --- /dev/null +++ b/share/icons/multicolor/symbolic/actions/path-split-non-intersecting.svg @@ -0,0 +1,135 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/keys/inkscape.xml b/share/keys/inkscape.xml index 26e5014197..3640553cc9 100644 --- a/share/keys/inkscape.xml +++ b/share/keys/inkscape.xml @@ -559,6 +559,7 @@ override) the bindings in the main default.xml. + diff --git a/share/ui/menu-path.ui b/share/ui/menu-path.ui index c0bbe36d40..a75e157042 100644 --- a/share/ui/menu-path.ui +++ b/share/ui/menu-path.ui @@ -71,6 +71,11 @@ app.select-path-break-apart path-break-apart + + Split _Non-Intersecting Paths + app.select-path-split-non-intersecting + path-split-non-intersecting +
diff --git a/src/actions/actions-selection.cpp b/src/actions/actions-selection.cpp index e134107905..2937aa2205 100644 --- a/src/actions/actions-selection.cpp +++ b/src/actions/actions-selection.cpp @@ -311,6 +311,13 @@ select_path_break_apart(InkscapeApplication *app) selection->breakApart(); } +void +select_path_split_non_intersecting(InkscapeApplication *app) +{ + auto selection = app->get_active_selection(); + selection->splitNonIntersecting(); +} + void fill_between_paths(InkscapeApplication *app) { @@ -349,6 +356,7 @@ std::vector> raw_data_selection = {"app.select-path-fracture", N_("Fracture"), "Select", N_("Break the selected paths into non-overlapping (fractured) paths")}, {"app.select-path-combine", N_("Combine"), "Select", N_("Combine several paths into one")}, {"app.select-path-break-apart", N_("Break Apart"), "Select", N_("Break selected paths into subpaths")}, + {"app.select-path-split-non-intersecting", N_("Split Non-Intersecting paths"), "Select", N_("Split the combined path into separate non-intersecting paths")}, {"app.select-fill-between-paths", N_("Fill between paths"), "Select", N_("Create a fill object using the selected paths")}, {"app.select-path-simplify", N_("Simplify"), "Select", N_("Simplify selected paths (remove extra nodes)")} // clang-format on @@ -380,6 +388,7 @@ add_actions_selection(InkscapeApplication* app) gapp->add_action( "select-path-fracture", sigc::bind(sigc::ptr_fun(&select_path_fracture), app)); gapp->add_action( "select-path-combine", sigc::bind(sigc::ptr_fun(&select_path_combine), app)); gapp->add_action( "select-path-break-apart", sigc::bind(sigc::ptr_fun(&select_path_break_apart), app)); + gapp->add_action( "select-path-split-non-intersecting", sigc::bind(sigc::ptr_fun(&select_path_split_non_intersecting), app)); gapp->add_action( "select-fill-between-paths", sigc::bind(sigc::ptr_fun(&fill_between_paths), app)); gapp->add_action( "select-path-simplify", sigc::bind(sigc::ptr_fun(&select_path_simplify), app)); // clangt on diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 2d2d3a4553..8cca56ac08 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -171,7 +171,7 @@ static Geom::PathVector remove_lines_from_pathvec(const Geom::PathVector &paths) } template -bool is_intersecting(const Path1 &a, const Path2 &b) { +static bool is_intersecting(const Path1 &a, const Path2 &b) { for (auto &node : b.nodes()) { if (a.winding(node)) { return true; @@ -185,7 +185,7 @@ bool is_intersecting(const Path1 &a, const Path2 &b) { return false; } -std::vector break_apart_non_touching(Geom::PathVector &paths) +static std::vector break_apart_non_touching(Geom::PathVector &paths) { std::vector visited(paths.size()); std::vector result; diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index fcab37eaa2..b796d5e7a2 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -575,6 +575,88 @@ void ObjectSet::break_into_non_overlapping_pieces(bool fill_holes, bool is_destr builder.build(); } +template +static bool is_intersecting(const Path1 &a, const Path2 &b) { + for (auto &node : b.nodes()) { + if (a.winding(node)) { + return true; + } + } + for (auto &node : a.nodes()) { + if (b.winding(node)) { + return true; + } + } + return false; +} + +static std::vector break_apart_non_touching(Geom::PathVector &paths) +{ + std::vector visited(paths.size()); + std::vector result; + + int n = paths.size(); + for (int i = n - 1; i >= 0; i--) { + if (visited[i]) { + continue; + } + result.emplace_back(paths[i]); + visited[i] = true; + auto &curr = result.back(); + for (int j = 0; j < n; j++) { + if (visited[j]) { + continue; + } + if (is_intersecting(curr, paths[j])) { + visited[j] = true; + curr.push_back(paths[j]); + } + } + } + + return result; +} + +template // Geom::Path or Geom::PathVector +static XML::Node* draw_on_canvas(const Path &path, const SPItem *to_copy_from, XML::Node *parent) +{ + Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); + repr->setAttribute("d", sp_svg_write_path(path)); + + if (to_copy_from) { + gchar *style = g_strdup(to_copy_from->getRepr()->attribute("style")); + repr->setAttribute("style", style); + } + + auto str = sp_svg_write_path(path); + repr->setAttribute("d", str); + + parent->addChildAtPos(repr, 0); + + return repr; +} + +void ObjectSet::splitNonIntersecting() +{ + std::vector items_vec(items().begin(), items().end()); + int n = items_vec.size(); + std::vector result(n); + + auto parent = items_vec.front()->getRepr()->parent(); + + for (int i = 0; i < n; i++) { + auto pathvec = items_vec[i]->get_pathvector(); + auto broken = break_apart_non_touching(pathvec); + for (auto paths : broken) { + result.push_back(draw_on_canvas(paths, items_vec[i], parent)); + } + } + deleteItems(); + for (auto &node : result) { + add(node); + } +} + } // namespace Inkscape /* diff --git a/src/object/object-set.h b/src/object/object-set.h index fc492adb11..c3a3baba6d 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -472,6 +472,7 @@ public: void the_temporary_fix_for_the_transform_bug(); void break_into_non_overlapping_pieces(bool fill_holes = false, bool is_destructive = true, bool put_above_originals = false); + void splitNonIntersecting(); void additional_method(); protected: -- GitLab From 9d813de37667991ffa335451b3c8eeb1f66dd0f1 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 22 Jun 2021 07:09:30 +0200 Subject: [PATCH 049/235] Added the temporary fix for the transform bug in the splitting function. --- src/object/object-set.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index b796d5e7a2..4e28e4daad 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -638,6 +638,8 @@ static XML::Node* draw_on_canvas(const Path &path, const SPItem *to_copy_from, X void ObjectSet::splitNonIntersecting() { + the_temporary_fix_for_the_transform_bug(); + std::vector items_vec(items().begin(), items().end()); int n = items_vec.size(); std::vector result(n); -- GitLab From b39c53cfa5699f531af8da56d53903d4dae95861 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 22 Jun 2021 07:10:27 +0200 Subject: [PATCH 050/235] mirrored the icon for "Fracture" to match with the rest (temporary). --- .../Tango/scalable/actions/path-fracture.svg | 22 +++++++++---------- .../scalable/actions/path-fracture.svg | 22 +++++++++---------- .../symbolic/actions/path-fracture.svg | 22 +++++++++---------- .../symbolic/actions/path-fracture.svg | 22 +++++++++---------- 4 files changed, 40 insertions(+), 48 deletions(-) diff --git a/share/icons/Tango/scalable/actions/path-fracture.svg b/share/icons/Tango/scalable/actions/path-fracture.svg index f9b572987f..a0476e22b4 100644 --- a/share/icons/Tango/scalable/actions/path-fracture.svg +++ b/share/icons/Tango/scalable/actions/path-fracture.svg @@ -5,8 +5,8 @@ width="16" height="16" viewBox="0 0 16 16" - sodipodi:docname="path-division-all-symbolic.svg" - inkscape:version="1.1 (c68e22c387, 2021-05-23)" + sodipodi:docname="path-fracture.svg" + inkscape:version="1.2-dev (c301930b54, 2021-06-21, custom)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" @@ -45,11 +45,11 @@ showgrid="true" inkscape:zoom="32.4065" inkscape:cx="7.9459368" - inkscape:cy="-0.58630213" - inkscape:window-width="1858" - inkscape:window-height="1177" - inkscape:window-x="54" - inkscape:window-y="-8" + inkscape:cy="8.871677" + inkscape:window-width="1920" + inkscape:window-height="968" + inkscape:window-x="0" + inkscape:window-y="27" inkscape:window-maximized="1" inkscape:current-layer="path-division" inkscape:document-rotation="0" @@ -70,12 +70,11 @@ @@ -83,7 +82,6 @@ id="path12456" style="opacity:1;fill:#000000;fill-opacity:1" class="warning" - d="M 5.5,0 C 2.4624,0 0,2.4624 0,5.5 0,8.0172403 1.6917107,10.133629 4,10.785156 V 4 h 6.785156 C 10.133678,1.6917107 8.0172403,0 5.5,0 Z" - transform="translate(165,667.36218)" /> + d="m 174.5313,667.36216 c 3.0376,0 5.5,2.4624 5.5,5.5 0,2.51724 -1.69171,4.63363 -4,5.28516 v -6.78516 h -6.78516 c 0.65148,-2.30829 2.76792,-4 5.28516,-4 z" /> diff --git a/share/icons/hicolor/scalable/actions/path-fracture.svg b/share/icons/hicolor/scalable/actions/path-fracture.svg index f9b572987f..a0476e22b4 100644 --- a/share/icons/hicolor/scalable/actions/path-fracture.svg +++ b/share/icons/hicolor/scalable/actions/path-fracture.svg @@ -5,8 +5,8 @@ width="16" height="16" viewBox="0 0 16 16" - sodipodi:docname="path-division-all-symbolic.svg" - inkscape:version="1.1 (c68e22c387, 2021-05-23)" + sodipodi:docname="path-fracture.svg" + inkscape:version="1.2-dev (c301930b54, 2021-06-21, custom)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" @@ -45,11 +45,11 @@ showgrid="true" inkscape:zoom="32.4065" inkscape:cx="7.9459368" - inkscape:cy="-0.58630213" - inkscape:window-width="1858" - inkscape:window-height="1177" - inkscape:window-x="54" - inkscape:window-y="-8" + inkscape:cy="8.871677" + inkscape:window-width="1920" + inkscape:window-height="968" + inkscape:window-x="0" + inkscape:window-y="27" inkscape:window-maximized="1" inkscape:current-layer="path-division" inkscape:document-rotation="0" @@ -70,12 +70,11 @@ @@ -83,7 +82,6 @@ id="path12456" style="opacity:1;fill:#000000;fill-opacity:1" class="warning" - d="M 5.5,0 C 2.4624,0 0,2.4624 0,5.5 0,8.0172403 1.6917107,10.133629 4,10.785156 V 4 h 6.785156 C 10.133678,1.6917107 8.0172403,0 5.5,0 Z" - transform="translate(165,667.36218)" /> + d="m 174.5313,667.36216 c 3.0376,0 5.5,2.4624 5.5,5.5 0,2.51724 -1.69171,4.63363 -4,5.28516 v -6.78516 h -6.78516 c 0.65148,-2.30829 2.76792,-4 5.28516,-4 z" /> diff --git a/share/icons/hicolor/symbolic/actions/path-fracture.svg b/share/icons/hicolor/symbolic/actions/path-fracture.svg index f9b572987f..a0476e22b4 100644 --- a/share/icons/hicolor/symbolic/actions/path-fracture.svg +++ b/share/icons/hicolor/symbolic/actions/path-fracture.svg @@ -5,8 +5,8 @@ width="16" height="16" viewBox="0 0 16 16" - sodipodi:docname="path-division-all-symbolic.svg" - inkscape:version="1.1 (c68e22c387, 2021-05-23)" + sodipodi:docname="path-fracture.svg" + inkscape:version="1.2-dev (c301930b54, 2021-06-21, custom)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" @@ -45,11 +45,11 @@ showgrid="true" inkscape:zoom="32.4065" inkscape:cx="7.9459368" - inkscape:cy="-0.58630213" - inkscape:window-width="1858" - inkscape:window-height="1177" - inkscape:window-x="54" - inkscape:window-y="-8" + inkscape:cy="8.871677" + inkscape:window-width="1920" + inkscape:window-height="968" + inkscape:window-x="0" + inkscape:window-y="27" inkscape:window-maximized="1" inkscape:current-layer="path-division" inkscape:document-rotation="0" @@ -70,12 +70,11 @@ @@ -83,7 +82,6 @@ id="path12456" style="opacity:1;fill:#000000;fill-opacity:1" class="warning" - d="M 5.5,0 C 2.4624,0 0,2.4624 0,5.5 0,8.0172403 1.6917107,10.133629 4,10.785156 V 4 h 6.785156 C 10.133678,1.6917107 8.0172403,0 5.5,0 Z" - transform="translate(165,667.36218)" /> + d="m 174.5313,667.36216 c 3.0376,0 5.5,2.4624 5.5,5.5 0,2.51724 -1.69171,4.63363 -4,5.28516 v -6.78516 h -6.78516 c 0.65148,-2.30829 2.76792,-4 5.28516,-4 z" /> diff --git a/share/icons/multicolor/symbolic/actions/path-fracture.svg b/share/icons/multicolor/symbolic/actions/path-fracture.svg index f9b572987f..a0476e22b4 100644 --- a/share/icons/multicolor/symbolic/actions/path-fracture.svg +++ b/share/icons/multicolor/symbolic/actions/path-fracture.svg @@ -5,8 +5,8 @@ width="16" height="16" viewBox="0 0 16 16" - sodipodi:docname="path-division-all-symbolic.svg" - inkscape:version="1.1 (c68e22c387, 2021-05-23)" + sodipodi:docname="path-fracture.svg" + inkscape:version="1.2-dev (c301930b54, 2021-06-21, custom)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" @@ -45,11 +45,11 @@ showgrid="true" inkscape:zoom="32.4065" inkscape:cx="7.9459368" - inkscape:cy="-0.58630213" - inkscape:window-width="1858" - inkscape:window-height="1177" - inkscape:window-x="54" - inkscape:window-y="-8" + inkscape:cy="8.871677" + inkscape:window-width="1920" + inkscape:window-height="968" + inkscape:window-x="0" + inkscape:window-y="27" inkscape:window-maximized="1" inkscape:current-layer="path-division" inkscape:document-rotation="0" @@ -70,12 +70,11 @@ @@ -83,7 +82,6 @@ id="path12456" style="opacity:1;fill:#000000;fill-opacity:1" class="warning" - d="M 5.5,0 C 2.4624,0 0,2.4624 0,5.5 0,8.0172403 1.6917107,10.133629 4,10.785156 V 4 h 6.785156 C 10.133678,1.6917107 8.0172403,0 5.5,0 Z" - transform="translate(165,667.36218)" /> + d="m 174.5313,667.36216 c 3.0376,0 5.5,2.4624 5.5,5.5 0,2.51724 -1.69171,4.63363 -4,5.28516 v -6.78516 h -6.78516 c 0.65148,-2.30829 2.76792,-4 5.28516,-4 z" /> -- GitLab From 966bde44d6d21797150382326c7b8499b6fb38df Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 22 Jun 2021 11:05:47 +0200 Subject: [PATCH 051/235] Extracted common and useful functions into one file to avoid duplication. --- src/helper/CMakeLists.txt | 6 +- src/helper/NonOverlappingPathsBuilder.cpp | 184 ++--------------- src/helper/NonOverlappingPathsBuilder.h | 21 +- src/helper/useful-functions.cpp | 233 ++++++++++++++++++++++ src/helper/useful-functions.h | 52 +++++ src/object/object-set.cpp | 168 +--------------- src/object/object-set.h | 1 - src/verbs.cpp | 12 -- 8 files changed, 323 insertions(+), 354 deletions(-) create mode 100644 src/helper/useful-functions.cpp create mode 100644 src/helper/useful-functions.h diff --git a/src/helper/CMakeLists.txt b/src/helper/CMakeLists.txt index 0e6601a84e..de29ba6d60 100644 --- a/src/helper/CMakeLists.txt +++ b/src/helper/CMakeLists.txt @@ -22,7 +22,8 @@ set(helper_SRC pixbuf-ops.cpp png-write.cpp stock-items.cpp - verb-action.cpp + useful-functions.cpp + verb-action.cpp #units-test.cpp # we generate this file and it's .h counter-part @@ -45,7 +46,8 @@ set(helper_SRC pixbuf-ops.h png-write.h stock-items.h - verb-action.h + useful-functions.h + verb-action.h ) set_source_files_properties(sp_marshal_SRC PROPERTIES GENERATED true) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 8cca56ac08..28e87b8b0b 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -18,9 +18,9 @@ #include #include "ui/widget/canvas.h" #include "geom-pathstroke.h" -#include #include #include <2geom/intersection-graph.h> +#include "useful-functions.h" namespace Inkscape { @@ -32,7 +32,7 @@ void NonOverlappingPathsBuilder::build() // random (probably the it copies the style of the biggest // object). may also let the user decide. - set_desktop_busy(); + set_desktop_busy(desktop()); // Ideally shouldn't be converting to paths? set->toCurves(); @@ -52,7 +52,7 @@ void NonOverlappingPathsBuilder::build() } } - unset_desktop_busy(); + unset_desktop_busy(desktop()); } SPItem* NonOverlappingPathsBuilder::get_most_item(std::function cmp) @@ -88,131 +88,6 @@ SPDesktop *NonOverlappingPathsBuilder::desktop() return set->desktop(); } -void NonOverlappingPathsBuilder::set_desktop_busy() -{ - if (desktop()) { - // set "busy" cursor - desktop()->setWaitingCursor(); - // disable redrawing during the break-apart operation for remarkable speedup for large paths - desktop()->getCanvas()->set_drawing_disabled(true); - } -} - -void NonOverlappingPathsBuilder::unset_desktop_busy() -{ - if (desktop()) { - desktop()->getCanvas()->set_drawing_disabled(false); - desktop()->clearWaitingCursor(); - } -} - -static double path_area_non_self_intersecting_no_curves(const Geom::Path &path) -{ - // This function doesn't take curves into consideration. - auto nodes = path.nodes(); - int j = nodes.size() - 1; - - double area = 0; - for (int i = 1; i < nodes.size(); i++) { - auto x1 = nodes[i].x(); - auto x2 = nodes[j].x(); - auto y1 = nodes[i].y(); - auto y2 = nodes[j].y(); - area += (x1 + x2) * (y1 - y2); - j = i; - } - area /= 2; - - return std::abs(area); -} - -void print_bounds_and_nodes_count(const Geom::PathVector &path) { - auto bounds = path.boundsFast(); - std::cout << "Nodes count: " << path.nodes().size() << ", "; - std::cout << "Bounds: "; - std::cout << "Top: " << bounds->top() << ", "; - std::cout << "Bottom: " << bounds->bottom() << ", "; - std::cout << "Left: " << bounds->left() << ", "; - std::cout << "Right: " << bounds->right() << "\n"; -} - -static Geom::PathVector remove_paths_with_small_area_from_pathvec(const Geom::PathVector &paths, double minimum_area) -{ - // FIXME may want to edit this PathVector instead of creating a new one? - // FIXME take curves into consideration. Guess this will only work with - // the current way of breaking selection into non-overlapping pieces - // since it produces a paths with no curves. Other than that, this - // function shouldn't be used until fixed. - - Geom::PathVector result; - for (auto &path : paths) { - auto area = path_area_non_self_intersecting_no_curves(path); - if (area >= minimum_area) { - result.push_back(path); - } else { -// std::cout << "Deleted a path with an area of " << area << ".\n"; - // print_bounds_and_nodes_count(path); - } - } - - return result; -} - -static Geom::PathVector remove_lines_from_pathvec(const Geom::PathVector &paths) -{ - Geom::PathVector result; - for (auto &path : paths) { - if (path.nodes().size() > 2) { - result.push_back(path); - } - } - - return result; -} - -template -static bool is_intersecting(const Path1 &a, const Path2 &b) { - for (auto &node : b.nodes()) { - if (a.winding(node)) { - return true; - } - } - for (auto &node : a.nodes()) { - if (b.winding(node)) { - return true; - } - } - return false; -} - -static std::vector break_apart_non_touching(Geom::PathVector &paths) -{ - std::vector visited(paths.size()); - std::vector result; - - int n = paths.size(); - for (int i = n - 1; i >= 0; i--) { - if (visited[i]) { - continue; - } - result.emplace_back(paths[i]); - visited[i] = true; - auto &curr = result.back(); - for (int j = 0; j < n; j++) { - if (visited[j]) { - continue; - } - if (is_intersecting(curr, paths[j])) { - visited[j] = true; - curr.push_back(paths[j]); - } - } - } - - return result; -} - - class PathHelper { static int counter; @@ -222,11 +97,11 @@ class PathHelper paths = remove_lines_from_pathvec(paths); // paths = remove_loop_lines_from_pathvec(paths); // paths = remove_paths_with_small_area_from_pathvec(paths, 0.5); - // double max_area = 0; - // for (auto &path : paths) { - // max_area = std::max(max_area, path_area_non_self_intersecting_no_curves(path)); - // } - // paths = remove_paths_with_small_area_from_pathvec(paths, max_area/100); + double max_area = 0; + for (auto &path : paths) { + max_area = std::max(max_area, path_area_non_self_intersecting(path)); + } + paths = remove_paths_with_small_area_from_pathvec(paths, max_area/100); } public: @@ -235,18 +110,20 @@ public: SPItem *item; std::set operations; + // FIXME!! find a better way of doing this... this value is copied for each object... + double curve_length_divisor; + PathHelper() {} - PathHelper(Geom::PathVector paths, SPItem *item, std::set operations) + PathHelper(Geom::PathVector paths, SPItem *item, std::set operations, double curve_length_divisor) : paths(std::move(paths)) , item(item) , operations(std::move(operations)) + , curve_length_divisor(curve_length_divisor) {} - PathHelper(Geom::PathVector paths, SPItem *item, std::set operations1, const std::set &operations2) - : paths(std::move(paths)) - , item(item) - , operations(std::move(operations1)) + PathHelper(Geom::PathVector paths, SPItem *item, std::set operations1, const std::set &operations2, double curve_length_divisor) + : PathHelper(paths, item, operations1, curve_length_divisor) { operations.insert(operations2.begin(), operations2.end()); } @@ -279,7 +156,7 @@ public: auto intersection_paths = sp_pathvector_boolop(paths, path.paths, bool_op_inters, fill_nonZero, fill_nonZero); // auto intersection_paths = pig.getIntersection(); auto intersection_item = sp_item_repr_compare_position_bool(path.item, this->item) ? this->item : path.item; - PathHelper intersection(intersection_paths, intersection_item, operations, path.operations); + PathHelper intersection(intersection_paths, intersection_item, operations, path.operations, curve_length_divisor); intersection.prepare_for_current_operation(); if (intersection.paths.empty()) { @@ -288,12 +165,12 @@ public: // auto diff1_paths = pig.getAminusB(); auto diff1_paths = sp_pathvector_boolop(paths, path.paths, bool_op_diff, fill_nonZero, fill_nonZero); - PathHelper diff1(diff1_paths, path.item, path.operations); + PathHelper diff1(diff1_paths, path.item, path.operations, curve_length_divisor); diff1.prepare_for_current_operation(); // auto diff2_paths = pig.getBminusA(); auto diff2_paths = sp_pathvector_boolop(path.paths, paths, bool_op_diff, fill_nonZero, fill_nonZero); - PathHelper diff2(diff2_paths, item, operations); + PathHelper diff2(diff2_paths, item, operations, curve_length_divisor); diff2.prepare_for_current_operation(); counter++; @@ -321,31 +198,12 @@ std::vector break_apart_non_touching(std::vector &paths) for (auto &path : paths) { auto broken = break_apart_non_touching(path.paths); for (auto &broken_path : broken) { - result.emplace_back(broken_path, path.item, path.operations); + result.emplace_back(broken_path, path.item, path.operations, path.curve_length_divisor); } } return result; } -template // Geom::Path or Geom::PathVector -XML::Node *NonOverlappingPathsBuilder::draw_on_canvas(const Path &path, const SPItem *to_copy_from) -{ - Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); - repr->setAttribute("d", sp_svg_write_path(path)); - - if (to_copy_from) { - gchar *style = g_strdup(to_copy_from->getRepr()->attribute("style")); - repr->setAttribute("style", style); - } - - auto str = sp_svg_write_path(path); - repr->setAttribute("d", str); - - parent->addChild(repr, after); - - return repr; -} - void sort_pathhelper_vector(std::vector &vector) { auto cmp = [](const PathHelper &a, const PathHelper &b) { @@ -361,7 +219,7 @@ void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() result.resize(n); for (int i = 0; i < n; i++) { - result[i] = PathHelper(items[i]->get_pathvector(), items[i], {}); + result[i] = PathHelper(items[i]->get_pathvector(), items[i], {}, curve_length_divisor); } // FIXME for some reason, sometimes the output is not correct unless @@ -395,7 +253,7 @@ void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() n = result.size(); result_nodes.resize(n); for (int i = 0; i < n; i++) { - result_nodes[i] = draw_on_canvas(result[i].paths, result[i].item); + result_nodes[i] = draw_on_canvas(result[i].paths, result[i].item, parent, after); } } diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index feed6a1243..357a35e3d0 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -21,11 +21,16 @@ namespace Inkscape { class NonOverlappingPathsBuilder { + +public: + bool fill_holes = false; bool is_destructive = true; bool put_above_originals = true; // won't matter if is_destructive is true. bool add_result_to_set = true; + double curve_length_divisor = 5; +private: XML::Node *parent; XML::Node *after; @@ -36,25 +41,13 @@ class NonOverlappingPathsBuilder public: - NonOverlappingPathsBuilder(ObjectSet *set) : set(set) {} - + NonOverlappingPathsBuilder(ObjectSet *set) : set(set) {} void build(); - void setFillHoles(bool fillHoles) { fill_holes = fillHoles; } - void setIsDestructive(bool isDestructive) { is_destructive = isDestructive; } - void setPutOriginalsAtBottom(bool putOriginalsAtBottom) { put_above_originals = putOriginalsAtBottom; } - void setAddResultToSet(bool addResultToSet) { add_result_to_set = addResultToSet; } - private: - void construct_non_overlapping_pieces(); - - template // Geom::Path or Geom::PathVector - XML::Node *draw_on_canvas(const Path &path, const SPItem *to_copy_from = nullptr); + void construct_non_overlapping_pieces(); SPDesktop *desktop(); - void set_desktop_busy(); - void unset_desktop_busy(); - void set_parameters(); SPItem* get_most_item(std::function cmp); }; diff --git a/src/helper/useful-functions.cpp b/src/helper/useful-functions.cpp new file mode 100644 index 0000000000..dc8c905ba3 --- /dev/null +++ b/src/helper/useful-functions.cpp @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Builder class that construct non-overlapping paths given an ObjectSet. + * + * + *//* + * Authors: + * Osama Ahmad + * + * Copyright (C) 2021 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "useful-functions.h" + +#include <2geom/intersection-graph.h> +#include +#include +#include +#include +#include +#include + +#include "geom-pathstroke.h" +#include "ui/widget/canvas.h" + +namespace Inkscape +{ + +void set_desktop_busy(SPDesktop *desktop) +{ + if (desktop) { + // set "busy" cursor + desktop->setWaitingCursor(); + // disable redrawing during the break-apart operation for remarkable speedup for large paths + desktop->getCanvas()->set_drawing_disabled(true); + } +} + +void unset_desktop_busy(SPDesktop *desktop) +{ + if (desktop) { + desktop->getCanvas()->set_drawing_disabled(false); + desktop->clearWaitingCursor(); + } +} + +std::vector get_path_nodes(const Geom::Path &path, double curve_length_divisor) +{ + std::vector result; + for (auto &curve : path) { + if (curve.isLineSegment()) { + result.push_back(curve.initialPoint()); + continue; + } + int nodes_to_get = std::ceil(curve.length() / curve_length_divisor); + double step = 1.0 / nodes_to_get; + for (double coord = 0; coord < 1; coord += step) { + result.push_back(curve.pointAt(coord)); + } + } + return result; +} + +double path_area_non_self_intersecting(const std::vector& nodes) +{ + int j = nodes.size() - 1; + + double area = 0; + for (int i = 1; i < nodes.size(); i++) { + auto x1 = nodes[i].x(); + auto x2 = nodes[j].x(); + auto y1 = nodes[i].y(); + auto y2 = nodes[j].y(); + area += (x1 + x2) * (y1 - y2); + j = i; + } + area /= 2; + + return std::abs(area); +} + +double path_area_non_self_intersecting(const Geom::Path &path, double curve_length_divisor) +{ + return path_area_non_self_intersecting(get_path_nodes(path, curve_length_divisor)); +} + +double path_area_non_self_intersecting_no_curves(const Geom::Path &path) +{ + // This function doesn't take curves into consideration. + return path_area_non_self_intersecting(path.nodes()); +} + +void print_bounds_and_nodes_count(const Geom::PathVector &path) { + auto bounds = path.boundsFast(); + std::cout << "Nodes count: " << path.nodes().size() << ", "; + std::cout << "Bounds: "; + std::cout << "Top: " << bounds->top() << ", "; + std::cout << "Bottom: " << bounds->bottom() << ", "; + std::cout << "Left: " << bounds->left() << ", "; + std::cout << "Right: " << bounds->right() << "\n"; +} + +Geom::PathVector remove_paths_with_small_area_from_pathvec(const Geom::PathVector &paths, double minimum_area, double curve_length_divisor) +{ + // FIXME may want to edit this PathVector instead of creating a new one? + // FIXME take curves into consideration. Guess this will only work with + // the current way of breaking selection into non-overlapping pieces + // since it produces a paths with no curves. Other than that, this + // function shouldn't be used until fixed. + + Geom::PathVector result; + for (auto &path : paths) { + auto area = path_area_non_self_intersecting(path, curve_length_divisor); + if (area >= minimum_area) { + result.push_back(path); + } else { +// std::cout << "Deleted a path with an area of " << area << ".\n"; + // print_bounds_and_nodes_count(path); + } + } + + return result; +} + +Geom::PathVector remove_lines_from_pathvec(const Geom::PathVector &paths) +{ + Geom::PathVector result; + for (auto &path : paths) { + if (path.nodes().size() > 2) { + result.push_back(path); + } + } + + return result; +} + +template +bool is_intersecting(const Path1 &a, const Path2 &b) { + for (auto &node : b.nodes()) { + if (a.winding(node)) { + return true; + } + } + for (auto &node : a.nodes()) { + if (b.winding(node)) { + return true; + } + } + return false; +} + +std::vector break_apart_non_touching(const Geom::PathVector &paths) +{ + std::vector visited(paths.size()); + std::vector result; + + int n = paths.size(); + for (int i = n - 1; i >= 0; i--) { + if (visited[i]) { + continue; + } + result.emplace_back(paths[i]); + visited[i] = true; + auto &curr = result.back(); + for (int j = 0; j < n; j++) { + if (visited[j]) { + continue; + } + if (is_intersecting(curr, paths[j])) { + visited[j] = true; + curr.push_back(paths[j]); + } + } + } + + return result; +} + +std::string point_coords(const Geom::Point& point, int precision) +{ + std::ostringstream oss; + oss << std::fixed << std::setprecision(precision) << "(" << point.x() << ", " << point.y() << ")"; + return oss.str(); +} + +// FIXME this is not declared in the header file to avoid circular +// dependency. may remove it since it's for debugging anyways. +void print_coords_sorted_from_set(ObjectSet *set) +{ + auto cmp = [](const Geom::Point& a, const Geom::Point &b) { + return a.x() > b.x() || (a.x() == b.x() && a.y() > b.y()); + }; + + auto items = set->items(); + + int item_num = 1; + for (auto item : items) { + std::cout << "Item " << item_num++ << ":\n"; + auto paths = item->get_pathvector(); + auto nodes = paths.nodes(); + std::sort(nodes.begin(), nodes.end(), cmp); + for (auto node : nodes) { + std::cout << point_coords(node, 1) << ' '; + } + std::cout << "\n\n"; + } + +} + +XML::Node *draw_on_canvas(const Geom::PathVector &path, const SPItem *to_copy_from, XML::Node *parent, XML::Node *after) +{ + Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); + repr->setAttribute("d", sp_svg_write_path(path)); + + if (to_copy_from) { + gchar *style = g_strdup(to_copy_from->getRepr()->attribute("style")); + repr->setAttribute("style", style); + } + + auto str = sp_svg_write_path(path); + repr->setAttribute("d", str); + + if (after) { + parent->addChild(repr, after); + } else { + parent->addChildAtPos(repr, 0); + } + + return repr; +} + +} \ No newline at end of file diff --git a/src/helper/useful-functions.h b/src/helper/useful-functions.h new file mode 100644 index 0000000000..db64312da2 --- /dev/null +++ b/src/helper/useful-functions.h @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Some useful functions for dealing shapes and paths. + * + * + *//* + * Authors: + * Osama Ahmad + * + * Copyright (C) 2021 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#pragma once + +#include +#include +#include +#include + +#include "object/sp-item.h" +#include "xml/node.h" + +#define DEFAULT_CURVE_LENGTH_DIVISOR 5 +#define DEFAULT_MINIMUM_AREA 0.01 + +namespace Inkscape { + +void set_desktop_busy(SPDesktop *desktop); +void unset_desktop_busy(SPDesktop *desktop); + +std::vector get_path_nodes(const Geom::Path &path, double curve_length_divisor = DEFAULT_CURVE_LENGTH_DIVISOR); +double path_area_non_self_intersecting_no_curves(const Geom::Path &path); +double path_area_non_self_intersecting(const std::vector &nodes); +double path_area_non_self_intersecting(const Geom::Path &path, double curve_length_divisor = DEFAULT_CURVE_LENGTH_DIVISOR); +Geom::PathVector remove_paths_with_small_area_from_pathvec(const Geom::PathVector &paths, + double minimum_area = DEFAULT_MINIMUM_AREA, double curve_length_divisor = DEFAULT_CURVE_LENGTH_DIVISOR); +Geom::PathVector remove_lines_from_pathvec(const Geom::PathVector &paths); +std::vector break_apart_non_touching(const Geom::PathVector &paths); + +template +bool is_intersecting(const Path1 &a, const Path2 &b); + +// TODO make it work with both Geom::Path and Geom::PathVector. just use templates. +// remember that you'll encounter problems if you tried to have a templated function +// in both a header and a source file. this is why it's not being done for now. +XML::Node *draw_on_canvas(const Geom::PathVector &path, const SPItem *to_copy_from, XML::Node *parent, XML::Node *after = nullptr); + +void print_bounds_and_nodes_count(const Geom::PathVector &path); +std::string point_coords(const Geom::Point &point, int precision = 10); + +} \ No newline at end of file diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 4e28e4daad..2092713600 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -20,6 +20,7 @@ #include #include "helper/geom-pathstroke.h" +#include "helper/useful-functions.h" #include "helper/NonOverlappingPathsBuilder.h" #include "document.h" #include "xml/repr.h" @@ -447,37 +448,6 @@ void ObjectSet::_remove3DBoxesRecursively(SPObject *obj) { } } -std::string point_coords(const Geom::Point& point, int precision = 10) -{ - std::ostringstream oss; - oss << std::fixed << std::setprecision(precision) << "(" << point.x() << ", " << point.y() << ")"; - return oss.str(); -} - -template -void draw_sp_items(const SPItemList &items, ObjectSet *set) { - for (auto item : items) { - auto paths = get_path_data(item); - draw_on_canvas_and_copy_style_from_set(paths, set, true); - } -} - -SPItem* getAnItemThatIntersectsFromSet(const Geom::Point &point, ObjectSet *set) -{ - auto document = set->document(); - auto dkey = set->desktop()->dkey; - std::vector result_items = document->getItemsAtPoints(dkey, {point}); - std::cout << result_items.size() << '\n'; - for (auto item1 : result_items) { - for (auto item2 : set->items()) { - if (item1 == item2) { - return item1; - } - } - } - return nullptr; -} - void ObjectSet::the_temporary_fix_for_the_transform_bug() { // FIXME this is to get rid of the bug where @@ -488,67 +458,6 @@ void ObjectSet::the_temporary_fix_for_the_transform_bug() move(0, 0); } -void print_coords_sorted_from_set(ObjectSet *set) -{ - auto cmp = [](const Geom::Point& a, const Geom::Point &b) { - return a.x() > b.x() || (a.x() == b.x() && a.y() > b.y()); - }; - - auto items = set->items(); - - int item_num = 1; - for (auto item : items) { - std::cout << "Item " << item_num++ << ":\n"; - auto paths = item->get_pathvector(); - auto nodes = paths.nodes(); - std::sort(nodes.begin(), nodes.end(), cmp); - for (auto node : nodes) { - std::cout << point_coords(node, 1) << ' '; - } - std::cout << "\n\n"; - } - -} - -double path_area_non_self_intersecting_no_curves(const Geom::Path &path) -{ - // This function doesn't take curves into consideration. - auto nodes = path.nodes(); - int j = nodes.size() - 1; - - double area = 0; - for (int i = 1; i < nodes.size(); i++) { - auto x1 = nodes[i].x(); - auto x2 = nodes[j].x(); - auto y1 = nodes[i].y(); - auto y2 = nodes[j].y(); - area += (x1 + x2) * (y1 - y2); - j = i; - } - area /= 2; - - return std::abs(area); -} - -static Geom::PathVector remove_paths_with_small_area_from_pathvec(const Geom::PathVector &paths, double minimum_area) -{ - // FIXME may want to edit this PathVector instead of creating a new one? - // FIXME take curves into consideration. Guess this will only work with - // the current way of breaking selection into non-overlapping pieces - // since it produces a paths with no curves. Other than that, this - // function shouldn't be used until fixed. - - Geom::PathVector result; - for (auto &path : paths) { - auto area = path_area_non_self_intersecting_no_curves(path); - if (area >= minimum_area) { - result.push_back(path); - } - } - - return result; -} - void ObjectSet::remove_paths_with_small_area() { for (auto item : items()) { @@ -560,84 +469,19 @@ void ObjectSet::remove_paths_with_small_area() } } -void ObjectSet::additional_method() -{ - // called when clicked on "Cut Path" - remove_paths_with_small_area(); -} - void ObjectSet::break_into_non_overlapping_pieces(bool fill_holes, bool is_destructive, bool put_above_originals) { NonOverlappingPathsBuilder builder(this); - builder.setFillHoles(fill_holes); - builder.setIsDestructive(is_destructive); - builder.setPutOriginalsAtBottom(put_above_originals); + builder.fill_holes = fill_holes; + builder.is_destructive = is_destructive; + builder.put_above_originals = put_above_originals; builder.build(); } -template -static bool is_intersecting(const Path1 &a, const Path2 &b) { - for (auto &node : b.nodes()) { - if (a.winding(node)) { - return true; - } - } - for (auto &node : a.nodes()) { - if (b.winding(node)) { - return true; - } - } - return false; -} - -static std::vector break_apart_non_touching(Geom::PathVector &paths) -{ - std::vector visited(paths.size()); - std::vector result; - - int n = paths.size(); - for (int i = n - 1; i >= 0; i--) { - if (visited[i]) { - continue; - } - result.emplace_back(paths[i]); - visited[i] = true; - auto &curr = result.back(); - for (int j = 0; j < n; j++) { - if (visited[j]) { - continue; - } - if (is_intersecting(curr, paths[j])) { - visited[j] = true; - curr.push_back(paths[j]); - } - } - } - - return result; -} - -template // Geom::Path or Geom::PathVector -static XML::Node* draw_on_canvas(const Path &path, const SPItem *to_copy_from, XML::Node *parent) -{ - Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); - repr->setAttribute("d", sp_svg_write_path(path)); - - if (to_copy_from) { - gchar *style = g_strdup(to_copy_from->getRepr()->attribute("style")); - repr->setAttribute("style", style); - } - - auto str = sp_svg_write_path(path); - repr->setAttribute("d", str); - - parent->addChildAtPos(repr, 0); - - return repr; -} - void ObjectSet::splitNonIntersecting() { + // FIXME consider the case when splitting a group. + the_temporary_fix_for_the_transform_bug(); std::vector items_vec(items().begin(), items().end()); diff --git a/src/object/object-set.h b/src/object/object-set.h index c3a3baba6d..e079e0cbc2 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -473,7 +473,6 @@ public: void break_into_non_overlapping_pieces(bool fill_holes = false, bool is_destructive = true, bool put_above_originals = false); void splitNonIntersecting(); - void additional_method(); protected: virtual void _connectSignals(SPObject* object) {}; diff --git a/src/verbs.cpp b/src/verbs.cpp index 6d066abfe8..6c53266b73 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1170,12 +1170,6 @@ void SelectionVerb::perform(SPAction *action, void *data) selection->pathCut(); break; case SP_VERB_SELECTION_SLICE: - selection->removeLPESRecursive(true); - selection->unlinkRecursive(true); - // selection->remove_paths_with_small_area(); - selection->additional_method(); - break; - case 22332233: selection->removeLPESRecursive(true); selection->unlinkRecursive(true); selection->pathSlice(); @@ -1283,12 +1277,6 @@ void SelectionVerb::perform(SPAction *action, void *data) sp_selected_path_offset_screen(dt, 10); break; case SP_VERB_SELECTION_INSET: - selection->removeLPESRecursive(true); - selection->unlinkRecursive(true); - // TODO make this a single history item. - selection->break_into_non_overlapping_pieces(false, true, true); - break; - case 23232323: selection->removeLPESRecursive(true); selection->unlinkRecursive(true); sp_selected_path_inset(dt); -- GitLab From ff7627c2ee66accbc800ec46d6347d9d5347f53e Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 22 Jun 2021 13:40:11 +0200 Subject: [PATCH 052/235] Handled the case where the selection is less than expected. --- src/object/object-set.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 2092713600..936b9a197f 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -471,6 +471,10 @@ void ObjectSet::remove_paths_with_small_area() void ObjectSet::break_into_non_overlapping_pieces(bool fill_holes, bool is_destructive, bool put_above_originals) { + if (size() < 2) { + return; + } + NonOverlappingPathsBuilder builder(this); builder.fill_holes = fill_holes; builder.is_destructive = is_destructive; @@ -482,6 +486,10 @@ void ObjectSet::splitNonIntersecting() { // FIXME consider the case when splitting a group. + if (isEmpty()) { + return; + } + the_temporary_fix_for_the_transform_bug(); std::vector items_vec(items().begin(), items().end()); -- GitLab From 2ca79ec60cde6b08c9b3a382ff8cab4da031e07c Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 22 Jun 2021 23:12:43 +0200 Subject: [PATCH 053/235] Made "Fracture" support groups. While testing, I discovered that "sort_pathhelper_vector" isn't even covering the unknown bug for all cases. Have to find out why this is happening. --- src/helper/NonOverlappingPathsBuilder.cpp | 34 +++++++++++++++++++---- src/helper/NonOverlappingPathsBuilder.h | 2 +- src/helper/useful-functions.cpp | 10 +++++++ src/helper/useful-functions.h | 1 + src/object/object-set.cpp | 10 +++++-- src/object/sp-item.cpp | 15 ++++++++++ src/object/sp-item.h | 1 + 7 files changed, 64 insertions(+), 9 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 28e87b8b0b..3eaac99565 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -24,6 +24,18 @@ namespace Inkscape { +// TODO this is a duplicate code from selecction-chemistry.cpp. refactor it. +static void sp_selection_delete_impl(std::vector const &items, bool propagate = true, bool propagate_descendants = true) +{ + for (auto item : items) { + sp_object_ref(item, nullptr); + } + for (auto item : items) { + item->deleteObject(propagate, propagate_descendants); + sp_object_unref(item, nullptr); + } +} + void NonOverlappingPathsBuilder::build() { // TODO It should have its own widget. @@ -32,6 +44,10 @@ void NonOverlappingPathsBuilder::build() // random (probably the it copies the style of the biggest // object). may also let the user decide. + if (set->isEmpty()) { + return; + } + set_desktop_busy(desktop()); // Ideally shouldn't be converting to paths? @@ -42,7 +58,7 @@ void NonOverlappingPathsBuilder::build() construct_non_overlapping_pieces(); if (is_destructive) { - set->deleteItems(); + sp_selection_delete_impl(items); } if (add_result_to_set) { @@ -69,18 +85,26 @@ SPItem* NonOverlappingPathsBuilder::get_most_item(std::functionitems(); - items = std::vector(_items.begin(), _items.end()); - XML::Node *item = set->topRepr(); - parent = item->parent(); + items = std::vector(_items.begin(), _items.end()); if (put_above_originals) { auto cmp = [](SPItem *a, SPItem *b) { return sp_item_repr_compare_position_bool(a, b); }; after = get_most_item(cmp)->getRepr(); + parent = after->parent(); } else { auto cmp = [](SPItem *a, SPItem *b) { return !sp_item_repr_compare_position_bool(a, b); }; - after = get_most_item(cmp)->getRepr()->prev(); + after = get_most_item(cmp)->getRepr(); + // prev might be nullptr. this is why the parent is assigned before getting the prev. + parent = after->parent(); + after = after->prev(); } + // expanding after selecting the parent to + // avoid getting and using a parent of an item + // that was in a group before (which shows nothing + // on the canvas). + // TODO is this the best way to do it? + items = get_groups_expanded(items); } SPDesktop *NonOverlappingPathsBuilder::desktop() diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index 357a35e3d0..f49533edcf 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -24,7 +24,7 @@ class NonOverlappingPathsBuilder public: - bool fill_holes = false; + bool fill_holes = false; // should this be implemented here? bool is_destructive = true; bool put_above_originals = true; // won't matter if is_destructive is true. bool add_result_to_set = true; diff --git a/src/helper/useful-functions.cpp b/src/helper/useful-functions.cpp index dc8c905ba3..9eb8c82e3f 100644 --- a/src/helper/useful-functions.cpp +++ b/src/helper/useful-functions.cpp @@ -45,6 +45,16 @@ void unset_desktop_busy(SPDesktop *desktop) } } +std::vector get_groups_expanded(const std::vector &items) +{ + std::vector result; + for (auto item : items) { + auto sub_result = item->get_groups_expanded(); + result.insert(result.end(), sub_result.begin(), sub_result.end()); + } + return result; +} + std::vector get_path_nodes(const Geom::Path &path, double curve_length_divisor) { std::vector result; diff --git a/src/helper/useful-functions.h b/src/helper/useful-functions.h index db64312da2..5574a64acf 100644 --- a/src/helper/useful-functions.h +++ b/src/helper/useful-functions.h @@ -29,6 +29,7 @@ namespace Inkscape { void set_desktop_busy(SPDesktop *desktop); void unset_desktop_busy(SPDesktop *desktop); +std::vector get_groups_expanded(const std::vector &items); std::vector get_path_nodes(const Geom::Path &path, double curve_length_divisor = DEFAULT_CURVE_LENGTH_DIVISOR); double path_area_non_self_intersecting_no_curves(const Geom::Path &path); double path_area_non_self_intersecting(const std::vector &nodes); diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 936b9a197f..e7720fbb4c 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -471,7 +471,7 @@ void ObjectSet::remove_paths_with_small_area() void ObjectSet::break_into_non_overlapping_pieces(bool fill_holes, bool is_destructive, bool put_above_originals) { - if (size() < 2) { + if (isEmpty()) { return; } @@ -493,11 +493,14 @@ void ObjectSet::splitNonIntersecting() the_temporary_fix_for_the_transform_bug(); std::vector items_vec(items().begin(), items().end()); - int n = items_vec.size(); - std::vector result(n); + // getting the parent before getting the groups expanded. auto parent = items_vec.front()->getRepr()->parent(); + items_vec = get_groups_expanded(items_vec); + int n = items_vec.size(); + std::vector result(n); + for (int i = 0; i < n; i++) { auto pathvec = items_vec[i]->get_pathvector(); auto broken = break_apart_non_touching(pathvec); @@ -505,6 +508,7 @@ void ObjectSet::splitNonIntersecting() result.push_back(draw_on_canvas(paths, items_vec[i], parent)); } } + deleteItems(); for (auto &node : result) { add(node); diff --git a/src/object/sp-item.cpp b/src/object/sp-item.cpp index bfdda874db..408890da48 100644 --- a/src/object/sp-item.cpp +++ b/src/object/sp-item.cpp @@ -163,6 +163,21 @@ Geom::PathVector SPItem::get_pathvector() return result; } +std::vector SPItem::get_groups_expanded() +{ + std::vector result; + if (SPGroup *group = dynamic_cast(this)) { + std::vector items; + sp_item_group_ungroup(group, items); + for (SPItem *item : items) { + auto sub_result = item->get_groups_expanded(); + result.insert(result.end(), sub_result.begin(), sub_result.end()); + } + } else { + return { this }; + } + return result; +} bool SPItem::isLocked() const { for (SPObject const *o = this; o != nullptr; o = o->parent) { diff --git a/src/object/sp-item.h b/src/object/sp-item.h index 2466b1a733..ffb293fe1a 100644 --- a/src/object/sp-item.h +++ b/src/object/sp-item.h @@ -172,6 +172,7 @@ public: sigc::signal _transformed_signal; Geom::PathVector get_pathvector(); + std::vector get_groups_expanded(); bool isLocked() const; void setLocked(bool lock); -- GitLab From 0f284d98986004172a5c76d5beedb14e64ee83fb Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 23 Jun 2021 23:27:24 +0200 Subject: [PATCH 054/235] Set the fracture operation to be recorded in the edit histroy. --- src/helper/NonOverlappingPathsBuilder.cpp | 24 +++++++++++++++++++---- src/helper/NonOverlappingPathsBuilder.h | 1 + src/object/object-set.cpp | 6 +----- src/object/object-set.h | 2 +- src/object/sp-item.cpp | 6 +++++- src/selection-chemistry.cpp | 4 ++-- 6 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 3eaac99565..0421258ec7 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -13,13 +13,15 @@ #include "NonOverlappingPathsBuilder.h" +#include <2geom/intersection-graph.h> #include #include #include -#include "ui/widget/canvas.h" -#include "geom-pathstroke.h" +#include #include -#include <2geom/intersection-graph.h> + +#include "geom-pathstroke.h" +#include "ui/widget/canvas.h" #include "useful-functions.h" namespace Inkscape { @@ -36,7 +38,7 @@ static void sp_selection_delete_impl(std::vector const &items, bool pro } } -void NonOverlappingPathsBuilder::build() +void NonOverlappingPathsBuilder::do_work() { // TODO It should have its own widget. // TODO (for swipe-union thing) may want to copy the style of @@ -48,6 +50,11 @@ void NonOverlappingPathsBuilder::build() return; } + // FIXME this causes a crash if the function ObjectSet::move is + // called with a dx or dy equals 0. this is because of an assertion + // in the function maybeDone. fix it later. + DocumentUndo::ScopedInsensitive scopedInsensitive(set->document()); + set_desktop_busy(desktop()); // Ideally shouldn't be converting to paths? @@ -71,6 +78,15 @@ void NonOverlappingPathsBuilder::build() unset_desktop_busy(desktop()); } +void NonOverlappingPathsBuilder::build() +{ + do_work(); + + if (set->document()) { + DocumentUndo::done(set->document(), "Fracture", INKSCAPE_ICON("path-fracture")); + } +} + SPItem* NonOverlappingPathsBuilder::get_most_item(std::function cmp) { SPItem *result = items.front(); diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index f49533edcf..aebaef37fe 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -46,6 +46,7 @@ public: private: + void do_work(); void construct_non_overlapping_pieces(); SPDesktop *desktop(); void set_parameters(); diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index e7720fbb4c..6b9bd9f27e 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -455,7 +455,7 @@ void ObjectSet::the_temporary_fix_for_the_transform_bug() // operations like union. for some reason the // attribute disappears when the result object // is moved. - move(0, 0); + move(0, 0, true); } void ObjectSet::remove_paths_with_small_area() @@ -471,10 +471,6 @@ void ObjectSet::remove_paths_with_small_area() void ObjectSet::break_into_non_overlapping_pieces(bool fill_holes, bool is_destructive, bool put_above_originals) { - if (isEmpty()) { - return; - } - NonOverlappingPathsBuilder builder(this); builder.fill_holes = fill_holes; builder.is_destructive = is_destructive; diff --git a/src/object/object-set.h b/src/object/object-set.h index e079e0cbc2..b4d3b40c42 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -459,7 +459,7 @@ public: void scale(double); void scaleScreen(double); void scaleTimes(double); - void move(double dx, double dy); + void move(double dx, double dy, bool skip_undo = false); void moveScreen(double dx, double dy); // various diff --git a/src/object/sp-item.cpp b/src/object/sp-item.cpp index 408890da48..0132a640f7 100644 --- a/src/object/sp-item.cpp +++ b/src/object/sp-item.cpp @@ -168,7 +168,11 @@ std::vector SPItem::get_groups_expanded() std::vector result; if (SPGroup *group = dynamic_cast(this)) { std::vector items; - sp_item_group_ungroup(group, items); + // FIXME why have to set the last "do_done" argument to + // false for it to not record in the edit history list + // even after setting the sensitivity of the document + // to false? + sp_item_group_ungroup(group, items, false); for (SPItem *item : items) { auto sub_result = item->get_groups_expanded(); result.insert(result.end(), sub_result.begin(), sub_result.end()); diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 520e6fbeb8..89d7fd0074 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -2330,7 +2330,7 @@ void ObjectSet::scaleTimes(double times) DocumentUndo::done(document(), _("Scale by whole factor"), INKSCAPE_ICON("tool-pointer")); } -void ObjectSet::move(double dx, double dy) +void ObjectSet::move(double dx, double dy, bool skip_undo) { if (isEmpty()) { return; @@ -2338,7 +2338,7 @@ void ObjectSet::move(double dx, double dy) moveRelative(dx, dy); - if (document()) { + if (document() && !skip_undo) { if (dx == 0) { DocumentUndo::maybeDone(document(), "selector:move:vertical", _("Move vertically"), INKSCAPE_ICON("tool-pointer")); } else if (dy == 0) { -- GitLab From 53567d544350b08a5fa0a0b7c03d17c57bcbe7f6 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 28 Jun 2021 13:34:17 +0200 Subject: [PATCH 055/235] A quick fix for the histroy regestiration. The program crashes when doing an undo if the ScopedUnsensitive is being used. just for now, it's not used, rather, each operation is not recorded. change this in the future. --- src/helper/NonOverlappingPathsBuilder.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 0421258ec7..d5fd25028e 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -53,12 +53,13 @@ void NonOverlappingPathsBuilder::do_work() // FIXME this causes a crash if the function ObjectSet::move is // called with a dx or dy equals 0. this is because of an assertion // in the function maybeDone. fix it later. - DocumentUndo::ScopedInsensitive scopedInsensitive(set->document()); + // FIXME enable this and investigate why the program crashes when undoing. +// DocumentUndo::ScopedInsensitive scopedInsensitive(set->document()); set_desktop_busy(desktop()); // Ideally shouldn't be converting to paths? - set->toCurves(); + set->toCurves(true); set->the_temporary_fix_for_the_transform_bug(); set_parameters(); -- GitLab From 931413ef0bcd33ce0e36efa042c9091176e72934 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 28 Jun 2021 19:23:12 +0200 Subject: [PATCH 056/235] Temporary fix for getting incorrect results when applying Fracture. the function for removing the paths with small areas is not correct currently, thus, removes some legit shapes in the middle (or at the end) of the breaking, thus resulting in a wrong results also, not all shapes are considered at the moment (don't know why yet). the current solution is simple, yet very slow, specially combined with the fact that the function for removing paths with small areas is disabled for now. need to figure out a better way of doing it soon. --- src/helper/NonOverlappingPathsBuilder.cpp | 75 +++++++++++++---------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index d5fd25028e..b985e1f4a6 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -138,11 +138,11 @@ class PathHelper paths = remove_lines_from_pathvec(paths); // paths = remove_loop_lines_from_pathvec(paths); // paths = remove_paths_with_small_area_from_pathvec(paths, 0.5); - double max_area = 0; - for (auto &path : paths) { - max_area = std::max(max_area, path_area_non_self_intersecting(path)); - } - paths = remove_paths_with_small_area_from_pathvec(paths, max_area/100); + // double max_area = 0; + // for (auto &path : paths) { + // max_area = std::max(max_area, path_area_non_self_intersecting(path)); + // } + // paths = remove_paths_with_small_area_from_pathvec(paths, max_area/100); } public: @@ -245,14 +245,6 @@ std::vector break_apart_non_touching(std::vector &paths) return result; } -void sort_pathhelper_vector(std::vector &vector) -{ - auto cmp = [](const PathHelper &a, const PathHelper &b) { - return sp_item_repr_compare_position_bool(a.item, b.item); - }; - std::sort(vector.begin(), vector.end(), cmp); -} - void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() { std::vector result; @@ -263,31 +255,50 @@ void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() result[i] = PathHelper(items[i]->get_pathvector(), items[i], {}, curve_length_divisor); } - // FIXME for some reason, sometimes the output is not correct unless - // the items are sorted such that the lowest item is at the beginning. - // THIS SHOULD NOT BE HERE. FIX THIS BUG. - // It doesn't happen as often. it happened with the 6 intersecting circles. - sort_pathhelper_vector(result); + // FIXME remove this variable and figure out a better + // and faster way of accessing all of the elements. + bool edited = true; + + while (edited) + { + edited = false; + for (int i = 0; i < result.size(); i++) { + for (int j = 0; j < result.size(); j++) { + + if (i == j) { continue; } + +// std::cout << "Path i (" << i << "):\n"; +// print_bounds_and_nodes_count(result[i].paths); +// +// std::cout << "Path j (" << j << "):\n"; +// print_bounds_and_nodes_count(result[j].paths); - for (int i = 0; i < result.size(); i++) { - for (int j = 0; j < result.size(); j++) { - if (i == j) { continue; } + int common_operation = result[i].get_common_operation(result[j]); + if (common_operation != -1) { +// std::cout << "Share a common operation: " << common_operation << ".\n\n"; + continue; + } - int common_operation = result[i].get_common_operation(result[j]); - if (common_operation != -1) { continue; } + auto broken = result[i].break_into_non_overlapping_pieces(result[j]); + if (broken.empty()) { +// std::cout << "They don't intersect.\n\n"; + continue; + } - auto broken = result[i].break_into_non_overlapping_pieces(result[j]); - if (broken.empty()) { continue; } + edited = true; - // the bigger index should be erased first. - int bigger_index = (i > j) ? i : j; - int smaller_index = (i > j) ? j : i; - result.erase(result.begin() + bigger_index); - result.erase(result.begin() + smaller_index); + // the bigger index should be erased first. + int bigger_index = (i > j) ? i : j; + int smaller_index = (i > j) ? j : i; + result.erase(result.begin() + bigger_index); + result.erase(result.begin() + smaller_index); - j--; // i is not being incremented in the next iteration. shouldn't be decremented. + j--; // i is not being incremented in the next iteration. shouldn't be decremented. - result.insert(result.end(), broken.begin(), broken.end()); + result.insert(result.end(), broken.begin(), broken.end()); + + std::cout << "\n"; + } } } -- GitLab From f3f3e6003b7e2f5070e7a82460191a4e718949cf Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 7 Jul 2021 23:57:10 +0200 Subject: [PATCH 057/235] Set Fracture to split non-overlapping parts after all of the calculations are finished. --- src/helper/NonOverlappingPathsBuilder.cpp | 52 +++++++++++------------ 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index b985e1f4a6..61bcafd91e 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -225,9 +225,10 @@ public: } } - std::vector break_apart_non_touching(std::vector &paths); + return result; - return break_apart_non_touching(result); +// std::vector break_apart_non_touching(std::vector &paths); +// return break_apart_non_touching(result); } }; @@ -257,15 +258,11 @@ void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() // FIXME remove this variable and figure out a better // and faster way of accessing all of the elements. - bool edited = true; - while (edited) - { - edited = false; - for (int i = 0; i < result.size(); i++) { - for (int j = 0; j < result.size(); j++) { + for (int i = 0; i < result.size(); i++) { + for (int j = 0; j < result.size(); j++) { - if (i == j) { continue; } + if (i == j) { continue; } // std::cout << "Path i (" << i << "):\n"; // print_bounds_and_nodes_count(result[i].paths); @@ -273,35 +270,34 @@ void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() // std::cout << "Path j (" << j << "):\n"; // print_bounds_and_nodes_count(result[j].paths); - int common_operation = result[i].get_common_operation(result[j]); - if (common_operation != -1) { + int common_operation = result[i].get_common_operation(result[j]); + if (common_operation != -1) { // std::cout << "Share a common operation: " << common_operation << ".\n\n"; - continue; - } + continue; + } - auto broken = result[i].break_into_non_overlapping_pieces(result[j]); - if (broken.empty()) { + auto broken = result[i].break_into_non_overlapping_pieces(result[j]); + if (broken.empty()) { // std::cout << "They don't intersect.\n\n"; - continue; - } - - edited = true; + continue; + } - // the bigger index should be erased first. - int bigger_index = (i > j) ? i : j; - int smaller_index = (i > j) ? j : i; - result.erase(result.begin() + bigger_index); - result.erase(result.begin() + smaller_index); + // the bigger index should be erased first. + int bigger_index = (i > j) ? i : j; + int smaller_index = (i > j) ? j : i; + result.erase(result.begin() + bigger_index); + result.erase(result.begin() + smaller_index); - j--; // i is not being incremented in the next iteration. shouldn't be decremented. + j--; // i is not being incremented in the next iteration. shouldn't be decremented. - result.insert(result.end(), broken.begin(), broken.end()); + result.insert(result.end(), broken.begin(), broken.end()); - std::cout << "\n"; - } + std::cout << "\n"; } } + result = break_apart_non_touching(result); + n = result.size(); result_nodes.resize(n); for (int i = 0; i < n; i++) { -- GitLab From 6af86d1cfee8aad041d6a8fb97275350779ced0f Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 7 Jul 2021 23:59:37 +0200 Subject: [PATCH 058/235] Initial implementation for a Disjoint Set. --- src/helper/useful-functions.cpp | 52 +++++++++++++++++++++++++++++++++ src/helper/useful-functions.h | 16 ++++++++++ 2 files changed, 68 insertions(+) diff --git a/src/helper/useful-functions.cpp b/src/helper/useful-functions.cpp index 9eb8c82e3f..bab09d914d 100644 --- a/src/helper/useful-functions.cpp +++ b/src/helper/useful-functions.cpp @@ -27,6 +27,58 @@ namespace Inkscape { +void DisjointSet::merge(int a, int b) +{ + int a_parent = parent_of(a); + int b_parent = parent_of(b); + + if (a_parent == b_parent) { return; } + + int a_size = size_of(a); + int b_size = size_of(b); + + if (a_size < b_size) { + parents[a] = b; + } else { + parents[b] = a; + } +} + +int DisjointSet::parent_of(int x) +{ + if (parents[x] < 0) { + return x; + } + + int parent = parents[x]; + parents[x] = parent_of(parent); + return parents[x]; +} + +int DisjointSet::size_of(int x) +{ + int parent = parent_of(x); + return -parents[parent]; +} + +int DisjointSet::size() +{ + int n = parents.size(); + std::vector is_present(n, false); + + int result = 0; + + for (int i = 0; i < n; i++) { + int parent = parent_of(i); + if (!is_present[parent]) { + is_present[parent] = true; + result++; + } + } + + return result; +} + void set_desktop_busy(SPDesktop *desktop) { if (desktop) { diff --git a/src/helper/useful-functions.h b/src/helper/useful-functions.h index 5574a64acf..5d823be9a3 100644 --- a/src/helper/useful-functions.h +++ b/src/helper/useful-functions.h @@ -26,6 +26,22 @@ namespace Inkscape { +class DisjointSet +{ + // if parents[x] is negative, then x is a parent of itself, + // and the negative number represents the size of the set. + // else if parents[x] is positive, then x has a parent, parents[x] + // might not be the top parent, thus shouldn't access parents[x] + // directly, rather, use the method "parent_of". + std::vector parents; +public: + DisjointSet(int n) { parents.resize(n, -1); } + void merge(int a, int b); + int parent_of(int x); + int size_of(int x); + int size(); +}; + void set_desktop_busy(SPDesktop *desktop); void unset_desktop_busy(SPDesktop *desktop); -- GitLab From 013329e097f7830e01471e81f0103a030f80172a Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 8 Jul 2021 00:51:27 +0200 Subject: [PATCH 059/235] break_apart_non_touching is now using Disjoint Sets. --- src/helper/useful-functions.cpp | 41 ++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/helper/useful-functions.cpp b/src/helper/useful-functions.cpp index bab09d914d..882f75ea75 100644 --- a/src/helper/useful-functions.cpp +++ b/src/helper/useful-functions.cpp @@ -214,28 +214,41 @@ bool is_intersecting(const Path1 &a, const Path2 &b) { std::vector break_apart_non_touching(const Geom::PathVector &paths) { - std::vector visited(paths.size()); - std::vector result; - int n = paths.size(); + + DisjointSet set(n); + std::vector visited(n); + for (int i = n - 1; i >= 0; i--) { - if (visited[i]) { - continue; - } - result.emplace_back(paths[i]); + + if (visited[i]) { continue; } visited[i] = true; - auto &curr = result.back(); + for (int j = 0; j < n; j++) { - if (visited[j]) { - continue; - } - if (is_intersecting(curr, paths[j])) { - visited[j] = true; - curr.push_back(paths[j]); + if (visited[j]) { continue; } + if (is_intersecting(paths[i], paths[j])) { + set.merge(i, j); } } } + int set_size = set.size(); // this is O(N). + std::map> map; + for (int i = 0; i < n; i++) { + int parent = set.parent_of(i); + map[parent].push_back(i); + } + + int i = 0; + std::vector result(set_size); + for (auto &paths_idx : map) { + for (auto path_idx : paths_idx.second) { + auto &path = paths[path_idx]; + result[i].push_back(path); + } + i++; + } + return result; } -- GitLab From ea324400288305c6fc1ef3db717b9ae10515fd89 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 8 Jul 2021 02:01:54 +0200 Subject: [PATCH 060/235] Initial implementation for the "Flatten" operation. --- src/helper/NonOverlappingPathsBuilder.cpp | 37 ++++++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 61bcafd91e..4ccbdba6b3 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -187,6 +187,35 @@ public: clean(); } + std::vector flatten(const PathHelper &path) + { + const PathHelper *top = &path; + const PathHelper *bottom = this; + + if (sp_item_repr_compare_position_bool(top->item, bottom->item)) { + std::swap(top, bottom); + } + + auto bottom_diff_paths = sp_pathvector_boolop(top->paths, bottom->paths, bool_op_diff, fill_nonZero, fill_nonZero); + PathHelper bottom_diff(bottom_diff_paths, bottom->item, bottom->operations, curve_length_divisor); + + std::vector result = {*top, bottom_diff}; + result[0].prepare_for_current_operation(); + result[1].prepare_for_current_operation(); + + for (int i = 0; i < result.size(); i++) { + result[i].prepare_for_current_operation(); + if (result[i].paths.empty()) { + result.erase(result.begin() + i); + i--; + } + } + + counter++; + + return result; + } + std::vector break_into_non_overlapping_pieces(const PathHelper &path) { // FIXME you can't rely on the current area calculation method. make it work with curves. @@ -198,7 +227,6 @@ public: // auto intersection_paths = pig.getIntersection(); auto intersection_item = sp_item_repr_compare_position_bool(path.item, this->item) ? this->item : path.item; PathHelper intersection(intersection_paths, intersection_item, operations, path.operations, curve_length_divisor); - intersection.prepare_for_current_operation(); if (intersection.paths.empty()) { return {}; @@ -207,24 +235,23 @@ public: // auto diff1_paths = pig.getAminusB(); auto diff1_paths = sp_pathvector_boolop(paths, path.paths, bool_op_diff, fill_nonZero, fill_nonZero); PathHelper diff1(diff1_paths, path.item, path.operations, curve_length_divisor); - diff1.prepare_for_current_operation(); // auto diff2_paths = pig.getBminusA(); auto diff2_paths = sp_pathvector_boolop(path.paths, paths, bool_op_diff, fill_nonZero, fill_nonZero); PathHelper diff2(diff2_paths, item, operations, curve_length_divisor); - diff2.prepare_for_current_operation(); - - counter++; std::vector result = {intersection, diff1, diff2}; for (int i = 0; i < result.size(); i++) { + result[i].prepare_for_current_operation(); if (result[i].paths.empty()) { result.erase(result.begin() + i); i--; } } + counter++; + return result; // std::vector break_apart_non_touching(std::vector &paths); -- GitLab From 26219bc957f1af3a7f2b5d4cb9f00bc971285a3b Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 8 Jul 2021 17:19:53 +0200 Subject: [PATCH 061/235] Added a menu item for the "Flatten" operation. --- .../Tango/scalable/actions/path-flatten.svg | 96 ++++++++ .../hicolor/scalable/actions/path-flatten.svg | 96 ++++++++ .../hicolor/symbolic/actions/path-flatten.svg | 96 ++++++++ .../symbolic/actions/path-flatten.svg | 96 ++++++++ share/keys/inkscape.xml | 1 + share/ui/menu-path.ui | 7 +- src/actions/actions-selection.cpp | 11 + src/helper/NonOverlappingPathsBuilder.cpp | 220 ++++++++---------- src/helper/NonOverlappingPathsBuilder.h | 50 +++- src/object/object-set.cpp | 10 +- src/object/object-set.h | 1 + 11 files changed, 553 insertions(+), 131 deletions(-) create mode 100644 share/icons/Tango/scalable/actions/path-flatten.svg create mode 100644 share/icons/hicolor/scalable/actions/path-flatten.svg create mode 100644 share/icons/hicolor/symbolic/actions/path-flatten.svg create mode 100644 share/icons/multicolor/symbolic/actions/path-flatten.svg diff --git a/share/icons/Tango/scalable/actions/path-flatten.svg b/share/icons/Tango/scalable/actions/path-flatten.svg new file mode 100644 index 0000000000..bbb2a098b4 --- /dev/null +++ b/share/icons/Tango/scalable/actions/path-flatten.svg @@ -0,0 +1,96 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/scalable/actions/path-flatten.svg b/share/icons/hicolor/scalable/actions/path-flatten.svg new file mode 100644 index 0000000000..bbb2a098b4 --- /dev/null +++ b/share/icons/hicolor/scalable/actions/path-flatten.svg @@ -0,0 +1,96 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/symbolic/actions/path-flatten.svg b/share/icons/hicolor/symbolic/actions/path-flatten.svg new file mode 100644 index 0000000000..bbb2a098b4 --- /dev/null +++ b/share/icons/hicolor/symbolic/actions/path-flatten.svg @@ -0,0 +1,96 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/share/icons/multicolor/symbolic/actions/path-flatten.svg b/share/icons/multicolor/symbolic/actions/path-flatten.svg new file mode 100644 index 0000000000..bbb2a098b4 --- /dev/null +++ b/share/icons/multicolor/symbolic/actions/path-flatten.svg @@ -0,0 +1,96 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/share/keys/inkscape.xml b/share/keys/inkscape.xml index 3640553cc9..342d873cb4 100644 --- a/share/keys/inkscape.xml +++ b/share/keys/inkscape.xml @@ -556,6 +556,7 @@ override) the bindings in the main default.xml. + diff --git a/share/ui/menu-path.ui b/share/ui/menu-path.ui index a75e157042..96bb529423 100644 --- a/share/ui/menu-path.ui +++ b/share/ui/menu-path.ui @@ -53,6 +53,11 @@ app.select-path-cut path-cut + + Flatte_n + app.select-path-flatten + path-flatten + _Fracture app.select-path-fracture @@ -72,7 +77,7 @@ path-break-apart - Split _Non-Intersecting Paths + Split Non-Intersecting Paths app.select-path-split-non-intersecting path-split-non-intersecting diff --git a/src/actions/actions-selection.cpp b/src/actions/actions-selection.cpp index 2937aa2205..5f2fc5d285 100644 --- a/src/actions/actions-selection.cpp +++ b/src/actions/actions-selection.cpp @@ -287,6 +287,15 @@ select_path_cut(InkscapeApplication *app) selection->pathSlice(); } +void +select_path_flatten(InkscapeApplication *app) +{ + auto selection = app->get_active_selection(); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); + selection->flatten(); +} + void select_path_fracture(InkscapeApplication *app) { @@ -353,6 +362,7 @@ std::vector> raw_data_selection = {"app.select-path-exclusion", N_("Exclusion"), "Select", N_("Create exclusive OR of selected paths (those parts that belong to only one path)")}, {"app.select-path-division", N_("Division"), "Select", N_("Cut the bottom path into pieces")}, {"app.select-path-cut", N_("Cut Path"), "Select", N_("Cut the bottom path's stroke into pieces, removing fill")}, + {"app.select-path-flatten", N_("Flatten"), "Select", N_("Remove any hidden part part of the selection (has an item on top of it)")}, {"app.select-path-fracture", N_("Fracture"), "Select", N_("Break the selected paths into non-overlapping (fractured) paths")}, {"app.select-path-combine", N_("Combine"), "Select", N_("Combine several paths into one")}, {"app.select-path-break-apart", N_("Break Apart"), "Select", N_("Break selected paths into subpaths")}, @@ -385,6 +395,7 @@ add_actions_selection(InkscapeApplication* app) gapp->add_action( "select-path-exclusion", sigc::bind(sigc::ptr_fun(&select_path_exclusion), app)); gapp->add_action( "select-path-division", sigc::bind(sigc::ptr_fun(&select_path_division), app)); gapp->add_action( "select-path-cut", sigc::bind(sigc::ptr_fun(&select_path_cut), app)); + gapp->add_action( "select-path-flatten", sigc::bind(sigc::ptr_fun(&select_path_flatten), app)); gapp->add_action( "select-path-fracture", sigc::bind(sigc::ptr_fun(&select_path_fracture), app)); gapp->add_action( "select-path-combine", sigc::bind(sigc::ptr_fun(&select_path_combine), app)); gapp->add_action( "select-path-break-apart", sigc::bind(sigc::ptr_fun(&select_path_break_apart), app)); diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 4ccbdba6b3..81e4bc4ca8 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -24,8 +24,11 @@ #include "ui/widget/canvas.h" #include "useful-functions.h" + namespace Inkscape { +using PathHelper = NonOverlappingPathsBuilder::PathHelper; + // TODO this is a duplicate code from selecction-chemistry.cpp. refactor it. static void sp_selection_delete_impl(std::vector const &items, bool propagate = true, bool propagate_descendants = true) { @@ -38,7 +41,7 @@ static void sp_selection_delete_impl(std::vector const &items, bool pro } } -void NonOverlappingPathsBuilder::do_work() +void NonOverlappingPathsBuilder::do_work(PathHelperOperation operation) { // TODO It should have its own widget. // TODO (for swipe-union thing) may want to copy the style of @@ -63,7 +66,7 @@ void NonOverlappingPathsBuilder::do_work() set->the_temporary_fix_for_the_transform_bug(); set_parameters(); - construct_non_overlapping_pieces(); + construct_operation_shapes(operation); if (is_destructive) { sp_selection_delete_impl(items); @@ -79,15 +82,26 @@ void NonOverlappingPathsBuilder::do_work() unset_desktop_busy(desktop()); } -void NonOverlappingPathsBuilder::build() +void NonOverlappingPathsBuilder::fracture() { - do_work(); + auto operation = [](PathHelper& a, PathHelper& b) { return a.fracture(b); }; + do_work(operation); if (set->document()) { DocumentUndo::done(set->document(), "Fracture", INKSCAPE_ICON("path-fracture")); } } +void NonOverlappingPathsBuilder::flatten() +{ + auto operation = [](PathHelper& a, PathHelper& b) { return a.flatten(b); }; + do_work(operation); + + if (set->document()) { + DocumentUndo::done(set->document(), "Flatten", INKSCAPE_ICON("path-flatten")); + } +} + SPItem* NonOverlappingPathsBuilder::get_most_item(std::function cmp) { SPItem *result = items.front(); @@ -129,135 +143,104 @@ SPDesktop *NonOverlappingPathsBuilder::desktop() return set->desktop(); } -class PathHelper +void PathHelper::clean() { - static int counter; - - void clean() - { - paths = remove_lines_from_pathvec(paths); - // paths = remove_loop_lines_from_pathvec(paths); - // paths = remove_paths_with_small_area_from_pathvec(paths, 0.5); - // double max_area = 0; - // for (auto &path : paths) { - // max_area = std::max(max_area, path_area_non_self_intersecting(path)); - // } - // paths = remove_paths_with_small_area_from_pathvec(paths, max_area/100); - } - -public: - - Geom::PathVector paths; - SPItem *item; - std::set operations; - - // FIXME!! find a better way of doing this... this value is copied for each object... - double curve_length_divisor; - - PathHelper() {} - - PathHelper(Geom::PathVector paths, SPItem *item, std::set operations, double curve_length_divisor) - : paths(std::move(paths)) - , item(item) - , operations(std::move(operations)) - , curve_length_divisor(curve_length_divisor) - {} - - PathHelper(Geom::PathVector paths, SPItem *item, std::set operations1, const std::set &operations2, double curve_length_divisor) - : PathHelper(paths, item, operations1, curve_length_divisor) - { - operations.insert(operations2.begin(), operations2.end()); - } - - bool part_of_operation(int operation) { return operations.find(operation) != operations.end(); } + paths = remove_lines_from_pathvec(paths); + // paths = remove_loop_lines_from_pathvec(paths); + // paths = remove_paths_with_small_area_from_pathvec(paths, 0.5); + // double max_area = 0; + // for (auto &path : paths) { + // max_area = std::max(max_area, path_area_non_self_intersecting(path)); + // } + // paths = remove_paths_with_small_area_from_pathvec(paths, max_area/100); +} - int get_common_operation(const PathHelper &path) - { - for (auto operation : path.operations) { - if (part_of_operation(operation)) { - return operation; - } +int PathHelper::get_common_operation(const PathHelper &path) +{ + for (auto operation : path.operations) { + if (part_of_operation(operation)) { + return operation; } - return -1; } + return -1; +} - void prepare_for_current_operation() - { - operations.insert(counter); - clean(); - } +void PathHelper::prepare_for_current_operation() +{ + operations.insert(counter); + clean(); +} - std::vector flatten(const PathHelper &path) - { - const PathHelper *top = &path; - const PathHelper *bottom = this; +std::vector PathHelper::flatten(const PathHelper &path) +{ + const PathHelper *top = &path; + const PathHelper *bottom = this; - if (sp_item_repr_compare_position_bool(top->item, bottom->item)) { - std::swap(top, bottom); - } + if (sp_item_repr_compare_position_bool(top->item, bottom->item)) { + std::swap(top, bottom); + } - auto bottom_diff_paths = sp_pathvector_boolop(top->paths, bottom->paths, bool_op_diff, fill_nonZero, fill_nonZero); - PathHelper bottom_diff(bottom_diff_paths, bottom->item, bottom->operations, curve_length_divisor); + auto bottom_diff_paths = sp_pathvector_boolop(top->paths, bottom->paths, bool_op_diff, fill_nonZero, fill_nonZero); + PathHelper bottom_diff(bottom_diff_paths, bottom->item, bottom->operations, curve_length_divisor); - std::vector result = {*top, bottom_diff}; - result[0].prepare_for_current_operation(); - result[1].prepare_for_current_operation(); + std::vector result = {*top, bottom_diff}; + result[0].prepare_for_current_operation(); + result[1].prepare_for_current_operation(); - for (int i = 0; i < result.size(); i++) { - result[i].prepare_for_current_operation(); - if (result[i].paths.empty()) { - result.erase(result.begin() + i); - i--; - } + for (int i = 0; i < result.size(); i++) { + result[i].prepare_for_current_operation(); + if (result[i].paths.empty()) { + result.erase(result.begin() + i); + i--; } + } - counter++; + counter++; - return result; - } + return result; +} - std::vector break_into_non_overlapping_pieces(const PathHelper &path) - { - // FIXME you can't rely on the current area calculation method. make it work with curves. - // assert(get_common_operation(path) == -1); +std::vector PathHelper::fracture(const PathHelper &path) +{ + // FIXME you can't rely on the current area calculation method. make it work with curves. + // assert(get_common_operation(path) == -1); - // Geom::PathIntersectionGraph pig(paths, path.paths, Geom::EPSILON); + // Geom::PathIntersectionGraph pig(paths, path.paths, Geom::EPSILON); - auto intersection_paths = sp_pathvector_boolop(paths, path.paths, bool_op_inters, fill_nonZero, fill_nonZero); - // auto intersection_paths = pig.getIntersection(); - auto intersection_item = sp_item_repr_compare_position_bool(path.item, this->item) ? this->item : path.item; - PathHelper intersection(intersection_paths, intersection_item, operations, path.operations, curve_length_divisor); + auto intersection_paths = sp_pathvector_boolop(paths, path.paths, bool_op_inters, fill_nonZero, fill_nonZero); + // auto intersection_paths = pig.getIntersection(); + auto intersection_item = sp_item_repr_compare_position_bool(path.item, this->item) ? this->item : path.item; + PathHelper intersection(intersection_paths, intersection_item, operations, path.operations, curve_length_divisor); - if (intersection.paths.empty()) { - return {}; - } + if (intersection.paths.empty()) { + return {}; + } - // auto diff1_paths = pig.getAminusB(); - auto diff1_paths = sp_pathvector_boolop(paths, path.paths, bool_op_diff, fill_nonZero, fill_nonZero); - PathHelper diff1(diff1_paths, path.item, path.operations, curve_length_divisor); + // auto diff1_paths = pig.getAminusB(); + auto diff1_paths = sp_pathvector_boolop(paths, path.paths, bool_op_diff, fill_nonZero, fill_nonZero); + PathHelper diff1(diff1_paths, path.item, path.operations, curve_length_divisor); - // auto diff2_paths = pig.getBminusA(); - auto diff2_paths = sp_pathvector_boolop(path.paths, paths, bool_op_diff, fill_nonZero, fill_nonZero); - PathHelper diff2(diff2_paths, item, operations, curve_length_divisor); + // auto diff2_paths = pig.getBminusA(); + auto diff2_paths = sp_pathvector_boolop(path.paths, paths, bool_op_diff, fill_nonZero, fill_nonZero); + PathHelper diff2(diff2_paths, item, operations, curve_length_divisor); - std::vector result = {intersection, diff1, diff2}; + std::vector result = {intersection, diff1, diff2}; - for (int i = 0; i < result.size(); i++) { - result[i].prepare_for_current_operation(); - if (result[i].paths.empty()) { - result.erase(result.begin() + i); - i--; - } + for (int i = 0; i < result.size(); i++) { + result[i].prepare_for_current_operation(); + if (result[i].paths.empty()) { + result.erase(result.begin() + i); + i--; } + } - counter++; + counter++; - return result; + return result; // std::vector break_apart_non_touching(std::vector &paths); // return break_apart_non_touching(result); - } -}; +} int PathHelper::counter = 0; @@ -273,7 +256,7 @@ std::vector break_apart_non_touching(std::vector &paths) return result; } -void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() +void NonOverlappingPathsBuilder::construct_operation_shapes(PathHelperOperation operation) { std::vector result; int n = items.size(); @@ -283,31 +266,16 @@ void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() result[i] = PathHelper(items[i]->get_pathvector(), items[i], {}, curve_length_divisor); } - // FIXME remove this variable and figure out a better - // and faster way of accessing all of the elements. - for (int i = 0; i < result.size(); i++) { for (int j = 0; j < result.size(); j++) { if (i == j) { continue; } -// std::cout << "Path i (" << i << "):\n"; -// print_bounds_and_nodes_count(result[i].paths); -// -// std::cout << "Path j (" << j << "):\n"; -// print_bounds_and_nodes_count(result[j].paths); - int common_operation = result[i].get_common_operation(result[j]); - if (common_operation != -1) { -// std::cout << "Share a common operation: " << common_operation << ".\n\n"; - continue; - } + if (common_operation != -1) { continue; } - auto broken = result[i].break_into_non_overlapping_pieces(result[j]); - if (broken.empty()) { -// std::cout << "They don't intersect.\n\n"; - continue; - } + auto broken = operation(result[i], result[j]); + if (broken.empty()) { continue; } // the bigger index should be erased first. int bigger_index = (i > j) ? i : j; @@ -332,4 +300,4 @@ void NonOverlappingPathsBuilder::construct_non_overlapping_pieces() } } -} \ No newline at end of file +}; diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index aebaef37fe..949c3cc304 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -41,16 +41,60 @@ private: public: + class PathHelper; NonOverlappingPathsBuilder(ObjectSet *set) : set(set) {} - void build(); + void fracture(); + void flatten(); private: - void do_work(); - void construct_non_overlapping_pieces(); + using PathHelperOperation = std::function(PathHelper&, PathHelper&)>; + + void do_work(PathHelperOperation operation); + void construct_operation_shapes(PathHelperOperation operation); SPDesktop *desktop(); void set_parameters(); SPItem* get_most_item(std::function cmp); + +public: + + class PathHelper + { + static int counter; + + void clean(); + + public: + + Geom::PathVector paths; + SPItem *item; + std::set operations; + + // FIXME!! find a better way of doing this... this value is copied for each object... + double curve_length_divisor; + + PathHelper() {} + + PathHelper(Geom::PathVector paths, SPItem *item, std::set operations, double curve_length_divisor) + : paths(std::move(paths)) + , item(item) + , operations(std::move(operations)) + , curve_length_divisor(curve_length_divisor) + {} + + PathHelper(Geom::PathVector paths, SPItem *item, std::set operations1, const std::set &operations2, double curve_length_divisor) + : PathHelper(paths, item, operations1, curve_length_divisor) + { + operations.insert(operations2.begin(), operations2.end()); + } + + bool part_of_operation(int operation) { return operations.find(operation) != operations.end(); } + + int get_common_operation(const PathHelper &path); + void prepare_for_current_operation(); + std::vector flatten(const PathHelper &path); + std::vector fracture(const PathHelper &path); + }; }; } \ No newline at end of file diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 6b9bd9f27e..43dd1e57dd 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -475,7 +475,15 @@ void ObjectSet::break_into_non_overlapping_pieces(bool fill_holes, bool is_destr builder.fill_holes = fill_holes; builder.is_destructive = is_destructive; builder.put_above_originals = put_above_originals; - builder.build(); + builder.fracture(); +} + +void ObjectSet::flatten(bool is_destructive, bool put_above_originals) +{ + NonOverlappingPathsBuilder builder(this); + builder.is_destructive = is_destructive; + builder.put_above_originals = put_above_originals; + builder.flatten(); } void ObjectSet::splitNonIntersecting() diff --git a/src/object/object-set.h b/src/object/object-set.h index b4d3b40c42..f25b7f8401 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -472,6 +472,7 @@ public: void the_temporary_fix_for_the_transform_bug(); void break_into_non_overlapping_pieces(bool fill_holes = false, bool is_destructive = true, bool put_above_originals = false); + void flatten(bool is_destructive = true, bool put_above_originals = false); void splitNonIntersecting(); protected: -- GitLab From 192e6e5ee236f542e83a0c72ec5aa9a13ffdc0cc Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 9 Jul 2021 01:43:46 +0200 Subject: [PATCH 062/235] Fixed the bug where one last (or possibly more?) shape/s were not participating in the operations. --- src/helper/NonOverlappingPathsBuilder.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 81e4bc4ca8..8f5c4e744f 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -278,16 +278,18 @@ void NonOverlappingPathsBuilder::construct_operation_shapes(PathHelperOperation if (broken.empty()) { continue; } // the bigger index should be erased first. - int bigger_index = (i > j) ? i : j; - int smaller_index = (i > j) ? j : i; + int &bigger_index = (i > j) ? i : j; + int &smaller_index = (i > j) ? j : i; result.erase(result.begin() + bigger_index); result.erase(result.begin() + smaller_index); - j--; // i is not being incremented in the next iteration. shouldn't be decremented. + bigger_index--; // bigger index will be moved by 1 by the smaller_index. - result.insert(result.end(), broken.begin(), broken.end()); + // This is to cancel the next incrementation of j. + // i is not being incremented in the next iteration. shouldn't be decremented. + j--; - std::cout << "\n"; + result.insert(result.end(), broken.begin(), broken.end()); } } -- GitLab From 6f4621a0994ebbc7319803bdc7ec7fcdbc292141 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 9 Jul 2021 05:05:58 +0200 Subject: [PATCH 063/235] An improvement for the shapes constructing method. --- src/helper/NonOverlappingPathsBuilder.cpp | 22 +++++++++++++++------- src/helper/useful-functions.h | 7 +++++++ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 8f5c4e744f..f0cfe83407 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -118,6 +118,7 @@ void NonOverlappingPathsBuilder::set_parameters() auto _items = set->items(); items = std::vector(_items.begin(), _items.end()); + // FIXME this doesn't work properly. fix it later. if (put_above_originals) { auto cmp = [](SPItem *a, SPItem *b) { return sp_item_repr_compare_position_bool(a, b); }; after = get_most_item(cmp)->getRepr(); @@ -145,6 +146,10 @@ SPDesktop *NonOverlappingPathsBuilder::desktop() void PathHelper::clean() { + // FIXME removing the shapes with only 2 nodes + // causes shapes that are not lines to be removed. + // also, not removing the lines causes some shapes + // to be removed when using fracture... investigate why. paths = remove_lines_from_pathvec(paths); // paths = remove_loop_lines_from_pathvec(paths); // paths = remove_paths_with_small_area_from_pathvec(paths, 0.5); @@ -278,18 +283,21 @@ void NonOverlappingPathsBuilder::construct_operation_shapes(PathHelperOperation if (broken.empty()) { continue; } // the bigger index should be erased first. - int &bigger_index = (i > j) ? i : j; - int &smaller_index = (i > j) ? j : i; + int bigger_index = (i > j) ? i : j; + int smaller_index = (i > j) ? j : i; + + // TODO for some reason, using erase is faster. + // investigate a bit more about that. + // remove_by_swapping_with_back(result, bigger_index); + // remove_by_swapping_with_back(result, smaller_index); result.erase(result.begin() + bigger_index); result.erase(result.begin() + smaller_index); - bigger_index--; // bigger index will be moved by 1 by the smaller_index. + result.insert(result.end(), broken.begin(), broken.end()); - // This is to cancel the next incrementation of j. - // i is not being incremented in the next iteration. shouldn't be decremented. - j--; + i--; // to cancel the next incrementation. - result.insert(result.end(), broken.begin(), broken.end()); + break; } } diff --git a/src/helper/useful-functions.h b/src/helper/useful-functions.h index 5d823be9a3..26bd111017 100644 --- a/src/helper/useful-functions.h +++ b/src/helper/useful-functions.h @@ -66,4 +66,11 @@ XML::Node *draw_on_canvas(const Geom::PathVector &path, const SPItem *to_copy_fr void print_bounds_and_nodes_count(const Geom::PathVector &path); std::string point_coords(const Geom::Point &point, int precision = 10); +template +void remove_by_swapping_with_back(std::vector& vector, int index) +{ + std::swap(vector[index], vector.back()); + vector.pop_back(); +} + } \ No newline at end of file -- GitLab From bfd558451c26fc3ded41b365655f43564a7d157d Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 9 Jul 2021 15:28:55 +0200 Subject: [PATCH 064/235] Commented out a line that was causing a problem for mac builds. This should be investigated more in the future. --- src/actions/actions-canvas-mode.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/actions/actions-canvas-mode.cpp b/src/actions/actions-canvas-mode.cpp index c03190e090..4934b5e67a 100644 --- a/src/actions/actions-canvas-mode.cpp +++ b/src/actions/actions-canvas-mode.cpp @@ -588,8 +588,10 @@ canvas_interface_mode(int value, InkscapeWindow *win) Inkscape::UI::UXManager::getInstance()->setTask(dt, value); #ifdef GDK_WINDOWING_QUARTZ + // TODO uncomment this or figure out what to do with it. + // this is just to be able to build successfuly for mac. // call later, crashes during startup if called directly - g_idle_add(sync_menubar, nullptr); + // g_idle_add(sync_menubar, nullptr); #endif // Message FIXME having some error -- GitLab From b6e250dc83d31f24f48ed3a914eda16d6abf0ae0 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 15 Jul 2021 05:49:36 +0200 Subject: [PATCH 065/235] Changed nullptr to "" in ObjectSet::toMarker. This is temporary until I figure out the right way of doing this. I did this one only so that all tests in object-set-test.cpp pass. --- src/selection-chemistry.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 89d7fd0074..24e280950c 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -3108,7 +3108,7 @@ void ObjectSet::toMarker(bool apply) - DocumentUndo::done(doc, _("Objects to marker"), nullptr); + DocumentUndo::done(doc, _("Objects to marker"), ""); } static void sp_selection_to_guides_recursive(SPItem *item, bool wholegroups) { -- GitLab From 94d3d5f179b11edb04db3e780aec6ce2e468db08 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 15 Jul 2021 06:01:21 +0200 Subject: [PATCH 066/235] changed all nullptr arguments to "" in for the DocumentUndo::done method in selection-chemistry.cpp. --- src/selection-chemistry.cpp | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 24e280950c..d1a7b4055a 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -638,7 +638,7 @@ void sp_edit_clear_all(Inkscape::Selection *selection) item->deleteObject(); } - DocumentUndo::done(doc, _("Delete all"), nullptr); + DocumentUndo::done(doc, _("Delete all"), ""); } /* @@ -1332,7 +1332,7 @@ void ObjectSet::pastePathEffect() { Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); if (cm->pastePathEffect(this)) { - DocumentUndo::done(document(), _("Paste live path effect"), nullptr); + DocumentUndo::done(document(), _("Paste live path effect"), ""); } } @@ -1364,7 +1364,7 @@ void ObjectSet::removeLPE() } if (document()) { - DocumentUndo::done(document(), _("Remove live path effect"), nullptr); + DocumentUndo::done(document(), _("Remove live path effect"), ""); } } @@ -1389,7 +1389,7 @@ void ObjectSet::removeFilter() set_active_tool (d, get_active_tool(d)); } if (document()) { - DocumentUndo::done(document(), _("Remove filter"), nullptr); + DocumentUndo::done(document(), _("Remove filter"), ""); } } @@ -1797,7 +1797,7 @@ void ObjectSet::removeTransform() } if (document()) { - DocumentUndo::done(document(), _("Remove transform"), nullptr); + DocumentUndo::done(document(), _("Remove transform"), ""); } } @@ -3152,7 +3152,7 @@ void ObjectSet::toGuides() sp_selection_delete_impl(items_); } - DocumentUndo::done(doc, _("Objects to guides"), nullptr); + DocumentUndo::done(doc, _("Objects to guides"), ""); } /* @@ -3318,7 +3318,7 @@ void ObjectSet::toSymbol() // Clean up Inkscape::GC::release(symbol_repr); - DocumentUndo::done(doc, _("Group to symbol"), nullptr); + DocumentUndo::done(doc, _("Group to symbol"), ""); } /* @@ -3335,7 +3335,7 @@ void ObjectSet::unSymbol() } } } - DocumentUndo::done(document(), _("unSymbol all selected symbols"), nullptr); + DocumentUndo::done(document(), _("unSymbol all selected symbols"), ""); } void ObjectSet::tile(bool apply) @@ -3435,7 +3435,7 @@ void ObjectSet::tile(bool apply) } - DocumentUndo::done(doc, _("Objects to pattern"), nullptr); + DocumentUndo::done(doc, _("Objects to pattern"), ""); } void ObjectSet::untile() @@ -3509,7 +3509,7 @@ void ObjectSet::untile() if(desktop()) desktop()->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("No pattern fills in the selection.")); } else { - DocumentUndo::done(document(), _("Pattern to objects"), nullptr); + DocumentUndo::done(document(), _("Pattern to objects"), ""); setList(new_select); } } @@ -3787,7 +3787,7 @@ void ObjectSet::setClipGroup() Inkscape::GC::release(clone); set(outer); - DocumentUndo::done(doc, _("Create Clip Group"), nullptr); + DocumentUndo::done(doc, _("Create Clip Group"), ""); } /** @@ -3982,9 +3982,9 @@ void ObjectSet::setClipGroup() addList(items_to_select); if (!skip_undo) { if (apply_clip_path) { - DocumentUndo::done(doc, _("Set clipping path"), nullptr); + DocumentUndo::done(doc, _("Set clipping path"), ""); } else { - DocumentUndo::done(doc, _("Set mask"), nullptr); + DocumentUndo::done(doc, _("Set mask"), ""); } } } @@ -4115,9 +4115,9 @@ void ObjectSet::unsetMask(const bool apply_clip_path, const bool skip_undo) { addList(items_to_select); if (!skip_undo) { if (apply_clip_path) { - DocumentUndo::done(doc, _("Release clipping path"), nullptr); + DocumentUndo::done(doc, _("Release clipping path"), ""); } else { - DocumentUndo::done(doc, _("Release mask"), nullptr); + DocumentUndo::done(doc, _("Release mask"), ""); } } } @@ -4140,7 +4140,7 @@ bool ObjectSet::fitCanvas(bool with_margins, bool skip_undo) if (bbox) { document()->fitToRect(*bbox, with_margins); if(!skip_undo) - DocumentUndo::done(document(), _("Fit Page to Selection"), nullptr); + DocumentUndo::done(document(), _("Fit Page to Selection"), ""); return true; } else { return false; @@ -4216,7 +4216,7 @@ void ObjectSet::swapFillStroke() sp_repr_css_attr_unref (css); } - DocumentUndo::done(document(), _("Swap fill and stroke of an object"), nullptr); + DocumentUndo::done(document(), _("Swap fill and stroke of an object"), ""); } /** @@ -4290,7 +4290,7 @@ void ObjectSet::fillBetweenMany() clear(); add(fillRepr); - DocumentUndo::done(doc, _("Create linked fill object between paths"), nullptr); + DocumentUndo::done(doc, _("Create linked fill object between paths"), ""); } /** @@ -4317,7 +4317,7 @@ void fit_canvas_to_drawing(SPDesktop *desktop) { if (fit_canvas_to_drawing(desktop->getDocument())) { - DocumentUndo::done(desktop->getDocument(), _("Fit Page to Drawing"), nullptr); + DocumentUndo::done(desktop->getDocument(), _("Fit Page to Drawing"), ""); } } @@ -4337,7 +4337,7 @@ void fit_canvas_to_selection_or_drawing(SPDesktop *desktop) { ? fit_canvas_to_drawing(doc, true) : desktop->selection->fitCanvas(true,true)); if (changed) { - DocumentUndo::done(desktop->getDocument(), _("Fit Page to Selection or Drawing"), nullptr); + DocumentUndo::done(desktop->getDocument(), _("Fit Page to Selection or Drawing"), ""); } }; -- GitLab From 596cc2f8696f66d4e8f9b14861678eb2de2b9dcb Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 15 Jul 2021 15:00:52 +0200 Subject: [PATCH 067/235] Added a missing file in POTFILES.src.in. --- po/POTFILES.src.in | 1 + 1 file changed, 1 insertion(+) diff --git a/po/POTFILES.src.in b/po/POTFILES.src.in index a2328f3aa7..7d4e39a170 100644 --- a/po/POTFILES.src.in +++ b/po/POTFILES.src.in @@ -8,6 +8,7 @@ ../src/actions/actions-canvas-snapping.cpp ../src/actions/actions-canvas-transform.cpp ../src/actions/actions-dialogs.cpp +../src/actions/actions-doc-file.cpp ../src/actions/actions-edit.cpp ../src/actions/actions-extra-data.cpp ../src/actions/actions-file.cpp -- GitLab From 6c707ec585eb0847674229e72891e95e62beaa15 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 15 Jul 2021 03:22:09 +0200 Subject: [PATCH 068/235] Duplicated the selector tool in the toolbox. Still didn't duplicate the toolbar and the preferences. --- .../Tango/scalable/actions/tool-builder.svg | 169 +++ .../hicolor/scalable/actions/tool-builder.svg | 31 + share/keys/inkscape.xml | 1 + share/ui/toolbar-tool.ui | 10 + src/actions/actions-tools.cpp | 3 + src/actions/actions-tools.h | 1 + src/ui/CMakeLists.txt | 2 + src/ui/dialog/inkscape-preferences.h | 2 + src/ui/tool-factory.cpp | 3 + src/ui/tools/builder-tool.cpp | 1132 +++++++++++++++++ src/ui/tools/builder-tool.h | 57 + src/verbs.cpp | 5 + src/verbs.h | 1 + src/widgets/toolbox.cpp | 1 + 14 files changed, 1418 insertions(+) create mode 100644 share/icons/Tango/scalable/actions/tool-builder.svg create mode 100644 share/icons/hicolor/scalable/actions/tool-builder.svg create mode 100644 src/ui/tools/builder-tool.cpp create mode 100644 src/ui/tools/builder-tool.h diff --git a/share/icons/Tango/scalable/actions/tool-builder.svg b/share/icons/Tango/scalable/actions/tool-builder.svg new file mode 100644 index 0000000000..c95560c287 --- /dev/null +++ b/share/icons/Tango/scalable/actions/tool-builder.svg @@ -0,0 +1,169 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/scalable/actions/tool-builder.svg b/share/icons/hicolor/scalable/actions/tool-builder.svg new file mode 100644 index 0000000000..c402bef553 --- /dev/null +++ b/share/icons/hicolor/scalable/actions/tool-builder.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/share/keys/inkscape.xml b/share/keys/inkscape.xml index 342d873cb4..49ca7073de 100644 --- a/share/keys/inkscape.xml +++ b/share/keys/inkscape.xml @@ -438,6 +438,7 @@ override) the bindings in the main default.xml. + diff --git a/share/ui/toolbar-tool.ui b/share/ui/toolbar-tool.ui index 2b3825689e..f83b0db84e 100644 --- a/share/ui/toolbar-tool.ui +++ b/share/ui/toolbar-tool.ui @@ -17,6 +17,16 @@ Select
+ + + True + win.tool-switch + 'Builder' + tool-builder + Builder + ToolGroup + + True diff --git a/src/actions/actions-tools.cpp b/src/actions/actions-tools.cpp index 18044eadf5..9668c0e838 100644 --- a/src/actions/actions-tools.cpp +++ b/src/actions/actions-tools.cpp @@ -49,6 +49,7 @@ public: static std::map tool_data = { {"Select", {TOOLS_SELECT, PREFS_PAGE_TOOLS_SELECTOR, "/tools/select", }}, + {"Builder", {TOOLS_BUILDER, PREFS_PAGE_TOOLS_BUILDER, "/tools/builder", }}, {"Node", {TOOLS_NODES, PREFS_PAGE_TOOLS_NODE, "/tools/nodes", }}, {"Rect", {TOOLS_SHAPES_RECT, PREFS_PAGE_TOOLS_SHAPES_RECT, "/tools/shapes/rect", }}, {"Arc", {TOOLS_SHAPES_ARC, PREFS_PAGE_TOOLS_SHAPES_ELLIPSE, "/tools/shapes/arc", }}, @@ -75,6 +76,7 @@ static std::map tool_data = static std::map tool_msg = { {"Select", N_("Click to Select and Transform objects, Drag to select many objects.") }, + {"Builder", N_("Click to Select and Transform objects, Drag to select many objects.") /* TODO change this message*/ }, {"Node", N_("Modify selected path points (nodes) directly.") }, {"Rect", N_("Drag to create a rectangle. Drag controls to round corners and resize. Click to select.") }, {"Arc", N_("Drag to create an ellipse. Drag controls to make an arc or segment. Click to select.") }, @@ -363,6 +365,7 @@ std::vector> raw_data_tools = { // clang-format off {"win.tool-switch('Select')", N_("Tool: Select"), "Tool Switch", N_("Select and transform objects.") }, + {"win.tool-switch('Builder')", N_("Tool: Builder"), "Tool Switch", N_("Build shapes") }, {"win.tool-switch('Node')", N_("Tool: Node"), "Tool Switch", N_("Edit paths by nodes.") }, {"win.tool-switch('Rect')", N_("Tool: Rectangle"), "Tool Switch", N_("Create rectangles and squares.") }, diff --git a/src/actions/actions-tools.h b/src/actions/actions-tools.h index 7a4049f74d..dfde0bfa0f 100644 --- a/src/actions/actions-tools.h +++ b/src/actions/actions-tools.h @@ -17,6 +17,7 @@ enum tools_enum { TOOLS_INVALID, TOOLS_SELECT, + TOOLS_BUILDER, TOOLS_NODES, TOOLS_TWEAK, TOOLS_SPRAY, diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index fa450ff2e4..e02567d56d 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -68,6 +68,7 @@ set(ui_SRC toolbar/zoom-toolbar.cpp tools/arc-tool.cpp + tools/builder-tool.cpp tools/box3d-tool.cpp tools/calligraphic-tool.cpp tools/connector-tool.cpp @@ -377,6 +378,7 @@ set(ui_SRC toolbar/zoom-toolbar.h tools/arc-tool.h + tools/builder-tool.h tools/box3d-tool.h tools/calligraphic-tool.h tools/connector-tool.h diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index e19208a68a..43e4bf4607 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -46,6 +46,7 @@ enum { PREFS_PAGE_TOOLS, PREFS_PAGE_TOOLS_SELECTOR, + PREFS_PAGE_TOOLS_BUILDER, PREFS_PAGE_TOOLS_NODE, PREFS_PAGE_TOOLS_TWEAK, PREFS_PAGE_TOOLS_ZOOM, @@ -141,6 +142,7 @@ protected: UI::Widget::DialogPage _page_tools; UI::Widget::DialogPage _page_selector; + // TODO add one for the builder UI::Widget::DialogPage _page_node; UI::Widget::DialogPage _page_tweak; UI::Widget::DialogPage _page_spray; diff --git a/src/ui/tool-factory.cpp b/src/ui/tool-factory.cpp index 8c80c39214..aa2afc32fa 100644 --- a/src/ui/tool-factory.cpp +++ b/src/ui/tool-factory.cpp @@ -13,6 +13,7 @@ #include "ui/tools/arc-tool.h" #include "ui/tools/box3d-tool.h" +#include "ui/tools/builder-tool.h" #include "ui/tools/calligraphic-tool.h" #include "ui/tools/connector-tool.h" #include "ui/tools/dropper-tool.h" @@ -43,6 +44,8 @@ ToolBase *ToolFactory::createObject(std::string const& id) tool = new ArcTool; else if (id == "/tools/shapes/3dbox") tool = new Box3dTool; + else if (id == "/tools/builder") + tool = new BuilderTool; else if (id == "/tools/calligraphic") tool = new CalligraphicTool; else if (id == "/tools/connector") diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp new file mode 100644 index 0000000000..35c2d4a8cb --- /dev/null +++ b/src/ui/tools/builder-tool.cpp @@ -0,0 +1,1132 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" // only include where actually required! +#endif + +#include +#include + +#include +#include +#include + +#include "desktop.h" +#include "document-undo.h" +#include "document.h" +#include "include/macros.h" +#include "message-stack.h" +#include "rubberband.h" +#include "selection-chemistry.h" +#include "selection-describer.h" +#include "selection.h" +#include "seltrans.h" + +#include "actions/actions-tools.h" // set_active_tool() + +#include "display/drawing-item.h" +#include "display/control/canvas-item-catchall.h" +#include "display/control/canvas-item-drawing.h" + +#include "object/box3d.h" +#include "style.h" + +#include "ui/cursor-utils.h" +#include "ui/modifiers.h" + +#include "ui/tools/builder-tool.h" + +#include "ui/widget/canvas.h" + +#ifdef WITH_DBUS +#include "extension/dbus/document-interface.h" +#endif + + +using Inkscape::DocumentUndo; +using Inkscape::Modifiers::Modifier; + +namespace Inkscape { +namespace UI { +namespace Tools { + +static gint rb_escaped = 0; // if non-zero, rubberband was canceled by esc, so the next button release should not deselect +static gint drag_escaped = 0; // if non-zero, drag was canceled by esc + +const std::string& BuilderTool::getPrefsPath() { + return BuilderTool::prefsPath; +} + +const std::string BuilderTool::prefsPath = "/tools/select"; + +BuilderTool::BuilderTool() + : ToolBase("select.svg") + , dragging(false) + , moved(false) + , button_press_state(0) + , cycling_wrap(true) + , item(nullptr) + , _seltrans(nullptr) + , _describer(nullptr) +{ +} + +//static gint xp = 0, yp = 0; // where drag started +//static gint tolerance = 0; +//static bool within_tolerance = false; +static bool is_cycling = false; + + +BuilderTool::~BuilderTool() { + this->enableGrDrag(false); + + if (grabbed) { + grabbed->ungrab(); + grabbed = nullptr; + } + + delete this->_seltrans; + this->_seltrans = nullptr; + + delete this->_describer; + this->_describer = nullptr; + g_free(no_selection_msg); + + if (item) { + sp_object_unref(item); + item = nullptr; + } + + forced_redraws_stop(); +} + +void BuilderTool::setup() { + ToolBase::setup(); + + auto select_click = Modifier::get(Modifiers::Type::SELECT_ADD_TO)->get_label(); + auto select_scroll = Modifier::get(Modifiers::Type::SELECT_CYCLE)->get_label(); + + // cursors in select context + Gtk::Widget *w = desktop->getCanvas(); + if (w->get_window()) { + // Window may not be open when tool is setup for the first time! + _cursor_mouseover = load_svg_cursor(w->get_display(), w->get_window(), "select-mouseover.svg"); + _cursor_dragging = load_svg_cursor(w->get_display(), w->get_window(), "select-dragging.svg"); + // Need to reload select.svg. + load_svg_cursor(w->get_display(), w->get_window(), "select.svg"); + } + + no_selection_msg = g_strdup_printf( + _("No objects selected. Click, %s+click, %s+scroll mouse on top of objects, or drag around objects to select."), + select_click.c_str(), select_scroll.c_str()); + + this->_describer = new Inkscape::SelectionDescriber( + desktop->selection, + desktop->messageStack(), + _("Click selection again to toggle scale/rotation handles"), + no_selection_msg); + + this->_seltrans = new Inkscape::SelTrans(desktop); + + sp_event_context_read(this, "show"); + sp_event_context_read(this, "transform"); + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + if (prefs->getBool("/tools/select/gradientdrag")) { + this->enableGrDrag(); + } +} + +void BuilderTool::set(const Inkscape::Preferences::Entry& val) { + Glib::ustring path = val.getEntryName(); + + if (path == "show") { + if (val.getString() == "outline") { + this->_seltrans->setShow(Inkscape::SelTrans::SHOW_OUTLINE); + } else { + this->_seltrans->setShow(Inkscape::SelTrans::SHOW_CONTENT); + } + } +} + +bool BuilderTool::sp_select_context_abort() { + Inkscape::SelTrans *seltrans = this->_seltrans; + + if (this->dragging) { + if (this->moved) { // cancel dragging an object + seltrans->ungrab(); + this->moved = FALSE; + this->dragging = FALSE; + sp_event_context_discard_delayed_snap_event(this); + drag_escaped = 1; + + if (this->item) { + // only undo if the item is still valid + if (this->item->document) { + DocumentUndo::undo(desktop->getDocument()); + } + + sp_object_unref( this->item, nullptr); + } + this->item = nullptr; + + desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Move canceled.")); + return true; + } + } else { + if (Inkscape::Rubberband::get(desktop)->is_started()) { + Inkscape::Rubberband::get(desktop)->stop(); + rb_escaped = 1; + defaultMessageContext()->clear(); + desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Selection canceled.")); + return true; + } + } + return false; +} + +static bool +key_is_a_modifier (guint key) { + return (key == GDK_KEY_Alt_L || + key == GDK_KEY_Alt_R || + key == GDK_KEY_Control_L || + key == GDK_KEY_Control_R || + key == GDK_KEY_Shift_L || + key == GDK_KEY_Shift_R || + key == GDK_KEY_Meta_L || // Meta is when you press Shift+Alt (at least on my machine) + key == GDK_KEY_Meta_R); +} + +static void +sp_select_context_up_one_layer(SPDesktop *desktop) +{ + /* Click in empty place, go up one level -- but don't leave a layer to root. + * + * (Rationale: we don't usually allow users to go to the root, since that + * detracts from the layer metaphor: objects at the root level can in front + * of or behind layers. Whereas it's fine to go to the root if editing + * a document that has no layers (e.g. a non-Inkscape document).) + * + * Once we support editing SVG "islands" (e.g. embedded in an xhtml + * document), we might consider further restricting the below to disallow + * leaving a layer to go to a non-layer. + */ + SPObject *const current_layer = desktop->currentLayer(); + if (current_layer) { + SPObject *const parent = current_layer->parent; + SPGroup *current_group = dynamic_cast(current_layer); + if ( parent + && ( parent->parent + || !( current_group + && ( SPGroup::LAYER == current_group->layerMode() ) ) ) ) + { + desktop->setCurrentLayer(parent); + if (current_group && (SPGroup::LAYER != current_group->layerMode())) { + desktop->getSelection()->set(current_layer); + } + } + } +} + +bool BuilderTool::item_handler(SPItem* item, GdkEvent* event) { + gint ret = FALSE; + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); + + // make sure we still have valid objects to move around + if (this->item && this->item->document == nullptr) { + this->sp_select_context_abort(); + } + + switch (event->type) { + case GDK_BUTTON_PRESS: + if (event->button.button == 1) { + /* Left mousebutton */ + + // save drag origin + xp = (gint) event->button.x; + yp = (gint) event->button.y; + within_tolerance = true; + + // remember what modifiers were on before button press + this->button_press_state = event->button.state; + bool first_hit = Modifier::get(Modifiers::Type::SELECT_FIRST_HIT)->active(this->button_press_state); + bool force_drag = Modifier::get(Modifiers::Type::SELECT_FORCE_DRAG)->active(this->button_press_state); + bool always_box = Modifier::get(Modifiers::Type::SELECT_ALWAYS_BOX)->active(this->button_press_state); + bool touch_path = Modifier::get(Modifiers::Type::SELECT_TOUCH_PATH)->active(this->button_press_state); + + // if shift or ctrl was pressed, do not move objects; + // pass the event to root handler which will perform rubberband, shift-click, ctrl-click, ctrl-drag + if (!(always_box || first_hit || touch_path)) { + + this->dragging = TRUE; + this->moved = FALSE; + + auto window = desktop->getCanvas()->get_window(); + window->set_cursor(_cursor_dragging); + + // remember the clicked item in this->item: + if (this->item) { + sp_object_unref(this->item, nullptr); + this->item = nullptr; + } + + this->item = sp_event_context_find_item (desktop, Geom::Point(event->button.x, event->button.y), force_drag, FALSE); + sp_object_ref(this->item, nullptr); + + rb_escaped = drag_escaped = 0; + + if (grabbed) { + grabbed->ungrab(); + grabbed = nullptr; + } + + grabbed = desktop->getCanvasDrawing(); + grabbed->grab(Gdk::KEY_PRESS_MASK | + Gdk::KEY_RELEASE_MASK | + Gdk::BUTTON_PRESS_MASK | + Gdk::BUTTON_RELEASE_MASK | + Gdk::POINTER_MOTION_MASK ); + + ret = TRUE; + } + } else if (event->button.button == 3 && !this->dragging) { + // right click; do not eat it so that right-click menu can appear, but cancel dragging & rubberband + this->sp_select_context_abort(); + } + break; + + + case GDK_ENTER_NOTIFY: { + if (!desktop->isWaitingCursor() && !this->dragging) { + auto window = desktop->getCanvas()->get_window(); + window->set_cursor(_cursor_mouseover); + } + break; + } + case GDK_LEAVE_NOTIFY: + if (!desktop->isWaitingCursor() && !this->dragging) { + auto window = desktop->getCanvas()->get_window(); + window->set_cursor(this->cursor); + } + break; + + case GDK_KEY_PRESS: + if (get_latin_keyval (&event->key) == GDK_KEY_space) { + if (this->dragging && this->grabbed) { + /* stamping mode: show content mode moving */ + _seltrans->stamp(); + ret = TRUE; + } + } else if (get_latin_keyval (&event->key) == GDK_KEY_Tab) { + if (this->dragging && this->grabbed) { + _seltrans->getNextClosestPoint(false); + ret = TRUE; + } + } else if (get_latin_keyval (&event->key) == GDK_KEY_ISO_Left_Tab) { + if (this->dragging && this->grabbed) { + _seltrans->getNextClosestPoint(true); + ret = TRUE; + } + } + break; + + default: + break; + } + + if (!ret) { + ret = ToolBase::item_handler(item, event); + } + + return ret; +} + +void BuilderTool::sp_select_context_cycle_through_items(Inkscape::Selection *selection, GdkEventScroll *scroll_event) { + if ( this->cycling_items.empty() ) + return; + + Inkscape::DrawingItem *arenaitem; + + if(cycling_cur_item) { + arenaitem = cycling_cur_item->get_arenaitem(desktop->dkey); + arenaitem->setOpacity(0.3); + } + + // Find next item and activate it + + + std::vector::iterator next = cycling_items.end(); + + if ((scroll_event->direction == GDK_SCROLL_UP) || + (scroll_event->direction == GDK_SCROLL_SMOOTH && scroll_event->delta_y < 0)) { + if (! cycling_cur_item) { + next = cycling_items.begin(); + } else { + next = std::find( cycling_items.begin(), cycling_items.end(), cycling_cur_item ); + g_assert (next != cycling_items.end()); + ++next; + if (next == cycling_items.end()) { + if ( cycling_wrap ) { + next = cycling_items.begin(); + } else { + --next; + } + } + } + } else { + if (! cycling_cur_item) { + next = cycling_items.end(); + --next; + } else { + next = std::find( cycling_items.begin(), cycling_items.end(), cycling_cur_item ); + g_assert (next != cycling_items.end()); + if (next == cycling_items.begin()){ + if ( cycling_wrap ) { + next = cycling_items.end(); + --next; + } + } else { + --next; + } + } + } + + this->cycling_cur_item = *next; + g_assert(next != cycling_items.end()); + g_assert(cycling_cur_item != nullptr); + + arenaitem = cycling_cur_item->get_arenaitem(desktop->dkey); + arenaitem->setOpacity(1.0); + + if (Modifier::get(Modifiers::Type::SELECT_ADD_TO)->active(scroll_event->state)) { + selection->add(cycling_cur_item); + } else { + selection->set(cycling_cur_item); + } +} + +void BuilderTool::sp_select_context_reset_opacities() { + for (auto item : this->cycling_items_cmp) { + if (item) { + Inkscape::DrawingItem *arenaitem = item->get_arenaitem(desktop->dkey); + arenaitem->setOpacity(SP_SCALE24_TO_FLOAT(item->style->opacity.value)); + } else { + g_assert_not_reached(); + } + } + + this->cycling_items_cmp.clear(); + this->cycling_cur_item = nullptr; +} + +bool BuilderTool::root_handler(GdkEvent* event) { + SPItem *item = nullptr; + SPItem *item_at_point = nullptr, *group_at_point = nullptr, *item_in_group = nullptr; + gint ret = FALSE; + + Inkscape::Selection *selection = desktop->getSelection(); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + // make sure we still have valid objects to move around + if (this->item && this->item->document == nullptr) { + this->sp_select_context_abort(); + } + forced_redraws_start(5); + + switch (event->type) { + case GDK_2BUTTON_PRESS: + if (event->button.button == 1) { + if (!selection->isEmpty()) { + SPItem *clicked_item = selection->items().front(); + + if (dynamic_cast(clicked_item) && !dynamic_cast(clicked_item)) { // enter group if it's not a 3D box + desktop->setCurrentLayer(clicked_item); + desktop->getSelection()->clear(); + this->dragging = false; + sp_event_context_discard_delayed_snap_event(this); + + + } else { // switch tool + Geom::Point const button_pt(event->button.x, event->button.y); + Geom::Point const p(desktop->w2d(button_pt)); + set_active_tool(desktop, clicked_item, p); + } + } else { + sp_select_context_up_one_layer(desktop); + } + + ret = TRUE; + } + break; + + case GDK_BUTTON_PRESS: + if (event->button.button == 1) { + // save drag origin + xp = (gint) event->button.x; + yp = (gint) event->button.y; + within_tolerance = true; + + Geom::Point const button_pt(event->button.x, event->button.y); + Geom::Point const p(desktop->w2d(button_pt)); + + if(Modifier::get(Modifiers::Type::SELECT_TOUCH_PATH)->active(event->button.state)) { + Inkscape::Rubberband::get(desktop)->setMode(RUBBERBAND_MODE_TOUCHPATH); + } else { + Inkscape::Rubberband::get(desktop)->defaultMode(); + } + + Inkscape::Rubberband::get(desktop)->start(desktop, p); + + if (this->grabbed) { + grabbed->ungrab(); + this->grabbed = nullptr; + } + + grabbed = desktop->getCanvasCatchall(); + grabbed->grab(Gdk::KEY_PRESS_MASK | + Gdk::KEY_RELEASE_MASK | + Gdk::BUTTON_PRESS_MASK | + Gdk::BUTTON_RELEASE_MASK | + Gdk::POINTER_MOTION_MASK ); + + // remember what modifiers were on before button press + this->button_press_state = event->button.state; + + this->moved = FALSE; + + rb_escaped = drag_escaped = 0; + + ret = TRUE; + } else if (event->button.button == 3) { + // right click; do not eat it so that right-click menu can appear, but cancel dragging & rubberband + this->sp_select_context_abort(); + } + break; + + case GDK_MOTION_NOTIFY: + { + tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); + + bool first_hit = Modifier::get(Modifiers::Type::SELECT_FIRST_HIT)->active(this->button_press_state); + bool force_drag = Modifier::get(Modifiers::Type::SELECT_FORCE_DRAG)->active(this->button_press_state); + bool always_box = Modifier::get(Modifiers::Type::SELECT_ALWAYS_BOX)->active(this->button_press_state); + + if ((event->motion.state & GDK_BUTTON1_MASK)) { + Geom::Point const motion_pt(event->motion.x, event->motion.y); + Geom::Point const p(desktop->w2d(motion_pt)); + if ( within_tolerance + && ( abs( (gint) event->motion.x - xp ) < tolerance ) + && ( abs( (gint) event->motion.y - yp ) < tolerance ) ) { + break; // do not drag if we're within tolerance from origin + } + // Once the user has moved farther than tolerance from the original location + // (indicating they intend to move the object, not click), then always process the + // motion notify coordinates as given (no snapping back to origin) + within_tolerance = false; + + if (first_hit || (force_drag && !always_box && !selection->isEmpty())) { + // if it's not click and ctrl or alt was pressed (the latter with some selection + // but not with shift) we want to drag rather than rubberband + this->dragging = TRUE; + + auto window = desktop->getCanvas()->get_window(); + window->set_cursor(_cursor_dragging); + } + + if (this->dragging) { + /* User has dragged fast, so we get events on root (lauris)*/ + // not only that; we will end up here when ctrl-dragging as well + // and also when we started within tolerance, but trespassed tolerance outside of item + if (Inkscape::Rubberband::get(desktop)->is_started()) { + Inkscape::Rubberband::get(desktop)->stop(); + } + this->defaultMessageContext()->clear(); + + item_at_point = desktop->getItemAtPoint(Geom::Point(event->button.x, event->button.y), FALSE); + + if (!item_at_point) { // if no item at this point, try at the click point (bug 1012200) + item_at_point = desktop->getItemAtPoint(Geom::Point(xp, yp), FALSE); + } + + if (item_at_point || this->moved || force_drag) { + // drag only if starting from an item, or if something is already grabbed, or if alt-dragging + if (!this->moved) { + item_in_group = desktop->getItemAtPoint(Geom::Point(event->button.x, event->button.y), TRUE); + group_at_point = desktop->getGroupAtPoint(Geom::Point(event->button.x, event->button.y)); + + { + SPGroup *selGroup = dynamic_cast(selection->single()); + if (selGroup && (selGroup->layerMode() == SPGroup::LAYER)) { + group_at_point = selGroup; + } + } + + // group-at-point is meant to be topmost item if it's a group, + // not topmost group of all items at point + if (group_at_point != item_in_group && + !(group_at_point && item_at_point && + group_at_point->isAncestorOf(item_at_point))) { + group_at_point = nullptr; + } + + // if neither a group nor an item (possibly in a group) at point are selected, set selection to the item at point + if ((!item_in_group || !selection->includes(item_in_group)) && + (!group_at_point || !selection->includes(group_at_point)) && !force_drag) { + // select what is under cursor + if (!_seltrans->isEmpty()) { + _seltrans->resetState(); + } + + // when simply ctrl-dragging, we don't want to go into groups + if (item_at_point && !selection->includes(item_at_point)) { + selection->set(item_at_point); + } + } // otherwise, do not change selection so that dragging selected-within-group items, as well as alt-dragging, is possible + + _seltrans->grab(p, -1, -1, FALSE, TRUE); + this->moved = TRUE; + } + + if (!_seltrans->isEmpty()) { + sp_event_context_discard_delayed_snap_event(this); + _seltrans->moveTo(p, event->button.state); + } + + desktop->scroll_to_point(p); + gobble_motion_events(GDK_BUTTON1_MASK); + ret = TRUE; + } else { + this->dragging = FALSE; + sp_event_context_discard_delayed_snap_event(this); + + } + + } else { + if (Inkscape::Rubberband::get(desktop)->is_started()) { + Inkscape::Rubberband::get(desktop)->move(p); + + auto touch_path = Modifier::get(Modifiers::Type::SELECT_TOUCH_PATH)->get_label(); + auto mode = Inkscape::Rubberband::get(desktop)->getMode(); + if (mode == RUBBERBAND_MODE_TOUCHPATH) { + this->defaultMessageContext()->setF(Inkscape::NORMAL_MESSAGE, + _("Draw over objects to select them; release %s to switch to rubberband selection"), touch_path.c_str()); + } else if (mode == RUBBERBAND_MODE_TOUCHRECT) { + this->defaultMessageContext()->setF(Inkscape::NORMAL_MESSAGE, + _("Drag near objects to select them; press %s to switch to touch selection"), touch_path.c_str()); + } else { + this->defaultMessageContext()->setF(Inkscape::NORMAL_MESSAGE, + _("Drag around objects to select them; press %s to switch to touch selection"), touch_path.c_str()); + } + + gobble_motion_events(GDK_BUTTON1_MASK); + } + } + } + break; + } + case GDK_BUTTON_RELEASE: + xp = yp = 0; + + if ((event->button.button == 1) && (this->grabbed)) { + if (this->dragging) { + + if (this->moved) { + // item has been moved + _seltrans->ungrab(); + this->moved = FALSE; +#ifdef WITH_DBUS + dbus_send_ping(desktop, this->item); +#endif + } else if (this->item && !drag_escaped) { + // item has not been moved -> simply a click, do selecting + if (!selection->isEmpty()) { + if(Modifier::get(Modifiers::Type::SELECT_ADD_TO)->active(event->button.state)) { + // with shift, toggle selection + _seltrans->resetState(); + selection->toggle(this->item); + } else { + SPObject* single = selection->single(); + SPGroup *singleGroup = dynamic_cast(single); + // without shift, increase state (i.e. toggle scale/rotation handles) + if (selection->includes(this->item)) { + _seltrans->increaseState(); + } else if (singleGroup && (singleGroup->layerMode() == SPGroup::LAYER) && single->isAncestorOf(this->item)) { + _seltrans->increaseState(); + } else { + _seltrans->resetState(); + selection->set(this->item); + } + } + } else { // simple or shift click, no previous selection + _seltrans->resetState(); + selection->set(this->item); + } + } + + this->dragging = FALSE; + + auto window = desktop->getCanvas()->get_window(); + window->set_cursor(_cursor_mouseover); + + sp_event_context_discard_delayed_snap_event(this); + + if (this->item) { + sp_object_unref( this->item, nullptr); + } + + this->item = nullptr; + } else { + Inkscape::Rubberband *r = Inkscape::Rubberband::get(desktop); + + if (r->is_started() && !within_tolerance) { + // this was a rubberband drag + std::vector items; + + if (r->getMode() == RUBBERBAND_MODE_RECT) { + Geom::OptRect const b = r->getRectangle(); + items = desktop->getDocument()->getItemsInBox(desktop->dkey, (*b) * desktop->dt2doc()); + } else if (r->getMode() == RUBBERBAND_MODE_TOUCHRECT) { + Geom::OptRect const b = r->getRectangle(); + items = desktop->getDocument()->getItemsPartiallyInBox(desktop->dkey, (*b) * desktop->dt2doc()); + } else if (r->getMode() == RUBBERBAND_MODE_TOUCHPATH) { + items = desktop->getDocument()->getItemsAtPoints(desktop->dkey, r->getPoints()); + } + + _seltrans->resetState(); + r->stop(); + this->defaultMessageContext()->clear(); + + if(Modifier::get(Modifiers::Type::SELECT_ADD_TO)->active(event->button.state)) { + // with shift, add to selection + selection->addList (items); + } else { + // without shift, simply select anew + selection->setList (items); + if(Modifier::get(Modifiers::Type::SELECT_TOUCH_PATH)->active(event->button.state)) { + selection->pathUnion(); + selection->the_temporary_fix_for_the_transform_bug(); + selection->remove_paths_with_small_area(); + selection->clear(); + } + } + + } else { // it was just a click, or a too small rubberband + r->stop(); + + bool add_to = Modifier::get(Modifiers::Type::SELECT_ADD_TO)->active(event->button.state); + bool in_groups = Modifier::get(Modifiers::Type::SELECT_IN_GROUPS)->active(event->button.state); + bool force_drag = Modifier::get(Modifiers::Type::SELECT_FORCE_DRAG)->active(event->button.state); + + if (add_to && !rb_escaped && !drag_escaped) { + // this was a shift+click or alt+shift+click, select what was clicked upon + + if (in_groups) { + // go into groups, honoring force_drag (Alt) + item = sp_event_context_find_item (desktop, + Geom::Point(event->button.x, event->button.y), force_drag, TRUE); + } else { + // don't go into groups, honoring Alt + item = sp_event_context_find_item (desktop, + Geom::Point(event->button.x, event->button.y), force_drag, FALSE); + } + + if (item) { + selection->toggle(item); + item = nullptr; + } + + } else if ((in_groups || force_drag) && !rb_escaped && !drag_escaped) { // ctrl+click, alt+click + item = sp_event_context_find_item (desktop, + Geom::Point(event->button.x, event->button.y), force_drag, in_groups); + + if (item) { + if (selection->includes(item)) { + _seltrans->increaseState(); + } else { + _seltrans->resetState(); + selection->set(item); + } + + item = nullptr; + } + } else { // click without shift, simply deselect, unless with Alt or something was cancelled + if (!selection->isEmpty()) { + if (!(rb_escaped) && !(drag_escaped) && !force_drag) { + selection->clear(); + } + + rb_escaped = 0; + } + } + } + + ret = TRUE; + } + if (grabbed) { + grabbed->ungrab(); + grabbed = nullptr; + } + // Think is not necesary now + // desktop->updateNow(); + } + + if (event->button.button == 1) { + Inkscape::Rubberband::get(desktop)->stop(); // might have been started in another tool! + } + + this->button_press_state = 0; + break; + + case GDK_SCROLL: { + + GdkEventScroll *scroll_event = (GdkEventScroll*) event; + + // do nothing specific if alt was not pressed + if ( ! Modifier::get(Modifiers::Type::SELECT_CYCLE)->active(scroll_event->state)) + break; + + is_cycling = true; + + /* Rebuild list of items underneath the mouse pointer */ + Geom::Point p = desktop->d2w(desktop->point()); + SPItem *item = desktop->getItemAtPoint(p, true, nullptr); + this->cycling_items.clear(); + + SPItem *tmp = nullptr; + while(item != nullptr) { + this->cycling_items.push_back(item); + item = desktop->getItemAtPoint(p, true, item); + if (selection->includes(item)) tmp = item; + } + + /* Compare current item list with item list during previous scroll ... */ + bool item_lists_differ = this->cycling_items != this->cycling_items_cmp; + + if(item_lists_differ) { + this->sp_select_context_reset_opacities(); + for (auto l : this->cycling_items_cmp) + selection->remove(l); // deselects the previous content of the cycling loop + this->cycling_items_cmp = (this->cycling_items); + + // set opacities in new stack + for(auto item : this->cycling_items) { + if (item) { + Inkscape::DrawingItem *arenaitem = item->get_arenaitem(desktop->dkey); + arenaitem->setOpacity(0.3); + } + } + } + if(!cycling_cur_item) cycling_cur_item = tmp; + + this->cycling_wrap = prefs->getBool("/options/selection/cycleWrap", true); + + // Cycle through the items underneath the mouse pointer, one-by-one + this->sp_select_context_cycle_through_items(selection, scroll_event); + + ret = TRUE; + + GtkWindow *w =GTK_WINDOW(gtk_widget_get_toplevel( GTK_WIDGET(desktop->getCanvas()->gobj()) )); + if (w) { + gtk_window_present(w); + desktop->getCanvas()->grab_focus(); + } + break; + } + + case GDK_KEY_PRESS: // keybindings for select context + { + { + guint keyval = get_latin_keyval(&event->key); + + bool alt = ( MOD__ALT(event) + || (keyval == GDK_KEY_Alt_L) + || (keyval == GDK_KEY_Alt_R) + || (keyval == GDK_KEY_Meta_L) + || (keyval == GDK_KEY_Meta_R)); + + if (!key_is_a_modifier (keyval)) { + this->defaultMessageContext()->clear(); + } else if (this->grabbed || _seltrans->isGrabbed()) { + + if (Inkscape::Rubberband::get(desktop)->is_started()) { + // if Alt then change cursor to moving cursor: + if (Modifier::get(Modifiers::Type::SELECT_TOUCH_PATH)->active(event->key.state | keyval)) { + Inkscape::Rubberband::get(desktop)->setMode(RUBBERBAND_MODE_TOUCHPATH); + } + } else { + // do not change the statusbar text when mousekey is down to move or transform the object, + // because the statusbar text is already updated somewhere else. + break; + } + } else { + Modifiers::responsive_tooltip(this->defaultMessageContext(), event, 6, + Modifiers::Type::SELECT_IN_GROUPS, Modifiers::Type::MOVE_CONFINE, + Modifiers::Type::SELECT_ADD_TO, Modifiers::Type::SELECT_TOUCH_PATH, + Modifiers::Type::SELECT_CYCLE, Modifiers::Type::SELECT_FORCE_DRAG); + + // if Alt and nonempty selection, show moving cursor ("move selected"): + if (alt && !selection->isEmpty() && !desktop->isWaitingCursor()) { + auto window = desktop->getCanvas()->get_window(); + window->set_cursor(_cursor_dragging); + } + //*/ + break; + } + } + + gdouble const nudge = prefs->getDoubleLimited("/options/nudgedistance/value", 2, 0, 1000, "px"); // in px + int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); + auto const y_dir = desktop->yaxisdir(); + + switch (get_latin_keyval (&event->key)) { + case GDK_KEY_Left: // move selection left + case GDK_KEY_KP_Left: + if (!MOD__CTRL(event)) { // not ctrl + gint mul = 1 + gobble_key_events( get_latin_keyval(&event->key), 0); // with any mask + + if (MOD__ALT(event)) { // alt + if (MOD__SHIFT(event)) { + desktop->getSelection()->moveScreen(mul*-10, 0); // shift + } else { + desktop->getSelection()->moveScreen(mul*-1, 0); // no shift + } + } else { // no alt + if (MOD__SHIFT(event)) { + desktop->getSelection()->move(mul*-10*nudge, 0); // shift + } else { + desktop->getSelection()->move(mul*-nudge, 0); // no shift + } + } + + ret = TRUE; + } + break; + + case GDK_KEY_Up: // move selection up + case GDK_KEY_KP_Up: + if (!MOD__CTRL(event)) { // not ctrl + gint mul = 1 + gobble_key_events(get_latin_keyval(&event->key), 0); // with any mask + mul *= -y_dir; + + if (MOD__ALT(event)) { // alt + if (MOD__SHIFT(event)) { + desktop->getSelection()->moveScreen(0, mul*10); // shift + } else { + desktop->getSelection()->moveScreen(0, mul*1); // no shift + } + } else { // no alt + if (MOD__SHIFT(event)) { + desktop->getSelection()->move(0, mul*10*nudge); // shift + } else { + desktop->getSelection()->move(0, mul*nudge); // no shift + } + } + + ret = TRUE; + } + break; + + case GDK_KEY_Right: // move selection right + case GDK_KEY_KP_Right: + if (!MOD__CTRL(event)) { // not ctrl + gint mul = 1 + gobble_key_events(get_latin_keyval(&event->key), 0); // with any mask + + if (MOD__ALT(event)) { // alt + if (MOD__SHIFT(event)) { + desktop->getSelection()->moveScreen(mul*10, 0); // shift + } else { + desktop->getSelection()->moveScreen(mul*1, 0); // no shift + } + } else { // no alt + if (MOD__SHIFT(event)) { + desktop->getSelection()->move(mul*10*nudge, 0); // shift + } else { + desktop->getSelection()->move(mul*nudge, 0); // no shift + } + } + + ret = TRUE; + } + break; + + case GDK_KEY_Down: // move selection down + case GDK_KEY_KP_Down: + if (!MOD__CTRL(event)) { // not ctrl + gint mul = 1 + gobble_key_events(get_latin_keyval(&event->key), 0); // with any mask + mul *= -y_dir; + + if (MOD__ALT(event)) { // alt + if (MOD__SHIFT(event)) { + desktop->getSelection()->moveScreen(0, mul*-10); // shift + } else { + desktop->getSelection()->moveScreen(0, mul*-1); // no shift + } + } else { // no alt + if (MOD__SHIFT(event)) { + desktop->getSelection()->move(0, mul*-10*nudge); // shift + } else { + desktop->getSelection()->move(0, mul*-nudge); // no shift + } + } + + ret = TRUE; + } + break; + + case GDK_KEY_Escape: + if (!this->sp_select_context_abort()) { + selection->clear(); + } + + ret = TRUE; + break; + + case GDK_KEY_a: + case GDK_KEY_A: + if (MOD__CTRL_ONLY(event)) { + sp_edit_select_all(desktop); + ret = TRUE; + } + break; + + case GDK_KEY_space: + /* stamping mode: show outline mode moving */ + /* FIXME: Is next condition ok? (lauris) */ + if (this->dragging && this->grabbed) { + _seltrans->stamp(); + ret = TRUE; + } + break; + + case GDK_KEY_x: + case GDK_KEY_X: + if (MOD__ALT_ONLY(event)) { + desktop->setToolboxFocusTo ("select-x"); + ret = TRUE; + } + break; + + case GDK_KEY_bracketleft: + if (MOD__ALT(event)) { + gint mul = 1 + gobble_key_events(get_latin_keyval(&event->key), 0); // with any mask + selection->rotateScreen(-mul * y_dir); + } else if (MOD__CTRL(event)) { + selection->rotate(-90 * y_dir); + } else if (snaps) { + selection->rotate(-180.0/snaps * y_dir); + } + + ret = TRUE; + break; + + case GDK_KEY_bracketright: + if (MOD__ALT(event)) { + gint mul = 1 + gobble_key_events(get_latin_keyval(&event->key), 0); // with any mask + selection->rotateScreen(mul * y_dir); + } else if (MOD__CTRL(event)) { + selection->rotate(90 * y_dir); + } else if (snaps) { + selection->rotate(180.0/snaps * y_dir); + } + + ret = TRUE; + break; + + case GDK_KEY_Return: + if (MOD__CTRL_ONLY(event)) { + if (selection->singleItem()) { + SPItem *clicked_item = selection->singleItem(); + SPGroup *clickedGroup = dynamic_cast(clicked_item); + if ( (clickedGroup && (clickedGroup->layerMode() != SPGroup::LAYER)) || dynamic_cast(clicked_item)) { // enter group or a 3D box + desktop->setCurrentLayer(clicked_item); + desktop->getSelection()->clear(); + } else { + this->desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Selected object is not a group. Cannot enter.")); + } + } + + ret = TRUE; + } + break; + + case GDK_KEY_BackSpace: + if (MOD__CTRL_ONLY(event)) { + sp_select_context_up_one_layer(desktop); + ret = TRUE; + } + break; + + case GDK_KEY_s: + case GDK_KEY_S: + if (MOD__SHIFT_ONLY(event)) { + if (!selection->isEmpty()) { + _seltrans->increaseState(); + } + + ret = TRUE; + } + break; + + case GDK_KEY_g: + case GDK_KEY_G: + if (MOD__SHIFT_ONLY(event)) { + desktop->selection->toGuides(); + ret = true; + } + break; + + default: + break; + } + break; + } + case GDK_KEY_RELEASE: { + guint keyval = get_latin_keyval(&event->key); + if (key_is_a_modifier (keyval)) { + this->defaultMessageContext()->clear(); + } + + bool alt = ( MOD__ALT(event) + || (keyval == GDK_KEY_Alt_L) + || (keyval == GDK_KEY_Alt_R) + || (keyval == GDK_KEY_Meta_L) + || (keyval == GDK_KEY_Meta_R)); + + if (Inkscape::Rubberband::get(desktop)->is_started()) { + // if Alt then change cursor to moving cursor: + if (alt) { + Inkscape::Rubberband::get(desktop)->defaultMode(); + } + } else { + if (alt) { + // quit cycle-selection and reset opacities + if (is_cycling) { + this->sp_select_context_reset_opacities(); + is_cycling = false; + } + } + } + + // set cursor to default. + if (!desktop->isWaitingCursor()) { + // Do we need to reset the cursor here on key release ? + //GdkWindow* window = gtk_widget_get_window (GTK_WIDGET (desktop->getCanvas())); + //gdk_window_set_cursor(window, event_context->cursor); + } + break; + } + default: + break; + } + + if (!ret) { + ret = ToolBase::root_handler(event); + } + + return ret; +} + +} +} +} diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h new file mode 100644 index 0000000000..1864465dda --- /dev/null +++ b/src/ui/tools/builder-tool.h @@ -0,0 +1,57 @@ +#pragma once + +#include "ui/tools/tool-base.h" + +#define SP_SELECT_CONTEXT(obj) (dynamic_cast((Inkscape::UI::Tools::ToolBase*)obj)) +#define SP_IS_SELECT_CONTEXT(obj) (dynamic_cast((const Inkscape::UI::Tools::ToolBase*)obj) != NULL) + +namespace Inkscape { +class SelTrans; +class SelectionDescriber; +} + +namespace Inkscape { +namespace UI { +namespace Tools { + +class BuilderTool : public ToolBase { +public: + BuilderTool(); + ~BuilderTool() override; + + bool dragging; + bool moved; + guint button_press_state; + + std::vector cycling_items; + std::vector cycling_items_cmp; + SPItem *cycling_cur_item; + bool cycling_wrap; + + SPItem *item; + Inkscape::CanvasItem *grabbed = nullptr; + Inkscape::SelTrans *_seltrans; + Inkscape::SelectionDescriber *_describer; + gchar *no_selection_msg = nullptr; + + static const std::string prefsPath; + + void setup() override; + void set(const Inkscape::Preferences::Entry& val) override; + bool root_handler(GdkEvent* event) override; + bool item_handler(SPItem* item, GdkEvent* event) override; + + const std::string& getPrefsPath() override; + +private: + bool sp_select_context_abort(); + void sp_select_context_cycle_through_items(Inkscape::Selection *selection, GdkEventScroll *scroll_event); + void sp_select_context_reset_opacities(); + + Glib::RefPtr _cursor_mouseover; + Glib::RefPtr _cursor_dragging; +}; + +} +} +} diff --git a/src/verbs.cpp b/src/verbs.cpp index 6c53266b73..36f61f5410 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1723,6 +1723,9 @@ void ContextVerb::perform(SPAction *action, void *data) switch (verb) { case SP_VERB_CONTEXT_SELECT: break; + case SP_VERB_CONTEXT_BUILDER: + set_active_tool(dt, "Builder"); + break; case SP_VERB_CONTEXT_NODE: set_active_tool(dt, "Node"); break; @@ -2624,6 +2627,8 @@ Verb *Verb::_base_verbs[] = { // Tools new ContextVerb(SP_VERB_CONTEXT_SELECT, "ToolSelector", NC_("ContextVerb", "Select"), N_("Select and transform objects"), INKSCAPE_ICON("tool-pointer")), + new ContextVerb(SP_VERB_CONTEXT_BUILDER, "ToolBuilder", NC_("ContextVerb", "Builder"), + N_("Build shapes"), INKSCAPE_ICON("tool-builder")), new ContextVerb(SP_VERB_CONTEXT_NODE, "ToolNode", NC_("ContextVerb", "Node Edit"), N_("Edit paths by nodes"), INKSCAPE_ICON("tool-node-editor")), new ContextVerb(SP_VERB_CONTEXT_TWEAK, "ToolTweak", NC_("ContextVerb", "Tweak"), diff --git a/src/verbs.h b/src/verbs.h index a659b8acef..9eebac34cb 100644 --- a/src/verbs.h +++ b/src/verbs.h @@ -198,6 +198,7 @@ enum { SP_VERB_OBJECT_UNSET_CLIPPATH, /* Tools */ SP_VERB_CONTEXT_SELECT, + SP_VERB_CONTEXT_BUILDER, SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_SPRAY, diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index 7dc933fd5d..9ed1c3e726 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -141,6 +141,7 @@ static struct { // If you change the tool_name for Measure or Text here, change it also in desktop-widget.cpp. // clang-format off { "/tools/select", "Select", Inkscape::UI::Toolbar::SelectToolbar::create, nullptr}, + { "/tools/builder", "Builder", Inkscape::UI::Toolbar::SelectToolbar::create, nullptr}, // TODO change this to the builder toolbar { "/tools/nodes", "Node", Inkscape::UI::Toolbar::NodeToolbar::create, nullptr}, { "/tools/shapes/rect", "Rect", Inkscape::UI::Toolbar::RectToolbar::create, N_("Style of new rectangles")}, { "/tools/shapes/arc", "Arc", Inkscape::UI::Toolbar::ArcToolbar::create, N_("Style of new ellipses")}, -- GitLab From a1eea0c60f5711225abab1cf33e015deecb6950e Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 15 Jul 2021 17:56:16 +0200 Subject: [PATCH 069/235] Duplicated the select toolbar. --- src/ui/CMakeLists.txt | 2 + src/ui/toolbar/builder-toolbar.cpp | 565 +++++++++++++++++++++++++++++ src/ui/toolbar/builder-toolbar.h | 90 +++++ src/ui/toolbar/select-toolbar.h | 5 +- src/widgets/toolbox.cpp | 3 +- 5 files changed, 660 insertions(+), 5 deletions(-) create mode 100644 src/ui/toolbar/builder-toolbar.cpp create mode 100644 src/ui/toolbar/builder-toolbar.h diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index e02567d56d..100a2eb1ef 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -46,6 +46,7 @@ set(ui_SRC toolbar/arc-toolbar.cpp toolbar/box3d-toolbar.cpp + toolbar/builder-toolbar.cpp toolbar/calligraphy-toolbar.cpp toolbar/connector-toolbar.cpp toolbar/dropper-toolbar.cpp @@ -356,6 +357,7 @@ set(ui_SRC toolbar/arc-toolbar.h toolbar/box3d-toolbar.h + toolbar/builder-toolbar.h toolbar/calligraphy-toolbar.h toolbar/connector-toolbar.h toolbar/dropper-toolbar.h diff --git a/src/ui/toolbar/builder-toolbar.cpp b/src/ui/toolbar/builder-toolbar.cpp new file mode 100644 index 0000000000..12a2a507de --- /dev/null +++ b/src/ui/toolbar/builder-toolbar.cpp @@ -0,0 +1,565 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Selector aux toolbar + * + * Authors: + * Lauris Kaplinski + * bulia byak + * Jon A. Cruz + * Abhishek Sharma + * + * Copyright (C) 2003-2005 authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "builder-toolbar.h" + +#include + +#include +#include + +#include <2geom/rect.h> + +#include "desktop.h" +#include "document-undo.h" +#include "document.h" +#include "selection.h" +#include "message-stack.h" +#include "selection-chemistry.h" +#include "verbs.h" + + +#include "object/sp-item-transform.h" +#include "object/sp-namedview.h" + +#include "ui/icon-names.h" +#include "ui/widget/canvas.h" // Focus widget +#include "ui/widget/combo-tool-item.h" +#include "ui/widget/spin-button-tool-item.h" +#include "ui/widget/spinbutton.h" +#include "ui/widget/unit-tracker.h" + +#include "widgets/widget-sizes.h" +#include "io/resource.h" + +using Inkscape::IO::Resource::UIS; + + +using Inkscape::UI::Widget::UnitTracker; +using Inkscape::Util::Unit; +using Inkscape::Util::Quantity; +using Inkscape::DocumentUndo; +using Inkscape::Util::unit_table; + +namespace Inkscape { +namespace UI { +namespace Toolbar { + +BuilderToolbar::BuilderToolbar(SPDesktop *desktop) : + Toolbar(desktop), + _tracker(new UnitTracker(Inkscape::Util::UNIT_TYPE_LINEAR)), + _update(false), + _lock_btn(Gtk::manage(new Gtk::ToggleToolButton())), + _select_touch_btn(Gtk::manage(new Gtk::ToggleToolButton())), + _transform_stroke_btn(Gtk::manage(new Gtk::ToggleToolButton())), + _transform_corners_btn(Gtk::manage(new Gtk::ToggleToolButton())), + _transform_gradient_btn(Gtk::manage(new Gtk::ToggleToolButton())), + _transform_pattern_btn(Gtk::manage(new Gtk::ToggleToolButton())) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + add_toolbutton_for_verb(SP_VERB_EDIT_SELECT_ALL); + add_toolbutton_for_verb(SP_VERB_EDIT_SELECT_ALL_IN_ALL_LAYERS); + auto deselect_button = add_toolbutton_for_verb(SP_VERB_EDIT_DESELECT); + _context_items.push_back(deselect_button); + + _select_touch_btn->set_label(_("Select by touch")); + _select_touch_btn->set_tooltip_text(_("Toggle selection box to select all touched objects.")); + _select_touch_btn->set_icon_name(INKSCAPE_ICON("selection-touch")); + _select_touch_btn->set_active(prefs->getBool("/tools/select/touch_box", false)); + _select_touch_btn->signal_toggled().connect(sigc::mem_fun(*this, &BuilderToolbar::toggle_touch)); + + add(*_select_touch_btn); + + add(* Gtk::manage(new Gtk::SeparatorToolItem())); + + auto object_rotate_90_ccw_button = add_toolbutton_for_verb(SP_VERB_OBJECT_ROTATE_90_CCW); + _context_items.push_back(object_rotate_90_ccw_button); + + auto object_rotate_90_cw_button = add_toolbutton_for_verb(SP_VERB_OBJECT_ROTATE_90_CW); + _context_items.push_back(object_rotate_90_cw_button); + + auto object_flip_horizontal_button = add_toolbutton_for_verb(SP_VERB_OBJECT_FLIP_HORIZONTAL); + _context_items.push_back(object_flip_horizontal_button); + + auto object_flip_vertical_button = add_toolbutton_for_verb(SP_VERB_OBJECT_FLIP_VERTICAL); + _context_items.push_back(object_flip_vertical_button); + + add(* Gtk::manage(new Gtk::SeparatorToolItem())); + + auto selection_to_front_button = add_toolbutton_for_verb(SP_VERB_SELECTION_TO_FRONT); + _context_items.push_back(selection_to_front_button); + + auto selection_raise_button = add_toolbutton_for_verb(SP_VERB_SELECTION_RAISE); + _context_items.push_back(selection_raise_button); + + auto selection_lower_button = add_toolbutton_for_verb(SP_VERB_SELECTION_LOWER); + _context_items.push_back(selection_lower_button); + + auto selection_to_back_button = add_toolbutton_for_verb(SP_VERB_SELECTION_TO_BACK); + _context_items.push_back(selection_to_back_button); + + add(* Gtk::manage(new Gtk::SeparatorToolItem())); + + _tracker->addUnit(unit_table.getUnit("%")); + _tracker->setActiveUnit( desktop->getNamedView()->display_units ); + + // x-value control + auto x_val = prefs->getDouble("/tools/select/X", 0.0); + _adj_x = Gtk::Adjustment::create(x_val, -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP); + _adj_x->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &BuilderToolbar::any_value_changed), _adj_x)); + _tracker->addAdjustment(_adj_x->gobj()); + + auto x_btn = Gtk::manage(new UI::Widget::SpinButtonToolItem("select-x", + C_("Select toolbar", "X:"), + _adj_x, + SPIN_STEP, 3)); + x_btn->get_spin_button()->addUnitTracker(_tracker.get()); + x_btn->set_focus_widget(_desktop->getCanvas()); + x_btn->set_all_tooltip_text(C_("Select toolbar", "Horizontal coordinate of selection")); + _context_items.push_back(x_btn); + add(*x_btn); + + // y-value control + auto y_val = prefs->getDouble("/tools/select/Y", 0.0); + _adj_y = Gtk::Adjustment::create(y_val, -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP); + _adj_y->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &BuilderToolbar::any_value_changed), _adj_y)); + _tracker->addAdjustment(_adj_y->gobj()); + + auto y_btn = Gtk::manage(new UI::Widget::SpinButtonToolItem("select-y", + C_("Select toolbar", "Y:"), + _adj_y, + SPIN_STEP, 3)); + y_btn->get_spin_button()->addUnitTracker(_tracker.get()); + y_btn->set_focus_widget(_desktop->getCanvas()); + y_btn->set_all_tooltip_text(C_("Select toolbar", "Vertical coordinate of selection")); + _context_items.push_back(y_btn); + add(*y_btn); + + // width-value control + auto w_val = prefs->getDouble("/tools/select/width", 0.0); + _adj_w = Gtk::Adjustment::create(w_val, 0.0, 1e6, SPIN_STEP, SPIN_PAGE_STEP); + _adj_w->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &BuilderToolbar::any_value_changed), _adj_w)); + _tracker->addAdjustment(_adj_w->gobj()); + + auto w_btn = Gtk::manage(new UI::Widget::SpinButtonToolItem("select-width", + C_("Select toolbar", "W:"), + _adj_w, + SPIN_STEP, 3)); + w_btn->get_spin_button()->addUnitTracker(_tracker.get()); + w_btn->set_focus_widget(_desktop->getCanvas()); + w_btn->set_all_tooltip_text(C_("Select toolbar", "Width of selection")); + _context_items.push_back(w_btn); + add(*w_btn); + + // lock toggle + _lock_btn->set_label(_("Lock width and height")); + _lock_btn->set_tooltip_text(_("When locked, change both width and height by the same proportion")); + _lock_btn->set_icon_name(INKSCAPE_ICON("object-unlocked")); + _lock_btn->signal_toggled().connect(sigc::mem_fun(*this, &BuilderToolbar::toggle_lock)); + _lock_btn->set_name("lock"); + add(*_lock_btn); + + // height-value control + auto h_val = prefs->getDouble("/tools/select/height", 0.0); + _adj_h = Gtk::Adjustment::create(h_val, 0.0, 1e6, SPIN_STEP, SPIN_PAGE_STEP); + _adj_h->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &BuilderToolbar::any_value_changed), _adj_h)); + _tracker->addAdjustment(_adj_h->gobj()); + + auto h_btn = Gtk::manage(new UI::Widget::SpinButtonToolItem("select-height", + C_("Select toolbar", "H:"), + _adj_h, + SPIN_STEP, 3)); + h_btn->get_spin_button()->addUnitTracker(_tracker.get()); + h_btn->set_focus_widget(_desktop->getCanvas()); + h_btn->set_all_tooltip_text(C_("Select toolbar", "Height of selection")); + _context_items.push_back(h_btn); + add(*h_btn); + + // units menu + auto unit_menu = _tracker->create_tool_item(_("Units"), ("") ); + add(*unit_menu); + + add(* Gtk::manage(new Gtk::SeparatorToolItem())); + + _transform_stroke_btn->set_label(_("Scale stroke width")); + _transform_stroke_btn->set_tooltip_text(_("When scaling objects, scale the stroke width by the same proportion")); + _transform_stroke_btn->set_icon_name(INKSCAPE_ICON("transform-affect-stroke")); + _transform_stroke_btn->set_active(prefs->getBool("/options/transform/stroke", true)); + _transform_stroke_btn->signal_toggled().connect(sigc::mem_fun(*this, &BuilderToolbar::toggle_stroke)); + add(*_transform_stroke_btn); + + _transform_corners_btn->set_label(_("Scale rounded corners")); + _transform_corners_btn->set_tooltip_text(_("When scaling rectangles, scale the radii of rounded corners")); + _transform_corners_btn->set_icon_name(INKSCAPE_ICON("transform-affect-rounded-corners")); + _transform_corners_btn->set_active(prefs->getBool("/options/transform/rectcorners", true)); + _transform_corners_btn->signal_toggled().connect(sigc::mem_fun(*this, &BuilderToolbar::toggle_corners)); + add(*_transform_corners_btn); + + _transform_gradient_btn->set_label(_("Move gradients")); + _transform_gradient_btn->set_tooltip_text(_("Move gradients (in fill or stroke) along with the objects")); + _transform_gradient_btn->set_icon_name(INKSCAPE_ICON("transform-affect-gradient")); + _transform_gradient_btn->set_active(prefs->getBool("/options/transform/gradient", true)); + _transform_gradient_btn->signal_toggled().connect(sigc::mem_fun(*this, &BuilderToolbar::toggle_gradient)); + add(*_transform_gradient_btn); + + _transform_pattern_btn->set_label(_("Move patterns")); + _transform_pattern_btn->set_tooltip_text(_("Move patterns (in fill or stroke) along with the objects")); + _transform_pattern_btn->set_icon_name(INKSCAPE_ICON("transform-affect-pattern")); + _transform_pattern_btn->set_active(prefs->getBool("/options/transform/pattern", true)); + _transform_pattern_btn->signal_toggled().connect(sigc::mem_fun(*this, &BuilderToolbar::toggle_pattern)); + add(*_transform_pattern_btn); + + assert(desktop); + auto *selection = desktop->getSelection(); + + // Force update when selection changes. + _connections.emplace_back( // + selection->connectModified(sigc::mem_fun(*this, &BuilderToolbar::on_inkscape_selection_modified))); + _connections.emplace_back( + selection->connectChanged(sigc::mem_fun(*this, &BuilderToolbar::on_inkscape_selection_changed))); + + // Update now. + layout_widget_update(selection); + + for (auto item : _context_items) { + if ( item->is_sensitive() ) { + item->set_sensitive(false); + } + } + + show_all(); +} + +void BuilderToolbar::on_unrealize() +{ + for (auto &conn : _connections) { + conn.disconnect(); + } + + parent_type::on_unrealize(); +} + +GtkWidget * +BuilderToolbar::create(SPDesktop *desktop) +{ + // Select TOOLBAR : SpinButton Addition Left + + /* Glib::ustring select_toolbar_builder_file = get_filename(UIS, "toolbar-select.ui"); + auto builder = Gtk::Builder::create(); + try + { + builder->add_from_file(select_toolbar_builder_file); + } + catch (const Glib::Error& ex) + { + std::cerr << "BuilderToolbar: " << select_toolbar_builder_file << " file not read! " << ex.what() << std::endl; + } + + Gtk::Toolbar* toolbar = nullptr; + builder->get_widget("select-toolbar", toolbar); + if (!toolbar) { + std::cerr << "InkscapeWindow: Failed to load select toolbar!" << std::endl; + return nullptr; + } + + toolbar->reference(); + return GTK_WIDGET(toolbar->gobj()); */ + + auto toolbar = new BuilderToolbar(desktop); + return GTK_WIDGET(toolbar->gobj()); +} + +void +BuilderToolbar::any_value_changed(Glib::RefPtr& adj) +{ + if (_update) { + return; + } + + if ( !_tracker || _tracker->isUpdating() ) { + /* + * When only units are being changed, don't treat changes + * to adjuster values as object changes. + */ + return; + } + _update = true; + + SPDesktop *desktop = _desktop; + Inkscape::Selection *selection = desktop->getSelection(); + SPDocument *document = desktop->getDocument(); + + document->ensureUpToDate (); + + Geom::OptRect bbox_vis = selection->visualBounds(); + Geom::OptRect bbox_geom = selection->geometricBounds(); + Geom::OptRect bbox_user = selection->preferredBounds(); + + if ( !bbox_user ) { + _update = false; + return; + } + + Unit const *unit = _tracker->getActiveUnit(); + g_return_if_fail(unit != nullptr); + + gdouble old_w = bbox_user->dimensions()[Geom::X]; + gdouble old_h = bbox_user->dimensions()[Geom::Y]; + gdouble new_w, new_h, new_x, new_y = 0; + + if (unit->type == Inkscape::Util::UNIT_TYPE_LINEAR) { + new_w = Quantity::convert(_adj_w->get_value(), unit, "px"); + new_h = Quantity::convert(_adj_h->get_value(), unit, "px"); + new_x = Quantity::convert(_adj_x->get_value(), unit, "px"); + new_y = Quantity::convert(_adj_y->get_value(), unit, "px"); + + } else { + gdouble old_x = bbox_user->min()[Geom::X] + (old_w * selection->anchor_x); + gdouble old_y = bbox_user->min()[Geom::Y] + (old_h * selection->anchor_y); + + new_x = old_x * (_adj_x->get_value() / 100 / unit->factor); + new_y = old_y * (_adj_y->get_value() / 100 / unit->factor); + new_w = old_w * (_adj_w->get_value() / 100 / unit->factor); + new_h = old_h * (_adj_h->get_value() / 100 / unit->factor); + } + + // Adjust depending on the selected anchor. + gdouble x0 = (new_x - (old_w * selection->anchor_x)) - ((new_w - old_w) * selection->anchor_x); + gdouble y0 = (new_y - (old_h * selection->anchor_y)) - ((new_h - old_h) * selection->anchor_y); + + gdouble x1 = x0 + new_w; + gdouble xrel = new_w / old_w; + gdouble y1 = y0 + new_h; + gdouble yrel = new_h / old_h; + + // Keep proportions if lock is on + if ( _lock_btn->get_active() ) { + if (adj == _adj_h) { + x1 = x0 + yrel * bbox_user->dimensions()[Geom::X]; + } else if (adj == _adj_w) { + y1 = y0 + xrel * bbox_user->dimensions()[Geom::Y]; + } + } + + // scales and moves, in px + double mh = fabs(x0 - bbox_user->min()[Geom::X]); + double sh = fabs(x1 - bbox_user->max()[Geom::X]); + double mv = fabs(y0 - bbox_user->min()[Geom::Y]); + double sv = fabs(y1 - bbox_user->max()[Geom::Y]); + + // unless the unit is %, convert the scales and moves to the unit + if (unit->type == Inkscape::Util::UNIT_TYPE_LINEAR) { + mh = Quantity::convert(mh, "px", unit); + sh = Quantity::convert(sh, "px", unit); + mv = Quantity::convert(mv, "px", unit); + sv = Quantity::convert(sv, "px", unit); + } + + // do the action only if one of the scales/moves is greater than half the last significant + // digit in the spinbox (currently spinboxes have 3 fractional digits, so that makes 0.0005). If + // the value was changed by the user, the difference will be at least that much; otherwise it's + // just rounding difference between the spinbox value and actual value, so no action is + // performed + char const * const actionkey = ( mh > 5e-4 ? "selector:toolbar:move:horizontal" : + sh > 5e-4 ? "selector:toolbar:scale:horizontal" : + mv > 5e-4 ? "selector:toolbar:move:vertical" : + sv > 5e-4 ? "selector:toolbar:scale:vertical" : nullptr ); + + if (actionkey != nullptr) { + + // FIXME: fix for GTK breakage, see comment in SelectedStyle::on_opacity_changed + desktop->getCanvas()->forced_redraws_start(0); + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool transform_stroke = prefs->getBool("/options/transform/stroke", true); + bool preserve = prefs->getBool("/options/preservetransform/value", false); + + Geom::Affine scaler; + if (prefs->getInt("/tools/bounding_box") == 0) { // SPItem::VISUAL_BBOX + scaler = get_scale_transform_for_variable_stroke (*bbox_vis, *bbox_geom, transform_stroke, preserve, x0, y0, x1, y1); + } else { + // 1) We could have use the newer get_scale_transform_for_variable_stroke() here, but to avoid regressions + // we'll just use the old get_scale_transform_for_uniform_stroke() for now. + // 2) get_scale_transform_for_uniform_stroke() is intended for visual bounding boxes, not geometrical ones! + // we'll trick it into using a geometric bounding box though, by setting the stroke width to zero + scaler = get_scale_transform_for_uniform_stroke (*bbox_geom, 0, 0, false, false, x0, y0, x1, y1); + } + + selection->applyAffine(scaler); + DocumentUndo::maybeDone(document, actionkey, SP_VERB_CONTEXT_SELECT, + _("Transform by toolbar")); + + // resume interruptibility + desktop->getCanvas()->forced_redraws_stop(); + } + + _update = false; +} + +void +BuilderToolbar::layout_widget_update(Inkscape::Selection *sel) +{ + if (_update) { + return; + } + + _update = true; + using Geom::X; + using Geom::Y; + if ( sel && !sel->isEmpty() ) { + Geom::OptRect const bbox(sel->preferredBounds()); + if ( bbox ) { + Unit const *unit = _tracker->getActiveUnit(); + g_return_if_fail(unit != nullptr); + + auto width = bbox->dimensions()[X]; + auto height = bbox->dimensions()[Y]; + auto x = bbox->min()[X] + (width * sel->anchor_x); + auto y = bbox->min()[Y] + (height * sel->anchor_y); + + if (unit->type == Inkscape::Util::UNIT_TYPE_DIMENSIONLESS) { + double const val = unit->factor * 100; + _adj_x->set_value(val); + _adj_y->set_value(val); + _adj_w->set_value(val); + _adj_h->set_value(val); + _tracker->setFullVal( _adj_x->gobj(), x ); + _tracker->setFullVal( _adj_y->gobj(), y ); + _tracker->setFullVal( _adj_w->gobj(), width ); + _tracker->setFullVal( _adj_h->gobj(), height ); + } else { + _adj_x->set_value(Quantity::convert(x, "px", unit)); + _adj_y->set_value(Quantity::convert(y, "px", unit)); + _adj_w->set_value(Quantity::convert(width, "px", unit)); + _adj_h->set_value(Quantity::convert(height, "px", unit)); + } + } + } + + _update = false; +} + +void +BuilderToolbar::on_inkscape_selection_modified(Inkscape::Selection *selection, guint flags) +{ + assert(_desktop->getSelection() == selection); + if ((flags & (SP_OBJECT_MODIFIED_FLAG | + SP_OBJECT_PARENT_MODIFIED_FLAG | + SP_OBJECT_CHILD_MODIFIED_FLAG ))) + { + layout_widget_update(selection); + } +} + +void +BuilderToolbar::on_inkscape_selection_changed(Inkscape::Selection *selection) +{ + assert(_desktop->getSelection() == selection); + { + bool setActive = (selection && !selection->isEmpty()); + + for (auto item : _context_items) { + if ( setActive != item->get_sensitive() ) { + item->set_sensitive(setActive); + } + } + + layout_widget_update(selection); + } +} + +void +BuilderToolbar::toggle_lock() { + if ( _lock_btn->get_active() ) { + _lock_btn->set_icon_name(INKSCAPE_ICON("object-locked")); + } else { + _lock_btn->set_icon_name(INKSCAPE_ICON("object-unlocked")); + } +} + +void +BuilderToolbar::toggle_touch() +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setBool("/tools/select/touch_box", _select_touch_btn->get_active()); +} + +void +BuilderToolbar::toggle_stroke() +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool active = _transform_stroke_btn->get_active(); + prefs->setBool("/options/transform/stroke", active); + if ( active ) { + _desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now stroke width is scaled when objects are scaled.")); + } else { + _desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now stroke width is not scaled when objects are scaled.")); + } +} + +void +BuilderToolbar::toggle_corners() +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool active = _transform_corners_btn->get_active(); + prefs->setBool("/options/transform/rectcorners", active); + if ( active ) { + _desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now rounded rectangle corners are scaled when rectangles are scaled.")); + } else { + _desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now rounded rectangle corners are not scaled when rectangles are scaled.")); + } +} + +void +BuilderToolbar::toggle_gradient() +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool active = _transform_gradient_btn->get_active(); + prefs->setBool("/options/transform/gradient", active); + if ( active ) { + _desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now gradients are transformed along with their objects when those are transformed (moved, scaled, rotated, or skewed).")); + } else { + _desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now gradients remain fixed when objects are transformed (moved, scaled, rotated, or skewed).")); + } +} + +void +BuilderToolbar::toggle_pattern() +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool active = _transform_pattern_btn->get_active(); + prefs->setInt("/options/transform/pattern", active); + if ( active ) { + _desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now patterns are transformed along with their objects when those are transformed (moved, scaled, rotated, or skewed).")); + } else { + _desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now patterns remain fixed when objects are transformed (moved, scaled, rotated, or skewed).")); + } +} + +} +} +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/ui/toolbar/builder-toolbar.h b/src/ui/toolbar/builder-toolbar.h new file mode 100644 index 0000000000..07ca7f154b --- /dev/null +++ b/src/ui/toolbar/builder-toolbar.h @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#ifndef SEEN_SELECT_TOOLBAR_H +#define SEEN_SELECT_TOOLBAR_H + +/** \file + * Selector aux toolbar + */ +/* + * Authors: + * Lauris Kaplinski + * bulia byak + * + * Copyright (C) 2003 authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "toolbar.h" + +class SPDesktop; + +namespace Inkscape { +class Selection; + +namespace UI { + +namespace Widget { +class UnitTracker; +} + +namespace Toolbar { + +class BuilderToolbar : public Toolbar { + using parent_type = Toolbar; + +private: + std::unique_ptr _tracker; + + Glib::RefPtr _adj_x; + Glib::RefPtr _adj_y; + Glib::RefPtr _adj_w; + Glib::RefPtr _adj_h; + Gtk::ToggleToolButton *_lock_btn; + Gtk::ToggleToolButton *_select_touch_btn; + Gtk::ToggleToolButton *_transform_stroke_btn; + Gtk::ToggleToolButton *_transform_corners_btn; + Gtk::ToggleToolButton *_transform_gradient_btn; + Gtk::ToggleToolButton *_transform_pattern_btn; + + std::vector _context_items; + + std::vector _connections; + + bool _update; + + void any_value_changed(Glib::RefPtr& adj); + void layout_widget_update(Inkscape::Selection *sel); + void on_inkscape_selection_modified(Inkscape::Selection *selection, guint flags); + void on_inkscape_selection_changed(Inkscape::Selection *selection); + void toggle_lock(); + void toggle_touch(); + void toggle_stroke(); + void toggle_corners(); + void toggle_gradient(); + void toggle_pattern(); + +protected: + BuilderToolbar(SPDesktop *desktop); + + void on_unrealize() override; + +public: + static GtkWidget * create(SPDesktop *desktop); +}; + +} +} +} +#endif /* !SEEN_SELECT_TOOLBAR_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/toolbar/select-toolbar.h b/src/ui/toolbar/select-toolbar.h index 9e05fe73e8..9e19283257 100644 --- a/src/ui/toolbar/select-toolbar.h +++ b/src/ui/toolbar/select-toolbar.h @@ -1,6 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#ifndef SEEN_SELECT_TOOLBAR_H -#define SEEN_SELECT_TOOLBAR_H +#pragma once /** \file * Selector aux toolbar @@ -76,7 +74,6 @@ public: } } } -#endif /* !SEEN_SELECT_TOOLBAR_H */ /* Local Variables: diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index 9ed1c3e726..c204e8a738 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -63,6 +63,7 @@ #include "ui/toolbar/arc-toolbar.h" #include "ui/toolbar/box3d-toolbar.h" +#include "ui/toolbar/builder-toolbar.h" #include "ui/toolbar/calligraphy-toolbar.h" #include "ui/toolbar/connector-toolbar.h" #include "ui/toolbar/dropper-toolbar.h" @@ -141,7 +142,7 @@ static struct { // If you change the tool_name for Measure or Text here, change it also in desktop-widget.cpp. // clang-format off { "/tools/select", "Select", Inkscape::UI::Toolbar::SelectToolbar::create, nullptr}, - { "/tools/builder", "Builder", Inkscape::UI::Toolbar::SelectToolbar::create, nullptr}, // TODO change this to the builder toolbar + { "/tools/builder", "Builder", Inkscape::UI::Toolbar::BuilderToolbar::create, nullptr}, { "/tools/nodes", "Node", Inkscape::UI::Toolbar::NodeToolbar::create, nullptr}, { "/tools/shapes/rect", "Rect", Inkscape::UI::Toolbar::RectToolbar::create, N_("Style of new rectangles")}, { "/tools/shapes/arc", "Arc", Inkscape::UI::Toolbar::ArcToolbar::create, N_("Style of new ellipses")}, -- GitLab From edb415468b348e693403d90352bda46a3ee0c094 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 15 Jul 2021 19:14:56 +0200 Subject: [PATCH 070/235] Changed the prefsPath in the BuilderTool so that the right toolbar gets created when the tool is selected. --- src/ui/tools/builder-tool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 35c2d4a8cb..f89ac0d256 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -55,7 +55,7 @@ const std::string& BuilderTool::getPrefsPath() { return BuilderTool::prefsPath; } -const std::string BuilderTool::prefsPath = "/tools/select"; +const std::string BuilderTool::prefsPath = "/tools/builder"; BuilderTool::BuilderTool() : ToolBase("select.svg") -- GitLab From c5f79399cf34deb9989cb81125996d16b05fd035 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 15 Jul 2021 19:15:33 +0200 Subject: [PATCH 071/235] Removed all elements from BuilderToolbar. --- src/ui/toolbar/builder-toolbar.cpp | 480 +---------------------------- src/ui/toolbar/builder-toolbar.h | 33 -- 2 files changed, 1 insertion(+), 512 deletions(-) diff --git a/src/ui/toolbar/builder-toolbar.cpp b/src/ui/toolbar/builder-toolbar.cpp index 12a2a507de..74cbf7e3b7 100644 --- a/src/ui/toolbar/builder-toolbar.cpp +++ b/src/ui/toolbar/builder-toolbar.cpp @@ -58,497 +58,19 @@ namespace UI { namespace Toolbar { BuilderToolbar::BuilderToolbar(SPDesktop *desktop) : - Toolbar(desktop), - _tracker(new UnitTracker(Inkscape::Util::UNIT_TYPE_LINEAR)), - _update(false), - _lock_btn(Gtk::manage(new Gtk::ToggleToolButton())), - _select_touch_btn(Gtk::manage(new Gtk::ToggleToolButton())), - _transform_stroke_btn(Gtk::manage(new Gtk::ToggleToolButton())), - _transform_corners_btn(Gtk::manage(new Gtk::ToggleToolButton())), - _transform_gradient_btn(Gtk::manage(new Gtk::ToggleToolButton())), - _transform_pattern_btn(Gtk::manage(new Gtk::ToggleToolButton())) -{ - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - - add_toolbutton_for_verb(SP_VERB_EDIT_SELECT_ALL); - add_toolbutton_for_verb(SP_VERB_EDIT_SELECT_ALL_IN_ALL_LAYERS); - auto deselect_button = add_toolbutton_for_verb(SP_VERB_EDIT_DESELECT); - _context_items.push_back(deselect_button); - - _select_touch_btn->set_label(_("Select by touch")); - _select_touch_btn->set_tooltip_text(_("Toggle selection box to select all touched objects.")); - _select_touch_btn->set_icon_name(INKSCAPE_ICON("selection-touch")); - _select_touch_btn->set_active(prefs->getBool("/tools/select/touch_box", false)); - _select_touch_btn->signal_toggled().connect(sigc::mem_fun(*this, &BuilderToolbar::toggle_touch)); - - add(*_select_touch_btn); - - add(* Gtk::manage(new Gtk::SeparatorToolItem())); - - auto object_rotate_90_ccw_button = add_toolbutton_for_verb(SP_VERB_OBJECT_ROTATE_90_CCW); - _context_items.push_back(object_rotate_90_ccw_button); - - auto object_rotate_90_cw_button = add_toolbutton_for_verb(SP_VERB_OBJECT_ROTATE_90_CW); - _context_items.push_back(object_rotate_90_cw_button); - - auto object_flip_horizontal_button = add_toolbutton_for_verb(SP_VERB_OBJECT_FLIP_HORIZONTAL); - _context_items.push_back(object_flip_horizontal_button); - - auto object_flip_vertical_button = add_toolbutton_for_verb(SP_VERB_OBJECT_FLIP_VERTICAL); - _context_items.push_back(object_flip_vertical_button); - - add(* Gtk::manage(new Gtk::SeparatorToolItem())); - - auto selection_to_front_button = add_toolbutton_for_verb(SP_VERB_SELECTION_TO_FRONT); - _context_items.push_back(selection_to_front_button); - - auto selection_raise_button = add_toolbutton_for_verb(SP_VERB_SELECTION_RAISE); - _context_items.push_back(selection_raise_button); - - auto selection_lower_button = add_toolbutton_for_verb(SP_VERB_SELECTION_LOWER); - _context_items.push_back(selection_lower_button); - - auto selection_to_back_button = add_toolbutton_for_verb(SP_VERB_SELECTION_TO_BACK); - _context_items.push_back(selection_to_back_button); - - add(* Gtk::manage(new Gtk::SeparatorToolItem())); - - _tracker->addUnit(unit_table.getUnit("%")); - _tracker->setActiveUnit( desktop->getNamedView()->display_units ); - - // x-value control - auto x_val = prefs->getDouble("/tools/select/X", 0.0); - _adj_x = Gtk::Adjustment::create(x_val, -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP); - _adj_x->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &BuilderToolbar::any_value_changed), _adj_x)); - _tracker->addAdjustment(_adj_x->gobj()); - - auto x_btn = Gtk::manage(new UI::Widget::SpinButtonToolItem("select-x", - C_("Select toolbar", "X:"), - _adj_x, - SPIN_STEP, 3)); - x_btn->get_spin_button()->addUnitTracker(_tracker.get()); - x_btn->set_focus_widget(_desktop->getCanvas()); - x_btn->set_all_tooltip_text(C_("Select toolbar", "Horizontal coordinate of selection")); - _context_items.push_back(x_btn); - add(*x_btn); - - // y-value control - auto y_val = prefs->getDouble("/tools/select/Y", 0.0); - _adj_y = Gtk::Adjustment::create(y_val, -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP); - _adj_y->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &BuilderToolbar::any_value_changed), _adj_y)); - _tracker->addAdjustment(_adj_y->gobj()); - - auto y_btn = Gtk::manage(new UI::Widget::SpinButtonToolItem("select-y", - C_("Select toolbar", "Y:"), - _adj_y, - SPIN_STEP, 3)); - y_btn->get_spin_button()->addUnitTracker(_tracker.get()); - y_btn->set_focus_widget(_desktop->getCanvas()); - y_btn->set_all_tooltip_text(C_("Select toolbar", "Vertical coordinate of selection")); - _context_items.push_back(y_btn); - add(*y_btn); - - // width-value control - auto w_val = prefs->getDouble("/tools/select/width", 0.0); - _adj_w = Gtk::Adjustment::create(w_val, 0.0, 1e6, SPIN_STEP, SPIN_PAGE_STEP); - _adj_w->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &BuilderToolbar::any_value_changed), _adj_w)); - _tracker->addAdjustment(_adj_w->gobj()); - - auto w_btn = Gtk::manage(new UI::Widget::SpinButtonToolItem("select-width", - C_("Select toolbar", "W:"), - _adj_w, - SPIN_STEP, 3)); - w_btn->get_spin_button()->addUnitTracker(_tracker.get()); - w_btn->set_focus_widget(_desktop->getCanvas()); - w_btn->set_all_tooltip_text(C_("Select toolbar", "Width of selection")); - _context_items.push_back(w_btn); - add(*w_btn); - - // lock toggle - _lock_btn->set_label(_("Lock width and height")); - _lock_btn->set_tooltip_text(_("When locked, change both width and height by the same proportion")); - _lock_btn->set_icon_name(INKSCAPE_ICON("object-unlocked")); - _lock_btn->signal_toggled().connect(sigc::mem_fun(*this, &BuilderToolbar::toggle_lock)); - _lock_btn->set_name("lock"); - add(*_lock_btn); - - // height-value control - auto h_val = prefs->getDouble("/tools/select/height", 0.0); - _adj_h = Gtk::Adjustment::create(h_val, 0.0, 1e6, SPIN_STEP, SPIN_PAGE_STEP); - _adj_h->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &BuilderToolbar::any_value_changed), _adj_h)); - _tracker->addAdjustment(_adj_h->gobj()); - - auto h_btn = Gtk::manage(new UI::Widget::SpinButtonToolItem("select-height", - C_("Select toolbar", "H:"), - _adj_h, - SPIN_STEP, 3)); - h_btn->get_spin_button()->addUnitTracker(_tracker.get()); - h_btn->set_focus_widget(_desktop->getCanvas()); - h_btn->set_all_tooltip_text(C_("Select toolbar", "Height of selection")); - _context_items.push_back(h_btn); - add(*h_btn); + Toolbar(desktop) - // units menu - auto unit_menu = _tracker->create_tool_item(_("Units"), ("") ); - add(*unit_menu); - - add(* Gtk::manage(new Gtk::SeparatorToolItem())); - - _transform_stroke_btn->set_label(_("Scale stroke width")); - _transform_stroke_btn->set_tooltip_text(_("When scaling objects, scale the stroke width by the same proportion")); - _transform_stroke_btn->set_icon_name(INKSCAPE_ICON("transform-affect-stroke")); - _transform_stroke_btn->set_active(prefs->getBool("/options/transform/stroke", true)); - _transform_stroke_btn->signal_toggled().connect(sigc::mem_fun(*this, &BuilderToolbar::toggle_stroke)); - add(*_transform_stroke_btn); - - _transform_corners_btn->set_label(_("Scale rounded corners")); - _transform_corners_btn->set_tooltip_text(_("When scaling rectangles, scale the radii of rounded corners")); - _transform_corners_btn->set_icon_name(INKSCAPE_ICON("transform-affect-rounded-corners")); - _transform_corners_btn->set_active(prefs->getBool("/options/transform/rectcorners", true)); - _transform_corners_btn->signal_toggled().connect(sigc::mem_fun(*this, &BuilderToolbar::toggle_corners)); - add(*_transform_corners_btn); - - _transform_gradient_btn->set_label(_("Move gradients")); - _transform_gradient_btn->set_tooltip_text(_("Move gradients (in fill or stroke) along with the objects")); - _transform_gradient_btn->set_icon_name(INKSCAPE_ICON("transform-affect-gradient")); - _transform_gradient_btn->set_active(prefs->getBool("/options/transform/gradient", true)); - _transform_gradient_btn->signal_toggled().connect(sigc::mem_fun(*this, &BuilderToolbar::toggle_gradient)); - add(*_transform_gradient_btn); - - _transform_pattern_btn->set_label(_("Move patterns")); - _transform_pattern_btn->set_tooltip_text(_("Move patterns (in fill or stroke) along with the objects")); - _transform_pattern_btn->set_icon_name(INKSCAPE_ICON("transform-affect-pattern")); - _transform_pattern_btn->set_active(prefs->getBool("/options/transform/pattern", true)); - _transform_pattern_btn->signal_toggled().connect(sigc::mem_fun(*this, &BuilderToolbar::toggle_pattern)); - add(*_transform_pattern_btn); - - assert(desktop); - auto *selection = desktop->getSelection(); - - // Force update when selection changes. - _connections.emplace_back( // - selection->connectModified(sigc::mem_fun(*this, &BuilderToolbar::on_inkscape_selection_modified))); - _connections.emplace_back( - selection->connectChanged(sigc::mem_fun(*this, &BuilderToolbar::on_inkscape_selection_changed))); - - // Update now. - layout_widget_update(selection); - - for (auto item : _context_items) { - if ( item->is_sensitive() ) { - item->set_sensitive(false); - } - } - - show_all(); -} - -void BuilderToolbar::on_unrealize() { - for (auto &conn : _connections) { - conn.disconnect(); - } - parent_type::on_unrealize(); } GtkWidget * BuilderToolbar::create(SPDesktop *desktop) { - // Select TOOLBAR : SpinButton Addition Left - - /* Glib::ustring select_toolbar_builder_file = get_filename(UIS, "toolbar-select.ui"); - auto builder = Gtk::Builder::create(); - try - { - builder->add_from_file(select_toolbar_builder_file); - } - catch (const Glib::Error& ex) - { - std::cerr << "BuilderToolbar: " << select_toolbar_builder_file << " file not read! " << ex.what() << std::endl; - } - - Gtk::Toolbar* toolbar = nullptr; - builder->get_widget("select-toolbar", toolbar); - if (!toolbar) { - std::cerr << "InkscapeWindow: Failed to load select toolbar!" << std::endl; - return nullptr; - } - - toolbar->reference(); - return GTK_WIDGET(toolbar->gobj()); */ - auto toolbar = new BuilderToolbar(desktop); return GTK_WIDGET(toolbar->gobj()); } -void -BuilderToolbar::any_value_changed(Glib::RefPtr& adj) -{ - if (_update) { - return; - } - - if ( !_tracker || _tracker->isUpdating() ) { - /* - * When only units are being changed, don't treat changes - * to adjuster values as object changes. - */ - return; - } - _update = true; - - SPDesktop *desktop = _desktop; - Inkscape::Selection *selection = desktop->getSelection(); - SPDocument *document = desktop->getDocument(); - - document->ensureUpToDate (); - - Geom::OptRect bbox_vis = selection->visualBounds(); - Geom::OptRect bbox_geom = selection->geometricBounds(); - Geom::OptRect bbox_user = selection->preferredBounds(); - - if ( !bbox_user ) { - _update = false; - return; - } - - Unit const *unit = _tracker->getActiveUnit(); - g_return_if_fail(unit != nullptr); - - gdouble old_w = bbox_user->dimensions()[Geom::X]; - gdouble old_h = bbox_user->dimensions()[Geom::Y]; - gdouble new_w, new_h, new_x, new_y = 0; - - if (unit->type == Inkscape::Util::UNIT_TYPE_LINEAR) { - new_w = Quantity::convert(_adj_w->get_value(), unit, "px"); - new_h = Quantity::convert(_adj_h->get_value(), unit, "px"); - new_x = Quantity::convert(_adj_x->get_value(), unit, "px"); - new_y = Quantity::convert(_adj_y->get_value(), unit, "px"); - - } else { - gdouble old_x = bbox_user->min()[Geom::X] + (old_w * selection->anchor_x); - gdouble old_y = bbox_user->min()[Geom::Y] + (old_h * selection->anchor_y); - - new_x = old_x * (_adj_x->get_value() / 100 / unit->factor); - new_y = old_y * (_adj_y->get_value() / 100 / unit->factor); - new_w = old_w * (_adj_w->get_value() / 100 / unit->factor); - new_h = old_h * (_adj_h->get_value() / 100 / unit->factor); - } - - // Adjust depending on the selected anchor. - gdouble x0 = (new_x - (old_w * selection->anchor_x)) - ((new_w - old_w) * selection->anchor_x); - gdouble y0 = (new_y - (old_h * selection->anchor_y)) - ((new_h - old_h) * selection->anchor_y); - - gdouble x1 = x0 + new_w; - gdouble xrel = new_w / old_w; - gdouble y1 = y0 + new_h; - gdouble yrel = new_h / old_h; - - // Keep proportions if lock is on - if ( _lock_btn->get_active() ) { - if (adj == _adj_h) { - x1 = x0 + yrel * bbox_user->dimensions()[Geom::X]; - } else if (adj == _adj_w) { - y1 = y0 + xrel * bbox_user->dimensions()[Geom::Y]; - } - } - - // scales and moves, in px - double mh = fabs(x0 - bbox_user->min()[Geom::X]); - double sh = fabs(x1 - bbox_user->max()[Geom::X]); - double mv = fabs(y0 - bbox_user->min()[Geom::Y]); - double sv = fabs(y1 - bbox_user->max()[Geom::Y]); - - // unless the unit is %, convert the scales and moves to the unit - if (unit->type == Inkscape::Util::UNIT_TYPE_LINEAR) { - mh = Quantity::convert(mh, "px", unit); - sh = Quantity::convert(sh, "px", unit); - mv = Quantity::convert(mv, "px", unit); - sv = Quantity::convert(sv, "px", unit); - } - - // do the action only if one of the scales/moves is greater than half the last significant - // digit in the spinbox (currently spinboxes have 3 fractional digits, so that makes 0.0005). If - // the value was changed by the user, the difference will be at least that much; otherwise it's - // just rounding difference between the spinbox value and actual value, so no action is - // performed - char const * const actionkey = ( mh > 5e-4 ? "selector:toolbar:move:horizontal" : - sh > 5e-4 ? "selector:toolbar:scale:horizontal" : - mv > 5e-4 ? "selector:toolbar:move:vertical" : - sv > 5e-4 ? "selector:toolbar:scale:vertical" : nullptr ); - - if (actionkey != nullptr) { - - // FIXME: fix for GTK breakage, see comment in SelectedStyle::on_opacity_changed - desktop->getCanvas()->forced_redraws_start(0); - - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - bool transform_stroke = prefs->getBool("/options/transform/stroke", true); - bool preserve = prefs->getBool("/options/preservetransform/value", false); - - Geom::Affine scaler; - if (prefs->getInt("/tools/bounding_box") == 0) { // SPItem::VISUAL_BBOX - scaler = get_scale_transform_for_variable_stroke (*bbox_vis, *bbox_geom, transform_stroke, preserve, x0, y0, x1, y1); - } else { - // 1) We could have use the newer get_scale_transform_for_variable_stroke() here, but to avoid regressions - // we'll just use the old get_scale_transform_for_uniform_stroke() for now. - // 2) get_scale_transform_for_uniform_stroke() is intended for visual bounding boxes, not geometrical ones! - // we'll trick it into using a geometric bounding box though, by setting the stroke width to zero - scaler = get_scale_transform_for_uniform_stroke (*bbox_geom, 0, 0, false, false, x0, y0, x1, y1); - } - - selection->applyAffine(scaler); - DocumentUndo::maybeDone(document, actionkey, SP_VERB_CONTEXT_SELECT, - _("Transform by toolbar")); - - // resume interruptibility - desktop->getCanvas()->forced_redraws_stop(); - } - - _update = false; -} - -void -BuilderToolbar::layout_widget_update(Inkscape::Selection *sel) -{ - if (_update) { - return; - } - - _update = true; - using Geom::X; - using Geom::Y; - if ( sel && !sel->isEmpty() ) { - Geom::OptRect const bbox(sel->preferredBounds()); - if ( bbox ) { - Unit const *unit = _tracker->getActiveUnit(); - g_return_if_fail(unit != nullptr); - - auto width = bbox->dimensions()[X]; - auto height = bbox->dimensions()[Y]; - auto x = bbox->min()[X] + (width * sel->anchor_x); - auto y = bbox->min()[Y] + (height * sel->anchor_y); - - if (unit->type == Inkscape::Util::UNIT_TYPE_DIMENSIONLESS) { - double const val = unit->factor * 100; - _adj_x->set_value(val); - _adj_y->set_value(val); - _adj_w->set_value(val); - _adj_h->set_value(val); - _tracker->setFullVal( _adj_x->gobj(), x ); - _tracker->setFullVal( _adj_y->gobj(), y ); - _tracker->setFullVal( _adj_w->gobj(), width ); - _tracker->setFullVal( _adj_h->gobj(), height ); - } else { - _adj_x->set_value(Quantity::convert(x, "px", unit)); - _adj_y->set_value(Quantity::convert(y, "px", unit)); - _adj_w->set_value(Quantity::convert(width, "px", unit)); - _adj_h->set_value(Quantity::convert(height, "px", unit)); - } - } - } - - _update = false; -} - -void -BuilderToolbar::on_inkscape_selection_modified(Inkscape::Selection *selection, guint flags) -{ - assert(_desktop->getSelection() == selection); - if ((flags & (SP_OBJECT_MODIFIED_FLAG | - SP_OBJECT_PARENT_MODIFIED_FLAG | - SP_OBJECT_CHILD_MODIFIED_FLAG ))) - { - layout_widget_update(selection); - } -} - -void -BuilderToolbar::on_inkscape_selection_changed(Inkscape::Selection *selection) -{ - assert(_desktop->getSelection() == selection); - { - bool setActive = (selection && !selection->isEmpty()); - - for (auto item : _context_items) { - if ( setActive != item->get_sensitive() ) { - item->set_sensitive(setActive); - } - } - - layout_widget_update(selection); - } -} - -void -BuilderToolbar::toggle_lock() { - if ( _lock_btn->get_active() ) { - _lock_btn->set_icon_name(INKSCAPE_ICON("object-locked")); - } else { - _lock_btn->set_icon_name(INKSCAPE_ICON("object-unlocked")); - } -} - -void -BuilderToolbar::toggle_touch() -{ - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setBool("/tools/select/touch_box", _select_touch_btn->get_active()); -} - -void -BuilderToolbar::toggle_stroke() -{ - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - bool active = _transform_stroke_btn->get_active(); - prefs->setBool("/options/transform/stroke", active); - if ( active ) { - _desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now stroke width is scaled when objects are scaled.")); - } else { - _desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now stroke width is not scaled when objects are scaled.")); - } -} - -void -BuilderToolbar::toggle_corners() -{ - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - bool active = _transform_corners_btn->get_active(); - prefs->setBool("/options/transform/rectcorners", active); - if ( active ) { - _desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now rounded rectangle corners are scaled when rectangles are scaled.")); - } else { - _desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now rounded rectangle corners are not scaled when rectangles are scaled.")); - } -} - -void -BuilderToolbar::toggle_gradient() -{ - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - bool active = _transform_gradient_btn->get_active(); - prefs->setBool("/options/transform/gradient", active); - if ( active ) { - _desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now gradients are transformed along with their objects when those are transformed (moved, scaled, rotated, or skewed).")); - } else { - _desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now gradients remain fixed when objects are transformed (moved, scaled, rotated, or skewed).")); - } -} - -void -BuilderToolbar::toggle_pattern() -{ - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - bool active = _transform_pattern_btn->get_active(); - prefs->setInt("/options/transform/pattern", active); - if ( active ) { - _desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now patterns are transformed along with their objects when those are transformed (moved, scaled, rotated, or skewed).")); - } else { - _desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Now patterns remain fixed when objects are transformed (moved, scaled, rotated, or skewed).")); - } -} - } } } diff --git a/src/ui/toolbar/builder-toolbar.h b/src/ui/toolbar/builder-toolbar.h index 07ca7f154b..6173852d74 100644 --- a/src/ui/toolbar/builder-toolbar.h +++ b/src/ui/toolbar/builder-toolbar.h @@ -33,42 +33,9 @@ namespace Toolbar { class BuilderToolbar : public Toolbar { using parent_type = Toolbar; -private: - std::unique_ptr _tracker; - - Glib::RefPtr _adj_x; - Glib::RefPtr _adj_y; - Glib::RefPtr _adj_w; - Glib::RefPtr _adj_h; - Gtk::ToggleToolButton *_lock_btn; - Gtk::ToggleToolButton *_select_touch_btn; - Gtk::ToggleToolButton *_transform_stroke_btn; - Gtk::ToggleToolButton *_transform_corners_btn; - Gtk::ToggleToolButton *_transform_gradient_btn; - Gtk::ToggleToolButton *_transform_pattern_btn; - - std::vector _context_items; - - std::vector _connections; - - bool _update; - - void any_value_changed(Glib::RefPtr& adj); - void layout_widget_update(Inkscape::Selection *sel); - void on_inkscape_selection_modified(Inkscape::Selection *selection, guint flags); - void on_inkscape_selection_changed(Inkscape::Selection *selection); - void toggle_lock(); - void toggle_touch(); - void toggle_stroke(); - void toggle_corners(); - void toggle_gradient(); - void toggle_pattern(); - protected: BuilderToolbar(SPDesktop *desktop); - void on_unrealize() override; - public: static GtkWidget * create(SPDesktop *desktop); }; -- GitLab From 1b2218fdf1c5cc6fcc1d5bc1d32455f70fc08cf7 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 15 Jul 2021 23:39:51 +0200 Subject: [PATCH 072/235] Added symbolic icons for the Builder tool and changed the old icons. --- .../Tango/scalable/actions/tool-builder.svg | 149 ++++-------------- .../hicolor/scalable/actions/tool-builder.svg | 9 +- .../actions/tool-builder-symbolic.svg | 11 ++ .../actions/tool-builder-symbolic.svg | 76 +++++++++ 4 files changed, 122 insertions(+), 123 deletions(-) create mode 100644 share/icons/hicolor/symbolic/actions/tool-builder-symbolic.svg create mode 100644 share/icons/multicolor/symbolic/actions/tool-builder-symbolic.svg diff --git a/share/icons/Tango/scalable/actions/tool-builder.svg b/share/icons/Tango/scalable/actions/tool-builder.svg index c95560c287..d2d7b45fb9 100644 --- a/share/icons/Tango/scalable/actions/tool-builder.svg +++ b/share/icons/Tango/scalable/actions/tool-builder.svg @@ -9,13 +9,13 @@ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg1" - width="23.999981" - height="23.999981" + width="16.000051" + height="15.999972" version="1.1" - sodipodi:docname="tool-pointer.svg" - viewBox="0 0 23.999981 23.999981"> + sodipodi:docname="path-union.svg" + viewBox="0 0 16.000051 15.999972"> + id="metadata23"> @@ -36,134 +36,47 @@ inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480" - id="namedview35" + id="namedview21" showgrid="false" /> + x1="226.0206" + x2="226.0206" + xlink:href="#linearGradient10737" + y1="101.1009" + y2="84.907883" /> - - - - - - - - - - - + id="linearGradient10737"> + style="stop-color:#a7bfd9;stop-opacity:1;" /> + style="stop-color:#4581c1;stop-opacity:1;" /> - - - - - - - + id="union" + inkscape:label="#union" + style="display:inline" + transform="matrix(0.988078,0,0,0.988078,-214.32597,-83.895605)"> + + sodipodi:nodetypes="csccccccssc" + style="display:inline;opacity:0.32000002;fill:#729fcf;fill-opacity:0;fill-rule:evenodd;stroke:#eeeeec;stroke-width:0.74440622;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none" /> diff --git a/share/icons/hicolor/scalable/actions/tool-builder.svg b/share/icons/hicolor/scalable/actions/tool-builder.svg index c402bef553..b7cec90686 100644 --- a/share/icons/hicolor/scalable/actions/tool-builder.svg +++ b/share/icons/hicolor/scalable/actions/tool-builder.svg @@ -9,8 +9,8 @@ xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg1" - width="24" - height="24" + width="16.0937" + height="16" > - - - + + diff --git a/share/icons/hicolor/symbolic/actions/tool-builder-symbolic.svg b/share/icons/hicolor/symbolic/actions/tool-builder-symbolic.svg new file mode 100644 index 0000000000..a0b6fd0a24 --- /dev/null +++ b/share/icons/hicolor/symbolic/actions/tool-builder-symbolic.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/share/icons/multicolor/symbolic/actions/tool-builder-symbolic.svg b/share/icons/multicolor/symbolic/actions/tool-builder-symbolic.svg new file mode 100644 index 0000000000..3c4525c3d0 --- /dev/null +++ b/share/icons/multicolor/symbolic/actions/tool-builder-symbolic.svg @@ -0,0 +1,76 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + -- GitLab From 78f9f8815be39035d17733aaa806c1e454655a73 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 15 Jul 2021 23:42:21 +0200 Subject: [PATCH 073/235] Edited the comments and the macros for the BuilderToolbar. --- src/ui/toolbar/builder-toolbar.cpp | 20 +++----------------- src/ui/toolbar/builder-toolbar.h | 29 +++++++---------------------- 2 files changed, 10 insertions(+), 39 deletions(-) diff --git a/src/ui/toolbar/builder-toolbar.cpp b/src/ui/toolbar/builder-toolbar.cpp index 74cbf7e3b7..c1faf4e713 100644 --- a/src/ui/toolbar/builder-toolbar.cpp +++ b/src/ui/toolbar/builder-toolbar.cpp @@ -1,14 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Selector aux toolbar + * A toolbar for the Builder tool. * * Authors: - * Lauris Kaplinski - * bulia byak - * Jon A. Cruz - * Abhishek Sharma + * Osama Ahmad * - * Copyright (C) 2003-2005 authors + * Copyright (C) 2021 authors * * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ @@ -74,14 +71,3 @@ BuilderToolbar::create(SPDesktop *desktop) } } } - -/* - Local Variables: - mode:c++ - c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) - indent-tabs-mode:nil - fill-column:99 - End: -*/ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/ui/toolbar/builder-toolbar.h b/src/ui/toolbar/builder-toolbar.h index 6173852d74..707325c67f 100644 --- a/src/ui/toolbar/builder-toolbar.h +++ b/src/ui/toolbar/builder-toolbar.h @@ -1,16 +1,13 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#ifndef SEEN_SELECT_TOOLBAR_H -#define SEEN_SELECT_TOOLBAR_H +#pragma once -/** \file - * Selector aux toolbar - */ +// SPDX-License-Identifier: GPL-2.0-or-later /* + * A toolbar for the Builder tool. + * * Authors: - * Lauris Kaplinski - * bulia byak + * Osama Ahmad * - * Copyright (C) 2003 authors + * Copyright (C) 2021 authors * * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ @@ -42,16 +39,4 @@ public: } } -} -#endif /* !SEEN_SELECT_TOOLBAR_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 : +} \ No newline at end of file -- GitLab From 6919dc3a47675d31a99bddbff1a78bfe1e7c1fdd Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 15 Jul 2021 23:45:44 +0200 Subject: [PATCH 074/235] Added the mode buttons to the BuilderToolbar. --- src/ui/toolbar/builder-toolbar.cpp | 99 +++++++++++++++++++++++++++--- src/ui/toolbar/builder-toolbar.h | 37 +++++++++++ 2 files changed, 127 insertions(+), 9 deletions(-) diff --git a/src/ui/toolbar/builder-toolbar.cpp b/src/ui/toolbar/builder-toolbar.cpp index c1faf4e713..ea4770b77d 100644 --- a/src/ui/toolbar/builder-toolbar.cpp +++ b/src/ui/toolbar/builder-toolbar.cpp @@ -41,26 +41,107 @@ #include "widgets/widget-sizes.h" #include "io/resource.h" -using Inkscape::IO::Resource::UIS; - - -using Inkscape::UI::Widget::UnitTracker; -using Inkscape::Util::Unit; -using Inkscape::Util::Quantity; -using Inkscape::DocumentUndo; -using Inkscape::Util::unit_table; - namespace Inkscape { namespace UI { namespace Toolbar { BuilderToolbar::BuilderToolbar(SPDesktop *desktop) : Toolbar(desktop) +{ + mode_buttons_init(); + add_separator(); + show_all(); +} + +void BuilderToolbar::mode_buttons_init() +{ + // TODO change the icons. + const static std::vector mode_buttons_descriptors = { + { + .label = _("Just Select"), + .tooltip_text = _("Just select whatever the mouse moves over"), + .icon_name = "tool-pointer", + .handler = &BuilderToolbar::set_mode_just_select, + }, + { + .label = _("Union"), + .tooltip_text = _("Union whatever the mouse moves over"), + .icon_name = "path-union", + .handler = &BuilderToolbar::set_mode_union, + }, + { + .label = _("Delete"), + .tooltip_text = _("Delete whatever the mouse moves over"), + .icon_name = "path-difference", + .handler = &BuilderToolbar::set_mode_delete, + }, + }; + + mode_buttons_init_create_buttons(mode_buttons_descriptors); + mode_buttons_init_set_active_button(); + mode_buttons_init_add_buttons(); +} +void BuilderToolbar::mode_buttons_init_create_buttons(const std::vector& descriptors) { + Gtk::RadioToolButton::Group mode_group; + + for (auto& mode : descriptors) + { + auto button = Gtk::manage(new Gtk::RadioToolButton((mode.label))); + button->set_tooltip_text((mode.tooltip_text)); + button->set_icon_name(INKSCAPE_ICON(mode.icon_name)); + button->set_group(mode_group); + _mode_buttons.push_back(button); + _mode_handlers.push_back(mode.handler); + } } +void BuilderToolbar::mode_buttons_init_set_active_button() +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + gint type = prefs->getInt("/tools/builder/mode", 0); + _mode_buttons[type]->set_active(); +} + +void BuilderToolbar::mode_buttons_init_add_buttons() +{ + int button_index = 0; + for (auto button : _mode_buttons) + { + button->set_sensitive(); + button->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &BuilderToolbar::mode_changed), button_index++)); + add(*button); + } +} + +void BuilderToolbar::mode_changed(int mode) +{ + auto handler = _mode_handlers[mode]; + (this->*handler)(); +} + +void BuilderToolbar::set_mode_just_select() +{ + std::cout << "Just Select\n"; +} + +void BuilderToolbar::set_mode_union() +{ + std::cout << "Union\n"; +} + +void BuilderToolbar::set_mode_delete() +{ + std::cout << "Delete\n"; +} + +void BuilderToolbar::add_separator() +{ + add(* Gtk::manage(new Gtk::SeparatorToolItem())); +} + GtkWidget * BuilderToolbar::create(SPDesktop *desktop) { diff --git a/src/ui/toolbar/builder-toolbar.h b/src/ui/toolbar/builder-toolbar.h index 707325c67f..8095d7e264 100644 --- a/src/ui/toolbar/builder-toolbar.h +++ b/src/ui/toolbar/builder-toolbar.h @@ -12,6 +12,10 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ +#include + +#include + #include "toolbar.h" class SPDesktop; @@ -27,9 +31,42 @@ class UnitTracker; namespace Toolbar { +class BuilderToolbar; + +// A method that belongs to BuilderToolbar that returns void and accepts noting. +typedef void (BuilderToolbar::*BuilderToolbarVoidMethod)(); + +struct ButtonDescriptor +{ + std::string label; + std::string tooltip_text; + std::string icon_name; + BuilderToolbarVoidMethod handler; +}; + class BuilderToolbar : public Toolbar { using parent_type = Toolbar; +private: + std::vector _mode_buttons; + std::vector _mode_handlers; + +// Mode related methods { + void mode_buttons_init(); + void mode_buttons_init_create_buttons(const std::vector& descriptors); + void mode_buttons_init_set_active_button(); + void mode_buttons_init_add_buttons(); + + void mode_changed(int mode); + + // handlers that gets called when mode is changed: + void set_mode_just_select(); + void set_mode_union(); + void set_mode_delete(); +// } + + void add_separator(); + protected: BuilderToolbar(SPDesktop *desktop); -- GitLab From 5455a595bbe55f349b75a9e2d31b94b216b489fb Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 16 Jul 2021 00:46:41 +0200 Subject: [PATCH 075/235] Added the buttons for the boolean operations. --- src/ui/toolbar/builder-toolbar.cpp | 64 ++++++++++++++++++++++++++++++ src/ui/toolbar/builder-toolbar.h | 8 ++++ 2 files changed, 72 insertions(+) diff --git a/src/ui/toolbar/builder-toolbar.cpp b/src/ui/toolbar/builder-toolbar.cpp index ea4770b77d..5166bfcd01 100644 --- a/src/ui/toolbar/builder-toolbar.cpp +++ b/src/ui/toolbar/builder-toolbar.cpp @@ -50,6 +50,9 @@ BuilderToolbar::BuilderToolbar(SPDesktop *desktop) : { mode_buttons_init(); add_separator(); + boolop_buttons_init(); + add_separator(); + show_all(); } @@ -137,6 +140,67 @@ void BuilderToolbar::set_mode_delete() std::cout << "Delete\n"; } +void BuilderToolbar::boolop_buttons_init() +{ + boolop_buttons_init_actions(); + boolop_buttons_init_verbs(); +} + +void BuilderToolbar::boolop_buttons_init_actions() +{ + // TODO find a better way than this. Using verbs is easier. + const static std::vector boolop_buttons_descriptors = { + { + .label = _("Fracture"), + .tooltip_text = _("Break the selected paths into non-overlapping (fractured) paths"), + .icon_name = "path-fracture", + .handler = &BuilderToolbar::perform_fracture, + }, + { + .label = _("Flatten"), + .tooltip_text = _("Remove any hidden part part of the selection (has an item on top of it)"), + .icon_name = "path-flatten", + .handler = &BuilderToolbar::perform_flatten, + }, + }; + + boolop_buttons_init_actions_add_buttons(boolop_buttons_descriptors); +} + +void BuilderToolbar::boolop_buttons_init_actions_add_buttons(const std::vector& descriptors) +{ + for (auto& boolop : descriptors) + { + auto button = Gtk::manage(new Gtk::ToolButton(boolop.label)); + button->set_tooltip_text((boolop.tooltip_text)); + button->set_icon_name(INKSCAPE_ICON(boolop.icon_name)); + button->signal_clicked().connect(sigc::mem_fun(*this, boolop.handler)); + add(*button); + } +} + +void BuilderToolbar::perform_fracture() +{ + auto selection = _desktop->getSelection(); + selection->break_into_non_overlapping_pieces(); +} + +void BuilderToolbar::perform_flatten() +{ + auto selection = _desktop->getSelection(); + selection->flatten(); +} + +void BuilderToolbar::boolop_buttons_init_verbs() +{ + add_toolbutton_for_verb(SP_VERB_SELECTION_UNION); + add_toolbutton_for_verb(SP_VERB_SELECTION_DIFF); + add_toolbutton_for_verb(SP_VERB_SELECTION_INTERSECT); + add_toolbutton_for_verb(SP_VERB_SELECTION_SYMDIFF); + add_toolbutton_for_verb(SP_VERB_SELECTION_CUT); + add_toolbutton_for_verb(SP_VERB_SELECTION_SLICE); +} + void BuilderToolbar::add_separator() { add(* Gtk::manage(new Gtk::SeparatorToolItem())); diff --git a/src/ui/toolbar/builder-toolbar.h b/src/ui/toolbar/builder-toolbar.h index 8095d7e264..885cc78136 100644 --- a/src/ui/toolbar/builder-toolbar.h +++ b/src/ui/toolbar/builder-toolbar.h @@ -65,6 +65,14 @@ private: void set_mode_delete(); // } + void boolop_buttons_init(); + void boolop_buttons_init_actions(); + void boolop_buttons_init_actions_add_buttons(const std::vector& descriptors); + void handle_button_with_handler(BuilderToolbarVoidMethod handler); + void boolop_buttons_init_verbs(); + void perform_fracture(); + void perform_flatten(); + void add_separator(); protected: -- GitLab From 1f30a53883ffcf1fd23c3bc1b8b692ea98a16dd0 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 16 Jul 2021 11:49:57 +0200 Subject: [PATCH 076/235] Added the buttons for the "Compound path operations". Compound path operations are like Combine and Break Apart. --- src/ui/toolbar/builder-toolbar.cpp | 7 +++++++ src/ui/toolbar/builder-toolbar.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/src/ui/toolbar/builder-toolbar.cpp b/src/ui/toolbar/builder-toolbar.cpp index 5166bfcd01..8b68c0ca08 100644 --- a/src/ui/toolbar/builder-toolbar.cpp +++ b/src/ui/toolbar/builder-toolbar.cpp @@ -52,6 +52,7 @@ BuilderToolbar::BuilderToolbar(SPDesktop *desktop) : add_separator(); boolop_buttons_init(); add_separator(); + compound_operations_buttons_init(); show_all(); } @@ -201,6 +202,12 @@ void BuilderToolbar::boolop_buttons_init_verbs() add_toolbutton_for_verb(SP_VERB_SELECTION_SLICE); } +void BuilderToolbar::compound_operations_buttons_init() +{ + add_toolbutton_for_verb(SP_VERB_SELECTION_COMBINE); + add_toolbutton_for_verb(SP_VERB_SELECTION_BREAK_APART); +} + void BuilderToolbar::add_separator() { add(* Gtk::manage(new Gtk::SeparatorToolItem())); diff --git a/src/ui/toolbar/builder-toolbar.h b/src/ui/toolbar/builder-toolbar.h index 885cc78136..715b00993f 100644 --- a/src/ui/toolbar/builder-toolbar.h +++ b/src/ui/toolbar/builder-toolbar.h @@ -73,6 +73,8 @@ private: void perform_fracture(); void perform_flatten(); + void compound_operations_buttons_init(); + void add_separator(); protected: -- GitLab From ec94381d96b11c7a76abb39070442ca3fc00630a Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 16 Jul 2021 11:57:02 +0200 Subject: [PATCH 077/235] Added the BuilderToolMode enum class and _current_mode field. --- src/ui/toolbar/builder-toolbar.cpp | 16 ++++++++++++---- src/ui/toolbar/builder-toolbar.h | 8 ++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/ui/toolbar/builder-toolbar.cpp b/src/ui/toolbar/builder-toolbar.cpp index 8b68c0ca08..1f4ead0992 100644 --- a/src/ui/toolbar/builder-toolbar.cpp +++ b/src/ui/toolbar/builder-toolbar.cpp @@ -60,6 +60,8 @@ BuilderToolbar::BuilderToolbar(SPDesktop *desktop) : void BuilderToolbar::mode_buttons_init() { // TODO change the icons. + // if you're going to edit this, remember to edit the BuilderToolMode enum + // in the header file as well. remember that they have to be in the same order. const static std::vector mode_buttons_descriptors = { { .label = _("Just Select"), @@ -99,7 +101,6 @@ void BuilderToolbar::mode_buttons_init_create_buttons(const std::vector*handler)(); } void BuilderToolbar::set_mode_just_select() { - std::cout << "Just Select\n"; + _current_mode = BuilderToolMode::JUST_SELECT; } void BuilderToolbar::set_mode_union() { - std::cout << "Union\n"; + _current_mode = BuilderToolMode::UNION; } void BuilderToolbar::set_mode_delete() { - std::cout << "Delete\n"; + _current_mode = BuilderToolMode::DELETE; } void BuilderToolbar::boolop_buttons_init() @@ -220,6 +223,11 @@ BuilderToolbar::create(SPDesktop *desktop) return GTK_WIDGET(toolbar->gobj()); } +BuilderToolMode BuilderToolbar::get_mode() const +{ + return _current_mode; +} + } } } diff --git a/src/ui/toolbar/builder-toolbar.h b/src/ui/toolbar/builder-toolbar.h index 715b00993f..69d205389b 100644 --- a/src/ui/toolbar/builder-toolbar.h +++ b/src/ui/toolbar/builder-toolbar.h @@ -44,12 +44,19 @@ struct ButtonDescriptor BuilderToolbarVoidMethod handler; }; +enum class BuilderToolMode { + JUST_SELECT = 0, + UNION = 1, + DELETE = 2, +}; + class BuilderToolbar : public Toolbar { using parent_type = Toolbar; private: std::vector _mode_buttons; std::vector _mode_handlers; + BuilderToolMode _current_mode; // Mode related methods { void mode_buttons_init(); @@ -82,6 +89,7 @@ protected: public: static GtkWidget * create(SPDesktop *desktop); + BuilderToolMode get_mode() const; }; } -- GitLab From acce6619b486f6e3f8666940df1264bf558f2b71 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 16 Jul 2021 12:10:45 +0200 Subject: [PATCH 078/235] Made the verb boolop buttons show first so that the order is the same as the path menu. --- src/ui/toolbar/builder-toolbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/toolbar/builder-toolbar.cpp b/src/ui/toolbar/builder-toolbar.cpp index 1f4ead0992..4b5f2bc95f 100644 --- a/src/ui/toolbar/builder-toolbar.cpp +++ b/src/ui/toolbar/builder-toolbar.cpp @@ -146,8 +146,8 @@ void BuilderToolbar::set_mode_delete() void BuilderToolbar::boolop_buttons_init() { - boolop_buttons_init_actions(); boolop_buttons_init_verbs(); + boolop_buttons_init_actions(); } void BuilderToolbar::boolop_buttons_init_actions() -- GitLab From 376c5754f5743750cd5cb1455b2013c91faf49ea Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 16 Jul 2021 12:13:36 +0200 Subject: [PATCH 079/235] removed a useless function that wasn't implemented in builder-toolbar.h. --- src/ui/toolbar/builder-toolbar.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ui/toolbar/builder-toolbar.h b/src/ui/toolbar/builder-toolbar.h index 69d205389b..68e0671c9c 100644 --- a/src/ui/toolbar/builder-toolbar.h +++ b/src/ui/toolbar/builder-toolbar.h @@ -75,7 +75,6 @@ private: void boolop_buttons_init(); void boolop_buttons_init_actions(); void boolop_buttons_init_actions_add_buttons(const std::vector& descriptors); - void handle_button_with_handler(BuilderToolbarVoidMethod handler); void boolop_buttons_init_verbs(); void perform_fracture(); void perform_flatten(); -- GitLab From d3bc61092d106804b8b76bbe14555b4c4263883f Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 16 Jul 2021 12:48:29 +0200 Subject: [PATCH 080/235] Added the Split Non-intersecting Paths button to the compound path operations buttons. --- src/ui/toolbar/builder-toolbar.cpp | 27 +++++++++++++++++++++++++++ src/ui/toolbar/builder-toolbar.h | 3 +++ 2 files changed, 30 insertions(+) diff --git a/src/ui/toolbar/builder-toolbar.cpp b/src/ui/toolbar/builder-toolbar.cpp index 4b5f2bc95f..23c7583bb4 100644 --- a/src/ui/toolbar/builder-toolbar.cpp +++ b/src/ui/toolbar/builder-toolbar.cpp @@ -206,6 +206,33 @@ void BuilderToolbar::boolop_buttons_init_verbs() } void BuilderToolbar::compound_operations_buttons_init() +{ + compound_operations_buttons_init_verbs(); + compound_operations_buttons_init_actions(); +} + +void BuilderToolbar::compound_operations_buttons_init_actions() +{ + // TODO find a better way than this. Using verbs is easier. + const static std::vector boolop_buttons_descriptors = { + { + .label = _("Split Non-Intersecting paths"), + .tooltip_text = _("Split the combined path into separate non-intersecting paths"), + .icon_name = "path-split-non-intersecting", + .handler = &BuilderToolbar::perform_split_non_intersecting, + }, + }; + + boolop_buttons_init_actions_add_buttons(boolop_buttons_descriptors); +} + +void BuilderToolbar::perform_split_non_intersecting() +{ + auto selection = _desktop->getSelection(); + selection->splitNonIntersecting(); +} + +void BuilderToolbar::compound_operations_buttons_init_verbs() { add_toolbutton_for_verb(SP_VERB_SELECTION_COMBINE); add_toolbutton_for_verb(SP_VERB_SELECTION_BREAK_APART); diff --git a/src/ui/toolbar/builder-toolbar.h b/src/ui/toolbar/builder-toolbar.h index 68e0671c9c..f0757dd1e7 100644 --- a/src/ui/toolbar/builder-toolbar.h +++ b/src/ui/toolbar/builder-toolbar.h @@ -80,6 +80,9 @@ private: void perform_flatten(); void compound_operations_buttons_init(); + void compound_operations_buttons_init_actions(); + void compound_operations_buttons_init_verbs(); + void perform_split_non_intersecting(); void add_separator(); -- GitLab From 388eb1ad5f9d6663bf7abd5848236794e781064b Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 16 Jul 2021 12:49:10 +0200 Subject: [PATCH 081/235] changed the symbolic icons for both flatten and fracture. --- ...-flatten.svg => path-flatten-symbolic.svg} | 28 ++++++++-------- ...racture.svg => path-fracture-symbolic.svg} | 32 +++++++++---------- ...-flatten.svg => path-flatten-symbolic.svg} | 28 ++++++++-------- ...racture.svg => path-fracture-symbolic.svg} | 32 +++++++++---------- 4 files changed, 58 insertions(+), 62 deletions(-) rename share/icons/hicolor/symbolic/actions/{path-flatten.svg => path-flatten-symbolic.svg} (81%) rename share/icons/hicolor/symbolic/actions/{path-fracture.svg => path-fracture-symbolic.svg} (66%) rename share/icons/multicolor/symbolic/actions/{path-flatten.svg => path-flatten-symbolic.svg} (81%) rename share/icons/multicolor/symbolic/actions/{path-fracture.svg => path-fracture-symbolic.svg} (66%) diff --git a/share/icons/hicolor/symbolic/actions/path-flatten.svg b/share/icons/hicolor/symbolic/actions/path-flatten-symbolic.svg similarity index 81% rename from share/icons/hicolor/symbolic/actions/path-flatten.svg rename to share/icons/hicolor/symbolic/actions/path-flatten-symbolic.svg index bbb2a098b4..a4c184ba39 100644 --- a/share/icons/hicolor/symbolic/actions/path-flatten.svg +++ b/share/icons/hicolor/symbolic/actions/path-flatten-symbolic.svg @@ -5,8 +5,8 @@ width="16" height="16" viewBox="0 0 16 16" - sodipodi:docname="path-flatten.svg" - inkscape:version="1.2-dev (ea32440028, 2021-07-08, custom)" + sodipodi:docname="path-flatten-symbolic.svg" + inkscape:version="1.1 (c68e22c387, 2021-05-23)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" @@ -44,12 +44,12 @@ id="namedview" showgrid="true" inkscape:zoom="45.254834" - inkscape:cx="7.6897862" - inkscape:cy="8.4742328" - inkscape:window-width="1920" - inkscape:window-height="968" - inkscape:window-x="0" - inkscape:window-y="27" + inkscape:cx="3.7123106" + inkscape:cy="5.4800775" + inkscape:window-width="1858" + inkscape:window-height="1177" + inkscape:window-x="54" + inkscape:window-y="-8" inkscape:window-maximized="1" inkscape:current-layer="svg1" inkscape:document-rotation="0" @@ -64,14 +64,13 @@ inkscape:bbox-nodes="true" inkscape:snap-bbox-edge-midpoints="true" inkscape:snap-bbox-midpoints="true" - inkscape:snap-center="true" - inkscape:lockguides="true"> + inkscape:snap-center="true"> @@ -89,8 +88,7 @@ + r="4.5" /> diff --git a/share/icons/hicolor/symbolic/actions/path-fracture.svg b/share/icons/hicolor/symbolic/actions/path-fracture-symbolic.svg similarity index 66% rename from share/icons/hicolor/symbolic/actions/path-fracture.svg rename to share/icons/hicolor/symbolic/actions/path-fracture-symbolic.svg index a0476e22b4..b3108eb60e 100644 --- a/share/icons/hicolor/symbolic/actions/path-fracture.svg +++ b/share/icons/hicolor/symbolic/actions/path-fracture-symbolic.svg @@ -5,8 +5,8 @@ width="16" height="16" viewBox="0 0 16 16" - sodipodi:docname="path-fracture.svg" - inkscape:version="1.2-dev (c301930b54, 2021-06-21, custom)" + sodipodi:docname="path-division-all-symbolic.svg" + inkscape:version="1.1 (c68e22c387, 2021-05-23)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" @@ -28,9 +28,8 @@ + + + + + + + + + + + diff --git a/share/icons/hicolor/cursors/cursor-intersect.svg b/share/icons/hicolor/cursors/cursor-intersect.svg new file mode 100644 index 0000000000..5217579dfd --- /dev/null +++ b/share/icons/hicolor/cursors/cursor-intersect.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/cursors/cursor-union.svg b/share/icons/hicolor/cursors/cursor-union.svg new file mode 100644 index 0000000000..8c657a2be5 --- /dev/null +++ b/share/icons/hicolor/cursors/cursor-union.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 02c6a8a5b8..b3494a6c4b 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -148,6 +148,15 @@ void BuilderTool::setup() { if (prefs->getBool("/tools/select/gradientdrag")) { this->enableGrDrag(); } + + mode_cursor_filenames = { + "cursor-union.svg", + "cursor-delete.svg", + "cursor-intersect.svg", + "select.svg", + }; + + set_current_mode(); } void BuilderTool::set(const Inkscape::Preferences::Entry& val) { @@ -469,7 +478,7 @@ bool BuilderTool::root_handler(GdkEvent* event) { case GDK_BUTTON_RELEASE: xp = yp = 0; - if ((event->button.button == 1) && (this->grabbed)) { + if ((event->button.button == 1) && (this->grabbed)) { Inkscape::Rubberband *r = Inkscape::Rubberband::get(desktop); @@ -693,12 +702,16 @@ bool BuilderTool::root_handler(GdkEvent* event) { ret = ToolBase::root_handler(event); } + // TODO don't call this function for each call. find a better way. + set_current_mode(); + return ret; } int BuilderTool::get_current_mode() { using namespace Toolbar; + if(Modifier::get(Modifiers::Type::SELECT_AND_INTERSECTION)->active(this->button_press_state)) return SELECT_AND_INTERSECT; if(Modifier::get(Modifiers::Type::SELECT_AND_UNION)->active(this->button_press_state)) return SELECT_AND_UNION; if(Modifier::get(Modifiers::Type::SELECT_AND_DELETE)->active(this->button_press_state)) return SELECT_AND_DELETE; @@ -708,12 +721,32 @@ int BuilderTool::get_current_mode() return prefs->getInt("/tools/builder/mode", 0); } -void BuilderTool::set_current_mode(int mode) +void BuilderTool::set_current_mode(int current_mode) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setInt("/tools/builder/mode", mode); - auto window = desktop->getCanvas()->get_window(); -// window->set_cursor(_cursor_dragging); + if (current_mode == -1) { + current_mode = get_current_mode(); + } + + if (current_mode == active_mode) { + return; + } + + active_mode = current_mode; + set_cursor_mode(); + // TODO add a function here to change the + // patter of the items the cursor went over. +} + +void BuilderTool::set_cursor_mode() +{ + if (active_mode > mode_cursor_filenames.size()) { + std::cerr << "BuilderTool: Mode " << active_mode << " is unknown.\n"; + return; + } + + auto ¤t_cursor = mode_cursor_filenames[active_mode]; + ToolBase::cursor_filename = current_cursor; + ToolBase::sp_event_context_update_cursor(); } } diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index b5a452a0cf..a5d47fd8b4 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -61,7 +61,12 @@ private: void sp_select_context_reset_opacities(); int get_current_mode(); - void set_current_mode(int mode); + void set_current_mode(int mode = -1); + void set_cursor_mode(); + + std::vector mode_cursor_filenames; + + int active_mode = 0; Glib::RefPtr _cursor_mouseover; Glib::RefPtr _cursor_dragging; -- GitLab From 0c97edc91afa09ff26ed1419d0755ea3b99f8e18 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 21 Jul 2021 01:27:54 +0200 Subject: [PATCH 096/235] Mostly working "Mode Toggle" when the corresponding modifiers are pressed. The only known issue for now is that the modifiers don't work if you launch inkscape, choose the builder tool (without clicking on the canvas at all), then try to toggle the mode. This is probably from inkscape itself and will probably need more investigation. This problem also happens in the Zoom tool. --- src/ui/toolbar/builder-toolbar.cpp | 17 +++++++++-- src/ui/toolbar/builder-toolbar.h | 8 +----- src/ui/tools/builder-tool.cpp | 45 ++++++++++++++++++++++-------- src/ui/tools/builder-tool.h | 19 +++++++++++-- 4 files changed, 66 insertions(+), 23 deletions(-) diff --git a/src/ui/toolbar/builder-toolbar.cpp b/src/ui/toolbar/builder-toolbar.cpp index 53a9d481e8..445ace4919 100644 --- a/src/ui/toolbar/builder-toolbar.cpp +++ b/src/ui/toolbar/builder-toolbar.cpp @@ -11,6 +11,7 @@ */ #include "builder-toolbar.h" +#include "ui/tools//builder-tool.h" #include @@ -60,8 +61,8 @@ BuilderToolbar::BuilderToolbar(SPDesktop *desktop) : void BuilderToolbar::mode_buttons_init() { // TODO change the icons. - // if you're going to edit this, remember to edit the BuilderToolMode enum - // in the header file as well. remember that they have to be in the same order. + // if you're going to edit this, remember to edit the BuilderTool::Mode enum and + // BuilderTool::mode_cursor_filenames as well. remember that they have to be in the same order. const static std::vector mode_buttons_descriptors = { { .label = _("Union"), @@ -138,20 +139,32 @@ void BuilderToolbar::mode_changed(int mode) prefs->setInt("/tools/builder/mode", mode); } +void BuilderToolbar::set_current_mode(int mode) +{ + auto builder_tool = dynamic_cast(_desktop->event_context); + if (builder_tool) { + builder_tool->set_current_mode(mode); + } +} + void BuilderToolbar::set_mode_union() { + set_current_mode(Tools::BuilderTool::SELECT_AND_UNION); } void BuilderToolbar::set_mode_delete() { + set_current_mode(Tools::BuilderTool::SELECT_AND_DELETE); } void BuilderToolbar::set_mode_intersection() { + set_current_mode(Tools::BuilderTool::SELECT_AND_INTERSECT); } void BuilderToolbar::set_mode_just_select() { + set_current_mode(Tools::BuilderTool::JUST_SELECT); } diff --git a/src/ui/toolbar/builder-toolbar.h b/src/ui/toolbar/builder-toolbar.h index 7f28e24b36..1fa9d63187 100644 --- a/src/ui/toolbar/builder-toolbar.h +++ b/src/ui/toolbar/builder-toolbar.h @@ -44,13 +44,6 @@ struct ButtonDescriptor BuilderToolbarVoidMethod handler; }; -enum BuilderToolMode { - SELECT_AND_UNION = 0, - SELECT_AND_DELETE = 1, - SELECT_AND_INTERSECT = 2, - JUST_SELECT = 3, -}; - class BuilderToolbar : public Toolbar { using parent_type = Toolbar; @@ -65,6 +58,7 @@ private: void mode_buttons_init_add_buttons(); void mode_changed(int mode); + void set_current_mode(int mode); // handlers that gets called when mode is changed: void set_mode_union(); diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index b3494a6c4b..8e28102923 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -46,7 +46,6 @@ #include "ui/modifiers.h" #include "ui/tools/builder-tool.h" -#include "ui/toolbar//builder-toolbar.h" #include "ui/widget/canvas.h" @@ -54,6 +53,8 @@ #include "extension/dbus/document-interface.h" #endif +// TODO refactor the duplication between this tool and the selector tool. +// TODO break the methods below into smaller and more descriptive methods. using Inkscape::DocumentUndo; using Inkscape::Modifiers::Modifier; @@ -501,7 +502,6 @@ bool BuilderTool::root_handler(GdkEvent* event) { this->defaultMessageContext()->clear(); { - using namespace Toolbar; int mode = get_current_mode(); if(mode == JUST_SELECT && Modifier::get(Modifiers::Type::SELECT_ADD_TO)->active(event->button.state)) { @@ -590,7 +590,7 @@ bool BuilderTool::root_handler(GdkEvent* event) { case GDK_KEY_PRESS: // keybindings for select context { - guint keyval = get_latin_keyval(&event->key); + set_current_mode(event); int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); auto const y_dir = desktop->yaxisdir(); @@ -687,6 +687,7 @@ bool BuilderTool::root_handler(GdkEvent* event) { break; } case GDK_KEY_RELEASE: { + set_current_mode(event); guint keyval = get_latin_keyval(&event->key); if (key_is_a_modifier (keyval)) { this->defaultMessageContext()->clear(); @@ -702,20 +703,34 @@ bool BuilderTool::root_handler(GdkEvent* event) { ret = ToolBase::root_handler(event); } - // TODO don't call this function for each call. find a better way. - set_current_mode(); - return ret; } -int BuilderTool::get_current_mode() +void BuilderTool::set_modifiers_state(GdkEvent* event) { - using namespace Toolbar; + // TODO This function is deprecated. + GdkModifierType modifiers; + gdk_window_get_pointer(gdk_event_get_window(event), nullptr, nullptr, &modifiers); - if(Modifier::get(Modifiers::Type::SELECT_AND_INTERSECTION)->active(this->button_press_state)) return SELECT_AND_INTERSECT; - if(Modifier::get(Modifiers::Type::SELECT_AND_UNION)->active(this->button_press_state)) return SELECT_AND_UNION; - if(Modifier::get(Modifiers::Type::SELECT_AND_DELETE)->active(this->button_press_state)) return SELECT_AND_DELETE; - if(Modifier::get(Modifiers::Type::JUST_SELECT)->active(this->button_press_state)) return JUST_SELECT; + alt_on = modifiers & GDK_MOD1_MASK; + ctrl_on = modifiers & INK_GDK_PRIMARY_MASK; + shift_on = modifiers & GDK_SHIFT_MASK; +} + +int BuilderTool::get_current_mode() +{ +// TODO can it be done with the Modifier class? +// if (Modifier::get(Modifiers::Type::SELECT_AND_INTERSECTION)->active(this->button_press_state)) return SELECT_AND_INTERSECT; +// if (Modifier::get(Modifiers::Type::SELECT_AND_UNION)->active(this->button_press_state)) return SELECT_AND_UNION; +// if (Modifier::get(Modifiers::Type::SELECT_AND_DELETE)->active(this->button_press_state)) return SELECT_AND_DELETE; +// if (Modifier::get(Modifiers::Type::JUST_SELECT)->active(this->button_press_state)) return JUST_SELECT; + + if (ctrl_on) { + if (alt_on) return SELECT_AND_INTERSECT; + return SELECT_AND_UNION; + } + if (alt_on) return SELECT_AND_DELETE; + if (shift_on) return JUST_SELECT; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); return prefs->getInt("/tools/builder/mode", 0); @@ -737,6 +752,12 @@ void BuilderTool::set_current_mode(int current_mode) // patter of the items the cursor went over. } +void BuilderTool::set_current_mode(GdkEvent *event) +{ + set_modifiers_state(event); + set_current_mode(); +} + void BuilderTool::set_cursor_mode() { if (active_mode > mode_cursor_filenames.size()) { diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index a5d47fd8b4..fefacebaad 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -19,6 +19,7 @@ #define SP_IS_SELECT_CONTEXT(obj) (dynamic_cast((const Inkscape::UI::Tools::ToolBase*)obj) != NULL) namespace Inkscape { +class CanvasItem; class SelTrans; class SelectionDescriber; } @@ -29,6 +30,14 @@ namespace Tools { class BuilderTool : public ToolBase { public: + + enum Mode { + SELECT_AND_UNION = 0, + SELECT_AND_DELETE = 1, + SELECT_AND_INTERSECT = 2, + JUST_SELECT = 3, + }; + BuilderTool(); ~BuilderTool() override; @@ -56,17 +65,23 @@ public: const std::string& getPrefsPath() override; + void set_current_mode(GdkEvent* event); + void set_current_mode(int mode = -1); + private: bool sp_select_context_abort(); void sp_select_context_reset_opacities(); + void set_modifiers_state(GdkEvent* event); int get_current_mode(); - void set_current_mode(int mode = -1); void set_cursor_mode(); std::vector mode_cursor_filenames; - int active_mode = 0; + int active_mode = JUST_SELECT; // default to the select mode since this is the default cursor. + bool ctrl_on = false; + bool alt_on = false; + bool shift_on = false; Glib::RefPtr _cursor_mouseover; Glib::RefPtr _cursor_dragging; -- GitLab From 6a11127fed5cb9f5f7678d3402c69816e753cce8 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 21 Jul 2021 01:48:33 +0200 Subject: [PATCH 097/235] Fixed a typo in the intersection button in the BuilderToolbar. --- src/ui/toolbar/builder-toolbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/toolbar/builder-toolbar.cpp b/src/ui/toolbar/builder-toolbar.cpp index 445ace4919..ef54dbae82 100644 --- a/src/ui/toolbar/builder-toolbar.cpp +++ b/src/ui/toolbar/builder-toolbar.cpp @@ -78,7 +78,7 @@ void BuilderToolbar::mode_buttons_init() }, { .label = _("Intersection"), - .tooltip_text = _("Just select whatever the mouse moves over"), + .tooltip_text = _("Intersect whatever the mouse moves over"), .icon_name = "path-intersection", .handler = &BuilderToolbar::set_mode_intersection, }, -- GitLab From 349d5fca6dcbb44637c0e6e0a54c1d0949748c42 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 21 Jul 2021 01:51:07 +0200 Subject: [PATCH 098/235] moved mode_cursor_filenames to the header file. --- src/ui/tools/builder-tool.cpp | 7 ------- src/ui/tools/builder-tool.h | 7 ++++++- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 8e28102923..a52bd1c95f 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -150,13 +150,6 @@ void BuilderTool::setup() { this->enableGrDrag(); } - mode_cursor_filenames = { - "cursor-union.svg", - "cursor-delete.svg", - "cursor-intersect.svg", - "select.svg", - }; - set_current_mode(); } diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index fefacebaad..3ad587f79c 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -76,7 +76,12 @@ private: int get_current_mode(); void set_cursor_mode(); - std::vector mode_cursor_filenames; + const std::vector mode_cursor_filenames = { + "cursor-union.svg", + "cursor-delete.svg", + "cursor-intersect.svg", + "select.svg", + }; int active_mode = JUST_SELECT; // default to the select mode since this is the default cursor. bool ctrl_on = false; -- GitLab From 2bcc93c4336cf7e686482e9034881cbcb82caff9 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 21 Jul 2021 01:55:33 +0200 Subject: [PATCH 099/235] Cleaned up some of the unused code. --- src/ui/toolbar/builder-toolbar.h | 4 +- src/ui/tools/builder-tool.cpp | 136 ------------------------------- src/ui/tools/builder-tool.h | 6 -- 3 files changed, 2 insertions(+), 144 deletions(-) diff --git a/src/ui/toolbar/builder-toolbar.h b/src/ui/toolbar/builder-toolbar.h index 1fa9d63187..726a84981e 100644 --- a/src/ui/toolbar/builder-toolbar.h +++ b/src/ui/toolbar/builder-toolbar.h @@ -1,5 +1,3 @@ -#pragma once - // SPDX-License-Identifier: GPL-2.0-or-later /* * A toolbar for the Builder tool. @@ -12,6 +10,8 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ +#pragma once + #include #include diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index a52bd1c95f..eaa4cf87af 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -77,19 +77,12 @@ BuilderTool::BuilderTool() , dragging(false) , moved(false) , button_press_state(0) - , cycling_wrap(true) , item(nullptr) , _seltrans(nullptr) , _describer(nullptr) { } -//static gint xp = 0, yp = 0; // where drag started -//static gint tolerance = 0; -//static bool within_tolerance = false; -static bool is_cycling = false; - - BuilderTool::~BuilderTool() { this->enableGrDrag(false); @@ -223,138 +216,9 @@ bool BuilderTool::item_handler(SPItem* item, GdkEvent* event) { // TODO consider the case for when the ENTER_NOTIFY (to set a pattern). return root_handler(event); - - gint ret = FALSE; - - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); - - // make sure we still have valid objects to move around - if (this->item && this->item->document == nullptr) { - this->sp_select_context_abort(); - } - - switch (event->type) { - case GDK_BUTTON_PRESS: - if (event->button.button == 1) { - /* Left mousebutton */ - - // save drag origin - xp = (gint) event->button.x; - yp = (gint) event->button.y; - within_tolerance = true; - - // remember what modifiers were on before button press - this->button_press_state = event->button.state; - bool first_hit = Modifier::get(Modifiers::Type::SELECT_FIRST_HIT)->active(this->button_press_state); - bool force_drag = Modifier::get(Modifiers::Type::SELECT_FORCE_DRAG)->active(this->button_press_state); - bool always_box = Modifier::get(Modifiers::Type::SELECT_ALWAYS_BOX)->active(this->button_press_state); - bool touch_path = Modifier::get(Modifiers::Type::SELECT_TOUCH_PATH)->active(this->button_press_state); - - // if shift or ctrl was pressed, do not move objects; - // pass the event to root handler which will perform rubberband, shift-click, ctrl-click, ctrl-drag - if (!(always_box || first_hit || touch_path)) { - - this->dragging = TRUE; - this->moved = FALSE; - - auto window = desktop->getCanvas()->get_window(); - window->set_cursor(_cursor_dragging); - - // remember the clicked item in this->item: - if (this->item) { - sp_object_unref(this->item, nullptr); - this->item = nullptr; - } - - this->item = sp_event_context_find_item (desktop, Geom::Point(event->button.x, event->button.y), force_drag, FALSE); - sp_object_ref(this->item, nullptr); - - rb_escaped = drag_escaped = 0; - - if (grabbed) { - grabbed->ungrab(); - grabbed = nullptr; - } - - grabbed = desktop->getCanvasDrawing(); - grabbed->grab(Gdk::KEY_PRESS_MASK | - Gdk::KEY_RELEASE_MASK | - Gdk::BUTTON_PRESS_MASK | - Gdk::BUTTON_RELEASE_MASK | - Gdk::POINTER_MOTION_MASK ); - - ret = TRUE; - } - } else if (event->button.button == 3 && !this->dragging) { - // right click; do not eat it so that right-click menu can appear, but cancel dragging & rubberband - this->sp_select_context_abort(); - } - break; - - - case GDK_ENTER_NOTIFY: { - if (!desktop->isWaitingCursor() && !this->dragging) { - auto window = desktop->getCanvas()->get_window(); - window->set_cursor(_cursor_mouseover); - } - break; - } - case GDK_LEAVE_NOTIFY: - if (!desktop->isWaitingCursor() && !this->dragging) { - auto window = desktop->getCanvas()->get_window(); - window->set_cursor(this->cursor); - } - break; - - case GDK_KEY_PRESS: - if (get_latin_keyval (&event->key) == GDK_KEY_space) { - if (this->dragging && this->grabbed) { - /* stamping mode: show content mode moving */ - _seltrans->stamp(); - ret = TRUE; - } - } else if (get_latin_keyval (&event->key) == GDK_KEY_Tab) { - if (this->dragging && this->grabbed) { - _seltrans->getNextClosestPoint(false); - ret = TRUE; - } - } else if (get_latin_keyval (&event->key) == GDK_KEY_ISO_Left_Tab) { - if (this->dragging && this->grabbed) { - _seltrans->getNextClosestPoint(true); - ret = TRUE; - } - } - break; - - default: - break; - } - - if (!ret) { - ret = ToolBase::item_handler(item, event); - } - - return ret; -} - -void BuilderTool::sp_select_context_reset_opacities() { - for (auto item : this->cycling_items_cmp) { - if (item) { - Inkscape::DrawingItem *arenaitem = item->get_arenaitem(desktop->dkey); - arenaitem->setOpacity(SP_SCALE24_TO_FLOAT(item->style->opacity.value)); - } else { - g_assert_not_reached(); - } - } - - this->cycling_items_cmp.clear(); - this->cycling_cur_item = nullptr; } bool BuilderTool::root_handler(GdkEvent* event) { - SPItem *item = nullptr; - SPItem *item_at_point = nullptr, *group_at_point = nullptr, *item_in_group = nullptr; gint ret = FALSE; Inkscape::Selection *selection = desktop->getSelection(); diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index 3ad587f79c..e52a070e85 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -45,11 +45,6 @@ public: bool moved; guint button_press_state; - std::vector cycling_items; - std::vector cycling_items_cmp; - SPItem *cycling_cur_item; - bool cycling_wrap; - SPItem *item; Inkscape::CanvasItem *grabbed = nullptr; Inkscape::SelTrans *_seltrans; @@ -70,7 +65,6 @@ public: private: bool sp_select_context_abort(); - void sp_select_context_reset_opacities(); void set_modifiers_state(GdkEvent* event); int get_current_mode(); -- GitLab From 23267fe7320842976bd120b2dc72d0cc905a74ec Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 21 Jul 2021 04:49:53 +0200 Subject: [PATCH 100/235] Refactoring: split the BuilderTool::root_handler method into multiple handler functions for each event. --- src/ui/tools/builder-tool.cpp | 576 ++++++++++++++++++---------------- src/ui/tools/builder-tool.h | 20 ++ 2 files changed, 324 insertions(+), 272 deletions(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index eaa4cf87af..3b03872d24 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -63,6 +63,8 @@ namespace Inkscape { namespace UI { namespace Tools { +using EventHandler = BuilderTool::EventHandler; + static gint rb_escaped = 0; // if non-zero, rubberband was canceled by esc, so the next button release should not deselect static gint drag_escaped = 0; // if non-zero, drag was canceled by esc @@ -218,349 +220,379 @@ bool BuilderTool::item_handler(SPItem* item, GdkEvent* event) { return root_handler(event); } -bool BuilderTool::root_handler(GdkEvent* event) { - gint ret = FALSE; +EventHandler BuilderTool::get_event_handler(GdkEvent* event) +{ + auto handler = handlers.find(event->type); + if (handler != handlers.end()) { + return handler->second; // first is the key + } + return nullptr; +} - Inkscape::Selection *selection = desktop->getSelection(); - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); +bool BuilderTool::root_handler(GdkEvent* event) { // make sure we still have valid objects to move around if (this->item && this->item->document == nullptr) { this->sp_select_context_abort(); } + forced_redraws_start(5); - switch (event->type) { - case GDK_2BUTTON_PRESS: - if (event->button.button == 1) { - if (!selection->isEmpty()) { - SPItem *clicked_item = selection->items().front(); + bool ret = false; - if (dynamic_cast(clicked_item) && !dynamic_cast(clicked_item)) { // enter group if it's not a 3D box - desktop->setCurrentLayer(clicked_item); - desktop->getSelection()->clear(); - this->dragging = false; - sp_event_context_discard_delayed_snap_event(this); + auto handler = get_event_handler(event); + if (handler) { + ret = (this->*handler)(event); + } + if (!ret) { + ret = ToolBase::root_handler(event); + } - } else { // switch tool - Geom::Point const button_pt(event->button.x, event->button.y); - Geom::Point const p(desktop->w2d(button_pt)); - set_active_tool(desktop, clicked_item, p); - } - } else { - sp_select_context_up_one_layer(desktop); - } + return ret; +} - ret = TRUE; - } - break; +bool BuilderTool::event_button_double_press_handler(GdkEvent *event) +{ + Inkscape::Selection *selection = desktop->getSelection(); - case GDK_BUTTON_PRESS: - if (event->button.button == 1) { - // save drag origin - xp = (gint) event->button.x; - yp = (gint) event->button.y; - within_tolerance = true; + if (event->button.button == 1) { + if (!selection->isEmpty()) { + SPItem *clicked_item = selection->items().front(); + if (dynamic_cast(clicked_item) && !dynamic_cast(clicked_item)) { // enter group if it's not a 3D box + desktop->setCurrentLayer(clicked_item); + desktop->getSelection()->clear(); + this->dragging = false; + sp_event_context_discard_delayed_snap_event(this); + + + } else { // switch tool Geom::Point const button_pt(event->button.x, event->button.y); Geom::Point const p(desktop->w2d(button_pt)); + set_active_tool(desktop, clicked_item, p); + } + } else { + sp_select_context_up_one_layer(desktop); + } - Inkscape::Rubberband::get(desktop)->setMode(RUBBERBAND_MODE_TOUCHPATH); - Inkscape::Rubberband::get(desktop)->start(desktop, p); + return true; + } + return false; +} - if (this->grabbed) { - grabbed->ungrab(); - this->grabbed = nullptr; - } +bool BuilderTool::event_button_press_handler(GdkEvent *event) +{ + if (event->button.button == 1) { + // save drag origin + xp = (gint) event->button.x; + yp = (gint) event->button.y; + within_tolerance = true; - grabbed = desktop->getCanvasCatchall(); - grabbed->grab(Gdk::KEY_PRESS_MASK | - Gdk::KEY_RELEASE_MASK | - Gdk::BUTTON_PRESS_MASK | - Gdk::BUTTON_RELEASE_MASK | - Gdk::POINTER_MOTION_MASK ); + Geom::Point const button_pt(event->button.x, event->button.y); + Geom::Point const p(desktop->w2d(button_pt)); - // remember what modifiers were on before button press - this->button_press_state = event->button.state; + Inkscape::Rubberband::get(desktop)->setMode(RUBBERBAND_MODE_TOUCHPATH); + Inkscape::Rubberband::get(desktop)->start(desktop, p); - this->moved = FALSE; + if (this->grabbed) { + grabbed->ungrab(); + this->grabbed = nullptr; + } - rb_escaped = drag_escaped = 0; + grabbed = desktop->getCanvasCatchall(); + grabbed->grab(Gdk::KEY_PRESS_MASK | + Gdk::KEY_RELEASE_MASK | + Gdk::BUTTON_PRESS_MASK | + Gdk::BUTTON_RELEASE_MASK | + Gdk::POINTER_MOTION_MASK ); - ret = TRUE; - } else if (event->button.button == 3) { - // right click; do not eat it so that right-click menu can appear, but cancel dragging & rubberband - this->sp_select_context_abort(); - } - break; + // remember what modifiers were on before button press + this->button_press_state = event->button.state; - case GDK_MOTION_NOTIFY: - { - tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); - - if ((event->motion.state & GDK_BUTTON1_MASK)) { - Geom::Point const motion_pt(event->motion.x, event->motion.y); - Geom::Point const p(desktop->w2d(motion_pt)); - if ( within_tolerance - && ( abs( (gint) event->motion.x - xp ) < tolerance ) - && ( abs( (gint) event->motion.y - yp ) < tolerance ) ) { - break; // do not drag if we're within tolerance from origin - } - // Once the user has moved farther than tolerance from the original location - // (indicating they intend to move the object, not click), then always process the - // motion notify coordinates as given (no snapping back to origin) - within_tolerance = false; - - if (Inkscape::Rubberband::get(desktop)->is_started()) { - Inkscape::Rubberband::get(desktop)->move(p); - - auto touch_path = Modifier::get(Modifiers::Type::SELECT_TOUCH_PATH)->get_label(); - auto mode = Inkscape::Rubberband::get(desktop)->getMode(); - if (mode == RUBBERBAND_MODE_TOUCHPATH) { - this->defaultMessageContext()->setF(Inkscape::NORMAL_MESSAGE, - _("Draw over objects to select them; release %s to switch to rubberband selection"), touch_path.c_str()); - } else if (mode == RUBBERBAND_MODE_TOUCHRECT) { - this->defaultMessageContext()->setF(Inkscape::NORMAL_MESSAGE, - _("Drag near objects to select them; press %s to switch to touch selection"), touch_path.c_str()); - } else { - this->defaultMessageContext()->setF(Inkscape::NORMAL_MESSAGE, - _("Drag around objects to select them; press %s to switch to touch selection"), touch_path.c_str()); - } + this->moved = false; - gobble_motion_events(GDK_BUTTON1_MASK); - } - } - break; - } - case GDK_BUTTON_RELEASE: - xp = yp = 0; + rb_escaped = drag_escaped = 0; - if ((event->button.button == 1) && (this->grabbed)) { + return true; - Inkscape::Rubberband *r = Inkscape::Rubberband::get(desktop); + } else if (event->button.button == 3) { + // right click; do not eat it so that right-click menu can appear, but cancel dragging & rubberband + this->sp_select_context_abort(); + } - if (r->is_started() && !within_tolerance) { - // this was a rubberband drag - std::vector items; + return false; +} - if (r->getMode() == RUBBERBAND_MODE_RECT) { - Geom::OptRect const b = r->getRectangle(); - items = desktop->getDocument()->getItemsInBox(desktop->dkey, (*b) * desktop->dt2doc()); - } else if (r->getMode() == RUBBERBAND_MODE_TOUCHRECT) { - Geom::OptRect const b = r->getRectangle(); - items = desktop->getDocument()->getItemsPartiallyInBox(desktop->dkey, (*b) * desktop->dt2doc()); - } else if (r->getMode() == RUBBERBAND_MODE_TOUCHPATH) { - items = desktop->getDocument()->getItemsAtPoints(desktop->dkey, r->getPoints()); - } +bool BuilderTool::event_button_release_handler(GdkEvent *event) +{ + xp = yp = 0; + Inkscape::Selection *selection = desktop->getSelection(); - _seltrans->resetState(); - r->stop(); - this->defaultMessageContext()->clear(); - - { - int mode = get_current_mode(); - - if(mode == JUST_SELECT && Modifier::get(Modifiers::Type::SELECT_ADD_TO)->active(event->button.state)) { - // with shift, add to selection - selection->addList (items); - } else { - // without shift, simply select anew - selection->setList (items); - - if (mode != JUST_SELECT) { - if (mode == SELECT_AND_UNION) { - selection->pathUnion(); - } else if (mode == SELECT_AND_DELETE) { - selection->deleteItems(); - } else if (mode == SELECT_AND_INTERSECT) { - selection->pathIntersect(); - } - selection->clear(); - } - } - } - } else { // it was just a click, or a too small rubberband - r->stop(); + if ((event->button.button == 1) && (this->grabbed)) { - bool add_to = Modifier::get(Modifiers::Type::SELECT_ADD_TO)->active(event->button.state); - bool in_groups = Modifier::get(Modifiers::Type::SELECT_IN_GROUPS)->active(event->button.state); - bool force_drag = Modifier::get(Modifiers::Type::SELECT_FORCE_DRAG)->active(event->button.state); + Inkscape::Rubberband *r = Inkscape::Rubberband::get(desktop); - if (add_to && !rb_escaped && !drag_escaped) { - // this was a shift+click or alt+shift+click, select what was clicked upon + if (r->is_started() && !within_tolerance) { + // this was a rubberband drag + std::vector items; - if (in_groups) { - // go into groups, honoring force_drag (Alt) - item = sp_event_context_find_item (desktop, - Geom::Point(event->button.x, event->button.y), force_drag, TRUE); - } else { - // don't go into groups, honoring Alt - item = sp_event_context_find_item (desktop, - Geom::Point(event->button.x, event->button.y), force_drag, FALSE); - } + if (r->getMode() == RUBBERBAND_MODE_RECT) { + Geom::OptRect const b = r->getRectangle(); + items = desktop->getDocument()->getItemsInBox(desktop->dkey, (*b) * desktop->dt2doc()); + } else if (r->getMode() == RUBBERBAND_MODE_TOUCHRECT) { + Geom::OptRect const b = r->getRectangle(); + items = desktop->getDocument()->getItemsPartiallyInBox(desktop->dkey, (*b) * desktop->dt2doc()); + } else if (r->getMode() == RUBBERBAND_MODE_TOUCHPATH) { + items = desktop->getDocument()->getItemsAtPoints(desktop->dkey, r->getPoints()); + } + + _seltrans->resetState(); + r->stop(); + this->defaultMessageContext()->clear(); + + { + int mode = get_current_mode(); - if (item) { - selection->toggle(item); - item = nullptr; + if(mode == JUST_SELECT && Modifier::get(Modifiers::Type::SELECT_ADD_TO)->active(event->button.state)) { + // with shift, add to selection + selection->addList (items); + } else { + // without shift, simply select anew + selection->setList (items); + + if (mode != JUST_SELECT) { + if (mode == SELECT_AND_UNION) { + selection->pathUnion(); + } else if (mode == SELECT_AND_DELETE) { + selection->deleteItems(); + } else if (mode == SELECT_AND_INTERSECT) { + selection->pathIntersect(); } + selection->clear(); + } + } + } - } else if ((in_groups || force_drag) && !rb_escaped && !drag_escaped) { // ctrl+click, alt+click - item = sp_event_context_find_item (desktop, - Geom::Point(event->button.x, event->button.y), force_drag, in_groups); + } else { // it was just a click, or a too small rubberband + r->stop(); - if (item) { - if (selection->includes(item)) { - _seltrans->increaseState(); - } else { - _seltrans->resetState(); - selection->set(item); - } + bool add_to = Modifier::get(Modifiers::Type::SELECT_ADD_TO)->active(event->button.state); + bool in_groups = Modifier::get(Modifiers::Type::SELECT_IN_GROUPS)->active(event->button.state); + bool force_drag = Modifier::get(Modifiers::Type::SELECT_FORCE_DRAG)->active(event->button.state); - item = nullptr; - } - } else { // click without shift, simply deselect, unless with Alt or something was cancelled - if (!selection->isEmpty()) { - if (!(rb_escaped) && !(drag_escaped) && !force_drag) { - selection->clear(); - } + if (add_to && !rb_escaped && !drag_escaped) { + // this was a shift+click or alt+shift+click, select what was clicked upon - rb_escaped = 0; - } + if (in_groups) { + // go into groups, honoring force_drag (Alt) + item = sp_event_context_find_item (desktop, + Geom::Point(event->button.x, event->button.y), force_drag, TRUE); + } else { + // don't go into groups, honoring Alt + item = sp_event_context_find_item (desktop, + Geom::Point(event->button.x, event->button.y), force_drag, FALSE); + } + + if (item) { + selection->toggle(item); + item = nullptr; + } + + } else if ((in_groups || force_drag) && !rb_escaped && !drag_escaped) { // ctrl+click, alt+click + item = sp_event_context_find_item (desktop, + Geom::Point(event->button.x, event->button.y), force_drag, in_groups); + + if (item) { + if (selection->includes(item)) { + _seltrans->increaseState(); + } else { + _seltrans->resetState(); + selection->set(item); } + + item = nullptr; } + } else { // click without shift, simply deselect, unless with Alt or something was cancelled + if (!selection->isEmpty()) { + if (!(rb_escaped) && !(drag_escaped) && !force_drag) { + selection->clear(); + } - ret = TRUE; - } - if (grabbed) { - grabbed->ungrab(); - grabbed = nullptr; + rb_escaped = 0; + } } + } + } + if (grabbed) { + grabbed->ungrab(); + grabbed = nullptr; + } + + if (event->button.button == 1) { + Inkscape::Rubberband::get(desktop)->stop(); // might have been started in another tool! + } + + this->button_press_state = 0; + + return true; +} - if (event->button.button == 1) { - Inkscape::Rubberband::get(desktop)->stop(); // might have been started in another tool! +bool BuilderTool::event_motion_handler(GdkEvent *event) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); + + if ((event->motion.state & GDK_BUTTON1_MASK)) { + Geom::Point const motion_pt(event->motion.x, event->motion.y); + Geom::Point const p(desktop->w2d(motion_pt)); + if ( within_tolerance + && ( abs( (gint) event->motion.x - xp ) < tolerance ) + && ( abs( (gint) event->motion.y - yp ) < tolerance ) ) { + return false; // do not drag if we're within tolerance from origin + } + // Once the user has moved farther than tolerance from the original location + // (indicating they intend to move the object, not click), then always process the + // motion notify coordinates as given (no snapping back to origin) + within_tolerance = false; + + if (Inkscape::Rubberband::get(desktop)->is_started()) { + Inkscape::Rubberband::get(desktop)->move(p); + + auto touch_path = Modifier::get(Modifiers::Type::SELECT_TOUCH_PATH)->get_label(); + auto mode = Inkscape::Rubberband::get(desktop)->getMode(); + if (mode == RUBBERBAND_MODE_TOUCHPATH) { + this->defaultMessageContext()->setF(Inkscape::NORMAL_MESSAGE, + _("Draw over objects to select them; release %s to switch to rubberband selection"), touch_path.c_str()); + } else if (mode == RUBBERBAND_MODE_TOUCHRECT) { + this->defaultMessageContext()->setF(Inkscape::NORMAL_MESSAGE, + _("Drag near objects to select them; press %s to switch to touch selection"), touch_path.c_str()); + } else { + this->defaultMessageContext()->setF(Inkscape::NORMAL_MESSAGE, + _("Drag around objects to select them; press %s to switch to touch selection"), touch_path.c_str()); } - this->button_press_state = 0; - break; + gobble_motion_events(GDK_BUTTON1_MASK); + } + } - case GDK_KEY_PRESS: // keybindings for select context - { - set_current_mode(event); + return false; +} - int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); - auto const y_dir = desktop->yaxisdir(); +bool BuilderTool::event_key_press_handler(GdkEvent *event) +{ + set_current_mode(event); - switch (get_latin_keyval (&event->key)) { - case GDK_KEY_Escape: - if (!this->sp_select_context_abort()) { - selection->clear(); - } + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + Inkscape::Selection *selection = desktop->getSelection(); - ret = TRUE; - break; + int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); + auto const y_dir = desktop->yaxisdir(); - case GDK_KEY_a: - case GDK_KEY_A: - if (MOD__CTRL_ONLY(event)) { - sp_edit_select_all(desktop); - ret = TRUE; - } - break; - - case GDK_KEY_space: - /* stamping mode: show outline mode moving */ - /* FIXME: Is next condition ok? (lauris) */ - if (this->dragging && this->grabbed) { - _seltrans->stamp(); - ret = TRUE; - } - break; + bool ret = false; + switch (get_latin_keyval (&event->key)) { + case GDK_KEY_Escape: + if (!this->sp_select_context_abort()) { + selection->clear(); + } - case GDK_KEY_x: - case GDK_KEY_X: - if (MOD__ALT_ONLY(event)) { - desktop->setToolboxFocusTo ("select-x"); - ret = TRUE; - } - break; - - case GDK_KEY_bracketleft: - if (MOD__ALT(event)) { - gint mul = 1 + gobble_key_events(get_latin_keyval(&event->key), 0); // with any mask - selection->rotateScreen(-mul * y_dir); - } else if (MOD__CTRL(event)) { - selection->rotate(-90 * y_dir); - } else if (snaps) { - selection->rotate(-180.0/snaps * y_dir); - } + ret = true; + break; - ret = TRUE; - break; - - case GDK_KEY_bracketright: - if (MOD__ALT(event)) { - gint mul = 1 + gobble_key_events(get_latin_keyval(&event->key), 0); // with any mask - selection->rotateScreen(mul * y_dir); - } else if (MOD__CTRL(event)) { - selection->rotate(90 * y_dir); - } else if (snaps) { - selection->rotate(180.0/snaps * y_dir); - } + case GDK_KEY_a: + case GDK_KEY_A: + if (MOD__CTRL_ONLY(event)) { + sp_edit_select_all(desktop); + ret = true; + } + break; - ret = TRUE; - break; + case GDK_KEY_space: + /* stamping mode: show outline mode moving */ + /* FIXME: Is next condition ok? (lauris) */ + if (this->dragging && this->grabbed) { + _seltrans->stamp(); + ret = true; + } + break; - case GDK_KEY_BackSpace: - if (MOD__CTRL_ONLY(event)) { - sp_select_context_up_one_layer(desktop); - ret = TRUE; - } - break; + case GDK_KEY_x: + case GDK_KEY_X: + if (MOD__ALT_ONLY(event)) { + desktop->setToolboxFocusTo ("select-x"); + ret = true; + } + break; - case GDK_KEY_s: - case GDK_KEY_S: - if (MOD__SHIFT_ONLY(event)) { - if (!selection->isEmpty()) { - _seltrans->increaseState(); - } + case GDK_KEY_bracketleft: + if (MOD__ALT(event)) { + gint mul = 1 + gobble_key_events(get_latin_keyval(&event->key), 0); // with any mask + selection->rotateScreen(-mul * y_dir); + } else if (MOD__CTRL(event)) { + selection->rotate(-90 * y_dir); + } else if (snaps) { + selection->rotate(-180.0/snaps * y_dir); + } - ret = TRUE; - } - break; + ret = true; + break; - case GDK_KEY_g: - case GDK_KEY_G: - if (MOD__SHIFT_ONLY(event)) { - desktop->selection->toGuides(); - ret = true; - } - break; + case GDK_KEY_bracketright: + if (MOD__ALT(event)) { + gint mul = 1 + gobble_key_events(get_latin_keyval(&event->key), 0); // with any mask + selection->rotateScreen(mul * y_dir); + } else if (MOD__CTRL(event)) { + selection->rotate(90 * y_dir); + } else if (snaps) { + selection->rotate(180.0/snaps * y_dir); + } + + ret = true; + break; - default: - break; + case GDK_KEY_BackSpace: + if (MOD__CTRL_ONLY(event)) { + sp_select_context_up_one_layer(desktop); + ret = true; } break; - } - case GDK_KEY_RELEASE: { - set_current_mode(event); - guint keyval = get_latin_keyval(&event->key); - if (key_is_a_modifier (keyval)) { - this->defaultMessageContext()->clear(); + + case GDK_KEY_s: + case GDK_KEY_S: + if (MOD__SHIFT_ONLY(event)) { + if (!selection->isEmpty()) { + _seltrans->increaseState(); + } + + ret = true; } + break; + case GDK_KEY_g: + case GDK_KEY_G: + if (MOD__SHIFT_ONLY(event)) { + desktop->selection->toGuides(); + ret = true; + } break; - } + default: break; } - if (!ret) { - ret = ToolBase::root_handler(event); + return ret; +} + +bool BuilderTool::event_key_release_handler(GdkEvent *event) +{ + set_current_mode(event); + guint keyval = get_latin_keyval(&event->key); + if (key_is_a_modifier (keyval)) { + this->defaultMessageContext()->clear(); } - return ret; + return false; } void BuilderTool::set_modifiers_state(GdkEvent* event) diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index e52a070e85..c2fc691176 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -31,6 +31,8 @@ namespace Tools { class BuilderTool : public ToolBase { public: + typedef bool (BuilderTool::*EventHandler)(GdkEvent*); + enum Mode { SELECT_AND_UNION = 0, SELECT_AND_DELETE = 1, @@ -64,8 +66,17 @@ public: void set_current_mode(int mode = -1); private: + bool sp_select_context_abort(); + EventHandler get_event_handler(GdkEvent* event); + bool event_button_double_press_handler(GdkEvent* event); + bool event_button_press_handler(GdkEvent* event); + bool event_button_release_handler(GdkEvent* event); + bool event_motion_handler(GdkEvent* event); + bool event_key_press_handler(GdkEvent* event); + bool event_key_release_handler(GdkEvent* event); + void set_modifiers_state(GdkEvent* event); int get_current_mode(); void set_cursor_mode(); @@ -77,6 +88,15 @@ private: "select.svg", }; + const std::map handlers = { + {GDK_2BUTTON_PRESS, &BuilderTool::event_button_double_press_handler}, + {GDK_BUTTON_PRESS, &BuilderTool::event_button_press_handler}, + {GDK_BUTTON_RELEASE, &BuilderTool::event_button_release_handler}, + {GDK_KEY_PRESS, &BuilderTool::event_key_press_handler}, + {GDK_KEY_RELEASE, &BuilderTool::event_key_release_handler}, + {GDK_MOTION_NOTIFY, &BuilderTool::event_motion_handler}, + }; + int active_mode = JUST_SELECT; // default to the select mode since this is the default cursor. bool ctrl_on = false; bool alt_on = false; -- GitLab From c9ef48ace5e270b32ee5f256d40a55272892f5d0 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 21 Jul 2021 04:52:48 +0200 Subject: [PATCH 101/235] Removed the double click handler for the BuilderTool since it's not used for now. --- src/ui/tools/builder-tool.cpp | 29 ----------------------------- src/ui/tools/builder-tool.h | 2 -- 2 files changed, 31 deletions(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 3b03872d24..0a1f2795de 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -252,35 +252,6 @@ bool BuilderTool::root_handler(GdkEvent* event) { return ret; } -bool BuilderTool::event_button_double_press_handler(GdkEvent *event) -{ - Inkscape::Selection *selection = desktop->getSelection(); - - if (event->button.button == 1) { - if (!selection->isEmpty()) { - SPItem *clicked_item = selection->items().front(); - - if (dynamic_cast(clicked_item) && !dynamic_cast(clicked_item)) { // enter group if it's not a 3D box - desktop->setCurrentLayer(clicked_item); - desktop->getSelection()->clear(); - this->dragging = false; - sp_event_context_discard_delayed_snap_event(this); - - - } else { // switch tool - Geom::Point const button_pt(event->button.x, event->button.y); - Geom::Point const p(desktop->w2d(button_pt)); - set_active_tool(desktop, clicked_item, p); - } - } else { - sp_select_context_up_one_layer(desktop); - } - - return true; - } - return false; -} - bool BuilderTool::event_button_press_handler(GdkEvent *event) { if (event->button.button == 1) { diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index c2fc691176..6e63e955c6 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -70,7 +70,6 @@ private: bool sp_select_context_abort(); EventHandler get_event_handler(GdkEvent* event); - bool event_button_double_press_handler(GdkEvent* event); bool event_button_press_handler(GdkEvent* event); bool event_button_release_handler(GdkEvent* event); bool event_motion_handler(GdkEvent* event); @@ -89,7 +88,6 @@ private: }; const std::map handlers = { - {GDK_2BUTTON_PRESS, &BuilderTool::event_button_double_press_handler}, {GDK_BUTTON_PRESS, &BuilderTool::event_button_press_handler}, {GDK_BUTTON_RELEASE, &BuilderTool::event_button_release_handler}, {GDK_KEY_PRESS, &BuilderTool::event_key_press_handler}, -- GitLab From 49a44c04419c4f74666f8fe3d52e971e06b5c228 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 21 Jul 2021 04:58:13 +0200 Subject: [PATCH 102/235] Removed the unused cursors from the BuilderTool. --- src/ui/tools/builder-tool.cpp | 10 ---------- src/ui/tools/builder-tool.h | 5 ++--- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 0a1f2795de..770b735e55 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -114,16 +114,6 @@ void BuilderTool::setup() { auto select_click = Modifier::get(Modifiers::Type::SELECT_ADD_TO)->get_label(); auto select_scroll = Modifier::get(Modifiers::Type::SELECT_CYCLE)->get_label(); - // cursors in select context - Gtk::Widget *w = desktop->getCanvas(); - if (w->get_window()) { - // Window may not be open when tool is setup for the first time! - _cursor_mouseover = load_svg_cursor(w->get_display(), w->get_window(), "select-mouseover.svg"); - _cursor_dragging = load_svg_cursor(w->get_display(), w->get_window(), "select-dragging.svg"); - // Need to reload select.svg. - load_svg_cursor(w->get_display(), w->get_window(), "select.svg"); - } - no_selection_msg = g_strdup_printf( _("No objects selected. Click, %s+click, %s+scroll mouse on top of objects, or drag around objects to select."), select_click.c_str(), select_scroll.c_str()); diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index 6e63e955c6..5f9af37376 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -80,6 +80,8 @@ private: int get_current_mode(); void set_cursor_mode(); + // TODO you might pre-load the cursors and store them + // in this vector instead of loading them each time. const std::vector mode_cursor_filenames = { "cursor-union.svg", "cursor-delete.svg", @@ -99,9 +101,6 @@ private: bool ctrl_on = false; bool alt_on = false; bool shift_on = false; - - Glib::RefPtr _cursor_mouseover; - Glib::RefPtr _cursor_dragging; }; } -- GitLab From e700d43772502a1a4ede00bf8222fd75c02de440 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 21 Jul 2021 13:37:35 +0200 Subject: [PATCH 103/235] Deleted the code for the arrow keys in the BuilderTool. --- src/ui/tools/select-tool.cpp | 94 ------------------------------------ 1 file changed, 94 deletions(-) diff --git a/src/ui/tools/select-tool.cpp b/src/ui/tools/select-tool.cpp index ff7b233660..dfd964f184 100644 --- a/src/ui/tools/select-tool.cpp +++ b/src/ui/tools/select-tool.cpp @@ -891,100 +891,6 @@ bool SelectTool::root_handler(GdkEvent* event) { auto const y_dir = desktop->yaxisdir(); switch (get_latin_keyval (&event->key)) { - case GDK_KEY_Left: // move selection left - case GDK_KEY_KP_Left: - if (!MOD__CTRL(event)) { // not ctrl - gint mul = 1 + gobble_key_events( get_latin_keyval(&event->key), 0); // with any mask - - if (MOD__ALT(event)) { // alt - if (MOD__SHIFT(event)) { - desktop->getSelection()->moveScreen(mul*-10, 0); // shift - } else { - desktop->getSelection()->moveScreen(mul*-1, 0); // no shift - } - } else { // no alt - if (MOD__SHIFT(event)) { - desktop->getSelection()->move(mul*-10*nudge, 0); // shift - } else { - desktop->getSelection()->move(mul*-nudge, 0); // no shift - } - } - - ret = TRUE; - } - break; - - case GDK_KEY_Up: // move selection up - case GDK_KEY_KP_Up: - if (!MOD__CTRL(event)) { // not ctrl - gint mul = 1 + gobble_key_events(get_latin_keyval(&event->key), 0); // with any mask - mul *= -y_dir; - - if (MOD__ALT(event)) { // alt - if (MOD__SHIFT(event)) { - desktop->getSelection()->moveScreen(0, mul*10); // shift - } else { - desktop->getSelection()->moveScreen(0, mul*1); // no shift - } - } else { // no alt - if (MOD__SHIFT(event)) { - desktop->getSelection()->move(0, mul*10*nudge); // shift - } else { - desktop->getSelection()->move(0, mul*nudge); // no shift - } - } - - ret = TRUE; - } - break; - - case GDK_KEY_Right: // move selection right - case GDK_KEY_KP_Right: - if (!MOD__CTRL(event)) { // not ctrl - gint mul = 1 + gobble_key_events(get_latin_keyval(&event->key), 0); // with any mask - - if (MOD__ALT(event)) { // alt - if (MOD__SHIFT(event)) { - desktop->getSelection()->moveScreen(mul*10, 0); // shift - } else { - desktop->getSelection()->moveScreen(mul*1, 0); // no shift - } - } else { // no alt - if (MOD__SHIFT(event)) { - desktop->getSelection()->move(mul*10*nudge, 0); // shift - } else { - desktop->getSelection()->move(mul*nudge, 0); // no shift - } - } - - ret = TRUE; - } - break; - - case GDK_KEY_Down: // move selection down - case GDK_KEY_KP_Down: - if (!MOD__CTRL(event)) { // not ctrl - gint mul = 1 + gobble_key_events(get_latin_keyval(&event->key), 0); // with any mask - mul *= -y_dir; - - if (MOD__ALT(event)) { // alt - if (MOD__SHIFT(event)) { - desktop->getSelection()->moveScreen(0, mul*-10); // shift - } else { - desktop->getSelection()->moveScreen(0, mul*-1); // no shift - } - } else { // no alt - if (MOD__SHIFT(event)) { - desktop->getSelection()->move(0, mul*-10*nudge); // shift - } else { - desktop->getSelection()->move(0, mul*-nudge); // no shift - } - } - - ret = TRUE; - } - break; - case GDK_KEY_Escape: if (!this->sp_select_context_abort()) { selection->clear(); -- GitLab From 2d40349830a0fbd53d5b5de81c38769bba9741ce Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 21 Jul 2021 13:43:27 +0200 Subject: [PATCH 104/235] Deleted the alt + x shortcut from the BuilderTool. --- src/ui/tools/builder-tool.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 770b735e55..3a9d90f42c 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -478,14 +478,6 @@ bool BuilderTool::event_key_press_handler(GdkEvent *event) } break; - case GDK_KEY_x: - case GDK_KEY_X: - if (MOD__ALT_ONLY(event)) { - desktop->setToolboxFocusTo ("select-x"); - ret = true; - } - break; - case GDK_KEY_bracketleft: if (MOD__ALT(event)) { gint mul = 1 + gobble_key_events(get_latin_keyval(&event->key), 0); // with any mask -- GitLab From f4c67326df3e607408543fb155a42f93c0c4fcb1 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 21 Jul 2021 13:44:21 +0200 Subject: [PATCH 105/235] Deleted the ctrl + backspace shortcut from the BuilderTool. --- src/ui/tools/builder-tool.cpp | 38 ----------------------------------- 1 file changed, 38 deletions(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 3a9d90f42c..6c26d34c97 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -173,37 +173,6 @@ key_is_a_modifier (guint key) { key == GDK_KEY_Meta_R); } -static void -sp_select_context_up_one_layer(SPDesktop *desktop) -{ - /* Click in empty place, go up one level -- but don't leave a layer to root. - * - * (Rationale: we don't usually allow users to go to the root, since that - * detracts from the layer metaphor: objects at the root level can in front - * of or behind layers. Whereas it's fine to go to the root if editing - * a document that has no layers (e.g. a non-Inkscape document).) - * - * Once we support editing SVG "islands" (e.g. embedded in an xhtml - * document), we might consider further restricting the below to disallow - * leaving a layer to go to a non-layer. - */ - SPObject *const current_layer = desktop->currentLayer(); - if (current_layer) { - SPObject *const parent = current_layer->parent; - SPGroup *current_group = dynamic_cast(current_layer); - if ( parent - && ( parent->parent - || !( current_group - && ( SPGroup::LAYER == current_group->layerMode() ) ) ) ) - { - desktop->setCurrentLayer(parent); - if (current_group && (SPGroup::LAYER != current_group->layerMode())) { - desktop->getSelection()->set(current_layer); - } - } - } -} - bool BuilderTool::item_handler(SPItem* item, GdkEvent* event) { // TODO consider the case for when the ENTER_NOTIFY (to set a pattern). @@ -504,13 +473,6 @@ bool BuilderTool::event_key_press_handler(GdkEvent *event) ret = true; break; - case GDK_KEY_BackSpace: - if (MOD__CTRL_ONLY(event)) { - sp_select_context_up_one_layer(desktop); - ret = true; - } - break; - case GDK_KEY_s: case GDK_KEY_S: if (MOD__SHIFT_ONLY(event)) { -- GitLab From 63406abd339d1440966d280da834405121fd1df0 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 22 Jul 2021 19:26:40 +0200 Subject: [PATCH 106/235] Set the operations to be done automatically when the mode is changed. --- src/ui/tools/builder-tool.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 6c26d34c97..4fe4992b42 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -552,6 +552,10 @@ void BuilderTool::set_current_mode(int current_mode) active_mode = current_mode; set_cursor_mode(); + + auto selection = desktop->getSelection(); + perform_operation(selection, current_mode); + // TODO add a function here to change the // patter of the items the cursor went over. } -- GitLab From 0fd0b1ee12de50455b3730a5cee6bde2f9f01ddf Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 22 Jul 2021 19:27:04 +0200 Subject: [PATCH 107/235] Implemented the logic for a single click in the BuilderTool. --- src/ui/tools/builder-tool.cpp | 110 ++++++++++++++-------------------- src/ui/tools/builder-tool.h | 4 ++ 2 files changed, 50 insertions(+), 64 deletions(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 4fe4992b42..0ef12db3e9 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -173,8 +173,8 @@ key_is_a_modifier (guint key) { key == GDK_KEY_Meta_R); } -bool BuilderTool::item_handler(SPItem* item, GdkEvent* event) { - +bool BuilderTool::item_handler(SPItem* item, GdkEvent* event) +{ // TODO consider the case for when the ENTER_NOTIFY (to set a pattern). return root_handler(event); } @@ -282,76 +282,30 @@ bool BuilderTool::event_button_release_handler(GdkEvent *event) r->stop(); this->defaultMessageContext()->clear(); - { - int mode = get_current_mode(); - - if(mode == JUST_SELECT && Modifier::get(Modifiers::Type::SELECT_ADD_TO)->active(event->button.state)) { - // with shift, add to selection - selection->addList (items); - } else { - // without shift, simply select anew - selection->setList (items); - - if (mode != JUST_SELECT) { - if (mode == SELECT_AND_UNION) { - selection->pathUnion(); - } else if (mode == SELECT_AND_DELETE) { - selection->deleteItems(); - } else if (mode == SELECT_AND_INTERSECT) { - selection->pathIntersect(); - } - selection->clear(); - } - } + int mode = get_current_mode(); + + if(is_mode_add_to_selection(mode, event)) { + selection->addList (items); + } else { + selection->setList (items); + perform_operation(selection, mode); } } else { // it was just a click, or a too small rubberband r->stop(); - bool add_to = Modifier::get(Modifiers::Type::SELECT_ADD_TO)->active(event->button.state); - bool in_groups = Modifier::get(Modifiers::Type::SELECT_IN_GROUPS)->active(event->button.state); - bool force_drag = Modifier::get(Modifiers::Type::SELECT_FORCE_DRAG)->active(event->button.state); - - if (add_to && !rb_escaped && !drag_escaped) { - // this was a shift+click or alt+shift+click, select what was clicked upon - - if (in_groups) { - // go into groups, honoring force_drag (Alt) - item = sp_event_context_find_item (desktop, - Geom::Point(event->button.x, event->button.y), force_drag, TRUE); - } else { - // don't go into groups, honoring Alt - item = sp_event_context_find_item (desktop, - Geom::Point(event->button.x, event->button.y), force_drag, FALSE); - } - - if (item) { - selection->toggle(item); - item = nullptr; - } - - } else if ((in_groups || force_drag) && !rb_escaped && !drag_escaped) { // ctrl+click, alt+click - item = sp_event_context_find_item (desktop, - Geom::Point(event->button.x, event->button.y), force_drag, in_groups); + int mode = get_current_mode(); - if (item) { - if (selection->includes(item)) { - _seltrans->increaseState(); - } else { - _seltrans->resetState(); - selection->set(item); - } + if (mode == JUST_SELECT && !is_mode_add_to_selection(mode, event)) { + selection->clear(); + } - item = nullptr; - } - } else { // click without shift, simply deselect, unless with Alt or something was cancelled - if (!selection->isEmpty()) { - if (!(rb_escaped) && !(drag_escaped) && !force_drag) { - selection->clear(); - } + bool in_groups = Modifier::get(Modifiers::Type::SELECT_IN_GROUPS)->active(event->button.state); - rb_escaped = 0; - } + auto item = sp_event_context_find_item(desktop, Geom::Point(event->button.x, event->button.y), false, in_groups); + if (item) { + selection->add(item); + perform_operation(selection, mode); } } } @@ -510,6 +464,29 @@ bool BuilderTool::event_key_release_handler(GdkEvent *event) return false; } +void BuilderTool::perform_operation(Selection *selection, int mode) +{ + int size = selection->size(); + if (mode != JUST_SELECT) { + if (mode == SELECT_AND_UNION && size > 1) { + selection->pathUnion(); + selection->clear(); + } else if (mode == SELECT_AND_DELETE) { + selection->deleteItems(); + selection->clear(); + } else if (mode == SELECT_AND_INTERSECT && size > 1) { + selection->pathIntersect(); + selection->clear(); + } + } +} + +void BuilderTool::perform_current_operation(Selection *selection) +{ + int mode = get_current_mode(); + return perform_operation(selection, mode); +} + void BuilderTool::set_modifiers_state(GdkEvent* event) { // TODO This function is deprecated. @@ -578,6 +555,11 @@ void BuilderTool::set_cursor_mode() ToolBase::sp_event_context_update_cursor(); } +bool BuilderTool::is_mode_add_to_selection(int mode, GdkEvent *event) +{ + return mode == JUST_SELECT && Modifier::get(Modifiers::Type::SELECT_ADD_TO)->active(event->button.state); +} + } } } diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index 5f9af37376..66386c2cb1 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -76,8 +76,12 @@ private: bool event_key_press_handler(GdkEvent* event); bool event_key_release_handler(GdkEvent* event); + void perform_operation(Selection *selection, int mode); + void perform_current_operation(Selection *selection); + void set_modifiers_state(GdkEvent* event); int get_current_mode(); + bool is_mode_add_to_selection(int mode, GdkEvent *event); void set_cursor_mode(); // TODO you might pre-load the cursors and store them -- GitLab From c937fd1a2a70f3089704c29fb93e3d443a38580a Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 22 Jul 2021 19:28:44 +0200 Subject: [PATCH 108/235] Disabled the deletion of the shapes with two nodes int the NonOverlappingPathsBuilder. --- src/helper/NonOverlappingPathsBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index f0cfe83407..5715d61875 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -150,7 +150,7 @@ void PathHelper::clean() // causes shapes that are not lines to be removed. // also, not removing the lines causes some shapes // to be removed when using fracture... investigate why. - paths = remove_lines_from_pathvec(paths); + // paths = remove_lines_from_pathvec(paths); // paths = remove_loop_lines_from_pathvec(paths); // paths = remove_paths_with_small_area_from_pathvec(paths, 0.5); // double max_area = 0; -- GitLab From c52a53d230f34ace17af64a6b0fd913f796bd652 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 22 Jul 2021 19:39:15 +0200 Subject: [PATCH 109/235] Set the Builder tool to not perform the operation in the setup. --- src/ui/tools/builder-tool.cpp | 11 +++++++---- src/ui/tools/builder-tool.h | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 0ef12db3e9..a47dc78090 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -135,7 +135,7 @@ void BuilderTool::setup() { this->enableGrDrag(); } - set_current_mode(); + set_current_mode(-1, false); } void BuilderTool::set(const Inkscape::Preferences::Entry& val) { @@ -517,7 +517,7 @@ int BuilderTool::get_current_mode() return prefs->getInt("/tools/builder/mode", 0); } -void BuilderTool::set_current_mode(int current_mode) +void BuilderTool::set_current_mode(int current_mode, bool perform_operation) { if (current_mode == -1) { current_mode = get_current_mode(); @@ -530,8 +530,11 @@ void BuilderTool::set_current_mode(int current_mode) active_mode = current_mode; set_cursor_mode(); - auto selection = desktop->getSelection(); - perform_operation(selection, current_mode); + if (perform_operation) { + // should the name of the bool be changed? + auto selection = desktop->getSelection(); + this->perform_operation(selection, current_mode); + } // TODO add a function here to change the // patter of the items the cursor went over. diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index 66386c2cb1..e185dc30a5 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -63,7 +63,7 @@ public: const std::string& getPrefsPath() override; void set_current_mode(GdkEvent* event); - void set_current_mode(int mode = -1); + void set_current_mode(int current_mode = -1, bool perform_operation = true); private: -- GitLab From 60e17895a8bdac43320f73fc663ae21b0928cd05 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 22 Jul 2021 21:35:30 +0200 Subject: [PATCH 110/235] Reverted the automatic operation performance on mode change in the Builder tool. --- src/ui/tools/builder-tool.cpp | 10 ++-------- src/ui/tools/builder-tool.h | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index a47dc78090..b1d0273b6d 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -135,7 +135,7 @@ void BuilderTool::setup() { this->enableGrDrag(); } - set_current_mode(-1, false); + set_current_mode(); } void BuilderTool::set(const Inkscape::Preferences::Entry& val) { @@ -517,7 +517,7 @@ int BuilderTool::get_current_mode() return prefs->getInt("/tools/builder/mode", 0); } -void BuilderTool::set_current_mode(int current_mode, bool perform_operation) +void BuilderTool::set_current_mode(int current_mode) { if (current_mode == -1) { current_mode = get_current_mode(); @@ -530,12 +530,6 @@ void BuilderTool::set_current_mode(int current_mode, bool perform_operation) active_mode = current_mode; set_cursor_mode(); - if (perform_operation) { - // should the name of the bool be changed? - auto selection = desktop->getSelection(); - this->perform_operation(selection, current_mode); - } - // TODO add a function here to change the // patter of the items the cursor went over. } diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index e185dc30a5..e96a37a22b 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -63,7 +63,7 @@ public: const std::string& getPrefsPath() override; void set_current_mode(GdkEvent* event); - void set_current_mode(int current_mode = -1, bool perform_operation = true); + void set_current_mode(int current_mode = -1); private: -- GitLab From 3f9e1f8ddf196c32629fe6005468c9669b5514f5 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 22 Jul 2021 21:38:23 +0200 Subject: [PATCH 111/235] Changed the subtraction mode in the Builder tool to perform difference instead of deleting. --- src/ui/tools/builder-tool.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index b1d0273b6d..cec4b03879 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -467,17 +467,15 @@ bool BuilderTool::event_key_release_handler(GdkEvent *event) void BuilderTool::perform_operation(Selection *selection, int mode) { int size = selection->size(); - if (mode != JUST_SELECT) { - if (mode == SELECT_AND_UNION && size > 1) { + if (mode != JUST_SELECT && size > 1) { + if (mode == SELECT_AND_UNION) { selection->pathUnion(); - selection->clear(); } else if (mode == SELECT_AND_DELETE) { - selection->deleteItems(); - selection->clear(); - } else if (mode == SELECT_AND_INTERSECT && size > 1) { + selection->pathDiff(); + } else if (mode == SELECT_AND_INTERSECT) { selection->pathIntersect(); - selection->clear(); } + selection->clear(); } } -- GitLab From 5fddf97f616ff345f0de9baba6addc2bc3492c02 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 22 Jul 2021 21:40:41 +0200 Subject: [PATCH 112/235] Now in the Builder tool, clicking in an empty area will clear the selection. --- src/ui/tools/builder-tool.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index cec4b03879..13d1d48ffc 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -306,6 +306,9 @@ bool BuilderTool::event_button_release_handler(GdkEvent *event) if (item) { selection->add(item); perform_operation(selection, mode); + } else { + // clicked in an empty area + selection->clear(); } } } -- GitLab From ed59e468cd15655e0310ab760c78b25e8ec953b9 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 02:01:22 +0200 Subject: [PATCH 113/235] Removed curve_length_divisor from the NonOverlappingPathsBuilder. --- src/helper/NonOverlappingPathsBuilder.cpp | 12 ++++++------ src/helper/NonOverlappingPathsBuilder.h | 11 +++-------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 5715d61875..be592600c5 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -186,7 +186,7 @@ std::vector PathHelper::flatten(const PathHelper &path) } auto bottom_diff_paths = sp_pathvector_boolop(top->paths, bottom->paths, bool_op_diff, fill_nonZero, fill_nonZero); - PathHelper bottom_diff(bottom_diff_paths, bottom->item, bottom->operations, curve_length_divisor); + PathHelper bottom_diff(bottom_diff_paths, bottom->item, bottom->operations); std::vector result = {*top, bottom_diff}; result[0].prepare_for_current_operation(); @@ -215,7 +215,7 @@ std::vector PathHelper::fracture(const PathHelper &path) auto intersection_paths = sp_pathvector_boolop(paths, path.paths, bool_op_inters, fill_nonZero, fill_nonZero); // auto intersection_paths = pig.getIntersection(); auto intersection_item = sp_item_repr_compare_position_bool(path.item, this->item) ? this->item : path.item; - PathHelper intersection(intersection_paths, intersection_item, operations, path.operations, curve_length_divisor); + PathHelper intersection(intersection_paths, intersection_item, operations, path.operations); if (intersection.paths.empty()) { return {}; @@ -223,11 +223,11 @@ std::vector PathHelper::fracture(const PathHelper &path) // auto diff1_paths = pig.getAminusB(); auto diff1_paths = sp_pathvector_boolop(paths, path.paths, bool_op_diff, fill_nonZero, fill_nonZero); - PathHelper diff1(diff1_paths, path.item, path.operations, curve_length_divisor); + PathHelper diff1(diff1_paths, path.item, path.operations); // auto diff2_paths = pig.getBminusA(); auto diff2_paths = sp_pathvector_boolop(path.paths, paths, bool_op_diff, fill_nonZero, fill_nonZero); - PathHelper diff2(diff2_paths, item, operations, curve_length_divisor); + PathHelper diff2(diff2_paths, item, operations); std::vector result = {intersection, diff1, diff2}; @@ -255,7 +255,7 @@ std::vector break_apart_non_touching(std::vector &paths) for (auto &path : paths) { auto broken = break_apart_non_touching(path.paths); for (auto &broken_path : broken) { - result.emplace_back(broken_path, path.item, path.operations, path.curve_length_divisor); + result.emplace_back(broken_path, path.item, path.operations); } } return result; @@ -268,7 +268,7 @@ void NonOverlappingPathsBuilder::construct_operation_shapes(PathHelperOperation result.resize(n); for (int i = 0; i < n; i++) { - result[i] = PathHelper(items[i]->get_pathvector(), items[i], {}, curve_length_divisor); + result[i] = PathHelper(items[i]->get_pathvector(), items[i], {}); } for (int i = 0; i < result.size(); i++) { diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index 949c3cc304..6b2cfeffa6 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -28,7 +28,6 @@ public: bool is_destructive = true; bool put_above_originals = true; // won't matter if is_destructive is true. bool add_result_to_set = true; - double curve_length_divisor = 5; private: @@ -70,20 +69,16 @@ public: SPItem *item; std::set operations; - // FIXME!! find a better way of doing this... this value is copied for each object... - double curve_length_divisor; - PathHelper() {} - PathHelper(Geom::PathVector paths, SPItem *item, std::set operations, double curve_length_divisor) + PathHelper(Geom::PathVector paths, SPItem *item, std::set operations) : paths(std::move(paths)) , item(item) , operations(std::move(operations)) - , curve_length_divisor(curve_length_divisor) {} - PathHelper(Geom::PathVector paths, SPItem *item, std::set operations1, const std::set &operations2, double curve_length_divisor) - : PathHelper(paths, item, operations1, curve_length_divisor) + PathHelper(Geom::PathVector paths, SPItem *item, std::set operations1, const std::set &operations2) + : PathHelper(paths, item, operations1) { operations.insert(operations2.begin(), operations2.end()); } -- GitLab From b5682c6ca959e71ecc89dd5c6892475caa75f4d5 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 02:19:05 +0200 Subject: [PATCH 114/235] Renamed the PathHelper class to SubItem. --- src/helper/NonOverlappingPathsBuilder.cpp | 48 +++++++++++------------ src/helper/NonOverlappingPathsBuilder.h | 31 +++++++++------ 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index be592600c5..482bbe441f 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -27,7 +27,7 @@ namespace Inkscape { -using PathHelper = NonOverlappingPathsBuilder::PathHelper; +using SubItem = NonOverlappingPathsBuilder::SubItem; // TODO this is a duplicate code from selecction-chemistry.cpp. refactor it. static void sp_selection_delete_impl(std::vector const &items, bool propagate = true, bool propagate_descendants = true) @@ -41,7 +41,7 @@ static void sp_selection_delete_impl(std::vector const &items, bool pro } } -void NonOverlappingPathsBuilder::do_work(PathHelperOperation operation) +void NonOverlappingPathsBuilder::do_work(SubItemOperation operation) { // TODO It should have its own widget. // TODO (for swipe-union thing) may want to copy the style of @@ -84,7 +84,7 @@ void NonOverlappingPathsBuilder::do_work(PathHelperOperation operation) void NonOverlappingPathsBuilder::fracture() { - auto operation = [](PathHelper& a, PathHelper& b) { return a.fracture(b); }; + auto operation = [](SubItem & a, SubItem & b) { return a.fracture(b); }; do_work(operation); if (set->document()) { @@ -94,7 +94,7 @@ void NonOverlappingPathsBuilder::fracture() void NonOverlappingPathsBuilder::flatten() { - auto operation = [](PathHelper& a, PathHelper& b) { return a.flatten(b); }; + auto operation = [](SubItem & a, SubItem & b) { return a.flatten(b); }; do_work(operation); if (set->document()) { @@ -144,7 +144,7 @@ SPDesktop *NonOverlappingPathsBuilder::desktop() return set->desktop(); } -void PathHelper::clean() +void SubItem::clean() { // FIXME removing the shapes with only 2 nodes // causes shapes that are not lines to be removed. @@ -160,7 +160,7 @@ void PathHelper::clean() // paths = remove_paths_with_small_area_from_pathvec(paths, max_area/100); } -int PathHelper::get_common_operation(const PathHelper &path) +int SubItem::get_common_operation(const SubItem &path) { for (auto operation : path.operations) { if (part_of_operation(operation)) { @@ -170,25 +170,25 @@ int PathHelper::get_common_operation(const PathHelper &path) return -1; } -void PathHelper::prepare_for_current_operation() +void SubItem::prepare_for_current_operation() { operations.insert(counter); clean(); } -std::vector PathHelper::flatten(const PathHelper &path) +std::vector SubItem::flatten(const SubItem &path) { - const PathHelper *top = &path; - const PathHelper *bottom = this; + const SubItem *top = &path; + const SubItem *bottom = this; if (sp_item_repr_compare_position_bool(top->item, bottom->item)) { std::swap(top, bottom); } auto bottom_diff_paths = sp_pathvector_boolop(top->paths, bottom->paths, bool_op_diff, fill_nonZero, fill_nonZero); - PathHelper bottom_diff(bottom_diff_paths, bottom->item, bottom->operations); + SubItem bottom_diff(bottom_diff_paths, bottom->item, bottom->operations); - std::vector result = {*top, bottom_diff}; + std::vector result = {*top, bottom_diff}; result[0].prepare_for_current_operation(); result[1].prepare_for_current_operation(); @@ -205,7 +205,7 @@ std::vector PathHelper::flatten(const PathHelper &path) return result; } -std::vector PathHelper::fracture(const PathHelper &path) +std::vector SubItem::fracture(const SubItem &path) { // FIXME you can't rely on the current area calculation method. make it work with curves. // assert(get_common_operation(path) == -1); @@ -215,7 +215,7 @@ std::vector PathHelper::fracture(const PathHelper &path) auto intersection_paths = sp_pathvector_boolop(paths, path.paths, bool_op_inters, fill_nonZero, fill_nonZero); // auto intersection_paths = pig.getIntersection(); auto intersection_item = sp_item_repr_compare_position_bool(path.item, this->item) ? this->item : path.item; - PathHelper intersection(intersection_paths, intersection_item, operations, path.operations); + SubItem intersection(intersection_paths, intersection_item, operations, path.operations); if (intersection.paths.empty()) { return {}; @@ -223,13 +223,13 @@ std::vector PathHelper::fracture(const PathHelper &path) // auto diff1_paths = pig.getAminusB(); auto diff1_paths = sp_pathvector_boolop(paths, path.paths, bool_op_diff, fill_nonZero, fill_nonZero); - PathHelper diff1(diff1_paths, path.item, path.operations); + SubItem diff1(diff1_paths, path.item, path.operations); // auto diff2_paths = pig.getBminusA(); auto diff2_paths = sp_pathvector_boolop(path.paths, paths, bool_op_diff, fill_nonZero, fill_nonZero); - PathHelper diff2(diff2_paths, item, operations); + SubItem diff2(diff2_paths, item, operations); - std::vector result = {intersection, diff1, diff2}; + std::vector result = {intersection, diff1, diff2}; for (int i = 0; i < result.size(); i++) { result[i].prepare_for_current_operation(); @@ -243,15 +243,15 @@ std::vector PathHelper::fracture(const PathHelper &path) return result; -// std::vector break_apart_non_touching(std::vector &paths); +// std::vector break_apart_non_touching(std::vector &paths); // return break_apart_non_touching(result); } -int PathHelper::counter = 0; +int SubItem::counter = 0; -std::vector break_apart_non_touching(std::vector &paths) +std::vector break_apart_non_touching(std::vector &paths) { - std::vector result; + std::vector result; for (auto &path : paths) { auto broken = break_apart_non_touching(path.paths); for (auto &broken_path : broken) { @@ -261,14 +261,14 @@ std::vector break_apart_non_touching(std::vector &paths) return result; } -void NonOverlappingPathsBuilder::construct_operation_shapes(PathHelperOperation operation) +void NonOverlappingPathsBuilder::construct_operation_shapes(SubItemOperation operation) { - std::vector result; + std::vector result; int n = items.size(); result.resize(n); for (int i = 0; i < n; i++) { - result[i] = PathHelper(items[i]->get_pathvector(), items[i], {}); + result[i] = SubItem(items[i]->get_pathvector(), items[i], {}); } for (int i = 0; i < result.size(); i++) { diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index 6b2cfeffa6..489ca457b1 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -40,24 +40,31 @@ private: public: - class PathHelper; + class SubItem; NonOverlappingPathsBuilder(ObjectSet *set) : set(set) {} void fracture(); void flatten(); private: - using PathHelperOperation = std::function(PathHelper&, PathHelper&)>; + using SubItemOperation = std::function(SubItem &, SubItem &)>; - void do_work(PathHelperOperation operation); - void construct_operation_shapes(PathHelperOperation operation); + void do_work(SubItemOperation operation); + void construct_operation_shapes(SubItemOperation operation); SPDesktop *desktop(); void set_parameters(); SPItem* get_most_item(std::function cmp); public: - class PathHelper + /** + * When an item from the original ObjectSet is broken, each + * broken part is represented by the SubItem class. This + * class hold information such as the original item it originated + * from, the operations that lead to the creation of this SubItem, + * and the paths that the SubItem consists of. + **/ + class SubItem { static int counter; @@ -69,26 +76,26 @@ public: SPItem *item; std::set operations; - PathHelper() {} + SubItem() {} - PathHelper(Geom::PathVector paths, SPItem *item, std::set operations) + SubItem(Geom::PathVector paths, SPItem *item, std::set operations) : paths(std::move(paths)) , item(item) , operations(std::move(operations)) {} - PathHelper(Geom::PathVector paths, SPItem *item, std::set operations1, const std::set &operations2) - : PathHelper(paths, item, operations1) + SubItem(Geom::PathVector paths, SPItem *item, std::set operations1, const std::set &operations2) + : SubItem(paths, item, operations1) { operations.insert(operations2.begin(), operations2.end()); } bool part_of_operation(int operation) { return operations.find(operation) != operations.end(); } - int get_common_operation(const PathHelper &path); + int get_common_operation(const SubItem &path); void prepare_for_current_operation(); - std::vector flatten(const PathHelper &path); - std::vector fracture(const PathHelper &path); + std::vector flatten(const SubItem &path); + std::vector fracture(const SubItem &path); }; }; -- GitLab From 6cf155488bb827831ef278f466ff62ffa38659dc Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 02:20:35 +0200 Subject: [PATCH 115/235] Removed the clean() function from the SubItem class. --- src/helper/NonOverlappingPathsBuilder.cpp | 17 ----------------- src/helper/NonOverlappingPathsBuilder.h | 2 -- 2 files changed, 19 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 482bbe441f..8d2b6688bf 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -144,22 +144,6 @@ SPDesktop *NonOverlappingPathsBuilder::desktop() return set->desktop(); } -void SubItem::clean() -{ - // FIXME removing the shapes with only 2 nodes - // causes shapes that are not lines to be removed. - // also, not removing the lines causes some shapes - // to be removed when using fracture... investigate why. - // paths = remove_lines_from_pathvec(paths); - // paths = remove_loop_lines_from_pathvec(paths); - // paths = remove_paths_with_small_area_from_pathvec(paths, 0.5); - // double max_area = 0; - // for (auto &path : paths) { - // max_area = std::max(max_area, path_area_non_self_intersecting(path)); - // } - // paths = remove_paths_with_small_area_from_pathvec(paths, max_area/100); -} - int SubItem::get_common_operation(const SubItem &path) { for (auto operation : path.operations) { @@ -173,7 +157,6 @@ int SubItem::get_common_operation(const SubItem &path) void SubItem::prepare_for_current_operation() { operations.insert(counter); - clean(); } std::vector SubItem::flatten(const SubItem &path) diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index 489ca457b1..9fa449015c 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -68,8 +68,6 @@ public: { static int counter; - void clean(); - public: Geom::PathVector paths; -- GitLab From 9bea875ec4d5e70ccc71229f57216b39b5108fe1 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 02:54:50 +0200 Subject: [PATCH 116/235] Renamed the method part_of_operation to is_part_of_operation. --- src/helper/NonOverlappingPathsBuilder.cpp | 2 +- src/helper/NonOverlappingPathsBuilder.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 8d2b6688bf..3c0dad8082 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -147,7 +147,7 @@ SPDesktop *NonOverlappingPathsBuilder::desktop() int SubItem::get_common_operation(const SubItem &path) { for (auto operation : path.operations) { - if (part_of_operation(operation)) { + if (is_part_of_operation(operation)) { return operation; } } diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index 9fa449015c..5a3988e024 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -88,7 +88,7 @@ public: operations.insert(operations2.begin(), operations2.end()); } - bool part_of_operation(int operation) { return operations.find(operation) != operations.end(); } + bool is_part_of_operation(int operation) { return operations.find(operation) != operations.end(); } int get_common_operation(const SubItem &path); void prepare_for_current_operation(); -- GitLab From 242052d262bc91ed828641fe5ddb22d71db642b6 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 02:59:29 +0200 Subject: [PATCH 117/235] Renamed SubItem::counter to operations_counter. --- src/helper/NonOverlappingPathsBuilder.cpp | 8 ++++---- src/helper/NonOverlappingPathsBuilder.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 3c0dad8082..7bbf65fd9c 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -156,7 +156,7 @@ int SubItem::get_common_operation(const SubItem &path) void SubItem::prepare_for_current_operation() { - operations.insert(counter); + operations.insert(operations_counter); } std::vector SubItem::flatten(const SubItem &path) @@ -183,7 +183,7 @@ std::vector SubItem::flatten(const SubItem &path) } } - counter++; + operations_counter++; return result; } @@ -222,7 +222,7 @@ std::vector SubItem::fracture(const SubItem &path) } } - counter++; + operations_counter++; return result; @@ -230,7 +230,7 @@ std::vector SubItem::fracture(const SubItem &path) // return break_apart_non_touching(result); } -int SubItem::counter = 0; +int SubItem::operations_counter = 0; std::vector break_apart_non_touching(std::vector &paths) { diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index 5a3988e024..b58260f381 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -66,7 +66,7 @@ public: **/ class SubItem { - static int counter; + static int operations_counter; public: -- GitLab From d4a9c4c627a007536d31a05f31b3eab2c8d185db Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 04:07:07 +0200 Subject: [PATCH 118/235] Removed some unuseful comments from NonOverlappingPathsBuilder.cpp. --- src/helper/NonOverlappingPathsBuilder.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 7bbf65fd9c..ed6528d7c0 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -13,7 +13,6 @@ #include "NonOverlappingPathsBuilder.h" -#include <2geom/intersection-graph.h> #include #include #include @@ -73,7 +72,6 @@ void NonOverlappingPathsBuilder::do_work(SubItemOperation operation) } if (add_result_to_set) { - // TODO probably there is a better way of doing this? for (auto node : result_nodes) { set->add(node); } @@ -190,13 +188,7 @@ std::vector SubItem::flatten(const SubItem &path) std::vector SubItem::fracture(const SubItem &path) { - // FIXME you can't rely on the current area calculation method. make it work with curves. - // assert(get_common_operation(path) == -1); - - // Geom::PathIntersectionGraph pig(paths, path.paths, Geom::EPSILON); - auto intersection_paths = sp_pathvector_boolop(paths, path.paths, bool_op_inters, fill_nonZero, fill_nonZero); - // auto intersection_paths = pig.getIntersection(); auto intersection_item = sp_item_repr_compare_position_bool(path.item, this->item) ? this->item : path.item; SubItem intersection(intersection_paths, intersection_item, operations, path.operations); @@ -204,11 +196,9 @@ std::vector SubItem::fracture(const SubItem &path) return {}; } - // auto diff1_paths = pig.getAminusB(); auto diff1_paths = sp_pathvector_boolop(paths, path.paths, bool_op_diff, fill_nonZero, fill_nonZero); SubItem diff1(diff1_paths, path.item, path.operations); - // auto diff2_paths = pig.getBminusA(); auto diff2_paths = sp_pathvector_boolop(path.paths, paths, bool_op_diff, fill_nonZero, fill_nonZero); SubItem diff2(diff2_paths, item, operations); @@ -225,9 +215,6 @@ std::vector SubItem::fracture(const SubItem &path) operations_counter++; return result; - -// std::vector break_apart_non_touching(std::vector &paths); -// return break_apart_non_touching(result); } int SubItem::operations_counter = 0; @@ -269,10 +256,6 @@ void NonOverlappingPathsBuilder::construct_operation_shapes(SubItemOperation ope int bigger_index = (i > j) ? i : j; int smaller_index = (i > j) ? j : i; - // TODO for some reason, using erase is faster. - // investigate a bit more about that. - // remove_by_swapping_with_back(result, bigger_index); - // remove_by_swapping_with_back(result, smaller_index); result.erase(result.begin() + bigger_index); result.erase(result.begin() + smaller_index); -- GitLab From 953cd8fbb8064ac2dfc507895880fad8a536aab5 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 04:10:50 +0200 Subject: [PATCH 119/235] Renamed NonOverlappingPathsBuilder::do_work to perform_operation. --- src/helper/NonOverlappingPathsBuilder.cpp | 6 +++--- src/helper/NonOverlappingPathsBuilder.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index ed6528d7c0..2553c1ea74 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -40,7 +40,7 @@ static void sp_selection_delete_impl(std::vector const &items, bool pro } } -void NonOverlappingPathsBuilder::do_work(SubItemOperation operation) +void NonOverlappingPathsBuilder::perform_operation(SubItemOperation operation) { // TODO It should have its own widget. // TODO (for swipe-union thing) may want to copy the style of @@ -83,7 +83,7 @@ void NonOverlappingPathsBuilder::do_work(SubItemOperation operation) void NonOverlappingPathsBuilder::fracture() { auto operation = [](SubItem & a, SubItem & b) { return a.fracture(b); }; - do_work(operation); + perform_operation(operation); if (set->document()) { DocumentUndo::done(set->document(), "Fracture", INKSCAPE_ICON("path-fracture")); @@ -93,7 +93,7 @@ void NonOverlappingPathsBuilder::fracture() void NonOverlappingPathsBuilder::flatten() { auto operation = [](SubItem & a, SubItem & b) { return a.flatten(b); }; - do_work(operation); + perform_operation(operation); if (set->document()) { DocumentUndo::done(set->document(), "Flatten", INKSCAPE_ICON("path-flatten")); diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index b58260f381..6612f51ccb 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -49,7 +49,7 @@ private: using SubItemOperation = std::function(SubItem &, SubItem &)>; - void do_work(SubItemOperation operation); + void perform_operation(SubItemOperation operation); void construct_operation_shapes(SubItemOperation operation); SPDesktop *desktop(); void set_parameters(); -- GitLab From 965a1f763bf15c8ec08f9bbfb451f4b69b9e094d Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 04:14:42 +0200 Subject: [PATCH 120/235] Renamed break_apart_non_touching to split_non_intersecting_paths. --- src/helper/NonOverlappingPathsBuilder.cpp | 10 +++++----- src/helper/useful-functions.cpp | 2 +- src/helper/useful-functions.h | 2 +- src/object/object-set.cpp | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 2553c1ea74..fefcd91eb0 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -219,13 +219,13 @@ std::vector SubItem::fracture(const SubItem &path) int SubItem::operations_counter = 0; -std::vector break_apart_non_touching(std::vector &paths) +std::vector split_non_intersecting_paths(std::vector &paths) { std::vector result; for (auto &path : paths) { - auto broken = break_apart_non_touching(path.paths); - for (auto &broken_path : broken) { - result.emplace_back(broken_path, path.item, path.operations); + auto split = split_non_intersecting_paths(path.paths); + for (auto &split_path : split) { + result.emplace_back(split_path, path.item, path.operations); } } return result; @@ -267,7 +267,7 @@ void NonOverlappingPathsBuilder::construct_operation_shapes(SubItemOperation ope } } - result = break_apart_non_touching(result); + result = split_non_intersecting_paths(result); n = result.size(); result_nodes.resize(n); diff --git a/src/helper/useful-functions.cpp b/src/helper/useful-functions.cpp index 882f75ea75..c82dc2e4d1 100644 --- a/src/helper/useful-functions.cpp +++ b/src/helper/useful-functions.cpp @@ -212,7 +212,7 @@ bool is_intersecting(const Path1 &a, const Path2 &b) { return false; } -std::vector break_apart_non_touching(const Geom::PathVector &paths) +std::vector split_non_intersecting_paths(const Geom::PathVector &paths) { int n = paths.size(); diff --git a/src/helper/useful-functions.h b/src/helper/useful-functions.h index 26bd111017..f180f5dc8b 100644 --- a/src/helper/useful-functions.h +++ b/src/helper/useful-functions.h @@ -53,7 +53,7 @@ double path_area_non_self_intersecting(const Geom::Path &path, double curve_leng Geom::PathVector remove_paths_with_small_area_from_pathvec(const Geom::PathVector &paths, double minimum_area = DEFAULT_MINIMUM_AREA, double curve_length_divisor = DEFAULT_CURVE_LENGTH_DIVISOR); Geom::PathVector remove_lines_from_pathvec(const Geom::PathVector &paths); -std::vector break_apart_non_touching(const Geom::PathVector &paths); +std::vector split_non_intersecting_paths(const Geom::PathVector &paths); template bool is_intersecting(const Path1 &a, const Path2 &b); diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 43dd1e57dd..53be0adca8 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -507,7 +507,7 @@ void ObjectSet::splitNonIntersecting() for (int i = 0; i < n; i++) { auto pathvec = items_vec[i]->get_pathvector(); - auto broken = break_apart_non_touching(pathvec); + auto broken = split_non_intersecting_paths(pathvec); for (auto paths : broken) { result.push_back(draw_on_canvas(paths, items_vec[i], parent)); } -- GitLab From 281620cb18844a7eaefc6840a779d7214b0b5857 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 04:27:48 +0200 Subject: [PATCH 121/235] Split NonOverlappingPathsBuilder::construct_operation_shapes into two functions for better readability. --- src/helper/NonOverlappingPathsBuilder.cpp | 18 ++++++++++++------ src/helper/NonOverlappingPathsBuilder.h | 3 ++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index fefcd91eb0..ee0daa8de1 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -56,16 +56,19 @@ void NonOverlappingPathsBuilder::perform_operation(SubItemOperation operation) // called with a dx or dy equals 0. this is because of an assertion // in the function maybeDone. fix it later. // FIXME enable this and investigate why the program crashes when undoing. -// DocumentUndo::ScopedInsensitive scopedInsensitive(set->document()); + // DocumentUndo::ScopedInsensitive scopedInsensitive(set->document()); set_desktop_busy(desktop()); // Ideally shouldn't be converting to paths? set->toCurves(true); + + // TODO get rid of this line and use affines. set->the_temporary_fix_for_the_transform_bug(); set_parameters(); - construct_operation_shapes(operation); + auto result = get_operation_result(operation); + draw_subitems(result); if (is_destructive) { sp_selection_delete_impl(items); @@ -231,7 +234,7 @@ std::vector split_non_intersecting_paths(std::vector &paths) return result; } -void NonOverlappingPathsBuilder::construct_operation_shapes(SubItemOperation operation) +std::vector NonOverlappingPathsBuilder::get_operation_result(SubItemOperation operation) { std::vector result; int n = items.size(); @@ -267,12 +270,15 @@ void NonOverlappingPathsBuilder::construct_operation_shapes(SubItemOperation ope } } - result = split_non_intersecting_paths(result); + return split_non_intersecting_paths(result); +} - n = result.size(); +void NonOverlappingPathsBuilder::draw_subitems(const std::vector &subitems) +{ + int n = subitems.size(); result_nodes.resize(n); for (int i = 0; i < n; i++) { - result_nodes[i] = draw_on_canvas(result[i].paths, result[i].item, parent, after); + result_nodes[i] = draw_on_canvas(subitems[i].paths, subitems[i].item, parent, after); } } diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index 6612f51ccb..de49aa166c 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -50,7 +50,8 @@ private: using SubItemOperation = std::function(SubItem &, SubItem &)>; void perform_operation(SubItemOperation operation); - void construct_operation_shapes(SubItemOperation operation); + std::vector get_operation_result(SubItemOperation operation); + void draw_subitems(const std::vector &subitems); SPDesktop *desktop(); void set_parameters(); SPItem* get_most_item(std::function cmp); -- GitLab From 0aa2ea8d17ef6335519e56c164aef2d05ae16f4b Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 04:36:14 +0200 Subject: [PATCH 122/235] Renamed SubItems in places they were called "paths". --- src/helper/NonOverlappingPathsBuilder.cpp | 26 +++++++++++------------ src/helper/NonOverlappingPathsBuilder.h | 6 +++--- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index ee0daa8de1..5721aad94d 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -145,9 +145,9 @@ SPDesktop *NonOverlappingPathsBuilder::desktop() return set->desktop(); } -int SubItem::get_common_operation(const SubItem &path) +int SubItem::get_common_operation(const SubItem &other_subitem) { - for (auto operation : path.operations) { + for (auto operation : other_subitem.operations) { if (is_part_of_operation(operation)) { return operation; } @@ -160,9 +160,9 @@ void SubItem::prepare_for_current_operation() operations.insert(operations_counter); } -std::vector SubItem::flatten(const SubItem &path) +std::vector SubItem::flatten(const SubItem &other_subitem) { - const SubItem *top = &path; + const SubItem *top = &other_subitem; const SubItem *bottom = this; if (sp_item_repr_compare_position_bool(top->item, bottom->item)) { @@ -189,20 +189,20 @@ std::vector SubItem::flatten(const SubItem &path) return result; } -std::vector SubItem::fracture(const SubItem &path) +std::vector SubItem::fracture(const SubItem &other_subitem) { - auto intersection_paths = sp_pathvector_boolop(paths, path.paths, bool_op_inters, fill_nonZero, fill_nonZero); - auto intersection_item = sp_item_repr_compare_position_bool(path.item, this->item) ? this->item : path.item; - SubItem intersection(intersection_paths, intersection_item, operations, path.operations); + auto intersection_paths = sp_pathvector_boolop(paths, other_subitem.paths, bool_op_inters, fill_nonZero, fill_nonZero); + auto intersection_item = sp_item_repr_compare_position_bool(other_subitem.item, this->item) ? this->item : other_subitem.item; + SubItem intersection(intersection_paths, intersection_item, operations, other_subitem.operations); if (intersection.paths.empty()) { return {}; } - auto diff1_paths = sp_pathvector_boolop(paths, path.paths, bool_op_diff, fill_nonZero, fill_nonZero); - SubItem diff1(diff1_paths, path.item, path.operations); + auto diff1_paths = sp_pathvector_boolop(paths, other_subitem.paths, bool_op_diff, fill_nonZero, fill_nonZero); + SubItem diff1(diff1_paths, other_subitem.item, other_subitem.operations); - auto diff2_paths = sp_pathvector_boolop(path.paths, paths, bool_op_diff, fill_nonZero, fill_nonZero); + auto diff2_paths = sp_pathvector_boolop(other_subitem.paths, paths, bool_op_diff, fill_nonZero, fill_nonZero); SubItem diff2(diff2_paths, item, operations); std::vector result = {intersection, diff1, diff2}; @@ -222,10 +222,10 @@ std::vector SubItem::fracture(const SubItem &path) int SubItem::operations_counter = 0; -std::vector split_non_intersecting_paths(std::vector &paths) +std::vector split_non_intersecting_paths(std::vector &subitems) { std::vector result; - for (auto &path : paths) { + for (auto &path : subitems) { auto split = split_non_intersecting_paths(path.paths); for (auto &split_path : split) { result.emplace_back(split_path, path.item, path.operations); diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index de49aa166c..bfaf159539 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -91,10 +91,10 @@ public: bool is_part_of_operation(int operation) { return operations.find(operation) != operations.end(); } - int get_common_operation(const SubItem &path); + int get_common_operation(const SubItem &other_subitem); void prepare_for_current_operation(); - std::vector flatten(const SubItem &path); - std::vector fracture(const SubItem &path); + std::vector flatten(const SubItem &other_subitem); + std::vector fracture(const SubItem &other_subitem); }; }; -- GitLab From ecf690400a680612f0fe6abe46433ec138ac1f4c Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 04:42:03 +0200 Subject: [PATCH 123/235] Set SubItem::operations_counter to be updated in get_operation_result instead of being updated inside each operation. --- src/helper/NonOverlappingPathsBuilder.cpp | 5 +---- src/helper/NonOverlappingPathsBuilder.h | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 5721aad94d..14b5df4a81 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -184,8 +184,6 @@ std::vector SubItem::flatten(const SubItem &other_subitem) } } - operations_counter++; - return result; } @@ -215,8 +213,6 @@ std::vector SubItem::fracture(const SubItem &other_subitem) } } - operations_counter++; - return result; } @@ -254,6 +250,7 @@ std::vector NonOverlappingPathsBuilder::get_operation_result(SubItemOpe auto broken = operation(result[i], result[j]); if (broken.empty()) { continue; } + SubItem::operations_counter++; // the bigger index should be erased first. int bigger_index = (i > j) ? i : j; diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index bfaf159539..077b0dcf7f 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -95,6 +95,8 @@ public: void prepare_for_current_operation(); std::vector flatten(const SubItem &other_subitem); std::vector fracture(const SubItem &other_subitem); + + friend class NonOverlappingPathsBuilder; }; }; -- GitLab From fd24f632b469ab7c73274aee4c5fa0b16dc34527 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 04:55:50 +0200 Subject: [PATCH 124/235] Refactored the common code between fracture and flatten. --- src/helper/NonOverlappingPathsBuilder.cpp | 50 ++++++++++------------- src/helper/NonOverlappingPathsBuilder.h | 3 +- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 14b5df4a81..529c082ea1 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -155,11 +155,6 @@ int SubItem::get_common_operation(const SubItem &other_subitem) return -1; } -void SubItem::prepare_for_current_operation() -{ - operations.insert(operations_counter); -} - std::vector SubItem::flatten(const SubItem &other_subitem) { const SubItem *top = &other_subitem; @@ -172,19 +167,7 @@ std::vector SubItem::flatten(const SubItem &other_subitem) auto bottom_diff_paths = sp_pathvector_boolop(top->paths, bottom->paths, bool_op_diff, fill_nonZero, fill_nonZero); SubItem bottom_diff(bottom_diff_paths, bottom->item, bottom->operations); - std::vector result = {*top, bottom_diff}; - result[0].prepare_for_current_operation(); - result[1].prepare_for_current_operation(); - - for (int i = 0; i < result.size(); i++) { - result[i].prepare_for_current_operation(); - if (result[i].paths.empty()) { - result.erase(result.begin() + i); - i--; - } - } - - return result; + return {*top, bottom_diff}; } std::vector SubItem::fracture(const SubItem &other_subitem) @@ -203,17 +186,7 @@ std::vector SubItem::fracture(const SubItem &other_subitem) auto diff2_paths = sp_pathvector_boolop(other_subitem.paths, paths, bool_op_diff, fill_nonZero, fill_nonZero); SubItem diff2(diff2_paths, item, operations); - std::vector result = {intersection, diff1, diff2}; - - for (int i = 0; i < result.size(); i++) { - result[i].prepare_for_current_operation(); - if (result[i].paths.empty()) { - result.erase(result.begin() + i); - i--; - } - } - - return result; + return {intersection, diff1, diff2}; } int SubItem::operations_counter = 0; @@ -249,7 +222,9 @@ std::vector NonOverlappingPathsBuilder::get_operation_result(SubItemOpe if (common_operation != -1) { continue; } auto broken = operation(result[i], result[j]); + remove_empty_subitems(broken); if (broken.empty()) { continue; } + add_current_operation_to_subitems(broken); SubItem::operations_counter++; // the bigger index should be erased first. @@ -279,4 +254,21 @@ void NonOverlappingPathsBuilder::draw_subitems(const std::vector &subit } } +void NonOverlappingPathsBuilder::remove_empty_subitems(std::vector &subitems) +{ + for (int i = 0; i < subitems.size(); i++) { + if (subitems[i].paths.empty()) { + subitems.erase(subitems.begin() + i); + i--; + } + } +} + +void NonOverlappingPathsBuilder::add_current_operation_to_subitems(std::vector &subitems) +{ + for (auto &subitem : subitems) { + subitem.operations.insert(SubItem::operations_counter); + } +} + }; diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index 077b0dcf7f..974ab497c1 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -55,6 +55,8 @@ private: SPDesktop *desktop(); void set_parameters(); SPItem* get_most_item(std::function cmp); + void remove_empty_subitems(std::vector &subitems); + void add_current_operation_to_subitems(std::vector &subitems); public: @@ -92,7 +94,6 @@ public: bool is_part_of_operation(int operation) { return operations.find(operation) != operations.end(); } int get_common_operation(const SubItem &other_subitem); - void prepare_for_current_operation(); std::vector flatten(const SubItem &other_subitem); std::vector fracture(const SubItem &other_subitem); -- GitLab From 33fc869efc40799755967a9bce883101e1525ba1 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 04:59:11 +0200 Subject: [PATCH 125/235] Moved SubItem::operations_counter to be inside NonOverlappingPathsBuilder. --- src/helper/NonOverlappingPathsBuilder.cpp | 6 ++---- src/helper/NonOverlappingPathsBuilder.h | 8 +++----- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index 529c082ea1..b06f42485c 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -189,8 +189,6 @@ std::vector SubItem::fracture(const SubItem &other_subitem) return {intersection, diff1, diff2}; } -int SubItem::operations_counter = 0; - std::vector split_non_intersecting_paths(std::vector &subitems) { std::vector result; @@ -225,7 +223,7 @@ std::vector NonOverlappingPathsBuilder::get_operation_result(SubItemOpe remove_empty_subitems(broken); if (broken.empty()) { continue; } add_current_operation_to_subitems(broken); - SubItem::operations_counter++; + operations_counter++; // the bigger index should be erased first. int bigger_index = (i > j) ? i : j; @@ -267,7 +265,7 @@ void NonOverlappingPathsBuilder::remove_empty_subitems(std::vector &sub void NonOverlappingPathsBuilder::add_current_operation_to_subitems(std::vector &subitems) { for (auto &subitem : subitems) { - subitem.operations.insert(SubItem::operations_counter); + subitem.operations.insert(operations_counter); } } diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index 974ab497c1..76b43986df 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -31,6 +31,8 @@ public: private: + int operations_counter; + XML::Node *parent; XML::Node *after; @@ -41,7 +43,7 @@ private: public: class SubItem; - NonOverlappingPathsBuilder(ObjectSet *set) : set(set) {} + NonOverlappingPathsBuilder(ObjectSet *set) : set(set), operations_counter(0) {} void fracture(); void flatten(); @@ -69,8 +71,6 @@ public: **/ class SubItem { - static int operations_counter; - public: Geom::PathVector paths; @@ -96,8 +96,6 @@ public: int get_common_operation(const SubItem &other_subitem); std::vector flatten(const SubItem &other_subitem); std::vector fracture(const SubItem &other_subitem); - - friend class NonOverlappingPathsBuilder; }; }; -- GitLab From 7cace1d02aaea14beed2d677ab55ed711d4414a2 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 05:17:52 +0200 Subject: [PATCH 126/235] Added and deleted some comments in NonOverlappingPathsBuilder.*. --- src/helper/NonOverlappingPathsBuilder.cpp | 19 +++++++++++-------- src/helper/NonOverlappingPathsBuilder.h | 2 ++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonOverlappingPathsBuilder.cpp index b06f42485c..940964a553 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonOverlappingPathsBuilder.cpp @@ -28,7 +28,7 @@ namespace Inkscape { using SubItem = NonOverlappingPathsBuilder::SubItem; -// TODO this is a duplicate code from selecction-chemistry.cpp. refactor it. +// TODO this is a duplicate code from selection-chemistry.cpp. refactor it. static void sp_selection_delete_impl(std::vector const &items, bool propagate = true, bool propagate_descendants = true) { for (auto item : items) { @@ -42,12 +42,6 @@ static void sp_selection_delete_impl(std::vector const &items, bool pro void NonOverlappingPathsBuilder::perform_operation(SubItemOperation operation) { - // TODO It should have its own widget. - // TODO (for swipe-union thing) may want to copy the style of - // the first object in the selection instead of letting it be - // random (probably the it copies the style of the biggest - // object). may also let the user decide. - if (set->isEmpty()) { return; } @@ -211,17 +205,26 @@ std::vector NonOverlappingPathsBuilder::get_operation_result(SubItemOpe result[i] = SubItem(items[i]->get_pathvector(), items[i], {}); } + // result will grow as items are pushed + // into it, so does result.size(). for (int i = 0; i < result.size(); i++) { for (int j = 0; j < result.size(); j++) { if (i == j) { continue; } + // if 2 subitems share at least one operation, then + // they don't intersect by definition (since operations + // in this class yields non-intersecting paths). continue. + // The concept of operations might be deleted in + // the future, but currently, the boolops are not + // sophisticated enough and this check is needed + // to avoid going into an infinite loop. int common_operation = result[i].get_common_operation(result[j]); if (common_operation != -1) { continue; } auto broken = operation(result[i], result[j]); remove_empty_subitems(broken); - if (broken.empty()) { continue; } + if (broken.empty()) { continue; } // don't intersect. continue. add_current_operation_to_subitems(broken); operations_counter++; diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonOverlappingPathsBuilder.h index 76b43986df..1fc9334334 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonOverlappingPathsBuilder.h @@ -21,6 +21,8 @@ namespace Inkscape { class NonOverlappingPathsBuilder { + // TODO you might simplify this class so that it only constructs + // paths, and move the rest of the logic somewhere else. public: -- GitLab From b840b9ba3a0ad794932410db129635b385d49efe Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 14:36:06 +0200 Subject: [PATCH 127/235] Renamed ObjectSet::break_into_non_overlapping_pieces to fracture. --- src/actions/actions-selection.cpp | 2 +- src/object/object-set.cpp | 2 +- src/object/object-set.h | 2 +- src/ui/toolbar/builder-toolbar.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/actions/actions-selection.cpp b/src/actions/actions-selection.cpp index 99e3242481..cd1c1f4f68 100644 --- a/src/actions/actions-selection.cpp +++ b/src/actions/actions-selection.cpp @@ -302,7 +302,7 @@ select_path_fracture(InkscapeApplication *app) auto selection = app->get_active_selection(); selection->removeLPESRecursive(true); selection->unlinkRecursive(true); - selection->break_into_non_overlapping_pieces(); + selection->fracture(); } void diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 53be0adca8..66b532bcfb 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -469,7 +469,7 @@ void ObjectSet::remove_paths_with_small_area() } } -void ObjectSet::break_into_non_overlapping_pieces(bool fill_holes, bool is_destructive, bool put_above_originals) +void ObjectSet::fracture(bool fill_holes, bool is_destructive, bool put_above_originals) { NonOverlappingPathsBuilder builder(this); builder.fill_holes = fill_holes; diff --git a/src/object/object-set.h b/src/object/object-set.h index f25b7f8401..676b921606 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -470,7 +470,7 @@ public: void remove_paths_with_small_area(); void the_temporary_fix_for_the_transform_bug(); - void break_into_non_overlapping_pieces(bool fill_holes = false, bool is_destructive = true, + void fracture(bool fill_holes = false, bool is_destructive = true, bool put_above_originals = false); void flatten(bool is_destructive = true, bool put_above_originals = false); void splitNonIntersecting(); diff --git a/src/ui/toolbar/builder-toolbar.cpp b/src/ui/toolbar/builder-toolbar.cpp index ef54dbae82..9d88b5d29a 100644 --- a/src/ui/toolbar/builder-toolbar.cpp +++ b/src/ui/toolbar/builder-toolbar.cpp @@ -210,7 +210,7 @@ void BuilderToolbar::boolop_buttons_init_actions_add_buttons(const std::vectorgetSelection(); - selection->break_into_non_overlapping_pieces(); + selection->fracture(); } void BuilderToolbar::perform_flatten() -- GitLab From f9174b9b06187225c072f36b5cfc918af7fef70b Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 15:47:57 +0200 Subject: [PATCH 128/235] Renamed NonOverlappingPathsBuilder to NonIntersectingPathsBuilder. --- src/helper/CMakeLists.txt | 4 ++-- ...er.cpp => NonIntersectingPathsBuilder.cpp} | 24 +++++++++---------- ...uilder.h => NonIntersectingPathsBuilder.h} | 4 ++-- src/object/object-set.cpp | 15 ++++++------ 4 files changed, 23 insertions(+), 24 deletions(-) rename src/helper/{NonOverlappingPathsBuilder.cpp => NonIntersectingPathsBuilder.cpp} (90%) rename src/helper/{NonOverlappingPathsBuilder.h => NonIntersectingPathsBuilder.h} (95%) diff --git a/src/helper/CMakeLists.txt b/src/helper/CMakeLists.txt index de29ba6d60..a31bd4d63d 100644 --- a/src/helper/CMakeLists.txt +++ b/src/helper/CMakeLists.txt @@ -18,7 +18,7 @@ set(helper_SRC geom-pathvectorsatellites.cpp geom-satellite.cpp gettext.cpp - NonOverlappingPathsBuilder.cpp + NonIntersectingPathsBuilder.cpp pixbuf-ops.cpp png-write.cpp stock-items.cpp @@ -42,7 +42,7 @@ set(helper_SRC geom.h gettext.h mathfns.h - NonOverlappingPathsBuilder.h + NonIntersectingPathsBuilder.h pixbuf-ops.h png-write.h stock-items.h diff --git a/src/helper/NonOverlappingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp similarity index 90% rename from src/helper/NonOverlappingPathsBuilder.cpp rename to src/helper/NonIntersectingPathsBuilder.cpp index 940964a553..b204bd3598 100644 --- a/src/helper/NonOverlappingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -11,7 +11,7 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ -#include "NonOverlappingPathsBuilder.h" +#include "NonIntersectingPathsBuilder.h" #include #include @@ -26,7 +26,7 @@ namespace Inkscape { -using SubItem = NonOverlappingPathsBuilder::SubItem; +using SubItem = NonIntersectingPathsBuilder::SubItem; // TODO this is a duplicate code from selection-chemistry.cpp. refactor it. static void sp_selection_delete_impl(std::vector const &items, bool propagate = true, bool propagate_descendants = true) @@ -40,7 +40,7 @@ static void sp_selection_delete_impl(std::vector const &items, bool pro } } -void NonOverlappingPathsBuilder::perform_operation(SubItemOperation operation) +void NonIntersectingPathsBuilder::perform_operation(SubItemOperation operation) { if (set->isEmpty()) { return; @@ -77,7 +77,7 @@ void NonOverlappingPathsBuilder::perform_operation(SubItemOperation operation) unset_desktop_busy(desktop()); } -void NonOverlappingPathsBuilder::fracture() +void NonIntersectingPathsBuilder::fracture() { auto operation = [](SubItem & a, SubItem & b) { return a.fracture(b); }; perform_operation(operation); @@ -87,7 +87,7 @@ void NonOverlappingPathsBuilder::fracture() } } -void NonOverlappingPathsBuilder::flatten() +void NonIntersectingPathsBuilder::flatten() { auto operation = [](SubItem & a, SubItem & b) { return a.flatten(b); }; perform_operation(operation); @@ -97,7 +97,7 @@ void NonOverlappingPathsBuilder::flatten() } } -SPItem* NonOverlappingPathsBuilder::get_most_item(std::function cmp) +SPItem*NonIntersectingPathsBuilder::get_most_item(std::function cmp) { SPItem *result = items.front(); for (auto item : items) { @@ -108,7 +108,7 @@ SPItem* NonOverlappingPathsBuilder::get_most_item(std::functionitems(); items = std::vector(_items.begin(), _items.end()); @@ -134,7 +134,7 @@ void NonOverlappingPathsBuilder::set_parameters() items = get_groups_expanded(items); } -SPDesktop *NonOverlappingPathsBuilder::desktop() +SPDesktop *NonIntersectingPathsBuilder::desktop() { return set->desktop(); } @@ -195,7 +195,7 @@ std::vector split_non_intersecting_paths(std::vector &subitems return result; } -std::vector NonOverlappingPathsBuilder::get_operation_result(SubItemOperation operation) +std::vector NonIntersectingPathsBuilder::get_operation_result(SubItemOperation operation) { std::vector result; int n = items.size(); @@ -246,7 +246,7 @@ std::vector NonOverlappingPathsBuilder::get_operation_result(SubItemOpe return split_non_intersecting_paths(result); } -void NonOverlappingPathsBuilder::draw_subitems(const std::vector &subitems) +void NonIntersectingPathsBuilder::draw_subitems(const std::vector &subitems) { int n = subitems.size(); result_nodes.resize(n); @@ -255,7 +255,7 @@ void NonOverlappingPathsBuilder::draw_subitems(const std::vector &subit } } -void NonOverlappingPathsBuilder::remove_empty_subitems(std::vector &subitems) +void NonIntersectingPathsBuilder::remove_empty_subitems(std::vector &subitems) { for (int i = 0; i < subitems.size(); i++) { if (subitems[i].paths.empty()) { @@ -265,7 +265,7 @@ void NonOverlappingPathsBuilder::remove_empty_subitems(std::vector &sub } } -void NonOverlappingPathsBuilder::add_current_operation_to_subitems(std::vector &subitems) +void NonIntersectingPathsBuilder::add_current_operation_to_subitems(std::vector &subitems) { for (auto &subitem : subitems) { subitem.operations.insert(operations_counter); diff --git a/src/helper/NonOverlappingPathsBuilder.h b/src/helper/NonIntersectingPathsBuilder.h similarity index 95% rename from src/helper/NonOverlappingPathsBuilder.h rename to src/helper/NonIntersectingPathsBuilder.h index 1fc9334334..672ed25192 100644 --- a/src/helper/NonOverlappingPathsBuilder.h +++ b/src/helper/NonIntersectingPathsBuilder.h @@ -19,7 +19,7 @@ namespace Inkscape { -class NonOverlappingPathsBuilder +class NonIntersectingPathsBuilder { // TODO you might simplify this class so that it only constructs // paths, and move the rest of the logic somewhere else. @@ -45,7 +45,7 @@ private: public: class SubItem; - NonOverlappingPathsBuilder(ObjectSet *set) : set(set), operations_counter(0) {} + NonIntersectingPathsBuilder(ObjectSet *set) : set(set), operations_counter(0) {} void fracture(); void flatten(); diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 66b532bcfb..093a2867da 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -19,19 +19,18 @@ #include #include -#include "helper/geom-pathstroke.h" -#include "helper/useful-functions.h" -#include "helper/NonOverlappingPathsBuilder.h" -#include "document.h" -#include "xml/repr.h" - #include "box3d.h" #include "display/curve.h" +#include "document.h" +#include "helper/NonIntersectingPathsBuilder.h" +#include "helper/geom-pathstroke.h" +#include "helper/useful-functions.h" #include "object/sp-path.h" #include "object/sp-shape.h" #include "persp3d.h" #include "preferences.h" #include "ui/widget/canvas.h" +#include "xml/repr.h" namespace Inkscape { @@ -471,7 +470,7 @@ void ObjectSet::remove_paths_with_small_area() void ObjectSet::fracture(bool fill_holes, bool is_destructive, bool put_above_originals) { - NonOverlappingPathsBuilder builder(this); + NonIntersectingPathsBuilder builder(this); builder.fill_holes = fill_holes; builder.is_destructive = is_destructive; builder.put_above_originals = put_above_originals; @@ -480,7 +479,7 @@ void ObjectSet::fracture(bool fill_holes, bool is_destructive, bool put_above_or void ObjectSet::flatten(bool is_destructive, bool put_above_originals) { - NonOverlappingPathsBuilder builder(this); + NonIntersectingPathsBuilder builder(this); builder.is_destructive = is_destructive; builder.put_above_originals = put_above_originals; builder.flatten(); -- GitLab From 4d90b7dc21f36176df3276c5e81d03f1db6fa355 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 16:47:14 +0200 Subject: [PATCH 129/235] Removed then put_above_originals since it's an overcomplication. --- src/helper/NonIntersectingPathsBuilder.cpp | 35 +++------------------- src/helper/NonIntersectingPathsBuilder.h | 1 - src/object/object-set.cpp | 6 ++-- src/object/object-set.h | 5 ++-- 4 files changed, 8 insertions(+), 39 deletions(-) diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp index b204bd3598..f44d476eae 100644 --- a/src/helper/NonIntersectingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -97,41 +97,14 @@ void NonIntersectingPathsBuilder::flatten() } } -SPItem*NonIntersectingPathsBuilder::get_most_item(std::function cmp) -{ - SPItem *result = items.front(); - for (auto item : items) { - if (cmp(item, result)) { - result = item; - } - } - return result; -} - void NonIntersectingPathsBuilder::set_parameters() { auto _items = set->items(); items = std::vector(_items.begin(), _items.end()); - - // FIXME this doesn't work properly. fix it later. - if (put_above_originals) { - auto cmp = [](SPItem *a, SPItem *b) { return sp_item_repr_compare_position_bool(a, b); }; - after = get_most_item(cmp)->getRepr(); - parent = after->parent(); - } else { - auto cmp = [](SPItem *a, SPItem *b) { return !sp_item_repr_compare_position_bool(a, b); }; - after = get_most_item(cmp)->getRepr(); - // prev might be nullptr. this is why the parent is assigned before getting the prev. - parent = after->parent(); - after = after->prev(); - } - - // expanding after selecting the parent to - // avoid getting and using a parent of an item - // that was in a group before (which shows nothing - // on the canvas). - // TODO is this the best way to do it? - items = get_groups_expanded(items); + parent = items.front()->parent->getRepr(); + // items will be placed in place + // of the first item in the selection. + after = items.front()->getRepr(); } SPDesktop *NonIntersectingPathsBuilder::desktop() diff --git a/src/helper/NonIntersectingPathsBuilder.h b/src/helper/NonIntersectingPathsBuilder.h index 672ed25192..e8651c4090 100644 --- a/src/helper/NonIntersectingPathsBuilder.h +++ b/src/helper/NonIntersectingPathsBuilder.h @@ -28,7 +28,6 @@ public: bool fill_holes = false; // should this be implemented here? bool is_destructive = true; - bool put_above_originals = true; // won't matter if is_destructive is true. bool add_result_to_set = true; private: diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 093a2867da..a0f1face70 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -468,20 +468,18 @@ void ObjectSet::remove_paths_with_small_area() } } -void ObjectSet::fracture(bool fill_holes, bool is_destructive, bool put_above_originals) +void ObjectSet::fracture(bool fill_holes, bool is_destructive) { NonIntersectingPathsBuilder builder(this); builder.fill_holes = fill_holes; builder.is_destructive = is_destructive; - builder.put_above_originals = put_above_originals; builder.fracture(); } -void ObjectSet::flatten(bool is_destructive, bool put_above_originals) +void ObjectSet::flatten(bool is_destructive) { NonIntersectingPathsBuilder builder(this); builder.is_destructive = is_destructive; - builder.put_above_originals = put_above_originals; builder.flatten(); } diff --git a/src/object/object-set.h b/src/object/object-set.h index 676b921606..c2165ca298 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -470,9 +470,8 @@ public: void remove_paths_with_small_area(); void the_temporary_fix_for_the_transform_bug(); - void fracture(bool fill_holes = false, bool is_destructive = true, - bool put_above_originals = false); - void flatten(bool is_destructive = true, bool put_above_originals = false); + void fracture(bool fill_holes = false, bool is_destructive = true); + void flatten(bool is_destructive = true); void splitNonIntersecting(); protected: -- GitLab From 4507fd858d2c4b10b42664063e954b91042ad407 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 16:53:56 +0200 Subject: [PATCH 130/235] Got rid of is_destructive and add_result_to_set since they're not very useful. --- src/helper/NonIntersectingPathsBuilder.cpp | 10 +++------- src/helper/NonIntersectingPathsBuilder.h | 2 -- src/object/object-set.cpp | 6 ++---- src/object/object-set.h | 4 ++-- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp index f44d476eae..6a5c6f65f9 100644 --- a/src/helper/NonIntersectingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -64,14 +64,10 @@ void NonIntersectingPathsBuilder::perform_operation(SubItemOperation operation) auto result = get_operation_result(operation); draw_subitems(result); - if (is_destructive) { - sp_selection_delete_impl(items); - } + sp_selection_delete_impl(items); - if (add_result_to_set) { - for (auto node : result_nodes) { - set->add(node); - } + for (auto node : result_nodes) { + set->add(node); } unset_desktop_busy(desktop()); diff --git a/src/helper/NonIntersectingPathsBuilder.h b/src/helper/NonIntersectingPathsBuilder.h index e8651c4090..10704ac25c 100644 --- a/src/helper/NonIntersectingPathsBuilder.h +++ b/src/helper/NonIntersectingPathsBuilder.h @@ -27,8 +27,6 @@ class NonIntersectingPathsBuilder public: bool fill_holes = false; // should this be implemented here? - bool is_destructive = true; - bool add_result_to_set = true; private: diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index a0f1face70..9e8de90f56 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -468,18 +468,16 @@ void ObjectSet::remove_paths_with_small_area() } } -void ObjectSet::fracture(bool fill_holes, bool is_destructive) +void ObjectSet::fracture(bool fill_holes) { NonIntersectingPathsBuilder builder(this); builder.fill_holes = fill_holes; - builder.is_destructive = is_destructive; builder.fracture(); } -void ObjectSet::flatten(bool is_destructive) +void ObjectSet::flatten() { NonIntersectingPathsBuilder builder(this); - builder.is_destructive = is_destructive; builder.flatten(); } diff --git a/src/object/object-set.h b/src/object/object-set.h index c2165ca298..82c66739a2 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -470,8 +470,8 @@ public: void remove_paths_with_small_area(); void the_temporary_fix_for_the_transform_bug(); - void fracture(bool fill_holes = false, bool is_destructive = true); - void flatten(bool is_destructive = true); + void fracture(bool fill_holes = false); + void flatten(); void splitNonIntersecting(); protected: -- GitLab From deeccdcacd9fd744bc1caa327d5d63880eb6033a Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 17:55:54 +0200 Subject: [PATCH 131/235] Removed the fill_holes option in the NonIntersectingPathsBuilder since it's not implemented at the moment. --- src/helper/NonIntersectingPathsBuilder.h | 4 ---- src/object/object-set.cpp | 3 +-- src/object/object-set.h | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/helper/NonIntersectingPathsBuilder.h b/src/helper/NonIntersectingPathsBuilder.h index 10704ac25c..b79f7603e0 100644 --- a/src/helper/NonIntersectingPathsBuilder.h +++ b/src/helper/NonIntersectingPathsBuilder.h @@ -24,10 +24,6 @@ class NonIntersectingPathsBuilder // TODO you might simplify this class so that it only constructs // paths, and move the rest of the logic somewhere else. -public: - - bool fill_holes = false; // should this be implemented here? - private: int operations_counter; diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 9e8de90f56..6639e30a6f 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -468,10 +468,9 @@ void ObjectSet::remove_paths_with_small_area() } } -void ObjectSet::fracture(bool fill_holes) +void ObjectSet::fracture() { NonIntersectingPathsBuilder builder(this); - builder.fill_holes = fill_holes; builder.fracture(); } diff --git a/src/object/object-set.h b/src/object/object-set.h index 82c66739a2..bc1eda0034 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -470,7 +470,7 @@ public: void remove_paths_with_small_area(); void the_temporary_fix_for_the_transform_bug(); - void fracture(bool fill_holes = false); + void fracture(); void flatten(); void splitNonIntersecting(); -- GitLab From cce3d1f6ddbab6116a3f920b7a67390bc21a59c3 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 23 Jul 2021 22:11:03 +0200 Subject: [PATCH 132/235] Removed unnecessary lines in the draw_on_canvas function. --- src/helper/useful-functions.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/helper/useful-functions.cpp b/src/helper/useful-functions.cpp index c82dc2e4d1..818a8b63f1 100644 --- a/src/helper/useful-functions.cpp +++ b/src/helper/useful-functions.cpp @@ -293,14 +293,7 @@ XML::Node *draw_on_canvas(const Geom::PathVector &path, const SPItem *to_copy_fr repr->setAttribute("style", style); } - auto str = sp_svg_write_path(path); - repr->setAttribute("d", str); - - if (after) { - parent->addChild(repr, after); - } else { - parent->addChildAtPos(repr, 0); - } + parent->addChild(repr, after); return repr; } -- GitLab From 2bcd5efcc2aefd2f295007575fab997bd7b74d1e Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 24 Jul 2021 00:33:58 +0200 Subject: [PATCH 133/235] Moved the DisjointSet class to a separate file. --- src/helper/CMakeLists.txt | 6 ++-- src/helper/disjoint-sets.cpp | 50 +++++++++++++++++++++++++++++++ src/helper/disjoint-sets.h | 19 ++++++++++++ src/helper/useful-functions.cpp | 53 +-------------------------------- src/helper/useful-functions.h | 16 ---------- 5 files changed, 74 insertions(+), 70 deletions(-) create mode 100644 src/helper/disjoint-sets.cpp create mode 100644 src/helper/disjoint-sets.h diff --git a/src/helper/CMakeLists.txt b/src/helper/CMakeLists.txt index a31bd4d63d..ebdc449092 100644 --- a/src/helper/CMakeLists.txt +++ b/src/helper/CMakeLists.txt @@ -18,7 +18,8 @@ set(helper_SRC geom-pathvectorsatellites.cpp geom-satellite.cpp gettext.cpp - NonIntersectingPathsBuilder.cpp + disjoint-sets.cpp + NonIntersectingPathsBuilder.cpp pixbuf-ops.cpp png-write.cpp stock-items.cpp @@ -42,7 +43,8 @@ set(helper_SRC geom.h gettext.h mathfns.h - NonIntersectingPathsBuilder.h + disjoint-sets.h + NonIntersectingPathsBuilder.h pixbuf-ops.h png-write.h stock-items.h diff --git a/src/helper/disjoint-sets.cpp b/src/helper/disjoint-sets.cpp new file mode 100644 index 0000000000..1b0e478733 --- /dev/null +++ b/src/helper/disjoint-sets.cpp @@ -0,0 +1,50 @@ +#include "disjoint-sets.h" + +void DisjointSet::merge(int a, int b) +{ + int a_parent = parent_of(a); + int b_parent = parent_of(b); + + if (a_parent == b_parent) { return; } + + int a_size = size_of(a); + int b_size = size_of(b); + + if (a_size < b_size) { + parents[a] = b; + } else { + parents[b] = a; + } +} +int DisjointSet::parent_of(int x) +{ + if (parents[x] < 0) { + return x; + } + + int parent = parents[x]; + parents[x] = parent_of(parent); + return parents[x]; +} +int DisjointSet::size_of(int x) +{ + int parent = parent_of(x); + return -parents[parent]; +} +int DisjointSet::size() +{ + int n = parents.size(); + std::vector is_present(n, false); + + int result = 0; + + for (int i = 0; i < n; i++) { + int parent = parent_of(i); + if (!is_present[parent]) { + is_present[parent] = true; + result++; + } + } + + return result; +} \ No newline at end of file diff --git a/src/helper/disjoint-sets.h b/src/helper/disjoint-sets.h new file mode 100644 index 0000000000..dd424ae4fa --- /dev/null +++ b/src/helper/disjoint-sets.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +class DisjointSet +{ + // if parents[x] is negative, then x is a parent of itself, + // and the negative number represents the size of the set. + // else if parents[x] is positive, then x has a parent, parents[x] + // might not be the top parent, thus shouldn't access parents[x] + // directly, rather, use the method "parent_of". + std::vector parents; +public: + DisjointSet(int n) { parents.resize(n, -1); } + void merge(int a, int b); + int parent_of(int x); + int size_of(int x); + int size(); +}; \ No newline at end of file diff --git a/src/helper/useful-functions.cpp b/src/helper/useful-functions.cpp index 818a8b63f1..1200c1c9b3 100644 --- a/src/helper/useful-functions.cpp +++ b/src/helper/useful-functions.cpp @@ -21,64 +21,13 @@ #include #include +#include "disjoint-sets.h" #include "geom-pathstroke.h" #include "ui/widget/canvas.h" namespace Inkscape { -void DisjointSet::merge(int a, int b) -{ - int a_parent = parent_of(a); - int b_parent = parent_of(b); - - if (a_parent == b_parent) { return; } - - int a_size = size_of(a); - int b_size = size_of(b); - - if (a_size < b_size) { - parents[a] = b; - } else { - parents[b] = a; - } -} - -int DisjointSet::parent_of(int x) -{ - if (parents[x] < 0) { - return x; - } - - int parent = parents[x]; - parents[x] = parent_of(parent); - return parents[x]; -} - -int DisjointSet::size_of(int x) -{ - int parent = parent_of(x); - return -parents[parent]; -} - -int DisjointSet::size() -{ - int n = parents.size(); - std::vector is_present(n, false); - - int result = 0; - - for (int i = 0; i < n; i++) { - int parent = parent_of(i); - if (!is_present[parent]) { - is_present[parent] = true; - result++; - } - } - - return result; -} - void set_desktop_busy(SPDesktop *desktop) { if (desktop) { diff --git a/src/helper/useful-functions.h b/src/helper/useful-functions.h index f180f5dc8b..c4dc1f687c 100644 --- a/src/helper/useful-functions.h +++ b/src/helper/useful-functions.h @@ -26,22 +26,6 @@ namespace Inkscape { -class DisjointSet -{ - // if parents[x] is negative, then x is a parent of itself, - // and the negative number represents the size of the set. - // else if parents[x] is positive, then x has a parent, parents[x] - // might not be the top parent, thus shouldn't access parents[x] - // directly, rather, use the method "parent_of". - std::vector parents; -public: - DisjointSet(int n) { parents.resize(n, -1); } - void merge(int a, int b); - int parent_of(int x); - int size_of(int x); - int size(); -}; - void set_desktop_busy(SPDesktop *desktop); void unset_desktop_busy(SPDesktop *desktop); -- GitLab From 92797fe42b2751969343be0228b7c165a1945693 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 24 Jul 2021 00:41:41 +0200 Subject: [PATCH 134/235] Re-wrote the DisjointSet::merge method. --- src/helper/disjoint-sets.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/helper/disjoint-sets.cpp b/src/helper/disjoint-sets.cpp index 1b0e478733..42820916d3 100644 --- a/src/helper/disjoint-sets.cpp +++ b/src/helper/disjoint-sets.cpp @@ -2,19 +2,20 @@ void DisjointSet::merge(int a, int b) { - int a_parent = parent_of(a); - int b_parent = parent_of(b); + int parent = parent_of(a); + int child = parent_of(b); - if (a_parent == b_parent) { return; } + if (child == parent) { return; } - int a_size = size_of(a); - int b_size = size_of(b); + int parent_size = size_of(parent); + int child_size = size_of(child); - if (a_size < b_size) { - parents[a] = b; - } else { - parents[b] = a; + if (parent_size < child_size) { + std::swap(parent, child); } + + parents[child] = parent; + parents[parent] = -(parent_size + child_size); } int DisjointSet::parent_of(int x) { -- GitLab From 213f6bd26a493f0571189597de0cbf22f60fa3c8 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 24 Jul 2021 01:03:05 +0200 Subject: [PATCH 135/235] Renamed the DisjointSet class and some of its methods. --- src/helper/disjoint-sets.cpp | 12 ++++++------ src/helper/disjoint-sets.h | 8 ++++---- src/helper/useful-functions.cpp | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/helper/disjoint-sets.cpp b/src/helper/disjoint-sets.cpp index 42820916d3..25ab24158d 100644 --- a/src/helper/disjoint-sets.cpp +++ b/src/helper/disjoint-sets.cpp @@ -1,14 +1,14 @@ #include "disjoint-sets.h" -void DisjointSet::merge(int a, int b) +void DisjointSets::merge(int a, int b) { int parent = parent_of(a); int child = parent_of(b); if (child == parent) { return; } - int parent_size = size_of(parent); - int child_size = size_of(child); + int parent_size = size_of_set(parent); + int child_size = size_of_set(child); if (parent_size < child_size) { std::swap(parent, child); @@ -17,7 +17,7 @@ void DisjointSet::merge(int a, int b) parents[child] = parent; parents[parent] = -(parent_size + child_size); } -int DisjointSet::parent_of(int x) +int DisjointSets::parent_of(int x) { if (parents[x] < 0) { return x; @@ -27,12 +27,12 @@ int DisjointSet::parent_of(int x) parents[x] = parent_of(parent); return parents[x]; } -int DisjointSet::size_of(int x) +int DisjointSets::size_of_set(int x) { int parent = parent_of(x); return -parents[parent]; } -int DisjointSet::size() +int DisjointSets::sets_count() { int n = parents.size(); std::vector is_present(n, false); diff --git a/src/helper/disjoint-sets.h b/src/helper/disjoint-sets.h index dd424ae4fa..3d93cfe7e2 100644 --- a/src/helper/disjoint-sets.h +++ b/src/helper/disjoint-sets.h @@ -2,7 +2,7 @@ #include -class DisjointSet +class DisjointSets { // if parents[x] is negative, then x is a parent of itself, // and the negative number represents the size of the set. @@ -11,9 +11,9 @@ class DisjointSet // directly, rather, use the method "parent_of". std::vector parents; public: - DisjointSet(int n) { parents.resize(n, -1); } + DisjointSets(int n) { parents.resize(n, -1); } void merge(int a, int b); int parent_of(int x); - int size_of(int x); - int size(); + int size_of_set(int x); + int sets_count(); }; \ No newline at end of file diff --git a/src/helper/useful-functions.cpp b/src/helper/useful-functions.cpp index 1200c1c9b3..152993cf65 100644 --- a/src/helper/useful-functions.cpp +++ b/src/helper/useful-functions.cpp @@ -165,7 +165,7 @@ std::vector split_non_intersecting_paths(const Geom::PathVecto { int n = paths.size(); - DisjointSet set(n); + DisjointSets sets(n); std::vector visited(n); for (int i = n - 1; i >= 0; i--) { @@ -176,20 +176,20 @@ std::vector split_non_intersecting_paths(const Geom::PathVecto for (int j = 0; j < n; j++) { if (visited[j]) { continue; } if (is_intersecting(paths[i], paths[j])) { - set.merge(i, j); + sets.merge(i, j); } } } - int set_size = set.size(); // this is O(N). + int sets_count = sets.sets_count(); // this is O(N). std::map> map; for (int i = 0; i < n; i++) { - int parent = set.parent_of(i); + int parent = sets.parent_of(i); map[parent].push_back(i); } int i = 0; - std::vector result(set_size); + std::vector result(sets_count); for (auto &paths_idx : map) { for (auto path_idx : paths_idx.second) { auto &path = paths[path_idx]; -- GitLab From e2d427146c0ef02334f2d470aecc96efc67a21bf Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 24 Jul 2021 02:12:16 +0200 Subject: [PATCH 136/235] Removed some functions that are not being used. --- src/helper/useful-functions.cpp | 126 -------------------------------- src/helper/useful-functions.h | 21 +----- src/object/object-set.cpp | 11 --- 3 files changed, 1 insertion(+), 157 deletions(-) diff --git a/src/helper/useful-functions.cpp b/src/helper/useful-functions.cpp index 152993cf65..00d94230ec 100644 --- a/src/helper/useful-functions.cpp +++ b/src/helper/useful-functions.cpp @@ -13,13 +13,8 @@ #include "useful-functions.h" -#include <2geom/intersection-graph.h> -#include -#include #include -#include #include -#include #include "disjoint-sets.h" #include "geom-pathstroke.h" @@ -56,96 +51,6 @@ std::vector get_groups_expanded(const std::vector &items) return result; } -std::vector get_path_nodes(const Geom::Path &path, double curve_length_divisor) -{ - std::vector result; - for (auto &curve : path) { - if (curve.isLineSegment()) { - result.push_back(curve.initialPoint()); - continue; - } - int nodes_to_get = std::ceil(curve.length() / curve_length_divisor); - double step = 1.0 / nodes_to_get; - for (double coord = 0; coord < 1; coord += step) { - result.push_back(curve.pointAt(coord)); - } - } - return result; -} - -double path_area_non_self_intersecting(const std::vector& nodes) -{ - int j = nodes.size() - 1; - - double area = 0; - for (int i = 1; i < nodes.size(); i++) { - auto x1 = nodes[i].x(); - auto x2 = nodes[j].x(); - auto y1 = nodes[i].y(); - auto y2 = nodes[j].y(); - area += (x1 + x2) * (y1 - y2); - j = i; - } - area /= 2; - - return std::abs(area); -} - -double path_area_non_self_intersecting(const Geom::Path &path, double curve_length_divisor) -{ - return path_area_non_self_intersecting(get_path_nodes(path, curve_length_divisor)); -} - -double path_area_non_self_intersecting_no_curves(const Geom::Path &path) -{ - // This function doesn't take curves into consideration. - return path_area_non_self_intersecting(path.nodes()); -} - -void print_bounds_and_nodes_count(const Geom::PathVector &path) { - auto bounds = path.boundsFast(); - std::cout << "Nodes count: " << path.nodes().size() << ", "; - std::cout << "Bounds: "; - std::cout << "Top: " << bounds->top() << ", "; - std::cout << "Bottom: " << bounds->bottom() << ", "; - std::cout << "Left: " << bounds->left() << ", "; - std::cout << "Right: " << bounds->right() << "\n"; -} - -Geom::PathVector remove_paths_with_small_area_from_pathvec(const Geom::PathVector &paths, double minimum_area, double curve_length_divisor) -{ - // FIXME may want to edit this PathVector instead of creating a new one? - // FIXME take curves into consideration. Guess this will only work with - // the current way of breaking selection into non-overlapping pieces - // since it produces a paths with no curves. Other than that, this - // function shouldn't be used until fixed. - - Geom::PathVector result; - for (auto &path : paths) { - auto area = path_area_non_self_intersecting(path, curve_length_divisor); - if (area >= minimum_area) { - result.push_back(path); - } else { -// std::cout << "Deleted a path with an area of " << area << ".\n"; - // print_bounds_and_nodes_count(path); - } - } - - return result; -} - -Geom::PathVector remove_lines_from_pathvec(const Geom::PathVector &paths) -{ - Geom::PathVector result; - for (auto &path : paths) { - if (path.nodes().size() > 2) { - result.push_back(path); - } - } - - return result; -} - template bool is_intersecting(const Path1 &a, const Path2 &b) { for (auto &node : b.nodes()) { @@ -201,37 +106,6 @@ std::vector split_non_intersecting_paths(const Geom::PathVecto return result; } -std::string point_coords(const Geom::Point& point, int precision) -{ - std::ostringstream oss; - oss << std::fixed << std::setprecision(precision) << "(" << point.x() << ", " << point.y() << ")"; - return oss.str(); -} - -// FIXME this is not declared in the header file to avoid circular -// dependency. may remove it since it's for debugging anyways. -void print_coords_sorted_from_set(ObjectSet *set) -{ - auto cmp = [](const Geom::Point& a, const Geom::Point &b) { - return a.x() > b.x() || (a.x() == b.x() && a.y() > b.y()); - }; - - auto items = set->items(); - - int item_num = 1; - for (auto item : items) { - std::cout << "Item " << item_num++ << ":\n"; - auto paths = item->get_pathvector(); - auto nodes = paths.nodes(); - std::sort(nodes.begin(), nodes.end(), cmp); - for (auto node : nodes) { - std::cout << point_coords(node, 1) << ' '; - } - std::cout << "\n\n"; - } - -} - XML::Node *draw_on_canvas(const Geom::PathVector &path, const SPItem *to_copy_from, XML::Node *parent, XML::Node *after) { Inkscape::XML::Node *repr = parent->document()->createElement("svg:path"); diff --git a/src/helper/useful-functions.h b/src/helper/useful-functions.h index c4dc1f687c..f4dd9d2609 100644 --- a/src/helper/useful-functions.h +++ b/src/helper/useful-functions.h @@ -21,22 +21,13 @@ #include "object/sp-item.h" #include "xml/node.h" -#define DEFAULT_CURVE_LENGTH_DIVISOR 5 -#define DEFAULT_MINIMUM_AREA 0.01 - namespace Inkscape { void set_desktop_busy(SPDesktop *desktop); void unset_desktop_busy(SPDesktop *desktop); std::vector get_groups_expanded(const std::vector &items); -std::vector get_path_nodes(const Geom::Path &path, double curve_length_divisor = DEFAULT_CURVE_LENGTH_DIVISOR); -double path_area_non_self_intersecting_no_curves(const Geom::Path &path); -double path_area_non_self_intersecting(const std::vector &nodes); -double path_area_non_self_intersecting(const Geom::Path &path, double curve_length_divisor = DEFAULT_CURVE_LENGTH_DIVISOR); -Geom::PathVector remove_paths_with_small_area_from_pathvec(const Geom::PathVector &paths, - double minimum_area = DEFAULT_MINIMUM_AREA, double curve_length_divisor = DEFAULT_CURVE_LENGTH_DIVISOR); -Geom::PathVector remove_lines_from_pathvec(const Geom::PathVector &paths); + std::vector split_non_intersecting_paths(const Geom::PathVector &paths); template @@ -47,14 +38,4 @@ bool is_intersecting(const Path1 &a, const Path2 &b); // in both a header and a source file. this is why it's not being done for now. XML::Node *draw_on_canvas(const Geom::PathVector &path, const SPItem *to_copy_from, XML::Node *parent, XML::Node *after = nullptr); -void print_bounds_and_nodes_count(const Geom::PathVector &path); -std::string point_coords(const Geom::Point &point, int precision = 10); - -template -void remove_by_swapping_with_back(std::vector& vector, int index) -{ - std::swap(vector[index], vector.back()); - vector.pop_back(); -} - } \ No newline at end of file diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 6639e30a6f..76b1095560 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -457,17 +457,6 @@ void ObjectSet::the_temporary_fix_for_the_transform_bug() move(0, 0, true); } -void ObjectSet::remove_paths_with_small_area() -{ - for (auto item : items()) { - auto paths = item->get_pathvector(); - // FIXME figure out whether to make the minimum area dependent on the selection or fixed. - double minimum_area = 1; - auto filtered_paths = remove_paths_with_small_area_from_pathvec(paths, minimum_area); - item->setAttribute("d", sp_svg_write_path(filtered_paths)); - } -} - void ObjectSet::fracture() { NonIntersectingPathsBuilder builder(this); -- GitLab From d2b718a7470c5cfb902a98d1e25b607989d2b8ba Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 24 Jul 2021 02:14:01 +0200 Subject: [PATCH 137/235] Added the license comment in disjoint-sets.* --- src/helper/disjoint-sets.cpp | 16 ++++++++++++++++ src/helper/disjoint-sets.h | 13 +++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/helper/disjoint-sets.cpp b/src/helper/disjoint-sets.cpp index 25ab24158d..7145361680 100644 --- a/src/helper/disjoint-sets.cpp +++ b/src/helper/disjoint-sets.cpp @@ -1,3 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * A class that represents the Disjoint Sets data structure. + * + * + *//* + * Authors: + * Osama Ahmad + * + * Copyright (C) 2021 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + #include "disjoint-sets.h" void DisjointSets::merge(int a, int b) @@ -17,6 +30,7 @@ void DisjointSets::merge(int a, int b) parents[child] = parent; parents[parent] = -(parent_size + child_size); } + int DisjointSets::parent_of(int x) { if (parents[x] < 0) { @@ -27,11 +41,13 @@ int DisjointSets::parent_of(int x) parents[x] = parent_of(parent); return parents[x]; } + int DisjointSets::size_of_set(int x) { int parent = parent_of(x); return -parents[parent]; } + int DisjointSets::sets_count() { int n = parents.size(); diff --git a/src/helper/disjoint-sets.h b/src/helper/disjoint-sets.h index 3d93cfe7e2..28a3111e4e 100644 --- a/src/helper/disjoint-sets.h +++ b/src/helper/disjoint-sets.h @@ -1,3 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * A class that represents the Disjoint Sets data structure. + * + * + *//* + * Authors: + * Osama Ahmad + * + * Copyright (C) 2021 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + #pragma once #include -- GitLab From 001dcdc3dc0911292b0ed9d93781dc4ce0de50af Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 24 Jul 2021 05:23:23 +0200 Subject: [PATCH 138/235] Added an option in the Rubberband class to set the color. --- src/rubberband.cpp | 27 ++++++++++++++++++++++++--- src/rubberband.h | 5 +++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/rubberband.cpp b/src/rubberband.cpp index f31aecc98a..e0a88964f6 100644 --- a/src/rubberband.cpp +++ b/src/rubberband.cpp @@ -73,6 +73,8 @@ void Inkscape::Rubberband::stop() if (_desktop && _desktop->getCanvas()) { _desktop->getCanvas()->forced_redraws_stop(); } + + resetColor(); } void Inkscape::Rubberband::move(Geom::Point const &p) @@ -101,11 +103,13 @@ void Inkscape::Rubberband::move(Geom::Point const &p) if (_touchpath) _touchpath->hide(); if (_rect) _rect->hide(); + guint32 color; switch (_mode) { case RUBBERBAND_MODE_RECT: + color = _color.has_value() ? _color.value() : 0x808080ff; if (_rect == nullptr) { _rect = new Inkscape::CanvasItemRect(_desktop->getCanvasControls()); - _rect->set_stroke(0x808080ff); + _rect->set_stroke(color); _rect->set_shadow(0xffffffff, 0); // Not a shadow _rect->set_dashed(false); _rect->set_inverted(true); @@ -114,9 +118,10 @@ void Inkscape::Rubberband::move(Geom::Point const &p) _rect->show(); break; case RUBBERBAND_MODE_TOUCHRECT: + color = _color.has_value() ? _color.value() : 0xff0000ff; if (_rect == nullptr) { _rect = new Inkscape::CanvasItemRect(_desktop->getCanvasControls()); - _rect->set_stroke(0xff0000ff); + _rect->set_stroke(color); _rect->set_shadow(0xffffffff, 0); // Not a shadow _rect->set_dashed(false); _rect->set_inverted(false); @@ -125,9 +130,10 @@ void Inkscape::Rubberband::move(Geom::Point const &p) _rect->show(); break; case RUBBERBAND_MODE_TOUCHPATH: + color = _color.has_value() ? _color.value() : 0xff0000ff; if (_touchpath == nullptr) { _touchpath = new Inkscape::CanvasItemBpath(_desktop->getCanvasControls()); // Should be sketch? - _touchpath->set_stroke(0xff0000ff); + _touchpath->set_stroke(color); _touchpath->set_fill(0x0, SP_WIND_RULE_NONZERO); } _touchpath->set_bpath(_touchpath_curve); @@ -155,6 +161,21 @@ void Inkscape::Rubberband::defaultMode() } } +void Inkscape::Rubberband::setColor(guint32 color) +{ + _color = color; + + if (_mode == RUBBERBAND_MODE_TOUCHPATH) { + if (_touchpath) { + _touchpath->set_stroke(color); + } + } else { + if (_rect) { + _rect->set_stroke(color); + } + } +} + /** * @return Rectangle in desktop coordinates */ diff --git a/src/rubberband.h b/src/rubberband.h index 0d93d7cd77..9d5ebabbbe 100644 --- a/src/rubberband.h +++ b/src/rubberband.h @@ -52,6 +52,9 @@ public: void setMode(int mode); void defaultMode(); + void setColor(guint32 color); + void resetColor() { _color.reset(); } + static Rubberband* get(SPDesktop *desktop); private: @@ -73,6 +76,8 @@ private: bool _started = false; int _mode = RUBBERBAND_MODE_RECT; + + std::optional _color; }; } -- GitLab From c20638e1268d40ed3506772265b154459728f072 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 24 Jul 2021 05:24:21 +0200 Subject: [PATCH 139/235] Added different (temporary) colors for each mode in the Builder tool. --- src/ui/tools/builder-tool.cpp | 16 ++++++++++++++++ src/ui/tools/builder-tool.h | 8 ++++++++ 2 files changed, 24 insertions(+) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 13d1d48ffc..c873942d17 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -222,6 +222,10 @@ bool BuilderTool::event_button_press_handler(GdkEvent *event) Geom::Point const button_pt(event->button.x, event->button.y); Geom::Point const p(desktop->w2d(button_pt)); + int current_mode = get_current_mode(); + guint32 current_color = mode_colors[current_mode]; + Inkscape::Rubberband::get(desktop)->setColor(current_color); + Inkscape::Rubberband::get(desktop)->setMode(RUBBERBAND_MODE_TOUCHPATH); Inkscape::Rubberband::get(desktop)->start(desktop, p); @@ -530,6 +534,7 @@ void BuilderTool::set_current_mode(int current_mode) active_mode = current_mode; set_cursor_mode(); + set_rubberband_color(); // TODO add a function here to change the // patter of the items the cursor went over. @@ -553,6 +558,17 @@ void BuilderTool::set_cursor_mode() ToolBase::sp_event_context_update_cursor(); } +void BuilderTool::set_rubberband_color() +{ + if (active_mode > mode_colors.size()) { + std::cerr << "BuilderTool: Mode " << active_mode << " is unknown.\n"; + return; + } + + auto instance = Rubberband::get(desktop); + instance->setColor(mode_colors[active_mode]); +} + bool BuilderTool::is_mode_add_to_selection(int mode, GdkEvent *event) { return mode == JUST_SELECT && Modifier::get(Modifiers::Type::SELECT_ADD_TO)->active(event->button.state); diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index e96a37a22b..a6bd32854c 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -83,6 +83,7 @@ private: int get_current_mode(); bool is_mode_add_to_selection(int mode, GdkEvent *event); void set_cursor_mode(); + void set_rubberband_color(); // TODO you might pre-load the cursors and store them // in this vector instead of loading them each time. @@ -93,6 +94,13 @@ private: "select.svg", }; + const std::vector mode_colors = { + 0x0000ffff, + 0x000000ff, + 0xff00ffff, + 0xff0000ff, + }; + const std::map handlers = { {GDK_BUTTON_PRESS, &BuilderTool::event_button_press_handler}, {GDK_BUTTON_RELEASE, &BuilderTool::event_button_release_handler}, -- GitLab From 50bae6c8ee9d14e18205121ec5dfd575fbfa20b8 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 24 Jul 2021 05:32:27 +0200 Subject: [PATCH 140/235] Made the const fields if the Builder tool static and moved them into the source file. --- src/ui/tools/builder-tool.cpp | 22 ++++++++++++++++++++++ src/ui/tools/builder-tool.h | 24 +++--------------------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index c873942d17..3b40f9ecb0 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -74,6 +74,28 @@ const std::string& BuilderTool::getPrefsPath() { const std::string BuilderTool::prefsPath = "/tools/builder"; +const std::vector BuilderTool::mode_cursor_filenames = { + "cursor-union.svg", + "cursor-delete.svg", + "cursor-intersect.svg", + "select.svg", +}; + +const std::vector BuilderTool::mode_colors = { + 0x0000ffff, + 0x000000ff, + 0xff00ffff, + 0xff0000ff, +}; + +const std::map BuilderTool::handlers = { + {GDK_BUTTON_PRESS, &BuilderTool::event_button_press_handler}, + {GDK_BUTTON_RELEASE, &BuilderTool::event_button_release_handler}, + {GDK_KEY_PRESS, &BuilderTool::event_key_press_handler}, + {GDK_KEY_RELEASE, &BuilderTool::event_key_release_handler}, + {GDK_MOTION_NOTIFY, &BuilderTool::event_motion_handler}, +}; + BuilderTool::BuilderTool() : ToolBase("select.svg") , dragging(false) diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index a6bd32854c..de1095238f 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -87,27 +87,9 @@ private: // TODO you might pre-load the cursors and store them // in this vector instead of loading them each time. - const std::vector mode_cursor_filenames = { - "cursor-union.svg", - "cursor-delete.svg", - "cursor-intersect.svg", - "select.svg", - }; - - const std::vector mode_colors = { - 0x0000ffff, - 0x000000ff, - 0xff00ffff, - 0xff0000ff, - }; - - const std::map handlers = { - {GDK_BUTTON_PRESS, &BuilderTool::event_button_press_handler}, - {GDK_BUTTON_RELEASE, &BuilderTool::event_button_release_handler}, - {GDK_KEY_PRESS, &BuilderTool::event_key_press_handler}, - {GDK_KEY_RELEASE, &BuilderTool::event_key_release_handler}, - {GDK_MOTION_NOTIFY, &BuilderTool::event_motion_handler}, - }; + static const std::vector mode_cursor_filenames; + static const std::vector mode_colors; + static const std::map handlers; int active_mode = JUST_SELECT; // default to the select mode since this is the default cursor. bool ctrl_on = false; -- GitLab From 4566f72c35c19d7fe4d441d1544bac814dcfea4f Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 24 Jul 2021 15:09:48 +0200 Subject: [PATCH 141/235] Used * instead of .value() to get the value of an std::optional (to avoid fails in the mac builder). --- src/rubberband.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rubberband.cpp b/src/rubberband.cpp index e0a88964f6..4818cb391b 100644 --- a/src/rubberband.cpp +++ b/src/rubberband.cpp @@ -106,7 +106,7 @@ void Inkscape::Rubberband::move(Geom::Point const &p) guint32 color; switch (_mode) { case RUBBERBAND_MODE_RECT: - color = _color.has_value() ? _color.value() : 0x808080ff; + color = _color.has_value() ? *_color : 0x808080ff; if (_rect == nullptr) { _rect = new Inkscape::CanvasItemRect(_desktop->getCanvasControls()); _rect->set_stroke(color); @@ -118,7 +118,7 @@ void Inkscape::Rubberband::move(Geom::Point const &p) _rect->show(); break; case RUBBERBAND_MODE_TOUCHRECT: - color = _color.has_value() ? _color.value() : 0xff0000ff; + color = _color.has_value() ? *_color : 0xff0000ff; if (_rect == nullptr) { _rect = new Inkscape::CanvasItemRect(_desktop->getCanvasControls()); _rect->set_stroke(color); @@ -130,7 +130,7 @@ void Inkscape::Rubberband::move(Geom::Point const &p) _rect->show(); break; case RUBBERBAND_MODE_TOUCHPATH: - color = _color.has_value() ? _color.value() : 0xff0000ff; + color = _color.has_value() ? *_color : 0xff0000ff; if (_touchpath == nullptr) { _touchpath = new Inkscape::CanvasItemBpath(_desktop->getCanvasControls()); // Should be sketch? _touchpath->set_stroke(color); -- GitLab From ed7fdc721b4a82aa448675cb40f79a33db9a063a Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 24 Jul 2021 15:52:46 +0200 Subject: [PATCH 142/235] Removed the get_most_item declaration since it had no definition. --- src/helper/NonIntersectingPathsBuilder.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/helper/NonIntersectingPathsBuilder.h b/src/helper/NonIntersectingPathsBuilder.h index b79f7603e0..0d60757892 100644 --- a/src/helper/NonIntersectingPathsBuilder.h +++ b/src/helper/NonIntersectingPathsBuilder.h @@ -51,7 +51,6 @@ private: void draw_subitems(const std::vector &subitems); SPDesktop *desktop(); void set_parameters(); - SPItem* get_most_item(std::function cmp); void remove_empty_subitems(std::vector &subitems); void add_current_operation_to_subitems(std::vector &subitems); -- GitLab From bbccdb2046ba8a881340935420f6409033a7a2ac Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 24 Jul 2021 23:55:39 +0200 Subject: [PATCH 143/235] Moved the flatten function out of NonIntersectingPathsBuilder. Flatten doesn't need all of the computations done there. Faster if separated. --- src/helper/NonIntersectingPathsBuilder.cpp | 25 --------- src/helper/NonIntersectingPathsBuilder.h | 2 - src/object/object-set.cpp | 60 +++++++++++++++++++++- 3 files changed, 58 insertions(+), 29 deletions(-) diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp index 6a5c6f65f9..04d96fa180 100644 --- a/src/helper/NonIntersectingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -83,16 +83,6 @@ void NonIntersectingPathsBuilder::fracture() } } -void NonIntersectingPathsBuilder::flatten() -{ - auto operation = [](SubItem & a, SubItem & b) { return a.flatten(b); }; - perform_operation(operation); - - if (set->document()) { - DocumentUndo::done(set->document(), "Flatten", INKSCAPE_ICON("path-flatten")); - } -} - void NonIntersectingPathsBuilder::set_parameters() { auto _items = set->items(); @@ -118,21 +108,6 @@ int SubItem::get_common_operation(const SubItem &other_subitem) return -1; } -std::vector SubItem::flatten(const SubItem &other_subitem) -{ - const SubItem *top = &other_subitem; - const SubItem *bottom = this; - - if (sp_item_repr_compare_position_bool(top->item, bottom->item)) { - std::swap(top, bottom); - } - - auto bottom_diff_paths = sp_pathvector_boolop(top->paths, bottom->paths, bool_op_diff, fill_nonZero, fill_nonZero); - SubItem bottom_diff(bottom_diff_paths, bottom->item, bottom->operations); - - return {*top, bottom_diff}; -} - std::vector SubItem::fracture(const SubItem &other_subitem) { auto intersection_paths = sp_pathvector_boolop(paths, other_subitem.paths, bool_op_inters, fill_nonZero, fill_nonZero); diff --git a/src/helper/NonIntersectingPathsBuilder.h b/src/helper/NonIntersectingPathsBuilder.h index 0d60757892..5f907c5008 100644 --- a/src/helper/NonIntersectingPathsBuilder.h +++ b/src/helper/NonIntersectingPathsBuilder.h @@ -40,7 +40,6 @@ public: class SubItem; NonIntersectingPathsBuilder(ObjectSet *set) : set(set), operations_counter(0) {} void fracture(); - void flatten(); private: @@ -88,7 +87,6 @@ public: bool is_part_of_operation(int operation) { return operations.find(operation) != operations.end(); } int get_common_operation(const SubItem &other_subitem); - std::vector flatten(const SubItem &other_subitem); std::vector fracture(const SubItem &other_subitem); }; }; diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 76b1095560..f739c6d10f 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -465,8 +466,63 @@ void ObjectSet::fracture() void ObjectSet::flatten() { - NonIntersectingPathsBuilder builder(this); - builder.flatten(); + // TODO some refactoring might be good. + + the_temporary_fix_for_the_transform_bug(); + toCurves(); + + struct SubItem + { + Geom::PathVector paths; + SPItem *item; + + bool operator<(const SubItem& other) { + return sp_item_repr_compare_position_bool(item, other.item); + } + }; + + int n = size(); + std::vector paths; + paths.reserve(n); + + for (auto item : items()) { + paths.push_back({item->get_pathvector(), item}); + } + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + SubItem *top = &paths[i]; + SubItem *bottom = &paths[j]; + + if (*top < *bottom) { + std::swap(top, bottom); + } + + auto diff = sp_pathvector_boolop(top->paths, bottom->paths, bool_op_diff, fill_nonZero, fill_nonZero); + if (!diff.empty()) { + bottom->paths = diff; + } + } + } + + XML::Node *after = (*items().begin())->getRepr(); + XML::Node *parent = after->parent(); + std::vector nodes; + + for (int i = 0; i < n; i++) { + auto split = split_non_intersecting_paths(paths[i].paths); + for (auto pathvec : split) { + if (!pathvec.empty()) { + nodes.push_back(draw_on_canvas(pathvec, paths[i].item, parent, after)); + } + } + } + + deleteItems(); + + for (int i = 0; i < n; i++) { + add(nodes[i]); + } } void ObjectSet::splitNonIntersecting() -- GitLab From edeb5d36559b438eb24128cad2545b7f0d406ace Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 25 Jul 2021 00:53:40 +0200 Subject: [PATCH 144/235] Changed NonIntersectingPathsBuilder to store items instead of operations. --- src/helper/NonIntersectingPathsBuilder.cpp | 60 ++++++++++++---------- src/helper/NonIntersectingPathsBuilder.h | 32 ++++++------ 2 files changed, 47 insertions(+), 45 deletions(-) diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp index 04d96fa180..af33dc3205 100644 --- a/src/helper/NonIntersectingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -98,31 +98,48 @@ SPDesktop *NonIntersectingPathsBuilder::desktop() return set->desktop(); } -int SubItem::get_common_operation(const SubItem &other_subitem) +SPItem* SubItem::get_common_item(const SubItem &other_subitem) const { - for (auto operation : other_subitem.operations) { - if (is_part_of_operation(operation)) { - return operation; + for (auto item : other_subitem.items) { + if (is_subitem_of(item)) { + return item; } } - return -1; + return nullptr; +} + +SPItem* SubItem::get_top_item() const +{ + SPItem *result = *items.begin(); + for (SPItem *item : items) { + if (sp_item_repr_compare_position_bool(result, item)) { + result = item; + } + } + return result; +} + +bool SubItem::is_virgin() const +{ + return items.size() == 1; } std::vector SubItem::fracture(const SubItem &other_subitem) { auto intersection_paths = sp_pathvector_boolop(paths, other_subitem.paths, bool_op_inters, fill_nonZero, fill_nonZero); - auto intersection_item = sp_item_repr_compare_position_bool(other_subitem.item, this->item) ? this->item : other_subitem.item; - SubItem intersection(intersection_paths, intersection_item, operations, other_subitem.operations); + auto intersection_top_item = + sp_item_repr_compare_position_bool(other_subitem.top_item, this->top_item) ? this->top_item : other_subitem.top_item; + SubItem intersection(intersection_paths, items, other_subitem.items, intersection_top_item); if (intersection.paths.empty()) { return {}; } auto diff1_paths = sp_pathvector_boolop(paths, other_subitem.paths, bool_op_diff, fill_nonZero, fill_nonZero); - SubItem diff1(diff1_paths, other_subitem.item, other_subitem.operations); + SubItem diff1(diff1_paths, other_subitem.items, other_subitem.top_item); auto diff2_paths = sp_pathvector_boolop(other_subitem.paths, paths, bool_op_diff, fill_nonZero, fill_nonZero); - SubItem diff2(diff2_paths, item, operations); + SubItem diff2(diff2_paths, items, top_item); return {intersection, diff1, diff2}; } @@ -133,7 +150,7 @@ std::vector split_non_intersecting_paths(std::vector &subitems for (auto &path : subitems) { auto split = split_non_intersecting_paths(path.paths); for (auto &split_path : split) { - result.emplace_back(split_path, path.item, path.operations); + result.emplace_back(split_path, path.items, path.top_item); } } return result; @@ -146,7 +163,7 @@ std::vector NonIntersectingPathsBuilder::get_operation_result(SubItemOp result.resize(n); for (int i = 0; i < n; i++) { - result[i] = SubItem(items[i]->get_pathvector(), items[i], {}); + result[i] = SubItem(items[i]->get_pathvector(), {items[i]}, items[i]); } // result will grow as items are pushed @@ -156,21 +173,15 @@ std::vector NonIntersectingPathsBuilder::get_operation_result(SubItemOp if (i == j) { continue; } - // if 2 subitems share at least one operation, then + // if 2 subitems share at least one item, then // they don't intersect by definition (since operations // in this class yields non-intersecting paths). continue. - // The concept of operations might be deleted in - // the future, but currently, the boolops are not - // sophisticated enough and this check is needed - // to avoid going into an infinite loop. - int common_operation = result[i].get_common_operation(result[j]); - if (common_operation != -1) { continue; } + SPItem *common_item = result[i].get_common_item(result[j]); + if (common_item) { continue; } auto broken = operation(result[i], result[j]); remove_empty_subitems(broken); if (broken.empty()) { continue; } // don't intersect. continue. - add_current_operation_to_subitems(broken); - operations_counter++; // the bigger index should be erased first. int bigger_index = (i > j) ? i : j; @@ -195,7 +206,7 @@ void NonIntersectingPathsBuilder::draw_subitems(const std::vector &subi int n = subitems.size(); result_nodes.resize(n); for (int i = 0; i < n; i++) { - result_nodes[i] = draw_on_canvas(subitems[i].paths, subitems[i].item, parent, after); + result_nodes[i] = draw_on_canvas(subitems[i].paths, subitems[i].top_item, parent, after); } } @@ -209,11 +220,4 @@ void NonIntersectingPathsBuilder::remove_empty_subitems(std::vector &su } } -void NonIntersectingPathsBuilder::add_current_operation_to_subitems(std::vector &subitems) -{ - for (auto &subitem : subitems) { - subitem.operations.insert(operations_counter); - } -} - }; diff --git a/src/helper/NonIntersectingPathsBuilder.h b/src/helper/NonIntersectingPathsBuilder.h index 5f907c5008..c24452c7d3 100644 --- a/src/helper/NonIntersectingPathsBuilder.h +++ b/src/helper/NonIntersectingPathsBuilder.h @@ -26,8 +26,6 @@ class NonIntersectingPathsBuilder private: - int operations_counter; - XML::Node *parent; XML::Node *after; @@ -38,7 +36,7 @@ private: public: class SubItem; - NonIntersectingPathsBuilder(ObjectSet *set) : set(set), operations_counter(0) {} + NonIntersectingPathsBuilder(ObjectSet *set) : set(set) {} void fracture(); private: @@ -51,42 +49,42 @@ private: SPDesktop *desktop(); void set_parameters(); void remove_empty_subitems(std::vector &subitems); - void add_current_operation_to_subitems(std::vector &subitems); public: /** * When an item from the original ObjectSet is broken, each * broken part is represented by the SubItem class. This - * class hold information such as the original item it originated - * from, the operations that lead to the creation of this SubItem, - * and the paths that the SubItem consists of. + * class hold information such as the original items it originated + * from and the paths that the SubItem consists of. **/ class SubItem { public: Geom::PathVector paths; - SPItem *item; - std::set operations; + std::set items; + SPItem *top_item; SubItem() {} - SubItem(Geom::PathVector paths, SPItem *item, std::set operations) + SubItem(Geom::PathVector paths, std::set items, SPItem *top_item) : paths(std::move(paths)) - , item(item) - , operations(std::move(operations)) + , items(std::move(items)) + , top_item(top_item) {} - SubItem(Geom::PathVector paths, SPItem *item, std::set operations1, const std::set &operations2) - : SubItem(paths, item, operations1) + SubItem(Geom::PathVector paths, const std::set &items1, const std::set &items2, SPItem *top_item) + : SubItem(paths, items1, top_item) { - operations.insert(operations2.begin(), operations2.end()); + items.insert(items2.begin(), items2.end()); } - bool is_part_of_operation(int operation) { return operations.find(operation) != operations.end(); } + bool is_subitem_of(SPItem* item) const { return items.find(item) != items.end(); } - int get_common_operation(const SubItem &other_subitem); + SPItem* get_common_item(const SubItem &other_subitem) const; + SPItem* get_top_item() const; + bool is_virgin() const; std::vector fracture(const SubItem &other_subitem); }; }; -- GitLab From c6eb7f31b981c046ebabc16698fbeb4aad4803fc Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 25 Jul 2021 01:07:24 +0200 Subject: [PATCH 145/235] Added operator< that uses sp_object_compare_position_bool. --- src/helper/NonIntersectingPathsBuilder.cpp | 8 ++++++-- src/helper/NonIntersectingPathsBuilder.h | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp index af33dc3205..c7a49bc160 100644 --- a/src/helper/NonIntersectingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -124,11 +124,15 @@ bool SubItem::is_virgin() const return items.size() == 1; } +bool SubItem::operator<(const SubItem &other) const +{ + return sp_object_compare_position_bool(top_item, other.top_item); +} + std::vector SubItem::fracture(const SubItem &other_subitem) { auto intersection_paths = sp_pathvector_boolop(paths, other_subitem.paths, bool_op_inters, fill_nonZero, fill_nonZero); - auto intersection_top_item = - sp_item_repr_compare_position_bool(other_subitem.top_item, this->top_item) ? this->top_item : other_subitem.top_item; + auto intersection_top_item = other_subitem < *this ? this->top_item : other_subitem.top_item; SubItem intersection(intersection_paths, items, other_subitem.items, intersection_top_item); if (intersection.paths.empty()) { diff --git a/src/helper/NonIntersectingPathsBuilder.h b/src/helper/NonIntersectingPathsBuilder.h index c24452c7d3..4fb752e0b9 100644 --- a/src/helper/NonIntersectingPathsBuilder.h +++ b/src/helper/NonIntersectingPathsBuilder.h @@ -86,6 +86,8 @@ public: SPItem* get_top_item() const; bool is_virgin() const; std::vector fracture(const SubItem &other_subitem); + + bool operator<(const SubItem &other) const; }; }; -- GitLab From c680d2676fb06141dcee0509832e8770ac538d0e Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 25 Jul 2021 02:34:45 +0200 Subject: [PATCH 146/235] Split the NonIntersectingPathsBuilder::perform_operation method into 3 methods. --- src/helper/NonIntersectingPathsBuilder.cpp | 28 +++++++++++++++------- src/helper/NonIntersectingPathsBuilder.h | 6 ++++- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp index c7a49bc160..635199dc00 100644 --- a/src/helper/NonIntersectingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -40,20 +40,14 @@ static void sp_selection_delete_impl(std::vector const &items, bool pro } } -void NonIntersectingPathsBuilder::perform_operation(SubItemOperation operation) +void NonIntersectingPathsBuilder::prepare_input() { - if (set->isEmpty()) { - return; - } - // FIXME this causes a crash if the function ObjectSet::move is // called with a dx or dy equals 0. this is because of an assertion // in the function maybeDone. fix it later. // FIXME enable this and investigate why the program crashes when undoing. // DocumentUndo::ScopedInsensitive scopedInsensitive(set->document()); - set_desktop_busy(desktop()); - // Ideally shouldn't be converting to paths? set->toCurves(true); @@ -61,14 +55,30 @@ void NonIntersectingPathsBuilder::perform_operation(SubItemOperation operation) set->the_temporary_fix_for_the_transform_bug(); set_parameters(); - auto result = get_operation_result(operation); - draw_subitems(result); +} + +void NonIntersectingPathsBuilder::show_output() +{ + draw_subitems(result_subitems); sp_selection_delete_impl(items); for (auto node : result_nodes) { set->add(node); } +} + +void NonIntersectingPathsBuilder::perform_operation(SubItemOperation operation) +{ + if (set->isEmpty()) { + return; + } + + set_desktop_busy(desktop()); + + prepare_input(); + result_subitems = get_operation_result(operation); + show_output(); unset_desktop_busy(desktop()); } diff --git a/src/helper/NonIntersectingPathsBuilder.h b/src/helper/NonIntersectingPathsBuilder.h index 4fb752e0b9..c8e1356f36 100644 --- a/src/helper/NonIntersectingPathsBuilder.h +++ b/src/helper/NonIntersectingPathsBuilder.h @@ -24,6 +24,8 @@ class NonIntersectingPathsBuilder // TODO you might simplify this class so that it only constructs // paths, and move the rest of the logic somewhere else. +public: class SubItem; + private: XML::Node *parent; @@ -31,11 +33,11 @@ private: ObjectSet *set; std::vector items; + std::vector result_subitems; std::vector result_nodes; public: - class SubItem; NonIntersectingPathsBuilder(ObjectSet *set) : set(set) {} void fracture(); @@ -43,6 +45,8 @@ private: using SubItemOperation = std::function(SubItem &, SubItem &)>; + void prepare_input(); + void show_output(); void perform_operation(SubItemOperation operation); std::vector get_operation_result(SubItemOperation operation); void draw_subitems(const std::vector &subitems); -- GitLab From 8a26cdb21fdc1c0adac9f5e5b6f184fc1df7a6e9 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 25 Jul 2021 03:56:11 +0200 Subject: [PATCH 147/235] Fixed the history for the flatten operation. --- src/object/object-set.cpp | 23 ++++++++++++++++++++--- src/object/object-set.h | 2 +- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index f739c6d10f..be0bd4321a 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "box3d.h" #include "display/curve.h" @@ -464,12 +465,24 @@ void ObjectSet::fracture() builder.fracture(); } -void ObjectSet::flatten() +// TODO this is a duplicate code from selection-chemistry.cpp. refactor it. +static void sp_selection_delete_impl(std::vector const &items, bool propagate = true, bool propagate_descendants = true) +{ + for (auto item : items) { + sp_object_ref(item, nullptr); + } + for (auto item : items) { + item->deleteObject(propagate, propagate_descendants); + sp_object_unref(item, nullptr); + } +} + +void ObjectSet::flatten(bool skip_undo) { // TODO some refactoring might be good. the_temporary_fix_for_the_transform_bug(); - toCurves(); + toCurves(true); struct SubItem { @@ -518,11 +531,15 @@ void ObjectSet::flatten() } } - deleteItems(); + sp_selection_delete_impl(std::vector(items().begin(), items().end())); for (int i = 0; i < n; i++) { add(nodes[i]); } + + if (!skip_undo && document()) { + DocumentUndo::done(document(), "Flatten", INKSCAPE_ICON("path-flatten")); + } } void ObjectSet::splitNonIntersecting() diff --git a/src/object/object-set.h b/src/object/object-set.h index bc1eda0034..5b0c75c077 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -471,7 +471,7 @@ public: void remove_paths_with_small_area(); void the_temporary_fix_for_the_transform_bug(); void fracture(); - void flatten(); + void flatten(bool skip_undo = false); void splitNonIntersecting(); protected: -- GitLab From 27af62340f4b5a4c2f5b7ffd1ff1b81104b28396 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 25 Jul 2021 04:16:53 +0200 Subject: [PATCH 148/235] Fixed the history for the Split non-intersecting paths operation. --- src/object/object-set.cpp | 9 +++++++-- src/object/object-set.h | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index be0bd4321a..a1cb900289 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -542,7 +542,7 @@ void ObjectSet::flatten(bool skip_undo) } } -void ObjectSet::splitNonIntersecting() +void ObjectSet::splitNonIntersecting(bool skip_undo) { // FIXME consider the case when splitting a group. @@ -569,10 +569,15 @@ void ObjectSet::splitNonIntersecting() } } - deleteItems(); + sp_selection_delete_impl(std::vector(items().begin(), items().end())); + for (auto &node : result) { add(node); } + + if (!skip_undo && document()) { + DocumentUndo::done(document(), "Split Non-Intersecting Paths", INKSCAPE_ICON("path-split-non-intersecting")); + } } } // namespace Inkscape diff --git a/src/object/object-set.h b/src/object/object-set.h index 5b0c75c077..f9a7e41c14 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -472,7 +472,7 @@ public: void the_temporary_fix_for_the_transform_bug(); void fracture(); void flatten(bool skip_undo = false); - void splitNonIntersecting(); + void splitNonIntersecting(bool skip_undo = false); protected: virtual void _connectSignals(SPObject* object) {}; -- GitLab From faa0d9dde60f4b9805bf959362d67fd8c1b4fd7c Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 25 Jul 2021 04:37:08 +0200 Subject: [PATCH 149/235] Added the skip_undo option to the Fracture operation. --- src/helper/NonIntersectingPathsBuilder.cpp | 4 ++-- src/helper/NonIntersectingPathsBuilder.h | 2 +- src/object/object-set.cpp | 4 ++-- src/object/object-set.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp index 635199dc00..cf6a4fceae 100644 --- a/src/helper/NonIntersectingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -83,12 +83,12 @@ void NonIntersectingPathsBuilder::perform_operation(SubItemOperation operation) unset_desktop_busy(desktop()); } -void NonIntersectingPathsBuilder::fracture() +void NonIntersectingPathsBuilder::fracture(bool skip_undo) { auto operation = [](SubItem & a, SubItem & b) { return a.fracture(b); }; perform_operation(operation); - if (set->document()) { + if (!skip_undo && set->document()) { DocumentUndo::done(set->document(), "Fracture", INKSCAPE_ICON("path-fracture")); } } diff --git a/src/helper/NonIntersectingPathsBuilder.h b/src/helper/NonIntersectingPathsBuilder.h index c8e1356f36..1ab9ee3278 100644 --- a/src/helper/NonIntersectingPathsBuilder.h +++ b/src/helper/NonIntersectingPathsBuilder.h @@ -39,7 +39,7 @@ private: public: NonIntersectingPathsBuilder(ObjectSet *set) : set(set) {} - void fracture(); + void fracture(bool skip_undo = false); private: diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index a1cb900289..3322bf2294 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -459,10 +459,10 @@ void ObjectSet::the_temporary_fix_for_the_transform_bug() move(0, 0, true); } -void ObjectSet::fracture() +void ObjectSet::fracture(bool skip_undo) { NonIntersectingPathsBuilder builder(this); - builder.fracture(); + builder.fracture(skip_undo); } // TODO this is a duplicate code from selection-chemistry.cpp. refactor it. diff --git a/src/object/object-set.h b/src/object/object-set.h index f9a7e41c14..6ac8cd6e62 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -470,7 +470,7 @@ public: void remove_paths_with_small_area(); void the_temporary_fix_for_the_transform_bug(); - void fracture(); + void fracture(bool skip_undo = false); void flatten(bool skip_undo = false); void splitNonIntersecting(bool skip_undo = false); -- GitLab From 89908bdd4ac1d17175d2fabe0248ec9c1e8bb0db Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 25 Jul 2021 04:40:23 +0200 Subject: [PATCH 150/235] Moved the set/unset_desktop_busy call to ObjectSet::fracture. --- src/helper/NonIntersectingPathsBuilder.cpp | 4 ---- src/object/object-set.cpp | 2 ++ 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp index cf6a4fceae..f9be197963 100644 --- a/src/helper/NonIntersectingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -74,13 +74,9 @@ void NonIntersectingPathsBuilder::perform_operation(SubItemOperation operation) return; } - set_desktop_busy(desktop()); - prepare_input(); result_subitems = get_operation_result(operation); show_output(); - - unset_desktop_busy(desktop()); } void NonIntersectingPathsBuilder::fracture(bool skip_undo) diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 3322bf2294..3e056c1919 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -461,8 +461,10 @@ void ObjectSet::the_temporary_fix_for_the_transform_bug() void ObjectSet::fracture(bool skip_undo) { + set_desktop_busy(desktop()); NonIntersectingPathsBuilder builder(this); builder.fracture(skip_undo); + unset_desktop_busy(desktop()); } // TODO this is a duplicate code from selection-chemistry.cpp. refactor it. -- GitLab From c67e601b374807659ca63b6c4429fbbe29eee933 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 25 Jul 2021 06:02:59 +0200 Subject: [PATCH 151/235] Split NonIntersectingPathsBuilder::fracture into 2 functions. --- src/helper/NonIntersectingPathsBuilder.cpp | 9 +++++++-- src/helper/NonIntersectingPathsBuilder.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp index f9be197963..57fe1d0500 100644 --- a/src/helper/NonIntersectingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -76,13 +76,18 @@ void NonIntersectingPathsBuilder::perform_operation(SubItemOperation operation) prepare_input(); result_subitems = get_operation_result(operation); - show_output(); } -void NonIntersectingPathsBuilder::fracture(bool skip_undo) +void NonIntersectingPathsBuilder::perform_fracture() { auto operation = [](SubItem & a, SubItem & b) { return a.fracture(b); }; perform_operation(operation); +} + +void NonIntersectingPathsBuilder::fracture(bool skip_undo) +{ + perform_fracture(); + show_output(); if (!skip_undo && set->document()) { DocumentUndo::done(set->document(), "Fracture", INKSCAPE_ICON("path-fracture")); diff --git a/src/helper/NonIntersectingPathsBuilder.h b/src/helper/NonIntersectingPathsBuilder.h index 1ab9ee3278..4cf3d4d308 100644 --- a/src/helper/NonIntersectingPathsBuilder.h +++ b/src/helper/NonIntersectingPathsBuilder.h @@ -45,6 +45,7 @@ private: using SubItemOperation = std::function(SubItem &, SubItem &)>; + void perform_fracture(); void prepare_input(); void show_output(); void perform_operation(SubItemOperation operation); -- GitLab From 5c74ea9c2b079754d06f6bd27044591f37c9c750 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 25 Jul 2021 06:05:13 +0200 Subject: [PATCH 152/235] Made it doable for the subitems to be returned without having to draw the shapes. --- src/helper/NonIntersectingPathsBuilder.cpp | 5 +++++ src/helper/NonIntersectingPathsBuilder.h | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp index 57fe1d0500..e8dc89777e 100644 --- a/src/helper/NonIntersectingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -84,6 +84,11 @@ void NonIntersectingPathsBuilder::perform_fracture() perform_operation(operation); } +const std::vector& NonIntersectingPathsBuilder::get_result_subitems() const +{ + return result_subitems; +} + void NonIntersectingPathsBuilder::fracture(bool skip_undo) { perform_fracture(); diff --git a/src/helper/NonIntersectingPathsBuilder.h b/src/helper/NonIntersectingPathsBuilder.h index 4cf3d4d308..3c28470302 100644 --- a/src/helper/NonIntersectingPathsBuilder.h +++ b/src/helper/NonIntersectingPathsBuilder.h @@ -40,14 +40,15 @@ public: NonIntersectingPathsBuilder(ObjectSet *set) : set(set) {} void fracture(bool skip_undo = false); + void perform_fracture(); + void show_output(); + const std::vector& get_result_subitems() const; private: using SubItemOperation = std::function(SubItem &, SubItem &)>; - void perform_fracture(); void prepare_input(); - void show_output(); void perform_operation(SubItemOperation operation); std::vector get_operation_result(SubItemOperation operation); void draw_subitems(const std::vector &subitems); -- GitLab From cad1e0042e7395e6471b1866b7d25117998f15e5 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 25 Jul 2021 06:10:38 +0200 Subject: [PATCH 153/235] Removed ObjectSet::remove_paths_with_small_area declaration since it had no difinition. --- src/object/object-set.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/object/object-set.h b/src/object/object-set.h index 6ac8cd6e62..26f13c5e6d 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -468,7 +468,6 @@ public: void swapFillStroke(); void fillBetweenMany(); - void remove_paths_with_small_area(); void the_temporary_fix_for_the_transform_bug(); void fracture(bool skip_undo = false); void flatten(bool skip_undo = false); -- GitLab From b98da41ad49f5688c445e682ad11c67da4fa33c9 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 25 Jul 2021 22:30:15 +0200 Subject: [PATCH 154/235] Created InteractiveShapesBuilder.*. --- src/helper/InteractiveShapesBuilder.cpp | 14 ++++++++++++++ src/helper/InteractiveShapesBuilder.h | 17 +++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/helper/InteractiveShapesBuilder.cpp create mode 100644 src/helper/InteractiveShapesBuilder.h diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp new file mode 100644 index 0000000000..c1c5b5a0e9 --- /dev/null +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Interactive Shapes Builder. + * + * + *//* + * Authors: + * Osama Ahmad + * + * Copyright (C) 2021 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "InteractiveShapesBuilder.h" diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h new file mode 100644 index 0000000000..a33c0b048b --- /dev/null +++ b/src/helper/InteractiveShapesBuilder.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Interactive Shapes Builder. + * + * + *//* + * Authors: + * Osama Ahmad + * + * Copyright (C) 2021 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +class InteractiveShapesBuilder +{ + +}; \ No newline at end of file -- GitLab From 9b8a5fb30d912c65ac006767f8b27896af04f6e0 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 25 Jul 2021 22:46:44 +0200 Subject: [PATCH 155/235] Split the function NonIntersectingPathsBuilder::show_output into two functions and added an optional bool parameter. --- src/helper/NonIntersectingPathsBuilder.cpp | 10 ++++++++-- src/helper/NonIntersectingPathsBuilder.h | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp index e8dc89777e..58f614f272 100644 --- a/src/helper/NonIntersectingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -57,12 +57,17 @@ void NonIntersectingPathsBuilder::prepare_input() set_parameters(); } -void NonIntersectingPathsBuilder::show_output() +void NonIntersectingPathsBuilder::show_output(bool delete_original) { draw_subitems(result_subitems); - sp_selection_delete_impl(items); + if (delete_original) { + sp_selection_delete_impl(items); + } +} +void NonIntersectingPathsBuilder::add_result_to_set() +{ for (auto node : result_nodes) { set->add(node); } @@ -93,6 +98,7 @@ void NonIntersectingPathsBuilder::fracture(bool skip_undo) { perform_fracture(); show_output(); + add_result_to_set(); if (!skip_undo && set->document()) { DocumentUndo::done(set->document(), "Fracture", INKSCAPE_ICON("path-fracture")); diff --git a/src/helper/NonIntersectingPathsBuilder.h b/src/helper/NonIntersectingPathsBuilder.h index 3c28470302..4cfdcdfcf3 100644 --- a/src/helper/NonIntersectingPathsBuilder.h +++ b/src/helper/NonIntersectingPathsBuilder.h @@ -41,7 +41,8 @@ public: NonIntersectingPathsBuilder(ObjectSet *set) : set(set) {} void fracture(bool skip_undo = false); void perform_fracture(); - void show_output(); + void show_output(bool delete_original = true); + void add_result_to_set(); const std::vector& get_result_subitems() const; private: -- GitLab From 8ab363175284d54d91f512913e12f10c92b0b3f2 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 25 Jul 2021 22:47:16 +0200 Subject: [PATCH 156/235] Added a method to expose the result nodes. --- src/helper/NonIntersectingPathsBuilder.cpp | 5 +++++ src/helper/NonIntersectingPathsBuilder.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp index 58f614f272..7168ec5680 100644 --- a/src/helper/NonIntersectingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -94,6 +94,11 @@ const std::vector& NonIntersectingPathsBuilder::get_result_subitems() c return result_subitems; } +const std::vector& NonIntersectingPathsBuilder::get_result_nodes() const +{ + return result_nodes; +} + void NonIntersectingPathsBuilder::fracture(bool skip_undo) { perform_fracture(); diff --git a/src/helper/NonIntersectingPathsBuilder.h b/src/helper/NonIntersectingPathsBuilder.h index 4cfdcdfcf3..6e6a2efb75 100644 --- a/src/helper/NonIntersectingPathsBuilder.h +++ b/src/helper/NonIntersectingPathsBuilder.h @@ -44,6 +44,7 @@ public: void show_output(bool delete_original = true); void add_result_to_set(); const std::vector& get_result_subitems() const; + const std::vector& get_result_nodes() const; private: -- GitLab From 4b4993281a6727526d8c8cf2effec71e6173cd63 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 25 Jul 2021 23:07:25 +0200 Subject: [PATCH 157/235] Added InteractiveShapesBuilder.* to the CMakeLists.txt. --- src/helper/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/helper/CMakeLists.txt b/src/helper/CMakeLists.txt index ebdc449092..8832276c09 100644 --- a/src/helper/CMakeLists.txt +++ b/src/helper/CMakeLists.txt @@ -18,6 +18,7 @@ set(helper_SRC geom-pathvectorsatellites.cpp geom-satellite.cpp gettext.cpp + InteractiveShapesBuilder.cpp disjoint-sets.cpp NonIntersectingPathsBuilder.cpp pixbuf-ops.cpp @@ -42,6 +43,7 @@ set(helper_SRC geom-satellite.h geom.h gettext.h + InteractiveShapesBuilder.h mathfns.h disjoint-sets.h NonIntersectingPathsBuilder.h -- GitLab From 2d384228f25277f7fc41588a4bb269a56dbb3dda Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 25 Jul 2021 23:28:07 +0200 Subject: [PATCH 158/235] Initial header file for the InteractiveShapesBuilder. --- src/helper/InteractiveShapesBuilder.cpp | 20 +++++++++++++ src/helper/InteractiveShapesBuilder.h | 38 ++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index c1c5b5a0e9..e5842218f6 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -12,3 +12,23 @@ */ #include "InteractiveShapesBuilder.h" + +void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) {} + +void Inkscape::InteractiveShapesBuilder::set_union(Inkscape::ObjectSet *set) {} + +void Inkscape::InteractiveShapesBuilder::set_delete(Inkscape::ObjectSet *set) {} + +void Inkscape::InteractiveShapesBuilder::done() {} + +void Inkscape::InteractiveShapesBuilder::add_disabled_node(Inkscape::XML::Node *node) {} + +void Inkscape::InteractiveShapesBuilder::remove_disabled_node(Inkscape::XML::Node *node) {} + +void Inkscape::InteractiveShapesBuilder::add_enabled_node(Inkscape::XML::Node *node) {} + +void Inkscape::InteractiveShapesBuilder::remove_enabled_node(Inkscape::XML::Node *node) {} + +void Inkscape::InteractiveShapesBuilder::set_style_disabled(Inkscape::XML::Node *node) {} + +void Inkscape::InteractiveShapesBuilder::restore_original_style(Inkscape::XML::Node *node) {} diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index a33c0b048b..01b4ac8380 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -11,7 +11,43 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ +#include +#include "NonIntersectingPathsBuilder.h" + +namespace Inkscape { + class InteractiveShapesBuilder { -}; \ No newline at end of file +public: using SubItem = NonIntersectingPathsBuilder::SubItem; + +private: + + std::vector original_items; + + std::map enabled; + std::map disabled; + std::map original_style; + + bool started = false; + + void add_disabled_node(XML::Node *node); + void remove_disabled_node(XML::Node *node); + + void add_enabled_node(XML::Node *node); + void remove_enabled_node(XML::Node *node); + + void set_style_disabled(XML::Node *node); + void restore_original_style(XML::Node *node); + + +public: + + void start(ObjectSet *set); + void set_union(ObjectSet *set); + void set_delete(ObjectSet *set); + void done(); + +}; + +} \ No newline at end of file -- GitLab From 4a1df30c25e18684bf944d6351de14a8cac03474 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 25 Jul 2021 23:56:37 +0200 Subject: [PATCH 159/235] Changed the functions in the InteractiveShapesBuilder and implemented the start function. --- src/helper/InteractiveShapesBuilder.cpp | 47 +++++++++++++++++++------ src/helper/InteractiveShapesBuilder.h | 13 +++---- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index e5842218f6..77add377b8 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -13,22 +13,49 @@ #include "InteractiveShapesBuilder.h" -void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) {} +namespace Inkscape { -void Inkscape::InteractiveShapesBuilder::set_union(Inkscape::ObjectSet *set) {} +void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) +{ + if (started) { + std::cerr << "InteractiveShapesBuilder: already started. Resetting before starting again.\n"; + reset(); + } -void Inkscape::InteractiveShapesBuilder::set_delete(Inkscape::ObjectSet *set) {} + original_items = std::vector(set->items().begin(), set->items().end()); -void Inkscape::InteractiveShapesBuilder::done() {} + NonIntersectingPathsBuilder builder(set); + builder.perform_fracture(); + builder.show_output(false); -void Inkscape::InteractiveShapesBuilder::add_disabled_node(Inkscape::XML::Node *node) {} + auto &nodes = builder.get_result_nodes(); + auto &subitems = builder.get_result_subitems(); + int n = nodes.size(); -void Inkscape::InteractiveShapesBuilder::remove_disabled_node(Inkscape::XML::Node *node) {} + for (int i = 0; i < n; i++) { + add_disabled_node(nodes[i], subitems[i]); + } -void Inkscape::InteractiveShapesBuilder::add_enabled_node(Inkscape::XML::Node *node) {} + started = true; +} -void Inkscape::InteractiveShapesBuilder::remove_enabled_node(Inkscape::XML::Node *node) {} +void InteractiveShapesBuilder::set_union(ObjectSet *set) {} -void Inkscape::InteractiveShapesBuilder::set_style_disabled(Inkscape::XML::Node *node) {} +void InteractiveShapesBuilder::set_delete(ObjectSet *set) {} -void Inkscape::InteractiveShapesBuilder::restore_original_style(Inkscape::XML::Node *node) {} +void InteractiveShapesBuilder::done() {} + +void InteractiveShapesBuilder::add_disabled_node(XML::Node *node, const SubItem &subitem) {} + +void InteractiveShapesBuilder::remove_disabled_node(XML::Node *node) {} + +void InteractiveShapesBuilder::add_enabled_node(XML::Node *node, const SubItem &subitem) {} + +void InteractiveShapesBuilder::remove_enabled_node(XML::Node *node) {} + +void InteractiveShapesBuilder::set_style_disabled(XML::Node *node) {} + +void InteractiveShapesBuilder::restore_original_style(XML::Node *node) {} + +void InteractiveShapesBuilder::reset() {} +} \ No newline at end of file diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index 01b4ac8380..7a4bc5a60f 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -23,23 +23,24 @@ public: using SubItem = NonIntersectingPathsBuilder::SubItem; private: - std::vector original_items; + std::vector original_items; - std::map enabled; - std::map disabled; - std::map original_style; + std::map enabled; + std::map disabled; + std::map original_style; bool started = false; - void add_disabled_node(XML::Node *node); + void add_disabled_node(XML::Node *node, const SubItem &subitem); void remove_disabled_node(XML::Node *node); - void add_enabled_node(XML::Node *node); + void add_enabled_node(XML::Node *node, const SubItem &subitem); void remove_enabled_node(XML::Node *node); void set_style_disabled(XML::Node *node); void restore_original_style(XML::Node *node); + void reset(); public: -- GitLab From 51be1f5a67a6773ce1ce8d2992552134ea8fc511 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 00:16:18 +0200 Subject: [PATCH 160/235] Initial implementation for the InteractiveShapesBuilder::reset method. --- src/helper/InteractiveShapesBuilder.cpp | 31 ++++++++++++++++++++++++- src/helper/InteractiveShapesBuilder.h | 3 +++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 77add377b8..b8d2ce0fb6 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -23,6 +23,7 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) } original_items = std::vector(set->items().begin(), set->items().end()); + hide_original_items(); NonIntersectingPathsBuilder builder(set); builder.perform_fracture(); @@ -57,5 +58,33 @@ void InteractiveShapesBuilder::set_style_disabled(XML::Node *node) {} void InteractiveShapesBuilder::restore_original_style(XML::Node *node) {} -void InteractiveShapesBuilder::reset() {} +void InteractiveShapesBuilder::hide_original_items() {} + +void InteractiveShapesBuilder::show_original_items() {} + +void InteractiveShapesBuilder::reset() +{ + SPDocument *document = original_items.front()->document; + + for (auto node : enabled) { + auto object = document->getObjectByRepr(node.first); + + // TODO make sure this works. + sp_object_ref(object, nullptr); + object->deleteObject(true, true); + sp_object_unref(object, nullptr); + } + + for (auto node : disabled) { + auto object = document->getObjectByRepr(node.first); + + // TODO make sure this works. + sp_object_ref(object, nullptr); + object->deleteObject(true, true); + sp_object_unref(object, nullptr); + } + + show_original_items(); +} + } \ No newline at end of file diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index 7a4bc5a60f..4999db4694 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -40,6 +40,9 @@ private: void set_style_disabled(XML::Node *node); void restore_original_style(XML::Node *node); + void hide_original_items(); + void show_original_items(); + void reset(); public: -- GitLab From 8251720934531c8a2c71b7445b292d59ed7434bb Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 00:28:05 +0200 Subject: [PATCH 161/235] Initial implementation for InteractiveShapesBuilder::add/remove_enabled_node. --- src/helper/InteractiveShapesBuilder.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index b8d2ce0fb6..a16431d0d5 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -50,9 +50,20 @@ void InteractiveShapesBuilder::add_disabled_node(XML::Node *node, const SubItem void InteractiveShapesBuilder::remove_disabled_node(XML::Node *node) {} -void InteractiveShapesBuilder::add_enabled_node(XML::Node *node, const SubItem &subitem) {} +void InteractiveShapesBuilder::add_enabled_node(XML::Node *node, const SubItem &subitem) +{ + enabled[node] = subitem; +} -void InteractiveShapesBuilder::remove_enabled_node(XML::Node *node) {} +void InteractiveShapesBuilder::remove_enabled_node(XML::Node *node) +{ + auto result = enabled.find(node); + if (result == enabled.end()) { + std::cerr << "InteractiveShapesBuilder: Node " << node << " isn't in the enabled set.\n"; + } else { + enabled.erase(result); + } +} void InteractiveShapesBuilder::set_style_disabled(XML::Node *node) {} -- GitLab From a8a57b2e1bca54f1d63d608a2edc3628475582e8 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 03:37:53 +0200 Subject: [PATCH 162/235] Initial implementation for InteractiveShapesBuilder::set_style_disabled and InteractiveShapesBuilder::restore_original_style. --- src/helper/InteractiveShapesBuilder.cpp | 48 +++++++++++++++++++++---- src/helper/InteractiveShapesBuilder.h | 5 ++- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index a16431d0d5..88787c2d39 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -12,11 +12,16 @@ */ #include "InteractiveShapesBuilder.h" +#include "display/drawing-item.h" +#include "style.h" namespace Inkscape { void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) { + desktop = set->desktop(); + document = set->document(); + if (started) { std::cerr << "InteractiveShapesBuilder: already started. Resetting before starting again.\n"; reset(); @@ -46,9 +51,22 @@ void InteractiveShapesBuilder::set_delete(ObjectSet *set) {} void InteractiveShapesBuilder::done() {} -void InteractiveShapesBuilder::add_disabled_node(XML::Node *node, const SubItem &subitem) {} +void InteractiveShapesBuilder::add_disabled_node(XML::Node *node, const SubItem &subitem) +{ + set_style_disabled(node); + disabled[node] = subitem; +} -void InteractiveShapesBuilder::remove_disabled_node(XML::Node *node) {} +void InteractiveShapesBuilder::remove_disabled_node(XML::Node *node) +{ + auto result = disabled.find(node); + if (result == disabled.end()) { + std::cerr << "InteractiveShapesBuilder: Node " << node << " isn't in the disabled set.\n"; + } else { + restore_original_style(node); + disabled.erase(result); + } +} void InteractiveShapesBuilder::add_enabled_node(XML::Node *node, const SubItem &subitem) { @@ -65,9 +83,29 @@ void InteractiveShapesBuilder::remove_enabled_node(XML::Node *node) } } -void InteractiveShapesBuilder::set_style_disabled(XML::Node *node) {} +void InteractiveShapesBuilder::set_style_disabled(XML::Node *node) +{ + auto item = dynamic_cast(document->getObjectByRepr(node)); + if (item) { + Inkscape::DrawingItem *arenaitem = item->get_arenaitem(desktop->dkey); + original_opacity[node] = SP_SCALE24_TO_FLOAT(item->style->opacity.value); + arenaitem->setOpacity(0.5); + } +} -void InteractiveShapesBuilder::restore_original_style(XML::Node *node) {} +void InteractiveShapesBuilder::restore_original_style(XML::Node *node) +{ + auto opacity = original_opacity.find(node); + if (opacity == original_opacity.end()) { + std::cerr << "InteractiveShapeBuilder: The node " << node << " doesn't have its original style stored.\n"; + return; + } + auto item = dynamic_cast(document->getObjectByRepr(node)); + if (item) { + Inkscape::DrawingItem *arenaitem = item->get_arenaitem(desktop->dkey); + arenaitem->setOpacity(opacity->second); + } +} void InteractiveShapesBuilder::hide_original_items() {} @@ -75,8 +113,6 @@ void InteractiveShapesBuilder::show_original_items() {} void InteractiveShapesBuilder::reset() { - SPDocument *document = original_items.front()->document; - for (auto node : enabled) { auto object = document->getObjectByRepr(node.first); diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index 4999db4694..8fb3350f3d 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -23,11 +23,14 @@ public: using SubItem = NonIntersectingPathsBuilder::SubItem; private: + SPDesktop *desktop; + SPDocument *document; + std::vector original_items; std::map enabled; std::map disabled; - std::map original_style; + std::map original_opacity; bool started = false; -- GitLab From f4251327dbeb44c30f6d01696f6238289d305150 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 04:11:33 +0200 Subject: [PATCH 163/235] Initial implementation for InteractiveShapesBuilder::hide/show_original_items. --- src/helper/InteractiveShapesBuilder.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 88787c2d39..46714f537d 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -107,9 +107,19 @@ void InteractiveShapesBuilder::restore_original_style(XML::Node *node) } } -void InteractiveShapesBuilder::hide_original_items() {} +void InteractiveShapesBuilder::hide_original_items() +{ + for (auto item : original_items) { + item->setHidden(true); + } +} -void InteractiveShapesBuilder::show_original_items() {} +void InteractiveShapesBuilder::show_original_items() +{ + for (auto item : original_items) { + item->setHidden(false); + } +} void InteractiveShapesBuilder::reset() { -- GitLab From 8e073eec878e77069d8bfc4ddf3f2fbc2bad834b Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 04:26:44 +0200 Subject: [PATCH 164/235] Initial implementation for InteractiveShapesBuilder::done. --- src/helper/InteractiveShapesBuilder.cpp | 41 ++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 46714f537d..0b1a111d76 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -12,6 +12,8 @@ */ #include "InteractiveShapesBuilder.h" +#include "path/path-boolop.h" +#include "useful-functions.h" #include "display/drawing-item.h" #include "style.h" @@ -49,7 +51,44 @@ void InteractiveShapesBuilder::set_union(ObjectSet *set) {} void InteractiveShapesBuilder::set_delete(ObjectSet *set) {} -void InteractiveShapesBuilder::done() {} +void InteractiveShapesBuilder::done() +{ + std::map original_paths; + for (auto item : original_items) { + original_paths[item] = item->get_pathvector(); + } + + for (auto subitem_it : enabled) { + auto &subitem = subitem_it.second; + auto &items = subitem.items; + for (auto item : items) { + auto paths_it = original_paths.find(item); + if (paths_it == original_paths.end()) { + std::cerr << "InteractiveShapesBuilder: No Geom::PathVector is for the item " << item << ".\n"; + continue; + } + original_paths[item] = sp_pathvector_boolop(paths_it->second, subitem.paths, bool_op_diff, fill_nonZero, fill_nonZero); + } + } + + auto after = original_items.front()->getRepr(); + auto parent = after->parent(); + + show_original_items(); + for (auto item_it : original_paths) { + auto item = item_it.first; + auto &pathvec = item_it.second; + draw_on_canvas(pathvec, item, parent, after); + + // deleting the original item. + // TODO make sure this works. + sp_object_ref(item, nullptr); + item->deleteObject(true, true); + sp_object_unref(item, nullptr); + } + + started = false; +} void InteractiveShapesBuilder::add_disabled_node(XML::Node *node, const SubItem &subitem) { -- GitLab From 4d5ced402dfb2966fbfd1ca8d2447a9497cd72d0 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 04:38:30 +0200 Subject: [PATCH 165/235] Initial implementation for InteractiveShapesBuilder::set_delete. --- src/helper/InteractiveShapesBuilder.cpp | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 0b1a111d76..f7642b3d51 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -12,10 +12,13 @@ */ #include "InteractiveShapesBuilder.h" -#include "path/path-boolop.h" -#include "useful-functions.h" + +#include + #include "display/drawing-item.h" +#include "path/path-boolop.h" #include "style.h" +#include "useful-functions.h" namespace Inkscape { @@ -49,7 +52,22 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) void InteractiveShapesBuilder::set_union(ObjectSet *set) {} -void InteractiveShapesBuilder::set_delete(ObjectSet *set) {} +void InteractiveShapesBuilder::set_delete(ObjectSet *set) +{ + std::vector items(set->items().begin(), set->items().end()); + for (auto item : items) { + auto repr = item->getRepr(); + remove_enabled_node(repr); + remove_disabled_node(repr); + + // TODO make sure this works. + sp_object_ref(item, nullptr); + item->deleteObject(true, true); + sp_object_unref(item, nullptr); + } + + DocumentUndo::done(set->document(), "Interactive Delete", INKSCAPE_ICON("edit-delete")); +} void InteractiveShapesBuilder::done() { -- GitLab From bce956118f6b7b966d0c42a981a84979db5c7c7b Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 04:54:35 +0200 Subject: [PATCH 166/235] Initial implementation for InteractiveShapesBuilder::set_union. --- src/helper/InteractiveShapesBuilder.cpp | 51 ++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index f7642b3d51..be3d76b959 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -50,7 +50,56 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) started = true; } -void InteractiveShapesBuilder::set_union(ObjectSet *set) {} +void InteractiveShapesBuilder::set_union(ObjectSet *set) +{ + std::vector items(set->items().begin(), set->items().end()); + + SubItem res_subitem; + res_subitem.paths = items.front()->get_pathvector(); + res_subitem.top_item = items.front(); + + for (int i = 1; i < items.size(); i++) + { + auto item = items[i]; + auto repr = item->getRepr(); + + auto subitem_it = enabled.find(repr); + if (subitem_it == enabled.end()) { + subitem_it = disabled.find(repr); + if (subitem_it == disabled.end()) { + std::cerr << "InteractiveShapesBuilder: Unioning an to_copy_style_form outside of the selection.\n"; + return; + } + } + + SubItem &subitem = subitem_it->second; + res_subitem.items.insert(subitem.paths.begin(), subitem.paths.end()); + + res_subitem.paths = sp_pathvector_boolop(res_subitem.paths, item->get_pathvector(), bool_op_union, fill_nonZero, fill_nonZero); + } + + for (auto item : items) { + auto repr = item->getRepr(); + remove_enabled_node(repr); + remove_disabled_node(repr); + } + + auto to_copy_style_form = res_subitem.top_item; + auto after = to_copy_style_form->getRepr(); + auto parent = after->parent(); + + auto node = draw_on_canvas(res_subitem.paths, to_copy_style_form, parent, after); + add_enabled_node(node, res_subitem); + + for (auto item : items) { + // TODO make sure this works. + sp_object_ref(item, nullptr); + item->deleteObject(true, true); + sp_object_unref(item, nullptr); + } + + DocumentUndo::done(set->document(), "Interactive Union", INKSCAPE_ICON("path-union")); +} void InteractiveShapesBuilder::set_delete(ObjectSet *set) { -- GitLab From d46361efb0c656db2b54629a196da00226a548e6 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 04:55:58 +0200 Subject: [PATCH 167/235] Commented out some cerr messages in the InteractiveShapesBuilder. --- src/helper/InteractiveShapesBuilder.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index be3d76b959..2552ce80f0 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -167,7 +167,7 @@ void InteractiveShapesBuilder::remove_disabled_node(XML::Node *node) { auto result = disabled.find(node); if (result == disabled.end()) { - std::cerr << "InteractiveShapesBuilder: Node " << node << " isn't in the disabled set.\n"; +// std::cerr << "InteractiveShapesBuilder: Node " << node << " isn't in the disabled set.\n"; } else { restore_original_style(node); disabled.erase(result); @@ -183,7 +183,7 @@ void InteractiveShapesBuilder::remove_enabled_node(XML::Node *node) { auto result = enabled.find(node); if (result == enabled.end()) { - std::cerr << "InteractiveShapesBuilder: Node " << node << " isn't in the enabled set.\n"; +// std::cerr << "InteractiveShapesBuilder: Node " << node << " isn't in the enabled set.\n"; } else { enabled.erase(result); } @@ -203,7 +203,7 @@ void InteractiveShapesBuilder::restore_original_style(XML::Node *node) { auto opacity = original_opacity.find(node); if (opacity == original_opacity.end()) { - std::cerr << "InteractiveShapeBuilder: The node " << node << " doesn't have its original style stored.\n"; +// std::cerr << "InteractiveShapeBuilder: The node " << node << " doesn't have its original style stored.\n"; return; } auto item = dynamic_cast(document->getObjectByRepr(node)); -- GitLab From 2da13c0f9b8a99143f9ddb98949cc9be208ec27f Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 05:21:03 +0200 Subject: [PATCH 168/235] Fixed a wrong line in InteractiveShapesBuilder::set_union. --- src/helper/InteractiveShapesBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 2552ce80f0..44f1d83e70 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -73,7 +73,7 @@ void InteractiveShapesBuilder::set_union(ObjectSet *set) } SubItem &subitem = subitem_it->second; - res_subitem.items.insert(subitem.paths.begin(), subitem.paths.end()); + res_subitem.items.insert(subitem.items.begin(), subitem.items.end()); res_subitem.paths = sp_pathvector_boolop(res_subitem.paths, item->get_pathvector(), bool_op_union, fill_nonZero, fill_nonZero); } -- GitLab From d3c0422274441bb9ff0c0d2732fa53db776ccf1f Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 05:31:52 +0200 Subject: [PATCH 169/235] Added #pragma once in InteractiveShapesBuilder.h. --- src/helper/InteractiveShapesBuilder.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index 8fb3350f3d..3dcbe1aac8 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -10,6 +10,7 @@ * Copyright (C) 2021 Authors * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ +#pragma once #include #include "NonIntersectingPathsBuilder.h" -- GitLab From 01c34afc9a8a28b8cdf47a5d59d5796b1c73f693 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 05:48:57 +0200 Subject: [PATCH 170/235] Moved the SubItem class outside of the NonIntersectingPathsBuilder class and added an initial implementation for the interactive mode in the Builder tool. --- src/helper/InteractiveShapesBuilder.cpp | 5 +- src/helper/InteractiveShapesBuilder.h | 16 ++++- src/helper/NonIntersectingPathsBuilder.cpp | 3 +- src/helper/NonIntersectingPathsBuilder.h | 82 ++++++++++++---------- src/ui/tools/builder-tool.cpp | 23 ++++++ src/ui/tools/builder-tool.h | 7 ++ 6 files changed, 93 insertions(+), 43 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 44f1d83e70..9173396595 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -12,12 +12,13 @@ */ #include "InteractiveShapesBuilder.h" - +#include "NonIntersectingPathsBuilder.h" #include - #include "display/drawing-item.h" #include "path/path-boolop.h" #include "style.h" +#include "document.h" +#include "object/sp-item.h" #include "useful-functions.h" namespace Inkscape { diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index 3dcbe1aac8..39620aec0f 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -10,18 +10,28 @@ * Copyright (C) 2021 Authors * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ + #pragma once -#include +#include +#include #include "NonIntersectingPathsBuilder.h" +class SPDesktop; +class SPDocument; +class SPItem; + namespace Inkscape { +class ObjectSet; + +namespace XML { + class Node; +} + class InteractiveShapesBuilder { -public: using SubItem = NonIntersectingPathsBuilder::SubItem; - private: SPDesktop *desktop; diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp index 7168ec5680..5602f6cb06 100644 --- a/src/helper/NonIntersectingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -23,11 +23,10 @@ #include "ui/widget/canvas.h" #include "useful-functions.h" +#include "object/object-set.h" namespace Inkscape { -using SubItem = NonIntersectingPathsBuilder::SubItem; - // TODO this is a duplicate code from selection-chemistry.cpp. refactor it. static void sp_selection_delete_impl(std::vector const &items, bool propagate = true, bool propagate_descendants = true) { diff --git a/src/helper/NonIntersectingPathsBuilder.h b/src/helper/NonIntersectingPathsBuilder.h index 6e6a2efb75..6f53942076 100644 --- a/src/helper/NonIntersectingPathsBuilder.h +++ b/src/helper/NonIntersectingPathsBuilder.h @@ -13,19 +13,31 @@ #pragma once -#include "object/object-set.h" -#include "xml/node.h" -#include "object/sp-item.h" +#include +#include +#include +#include <2geom/pathvector.h> + +class SPItem; +class SPDesktop; +class SPDocument; namespace Inkscape { +class SubItem; +class ObjectSet; + +namespace XML { +class Node; +} + +class SubItem; + class NonIntersectingPathsBuilder { // TODO you might simplify this class so that it only constructs // paths, and move the rest of the logic somewhere else. -public: class SubItem; - private: XML::Node *parent; @@ -57,46 +69,44 @@ private: SPDesktop *desktop(); void set_parameters(); void remove_empty_subitems(std::vector &subitems); +}; +/** +* When an item from the original ObjectSet is broken, each +* broken part is represented by the SubItem class. This +* class hold information such as the original items it originated +* from and the paths that the SubItem consists of. +**/ +class SubItem +{ public: - /** - * When an item from the original ObjectSet is broken, each - * broken part is represented by the SubItem class. This - * class hold information such as the original items it originated - * from and the paths that the SubItem consists of. - **/ - class SubItem - { - public: + Geom::PathVector paths; + std::set items; + SPItem *top_item; - Geom::PathVector paths; - std::set items; - SPItem *top_item; + SubItem() {} - SubItem() {} + SubItem(Geom::PathVector paths, std::set items, SPItem *top_item) + : paths(std::move(paths)) + , items(std::move(items)) + , top_item(top_item) + {} - SubItem(Geom::PathVector paths, std::set items, SPItem *top_item) - : paths(std::move(paths)) - , items(std::move(items)) - , top_item(top_item) - {} - - SubItem(Geom::PathVector paths, const std::set &items1, const std::set &items2, SPItem *top_item) - : SubItem(paths, items1, top_item) - { - items.insert(items2.begin(), items2.end()); - } + SubItem(Geom::PathVector paths, const std::set &items1, const std::set &items2, SPItem *top_item) + : SubItem(paths, items1, top_item) + { + items.insert(items2.begin(), items2.end()); + } - bool is_subitem_of(SPItem* item) const { return items.find(item) != items.end(); } + bool is_subitem_of(SPItem* item) const { return items.find(item) != items.end(); } - SPItem* get_common_item(const SubItem &other_subitem) const; - SPItem* get_top_item() const; - bool is_virgin() const; - std::vector fracture(const SubItem &other_subitem); + SPItem* get_common_item(const SubItem &other_subitem) const; + SPItem* get_top_item() const; + bool is_virgin() const; + std::vector fracture(const SubItem &other_subitem); - bool operator<(const SubItem &other) const; - }; + bool operator<(const SubItem &other) const; }; } \ No newline at end of file diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 3b40f9ecb0..4fcc2ff969 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -496,6 +496,16 @@ bool BuilderTool::event_key_release_handler(GdkEvent *event) void BuilderTool::perform_operation(Selection *selection, int mode) { int size = selection->size(); + + if (in_interactive_mode) { + if (mode == SELECT_AND_UNION && size > 1) { + shapes_builder.set_union(selection); + } else if (mode == SELECT_AND_DELETE) { + shapes_builder.set_delete(selection); + } + return; + } + if (mode != JUST_SELECT && size > 1) { if (mode == SELECT_AND_UNION) { selection->pathUnion(); @@ -596,6 +606,19 @@ bool BuilderTool::is_mode_add_to_selection(int mode, GdkEvent *event) return mode == JUST_SELECT && Modifier::get(Modifiers::Type::SELECT_ADD_TO)->active(event->button.state); } +void BuilderTool::start_interactive_mode() +{ + in_interactive_mode = true; + Inkscape::Selection *selection = desktop->getSelection(); + shapes_builder.start(selection); +} + +void BuilderTool::end_interactive_mode() +{ + in_interactive_mode = false; + shapes_builder.done(); +} + } } } diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index de1095238f..d63610de3a 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -14,6 +14,7 @@ #pragma once #include "ui/tools/tool-base.h" +#include "helper/InteractiveShapesBuilder.h" #define SP_SELECT_CONTEXT(obj) (dynamic_cast((Inkscape::UI::Tools::ToolBase*)obj)) #define SP_IS_SELECT_CONTEXT(obj) (dynamic_cast((const Inkscape::UI::Tools::ToolBase*)obj) != NULL) @@ -65,6 +66,9 @@ public: void set_current_mode(GdkEvent* event); void set_current_mode(int current_mode = -1); + void start_interactive_mode(); + void end_interactive_mode(); + private: bool sp_select_context_abort(); @@ -91,6 +95,9 @@ private: static const std::vector mode_colors; static const std::map handlers; + bool in_interactive_mode = false; + InteractiveShapesBuilder shapes_builder; + int active_mode = JUST_SELECT; // default to the select mode since this is the default cursor. bool ctrl_on = false; bool alt_on = false; -- GitLab From 7bf06c3c8abeb3b0a5cdcc38e6154888b7a96214 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 05:56:20 +0200 Subject: [PATCH 171/235] Added the BuilderTool::toggle_interactive_mode method. --- src/ui/tools/builder-tool.cpp | 9 +++++++++ src/ui/tools/builder-tool.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 4fcc2ff969..e5867b1e5f 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -619,6 +619,15 @@ void BuilderTool::end_interactive_mode() shapes_builder.done(); } +void BuilderTool::toggle_interactive_mode() +{ + if (in_interactive_mode) { + end_interactive_mode(); + } else { + start_interactive_mode(); + } +} + } } } diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index d63610de3a..4bbc640831 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -68,6 +68,7 @@ public: void start_interactive_mode(); void end_interactive_mode(); + void toggle_interactive_mode(); private: -- GitLab From d04a5af1359b210b768708709480d078c3469e9d Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 06:57:54 +0200 Subject: [PATCH 172/235] Added a button for toggling the Interactive Builder mode in the Builder Toolbar. --- .../scalable/actions/interactive-builder.svg | 132 ++++++++++++++++++ .../scalable/actions/interactive-builder.svg | 50 +++++++ .../actions/interactive-builder-symbolic.svg | 80 +++++++++++ .../actions/interactive-builder-symbolic.svg | 113 +++++++++++++++ src/ui/toolbar/builder-toolbar.cpp | 37 +++++ src/ui/toolbar/builder-toolbar.h | 4 + 6 files changed, 416 insertions(+) create mode 100644 share/icons/Tango/scalable/actions/interactive-builder.svg create mode 100644 share/icons/hicolor/scalable/actions/interactive-builder.svg create mode 100644 share/icons/hicolor/symbolic/actions/interactive-builder-symbolic.svg create mode 100644 share/icons/multicolor/symbolic/actions/interactive-builder-symbolic.svg diff --git a/share/icons/Tango/scalable/actions/interactive-builder.svg b/share/icons/Tango/scalable/actions/interactive-builder.svg new file mode 100644 index 0000000000..9ff8259147 --- /dev/null +++ b/share/icons/Tango/scalable/actions/interactive-builder.svg @@ -0,0 +1,132 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/scalable/actions/interactive-builder.svg b/share/icons/hicolor/scalable/actions/interactive-builder.svg new file mode 100644 index 0000000000..01252829f6 --- /dev/null +++ b/share/icons/hicolor/scalable/actions/interactive-builder.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/symbolic/actions/interactive-builder-symbolic.svg b/share/icons/hicolor/symbolic/actions/interactive-builder-symbolic.svg new file mode 100644 index 0000000000..88c6401f2f --- /dev/null +++ b/share/icons/hicolor/symbolic/actions/interactive-builder-symbolic.svg @@ -0,0 +1,80 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/share/icons/multicolor/symbolic/actions/interactive-builder-symbolic.svg b/share/icons/multicolor/symbolic/actions/interactive-builder-symbolic.svg new file mode 100644 index 0000000000..69ebff2861 --- /dev/null +++ b/share/icons/multicolor/symbolic/actions/interactive-builder-symbolic.svg @@ -0,0 +1,113 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/src/ui/toolbar/builder-toolbar.cpp b/src/ui/toolbar/builder-toolbar.cpp index 9d88b5d29a..20daa9256c 100644 --- a/src/ui/toolbar/builder-toolbar.cpp +++ b/src/ui/toolbar/builder-toolbar.cpp @@ -54,6 +54,8 @@ BuilderToolbar::BuilderToolbar(SPDesktop *desktop) : boolop_buttons_init(); add_separator(); compound_operations_buttons_init(); + add_separator(); + interactive_mode_buttons_init(); show_all(); } @@ -262,6 +264,41 @@ void BuilderToolbar::compound_operations_buttons_init_verbs() add_toolbutton_for_verb(SP_VERB_SELECTION_BREAK_APART); } +void BuilderToolbar::interactive_mode_buttons_init() +{ + // TODO find a better way than this. Using verbs is easier. + const static std::vector interactive_mode_buttons_descriptors = { + { + .label = _("Interactive Mode"), + .tooltip_text = _("Start/End interactive mode"), + .icon_name = "interactive-builder", + .handler = &BuilderToolbar::perform_toggle_interactive_mode, + }, + }; + + interactive_mode_buttons_init_add_buttons(interactive_mode_buttons_descriptors); +} + +void BuilderToolbar::interactive_mode_buttons_init_add_buttons(const std::vector &descriptors) +{ + for (auto&descriptor : descriptors) + { + auto button = Gtk::manage(new Gtk::ToolButton(descriptor.label)); + button->set_tooltip_text((descriptor.tooltip_text)); + button->set_icon_name(INKSCAPE_ICON(descriptor.icon_name)); + button->signal_clicked().connect(sigc::mem_fun(*this, descriptor.handler)); + add(*button); + } +} + +void BuilderToolbar::perform_toggle_interactive_mode() +{ + auto builder_tool = dynamic_cast(_desktop->event_context); + if (builder_tool) { + builder_tool->toggle_interactive_mode(); + } +} + void BuilderToolbar::add_separator() { add(* Gtk::manage(new Gtk::SeparatorToolItem())); diff --git a/src/ui/toolbar/builder-toolbar.h b/src/ui/toolbar/builder-toolbar.h index 726a84981e..9f23f7181e 100644 --- a/src/ui/toolbar/builder-toolbar.h +++ b/src/ui/toolbar/builder-toolbar.h @@ -79,6 +79,10 @@ private: void compound_operations_buttons_init_verbs(); void perform_split_non_intersecting(); + void interactive_mode_buttons_init(); + void interactive_mode_buttons_init_add_buttons(const std::vector &descriptors); + void perform_toggle_interactive_mode(); + void add_separator(); protected: -- GitLab From e337ecd75ab6bc8c1de9b1947189dd58dd07e844 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 16:22:44 +0200 Subject: [PATCH 173/235] Set the InteractiveShapesBuilder to clear the selection when starting. --- src/helper/InteractiveShapesBuilder.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 9173396595..997014791a 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -48,6 +48,8 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) add_disabled_node(nodes[i], subitems[i]); } + set->clear(); + started = true; } -- GitLab From fc4dec57221310b5ebb7e02f515db36658aa500e Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 16:30:41 +0200 Subject: [PATCH 174/235] Set the original items to be hidden after the creation of the sub-items. --- src/helper/InteractiveShapesBuilder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 997014791a..76ecdd16a5 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -34,7 +34,6 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) } original_items = std::vector(set->items().begin(), set->items().end()); - hide_original_items(); NonIntersectingPathsBuilder builder(set); builder.perform_fracture(); @@ -48,8 +47,9 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) add_disabled_node(nodes[i], subitems[i]); } + hide_original_items(); set->clear(); - + started = true; } -- GitLab From 2eeb0e71315da5e2f2c091c770b7e8adaa1c0fcb Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 17:18:49 +0200 Subject: [PATCH 175/235] Set the assignment of the original_items to be after the manipulations of the NonIntersectingPathsBuilder. --- src/helper/InteractiveShapesBuilder.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 76ecdd16a5..00ca62f4f8 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -33,8 +33,6 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) reset(); } - original_items = std::vector(set->items().begin(), set->items().end()); - NonIntersectingPathsBuilder builder(set); builder.perform_fracture(); builder.show_output(false); @@ -47,6 +45,7 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) add_disabled_node(nodes[i], subitems[i]); } + original_items = std::vector(set->items().begin(), set->items().end()); hide_original_items(); set->clear(); -- GitLab From f5693d6b1168e01f4d76348f6a179fa2630634e0 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 17:49:46 +0200 Subject: [PATCH 176/235] Split the InteractiveShapesBuilder::set_union method into 2 methods so that some code gets reused in set_delete method later. --- src/helper/InteractiveShapesBuilder.cpp | 29 +++++++++++++++---------- src/helper/InteractiveShapesBuilder.h | 2 ++ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 00ca62f4f8..76368d1d14 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -52,32 +52,28 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) started = true; } -void InteractiveShapesBuilder::set_union(ObjectSet *set) +SubItem InteractiveShapesBuilder::get_union_subitem(const std::vector &items) { - std::vector items(set->items().begin(), set->items().end()); - SubItem res_subitem; res_subitem.paths = items.front()->get_pathvector(); res_subitem.top_item = items.front(); - for (int i = 1; i < items.size(); i++) + for (auto item : items) { - auto item = items[i]; + res_subitem.paths = sp_pathvector_boolop(res_subitem.paths, item->get_pathvector(), bool_op_union, fill_nonZero, fill_nonZero); + auto repr = item->getRepr(); auto subitem_it = enabled.find(repr); if (subitem_it == enabled.end()) { subitem_it = disabled.find(repr); if (subitem_it == disabled.end()) { - std::cerr << "InteractiveShapesBuilder: Unioning an to_copy_style_form outside of the selection.\n"; - return; + continue; } } SubItem &subitem = subitem_it->second; res_subitem.items.insert(subitem.items.begin(), subitem.items.end()); - - res_subitem.paths = sp_pathvector_boolop(res_subitem.paths, item->get_pathvector(), bool_op_union, fill_nonZero, fill_nonZero); } for (auto item : items) { @@ -86,12 +82,21 @@ void InteractiveShapesBuilder::set_union(ObjectSet *set) remove_disabled_node(repr); } - auto to_copy_style_form = res_subitem.top_item; + return res_subitem; +} + +void InteractiveShapesBuilder::set_union(ObjectSet *set) +{ + std::vector items(set->items().begin(), set->items().end()); + + SubItem subitem = get_union_subitem(items); + + auto to_copy_style_form = subitem.top_item; auto after = to_copy_style_form->getRepr(); auto parent = after->parent(); - auto node = draw_on_canvas(res_subitem.paths, to_copy_style_form, parent, after); - add_enabled_node(node, res_subitem); + auto node = draw_on_canvas(subitem.paths, to_copy_style_form, parent, after); + add_enabled_node(node, subitem); for (auto item : items) { // TODO make sure this works. diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index 39620aec0f..b051fbf58f 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -59,6 +59,8 @@ private: void reset(); + SubItem get_union_subitem(const std::vector &items); + public: void start(ObjectSet *set); -- GitLab From 0489b5fe31a4abfd43d9af6e01f2511e39912e34 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 17:54:56 +0200 Subject: [PATCH 177/235] Set the set_delete method to union the selection and add them to the enabled items without drawing the union result. --- src/helper/InteractiveShapesBuilder.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 76368d1d14..4a8847b6c1 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -111,11 +111,14 @@ void InteractiveShapesBuilder::set_union(ObjectSet *set) void InteractiveShapesBuilder::set_delete(ObjectSet *set) { std::vector items(set->items().begin(), set->items().end()); - for (auto item : items) { - auto repr = item->getRepr(); - remove_enabled_node(repr); - remove_disabled_node(repr); + // TODO quick hack. change it later. + static XML::Node *place_holder = 0x0; + + auto subitem = get_union_subitem(items); + enabled[place_holder++] = subitem; + + for (auto item : items) { // TODO make sure this works. sp_object_ref(item, nullptr); item->deleteObject(true, true); -- GitLab From 40415b0a1bb1721156d1e2ecf05e7af92260c08c Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 19:00:27 +0200 Subject: [PATCH 178/235] Set the deletion of the original items to be after the drawing of all of the items. --- src/helper/InteractiveShapesBuilder.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 4a8847b6c1..b5771afa6b 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -156,7 +156,10 @@ void InteractiveShapesBuilder::done() auto item = item_it.first; auto &pathvec = item_it.second; draw_on_canvas(pathvec, item, parent, after); + } + for (auto item_it : original_paths) { + auto item = item_it.first; // deleting the original item. // TODO make sure this works. sp_object_ref(item, nullptr); -- GitLab From 598b45415f491640c24408eab0e92318f2d131ee Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 20:02:48 +0200 Subject: [PATCH 179/235] Fixed the order of the difference operation in InteractiveShapesBuilder::done. --- src/helper/InteractiveShapesBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index b5771afa6b..3fc46e6c9c 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -144,7 +144,7 @@ void InteractiveShapesBuilder::done() std::cerr << "InteractiveShapesBuilder: No Geom::PathVector is for the item " << item << ".\n"; continue; } - original_paths[item] = sp_pathvector_boolop(paths_it->second, subitem.paths, bool_op_diff, fill_nonZero, fill_nonZero); + original_paths[item] = sp_pathvector_boolop(subitem.paths, paths_it->second, bool_op_diff, fill_nonZero, fill_nonZero); } } -- GitLab From 82254662fc6957d989d17d295d3928e6499b162f Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 20:20:48 +0200 Subject: [PATCH 180/235] Set the InteractiveShapesBuilder to reset when destructed. --- src/helper/InteractiveShapesBuilder.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index b051fbf58f..eb4e734d9c 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -68,6 +68,7 @@ public: void set_delete(ObjectSet *set); void done(); + ~InteractiveShapesBuilder() { reset(); } }; } \ No newline at end of file -- GitLab From 65c70c71794fdcdd7bf26fcdd3b0fcd0d2d0c3e9 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 20:43:14 +0200 Subject: [PATCH 181/235] Set the InteractiveShapesBuilder::done to clean the containers after it finishes. --- src/helper/InteractiveShapesBuilder.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 3fc46e6c9c..03ef82bf72 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -168,6 +168,10 @@ void InteractiveShapesBuilder::done() } started = false; + enabled.clear(); + disabled.clear(); + original_items.clear(); + original_opacity.clear(); } void InteractiveShapesBuilder::add_disabled_node(XML::Node *node, const SubItem &subitem) -- GitLab From 4083071ba203d37a8e03679cb3f174cf5d09446f Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 20:56:31 +0200 Subject: [PATCH 182/235] Set the InteractiveShapesBuilder::done method to remove the disabled objects as well. --- src/helper/InteractiveShapesBuilder.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 03ef82bf72..2f1890e0e7 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -158,15 +158,21 @@ void InteractiveShapesBuilder::done() draw_on_canvas(pathvec, item, parent, after); } - for (auto item_it : original_paths) { - auto item = item_it.first; - // deleting the original item. + for (auto item : original_items) { // TODO make sure this works. sp_object_ref(item, nullptr); item->deleteObject(true, true); sp_object_unref(item, nullptr); } + for (auto node : disabled) { + auto object = document->getObjectByRepr(node.first); + // TODO make sure this works. + sp_object_ref(object, nullptr); + object->deleteObject(true, true); + sp_object_unref(object, nullptr); + } + started = false; enabled.clear(); disabled.clear(); -- GitLab From 26857f1cb89adb906e2395f57e010171a81843ea Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Mon, 26 Jul 2021 21:16:27 +0200 Subject: [PATCH 183/235] Set the InteractiveShapesBuilder::done method to split the items before drawing it. --- src/helper/InteractiveShapesBuilder.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 2f1890e0e7..b5bdd9ddc5 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -155,7 +155,9 @@ void InteractiveShapesBuilder::done() for (auto item_it : original_paths) { auto item = item_it.first; auto &pathvec = item_it.second; - draw_on_canvas(pathvec, item, parent, after); + for (auto sub_pathvec : split_non_intersecting_paths(pathvec)) { + draw_on_canvas(sub_pathvec, item, parent, after); + } } for (auto item : original_items) { -- GitLab From bdb358273ddec6397c6fe9406a03f614656652fa Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 30 Jul 2021 15:16:16 +0200 Subject: [PATCH 184/235] Set the Interactive mode to not start if none of the selected shapes intersect. --- src/helper/InteractiveShapesBuilder.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index b5bdd9ddc5..474b0708e5 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -35,10 +35,15 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) NonIntersectingPathsBuilder builder(set); builder.perform_fracture(); + + auto &subitems = builder.get_result_subitems(); + if (subitems.size() == set->size()) { + return; // Noting intersects + } + builder.show_output(false); auto &nodes = builder.get_result_nodes(); - auto &subitems = builder.get_result_subitems(); int n = nodes.size(); for (int i = 0; i < n; i++) { @@ -130,6 +135,10 @@ void InteractiveShapesBuilder::set_delete(ObjectSet *set) void InteractiveShapesBuilder::done() { + if (!started) { + return; + } + std::map original_paths; for (auto item : original_items) { original_paths[item] = item->get_pathvector(); -- GitLab From 224b244cc78ced38dd90e65c99b4eecae4849665 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 30 Jul 2021 15:16:50 +0200 Subject: [PATCH 185/235] Added the function setSubAttribute to the useful functions. --- src/helper/useful-functions.cpp | 15 +++++++++++++++ src/helper/useful-functions.h | 2 ++ 2 files changed, 17 insertions(+) diff --git a/src/helper/useful-functions.cpp b/src/helper/useful-functions.cpp index 00d94230ec..45f5f4a5f6 100644 --- a/src/helper/useful-functions.cpp +++ b/src/helper/useful-functions.cpp @@ -121,4 +121,19 @@ XML::Node *draw_on_canvas(const Geom::PathVector &path, const SPItem *to_copy_fr return repr; } +std::string setSubAttribute(const std::string &str, std::string attr, const std::string &value) +{ + attr += ":"; + int start_idx = str.find(attr, 0); + if (start_idx == std::string::npos) { + return str + ";" + attr + value; + } + + start_idx += attr.size(); // including the "=". + int end_idx = start_idx; + while (end_idx < str.size() && str[end_idx] != ';') end_idx++; + + return str.substr(0, start_idx) + value + str.substr(end_idx, str.size() - end_idx); +} + } \ No newline at end of file diff --git a/src/helper/useful-functions.h b/src/helper/useful-functions.h index f4dd9d2609..56b2a7d687 100644 --- a/src/helper/useful-functions.h +++ b/src/helper/useful-functions.h @@ -38,4 +38,6 @@ bool is_intersecting(const Path1 &a, const Path2 &b); // in both a header and a source file. this is why it's not being done for now. XML::Node *draw_on_canvas(const Geom::PathVector &path, const SPItem *to_copy_from, XML::Node *parent, XML::Node *after = nullptr); +std::string setSubAttribute(const std::string &str, std::string attr, const std::string &value); + } \ No newline at end of file -- GitLab From 0192cbcf918ee9285f9b8cb2f2680b63c620cbe2 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 30 Jul 2021 15:18:02 +0200 Subject: [PATCH 186/235] Set the InteractiveShapesBuilder to get/set the whole style attribute instead of trying to only change the opacity. --- src/helper/InteractiveShapesBuilder.cpp | 22 ++++++++-------------- src/helper/InteractiveShapesBuilder.h | 2 +- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 474b0708e5..41bc602bb4 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -188,7 +188,7 @@ void InteractiveShapesBuilder::done() enabled.clear(); disabled.clear(); original_items.clear(); - original_opacity.clear(); + original_styles.clear(); } void InteractiveShapesBuilder::add_disabled_node(XML::Node *node, const SubItem &subitem) @@ -225,26 +225,20 @@ void InteractiveShapesBuilder::remove_enabled_node(XML::Node *node) void InteractiveShapesBuilder::set_style_disabled(XML::Node *node) { - auto item = dynamic_cast(document->getObjectByRepr(node)); - if (item) { - Inkscape::DrawingItem *arenaitem = item->get_arenaitem(desktop->dkey); - original_opacity[node] = SP_SCALE24_TO_FLOAT(item->style->opacity.value); - arenaitem->setOpacity(0.5); - } + std::string original_style = node->attribute("style"); + original_styles[node] = original_style; + std::string new_style = setSubAttribute(original_style, "opacity", "0.5"); + node->setAttribute("style", new_style); } void InteractiveShapesBuilder::restore_original_style(XML::Node *node) { - auto opacity = original_opacity.find(node); - if (opacity == original_opacity.end()) { + auto style = original_styles.find(node); + if (style == original_styles.end()) { // std::cerr << "InteractiveShapeBuilder: The node " << node << " doesn't have its original style stored.\n"; return; } - auto item = dynamic_cast(document->getObjectByRepr(node)); - if (item) { - Inkscape::DrawingItem *arenaitem = item->get_arenaitem(desktop->dkey); - arenaitem->setOpacity(opacity->second); - } + node->setAttribute("style", style->second); } void InteractiveShapesBuilder::hide_original_items() diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index eb4e734d9c..ae3db31a05 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -41,7 +41,7 @@ private: std::map enabled; std::map disabled; - std::map original_opacity; + std::map original_styles; bool started = false; -- GitLab From 8661339889bd874f2a3d9fbd17e839c56d4cf210 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 30 Jul 2021 15:26:45 +0200 Subject: [PATCH 187/235] Refactored the deletion of object into a function called delete_object. --- src/helper/InteractiveShapesBuilder.cpp | 39 +++++++++---------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 41bc602bb4..398c5c8243 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -23,6 +23,13 @@ namespace Inkscape { +void delete_object(SPObject* item) +{ + sp_object_ref(item, nullptr); + item->deleteObject(true, true); + sp_object_unref(item, nullptr); +} + void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) { desktop = set->desktop(); @@ -104,10 +111,7 @@ void InteractiveShapesBuilder::set_union(ObjectSet *set) add_enabled_node(node, subitem); for (auto item : items) { - // TODO make sure this works. - sp_object_ref(item, nullptr); - item->deleteObject(true, true); - sp_object_unref(item, nullptr); + delete_object(item); } DocumentUndo::done(set->document(), "Interactive Union", INKSCAPE_ICON("path-union")); @@ -124,10 +128,7 @@ void InteractiveShapesBuilder::set_delete(ObjectSet *set) enabled[place_holder++] = subitem; for (auto item : items) { - // TODO make sure this works. - sp_object_ref(item, nullptr); - item->deleteObject(true, true); - sp_object_unref(item, nullptr); + delete_object(item); } DocumentUndo::done(set->document(), "Interactive Delete", INKSCAPE_ICON("edit-delete")); @@ -170,18 +171,12 @@ void InteractiveShapesBuilder::done() } for (auto item : original_items) { - // TODO make sure this works. - sp_object_ref(item, nullptr); - item->deleteObject(true, true); - sp_object_unref(item, nullptr); + delete_object(item); } for (auto node : disabled) { auto object = document->getObjectByRepr(node.first); - // TODO make sure this works. - sp_object_ref(object, nullptr); - object->deleteObject(true, true); - sp_object_unref(object, nullptr); + delete_object(object); } started = false; @@ -259,20 +254,12 @@ void InteractiveShapesBuilder::reset() { for (auto node : enabled) { auto object = document->getObjectByRepr(node.first); - - // TODO make sure this works. - sp_object_ref(object, nullptr); - object->deleteObject(true, true); - sp_object_unref(object, nullptr); + delete_object(object); } for (auto node : disabled) { auto object = document->getObjectByRepr(node.first); - - // TODO make sure this works. - sp_object_ref(object, nullptr); - object->deleteObject(true, true); - sp_object_unref(object, nullptr); + delete_object(object); } show_original_items(); -- GitLab From 50029d6e0616cc7b971da35eedc996896ef12ed8 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 30 Jul 2021 18:33:21 +0200 Subject: [PATCH 188/235] Renamed the done() method to commit(). --- src/helper/InteractiveShapesBuilder.cpp | 2 +- src/helper/InteractiveShapesBuilder.h | 2 +- src/ui/tools/builder-tool.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 398c5c8243..136cc16a80 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -134,7 +134,7 @@ void InteractiveShapesBuilder::set_delete(ObjectSet *set) DocumentUndo::done(set->document(), "Interactive Delete", INKSCAPE_ICON("edit-delete")); } -void InteractiveShapesBuilder::done() +void InteractiveShapesBuilder::commit() { if (!started) { return; diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index ae3db31a05..babbb236f0 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -66,7 +66,7 @@ public: void start(ObjectSet *set); void set_union(ObjectSet *set); void set_delete(ObjectSet *set); - void done(); + void commit(); ~InteractiveShapesBuilder() { reset(); } }; diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index e5867b1e5f..2855e4d373 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -616,7 +616,7 @@ void BuilderTool::start_interactive_mode() void BuilderTool::end_interactive_mode() { in_interactive_mode = false; - shapes_builder.done(); + shapes_builder.commit(); } void BuilderTool::toggle_interactive_mode() -- GitLab From e9f88945c2af1a7d1ea90b1025e722e8848d443f Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 30 Jul 2021 18:45:26 +0200 Subject: [PATCH 189/235] Set the union and delete in the InteractiveShapesBuilder to not record in the history. --- src/helper/InteractiveShapesBuilder.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 136cc16a80..a5320506df 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -113,8 +113,6 @@ void InteractiveShapesBuilder::set_union(ObjectSet *set) for (auto item : items) { delete_object(item); } - - DocumentUndo::done(set->document(), "Interactive Union", INKSCAPE_ICON("path-union")); } void InteractiveShapesBuilder::set_delete(ObjectSet *set) @@ -130,8 +128,6 @@ void InteractiveShapesBuilder::set_delete(ObjectSet *set) for (auto item : items) { delete_object(item); } - - DocumentUndo::done(set->document(), "Interactive Delete", INKSCAPE_ICON("edit-delete")); } void InteractiveShapesBuilder::commit() -- GitLab From 796bf31b6103df7df53bc8ab7d617889b9466591 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 30 Jul 2021 18:50:20 +0200 Subject: [PATCH 190/235] Set the BuilderTool to commit the changes if the active tool is switched while in the interactive mode. --- src/helper/InteractiveShapesBuilder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index babbb236f0..7d3a8ccc5a 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -68,7 +68,7 @@ public: void set_delete(ObjectSet *set); void commit(); - ~InteractiveShapesBuilder() { reset(); } + ~InteractiveShapesBuilder() { commit(); } }; } \ No newline at end of file -- GitLab From efb6217c9c18ce82077633b231e07e506f5678f4 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 30 Jul 2021 18:53:22 +0200 Subject: [PATCH 191/235] Set the InteractiveShapesBuilder to commit instead of resetting in case of a double-start. --- src/helper/InteractiveShapesBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index a5320506df..97e00ec171 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -37,7 +37,7 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) if (started) { std::cerr << "InteractiveShapesBuilder: already started. Resetting before starting again.\n"; - reset(); + commit(); } NonIntersectingPathsBuilder builder(set); -- GitLab From 03c065831e623efddb93094ca9cb5c9ac4d22757 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 30 Jul 2021 19:09:57 +0200 Subject: [PATCH 192/235] Set the InteractiveShapesBuilder to record in the history when the comnmit operation is done. --- src/helper/InteractiveShapesBuilder.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 97e00ec171..8f115c7828 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -20,6 +20,7 @@ #include "document.h" #include "object/sp-item.h" #include "useful-functions.h" +#include "selection.h" namespace Inkscape { @@ -180,6 +181,9 @@ void InteractiveShapesBuilder::commit() disabled.clear(); original_items.clear(); original_styles.clear(); + + // FIXME for some reason, this is not working properly. + DocumentUndo::done(document, "Interactive Mode", INKSCAPE_ICON("interactive-builder")); } void InteractiveShapesBuilder::add_disabled_node(XML::Node *node, const SubItem &subitem) -- GitLab From 2336a222fe73321aec9fa7ecb6631ce2097a58a6 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 30 Jul 2021 19:18:13 +0200 Subject: [PATCH 193/235] Added a member variable called is_virgin in the InteractiveShapesBuilder. If is virgin, nothing will be recorded in the history. --- src/helper/InteractiveShapesBuilder.cpp | 11 +++++++++-- src/helper/InteractiveShapesBuilder.h | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 8f115c7828..426c9f14a0 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -63,6 +63,7 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) set->clear(); started = true; + is_virgin = true; } SubItem InteractiveShapesBuilder::get_union_subitem(const std::vector &items) @@ -114,6 +115,8 @@ void InteractiveShapesBuilder::set_union(ObjectSet *set) for (auto item : items) { delete_object(item); } + + is_virgin = false; } void InteractiveShapesBuilder::set_delete(ObjectSet *set) @@ -129,6 +132,8 @@ void InteractiveShapesBuilder::set_delete(ObjectSet *set) for (auto item : items) { delete_object(item); } + + is_virgin = false; } void InteractiveShapesBuilder::commit() @@ -182,8 +187,10 @@ void InteractiveShapesBuilder::commit() original_items.clear(); original_styles.clear(); - // FIXME for some reason, this is not working properly. - DocumentUndo::done(document, "Interactive Mode", INKSCAPE_ICON("interactive-builder")); + if (!is_virgin) { + // FIXME for some reason, this is not working properly. + DocumentUndo::done(document, "Interactive Mode", INKSCAPE_ICON("interactive-builder")); + } } void InteractiveShapesBuilder::add_disabled_node(XML::Node *node, const SubItem &subitem) diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index 7d3a8ccc5a..889b4fc5c4 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -44,6 +44,7 @@ private: std::map original_styles; bool started = false; + bool is_virgin = true; void add_disabled_node(XML::Node *node, const SubItem &subitem); void remove_disabled_node(XML::Node *node); -- GitLab From 06cb98f02e401607db7e87affa7ad7da429c7ebc Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 30 Jul 2021 19:36:48 +0200 Subject: [PATCH 194/235] Introduced the reset_internals() method in the InteractiveShapesBuilder and used it in the commit method. --- src/helper/InteractiveShapesBuilder.cpp | 39 ++++++++++++++----------- src/helper/InteractiveShapesBuilder.h | 1 + 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 426c9f14a0..c475ca1594 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -142,6 +142,10 @@ void InteractiveShapesBuilder::commit() return; } + if (is_virgin) { + return reset(); + } + std::map original_paths; for (auto item : original_items) { original_paths[item] = item->get_pathvector(); @@ -164,6 +168,7 @@ void InteractiveShapesBuilder::commit() auto parent = after->parent(); show_original_items(); + for (auto item_it : original_paths) { auto item = item_it.first; auto &pathvec = item_it.second; @@ -176,21 +181,10 @@ void InteractiveShapesBuilder::commit() delete_object(item); } - for (auto node : disabled) { - auto object = document->getObjectByRepr(node.first); - delete_object(object); - } + reset_internals(); - started = false; - enabled.clear(); - disabled.clear(); - original_items.clear(); - original_styles.clear(); - - if (!is_virgin) { - // FIXME for some reason, this is not working properly. - DocumentUndo::done(document, "Interactive Mode", INKSCAPE_ICON("interactive-builder")); - } + // FIXME for some reason, this is not working properly. + DocumentUndo::done(document, "Interactive Mode", INKSCAPE_ICON("interactive-builder")); } void InteractiveShapesBuilder::add_disabled_node(XML::Node *node, const SubItem &subitem) @@ -257,19 +251,30 @@ void InteractiveShapesBuilder::show_original_items() } } -void InteractiveShapesBuilder::reset() +void InteractiveShapesBuilder::reset_internals() { - for (auto node : enabled) { + for (auto node : disabled) { auto object = document->getObjectByRepr(node.first); delete_object(object); } - for (auto node : disabled) { + started = false; + is_virgin = true; + enabled.clear(); + disabled.clear(); + original_items.clear(); + original_styles.clear(); +} + +void InteractiveShapesBuilder::reset() +{ + for (auto node : enabled) { auto object = document->getObjectByRepr(node.first); delete_object(object); } show_original_items(); + reset_internals(); } } \ No newline at end of file diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index 889b4fc5c4..4801bdb2ce 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -58,6 +58,7 @@ private: void hide_original_items(); void show_original_items(); + void reset_internals(); void reset(); SubItem get_union_subitem(const std::vector &items); -- GitLab From 06ff4ddf48bd9d519cea609d0566e5ec5f7c4053 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 30 Jul 2021 19:52:32 +0200 Subject: [PATCH 195/235] Added is_started() method in the InteractiveShapesBuilder. --- src/helper/InteractiveShapesBuilder.cpp | 9 +++++++-- src/helper/InteractiveShapesBuilder.h | 4 +++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index c475ca1594..d0682ce667 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -31,12 +31,17 @@ void delete_object(SPObject* item) sp_object_unref(item, nullptr); } +bool InteractiveShapesBuilder::is_started() const +{ + return started; +} + void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) { desktop = set->desktop(); document = set->document(); - if (started) { + if (is_started()) { std::cerr << "InteractiveShapesBuilder: already started. Resetting before starting again.\n"; commit(); } @@ -138,7 +143,7 @@ void InteractiveShapesBuilder::set_delete(ObjectSet *set) void InteractiveShapesBuilder::commit() { - if (!started) { + if (!is_started()) { return; } diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index 4801bdb2ce..c9ccde14a5 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -65,10 +65,12 @@ private: public: + bool is_started() const; void start(ObjectSet *set); + void commit(); + void set_union(ObjectSet *set); void set_delete(ObjectSet *set); - void commit(); ~InteractiveShapesBuilder() { commit(); } }; -- GitLab From 494b6f0f86b3d35c55f1df43525f331856c1d558 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 30 Jul 2021 20:09:20 +0200 Subject: [PATCH 196/235] Set the interactive mood to start once the Builder tool is selected. --- src/helper/InteractiveShapesBuilder.h | 4 ++++ src/ui/tools/builder-tool.cpp | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index c9ccde14a5..5fde1a571c 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -32,6 +32,10 @@ namespace XML { class InteractiveShapesBuilder { + // FIXME find a way to keep references to items on the canvas. right now + // the program crashes if the items being used here are removed (or + // replaced) by any other operation other than the ones this tool supports. + private: SPDesktop *desktop; diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 2855e4d373..e51cd17b20 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -158,6 +158,8 @@ void BuilderTool::setup() { } set_current_mode(); + + start_interactive_mode(); } void BuilderTool::set(const Inkscape::Preferences::Entry& val) { -- GitLab From 197533fbeca53769d11a8f3c1709321ec4e63145 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 31 Jul 2021 02:07:26 +0200 Subject: [PATCH 197/235] Added the concept of being activated or deactivated to the ObjectSet class. If deactivated, noting will be added to the selection. --- src/object/object-set.cpp | 18 ++++++++++++++++++ src/object/object-set.h | 5 +++++ 2 files changed, 23 insertions(+) diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 3e056c1919..d75a010abd 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -142,6 +142,9 @@ void ObjectSet::_remove(SPObject *object) { } void ObjectSet::_add(SPObject *object) { + if (!is_active()) { + return; + } _releaseConnections[object] = object->connectRelease(sigc::hide_return(sigc::mem_fun(*this, &ObjectSet::remove))); _container.push_back(object); _add3DBoxesRecursively(object); @@ -582,6 +585,21 @@ void ObjectSet::splitNonIntersecting(bool skip_undo) } } +void ObjectSet::activate() +{ + _is_active = true; +} + +void ObjectSet::deactivate() +{ + _is_active = false; +} + +bool ObjectSet::is_active() const +{ + return _is_active; +} + } // namespace Inkscape /* diff --git a/src/object/object-set.h b/src/object/object-set.h index 26f13c5e6d..273fcec3dc 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -473,6 +473,10 @@ public: void flatten(bool skip_undo = false); void splitNonIntersecting(bool skip_undo = false); + void activate(); + void deactivate(); + bool is_active() const; + protected: virtual void _connectSignals(SPObject* object) {}; virtual void _releaseSignals(SPObject* object) {}; @@ -488,6 +492,7 @@ protected: virtual void _add3DBoxesRecursively(SPObject *obj); virtual void _remove3DBoxesRecursively(SPObject *obj); + bool _is_active = true; MultiIndexContainer _container; GC::soft_ptr _desktop; GC::soft_ptr _document; -- GitLab From 626a09ac4ed890cb18a6c927c28eb96af4161c8d Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 31 Jul 2021 02:30:06 +0200 Subject: [PATCH 198/235] Set the selection to be deactivated when in the interactive mode to avoid problems. --- src/ui/tools/builder-tool.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index e51cd17b20..b72fc7e93f 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -128,6 +128,8 @@ BuilderTool::~BuilderTool() { } forced_redraws_stop(); + + end_interactive_mode(); } void BuilderTool::setup() { @@ -175,6 +177,9 @@ void BuilderTool::set(const Inkscape::Preferences::Entry& val) { } bool BuilderTool::sp_select_context_abort() { + if (shapes_builder.is_started()) { + desktop->getSelection()->deactivate(); + } if (Inkscape::Rubberband::get(desktop)->is_started()) { Inkscape::Rubberband::get(desktop)->stop(); rb_escaped = 1; @@ -238,6 +243,11 @@ bool BuilderTool::root_handler(GdkEvent* event) { bool BuilderTool::event_button_press_handler(GdkEvent *event) { if (event->button.button == 1) { + + if (shapes_builder.is_started()) { + desktop->getSelection()->activate(); + } + // save drag origin xp = (gint) event->button.x; yp = (gint) event->button.y; @@ -339,6 +349,10 @@ bool BuilderTool::event_button_release_handler(GdkEvent *event) selection->clear(); } } + + if (shapes_builder.is_started()) { + desktop->getSelection()->deactivate(); + } } if (grabbed) { grabbed->ungrab(); @@ -613,12 +627,14 @@ void BuilderTool::start_interactive_mode() in_interactive_mode = true; Inkscape::Selection *selection = desktop->getSelection(); shapes_builder.start(selection); + desktop->getSelection()->deactivate(); } void BuilderTool::end_interactive_mode() { in_interactive_mode = false; shapes_builder.commit(); + desktop->getSelection()->activate(); } void BuilderTool::toggle_interactive_mode() -- GitLab From 6f9dc315bcebe984d6ff0e11c7835fdb9f89f16b Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 31 Jul 2021 02:35:33 +0200 Subject: [PATCH 199/235] Disabled the shortcuts during the interactive mode in the Builder tool. --- src/ui/tools/builder-tool.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index b72fc7e93f..c7be49ae9e 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -413,6 +413,10 @@ bool BuilderTool::event_key_press_handler(GdkEvent *event) { set_current_mode(event); + if (shapes_builder.is_started()) { + return true; + } + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); Inkscape::Selection *selection = desktop->getSelection(); -- GitLab From 92df33554999ed715fbe3802565f7d630393c3e8 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 31 Jul 2021 02:49:30 +0200 Subject: [PATCH 200/235] Set the get_current_mode() method to not return the selection or the intersection mode when in the interactive mode. --- src/ui/tools/builder-tool.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index c7be49ae9e..befdc90ec5 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -564,11 +564,11 @@ int BuilderTool::get_current_mode() // if (Modifier::get(Modifiers::Type::JUST_SELECT)->active(this->button_press_state)) return JUST_SELECT; if (ctrl_on) { - if (alt_on) return SELECT_AND_INTERSECT; + if (alt_on && !shapes_builder.is_started()) return SELECT_AND_INTERSECT; return SELECT_AND_UNION; } if (alt_on) return SELECT_AND_DELETE; - if (shift_on) return JUST_SELECT; + if (shift_on && !shapes_builder.is_started()) return JUST_SELECT; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); return prefs->getInt("/tools/builder/mode", 0); -- GitLab From a3b5202925bf536d43b537b4d7ad323b2d02e72c Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 31 Jul 2021 02:51:42 +0200 Subject: [PATCH 201/235] Removed the comment about the modifiers in the get_current_mode method. --- src/ui/tools/builder-tool.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index befdc90ec5..2416a76eaf 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -557,12 +557,6 @@ void BuilderTool::set_modifiers_state(GdkEvent* event) int BuilderTool::get_current_mode() { -// TODO can it be done with the Modifier class? -// if (Modifier::get(Modifiers::Type::SELECT_AND_INTERSECTION)->active(this->button_press_state)) return SELECT_AND_INTERSECT; -// if (Modifier::get(Modifiers::Type::SELECT_AND_UNION)->active(this->button_press_state)) return SELECT_AND_UNION; -// if (Modifier::get(Modifiers::Type::SELECT_AND_DELETE)->active(this->button_press_state)) return SELECT_AND_DELETE; -// if (Modifier::get(Modifiers::Type::JUST_SELECT)->active(this->button_press_state)) return JUST_SELECT; - if (ctrl_on) { if (alt_on && !shapes_builder.is_started()) return SELECT_AND_INTERSECT; return SELECT_AND_UNION; -- GitLab From 99a1093b6693550d4ec471833c4077d3728bbf30 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 31 Jul 2021 02:56:09 +0200 Subject: [PATCH 202/235] Set the ObjectSet::deactivate method to clear the selection as well. --- src/object/object-set.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index d75a010abd..d97fc63335 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -592,6 +592,7 @@ void ObjectSet::activate() void ObjectSet::deactivate() { + clear(); _is_active = false; } -- GitLab From 02c634f4453b983e8b6ffb51969d9c6d3dd89db6 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 31 Jul 2021 03:03:59 +0200 Subject: [PATCH 203/235] Set the selection to be deactivated only if the interactive mode is started in the Builder tool. --- src/ui/tools/builder-tool.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 2416a76eaf..e02c67555a 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -625,7 +625,9 @@ void BuilderTool::start_interactive_mode() in_interactive_mode = true; Inkscape::Selection *selection = desktop->getSelection(); shapes_builder.start(selection); - desktop->getSelection()->deactivate(); + if (shapes_builder.is_started()) { + desktop->getSelection()->deactivate(); + } } void BuilderTool::end_interactive_mode() -- GitLab From 6214e8afca1f27f5c5d0fc8f44e4ced7aedcb7ef Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 31 Jul 2021 03:15:34 +0200 Subject: [PATCH 204/235] Set the mode to not change if is being set to the intersection or the selection mode while in the interactive mode. --- src/ui/tools/builder-tool.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index e02c67555a..a2b93af242 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -578,6 +578,11 @@ void BuilderTool::set_current_mode(int current_mode) return; } + if (shapes_builder.is_started() && + (current_mode == SELECT_AND_INTERSECT || current_mode == JUST_SELECT)) { + return; + } + active_mode = current_mode; set_cursor_mode(); set_rubberband_color(); -- GitLab From 80f7af5c787ac98851a8c3f9ab139d7578b9dc77 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Thu, 22 Jul 2021 09:58:31 +0200 Subject: [PATCH 205/235] Added one more option to SPDocument::getItemsAtPoints to allow for the case of having multiple items overlapping at a point. SPDocument::getItemsAtPoints was only returning the topmost items. If there are two items that intersect with the same point, it'll return only the top item. Now, this is an option called "topmost_only". if topmost_only is false, all items that are at a same point will be returned as well. --- src/document.cpp | 64 +++++++++++++++++++++++++++++------------------- src/document.h | 2 +- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/document.cpp b/src/document.cpp index 1d7778fed6..c0638c84a0 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -1457,21 +1457,22 @@ void SPDocument::build_flat_item_list(unsigned int dkey, SPGroup *group, gboolea } /** -Returns the topmost (in z-order) item from the descendants of group (recursively) which -is at the point p, or NULL if none. Honors into_groups on whether to recurse into -non-layer groups or not. Honors take_insensitive on whether to return insensitive -items. If upto != NULL, then if item upto is encountered (at any level), stops searching -upwards in z-order and returns what it has found so far (i.e. the found item is -guaranteed to be lower than upto). Requires a list of nodes built by -build_flat_item_list. +Returns the items from the descendants of group (recursively) which are at the +point p, or NULL if none. Honors into_groups on whether to recurse into non-layer +groups or not. Honors take_insensitive on whether to return insensitive items. +If upto != NULL, then if item upto is encountered (at any level), stops searching +upwards in z-order and returns what it has found so far (i.e. the found items are +guaranteed to be lower than upto). Requires a list of nodes built by build_flat_item_list. +If items_count > 0, it'll return the topmost (in z-order) items_count items. */ -static SPItem *find_item_at_point(std::deque *nodes, unsigned int dkey, Geom::Point const &p, SPItem* upto=nullptr) +static std::vector find_items_at_point(std::deque *nodes, unsigned int dkey, + Geom::Point const &p, int items_count=0, SPItem* upto=nullptr) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); gdouble delta = prefs->getDouble("/options/cursortolerance/value", 1.0); - SPItem *seen = nullptr; SPItem *child; + std::vector result; bool seen_upto = (!upto); for (auto node : *nodes) { child = node; @@ -1484,13 +1485,24 @@ static SPItem *find_item_at_point(std::deque *nodes, unsigned int dkey, if (arenaitem) { arenaitem->drawing().update(); if (arenaitem->pick(p, delta, 1) != nullptr) { - seen = child; - break; + result.push_back(child); + if (--items_count == 0) { + break; + } } } } - return seen; + return result; +} + +static SPItem *find_item_at_point(std::deque *nodes, unsigned int dkey, Geom::Point const &p, SPItem* upto=nullptr) +{ + auto items = find_items_at_point(nodes, dkey, p, 1, upto); + if (items.empty()) { + return nullptr; + } + return items.back(); } /** @@ -1559,9 +1571,9 @@ std::vector SPDocument::getItemsPartiallyInBox(unsigned int dkey, Geom: return find_items_in_area(x, this->root, dkey, box, overlaps, take_hidden, take_insensitive, take_groups, enter_groups); } -std::vector SPDocument::getItemsAtPoints(unsigned const key, std::vector points, bool all_layers, size_t limit) const +std::vector SPDocument::getItemsAtPoints(unsigned const key, std::vector points, bool all_layers, bool topmost_only, size_t limit) const { - std::vector items; + std::vector result; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); // When picking along the path, we don't want small objects close together @@ -1585,23 +1597,25 @@ std::vector SPDocument::getItemsAtPoints(unsigned const key, std::vecto } size_t item_counter = 0; for(int i = points.size()-1;i>=0; i--) { - SPItem *item = find_item_at_point(&_node_cache, key, points[i]); - if (item && items.end()==find(items.begin(),items.end(), item)) - if(all_layers || (layer_model && layer_model->layerForObject(item) == current_layer)){ - items.push_back(item); - item_counter++; - //limit 0 = no limit - if(item_counter == limit){ - prefs->setDouble("/options/cursortolerance/value", saved_delta); - return items; + std::vector items = find_items_at_point(&_node_cache, key, points[i], topmost_only); + for (SPItem *item : items) { + if (item && result.end()==find(result.begin(), result.end(), item)) + if(all_layers || (layer_model && layer_model->layerForObject(item) == current_layer)){ + result.push_back(item); + item_counter++; + //limit 0 = no limit + if(item_counter == limit){ + prefs->setDouble("/options/cursortolerance/value", saved_delta); + return result; + } } - } + } } // and now we restore it back prefs->setDouble("/options/cursortolerance/value", saved_delta); - return items; + return result; } SPItem *SPDocument::getItemAtPoint( unsigned const key, Geom::Point const &p, diff --git a/src/document.h b/src/document.h index e011409f79..608d74a471 100644 --- a/src/document.h +++ b/src/document.h @@ -259,7 +259,7 @@ private: std::vector getItemsInBox (unsigned int dkey, Geom::Rect const &box, bool take_hidden = false, bool take_insensitive = false, bool take_groups = true, bool enter_groups = false) const; std::vector getItemsPartiallyInBox(unsigned int dkey, Geom::Rect const &box, bool take_hidden = false, bool take_insensitive = false, bool take_groups = true, bool enter_groups = false) const; SPItem *getItemAtPoint(unsigned int key, Geom::Point const &p, bool into_groups, SPItem *upto = nullptr) const; - std::vector getItemsAtPoints(unsigned const key, std::vector points, bool all_layers = true, size_t limit = 0) const ; + std::vector getItemsAtPoints(unsigned const key, std::vector points, bool all_layers = true, bool topmost_only = true, size_t limit = 0) const; SPItem *getGroupAtPoint(unsigned int key, Geom::Point const &p) const; /** -- GitLab From 8f56a956dcee78739335fee299ba4ce9cd012599 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 31 Jul 2021 04:39:50 +0200 Subject: [PATCH 206/235] Set the NonIntersectingPathsBuilder to expand the groups before doing its operations. --- src/helper/NonIntersectingPathsBuilder.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp index 5602f6cb06..94d00b96a0 100644 --- a/src/helper/NonIntersectingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -112,11 +112,15 @@ void NonIntersectingPathsBuilder::fracture(bool skip_undo) void NonIntersectingPathsBuilder::set_parameters() { auto _items = set->items(); - items = std::vector(_items.begin(), _items.end()); - parent = items.front()->parent->getRepr(); // items will be placed in place // of the first item in the selection. - after = items.front()->getRepr(); + after = _items.front()->getRepr(); + parent = after->parent(); + + // expanding the items after getting the parent to + // avoid getting an unwanted parent. + items = std::vector(_items.begin(), _items.end()); + items = get_groups_expanded(items); } SPDesktop *NonIntersectingPathsBuilder::desktop() -- GitLab From cfda2b70922a4d18748f1809f6a2fe39c6ba86a8 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 31 Jul 2021 04:40:20 +0200 Subject: [PATCH 207/235] Set the Builder tool to select all items and not the top most items only. --- src/ui/tools/builder-tool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index a2b93af242..8f52a2c5bb 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -313,7 +313,7 @@ bool BuilderTool::event_button_release_handler(GdkEvent *event) Geom::OptRect const b = r->getRectangle(); items = desktop->getDocument()->getItemsPartiallyInBox(desktop->dkey, (*b) * desktop->dt2doc()); } else if (r->getMode() == RUBBERBAND_MODE_TOUCHPATH) { - items = desktop->getDocument()->getItemsAtPoints(desktop->dkey, r->getPoints()); + items = desktop->getDocument()->getItemsAtPoints(desktop->dkey, r->getPoints(), true, false); } _seltrans->resetState(); -- GitLab From 7b9fddbc5d522bbefa91c573298cc062b5c97667 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 31 Jul 2021 12:07:40 +0200 Subject: [PATCH 208/235] Set the in_interactive_mode field to be a function instead. This was causing some bugs. Now fixed. --- src/helper/InteractiveShapesBuilder.cpp | 2 +- src/ui/tools/builder-tool.cpp | 27 ++++++++++++++----------- src/ui/tools/builder-tool.h | 3 ++- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index d0682ce667..957c394966 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -75,7 +75,7 @@ SubItem InteractiveShapesBuilder::get_union_subitem(const std::vector & { SubItem res_subitem; res_subitem.paths = items.front()->get_pathvector(); - res_subitem.top_item = items.front(); + res_subitem.top_item = items.back(); for (auto item : items) { diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 8f52a2c5bb..96c280812c 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -177,7 +177,7 @@ void BuilderTool::set(const Inkscape::Preferences::Entry& val) { } bool BuilderTool::sp_select_context_abort() { - if (shapes_builder.is_started()) { + if (in_interactive_mode()) { desktop->getSelection()->deactivate(); } if (Inkscape::Rubberband::get(desktop)->is_started()) { @@ -244,7 +244,7 @@ bool BuilderTool::event_button_press_handler(GdkEvent *event) { if (event->button.button == 1) { - if (shapes_builder.is_started()) { + if (in_interactive_mode()) { desktop->getSelection()->activate(); } @@ -350,7 +350,7 @@ bool BuilderTool::event_button_release_handler(GdkEvent *event) } } - if (shapes_builder.is_started()) { + if (in_interactive_mode()) { desktop->getSelection()->deactivate(); } } @@ -413,7 +413,7 @@ bool BuilderTool::event_key_press_handler(GdkEvent *event) { set_current_mode(event); - if (shapes_builder.is_started()) { + if (in_interactive_mode()) { return true; } @@ -517,7 +517,7 @@ void BuilderTool::perform_operation(Selection *selection, int mode) { int size = selection->size(); - if (in_interactive_mode) { + if (shapes_builder.is_started()) { if (mode == SELECT_AND_UNION && size > 1) { shapes_builder.set_union(selection); } else if (mode == SELECT_AND_DELETE) { @@ -558,11 +558,11 @@ void BuilderTool::set_modifiers_state(GdkEvent* event) int BuilderTool::get_current_mode() { if (ctrl_on) { - if (alt_on && !shapes_builder.is_started()) return SELECT_AND_INTERSECT; + if (alt_on && !in_interactive_mode()) return SELECT_AND_INTERSECT; return SELECT_AND_UNION; } if (alt_on) return SELECT_AND_DELETE; - if (shift_on && !shapes_builder.is_started()) return JUST_SELECT; + if (shift_on && !in_interactive_mode()) return JUST_SELECT; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); return prefs->getInt("/tools/builder/mode", 0); @@ -578,7 +578,7 @@ void BuilderTool::set_current_mode(int current_mode) return; } - if (shapes_builder.is_started() && + if (in_interactive_mode() && (current_mode == SELECT_AND_INTERSECT || current_mode == JUST_SELECT)) { return; } @@ -627,30 +627,33 @@ bool BuilderTool::is_mode_add_to_selection(int mode, GdkEvent *event) void BuilderTool::start_interactive_mode() { - in_interactive_mode = true; Inkscape::Selection *selection = desktop->getSelection(); shapes_builder.start(selection); - if (shapes_builder.is_started()) { + if (in_interactive_mode()) { desktop->getSelection()->deactivate(); } } void BuilderTool::end_interactive_mode() { - in_interactive_mode = false; shapes_builder.commit(); desktop->getSelection()->activate(); } void BuilderTool::toggle_interactive_mode() { - if (in_interactive_mode) { + if (in_interactive_mode()) { end_interactive_mode(); } else { start_interactive_mode(); } } +bool BuilderTool::in_interactive_mode() const +{ + return shapes_builder.is_started(); +} + } } } diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index 4bbc640831..a62537b8cb 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -90,13 +90,14 @@ private: void set_cursor_mode(); void set_rubberband_color(); + bool in_interactive_mode() const; + // TODO you might pre-load the cursors and store them // in this vector instead of loading them each time. static const std::vector mode_cursor_filenames; static const std::vector mode_colors; static const std::map handlers; - bool in_interactive_mode = false; InteractiveShapesBuilder shapes_builder; int active_mode = JUST_SELECT; // default to the select mode since this is the default cursor. -- GitLab From 2ae0fa480c3f8bd441daf7f1adae24429ee69b57 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 31 Jul 2021 12:36:32 +0200 Subject: [PATCH 209/235] Renamed original_items to selected_items. --- src/helper/InteractiveShapesBuilder.cpp | 14 +++++++------- src/helper/InteractiveShapesBuilder.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 957c394966..1329b2a1fa 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -63,7 +63,7 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) add_disabled_node(nodes[i], subitems[i]); } - original_items = std::vector(set->items().begin(), set->items().end()); + selected_items = std::vector(set->items().begin(), set->items().end()); hide_original_items(); set->clear(); @@ -152,7 +152,7 @@ void InteractiveShapesBuilder::commit() } std::map original_paths; - for (auto item : original_items) { + for (auto item : selected_items) { original_paths[item] = item->get_pathvector(); } @@ -169,7 +169,7 @@ void InteractiveShapesBuilder::commit() } } - auto after = original_items.front()->getRepr(); + auto after = selected_items.front()->getRepr(); auto parent = after->parent(); show_original_items(); @@ -182,7 +182,7 @@ void InteractiveShapesBuilder::commit() } } - for (auto item : original_items) { + for (auto item : selected_items) { delete_object(item); } @@ -244,14 +244,14 @@ void InteractiveShapesBuilder::restore_original_style(XML::Node *node) void InteractiveShapesBuilder::hide_original_items() { - for (auto item : original_items) { + for (auto item : selected_items) { item->setHidden(true); } } void InteractiveShapesBuilder::show_original_items() { - for (auto item : original_items) { + for (auto item : selected_items) { item->setHidden(false); } } @@ -267,7 +267,7 @@ void InteractiveShapesBuilder::reset_internals() is_virgin = true; enabled.clear(); disabled.clear(); - original_items.clear(); + selected_items.clear(); original_styles.clear(); } diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index 5fde1a571c..4b2e15ca07 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -41,7 +41,7 @@ private: SPDesktop *desktop; SPDocument *document; - std::vector original_items; + std::vector selected_items; std::map enabled; std::map disabled; -- GitLab From f5139cebeff646713ff588b33da13cdcad151998 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 31 Jul 2021 12:42:40 +0200 Subject: [PATCH 210/235] Renamed the show/hide_original_items methods to show/hide_items and let it accept a list of items. --- src/helper/InteractiveShapesBuilder.cpp | 14 +++++++------- src/helper/InteractiveShapesBuilder.h | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 1329b2a1fa..0c47d23dfe 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -64,7 +64,7 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) } selected_items = std::vector(set->items().begin(), set->items().end()); - hide_original_items(); + hide_items(selected_items); set->clear(); started = true; @@ -172,7 +172,7 @@ void InteractiveShapesBuilder::commit() auto after = selected_items.front()->getRepr(); auto parent = after->parent(); - show_original_items(); + show_items(selected_items); for (auto item_it : original_paths) { auto item = item_it.first; @@ -242,16 +242,16 @@ void InteractiveShapesBuilder::restore_original_style(XML::Node *node) node->setAttribute("style", style->second); } -void InteractiveShapesBuilder::hide_original_items() +void InteractiveShapesBuilder::hide_items(const std::vector &items) { - for (auto item : selected_items) { + for (auto item : items) { item->setHidden(true); } } -void InteractiveShapesBuilder::show_original_items() +void InteractiveShapesBuilder::show_items(const std::vector &items) { - for (auto item : selected_items) { + for (auto item : items) { item->setHidden(false); } } @@ -278,7 +278,7 @@ void InteractiveShapesBuilder::reset() delete_object(object); } - show_original_items(); + show_items(selected_items); reset_internals(); } diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index 4b2e15ca07..747653d82c 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -59,8 +59,8 @@ private: void set_style_disabled(XML::Node *node); void restore_original_style(XML::Node *node); - void hide_original_items(); - void show_original_items(); + void hide_items(const std::vector &items); + void show_items(const std::vector &items); void reset_internals(); void reset(); -- GitLab From fbf5312a79d099cbc27c311baf2c7339e69be911 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 31 Jul 2021 23:05:40 +0200 Subject: [PATCH 211/235] Corrected the order in which items are being drawn in InteractiveShapesBuilder::commit(). --- src/helper/InteractiveShapesBuilder.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 0c47d23dfe..26ed91b00e 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -169,20 +169,16 @@ void InteractiveShapesBuilder::commit() } } - auto after = selected_items.front()->getRepr(); - auto parent = after->parent(); - show_items(selected_items); - for (auto item_it : original_paths) { - auto item = item_it.first; - auto &pathvec = item_it.second; - for (auto sub_pathvec : split_non_intersecting_paths(pathvec)) { - draw_on_canvas(sub_pathvec, item, parent, after); - } - } - for (auto item : selected_items) { + for (auto sub_pathvec : split_non_intersecting_paths(original_paths[item])) { + auto after = item->getRepr(); + auto parent = after->parent(); + if (!sub_pathvec.empty()) { + draw_on_canvas(sub_pathvec, item, parent, after); + } + } delete_object(item); } -- GitLab From b8978cf09939398b262c98343e664d64dd19dea1 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 31 Jul 2021 23:34:55 +0200 Subject: [PATCH 212/235] Set the items that're not selected to be hidden while in the interactive mode. --- src/helper/InteractiveShapesBuilder.cpp | 47 ++++++++++++++++++++++--- src/helper/InteractiveShapesBuilder.h | 1 + 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 26ed91b00e..2b0150f1e8 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -24,6 +24,39 @@ namespace Inkscape { +// TODO this function is copied from selection-chemistry.cpp. Refactor later. +/* + * Return a list of SPItems that are the children of 'list' + * + * list - source list of items to search in + * desktop - desktop associated with the source list + * exclude - list of items to exclude from result + * onlyvisible - TRUE includes only items visible on canvas + * onlysensitive - TRUE includes only non-locked items + * ingroups - TRUE to recursively get grouped items children + */ +std::vector &get_all_items(std::vector &list, SPObject *from, SPDesktop *desktop, bool onlyvisible, bool onlysensitive, bool ingroups, std::vector const &exclude) +{ + for (auto& child: from->children) { + SPItem *item = dynamic_cast(&child); + if (item && + !desktop->isLayer(item) && + (!onlysensitive || !item->isLocked()) && + (!onlyvisible || !desktop->itemIsHidden(item)) && + (exclude.empty() || exclude.end() == std::find(exclude.begin(), exclude.end(), &child)) + ) + { + list.insert(list.begin(),item); + } + + if (ingroups || (item && desktop->isLayer(item))) { + list = get_all_items(list, &child, desktop, onlyvisible, onlysensitive, ingroups, exclude); + } + } + + return list; +} + void delete_object(SPObject* item) { sp_object_ref(item, nullptr); @@ -54,7 +87,14 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) return; // Noting intersects } + selected_items = std::vector(set->items().begin(), set->items().end()); + set->clear(); + + get_all_items(not_selected_items, desktop->currentRoot(), desktop, true, true, false, selected_items); + hide_items(not_selected_items); + builder.show_output(false); + hide_items(selected_items); auto &nodes = builder.get_result_nodes(); int n = nodes.size(); @@ -63,10 +103,6 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) add_disabled_node(nodes[i], subitems[i]); } - selected_items = std::vector(set->items().begin(), set->items().end()); - hide_items(selected_items); - set->clear(); - started = true; is_virgin = true; } @@ -259,11 +295,14 @@ void InteractiveShapesBuilder::reset_internals() delete_object(object); } + show_items(not_selected_items); + started = false; is_virgin = true; enabled.clear(); disabled.clear(); selected_items.clear(); + not_selected_items.clear(); original_styles.clear(); } diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index 747653d82c..7c684a541a 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -42,6 +42,7 @@ private: SPDocument *document; std::vector selected_items; + std::vector not_selected_items; std::map enabled; std::map disabled; -- GitLab From 3feced39562e2c905f5e1f3a6f30b554e8856775 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 1 Aug 2021 02:57:30 +0200 Subject: [PATCH 213/235] Set the InteractiveShapesBuilder::get_union_subitem to set res_subitem.paths to the first non-empty PathVector. --- src/helper/InteractiveShapesBuilder.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 2b0150f1e8..c292eddaf7 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -110,12 +110,18 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) SubItem InteractiveShapesBuilder::get_union_subitem(const std::vector &items) { SubItem res_subitem; - res_subitem.paths = items.front()->get_pathvector(); res_subitem.top_item = items.back(); for (auto item : items) { - res_subitem.paths = sp_pathvector_boolop(res_subitem.paths, item->get_pathvector(), bool_op_union, fill_nonZero, fill_nonZero); + auto item_pathvec = item->get_pathvector(); + if (item_pathvec.empty()) { + continue; + } else if (res_subitem.paths.empty()) { + res_subitem.paths = item_pathvec; + } else { + res_subitem.paths = sp_pathvector_boolop(res_subitem.paths, item_pathvec, bool_op_union, fill_nonZero, fill_nonZero); + } auto repr = item->getRepr(); -- GitLab From d27beb41b8d56f0f97fcd04a363c0476a063c20d Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 1 Aug 2021 03:02:34 +0200 Subject: [PATCH 214/235] Added the ungroup_all function (temporary implementation) and used a new version of the draw_on_canvas function. --- src/helper/InteractiveShapesBuilder.cpp | 10 ++++------ src/helper/NonIntersectingPathsBuilder.cpp | 7 +++---- src/helper/useful-functions.cpp | 19 +++++++++++++++++++ src/helper/useful-functions.h | 4 ++++ src/object/object-set.cpp | 14 +++++--------- 5 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index c292eddaf7..c90c013db6 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -79,6 +79,8 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) commit(); } + ungroup_all(set); + NonIntersectingPathsBuilder builder(set); builder.perform_fracture(); @@ -153,10 +155,8 @@ void InteractiveShapesBuilder::set_union(ObjectSet *set) SubItem subitem = get_union_subitem(items); auto to_copy_style_form = subitem.top_item; - auto after = to_copy_style_form->getRepr(); - auto parent = after->parent(); - auto node = draw_on_canvas(subitem.paths, to_copy_style_form, parent, after); + auto node = draw_on_canvas(subitem.paths, to_copy_style_form); add_enabled_node(node, subitem); for (auto item : items) { @@ -215,10 +215,8 @@ void InteractiveShapesBuilder::commit() for (auto item : selected_items) { for (auto sub_pathvec : split_non_intersecting_paths(original_paths[item])) { - auto after = item->getRepr(); - auto parent = after->parent(); if (!sub_pathvec.empty()) { - draw_on_canvas(sub_pathvec, item, parent, after); + draw_on_canvas(sub_pathvec, item); } } delete_object(item); diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp index 94d00b96a0..df56a6a513 100644 --- a/src/helper/NonIntersectingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -49,6 +49,7 @@ void NonIntersectingPathsBuilder::prepare_input() // Ideally shouldn't be converting to paths? set->toCurves(true); + ungroup_all(set); // TODO get rid of this line and use affines. set->the_temporary_fix_for_the_transform_bug(); @@ -117,10 +118,7 @@ void NonIntersectingPathsBuilder::set_parameters() after = _items.front()->getRepr(); parent = after->parent(); - // expanding the items after getting the parent to - // avoid getting an unwanted parent. items = std::vector(_items.begin(), _items.end()); - items = get_groups_expanded(items); } SPDesktop *NonIntersectingPathsBuilder::desktop() @@ -239,8 +237,9 @@ void NonIntersectingPathsBuilder::draw_subitems(const std::vector &subi { int n = subitems.size(); result_nodes.resize(n); + for (int i = 0; i < n; i++) { - result_nodes[i] = draw_on_canvas(subitems[i].paths, subitems[i].top_item, parent, after); + result_nodes[i] = draw_on_canvas(subitems[i].paths, subitems[i].top_item); } } diff --git a/src/helper/useful-functions.cpp b/src/helper/useful-functions.cpp index 45f5f4a5f6..6c75399488 100644 --- a/src/helper/useful-functions.cpp +++ b/src/helper/useful-functions.cpp @@ -20,6 +20,8 @@ #include "geom-pathstroke.h" #include "ui/widget/canvas.h" +#include "object/object-set.h" + namespace Inkscape { @@ -121,6 +123,13 @@ XML::Node *draw_on_canvas(const Geom::PathVector &path, const SPItem *to_copy_fr return repr; } +XML::Node *draw_on_canvas(const Geom::PathVector &path, SPItem *to_copy_from) +{ + XML::Node *after = to_copy_from->getRepr(); + XML::Node *parent = after->parent(); + return draw_on_canvas(path, to_copy_from, parent, after); +} + std::string setSubAttribute(const std::string &str, std::string attr, const std::string &value) { attr += ":"; @@ -136,4 +145,14 @@ std::string setSubAttribute(const std::string &str, std::string attr, const std: return str.substr(0, start_idx) + value + str.substr(end_idx, str.size() - end_idx); } +void ungroup_all(ObjectSet *set) +{ + // TODO this is stupid... find a better way. + int size = 0; + while (set->size() != size) { + size = set->size(); + set->ungroup(true); + } +} + } \ No newline at end of file diff --git a/src/helper/useful-functions.h b/src/helper/useful-functions.h index 56b2a7d687..50dbba2f67 100644 --- a/src/helper/useful-functions.h +++ b/src/helper/useful-functions.h @@ -23,6 +23,8 @@ namespace Inkscape { +class ObjectSet; + void set_desktop_busy(SPDesktop *desktop); void unset_desktop_busy(SPDesktop *desktop); @@ -37,7 +39,9 @@ bool is_intersecting(const Path1 &a, const Path2 &b); // remember that you'll encounter problems if you tried to have a templated function // in both a header and a source file. this is why it's not being done for now. XML::Node *draw_on_canvas(const Geom::PathVector &path, const SPItem *to_copy_from, XML::Node *parent, XML::Node *after = nullptr); +XML::Node *draw_on_canvas(const Geom::PathVector &path, SPItem *to_copy_from); std::string setSubAttribute(const std::string &str, std::string attr, const std::string &value); +void ungroup_all(ObjectSet *set); } \ No newline at end of file diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index a51abe7ca4..d7fe40ffa8 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -514,6 +514,7 @@ void ObjectSet::flatten(bool skip_undo) the_temporary_fix_for_the_transform_bug(); toCurves(true); + ungroup_all(this); struct SubItem { @@ -549,15 +550,12 @@ void ObjectSet::flatten(bool skip_undo) } } - XML::Node *after = (*items().begin())->getRepr(); - XML::Node *parent = after->parent(); std::vector nodes; - for (int i = 0; i < n; i++) { auto split = split_non_intersecting_paths(paths[i].paths); for (auto pathvec : split) { if (!pathvec.empty()) { - nodes.push_back(draw_on_canvas(pathvec, paths[i].item, parent, after)); + nodes.push_back(draw_on_canvas(pathvec, paths[i].item)); } } } @@ -583,12 +581,10 @@ void ObjectSet::splitNonIntersecting(bool skip_undo) the_temporary_fix_for_the_transform_bug(); - std::vector items_vec(items().begin(), items().end()); + ungroup_all(this); - // getting the parent before getting the groups expanded. - auto parent = items_vec.front()->getRepr()->parent(); + std::vector items_vec(items().begin(), items().end()); - items_vec = get_groups_expanded(items_vec); int n = items_vec.size(); std::vector result(n); @@ -596,7 +592,7 @@ void ObjectSet::splitNonIntersecting(bool skip_undo) auto pathvec = items_vec[i]->get_pathvector(); auto broken = split_non_intersecting_paths(pathvec); for (auto paths : broken) { - result.push_back(draw_on_canvas(paths, items_vec[i], parent)); + result.push_back(draw_on_canvas(paths, items_vec[i])); } } -- GitLab From cec1e9c0636248c3ec22da46a5b75339d3294a4c Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 1 Aug 2021 03:03:16 +0200 Subject: [PATCH 215/235] Renamed original_paths to final_paths. --- src/helper/InteractiveShapesBuilder.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index c90c013db6..cb7bf8ba5a 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -193,28 +193,28 @@ void InteractiveShapesBuilder::commit() return reset(); } - std::map original_paths; + std::map final_paths; for (auto item : selected_items) { - original_paths[item] = item->get_pathvector(); + final_paths[item] = item->get_pathvector(); } for (auto subitem_it : enabled) { auto &subitem = subitem_it.second; auto &items = subitem.items; for (auto item : items) { - auto paths_it = original_paths.find(item); - if (paths_it == original_paths.end()) { + auto paths_it = final_paths.find(item); + if (paths_it == final_paths.end()) { std::cerr << "InteractiveShapesBuilder: No Geom::PathVector is for the item " << item << ".\n"; continue; } - original_paths[item] = sp_pathvector_boolop(subitem.paths, paths_it->second, bool_op_diff, fill_nonZero, fill_nonZero); + final_paths[item] = sp_pathvector_boolop(subitem.paths, paths_it->second, bool_op_diff, fill_nonZero, fill_nonZero); } } show_items(selected_items); for (auto item : selected_items) { - for (auto sub_pathvec : split_non_intersecting_paths(original_paths[item])) { + for (auto sub_pathvec : split_non_intersecting_paths(final_paths[item])) { if (!sub_pathvec.empty()) { draw_on_canvas(sub_pathvec, item); } -- GitLab From 264a1525eb112a7a45da8051925821031a879146 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 3 Aug 2021 00:35:01 +0200 Subject: [PATCH 216/235] Split InteractiveShapesBuilder::get_union_subitem into multiple functions. --- src/helper/InteractiveShapesBuilder.cpp | 77 +++++++++++++------------ src/helper/InteractiveShapesBuilder.h | 4 +- 2 files changed, 43 insertions(+), 38 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index cb7bf8ba5a..04456093d1 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -109,60 +109,65 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) is_virgin = true; } -SubItem InteractiveShapesBuilder::get_union_subitem(const std::vector &items) +std::vector InteractiveShapesBuilder::get_subitems(const std::vector &items) { - SubItem res_subitem; - res_subitem.top_item = items.back(); - - for (auto item : items) - { - auto item_pathvec = item->get_pathvector(); - if (item_pathvec.empty()) { - continue; - } else if (res_subitem.paths.empty()) { - res_subitem.paths = item_pathvec; - } else { - res_subitem.paths = sp_pathvector_boolop(res_subitem.paths, item_pathvec, bool_op_union, fill_nonZero, fill_nonZero); - } - + std::vector result; + for (auto item : items) { auto repr = item->getRepr(); - - auto subitem_it = enabled.find(repr); - if (subitem_it == enabled.end()) { - subitem_it = disabled.find(repr); - if (subitem_it == disabled.end()) { + auto subitem = enabled.find(repr); + if (subitem == enabled.end()) { + subitem = disabled.find(repr); + if (subitem == disabled.end()) { continue; } } + result.push_back(subitem->second); + } + return result; +} - SubItem &subitem = subitem_it->second; +SubItem InteractiveShapesBuilder::get_union_subitem(const std::vector &subitems) +{ + SubItem res_subitem = subitems.back(); + + for (int i = 0; i < subitems.size() - 1; i++) + { + auto &subitem = subitems[i]; + res_subitem.paths = sp_pathvector_boolop(res_subitem.paths, subitem.paths, bool_op_union, fill_nonZero, fill_nonZero); res_subitem.items.insert(subitem.items.begin(), subitem.items.end()); } + return res_subitem; +} + +void InteractiveShapesBuilder::remove_items(const std::vector items) +{ for (auto item : items) { auto repr = item->getRepr(); + remove_enabled_node(repr); remove_disabled_node(repr); - } - return res_subitem; + delete_object(item); + } } void InteractiveShapesBuilder::set_union(ObjectSet *set) { std::vector items(set->items().begin(), set->items().end()); - SubItem subitem = get_union_subitem(items); + auto subitems = get_subitems(items); + SubItem subitem = get_union_subitem(subitems); - auto to_copy_style_form = subitem.top_item; + auto node = draw_on_canvas(subitem.paths, subitem.top_item); - auto node = draw_on_canvas(subitem.paths, to_copy_style_form); - add_enabled_node(node, subitem); + // TODO find a better way to do this. + auto item = dynamic_cast(document->getObjectByRepr(node)); + item->setHidden(false); - for (auto item : items) { - delete_object(item); - } + add_enabled_node(node, subitem); + remove_items(items); is_virgin = false; } @@ -170,16 +175,14 @@ void InteractiveShapesBuilder::set_delete(ObjectSet *set) { std::vector items(set->items().begin(), set->items().end()); + auto subitems = get_subitems(items); + auto subitem = get_union_subitem(subitems); + // TODO quick hack. change it later. static XML::Node *place_holder = 0x0; + add_enabled_node(place_holder++, subitem); - auto subitem = get_union_subitem(items); - enabled[place_holder++] = subitem; - - for (auto item : items) { - delete_object(item); - } - + remove_items(items); is_virgin = false; } diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index 7c684a541a..8eda0e7991 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -66,7 +66,9 @@ private: void reset_internals(); void reset(); - SubItem get_union_subitem(const std::vector &items); + std::vector get_subitems(const std::vector &items); + SubItem get_union_subitem(const std::vector &subitems); + void remove_items(const std::vector items); public: -- GitLab From e241939c2c433c30742fba0cc55c05dc52a8a7c1 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 3 Aug 2021 01:40:15 +0200 Subject: [PATCH 217/235] Initial implementation for the undo/redo in the InteractiveShapesBuilder --- src/helper/InteractiveShapesBuilder.cpp | 137 ++++++++++++++++++++---- src/helper/InteractiveShapesBuilder.h | 23 +++- src/ui/tools/builder-tool.cpp | 17 +++ 3 files changed, 153 insertions(+), 24 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 04456093d1..7b76ea5d2b 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -109,9 +109,9 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) is_virgin = true; } -std::vector InteractiveShapesBuilder::get_subitems(const std::vector &items) +std::vector> InteractiveShapesBuilder::get_subitems(const std::vector &items) { - std::vector result; + std::vector> result; for (auto item : items) { auto repr = item->getRepr(); auto subitem = enabled.find(repr); @@ -121,18 +121,21 @@ std::vector InteractiveShapesBuilder::get_subitems(const std::vectorsecond); + result.emplace_back(*subitem); } return result; } -SubItem InteractiveShapesBuilder::get_union_subitem(const std::vector &subitems) +SubItem InteractiveShapesBuilder::get_union_subitem(const std::vector> &subitems) { - SubItem res_subitem = subitems.back(); + // TODO find a better way of taking arguments. + // the first of the pair is not related here. + + SubItem res_subitem = subitems.back().second; for (int i = 0; i < subitems.size() - 1; i++) { - auto &subitem = subitems[i]; + auto &subitem = subitems[i].second; res_subitem.paths = sp_pathvector_boolop(res_subitem.paths, subitem.paths, bool_op_union, fill_nonZero, fill_nonZero); res_subitem.items.insert(subitem.items.begin(), subitem.items.end()); } @@ -152,38 +155,39 @@ void InteractiveShapesBuilder::remove_items(const std::vector items) } } -void InteractiveShapesBuilder::set_union(ObjectSet *set) +void InteractiveShapesBuilder::perform_union(ObjectSet *set, bool draw_result) { std::vector items(set->items().begin(), set->items().end()); auto subitems = get_subitems(items); SubItem subitem = get_union_subitem(subitems); - auto node = draw_on_canvas(subitem.paths, subitem.top_item); + XML::Node *node; - // TODO find a better way to do this. - auto item = dynamic_cast(document->getObjectByRepr(node)); - item->setHidden(false); + if (draw_result) { + node = draw_and_set_visible(subitem); + } else { + // TODO quick hack. change it later. + static XML::Node *place_holder = 0x0; + node = place_holder++; + } add_enabled_node(node, subitem); + push_undo_command({{node, subitem}, std::move(subitems), draw_result}); + remove_items(items); is_virgin = false; } -void InteractiveShapesBuilder::set_delete(ObjectSet *set) +void InteractiveShapesBuilder::set_union(ObjectSet *set) { - std::vector items(set->items().begin(), set->items().end()); - - auto subitems = get_subitems(items); - auto subitem = get_union_subitem(subitems); - - // TODO quick hack. change it later. - static XML::Node *place_holder = 0x0; - add_enabled_node(place_holder++, subitem); + perform_union(set, true); +} - remove_items(items); - is_virgin = false; +void InteractiveShapesBuilder::set_delete(ObjectSet *set) +{ + perform_union(set, false); } void InteractiveShapesBuilder::commit() @@ -311,6 +315,12 @@ void InteractiveShapesBuilder::reset_internals() selected_items.clear(); not_selected_items.clear(); original_styles.clear(); + while (!undo_stack.empty()) { + undo_stack.pop(); + } + while (!redo_stack.empty()) { + redo_stack.pop(); + } } void InteractiveShapesBuilder::reset() @@ -324,4 +334,87 @@ void InteractiveShapesBuilder::reset() reset_internals(); } +XML::Node* InteractiveShapesBuilder::draw_and_set_visible(const SubItem &subitem) +{ + auto node = draw_on_canvas(subitem.paths, subitem.top_item); + // TODO find a better way to do this. + auto item = dynamic_cast(document->getObjectByRepr(node)); + item->setHidden(false); + return node; +} + +void InteractiveShapesBuilder::push_undo_command(const UnionCommand &command) +{ + undo_stack.push(std::move(command)); + while (!redo_stack.empty()) { + redo_stack.pop(); + } +} + +void InteractiveShapesBuilder::undo() +{ + if (undo_stack.empty()) { + return; + } + + auto command = undo_stack.top(); + undo_stack.pop(); + + if (command.draw_result) { + auto node = command.result.first; + remove_enabled_node(node); + remove_disabled_node(node); + delete_object(document->getObjectByRepr(node)); + } + + for (auto &operand : command.operands) { + auto old_node = operand.first; + auto node = draw_and_set_visible(operand.second); + operand.first = node; + + // TODO quick hack. change it later. + // if the node exits in original_styles, then it was disabled. + if (original_styles.find(old_node) != original_styles.end()) { + set_style_disabled(node); + disabled.insert(operand); + } else { + enabled.insert(operand); + } + + } + + redo_stack.push(command); + + if (undo_stack.empty()) { + is_virgin = true; + } +} + +void InteractiveShapesBuilder::redo() +{ + if (redo_stack.empty()) { + return; + } + + auto command = redo_stack.top(); + redo_stack.pop(); + + if (command.draw_result) { + auto node = draw_and_set_visible(command.result.second); + command.result.first = node; + enabled.insert(command.result); + } + + for (auto &operand : command.operands) + { + auto node = operand.first; + remove_enabled_node(node); + remove_disabled_node(node); + delete_object(document->getObjectByRepr(node)); + } + + undo_stack.push(command); + is_virgin = false; +} + } \ No newline at end of file diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index 8eda0e7991..5f333d7e0f 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -15,6 +15,7 @@ #include #include +#include #include "NonIntersectingPathsBuilder.h" class SPDesktop; @@ -38,6 +39,13 @@ class InteractiveShapesBuilder private: + struct UnionCommand + { + std::pair result; + std::vector> operands; + bool draw_result; + }; + SPDesktop *desktop; SPDocument *document; @@ -48,6 +56,9 @@ private: std::map disabled; std::map original_styles; + std::stack undo_stack; + std::stack redo_stack; + bool started = false; bool is_virgin = true; @@ -66,10 +77,15 @@ private: void reset_internals(); void reset(); - std::vector get_subitems(const std::vector &items); - SubItem get_union_subitem(const std::vector &subitems); + XML::Node* draw_and_set_visible(const SubItem &subitem); + void perform_union(ObjectSet *set, bool draw_result); + + std::vector> get_subitems(const std::vector &items); + SubItem get_union_subitem(const std::vector> &subitems); void remove_items(const std::vector items); + void push_undo_command(const UnionCommand& command); + public: bool is_started() const; @@ -79,6 +95,9 @@ public: void set_union(ObjectSet *set); void set_delete(ObjectSet *set); + void undo(); + void redo(); + ~InteractiveShapesBuilder() { commit(); } }; diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 96c280812c..7e8aeeac3d 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -414,6 +414,23 @@ bool BuilderTool::event_key_press_handler(GdkEvent *event) set_current_mode(event); if (in_interactive_mode()) { + + switch (get_latin_keyval (&event->key)) { + case GDK_KEY_z: + case GDK_KEY_Z: + if (ctrl_on) { + shapes_builder.undo(); + } + break; + + case GDK_KEY_y: + case GDK_KEY_Y: + if (ctrl_on) { + shapes_builder.redo(); + } + break; + } + return true; } -- GitLab From 60f0dffa84f6cb23274fee010d053c908af322b3 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 3 Aug 2021 01:50:02 +0200 Subject: [PATCH 218/235] Enabled the key presses while in the interactive mode in the Builder tool --- src/ui/tools/builder-tool.cpp | 47 ++++++++++++++--------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 7e8aeeac3d..7021dfceff 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -244,10 +244,6 @@ bool BuilderTool::event_button_press_handler(GdkEvent *event) { if (event->button.button == 1) { - if (in_interactive_mode()) { - desktop->getSelection()->activate(); - } - // save drag origin xp = (gint) event->button.x; yp = (gint) event->button.y; @@ -325,8 +321,10 @@ bool BuilderTool::event_button_release_handler(GdkEvent *event) if(is_mode_add_to_selection(mode, event)) { selection->addList (items); } else { + if (in_interactive_mode()) selection->activate(); selection->setList (items); perform_operation(selection, mode); + if (in_interactive_mode()) selection->deactivate(); } } else { // it was just a click, or a too small rubberband @@ -349,10 +347,6 @@ bool BuilderTool::event_button_release_handler(GdkEvent *event) selection->clear(); } } - - if (in_interactive_mode()) { - desktop->getSelection()->deactivate(); - } } if (grabbed) { grabbed->ungrab(); @@ -413,27 +407,6 @@ bool BuilderTool::event_key_press_handler(GdkEvent *event) { set_current_mode(event); - if (in_interactive_mode()) { - - switch (get_latin_keyval (&event->key)) { - case GDK_KEY_z: - case GDK_KEY_Z: - if (ctrl_on) { - shapes_builder.undo(); - } - break; - - case GDK_KEY_y: - case GDK_KEY_Y: - if (ctrl_on) { - shapes_builder.redo(); - } - break; - } - - return true; - } - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); Inkscape::Selection *selection = desktop->getSelection(); @@ -512,6 +485,22 @@ bool BuilderTool::event_key_press_handler(GdkEvent *event) } break; + case GDK_KEY_z: + case GDK_KEY_Z: + if (ctrl_on && in_interactive_mode()) { + shapes_builder.undo(); + ret = true; + } + break; + + case GDK_KEY_y: + case GDK_KEY_Y: + if (ctrl_on && in_interactive_mode()) { + shapes_builder.redo(); + ret = true; + } + break; + default: break; } -- GitLab From ec5f3511f044663478f7912d63b3fc820aa45377 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 3 Aug 2021 02:10:59 +0200 Subject: [PATCH 219/235] Added the NonIntersectingPathsBuilder::items_intersected method and used it in the InteractiveShapesBuilder instead of checking by size --- src/helper/InteractiveShapesBuilder.cpp | 7 ++++--- src/helper/NonIntersectingPathsBuilder.cpp | 3 +++ src/helper/NonIntersectingPathsBuilder.h | 3 +++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 7b76ea5d2b..479a6a5332 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -82,12 +82,13 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) ungroup_all(set); NonIntersectingPathsBuilder builder(set); + builder.perform_fracture(); + if (!builder.items_intersected()) { + return; + } auto &subitems = builder.get_result_subitems(); - if (subitems.size() == set->size()) { - return; // Noting intersects - } selected_items = std::vector(set->items().begin(), set->items().end()); set->clear(); diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp index df56a6a513..ac82a7823a 100644 --- a/src/helper/NonIntersectingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -80,6 +80,7 @@ void NonIntersectingPathsBuilder::perform_operation(SubItemOperation operation) } prepare_input(); + items_intersect = false; result_subitems = get_operation_result(operation); } @@ -215,6 +216,8 @@ std::vector NonIntersectingPathsBuilder::get_operation_result(SubItemOp remove_empty_subitems(broken); if (broken.empty()) { continue; } // don't intersect. continue. + items_intersect = true; + // the bigger index should be erased first. int bigger_index = (i > j) ? i : j; int smaller_index = (i > j) ? j : i; diff --git a/src/helper/NonIntersectingPathsBuilder.h b/src/helper/NonIntersectingPathsBuilder.h index 6f53942076..8ddb6807aa 100644 --- a/src/helper/NonIntersectingPathsBuilder.h +++ b/src/helper/NonIntersectingPathsBuilder.h @@ -48,6 +48,8 @@ private: std::vector result_subitems; std::vector result_nodes; + bool items_intersect = false; + public: NonIntersectingPathsBuilder(ObjectSet *set) : set(set) {} @@ -57,6 +59,7 @@ public: void add_result_to_set(); const std::vector& get_result_subitems() const; const std::vector& get_result_nodes() const; + bool items_intersected() const { return items_intersect; }; private: -- GitLab From 717b9d59710d9677e89fd9e5ddeff82978697c33 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 3 Aug 2021 03:43:54 +0200 Subject: [PATCH 220/235] Minor improvements to the undo and redo methods in the InteractiveShapesBuilder --- src/helper/InteractiveShapesBuilder.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 479a6a5332..9a43c0d7b7 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -365,7 +365,12 @@ void InteractiveShapesBuilder::undo() auto node = command.result.first; remove_enabled_node(node); remove_disabled_node(node); - delete_object(document->getObjectByRepr(node)); + auto object = document->getObjectByRepr(node); + if (object) { + delete_object(object); + } else { + std::cerr << "InteractiveShapesBuilder::undo: Node " << node << " doesn't have an object...\n"; + } } for (auto &operand : command.operands) { @@ -376,10 +381,9 @@ void InteractiveShapesBuilder::undo() // TODO quick hack. change it later. // if the node exits in original_styles, then it was disabled. if (original_styles.find(old_node) != original_styles.end()) { - set_style_disabled(node); - disabled.insert(operand); + add_disabled_node(operand.first, operand.second); } else { - enabled.insert(operand); + add_enabled_node(operand.first, operand.second); } } @@ -411,7 +415,12 @@ void InteractiveShapesBuilder::redo() auto node = operand.first; remove_enabled_node(node); remove_disabled_node(node); - delete_object(document->getObjectByRepr(node)); + auto object = document->getObjectByRepr(node); + if (object) { + delete_object(object); + } else { + std::cerr << "InteractiveShapesBuilder::redo: Node " << node << " doesn't have an object...\n"; + } } undo_stack.push(command); -- GitLab From 3e2f8fd66af1cf356d5c0b3af655a287b7b623fb Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 3 Aug 2021 12:25:06 +0200 Subject: [PATCH 221/235] Set the selection to be activated for the single click --- src/ui/tools/builder-tool.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 7021dfceff..0fe8add71b 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -340,8 +340,10 @@ bool BuilderTool::event_button_release_handler(GdkEvent *event) auto item = sp_event_context_find_item(desktop, Geom::Point(event->button.x, event->button.y), false, in_groups); if (item) { + if (in_interactive_mode()) selection->activate(); selection->add(item); perform_operation(selection, mode); + if (in_interactive_mode()) selection->deactivate(); } else { // clicked in an empty area selection->clear(); -- GitLab From 509f7fe4686e35a744c7e9f48ddfc12939f43480 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 3 Aug 2021 12:43:28 +0200 Subject: [PATCH 222/235] Enabled the union operation to happen only on a single item when in the interactive mode. This allows for just "enabling" the selected item --- src/ui/tools/builder-tool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 0fe8add71b..9b957cc8de 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -526,7 +526,7 @@ void BuilderTool::perform_operation(Selection *selection, int mode) int size = selection->size(); if (shapes_builder.is_started()) { - if (mode == SELECT_AND_UNION && size > 1) { + if (mode == SELECT_AND_UNION) { shapes_builder.set_union(selection); } else if (mode == SELECT_AND_DELETE) { shapes_builder.set_delete(selection); -- GitLab From 03df2a2d3f8efe85859748862da69b52f67467d9 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 3 Aug 2021 12:56:50 +0200 Subject: [PATCH 223/235] Added a check for empty sets in InteractiveShapesBuilder::perform_union to avoid crashing when nothing is selected --- src/helper/InteractiveShapesBuilder.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 9a43c0d7b7..4899f1f576 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -158,6 +158,10 @@ void InteractiveShapesBuilder::remove_items(const std::vector items) void InteractiveShapesBuilder::perform_union(ObjectSet *set, bool draw_result) { + if (set->isEmpty()) { + return; + } + std::vector items(set->items().begin(), set->items().end()); auto subitems = get_subitems(items); -- GitLab From f51934583c1290988bf5f925fb7f4a7b153c8f13 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 3 Aug 2021 16:00:19 +0200 Subject: [PATCH 224/235] Fixed a bug where the deleted items were not removed from the enabled nodes when doing undo --- src/helper/InteractiveShapesBuilder.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 4899f1f576..cc61842b89 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -365,10 +365,11 @@ void InteractiveShapesBuilder::undo() auto command = undo_stack.top(); undo_stack.pop(); + auto node = command.result.first; + remove_enabled_node(node); + remove_disabled_node(node); + if (command.draw_result) { - auto node = command.result.first; - remove_enabled_node(node); - remove_disabled_node(node); auto object = document->getObjectByRepr(node); if (object) { delete_object(object); @@ -379,8 +380,8 @@ void InteractiveShapesBuilder::undo() for (auto &operand : command.operands) { auto old_node = operand.first; - auto node = draw_and_set_visible(operand.second); - operand.first = node; + auto new_node = draw_and_set_visible(operand.second); + operand.first = new_node; // TODO quick hack. change it later. // if the node exits in original_styles, then it was disabled. -- GitLab From a86c88d7e0377c7dea23adf16db1056a0f8b322e Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Tue, 3 Aug 2021 16:00:44 +0200 Subject: [PATCH 225/235] Used add_enabled_node() instead of inserting directly --- src/helper/InteractiveShapesBuilder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index cc61842b89..e0f6a3258f 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -411,8 +411,8 @@ void InteractiveShapesBuilder::redo() if (command.draw_result) { auto node = draw_and_set_visible(command.result.second); - command.result.first = node; - enabled.insert(command.result); + auto &subitem = command.result.second; + add_enabled_node(node, subitem); } for (auto &operand : command.operands) -- GitLab From 388c4d64563157262a528236085317508c3973c8 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 4 Aug 2021 13:00:27 +0200 Subject: [PATCH 226/235] Changed the name of boolops modes to operations. --- src/ui/toolbar/builder-toolbar.cpp | 80 +++++++++++++------------- src/ui/toolbar/builder-toolbar.h | 34 +++++------ src/ui/tools/builder-tool.cpp | 92 +++++++++++++++--------------- src/ui/tools/builder-tool.h | 20 +++---- 4 files changed, 113 insertions(+), 113 deletions(-) diff --git a/src/ui/toolbar/builder-toolbar.cpp b/src/ui/toolbar/builder-toolbar.cpp index 20daa9256c..59a27cf239 100644 --- a/src/ui/toolbar/builder-toolbar.cpp +++ b/src/ui/toolbar/builder-toolbar.cpp @@ -49,7 +49,7 @@ namespace Toolbar { BuilderToolbar::BuilderToolbar(SPDesktop *desktop) : Toolbar(desktop) { - mode_buttons_init(); + operation_buttons_init(); add_separator(); boolop_buttons_init(); add_separator(); @@ -60,113 +60,113 @@ BuilderToolbar::BuilderToolbar(SPDesktop *desktop) : show_all(); } -void BuilderToolbar::mode_buttons_init() +void BuilderToolbar::operation_buttons_init() { // TODO change the icons. // if you're going to edit this, remember to edit the BuilderTool::Mode enum and - // BuilderTool::mode_cursor_filenames as well. remember that they have to be in the same order. - const static std::vector mode_buttons_descriptors = { + // BuilderTool::operation_cursor_filenames as well. remember that they have to be in the same order. + const static std::vector operation_buttons_descriptors = { { .label = _("Union"), .tooltip_text = _("Union whatever the mouse moves over"), .icon_name = "path-union", - .handler = &BuilderToolbar::set_mode_union, + .handler = &BuilderToolbar::set_operation_union, }, { .label = _("Delete"), .tooltip_text = _("Delete whatever the mouse moves over"), .icon_name = "path-difference", - .handler = &BuilderToolbar::set_mode_delete, + .handler = &BuilderToolbar::set_operation_delete, }, { .label = _("Intersection"), .tooltip_text = _("Intersect whatever the mouse moves over"), .icon_name = "path-intersection", - .handler = &BuilderToolbar::set_mode_intersection, + .handler = &BuilderToolbar::set_operation_intersection, }, { .label = _("Just Select"), .tooltip_text = _("Just select whatever the mouse moves over"), .icon_name = "tool-pointer", - .handler = &BuilderToolbar::set_mode_just_select, + .handler = &BuilderToolbar::set_operation_just_select, }, }; - mode_buttons_init_create_buttons(mode_buttons_descriptors); - mode_buttons_init_set_active_button(); - mode_buttons_init_add_buttons(); + operation_buttons_init_create_buttons(operation_buttons_descriptors); + operation_buttons_init_set_active_button(); + operation_buttons_init_add_buttons(); } -void BuilderToolbar::mode_buttons_init_create_buttons(const std::vector& descriptors) +void BuilderToolbar::operation_buttons_init_create_buttons(const std::vector& descriptors) { - Gtk::RadioToolButton::Group mode_group; + Gtk::RadioToolButton::Group operation_group; - for (auto& mode : descriptors) + for (auto& operation : descriptors) { - auto button = Gtk::manage(new Gtk::RadioToolButton((mode.label))); - button->set_tooltip_text((mode.tooltip_text)); - button->set_icon_name(INKSCAPE_ICON(mode.icon_name)); - button->set_group(mode_group); - _mode_buttons.push_back(button); - _mode_handlers.push_back(mode.handler); + auto button = Gtk::manage(new Gtk::RadioToolButton((operation.label))); + button->set_tooltip_text((operation.tooltip_text)); + button->set_icon_name(INKSCAPE_ICON(operation.icon_name)); + button->set_group(operation_group); + _operation_buttons.push_back(button); + _operation_handlers.push_back(operation.handler); } } -void BuilderToolbar::mode_buttons_init_set_active_button() +void BuilderToolbar::operation_buttons_init_set_active_button() { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - gint type = prefs->getInt("/tools/builder/mode", 0); - _mode_buttons[type]->set_active(); + gint type = prefs->getInt("/tools/builder/operation", 0); + _operation_buttons[type]->set_active(); } -void BuilderToolbar::mode_buttons_init_add_buttons() +void BuilderToolbar::operation_buttons_init_add_buttons() { int button_index = 0; - for (auto button : _mode_buttons) + for (auto button : _operation_buttons) { button->set_sensitive(); - button->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &BuilderToolbar::mode_changed), button_index++)); + button->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &BuilderToolbar::operation_changed), button_index++)); add(*button); } } -void BuilderToolbar::mode_changed(int mode) +void BuilderToolbar::operation_changed(int operation) { - // each mode has its own handler so that it's + // each operation has its own handler so that it's // easier to attach more logic in the future. - auto handler = _mode_handlers[mode]; + auto handler = _operation_handlers[operation]; (this->*handler)(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setInt("/tools/builder/mode", mode); + prefs->setInt("/tools/builder/operation", operation); } -void BuilderToolbar::set_current_mode(int mode) +void BuilderToolbar::set_current_operation(int operation) { auto builder_tool = dynamic_cast(_desktop->event_context); if (builder_tool) { - builder_tool->set_current_mode(mode); + builder_tool->set_current_operation(operation); } } -void BuilderToolbar::set_mode_union() +void BuilderToolbar::set_operation_union() { - set_current_mode(Tools::BuilderTool::SELECT_AND_UNION); + set_current_operation(Tools::BuilderTool::SELECT_AND_UNION); } -void BuilderToolbar::set_mode_delete() +void BuilderToolbar::set_operation_delete() { - set_current_mode(Tools::BuilderTool::SELECT_AND_DELETE); + set_current_operation(Tools::BuilderTool::SELECT_AND_DELETE); } -void BuilderToolbar::set_mode_intersection() +void BuilderToolbar::set_operation_intersection() { - set_current_mode(Tools::BuilderTool::SELECT_AND_INTERSECT); + set_current_operation(Tools::BuilderTool::SELECT_AND_INTERSECT); } -void BuilderToolbar::set_mode_just_select() +void BuilderToolbar::set_operation_just_select() { - set_current_mode(Tools::BuilderTool::JUST_SELECT); + set_current_operation(Tools::BuilderTool::JUST_SELECT); } diff --git a/src/ui/toolbar/builder-toolbar.h b/src/ui/toolbar/builder-toolbar.h index 9f23f7181e..dba7edadd0 100644 --- a/src/ui/toolbar/builder-toolbar.h +++ b/src/ui/toolbar/builder-toolbar.h @@ -48,23 +48,23 @@ class BuilderToolbar : public Toolbar { using parent_type = Toolbar; private: - std::vector _mode_buttons; - std::vector _mode_handlers; - -// Mode related methods { - void mode_buttons_init(); - void mode_buttons_init_create_buttons(const std::vector& descriptors); - void mode_buttons_init_set_active_button(); - void mode_buttons_init_add_buttons(); - - void mode_changed(int mode); - void set_current_mode(int mode); - - // handlers that gets called when mode is changed: - void set_mode_union(); - void set_mode_delete(); - void set_mode_intersection(); - void set_mode_just_select(); + std::vector _operation_buttons; + std::vector _operation_handlers; + +// operation related methods { + void operation_buttons_init(); + void operation_buttons_init_create_buttons(const std::vector& descriptors); + void operation_buttons_init_set_active_button(); + void operation_buttons_init_add_buttons(); + + void operation_changed(int operation); + void set_current_operation(int operation); + + // handlers that gets called when the operation is changed: + void set_operation_union(); + void set_operation_delete(); + void set_operation_intersection(); + void set_operation_just_select(); // } void boolop_buttons_init(); diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 9b957cc8de..8b1562fb1d 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -74,14 +74,14 @@ const std::string& BuilderTool::getPrefsPath() { const std::string BuilderTool::prefsPath = "/tools/builder"; -const std::vector BuilderTool::mode_cursor_filenames = { +const std::vector BuilderTool::operation_cursor_filenames = { "cursor-union.svg", "cursor-delete.svg", "cursor-intersect.svg", "select.svg", }; -const std::vector BuilderTool::mode_colors = { +const std::vector BuilderTool::operation_colors = { 0x0000ffff, 0x000000ff, 0xff00ffff, @@ -159,7 +159,7 @@ void BuilderTool::setup() { this->enableGrDrag(); } - set_current_mode(); + set_current_operation(); start_interactive_mode(); } @@ -252,8 +252,8 @@ bool BuilderTool::event_button_press_handler(GdkEvent *event) Geom::Point const button_pt(event->button.x, event->button.y); Geom::Point const p(desktop->w2d(button_pt)); - int current_mode = get_current_mode(); - guint32 current_color = mode_colors[current_mode]; + int current_operation = get_current_operation(); + guint32 current_color = operation_colors[current_operation]; Inkscape::Rubberband::get(desktop)->setColor(current_color); Inkscape::Rubberband::get(desktop)->setMode(RUBBERBAND_MODE_TOUCHPATH); @@ -316,23 +316,23 @@ bool BuilderTool::event_button_release_handler(GdkEvent *event) r->stop(); this->defaultMessageContext()->clear(); - int mode = get_current_mode(); + int operation = get_current_operation(); - if(is_mode_add_to_selection(mode, event)) { + if(is_operation_add_to_selection(operation, event)) { selection->addList (items); } else { if (in_interactive_mode()) selection->activate(); selection->setList (items); - perform_operation(selection, mode); + perform_operation(selection, operation); if (in_interactive_mode()) selection->deactivate(); } } else { // it was just a click, or a too small rubberband r->stop(); - int mode = get_current_mode(); + int operation = get_current_operation(); - if (mode == JUST_SELECT && !is_mode_add_to_selection(mode, event)) { + if (operation == JUST_SELECT && !is_operation_add_to_selection(operation, event)) { selection->clear(); } @@ -342,7 +342,7 @@ bool BuilderTool::event_button_release_handler(GdkEvent *event) if (item) { if (in_interactive_mode()) selection->activate(); selection->add(item); - perform_operation(selection, mode); + perform_operation(selection, operation); if (in_interactive_mode()) selection->deactivate(); } else { // clicked in an empty area @@ -386,11 +386,11 @@ bool BuilderTool::event_motion_handler(GdkEvent *event) Inkscape::Rubberband::get(desktop)->move(p); auto touch_path = Modifier::get(Modifiers::Type::SELECT_TOUCH_PATH)->get_label(); - auto mode = Inkscape::Rubberband::get(desktop)->getMode(); - if (mode == RUBBERBAND_MODE_TOUCHPATH) { + auto operation = Inkscape::Rubberband::get(desktop)->getMode(); + if (operation == RUBBERBAND_MODE_TOUCHPATH) { this->defaultMessageContext()->setF(Inkscape::NORMAL_MESSAGE, _("Draw over objects to select them; release %s to switch to rubberband selection"), touch_path.c_str()); - } else if (mode == RUBBERBAND_MODE_TOUCHRECT) { + } else if (operation == RUBBERBAND_MODE_TOUCHRECT) { this->defaultMessageContext()->setF(Inkscape::NORMAL_MESSAGE, _("Drag near objects to select them; press %s to switch to touch selection"), touch_path.c_str()); } else { @@ -407,7 +407,7 @@ bool BuilderTool::event_motion_handler(GdkEvent *event) bool BuilderTool::event_key_press_handler(GdkEvent *event) { - set_current_mode(event); + set_current_operation(event); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); Inkscape::Selection *selection = desktop->getSelection(); @@ -434,7 +434,7 @@ bool BuilderTool::event_key_press_handler(GdkEvent *event) break; case GDK_KEY_space: - /* stamping mode: show outline mode moving */ + /* stamping operation: show outline operation moving */ /* FIXME: Is next condition ok? (lauris) */ if (this->dragging && this->grabbed) { _seltrans->stamp(); @@ -512,7 +512,7 @@ bool BuilderTool::event_key_press_handler(GdkEvent *event) bool BuilderTool::event_key_release_handler(GdkEvent *event) { - set_current_mode(event); + set_current_operation(event); guint keyval = get_latin_keyval(&event->key); if (key_is_a_modifier (keyval)) { this->defaultMessageContext()->clear(); @@ -521,25 +521,25 @@ bool BuilderTool::event_key_release_handler(GdkEvent *event) return false; } -void BuilderTool::perform_operation(Selection *selection, int mode) +void BuilderTool::perform_operation(Selection *selection, int operation) { int size = selection->size(); if (shapes_builder.is_started()) { - if (mode == SELECT_AND_UNION) { + if (operation == SELECT_AND_UNION) { shapes_builder.set_union(selection); - } else if (mode == SELECT_AND_DELETE) { + } else if (operation == SELECT_AND_DELETE) { shapes_builder.set_delete(selection); } return; } - if (mode != JUST_SELECT && size > 1) { - if (mode == SELECT_AND_UNION) { + if (operation != JUST_SELECT && size > 1) { + if (operation == SELECT_AND_UNION) { selection->pathUnion(); - } else if (mode == SELECT_AND_DELETE) { + } else if (operation == SELECT_AND_DELETE) { selection->pathDiff(); - } else if (mode == SELECT_AND_INTERSECT) { + } else if (operation == SELECT_AND_INTERSECT) { selection->pathIntersect(); } selection->clear(); @@ -548,8 +548,8 @@ void BuilderTool::perform_operation(Selection *selection, int mode) void BuilderTool::perform_current_operation(Selection *selection) { - int mode = get_current_mode(); - return perform_operation(selection, mode); + int operation = get_current_operation(); + return perform_operation(selection, operation); } void BuilderTool::set_modifiers_state(GdkEvent* event) @@ -563,7 +563,7 @@ void BuilderTool::set_modifiers_state(GdkEvent* event) shift_on = modifiers & GDK_SHIFT_MASK; } -int BuilderTool::get_current_mode() +int BuilderTool::get_current_operation() { if (ctrl_on) { if (alt_on && !in_interactive_mode()) return SELECT_AND_INTERSECT; @@ -573,64 +573,64 @@ int BuilderTool::get_current_mode() if (shift_on && !in_interactive_mode()) return JUST_SELECT; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - return prefs->getInt("/tools/builder/mode", 0); + return prefs->getInt("/tools/builder/operation", 0); } -void BuilderTool::set_current_mode(int current_mode) +void BuilderTool::set_current_operation(int current_operation) { - if (current_mode == -1) { - current_mode = get_current_mode(); + if (current_operation == -1) { + current_operation = get_current_operation(); } - if (current_mode == active_mode) { + if (current_operation == active_operation) { return; } if (in_interactive_mode() && - (current_mode == SELECT_AND_INTERSECT || current_mode == JUST_SELECT)) { + (current_operation == SELECT_AND_INTERSECT || current_operation == JUST_SELECT)) { return; } - active_mode = current_mode; - set_cursor_mode(); + active_operation = current_operation; + set_cursor_operation(); set_rubberband_color(); // TODO add a function here to change the // patter of the items the cursor went over. } -void BuilderTool::set_current_mode(GdkEvent *event) +void BuilderTool::set_current_operation(GdkEvent *event) { set_modifiers_state(event); - set_current_mode(); + set_current_operation(); } -void BuilderTool::set_cursor_mode() +void BuilderTool::set_cursor_operation() { - if (active_mode > mode_cursor_filenames.size()) { - std::cerr << "BuilderTool: Mode " << active_mode << " is unknown.\n"; + if (active_operation > operation_cursor_filenames.size()) { + std::cerr << "BuilderTool: operation " << active_operation << " is unknown.\n"; return; } - auto ¤t_cursor = mode_cursor_filenames[active_mode]; + auto ¤t_cursor = operation_cursor_filenames[active_operation]; ToolBase::cursor_filename = current_cursor; ToolBase::sp_event_context_update_cursor(); } void BuilderTool::set_rubberband_color() { - if (active_mode > mode_colors.size()) { - std::cerr << "BuilderTool: Mode " << active_mode << " is unknown.\n"; + if (active_operation > operation_colors.size()) { + std::cerr << "BuilderTool: operation " << active_operation << " is unknown.\n"; return; } auto instance = Rubberband::get(desktop); - instance->setColor(mode_colors[active_mode]); + instance->setColor(operation_colors[active_operation]); } -bool BuilderTool::is_mode_add_to_selection(int mode, GdkEvent *event) +bool BuilderTool::is_operation_add_to_selection(int operation, GdkEvent *event) { - return mode == JUST_SELECT && Modifier::get(Modifiers::Type::SELECT_ADD_TO)->active(event->button.state); + return operation == JUST_SELECT && Modifier::get(Modifiers::Type::SELECT_ADD_TO)->active(event->button.state); } void BuilderTool::start_interactive_mode() diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index a62537b8cb..a42a3ea120 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -34,7 +34,7 @@ public: typedef bool (BuilderTool::*EventHandler)(GdkEvent*); - enum Mode { + enum Operation { SELECT_AND_UNION = 0, SELECT_AND_DELETE = 1, SELECT_AND_INTERSECT = 2, @@ -63,8 +63,8 @@ public: const std::string& getPrefsPath() override; - void set_current_mode(GdkEvent* event); - void set_current_mode(int current_mode = -1); + void set_current_operation(GdkEvent* event); + void set_current_operation(int current_operation = -1); void start_interactive_mode(); void end_interactive_mode(); @@ -81,26 +81,26 @@ private: bool event_key_press_handler(GdkEvent* event); bool event_key_release_handler(GdkEvent* event); - void perform_operation(Selection *selection, int mode); + void perform_operation(Selection *selection, int operation); void perform_current_operation(Selection *selection); void set_modifiers_state(GdkEvent* event); - int get_current_mode(); - bool is_mode_add_to_selection(int mode, GdkEvent *event); - void set_cursor_mode(); + int get_current_operation(); + bool is_operation_add_to_selection(int operation, GdkEvent *event); + void set_cursor_operation(); void set_rubberband_color(); bool in_interactive_mode() const; // TODO you might pre-load the cursors and store them // in this vector instead of loading them each time. - static const std::vector mode_cursor_filenames; - static const std::vector mode_colors; + static const std::vector operation_cursor_filenames; + static const std::vector operation_colors; static const std::map handlers; InteractiveShapesBuilder shapes_builder; - int active_mode = JUST_SELECT; // default to the select mode since this is the default cursor. + int active_operation = JUST_SELECT; // default to the select operation since this is the default cursor. bool ctrl_on = false; bool alt_on = false; bool shift_on = false; -- GitLab From 1ed8c1c3dbbd0a30fcdeb81df2eac16597e4c907 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 4 Aug 2021 13:32:30 +0200 Subject: [PATCH 227/235] Set the method BuilderTool::in_interactive_mode() to be public. --- src/ui/tools/builder-tool.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index a42a3ea120..2c8a56ff6e 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -70,6 +70,8 @@ public: void end_interactive_mode(); void toggle_interactive_mode(); + bool in_interactive_mode() const; + private: bool sp_select_context_abort(); @@ -90,8 +92,6 @@ private: void set_cursor_operation(); void set_rubberband_color(); - bool in_interactive_mode() const; - // TODO you might pre-load the cursors and store them // in this vector instead of loading them each time. static const std::vector operation_cursor_filenames; -- GitLab From 6665d7583ab492805134e1475ef7a0fa0aa61559 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Wed, 4 Aug 2021 22:45:15 +0200 Subject: [PATCH 228/235] Implemented the 2 modes (interactive and normal) for the BuilderToolbar I rushed a lot through this commit. The code is not very clean and some bugs might have been introduced. Will refactor and fix later :) --- .../actions/interactive-mode-apply.svg | 43 +++ .../actions/interactive-mode-discard.svg | 46 +++ .../actions/interactive-mode-reset.svg | 66 +++++ .../actions/interactive-mode-apply.svg | 43 +++ .../actions/interactive-mode-discard.svg | 46 +++ .../actions/interactive-mode-reset.svg | 66 +++++ .../actions/interactive-mode-apply.svg | 43 +++ .../actions/interactive-mode-discard.svg | 46 +++ .../actions/interactive-mode-reset.svg | 66 +++++ .../actions/interactive-mode-apply.svg | 43 +++ .../actions/interactive-mode-discard.svg | 46 +++ .../actions/interactive-mode-reset.svg | 66 +++++ src/helper/InteractiveShapesBuilder.cpp | 13 +- src/helper/InteractiveShapesBuilder.h | 3 +- src/ui/toolbar/builder-toolbar.cpp | 273 +++++++++++++++--- src/ui/toolbar/builder-toolbar.h | 35 ++- src/ui/tools/builder-tool.cpp | 59 +++- src/ui/tools/builder-tool.h | 5 +- 18 files changed, 965 insertions(+), 43 deletions(-) create mode 100644 share/icons/Tango/scalable/actions/interactive-mode-apply.svg create mode 100644 share/icons/Tango/scalable/actions/interactive-mode-discard.svg create mode 100644 share/icons/Tango/scalable/actions/interactive-mode-reset.svg create mode 100644 share/icons/hicolor/scalable/actions/interactive-mode-apply.svg create mode 100644 share/icons/hicolor/scalable/actions/interactive-mode-discard.svg create mode 100644 share/icons/hicolor/scalable/actions/interactive-mode-reset.svg create mode 100644 share/icons/hicolor/symbolic/actions/interactive-mode-apply.svg create mode 100644 share/icons/hicolor/symbolic/actions/interactive-mode-discard.svg create mode 100644 share/icons/hicolor/symbolic/actions/interactive-mode-reset.svg create mode 100644 share/icons/multicolor/symbolic/actions/interactive-mode-apply.svg create mode 100644 share/icons/multicolor/symbolic/actions/interactive-mode-discard.svg create mode 100644 share/icons/multicolor/symbolic/actions/interactive-mode-reset.svg diff --git a/share/icons/Tango/scalable/actions/interactive-mode-apply.svg b/share/icons/Tango/scalable/actions/interactive-mode-apply.svg new file mode 100644 index 0000000000..f6896f8c8d --- /dev/null +++ b/share/icons/Tango/scalable/actions/interactive-mode-apply.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/Tango/scalable/actions/interactive-mode-discard.svg b/share/icons/Tango/scalable/actions/interactive-mode-discard.svg new file mode 100644 index 0000000000..df2c414b22 --- /dev/null +++ b/share/icons/Tango/scalable/actions/interactive-mode-discard.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/Tango/scalable/actions/interactive-mode-reset.svg b/share/icons/Tango/scalable/actions/interactive-mode-reset.svg new file mode 100644 index 0000000000..5f3b27f417 --- /dev/null +++ b/share/icons/Tango/scalable/actions/interactive-mode-reset.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/scalable/actions/interactive-mode-apply.svg b/share/icons/hicolor/scalable/actions/interactive-mode-apply.svg new file mode 100644 index 0000000000..f6896f8c8d --- /dev/null +++ b/share/icons/hicolor/scalable/actions/interactive-mode-apply.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/scalable/actions/interactive-mode-discard.svg b/share/icons/hicolor/scalable/actions/interactive-mode-discard.svg new file mode 100644 index 0000000000..df2c414b22 --- /dev/null +++ b/share/icons/hicolor/scalable/actions/interactive-mode-discard.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/scalable/actions/interactive-mode-reset.svg b/share/icons/hicolor/scalable/actions/interactive-mode-reset.svg new file mode 100644 index 0000000000..5f3b27f417 --- /dev/null +++ b/share/icons/hicolor/scalable/actions/interactive-mode-reset.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/symbolic/actions/interactive-mode-apply.svg b/share/icons/hicolor/symbolic/actions/interactive-mode-apply.svg new file mode 100644 index 0000000000..f6896f8c8d --- /dev/null +++ b/share/icons/hicolor/symbolic/actions/interactive-mode-apply.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/symbolic/actions/interactive-mode-discard.svg b/share/icons/hicolor/symbolic/actions/interactive-mode-discard.svg new file mode 100644 index 0000000000..df2c414b22 --- /dev/null +++ b/share/icons/hicolor/symbolic/actions/interactive-mode-discard.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/symbolic/actions/interactive-mode-reset.svg b/share/icons/hicolor/symbolic/actions/interactive-mode-reset.svg new file mode 100644 index 0000000000..5f3b27f417 --- /dev/null +++ b/share/icons/hicolor/symbolic/actions/interactive-mode-reset.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/multicolor/symbolic/actions/interactive-mode-apply.svg b/share/icons/multicolor/symbolic/actions/interactive-mode-apply.svg new file mode 100644 index 0000000000..f6896f8c8d --- /dev/null +++ b/share/icons/multicolor/symbolic/actions/interactive-mode-apply.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/multicolor/symbolic/actions/interactive-mode-discard.svg b/share/icons/multicolor/symbolic/actions/interactive-mode-discard.svg new file mode 100644 index 0000000000..df2c414b22 --- /dev/null +++ b/share/icons/multicolor/symbolic/actions/interactive-mode-discard.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/multicolor/symbolic/actions/interactive-mode-reset.svg b/share/icons/multicolor/symbolic/actions/interactive-mode-reset.svg new file mode 100644 index 0000000000..5f3b27f417 --- /dev/null +++ b/share/icons/multicolor/symbolic/actions/interactive-mode-reset.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index e0f6a3258f..99c50948b6 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -202,7 +202,7 @@ void InteractiveShapesBuilder::commit() } if (is_virgin) { - return reset(); + return discard(); } std::map final_paths; @@ -329,6 +329,17 @@ void InteractiveShapesBuilder::reset_internals() } void InteractiveShapesBuilder::reset() +{ + // TODO do this in a better way + while (!undo_stack.empty()) { + undo(); + } + while (!redo_stack.empty()) { + redo_stack.pop(); + } +} + +void InteractiveShapesBuilder::discard() { for (auto node : enabled) { auto object = document->getObjectByRepr(node.first); diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index 5f333d7e0f..62122ef9da 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -75,7 +75,6 @@ private: void show_items(const std::vector &items); void reset_internals(); - void reset(); XML::Node* draw_and_set_visible(const SubItem &subitem); void perform_union(ObjectSet *set, bool draw_result); @@ -90,6 +89,8 @@ public: bool is_started() const; void start(ObjectSet *set); + void reset(); + void discard(); void commit(); void set_union(ObjectSet *set); diff --git a/src/ui/toolbar/builder-toolbar.cpp b/src/ui/toolbar/builder-toolbar.cpp index 59a27cf239..162ba83739 100644 --- a/src/ui/toolbar/builder-toolbar.cpp +++ b/src/ui/toolbar/builder-toolbar.cpp @@ -22,25 +22,19 @@ #include "desktop.h" #include "document-undo.h" -#include "document.h" #include "selection.h" #include "message-stack.h" #include "selection-chemistry.h" #include "verbs.h" -#include "object/sp-item-transform.h" #include "object/sp-namedview.h" #include "ui/icon-names.h" #include "ui/widget/canvas.h" // Focus widget -#include "ui/widget/combo-tool-item.h" -#include "ui/widget/spin-button-tool-item.h" -#include "ui/widget/spinbutton.h" #include "ui/widget/unit-tracker.h" #include "widgets/widget-sizes.h" -#include "io/resource.h" namespace Inkscape { namespace UI { @@ -49,17 +43,183 @@ namespace Toolbar { BuilderToolbar::BuilderToolbar(SPDesktop *desktop) : Toolbar(desktop) { + init(); +} + +void BuilderToolbar::init() +{ + add_label("Mode: "); + mode_buttons_init(); + add_separator(_operation_widgets); + _operation_widgets.push_back(add_label("Operations: ")); operation_buttons_init(); - add_separator(); + add_separator(_command_widgets); + _command_widgets.push_back(add_label("Commands: ")); boolop_buttons_init(); - add_separator(); + add_separator(_command_widgets); compound_operations_buttons_init(); - add_separator(); + add_separator(_interactive_mode_widgets); interactive_mode_buttons_init(); show_all(); } +void BuilderToolbar::normal_mode_setup() +{ + auto builder_tool = dynamic_cast(_desktop->event_context); + + hide_interactive_mode_buttons(); + show_normal_mode_buttons(); + operation_buttons_init_set_active_button(); + + if (builder_tool) { + if (!builder_tool->in_interactive_mode() && notify_back) { +// std::cout << "In normal mode. Returning.\n"; + return; + } + + if (notify_back) { + interactive_mode_apply(); + } + } + +} + +void BuilderToolbar::set_mode_normal() +{ + mode_changed_called = false; + auto normal_mode_button = _mode_buttons[1]; + normal_mode_button->set_active(true); + if (!mode_changed_called) { + normal_mode_setup(); + } +} + +void BuilderToolbar::interactive_mode_setup() +{ + hide_normal_mode_buttons(); + show_interactive_mode_buttons(); + operation_buttons_init_set_active_button(); +// std::cout << "Set the buttons to interactive mode buttons.\n"; + + auto builder_tool = dynamic_cast(_desktop->event_context); + if (builder_tool) { + if (builder_tool->in_interactive_mode() && notify_back) { +// std::cout << "In builder mode. Returning.\n"; + return; + } + + if (notify_back) { +// std::cout << "Calling BuilderTool::start_interactive_mode\n"; + builder_tool->start_interactive_mode(); + } + + if (!builder_tool->in_interactive_mode()) { +// std::cout << "Couldn't start interactive mode.\n"; +// std::cout << "Setting the active button to normal.\n"; + set_mode_normal(); + } + } + +} + +void BuilderToolbar::set_mode_interactive() +{ + mode_changed_called = false; + auto normal_mode_button = _mode_buttons[0]; + normal_mode_button->set_active(true); + if (!mode_changed_called) { + interactive_mode_setup(); + } +} + +void set_widgets_visibility(const std::vector widgets, bool visibility) +{ + for (auto widget : widgets) { + widget->set_visible(visibility); + } +} + +void BuilderToolbar::show_normal_mode_buttons() +{ + set_widgets_visibility(_operation_widgets, true); + set_widgets_visibility(_command_widgets, true); +} + +void BuilderToolbar::hide_normal_mode_buttons() +{ + _operation_buttons[Tools::BuilderTool::SELECT_AND_INTERSECT]->set_visible(false); + _operation_buttons[Tools::BuilderTool::JUST_SELECT]->set_visible(false); + set_widgets_visibility(_command_widgets, false); +} + +void BuilderToolbar::show_interactive_mode_buttons() +{ + set_widgets_visibility(_interactive_mode_widgets, true); +} + +void BuilderToolbar::hide_interactive_mode_buttons() +{ + set_widgets_visibility(_interactive_mode_widgets, false); +} + +void BuilderToolbar::mode_buttons_init() +{ + // TODO change the icons and tooltips text. + const static std::vector mode_buttons_descriptors = { + { + .label = _("Interactive"), + .tooltip_text = _("Merge and Delete shapes interactively"), + .icon_name = "interactive-builder", + .handler = &BuilderToolbar::interactive_mode_setup, + }, + { + .label = _("Normal"), + .tooltip_text = _("Perform boolean operations"), + .icon_name = "path-union", + .handler = &BuilderToolbar::normal_mode_setup, + }, + }; + + mode_buttons_init_create_buttons(mode_buttons_descriptors); + mode_buttons_init_add_buttons(); +} + +void BuilderToolbar::mode_buttons_init_create_buttons(const std::vector& descriptors) +{ + Gtk::RadioToolButton::Group mode_group; + + for (auto& mode : descriptors) + { + auto button = Gtk::manage(new Gtk::RadioToolButton((mode.label))); + button->set_tooltip_text((mode.tooltip_text)); + button->set_icon_name(INKSCAPE_ICON(mode.icon_name)); + button->set_group(mode_group); + _mode_buttons.push_back(button); + _mode_handlers.push_back(mode.handler); + } +} + +void BuilderToolbar::mode_buttons_init_add_buttons() +{ + int button_index = 0; + for (auto button : _mode_buttons) + { + button->set_sensitive(); + button->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &BuilderToolbar::mode_changed), button_index++)); + _mode_widgets.push_back(button); + add(*button); + } +} + +void BuilderToolbar::mode_changed(int mode) +{ +// std::cout << "Mode changed to " << mode << '\n'; + mode_changed_called = true; + auto handler = _mode_handlers[mode]; + (this->*handler)(); +} + void BuilderToolbar::operation_buttons_init() { // TODO change the icons. @@ -115,7 +275,15 @@ void BuilderToolbar::operation_buttons_init_create_buttons(const std::vectorgetInt("/tools/builder/operation", 0); + + gint type; + auto builder_tool = dynamic_cast(_desktop->event_context); + if (builder_tool && builder_tool->in_interactive_mode()) { + type = prefs->getInt("/tools/builder/interactive_operation", 0); + } else { + type = prefs->getInt("/tools/builder/normal_operation", 0); + } + _operation_buttons[type]->set_active(); } @@ -126,6 +294,7 @@ void BuilderToolbar::operation_buttons_init_add_buttons() { button->set_sensitive(); button->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &BuilderToolbar::operation_changed), button_index++)); + _operation_widgets.push_back(button); add(*button); } } @@ -138,7 +307,13 @@ void BuilderToolbar::operation_changed(int operation) (this->*handler)(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setInt("/tools/builder/operation", operation); + + auto builder_tool = dynamic_cast(_desktop->event_context); + if (builder_tool && builder_tool->in_interactive_mode()) { + prefs->setInt("/tools/builder/interactive_operation", operation); + } else { + prefs->setInt("/tools/builder/normal_operation", operation); + } } void BuilderToolbar::set_current_operation(int operation) @@ -205,6 +380,7 @@ void BuilderToolbar::boolop_buttons_init_actions_add_buttons(const std::vectorset_tooltip_text((boolop.tooltip_text)); button->set_icon_name(INKSCAPE_ICON(boolop.icon_name)); button->signal_clicked().connect(sigc::mem_fun(*this, boolop.handler)); + _command_widgets.push_back(button); add(*button); } } @@ -223,12 +399,12 @@ void BuilderToolbar::perform_flatten() void BuilderToolbar::boolop_buttons_init_verbs() { - add_toolbutton_for_verb(SP_VERB_SELECTION_UNION); - add_toolbutton_for_verb(SP_VERB_SELECTION_DIFF); - add_toolbutton_for_verb(SP_VERB_SELECTION_INTERSECT); - add_toolbutton_for_verb(SP_VERB_SELECTION_SYMDIFF); - add_toolbutton_for_verb(SP_VERB_SELECTION_CUT); - add_toolbutton_for_verb(SP_VERB_SELECTION_SLICE); + _command_widgets.push_back(add_toolbutton_for_verb(SP_VERB_SELECTION_UNION)); + _command_widgets.push_back(add_toolbutton_for_verb(SP_VERB_SELECTION_DIFF)); + _command_widgets.push_back(add_toolbutton_for_verb(SP_VERB_SELECTION_INTERSECT)); + _command_widgets.push_back(add_toolbutton_for_verb(SP_VERB_SELECTION_SYMDIFF)); + _command_widgets.push_back(add_toolbutton_for_verb(SP_VERB_SELECTION_CUT)); + _command_widgets.push_back(add_toolbutton_for_verb(SP_VERB_SELECTION_SLICE)); } void BuilderToolbar::compound_operations_buttons_init() @@ -260,8 +436,8 @@ void BuilderToolbar::perform_split_non_intersecting() void BuilderToolbar::compound_operations_buttons_init_verbs() { - add_toolbutton_for_verb(SP_VERB_SELECTION_COMBINE); - add_toolbutton_for_verb(SP_VERB_SELECTION_BREAK_APART); + _command_widgets.push_back(add_toolbutton_for_verb(SP_VERB_SELECTION_COMBINE)); + _command_widgets.push_back(add_toolbutton_for_verb(SP_VERB_SELECTION_BREAK_APART)); } void BuilderToolbar::interactive_mode_buttons_init() @@ -269,16 +445,52 @@ void BuilderToolbar::interactive_mode_buttons_init() // TODO find a better way than this. Using verbs is easier. const static std::vector interactive_mode_buttons_descriptors = { { - .label = _("Interactive Mode"), - .tooltip_text = _("Start/End interactive mode"), - .icon_name = "interactive-builder", - .handler = &BuilderToolbar::perform_toggle_interactive_mode, + .label = _("Apply"), + .tooltip_text = _("Apply changes"), + .icon_name = "interactive-mode-apply", + .handler = &BuilderToolbar::interactive_mode_apply, + }, + { + .label = _("Reset"), + .tooltip_text = _("Reset changes"), + .icon_name = "interactive-mode-reset", + .handler = &BuilderToolbar::interactive_mode_reset, + }, + { + .label = _("Discard"), + .tooltip_text = _("Discard interactive mode"), + .icon_name = "interactive-mode-discard", + .handler = &BuilderToolbar::interactive_mode_discard, }, }; interactive_mode_buttons_init_add_buttons(interactive_mode_buttons_descriptors); } +void BuilderToolbar::interactive_mode_apply() +{ + auto builder_tool = dynamic_cast(_desktop->event_context); + if (builder_tool) { + builder_tool->apply(); + } +} + +void BuilderToolbar::interactive_mode_reset() +{ + auto builder_tool = dynamic_cast(_desktop->event_context); + if (builder_tool) { + builder_tool->reset(); + } +} + +void BuilderToolbar::interactive_mode_discard() +{ + auto builder_tool = dynamic_cast(_desktop->event_context); + if (builder_tool) { + builder_tool->discard(); + } +} + void BuilderToolbar::interactive_mode_buttons_init_add_buttons(const std::vector &descriptors) { for (auto&descriptor : descriptors) @@ -287,21 +499,16 @@ void BuilderToolbar::interactive_mode_buttons_init_add_buttons(const std::vector button->set_tooltip_text((descriptor.tooltip_text)); button->set_icon_name(INKSCAPE_ICON(descriptor.icon_name)); button->signal_clicked().connect(sigc::mem_fun(*this, descriptor.handler)); + _interactive_mode_widgets.push_back(button); add(*button); } } -void BuilderToolbar::perform_toggle_interactive_mode() -{ - auto builder_tool = dynamic_cast(_desktop->event_context); - if (builder_tool) { - builder_tool->toggle_interactive_mode(); - } -} - -void BuilderToolbar::add_separator() +void BuilderToolbar::add_separator(std::vector &group) { - add(* Gtk::manage(new Gtk::SeparatorToolItem())); + auto separator = Gtk::manage(new Gtk::SeparatorToolItem()); + group.push_back(separator); + add(*separator); } GtkWidget * diff --git a/src/ui/toolbar/builder-toolbar.h b/src/ui/toolbar/builder-toolbar.h index dba7edadd0..30f10fec7f 100644 --- a/src/ui/toolbar/builder-toolbar.h +++ b/src/ui/toolbar/builder-toolbar.h @@ -51,6 +51,31 @@ private: std::vector _operation_buttons; std::vector _operation_handlers; + std::vector _mode_buttons; + std::vector _mode_handlers; + + std::vector _mode_widgets; + std::vector _operation_widgets; + std::vector _command_widgets; + std::vector _interactive_mode_widgets; + + bool mode_changed_called = false; + + void init(); + + void mode_buttons_init(); + void mode_buttons_init_create_buttons(const std::vector& descriptors); + void mode_buttons_init_add_buttons(); + void mode_changed(int mode); + + void normal_mode_setup(); + void interactive_mode_setup(); + + void show_normal_mode_buttons(); + void hide_normal_mode_buttons(); + void show_interactive_mode_buttons(); + void hide_interactive_mode_buttons(); + // operation related methods { void operation_buttons_init(); void operation_buttons_init_create_buttons(const std::vector& descriptors); @@ -81,15 +106,21 @@ private: void interactive_mode_buttons_init(); void interactive_mode_buttons_init_add_buttons(const std::vector &descriptors); - void perform_toggle_interactive_mode(); + void interactive_mode_apply(); + void interactive_mode_reset(); + void interactive_mode_discard(); - void add_separator(); + void add_separator(std::vector &group); protected: BuilderToolbar(SPDesktop *desktop); public: + void set_mode_normal(); + void set_mode_interactive(); static GtkWidget * create(SPDesktop *desktop); + + bool notify_back = true; }; } diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 8b1562fb1d..3ec0a286c5 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -49,6 +49,8 @@ #include "ui/widget/canvas.h" +#include "ui/toolbar/builder-toolbar.h" + #ifdef WITH_DBUS #include "extension/dbus/document-interface.h" #endif @@ -573,7 +575,11 @@ int BuilderTool::get_current_operation() if (shift_on && !in_interactive_mode()) return JUST_SELECT; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - return prefs->getInt("/tools/builder/operation", 0); + if (in_interactive_mode()) { + return prefs->getInt("/tools/builder/interactive_operation", 0); + } else { + return prefs->getInt("/tools/builder/normal_operation", 0); + } } void BuilderTool::set_current_operation(int current_operation) @@ -636,30 +642,73 @@ bool BuilderTool::is_operation_add_to_selection(int operation, GdkEvent *event) void BuilderTool::start_interactive_mode() { Inkscape::Selection *selection = desktop->getSelection(); + + auto toolbar = desktop->get_toolbar_by_name("BuilderToolbar"); + auto builder_toolbar = dynamic_cast(toolbar); + shapes_builder.start(selection); + + builder_toolbar->notify_back = false; if (in_interactive_mode()) { desktop->getSelection()->deactivate(); +// std::cout << "Calling BuilderToolbar::set_mode_interactive\n"; + builder_toolbar->set_mode_interactive(); + } else { + builder_toolbar->set_mode_normal(); } + builder_toolbar->notify_back = true; } void BuilderTool::end_interactive_mode() { shapes_builder.commit(); desktop->getSelection()->activate(); + auto toolbar = desktop->get_toolbar_by_name("BuilderToolbar"); + auto builder_toolbar = dynamic_cast(toolbar); + if (builder_toolbar) { + builder_toolbar->notify_back = false; + builder_toolbar->set_mode_normal(); + builder_toolbar->notify_back = true; + } +} + +bool BuilderTool::in_interactive_mode() const +{ + return shapes_builder.is_started(); } -void BuilderTool::toggle_interactive_mode() +void BuilderTool::apply() { if (in_interactive_mode()) { end_interactive_mode(); } else { - start_interactive_mode(); + std::cerr << "Applying while not in interactive mode?...\n"; } } -bool BuilderTool::in_interactive_mode() const +void BuilderTool::reset() { - return shapes_builder.is_started(); + if (in_interactive_mode()) { + shapes_builder.reset(); + } else { + std::cerr << "Resetting while not in interactive mode?...\n"; + } +} + +void BuilderTool::discard() +{ + if (in_interactive_mode()) { + shapes_builder.discard(); + desktop->getSelection()->activate(); + auto toolbar = desktop->get_toolbar_by_name("BuilderToolbar"); + auto builder_toolbar = dynamic_cast(toolbar); + if (builder_toolbar) { + builder_toolbar->notify_back = false; + builder_toolbar->set_mode_normal(); + } + } else { + std::cerr << "Discarding while not in interactive mode?...\n"; + } } } diff --git a/src/ui/tools/builder-tool.h b/src/ui/tools/builder-tool.h index 2c8a56ff6e..8aa54133c4 100644 --- a/src/ui/tools/builder-tool.h +++ b/src/ui/tools/builder-tool.h @@ -68,10 +68,13 @@ public: void start_interactive_mode(); void end_interactive_mode(); - void toggle_interactive_mode(); bool in_interactive_mode() const; + void apply(); + void reset(); + void discard(); + private: bool sp_select_context_abort(); -- GitLab From 78d56238c2ea2a9c9d95695a0f5e6aed160cd325 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 6 Aug 2021 20:48:55 +0200 Subject: [PATCH 229/235] Set the InteractiveShapesBuilder to use IDs instead of relying on the xml nodes. This should solve the undo (and the reset) bug. One problem that is noticed (not because of this commit) is that the NonIntersectingPathsBuilder::get_operation_result keeps iterating forever in some cases. Also, fracture is not operating correctly. --- src/helper/InteractiveShapesBuilder.cpp | 186 ++++++++++++++++-------- src/helper/InteractiveShapesBuilder.h | 39 +++-- src/ui/toolbar/builder-toolbar.cpp | 2 + src/ui/tools/builder-tool.cpp | 5 + 4 files changed, 161 insertions(+), 71 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 99c50948b6..7c79174805 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -103,40 +103,45 @@ void Inkscape::InteractiveShapesBuilder::start(Inkscape::ObjectSet *set) int n = nodes.size(); for (int i = 0; i < n; i++) { - add_disabled_node(nodes[i], subitems[i]); + add_disabled_item(nodes[i], subitems[i]); } started = true; is_virgin = true; } -std::vector> InteractiveShapesBuilder::get_subitems(const std::vector &items) +std::vector InteractiveShapesBuilder::get_subitems(const std::vector &items) { - std::vector> result; + std::vector result; for (auto item : items) { + auto repr = item->getRepr(); - auto subitem = enabled.find(repr); + int item_id = get_id_from_node(repr); + + auto subitem = enabled.find(item_id); if (subitem == enabled.end()) { - subitem = disabled.find(repr); + subitem = disabled.find(item_id); if (subitem == disabled.end()) { + std::cerr << "InteractiveShapesBuilder::get_subitems: Item that is not in either the enabled or disabled sets is involved?...\n"; continue; } } + result.emplace_back(*subitem); + } + return result; } -SubItem InteractiveShapesBuilder::get_union_subitem(const std::vector> &subitems) +SubItem InteractiveShapesBuilder::get_union_subitem(const std::vector &subitems) { - // TODO find a better way of taking arguments. - // the first of the pair is not related here. - - SubItem res_subitem = subitems.back().second; + SubItem res_subitem = get_subitem_from_id(subitems.back()); for (int i = 0; i < subitems.size() - 1; i++) { - auto &subitem = subitems[i].second; + int subitem_id = subitems[i]; + auto &subitem = get_subitem_from_id(subitem_id); res_subitem.paths = sp_pathvector_boolop(res_subitem.paths, subitem.paths, bool_op_union, fill_nonZero, fill_nonZero); res_subitem.items.insert(subitem.items.begin(), subitem.items.end()); } @@ -147,10 +152,12 @@ SubItem InteractiveShapesBuilder::get_union_subitem(const std::vector items) { for (auto item : items) { + auto repr = item->getRepr(); + int id = get_id_from_node(repr); - remove_enabled_node(repr); - remove_disabled_node(repr); + remove_enabled_item(id); + remove_disabled_item(id); delete_object(item); } @@ -177,9 +184,9 @@ void InteractiveShapesBuilder::perform_union(ObjectSet *set, bool draw_result) node = place_holder++; } - add_enabled_node(node, subitem); + int id = add_enabled_item(node, subitem); - push_undo_command({{node, subitem}, std::move(subitems), draw_result}); + push_undo_command({id, std::move(subitems), draw_result}); remove_items(items); is_virgin = false; @@ -210,8 +217,8 @@ void InteractiveShapesBuilder::commit() final_paths[item] = item->get_pathvector(); } - for (auto subitem_it : enabled) { - auto &subitem = subitem_it.second; + for (auto subitem_id : enabled) { + auto &subitem = get_subitem_from_id(subitem_id); auto &items = subitem.items; for (auto item : items) { auto paths_it = final_paths.find(item); @@ -240,53 +247,108 @@ void InteractiveShapesBuilder::commit() DocumentUndo::done(document, "Interactive Mode", INKSCAPE_ICON("interactive-builder")); } -void InteractiveShapesBuilder::add_disabled_node(XML::Node *node, const SubItem &subitem) +XML::Node* InteractiveShapesBuilder::get_node_from_id(int id) +{ + auto node = id_to_node.find(id); + if (node == id_to_node.end()) { + std::cerr << "InteractiveShapesBuilder::get_node_from_id: ID << " << id << " is not registered.\n"; + } + return node->second; +} + +int InteractiveShapesBuilder::get_id_from_node(XML::Node *node) { - set_style_disabled(node); - disabled[node] = subitem; + auto id = node_to_id.find(node); + if (id == node_to_id.end()) { + std::cerr << "InteractiveShapesBuilder::get_node_from_id: Node << " << node << " is not registered.\n"; + } + return id->second; } -void InteractiveShapesBuilder::remove_disabled_node(XML::Node *node) +SubItem &InteractiveShapesBuilder::get_subitem_from_id(int id) { - auto result = disabled.find(node); - if (result == disabled.end()) { -// std::cerr << "InteractiveShapesBuilder: Node " << node << " isn't in the disabled set.\n"; - } else { - restore_original_style(node); + auto subitem = id_to_subitem.find(id); + if (subitem == id_to_subitem.end()) { + std::cerr << "InteractiveShapesBuilder::get_node_from_id: ID << " << id << " is not registered.\n"; + } + return subitem->second; +} + +void InteractiveShapesBuilder::renew_node_id(XML::Node *node, int id) +{ + id_to_node[id] = node; + node_to_id[node] = id; +} + +int InteractiveShapesBuilder::add_disabled_item(XML::Node *node, int id) +{ + renew_node_id(node, id); + disabled.insert(id); + set_style_disabled(id); + return id; +} + +int InteractiveShapesBuilder::add_disabled_item(XML::Node *node, const SubItem &subitem) +{ + int id = last_id++; + id_to_subitem[id] = subitem; + return add_disabled_item(node, id); +} + +void InteractiveShapesBuilder::remove_disabled_item(int id) +{ + auto result = disabled.find(id); + if (result != disabled.end()) { + restore_original_style(id); disabled.erase(result); + auto node = get_node_from_id(id); + id_to_node.erase(id); + node_to_id.erase(node); } } -void InteractiveShapesBuilder::add_enabled_node(XML::Node *node, const SubItem &subitem) +int InteractiveShapesBuilder::add_enabled_item(XML::Node *node, int id) { - enabled[node] = subitem; + renew_node_id(node, id); + enabled.insert(id); + return id; } -void InteractiveShapesBuilder::remove_enabled_node(XML::Node *node) +int InteractiveShapesBuilder::add_enabled_item(XML::Node *node, const SubItem &subitem) { - auto result = enabled.find(node); - if (result == enabled.end()) { -// std::cerr << "InteractiveShapesBuilder: Node " << node << " isn't in the enabled set.\n"; - } else { + int id = last_id++; + id_to_subitem[id] = subitem; + return add_enabled_item(node, id); +} + +void InteractiveShapesBuilder::remove_enabled_item(int id) +{ + auto result = enabled.find(id); + if (result != enabled.end()) { enabled.erase(result); + auto node = get_node_from_id(id); + id_to_node.erase(id); + node_to_id.erase(node); } } -void InteractiveShapesBuilder::set_style_disabled(XML::Node *node) +void InteractiveShapesBuilder::set_style_disabled(int id) { + auto node = get_node_from_id(id); std::string original_style = node->attribute("style"); - original_styles[node] = original_style; + original_styles[id] = original_style; std::string new_style = setSubAttribute(original_style, "opacity", "0.5"); node->setAttribute("style", new_style); } -void InteractiveShapesBuilder::restore_original_style(XML::Node *node) +void InteractiveShapesBuilder::restore_original_style(int id) { - auto style = original_styles.find(node); + auto style = original_styles.find(id); if (style == original_styles.end()) { // std::cerr << "InteractiveShapeBuilder: The node " << node << " doesn't have its original style stored.\n"; return; } + auto node = get_node_from_id(id); node->setAttribute("style", style->second); } @@ -306,13 +368,15 @@ void InteractiveShapesBuilder::show_items(const std::vector &items) void InteractiveShapesBuilder::reset_internals() { - for (auto node : disabled) { - auto object = document->getObjectByRepr(node.first); + for (auto node_id : disabled) { + auto repr = get_node_from_id(node_id); + auto object = document->getObjectByRepr(repr); delete_object(object); } show_items(not_selected_items); + last_id = 0; started = false; is_virgin = true; enabled.clear(); @@ -320,6 +384,9 @@ void InteractiveShapesBuilder::reset_internals() selected_items.clear(); not_selected_items.clear(); original_styles.clear(); + id_to_subitem.clear(); + id_to_node.clear(); + node_to_id.clear(); while (!undo_stack.empty()) { undo_stack.pop(); } @@ -341,8 +408,9 @@ void InteractiveShapesBuilder::reset() void InteractiveShapesBuilder::discard() { - for (auto node : enabled) { - auto object = document->getObjectByRepr(node.first); + for (auto node_id : enabled) { + auto repr = get_node_from_id(node_id); + auto object = document->getObjectByRepr(repr); delete_object(object); } @@ -376,11 +444,10 @@ void InteractiveShapesBuilder::undo() auto command = undo_stack.top(); undo_stack.pop(); - auto node = command.result.first; - remove_enabled_node(node); - remove_disabled_node(node); + int node_id = command.result; if (command.draw_result) { + auto node = get_node_from_id(node_id); auto object = document->getObjectByRepr(node); if (object) { delete_object(object); @@ -389,17 +456,18 @@ void InteractiveShapesBuilder::undo() } } - for (auto &operand : command.operands) { - auto old_node = operand.first; - auto new_node = draw_and_set_visible(operand.second); - operand.first = new_node; + remove_enabled_item(node_id); + remove_disabled_item(node_id); + + for (auto &id : command.operands) { + auto node = draw_and_set_visible(id_to_subitem[id]); // TODO quick hack. change it later. // if the node exits in original_styles, then it was disabled. - if (original_styles.find(old_node) != original_styles.end()) { - add_disabled_node(operand.first, operand.second); + if (original_styles.find(id) != original_styles.end()) { + add_disabled_item(node, id); } else { - add_enabled_node(operand.first, operand.second); + add_enabled_item(node, id); } } @@ -421,22 +489,24 @@ void InteractiveShapesBuilder::redo() redo_stack.pop(); if (command.draw_result) { - auto node = draw_and_set_visible(command.result.second); - auto &subitem = command.result.second; - add_enabled_node(node, subitem); + int id = command.result; + auto &subitem = get_subitem_from_id(id); + auto node = draw_and_set_visible(subitem); + add_enabled_item(node, id); } - for (auto &operand : command.operands) + for (auto &id : command.operands) { - auto node = operand.first; - remove_enabled_node(node); - remove_disabled_node(node); + auto node = get_node_from_id(id); auto object = document->getObjectByRepr(node); if (object) { delete_object(object); } else { std::cerr << "InteractiveShapesBuilder::redo: Node " << node << " doesn't have an object...\n"; } + + remove_enabled_item(id); + remove_disabled_item(id); } undo_stack.push(command); diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index 62122ef9da..97544d8968 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -41,8 +41,8 @@ private: struct UnionCommand { - std::pair result; - std::vector> operands; + int result; + std::vector operands; bool draw_result; }; @@ -52,24 +52,37 @@ private: std::vector selected_items; std::vector not_selected_items; - std::map enabled; - std::map disabled; - std::map original_styles; + std::set enabled; + std::set disabled; + std::map original_styles; std::stack undo_stack; std::stack redo_stack; + std::map id_to_node; + std::map node_to_id; + std::map id_to_subitem; + + int last_id = 0; bool started = false; bool is_virgin = true; - void add_disabled_node(XML::Node *node, const SubItem &subitem); - void remove_disabled_node(XML::Node *node); + XML::Node* get_node_from_id(int id); + int get_id_from_node(XML::Node *node); + SubItem& get_subitem_from_id(int id); + + void renew_node_id(XML::Node *node, int id); + + int add_disabled_item(XML::Node *node, int id); + int add_disabled_item(XML::Node *node, const SubItem &subitem); + void remove_disabled_item(int id); - void add_enabled_node(XML::Node *node, const SubItem &subitem); - void remove_enabled_node(XML::Node *node); + int add_enabled_item(XML::Node *node, int id); + int add_enabled_item(XML::Node *node, const SubItem &subitem); + void remove_enabled_item(int id); - void set_style_disabled(XML::Node *node); - void restore_original_style(XML::Node *node); + void set_style_disabled(int id); + void restore_original_style(int id); void hide_items(const std::vector &items); void show_items(const std::vector &items); @@ -79,8 +92,8 @@ private: XML::Node* draw_and_set_visible(const SubItem &subitem); void perform_union(ObjectSet *set, bool draw_result); - std::vector> get_subitems(const std::vector &items); - SubItem get_union_subitem(const std::vector> &subitems); + std::vector get_subitems(const std::vector &items); + SubItem get_union_subitem(const std::vector &subitems); void remove_items(const std::vector items); void push_undo_command(const UnionCommand& command); diff --git a/src/ui/toolbar/builder-toolbar.cpp b/src/ui/toolbar/builder-toolbar.cpp index 162ba83739..16eec591b3 100644 --- a/src/ui/toolbar/builder-toolbar.cpp +++ b/src/ui/toolbar/builder-toolbar.cpp @@ -66,6 +66,7 @@ void BuilderToolbar::init() void BuilderToolbar::normal_mode_setup() { +// std::cout << "In normal_mode_setup.\n"; auto builder_tool = dynamic_cast(_desktop->event_context); hide_interactive_mode_buttons(); @@ -97,6 +98,7 @@ void BuilderToolbar::set_mode_normal() void BuilderToolbar::interactive_mode_setup() { +// std::cout << "In interactive_mode_setup.\n"; hide_normal_mode_buttons(); show_interactive_mode_buttons(); operation_buttons_init_set_active_button(); diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp index 3ec0a286c5..6e7f9dcad4 100644 --- a/src/ui/tools/builder-tool.cpp +++ b/src/ui/tools/builder-tool.cpp @@ -641,6 +641,8 @@ bool BuilderTool::is_operation_add_to_selection(int operation, GdkEvent *event) void BuilderTool::start_interactive_mode() { +// std::cout << "started start_interactive_mode.\n"; + Inkscape::Selection *selection = desktop->getSelection(); auto toolbar = desktop->get_toolbar_by_name("BuilderToolbar"); @@ -654,9 +656,12 @@ void BuilderTool::start_interactive_mode() // std::cout << "Calling BuilderToolbar::set_mode_interactive\n"; builder_toolbar->set_mode_interactive(); } else { +// std::cout << "Calling BuilderToolbar::set_mode_normal\n"; builder_toolbar->set_mode_normal(); } builder_toolbar->notify_back = true; + +// std::cout << "finished start_interactive_mode.\n"; } void BuilderTool::end_interactive_mode() -- GitLab From 5337e8a7393454f8aa65b9d81f4841053c01bf52 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 6 Aug 2021 22:32:08 +0200 Subject: [PATCH 230/235] Not deleting every "enabled" item from the canvas. if it's now drawn, it should not be deleted --- src/helper/InteractiveShapesBuilder.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 7c79174805..2db7fadd3f 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -411,7 +411,11 @@ void InteractiveShapesBuilder::discard() for (auto node_id : enabled) { auto repr = get_node_from_id(node_id); auto object = document->getObjectByRepr(repr); - delete_object(object); + if (object) { + delete_object(object); + } else { + // this is a node that is not drawn (a deleted node). + } } show_items(selected_items); -- GitLab From 19be01486a6a98396f6bae445ef198ad419ef89c Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 7 Aug 2021 04:45:34 +0200 Subject: [PATCH 231/235] Added a maximum operations count in NonIntersectingPathsBuilder::get_operation_result. This should be temporary. --- src/helper/NonIntersectingPathsBuilder.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp index ac82a7823a..6f25888e01 100644 --- a/src/helper/NonIntersectingPathsBuilder.cpp +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -199,13 +199,20 @@ std::vector NonIntersectingPathsBuilder::get_operation_result(SubItemOp result[i] = SubItem(items[i]->get_pathvector(), {items[i]}, items[i]); } + // TODO This should not be there. + int max_operations_count = 5000; + // result will grow as items are pushed // into it, so does result.size(). for (int i = 0; i < result.size(); i++) { + if (!max_operations_count) { break; } for (int j = 0; j < result.size(); j++) { + if (!max_operations_count) { break; } if (i == j) { continue; } + max_operations_count--; + // if 2 subitems share at least one item, then // they don't intersect by definition (since operations // in this class yields non-intersecting paths). continue. -- GitLab From 825cd91317a1879e3fbe8b1ca7cc585788bcf3a2 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 7 Aug 2021 05:36:09 +0200 Subject: [PATCH 232/235] Set InteractiveShapesBuilder::remove_items to take its argument by reference --- src/helper/InteractiveShapesBuilder.cpp | 2 +- src/helper/InteractiveShapesBuilder.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 2db7fadd3f..93a59e61e8 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -149,7 +149,7 @@ SubItem InteractiveShapesBuilder::get_union_subitem(const std::vector &subi return res_subitem; } -void InteractiveShapesBuilder::remove_items(const std::vector items) +void InteractiveShapesBuilder::remove_items(const std::vector &items) { for (auto item : items) { diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h index 97544d8968..80f1b4f54e 100644 --- a/src/helper/InteractiveShapesBuilder.h +++ b/src/helper/InteractiveShapesBuilder.h @@ -94,7 +94,7 @@ private: std::vector get_subitems(const std::vector &items); SubItem get_union_subitem(const std::vector &subitems); - void remove_items(const std::vector items); + void remove_items(const std::vector &items); void push_undo_command(const UnionCommand& command); -- GitLab From 167d705c3b81d78557123a19fa0ae662e623d2e3 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 7 Aug 2021 05:57:28 +0200 Subject: [PATCH 233/235] Added the getSubAttribute function --- src/helper/useful-functions.cpp | 15 +++++++++++++++ src/helper/useful-functions.h | 1 + 2 files changed, 16 insertions(+) diff --git a/src/helper/useful-functions.cpp b/src/helper/useful-functions.cpp index 6c75399488..56f70abc7e 100644 --- a/src/helper/useful-functions.cpp +++ b/src/helper/useful-functions.cpp @@ -130,6 +130,21 @@ XML::Node *draw_on_canvas(const Geom::PathVector &path, SPItem *to_copy_from) return draw_on_canvas(path, to_copy_from, parent, after); } +std::string getSubAttribute(const std::string &str, std::string attr) +{ + attr += ":"; + int start_idx = str.find(attr, 0); + if (start_idx == std::string::npos) { + return ""; + } + + start_idx += attr.size(); // including the "=". + int end_idx = start_idx; + while (end_idx < str.size() && str[end_idx] != ';') end_idx++; + + return str.substr(start_idx, end_idx - start_idx); +} + std::string setSubAttribute(const std::string &str, std::string attr, const std::string &value) { attr += ":"; diff --git a/src/helper/useful-functions.h b/src/helper/useful-functions.h index 50dbba2f67..d14f27511b 100644 --- a/src/helper/useful-functions.h +++ b/src/helper/useful-functions.h @@ -41,6 +41,7 @@ bool is_intersecting(const Path1 &a, const Path2 &b); XML::Node *draw_on_canvas(const Geom::PathVector &path, const SPItem *to_copy_from, XML::Node *parent, XML::Node *after = nullptr); XML::Node *draw_on_canvas(const Geom::PathVector &path, SPItem *to_copy_from); +std::string getSubAttribute(const std::string &str, std::string attr); std::string setSubAttribute(const std::string &str, std::string attr, const std::string &value); void ungroup_all(ObjectSet *set); -- GitLab From 12dc863f935aeee8d2276ac5334efb750fd6e8d3 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 7 Aug 2021 05:57:45 +0200 Subject: [PATCH 234/235] Set a custom stroke for the disabled items --- src/helper/InteractiveShapesBuilder.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/helper/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp index 93a59e61e8..7d521b986b 100644 --- a/src/helper/InteractiveShapesBuilder.cpp +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -332,13 +332,24 @@ void InteractiveShapesBuilder::remove_enabled_item(int id) } } +std::string get_disabled_stroke(const std::string &style) +{ + std::string fill_color = getSubAttribute(style, "fill"); + if (fill_color == "#000000") { + return setSubAttribute(style, "stroke", "#ffffff"); + } + return setSubAttribute(style, "stroke", "#000000"); +} + void InteractiveShapesBuilder::set_style_disabled(int id) { auto node = get_node_from_id(id); std::string original_style = node->attribute("style"); original_styles[id] = original_style; std::string new_style = setSubAttribute(original_style, "opacity", "0.5"); + new_style = get_disabled_stroke(new_style); node->setAttribute("style", new_style); + std::cout << "Original: " << original_style << "\nNew: " << new_style << "\n\n"; } void InteractiveShapesBuilder::restore_original_style(int id) -- GitLab From 479fae960cf769f1a22dbeae294e037eaa770888 Mon Sep 17 00:00:00 2001 From: Jabiertxof Date: Mon, 13 Dec 2021 20:30:30 +0100 Subject: [PATCH 235/235] ... --- src/3rdparty/2geom | 2 +- src/path/path-boolop.cpp | 73 ++++++++++++++++++++++++++++++++++++++-- src/path/path-boolop.h | 3 +- 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/3rdparty/2geom b/src/3rdparty/2geom index 651c48a76e..9d38946b7d 160000 --- a/src/3rdparty/2geom +++ b/src/3rdparty/2geom @@ -1 +1 @@ -Subproject commit 651c48a76e2c0f361feba8a66d4d693a2a0605a9 +Subproject commit 9d38946b7d7a0486a4a75669008112d306309d9e diff --git a/src/path/path-boolop.cpp b/src/path/path-boolop.cpp index 04a66594b7..d5050e5b91 100644 --- a/src/path/path-boolop.cpp +++ b/src/path/path-boolop.cpp @@ -15,7 +15,9 @@ #include +#include <2geom/intersection-graph.h> #include <2geom/svg-path-parser.h> // to get from SVG on boolean to Geom::Path +#include <2geom/utils.h> #include "path-boolop.h" #include "path-util.h" @@ -143,13 +145,80 @@ double get_threshold(SPItem const *item, double threshold) return threshold; } +void +sp_flatten(Geom::PathVector &pathvector, FillRule fillkind) +{ + Path *orig = new Path; + orig->LoadPathVector(pathvector); + Shape *theShape = new Shape; + Shape *theRes = new Shape; + orig->ConvertWithBackData (1.0); + orig->Fill (theShape, 0); + theRes->ConvertToShape (theShape, fillkind); + Path *originaux[1]; + originaux[0] = orig; + Path *res = new Path; + theRes->ConvertToForme (res, 1, originaux, true); + + delete theShape; + delete theRes; + char *res_d = res->svg_dump_path (); + delete res; + delete orig; + pathvector = sp_svg_read_pathv(res_d); +} + + // boolean operations PathVectors A,B -> PathVector result. // This is derived from sp_selected_path_boolop // take the source paths from the file, do the operation, delete the originals and add the results // fra,fra are fill_rules for PathVectors a,b Geom::PathVector -sp_pathvector_boolop(Geom::PathVector const &pathva, Geom::PathVector const &pathvb, bool_op bop, fill_typ fra, fill_typ frb) -{ +sp_pathvector_boolop(Geom::PathVector const &pathva, Geom::PathVector const &pathvb, bool_op bop, fill_typ fra, fill_typ frb, bool livarotonly, bool flattenbefore) +{ + int error = 0; + return sp_pathvector_boolop(pathva, pathvb, bop, fra, frb, livarotonly, flattenbefore, error); +} + +Geom::PathVector +sp_pathvector_boolop(Geom::PathVector const &pathva, Geom::PathVector const &pathvb, bool_op bop, fill_typ fra, fill_typ frb, bool livarotonly, bool flattenbefore, int &error) +{ + if (!livarotonly) { + try { + Geom::PathVector a = pathv_to_linear_and_cubic_beziers(pathva); + Geom::PathVector b = pathv_to_linear_and_cubic_beziers(pathvb); + if (flattenbefore) { + sp_flatten(a, fra); + sp_flatten(b, frb); + } + Geom::PathVector out; + // dont change tolerande give errors on boolops + auto pig = Geom::PathIntersectionGraph(a, b, Geom::EPSILON); + if (bop == bool_op_inters) { + out = pig.getIntersection(); + } else if (bop == bool_op_union) { + out = pig.getUnion(); + } else if (bop == bool_op_diff) { + out = pig.getBminusA(); + } else if (bop == bool_op_symdiff) { + out = pig.getXOR(); + } else if (bop == bool_op_cut) { + out = pig.getBminusA(); + auto tmp = pig.getIntersection(); + out.insert(out.end(),tmp.begin(),tmp.end()); + } else if (bop == bool_op_slice) { + // go to livarot + } + if (out != b) { + return out; + } + } + catch (...) { + std::cerr << "fallback bollop to livarot" << std::endl; + } + } + error = 1; + // extract the livarot Paths from the source objects // also get the winding rule specified in the style diff --git a/src/path/path-boolop.h b/src/path/path-boolop.h index 2e9b5bfbad..f6434f109d 100644 --- a/src/path/path-boolop.h +++ b/src/path/path-boolop.h @@ -14,7 +14,8 @@ #include "livarot/Path.h" // FillRule #include "object/object-set.h" // bool_op -Geom::PathVector sp_pathvector_boolop(Geom::PathVector const &pathva, Geom::PathVector const &pathvb, bool_op bop, FillRule fra, FillRule frb); +Geom::PathVector sp_pathvector_boolop(Geom::PathVector const &pathva, Geom::PathVector const &pathvb, bool_op bop, FillRule fra, FillRule frb, bool livarotonly, bool flattenbefore, int &error); +Geom::PathVector sp_pathvector_boolop(Geom::PathVector const &pathva, Geom::PathVector const &pathvb, bool_op bop, FillRule fra, FillRule frb, bool livarotonly = false, bool flattenbefore = true); std::vector sp_pathvector_cutboolop(Geom::PathVector const &pathva, Geom::PathVector const &pathvb, fill_typ fra, fill_typ frb); #endif // PATH_BOOLOP_H -- GitLab