diff --git a/po/POTFILES.src.in b/po/POTFILES.src.in index 9af8d8553f3fd1c8fb1f6fef0b56361ae434d5b3..57a46162618fa83e37fea5adbf19d8716662ca4f 100644 --- a/po/POTFILES.src.in +++ b/po/POTFILES.src.in @@ -8,11 +8,19 @@ ../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 +../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 @@ -308,6 +316,7 @@ ../src/ui/tool/transform-handle-set.cpp ../src/ui/toolbar/arc-toolbar.cpp ../src/ui/toolbar/box3d-toolbar.cpp +../src/ui/toolbar/builder-toolbar.cpp ../src/ui/toolbar/calligraphy-toolbar.cpp ../src/ui/toolbar/connector-toolbar.cpp ../src/ui/toolbar/dropper-toolbar.cpp @@ -328,6 +337,7 @@ ../src/ui/toolbar/tweak-toolbar.cpp ../src/ui/tools/arc-tool.cpp ../src/ui/tools/box3d-tool.cpp +../src/ui/tools/builder-tool.cpp ../src/ui/tools/calligraphic-tool.cpp ../src/ui/tools/connector-tool.cpp ../src/ui/tools/dropper-tool.cpp diff --git a/po/POTFILES.ui.in b/po/POTFILES.ui.in index 17f6b019a8d961279d30b21113d7e878db293b2e..d24911bfc894458e4f1f41c6a7da06cab7aa51a6 100644 --- a/po/POTFILES.ui.in +++ b/po/POTFILES.ui.in @@ -9,7 +9,12 @@ ../share/ui/gradient-edit.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 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 0000000000000000000000000000000000000000..9ff82591478bcb66be5857627e1720201b973ea5 --- /dev/null +++ b/share/icons/Tango/scalable/actions/interactive-builder.svg @@ -0,0 +1,132 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + 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 0000000000000000000000000000000000000000..f6896f8c8d424dda78777e4194e9e52b661be57e --- /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 0000000000000000000000000000000000000000..df2c414b2279f1a8ad818f981e7f51f0e77b8eb0 --- /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 0000000000000000000000000000000000000000..5f3b27f41736fa5c6d2f5e731cea650b5cead52a --- /dev/null +++ b/share/icons/Tango/scalable/actions/interactive-mode-reset.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 0000000000000000000000000000000000000000..bbb2a098b46f150a19bd219530071f8e9157f108 --- /dev/null +++ b/share/icons/Tango/scalable/actions/path-flatten.svg @@ -0,0 +1,96 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + 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 0000000000000000000000000000000000000000..a0476e22b4cfdf73717699fa67cb0eba443f1b9e --- /dev/null +++ b/share/icons/Tango/scalable/actions/path-fracture.svg @@ -0,0 +1,87 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + 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 0000000000000000000000000000000000000000..51de27569f0793b461a313b6c2724c64a6d9f972 --- /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/Tango/scalable/actions/tool-builder.svg b/share/icons/Tango/scalable/actions/tool-builder.svg new file mode 100644 index 0000000000000000000000000000000000000000..d2d7b45fb9e14a79e96a0cae9c1b47bde3f9818a --- /dev/null +++ b/share/icons/Tango/scalable/actions/tool-builder.svg @@ -0,0 +1,82 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/cursors/cursor-delete.svg b/share/icons/hicolor/cursors/cursor-delete.svg new file mode 100644 index 0000000000000000000000000000000000000000..1562bb66bfcef698b38715115ee976aff25d296f --- /dev/null +++ b/share/icons/hicolor/cursors/cursor-delete.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/cursors/cursor-intersect.svg b/share/icons/hicolor/cursors/cursor-intersect.svg new file mode 100644 index 0000000000000000000000000000000000000000..5217579dfd38e31ce2a76a9b47f69be0e2472f40 --- /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 0000000000000000000000000000000000000000..8c657a2be541d70f31772b1a7762a8084c522d8a --- /dev/null +++ b/share/icons/hicolor/cursors/cursor-union.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + 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 0000000000000000000000000000000000000000..01252829f6cda66f7c9b8b0333077aff0eb52557 --- /dev/null +++ b/share/icons/hicolor/scalable/actions/interactive-builder.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 0000000000000000000000000000000000000000..f6896f8c8d424dda78777e4194e9e52b661be57e --- /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 0000000000000000000000000000000000000000..df2c414b2279f1a8ad818f981e7f51f0e77b8eb0 --- /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 0000000000000000000000000000000000000000..5f3b27f41736fa5c6d2f5e731cea650b5cead52a --- /dev/null +++ b/share/icons/hicolor/scalable/actions/interactive-mode-reset.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 0000000000000000000000000000000000000000..bbb2a098b46f150a19bd219530071f8e9157f108 --- /dev/null +++ b/share/icons/hicolor/scalable/actions/path-flatten.svg @@ -0,0 +1,96 @@ + + + + + + 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 0000000000000000000000000000000000000000..a0476e22b4cfdf73717699fa67cb0eba443f1b9e --- /dev/null +++ b/share/icons/hicolor/scalable/actions/path-fracture.svg @@ -0,0 +1,87 @@ + + + + + + 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 0000000000000000000000000000000000000000..51de27569f0793b461a313b6c2724c64a6d9f972 --- /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/scalable/actions/tool-builder.svg b/share/icons/hicolor/scalable/actions/tool-builder.svg new file mode 100644 index 0000000000000000000000000000000000000000..b7cec90686d2cdb675fb41edd2b7194d5d8617c4 --- /dev/null +++ b/share/icons/hicolor/scalable/actions/tool-builder.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + 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 0000000000000000000000000000000000000000..88c6401f2f8d26be87341a393ccb505cfd811fd1 --- /dev/null +++ b/share/icons/hicolor/symbolic/actions/interactive-builder-symbolic.svg @@ -0,0 +1,80 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + 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 0000000000000000000000000000000000000000..f6896f8c8d424dda78777e4194e9e52b661be57e --- /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 0000000000000000000000000000000000000000..df2c414b2279f1a8ad818f981e7f51f0e77b8eb0 --- /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 0000000000000000000000000000000000000000..5f3b27f41736fa5c6d2f5e731cea650b5cead52a --- /dev/null +++ b/share/icons/hicolor/symbolic/actions/interactive-mode-reset.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/symbolic/actions/path-flatten-symbolic.svg b/share/icons/hicolor/symbolic/actions/path-flatten-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..a4c184ba392344f912eba060e8eb20a20d106f82 --- /dev/null +++ b/share/icons/hicolor/symbolic/actions/path-flatten-symbolic.svg @@ -0,0 +1,94 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/share/icons/hicolor/symbolic/actions/path-fracture-symbolic.svg b/share/icons/hicolor/symbolic/actions/path-fracture-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..b3108eb60ec9251dd4a9724cc1856516a2840d19 --- /dev/null +++ b/share/icons/hicolor/symbolic/actions/path-fracture-symbolic.svg @@ -0,0 +1,87 @@ + + + + + + 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 0000000000000000000000000000000000000000..51de27569f0793b461a313b6c2724c64a6d9f972 --- /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/hicolor/symbolic/actions/tool-builder-symbolic.svg b/share/icons/hicolor/symbolic/actions/tool-builder-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..a0b6fd0a2425ab01b3b7c23b772ecfa8ea0c86f5 --- /dev/null +++ b/share/icons/hicolor/symbolic/actions/tool-builder-symbolic.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + 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 0000000000000000000000000000000000000000..69ebff2861ed737b74a4a288ee388da4c64a3507 --- /dev/null +++ b/share/icons/multicolor/symbolic/actions/interactive-builder-symbolic.svg @@ -0,0 +1,113 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + 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 0000000000000000000000000000000000000000..f6896f8c8d424dda78777e4194e9e52b661be57e --- /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 0000000000000000000000000000000000000000..df2c414b2279f1a8ad818f981e7f51f0e77b8eb0 --- /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 0000000000000000000000000000000000000000..5f3b27f41736fa5c6d2f5e731cea650b5cead52a --- /dev/null +++ b/share/icons/multicolor/symbolic/actions/interactive-mode-reset.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/multicolor/symbolic/actions/path-flatten-symbolic.svg b/share/icons/multicolor/symbolic/actions/path-flatten-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..a4c184ba392344f912eba060e8eb20a20d106f82 --- /dev/null +++ b/share/icons/multicolor/symbolic/actions/path-flatten-symbolic.svg @@ -0,0 +1,94 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/share/icons/multicolor/symbolic/actions/path-fracture-symbolic.svg b/share/icons/multicolor/symbolic/actions/path-fracture-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..b3108eb60ec9251dd4a9724cc1856516a2840d19 --- /dev/null +++ b/share/icons/multicolor/symbolic/actions/path-fracture-symbolic.svg @@ -0,0 +1,87 @@ + + + + + + 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 0000000000000000000000000000000000000000..51de27569f0793b461a313b6c2724c64a6d9f972 --- /dev/null +++ b/share/icons/multicolor/symbolic/actions/path-split-non-intersecting.svg @@ -0,0 +1,135 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + 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 0000000000000000000000000000000000000000..3c4525c3d0d11690fb3bd40b6239580927824c52 --- /dev/null +++ b/share/icons/multicolor/symbolic/actions/tool-builder-symbolic.svg @@ -0,0 +1,76 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/share/ui/menu-edit.ui b/share/ui/menu-edit.ui new file mode 100644 index 0000000000000000000000000000000000000000..51dcaf38109a0bebe2d245bbf24826e23955e0da --- /dev/null +++ b/share/ui/menu-edit.ui @@ -0,0 +1,267 @@ + + + + + +
+ + _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/menu-object.ui b/share/ui/menu-object.ui new file mode 100644 index 0000000000000000000000000000000000000000..253a14e78ac2d0c4b8c47968ba72d2ebe706ab65 --- /dev/null +++ b/share/ui/menu-object.ui @@ -0,0 +1,193 @@ + + + + + +
+ + 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 + +
+
+
+ + + 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 + +
+ +
+
\ 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 0000000000000000000000000000000000000000..96bb52942398ba1a472bcd20abdaaa7db0316a11 --- /dev/null +++ b/share/ui/menu-path.ui @@ -0,0 +1,147 @@ + + + + + +
+ + _Object to Path + app.object-to-path + object-to-path + + + _Stroke to Path + app.stroke-to-path + stroke-to-path + + + _Trace Bitmap... + win.dialog-open + 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 + + + Flatte_n + app.select-path-flatten + path-flatten + + + _Fracture + app.select-path-fracture + path-fracture + +
+ +
+ + _Combine + app.select-path-combine + path-combine + + + Break _Apart + app.select-path-break-apart + path-break-apart + + + Split Non-Intersecting Paths + app.select-path-split-non-intersecting + path-split-non-intersecting + +
+ +
+ + 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 + +
+ +
+
diff --git a/share/ui/menu-view.ui b/share/ui/menu-view.ui index ebbe4e5416953e6219d638119cecaefa6368f410..39f328ae97a64bd8c54140f1d2c8f41f23dd6517 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/toolbar-node.ui b/share/ui/toolbar-node.ui new file mode 100644 index 0000000000000000000000000000000000000000..8c4e4c515d83a38213d5a0f4fa6049bac4ccb850 --- /dev/null +++ b/share/ui/toolbar-node.ui @@ -0,0 +1,224 @@ + + + + + + + True + + + + + + True + app.node-edit-add + node-add + Insert node + + + + + True + app.node-node-delete + node-delete + Delete node + + + + + + True + + + + + + True + app.node-join + node-join + Join nodes + + + + + True + app.node-break + node-break + Break nodes + + + + + + True + + + + + + True + app.node-join-segment + node-join-segment + Join with segment + + + + + True + app.node-delete-segment + node-delete-segment + Delete segment + + + + + + True + + + + + + True + app.node-type-cusp + node-type-cusp + Node Cusp + + + + + True + app.node-type-smooth + node-type-smooth + Node Smooth + + + + + True + app.node-type-symmetric + node-type-symmetric + Node Smooth + + + + + True + app.node-type-auto-smooth + node-type-auto-smooth + Node Auto + + + + + + True + + + + + + True + app.node-segment-line + node-segment-line + Node Line + + + + + True + app.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 + app.node-path-clip-edit + path-clip-edit + Edit clipping paths + + + + + True + app.node-path-mask-edit + path-mask-edit + Edit masks + + + + + True + + win.path-effect-parameter-next + path-effect-parameter-next + Stroke to Path + + + + + + True + + + + + + True + app.node-transform + node-transform + Show Transform Handles + + + + + True + app.show-node-handles + show-node-handles + Show Handles + + + + + True + app.show-path-outline + show-path-outline + Show Outline + + + + + + \ No newline at end of file diff --git a/share/ui/toolbar-select.ui b/share/ui/toolbar-select.ui index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..313a76963c49704b0d2a3e744dbe4d25726cb284 100644 --- a/share/ui/toolbar-select.ui +++ b/share/ui/toolbar-select.ui @@ -0,0 +1,162 @@ + + + + + + + + + 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 + + + True + + + + + + + + + + diff --git a/share/ui/toolbar-tool.ui b/share/ui/toolbar-tool.ui index 2b3825689e53b3e5de068c69ef4b82ceac896474..f83b0db84efc480dfd05442da36697f60f480777 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/3rdparty/2geom b/src/3rdparty/2geom index 651c48a76e2c0f361feba8a66d4d693a2a0605a9..9d38946b7d7a0486a4a75669008112d306309d9e 160000 --- a/src/3rdparty/2geom +++ b/src/3rdparty/2geom @@ -1 +1 @@ -Subproject commit 651c48a76e2c0f361feba8a66d4d693a2a0605a9 +Subproject commit 9d38946b7d7a0486a4a75669008112d306309d9e diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 52fe72825c6b0ace3a183a13bbbb94f423f7b8c0..fb355b7617d554baf6fa8eb39a42aed3d6cfbd87 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -205,12 +205,21 @@ 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-doc-file.h + actions/actions-doc-file.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 @@ -218,13 +227,21 @@ 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 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-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 @@ -409,4 +426,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-canvas-mode.cpp b/src/actions/actions-canvas-mode.cpp index d7fb677d01f99a53aabb886617ba4b364546c146..8d751d19514c5ee5700f75b90daaf6dca29d5f86 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,350 @@ 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 + // 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); +#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 +617,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 +646,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 +671,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 0000000000000000000000000000000000000000..523152c2e0428f661f22c6d8ab433be33c24be56 --- /dev/null +++ b/src/actions/actions-doc-file.cpp @@ -0,0 +1,78 @@ +// 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" + +#include "file.h" +#include "ui/dialog/new-from-template.h" + +void +window_previous(SPDocument* document) +{ + INKSCAPE.switch_desktops_prev(); +} + +void +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.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) +{ + + 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( "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(); + 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 0000000000000000000000000000000000000000..b04d5e3909e6c43ee2ae834aa1eb80064d924ccc --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..2c9b6123b5589a555c54a0fc5a0791044b76b301 --- /dev/null +++ b/src/actions/actions-edit.cpp @@ -0,0 +1,506 @@ +// 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 +#include + +#include "actions-edit.h" +#include "inkscape-application.h" +#include "inkscape-window.h" +#include "desktop.h" +#include "selection-chemistry.h" +#include "object/sp-guide.h" + +void +object_to_pattern(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Objects to Pattern + dt->selection->tile(); +} + +void +pattern_to_object(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Pattern to Objects + dt->selection->untile(); +} + +void +object_to_marker(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Objects to Marker + dt->selection->toMarker(); +} + +void +object_to_guides(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Objects to Guides + dt->selection->toGuides(); +} + +void +undo(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Undo + sp_undo(dt, dt->getDocument()); +} + +void +redo(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Redo + sp_redo(dt, dt->getDocument()); +} + +void +cut(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Cut + dt->selection->cut(); +} + +void +copy(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Copy + dt->selection->copy(); +} + +void +paste(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Paste + sp_selection_paste(dt, false); +} + +void +paste_in_place(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Paste In Place + sp_selection_paste(dt, true); +} + +void +paste_style(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Paste Style + dt->selection->pasteStyle(); +} + +void +paste_size(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Paste Size + dt->selection->pasteSize(true,true); +} + +void +paste_width(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Paste Width + dt->selection->pasteSize(true, false); +} + +void +paste_height(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Paste Height + dt->selection->pasteSize(false, true); +} + +void +paste_size_separately(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Paste Size Separately + dt->selection->pasteSizeSeparately(true, true); +} + +void +paste_width_separately(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Paste Width Separately + dt->selection->pasteSizeSeparately(true, false); +} + +void +paste_height_separately(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Paste Height Separately + dt->selection->pasteSizeSeparately(false, true); +} + +void +duplicate(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Duplicate + dt->selection->duplicate(); +} + +void +clone(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Create Clone + dt->selection->clone(); +} + +void +clone_unlink(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Unlink Clone + dt->selection->unlink(); +} + +void +clone_unlink_recursively(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Unlink Clones recursively + dt->selection->unlinkRecursive(false, true); +} + +void +clone_link(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Relink to Copied + dt->selection->relink(); +} + +void +select_original(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Select Original + dt->selection->cloneOriginal(); +} + +void +clone_link_lpe(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Clone original path (LPE) + dt->selection->cloneOriginalPathLPE(); +} + +void +edit_delete(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Delete + dt->selection->deleteItems(); +} + +void +select_all(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Select All + Inkscape::SelectionHelper::selectAll(dt); +} + +void +select_all_layers(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Select All in All Layers + Inkscape::SelectionHelper::selectAllInAll(dt); +} + +void +select_same_fill_and_stroke(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Fill and Stroke + Inkscape::SelectionHelper::selectSameFillStroke(dt); +} + +void +select_same_fill(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Fill Color + Inkscape::SelectionHelper::selectSameFillColor(dt); +} + +void +select_same_stroke_color(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Stroke Color + Inkscape::SelectionHelper::selectSameStrokeColor(dt); +} + +void +select_same_stroke_style(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Stroke Style + Inkscape::SelectionHelper::selectSameStrokeStyle(dt); +} + +void +select_same_object_type(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Object Type + Inkscape::SelectionHelper::selectSameObjectType(dt); +} + +void +select_invert(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Invert Selection + Inkscape::SelectionHelper::invert(dt); +} + +void +select_none(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Deselect + Inkscape::SelectionHelper::selectNone(dt); +} + +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; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "lock_all_guides: action not SimpleAction!" << std::endl; + 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(); +} + +void +delete_all_guides(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Delete All Guides + sp_guide_delete_all_guides(dt); +} + +void +paste_path_effect(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Paste Path Effect + dt->selection->pastePathEffect(); +} + +void +remove_path_effect(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Remove Path Effect + dt->selection->removeLPE(); +} + +/* Node toolbar : deactivate button when no effect is done ( Currently not added this feature ) */ +void +path_effect_parameter_next(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Next path effect parameter + sp_selection_next_patheffect_param(dt); +} + +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_("Next path effect parameter"), "Edit", N_("Show next editable path effect parameter")} + // clang-format on +}; + +void +add_actions_edit(InkscapeWindow* win) +{ + + /* 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)); + // 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 0000000000000000000000000000000000000000..1f4a1c7fd3adc5c0bb6f3e2a559630b5196e168a --- /dev/null +++ b/src/actions/actions-edit.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_EDIT_H +#define INK_ACTIONS_EDIT_H + +class InkscapeWindow; + +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 79c6d741379cb67d8c94ffb1ad60a4c53b5978b6..1d9d135f60c20e9e8ce0c1ff080f4a0c6d3feacf 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-fit-canvas.cpp b/src/actions/actions-fit-canvas.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3a9dd6d2e3f94295560c081cba709baba7c9102b --- /dev/null +++ b/src/actions/actions-fit-canvas.cpp @@ -0,0 +1,76 @@ +// 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 + +#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(); + + // Fit Page to Selection + dt->selection->fitCanvas(true); +} + +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); + } +} + +void +canvas_to_selection_or_drawing(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Resize Page to Selection + 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) +{ + // 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_fit_canvas: 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 0000000000000000000000000000000000000000..0d47c0af020ee94f3413b102b1d1538b2489ec0a --- /dev/null +++ b/src/actions/actions-fit-canvas.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_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-hide-lock.cpp b/src/actions/actions-hide-lock.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3f4151acb73d448f66a83edecb7e4695c5205337 --- /dev/null +++ b/src/actions/actions-hide-lock.cpp @@ -0,0 +1,65 @@ +// 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 "inkscape-window.h" +#include "desktop.h" +#include "document-undo.h" +#include "selection-chemistry.h" + +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 objects in the current layer"), nullptr); +} + +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 objects in the current layer"), nullptr); +} + +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 0000000000000000000000000000000000000000..fc7fb3eb4f5471bc64ee7e3aef946e1410719afd --- /dev/null +++ b/src/actions/actions-hide-lock.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_HIDE_LOCK_H +#define INK_ACTIONS_HIDE_LOCK_H + +class InkscapeWindow; + +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 0000000000000000000000000000000000000000..f4b242249ab5c358714dc3834c7f3298f4c5dd56 --- /dev/null +++ b/src/actions/actions-hint-data.cpp @@ -0,0 +1,49 @@ +// 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 + +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); + } + return action_names; +} + +Glib::ustring +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); + 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) { + // Action Name , Hint + data.emplace(hint[0], hint[1]); + } +} \ 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 0000000000000000000000000000000000000000..8961d394ba4b0493e9fbd554e6c6b4ae630536dd --- /dev/null +++ b/src/actions/actions-hint-data.h @@ -0,0 +1,36 @@ +// 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 + +#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-node.cpp b/src/actions/actions-node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..44b99b409ffbd47761e781c853c451a8854b69cc --- /dev/null +++ b/src/actions/actions-node.cpp @@ -0,0 +1,362 @@ +// 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 "ui/tools/node-tool.h" +#include "inkscape.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(InkscapeApplication* app) +{ + NodeTool *nt = get_node_tool(); + + // Adding a Node + if (nt) { + nt->_multipath->insertNodes(); + } +} + + +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)); + } +} + +void +node_join(InkscapeApplication* app) +{ + NodeTool *nt = get_node_tool(); + + // Join Nodes + if (nt) { + nt->_multipath->joinNodes(); + } +} + +void +node_break(InkscapeApplication* app) +{ + NodeTool *nt = get_node_tool(); + + // Break nodes + if (nt) { + nt->_multipath->breakNodes(); + } +} + +void +node_join_segment(InkscapeApplication* app) +{ + NodeTool *nt = get_node_tool(); + + // Join with Line (segment) + if (nt) { + nt->_multipath->breakNodes(); + } +} + +void +node_delete_segment(InkscapeApplication* app) +{ + NodeTool *nt = get_node_tool(); + + // Delete segment + if (nt) { + nt->_multipath->breakNodes(); + } +} + +void +node_type_cusp(InkscapeApplication* app) +{ + NodeTool *nt = get_node_tool(); + + // Selected Nodes to Corner + if (nt) { + nt->_multipath->breakNodes(); + } +} + +void +node_type_smooth(InkscapeApplication* app) +{ + NodeTool *nt = get_node_tool(); + + // Node Smooth + if (nt) { + nt->_multipath->setNodeType(Inkscape::UI::NODE_SMOOTH); + } +} + +void +node_type_symmetric(InkscapeApplication* app) +{ + NodeTool *nt = get_node_tool(); + + // Makes selected nodes symmetric + if (nt) { + nt->_multipath->setNodeType(Inkscape::UI::NODE_SYMMETRIC); + } +} + +void +node_type_auto_smooth(InkscapeApplication* app) +{ + NodeTool *nt = get_node_tool(); + + // Node Smooth Auto + if (nt) { + nt->_multipath->setNodeType(Inkscape::UI::NODE_AUTO); + } +} + +void +node_segment_line(InkscapeApplication* app) +{ + NodeTool *nt = get_node_tool(); + + // Node selected segments line + if (nt) { + nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_STRAIGHT); + } +} + +void +node_segment_curve(InkscapeApplication* app) +{ + NodeTool *nt = get_node_tool(); + + // Node selected segments curves + if (nt) { + nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_CUBIC_BEZIER); + } +} + +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; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "node_path_clip_edit: action not SimpleAction!" << std::endl; + 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); +} + +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; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "node_path_mask_edit: action not SimpleAction!" << std::endl; + 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); +} + + +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; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "node_transform: action not SimpleAction!" << std::endl; + 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); +} + +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; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "show_node_handles: action not SimpleAction!" << std::endl; + 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); +} + +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; + return; + } + + auto saction = Glib::RefPtr::cast_dynamic(action); + if (!saction) { + std::cerr << "show_path_outline: action not SimpleAction!" << std::endl; + 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); +} + +std::vector> raw_node_data = +{ + // clang-format off + {"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(InkscapeApplication* app) +{ + auto *gapp = app->gio_app(); + + // clang-format off + 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(); + 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 0000000000000000000000000000000000000000..fce15ee9d814832ad9a3151fcd6de77efc6c77de --- /dev/null +++ b/src/actions/actions-node.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_NODE_H +#define INK_ACTIONS_NODE_H + +class InkscapeApplication; + +void add_actions_node(InkscapeApplication* app); + +#endif // INK_ACTIONS_NODE_H \ No newline at end of file diff --git a/src/actions/actions-object-align.cpp b/src/actions/actions-object-align.cpp index 8b873293e4ddce2661aa3da1ac368210a34e80a8..f3a901a73a5c557c46851a0d7d22816afc2dd8c3 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 e547901f62c58454cd73af3bd40318845cdaabf6..97a3515bf04fb26996f95dd0d51ec167241f5544 100644 --- a/src/actions/actions-object.cpp +++ b/src/actions/actions-object.cpp @@ -21,7 +21,9 @@ #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" +#include "ui/icon-names.h" // No sanity checking is done... should probably add. void @@ -49,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); } @@ -82,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); } @@ -98,6 +100,132 @@ object_unlink_clones(InkscapeApplication *app) selection->unlink(); } +void +set_clip(InkscapeApplication *app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + + // Object Clip Set + selection->setMask(true, false); +} + +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); +} + +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); +} + +void +set_mask(InkscapeApplication *app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + + // Object Mask Set + selection->setMask(false, false); +} + +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); +} + +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) +{ + Inkscape::Selection *selection = app->get_active_selection(); + + // Object Rotate 90 + selection->rotate90(false); +} + +void +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) +{ + Inkscape::Selection *selection = app->get_active_selection(); + + Geom::OptRect bbox = selection->visualBounds(); + if (!bbox) { + return; + } + + // 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) +{ + Inkscape::Selection *selection = app->get_active_selection(); + + Geom::OptRect bbox = selection->visualBounds(); + if (!bbox) { + return; + } + + // Get center + Geom::Point center; + if (selection->center()) { + center = *selection->center(); + } else { + center = bbox->midpoint(); + } + + // 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")); +} + void object_to_path(InkscapeApplication *app) @@ -137,16 +265,33 @@ 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.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 +}; + +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 +309,28 @@ 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( "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)); + 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 app->get_action_extra_data().add_data(raw_data_object); + app->get_action_hint_data().add_data(hint_data_object); } @@ -187,4 +343,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 f2128e24002935a82b1d5f3c6adef4289f8bc9f3..acc80396f1d405bd7aee720d56591c30da66a819 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-desktop.cpp b/src/actions/actions-selection-desktop.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0dce9135e765cedbea1396333ab09af026084562 --- /dev/null +++ b/src/actions/actions-selection-desktop.cpp @@ -0,0 +1,120 @@ +// 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 +#include + +#include "actions-selection-desktop.h" +#include "inkscape-application.h" +#include "inkscape-window.h" +#include "desktop.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) +{ + SPDesktop* dt = win->get_desktop(); + + // Make a Bitmap Copy + dt->selection->createBitmapCopy(); +} + +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); +} + +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); +} + +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); + set_active_tool(dt,"Node"); +} + +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); + set_active_tool(dt, "Node"); +} + +void +select_path_reverse(InkscapeWindow* win) +{ + SPDesktop* dt = win->get_desktop(); + + // Reverse + 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.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 +}; + +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( "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(); + 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 0000000000000000000000000000000000000000..1a2beb40e89f3172bde0a5455246e0d0fa407c73 --- /dev/null +++ b/src/actions/actions-selection-desktop.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_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/actions/actions-selection-object.cpp b/src/actions/actions-selection-object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..53873c9d612a9baaae15891bd487e043372dfce7 --- /dev/null +++ b/src/actions/actions-selection-object.cpp @@ -0,0 +1,116 @@ +// 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 +#include + +#include "actions-selection-object.h" +#include "actions-helper.h" +#include "inkscape-application.h" +#include "inkscape.h" +#include "selection.h" + +void +select_object_group(InkscapeApplication* app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + + // Group + selection->group(); +} + +void +select_object_ungroup(InkscapeApplication* app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + + // Ungroup + selection->ungroup(); +} + +void +select_object_ungroup_pop(InkscapeApplication* app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + + // Pop Selected Objects out of Group + selection->popFromGroup(); +} + +void +selection_top(InkscapeApplication* app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + + // Raise to Top + selection->raiseToTop(); +} + +void +selection_raise(InkscapeApplication* app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + + // Raise + selection->raise(); +} + +void +selection_lower(InkscapeApplication* app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + + // Lower + selection->lower(); +} + +void +selection_bottom(InkscapeApplication* app) +{ + Inkscape::Selection *selection = app->get_active_selection(); + + // Lower to Bottom + selection->lowerToBottom(); +} + +// SHOULD REALLY BE DOC ACTIONS +std::vector> raw_data_selection_object = +{ + // 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 +}; + +void +add_actions_selection_object(InkscapeApplication* app) +{ + auto *gapp = app->gio_app(); + + // clang-format off + 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); +} \ 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 0000000000000000000000000000000000000000..c67b9d26cbd5e43bba0c13824f7d5d1fc1cbdd29 --- /dev/null +++ b/src/actions/actions-selection-object.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_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 7ca7cf8e3bd503dcc530548cdc5a5a9be46ac474..cd1c1f4f68173d539f01f0bf0c10cc990520505c 100644 --- a/src/actions/actions-selection.cpp +++ b/src/actions/actions-selection.cpp @@ -232,21 +232,143 @@ 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_flatten(InkscapeApplication *app) +{ + auto selection = app->get_active_selection(); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); + selection->flatten(); +} + +void +select_path_fracture(InkscapeApplication *app) +{ + auto selection = app->get_active_selection(); + selection->removeLPESRecursive(true); + selection->unlinkRecursive(true); + selection->fracture(); +} + +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 +select_path_split_non_intersecting(InkscapeApplication *app) +{ + auto selection = app->get_active_selection(); + selection->splitNonIntersecting(); +} + +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 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 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-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")}, + {"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 }; @@ -256,18 +378,31 @@ 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-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)); + 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 app->get_action_extra_data().add_data(raw_data_selection); } @@ -282,4 +417,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-tools.cpp b/src/actions/actions-tools.cpp index c0ac1b96ff8170afe5a5ac31f5ff1f7f341c892a..46ea812f0034e311f837c7de514746f072acdcaf 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.") }, @@ -360,6 +362,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 7a4049f74d4791692dad5ae821582d42ff6a1f90..dfde0bfa0fc9f82d1e2a809ffc1c56d9e85e063e 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/actions/actions-transform.cpp b/src/actions/actions-transform.cpp index 81a9ca7a794dbe761ded6c0f90ba429556401450..34511f5efed02b68259adc4ef469ff32a56e7ae5 100644 --- a/src/actions/actions-transform.cpp +++ b/src/actions/actions-transform.cpp @@ -87,10 +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")} // clang-format on }; @@ -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/desktop.cpp b/src/desktop.cpp index 954283fa1bde05833694413a1bc75ae7b991369b..7b8c806b9493e873d67c2b769ce1db68a93f7153 100644 --- a/src/desktop.cpp +++ b/src/desktop.cpp @@ -1323,33 +1323,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(); } @@ -1514,10 +1503,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() @@ -1534,10 +1519,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 40c22f77bbd534755c718684a7cb66e43b2238a2..55df95d426b6d3fb8d597d24e573f0381f4b547b 100644 --- a/src/desktop.h +++ b/src/desktop.h @@ -459,7 +459,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 c76968a9623b90b3a1a734218769a1b993e35e39..c0638c84a08e98a4a638f7ae3dc6cce698655c05 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() { @@ -1455,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; @@ -1482,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(); } /** @@ -1557,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 @@ -1583,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 e011409f79003c841f9f58cf7288451f888f6006..608d74a47199eaf0ef2e1540b0f4f05791e216f6 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; /** diff --git a/src/helper/CMakeLists.txt b/src/helper/CMakeLists.txt index 60e42ac6717b8e2deafe47cf17e0f15b21a0e6fe..8832276c09f8137a4033303c407f17d0c22d3dd4 100644 --- a/src/helper/CMakeLists.txt +++ b/src/helper/CMakeLists.txt @@ -18,10 +18,14 @@ set(helper_SRC geom-pathvectorsatellites.cpp geom-satellite.cpp gettext.cpp + InteractiveShapesBuilder.cpp + disjoint-sets.cpp + NonIntersectingPathsBuilder.cpp 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 @@ -39,11 +43,15 @@ set(helper_SRC geom-satellite.h geom.h gettext.h + InteractiveShapesBuilder.h mathfns.h + disjoint-sets.h + NonIntersectingPathsBuilder.h 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/InteractiveShapesBuilder.cpp b/src/helper/InteractiveShapesBuilder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7d521b986bd066705dfa2ea86b2491955aa16a6c --- /dev/null +++ b/src/helper/InteractiveShapesBuilder.cpp @@ -0,0 +1,531 @@ +// 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" +#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" +#include "selection.h" + +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); + item->deleteObject(true, true); + 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 (is_started()) { + std::cerr << "InteractiveShapesBuilder: already started. Resetting before starting again.\n"; + commit(); + } + + ungroup_all(set); + + NonIntersectingPathsBuilder builder(set); + + builder.perform_fracture(); + if (!builder.items_intersected()) { + return; + } + + auto &subitems = builder.get_result_subitems(); + + 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(); + + for (int i = 0; i < n; i++) { + add_disabled_item(nodes[i], subitems[i]); + } + + started = true; + is_virgin = true; +} + +std::vector InteractiveShapesBuilder::get_subitems(const std::vector &items) +{ + std::vector result; + for (auto item : items) { + + auto repr = item->getRepr(); + int item_id = get_id_from_node(repr); + + auto subitem = enabled.find(item_id); + if (subitem == enabled.end()) { + 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 res_subitem = get_subitem_from_id(subitems.back()); + + for (int i = 0; i < subitems.size() - 1; i++) + { + 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()); + } + + return res_subitem; +} + +void InteractiveShapesBuilder::remove_items(const std::vector &items) +{ + for (auto item : items) { + + auto repr = item->getRepr(); + int id = get_id_from_node(repr); + + remove_enabled_item(id); + remove_disabled_item(id); + + delete_object(item); + } +} + +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); + SubItem subitem = get_union_subitem(subitems); + + XML::Node *node; + + 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++; + } + + int id = add_enabled_item(node, subitem); + + push_undo_command({id, std::move(subitems), draw_result}); + + remove_items(items); + is_virgin = false; +} + +void InteractiveShapesBuilder::set_union(ObjectSet *set) +{ + perform_union(set, true); +} + +void InteractiveShapesBuilder::set_delete(ObjectSet *set) +{ + perform_union(set, false); +} + +void InteractiveShapesBuilder::commit() +{ + if (!is_started()) { + return; + } + + if (is_virgin) { + return discard(); + } + + std::map final_paths; + for (auto item : selected_items) { + final_paths[item] = item->get_pathvector(); + } + + 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); + if (paths_it == final_paths.end()) { + std::cerr << "InteractiveShapesBuilder: No Geom::PathVector is for the item " << item << ".\n"; + continue; + } + 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(final_paths[item])) { + if (!sub_pathvec.empty()) { + draw_on_canvas(sub_pathvec, item); + } + } + delete_object(item); + } + + reset_internals(); + + // FIXME for some reason, this is not working properly. + DocumentUndo::done(document, "Interactive Mode", INKSCAPE_ICON("interactive-builder")); +} + +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) +{ + 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; +} + +SubItem &InteractiveShapesBuilder::get_subitem_from_id(int id) +{ + 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); + } +} + +int InteractiveShapesBuilder::add_enabled_item(XML::Node *node, int id) +{ + renew_node_id(node, id); + enabled.insert(id); + return id; +} + +int InteractiveShapesBuilder::add_enabled_item(XML::Node *node, const SubItem &subitem) +{ + 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); + } +} + +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) +{ + 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); +} + +void InteractiveShapesBuilder::hide_items(const std::vector &items) +{ + for (auto item : items) { + item->setHidden(true); + } +} + +void InteractiveShapesBuilder::show_items(const std::vector &items) +{ + for (auto item : items) { + item->setHidden(false); + } +} + +void InteractiveShapesBuilder::reset_internals() +{ + 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(); + disabled.clear(); + 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(); + } + while (!redo_stack.empty()) { + redo_stack.pop(); + } +} + +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_id : enabled) { + auto repr = get_node_from_id(node_id); + auto object = document->getObjectByRepr(repr); + if (object) { + delete_object(object); + } else { + // this is a node that is not drawn (a deleted node). + } + } + + show_items(selected_items); + 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(); + + 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); + } else { + std::cerr << "InteractiveShapesBuilder::undo: Node " << node << " doesn't have an object...\n"; + } + } + + 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(id) != original_styles.end()) { + add_disabled_item(node, id); + } else { + add_enabled_item(node, id); + } + + } + + 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) { + 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 &id : command.operands) + { + 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); + is_virgin = false; +} + +} \ No newline at end of file diff --git a/src/helper/InteractiveShapesBuilder.h b/src/helper/InteractiveShapesBuilder.h new file mode 100644 index 0000000000000000000000000000000000000000..80f1b4f54e9d0e0a35154cfe1bf9a12051e04295 --- /dev/null +++ b/src/helper/InteractiveShapesBuilder.h @@ -0,0 +1,118 @@ +// 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. + */ + +#pragma once + +#include +#include +#include +#include "NonIntersectingPathsBuilder.h" + +class SPDesktop; +class SPDocument; +class SPItem; + +namespace Inkscape { + +class ObjectSet; + +namespace XML { + class Node; +} + +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: + + struct UnionCommand + { + int result; + std::vector operands; + bool draw_result; + }; + + SPDesktop *desktop; + SPDocument *document; + + std::vector selected_items; + std::vector not_selected_items; + + 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; + + 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); + + 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(int id); + void restore_original_style(int id); + + void hide_items(const std::vector &items); + void show_items(const std::vector &items); + + void reset_internals(); + + 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; + void start(ObjectSet *set); + void reset(); + void discard(); + void commit(); + + void set_union(ObjectSet *set); + void set_delete(ObjectSet *set); + + void undo(); + void redo(); + + ~InteractiveShapesBuilder() { commit(); } +}; + +} \ No newline at end of file diff --git a/src/helper/NonIntersectingPathsBuilder.cpp b/src/helper/NonIntersectingPathsBuilder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6f25888e01c6688ce1f81018c7b61a67ab5905ef --- /dev/null +++ b/src/helper/NonIntersectingPathsBuilder.cpp @@ -0,0 +1,266 @@ +// 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 "NonIntersectingPathsBuilder.h" + +#include +#include +#include +#include +#include + +#include "geom-pathstroke.h" +#include "ui/widget/canvas.h" +#include "useful-functions.h" + +#include "object/object-set.h" + +namespace Inkscape { + +// 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 NonIntersectingPathsBuilder::prepare_input() +{ + // 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()); + + // 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(); + + set_parameters(); +} + +void NonIntersectingPathsBuilder::show_output(bool delete_original) +{ + draw_subitems(result_subitems); + + if (delete_original) { + sp_selection_delete_impl(items); + } +} + +void NonIntersectingPathsBuilder::add_result_to_set() +{ + for (auto node : result_nodes) { + set->add(node); + } +} + +void NonIntersectingPathsBuilder::perform_operation(SubItemOperation operation) +{ + if (set->isEmpty()) { + return; + } + + prepare_input(); + items_intersect = false; + result_subitems = get_operation_result(operation); +} + +void NonIntersectingPathsBuilder::perform_fracture() +{ + auto operation = [](SubItem & a, SubItem & b) { return a.fracture(b); }; + perform_operation(operation); +} + +const std::vector& NonIntersectingPathsBuilder::get_result_subitems() const +{ + return result_subitems; +} + +const std::vector& NonIntersectingPathsBuilder::get_result_nodes() const +{ + return result_nodes; +} + +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")); + } +} + +void NonIntersectingPathsBuilder::set_parameters() +{ + auto _items = set->items(); + // items will be placed in place + // of the first item in the selection. + after = _items.front()->getRepr(); + parent = after->parent(); + + items = std::vector(_items.begin(), _items.end()); +} + +SPDesktop *NonIntersectingPathsBuilder::desktop() +{ + return set->desktop(); +} + +SPItem* SubItem::get_common_item(const SubItem &other_subitem) const +{ + for (auto item : other_subitem.items) { + if (is_subitem_of(item)) { + return item; + } + } + 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; +} + +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 = 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()) { + return {}; + } + + auto diff1_paths = sp_pathvector_boolop(paths, other_subitem.paths, bool_op_diff, fill_nonZero, fill_nonZero); + 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, items, top_item); + + return {intersection, diff1, diff2}; +} + +std::vector split_non_intersecting_paths(std::vector &subitems) +{ + std::vector result; + for (auto &path : subitems) { + auto split = split_non_intersecting_paths(path.paths); + for (auto &split_path : split) { + result.emplace_back(split_path, path.items, path.top_item); + } + } + return result; +} + +std::vector NonIntersectingPathsBuilder::get_operation_result(SubItemOperation operation) +{ + std::vector result; + int n = items.size(); + result.resize(n); + + for (int i = 0; i < n; i++) { + 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. + 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. + + items_intersect = 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); + + result.insert(result.end(), broken.begin(), broken.end()); + + i--; // to cancel the next incrementation. + + break; + } + } + + return split_non_intersecting_paths(result); +} + +void NonIntersectingPathsBuilder::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(subitems[i].paths, subitems[i].top_item); + } +} + +void NonIntersectingPathsBuilder::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--; + } + } +} + +}; diff --git a/src/helper/NonIntersectingPathsBuilder.h b/src/helper/NonIntersectingPathsBuilder.h new file mode 100644 index 0000000000000000000000000000000000000000..8ddb6807aa31606746a6a0c5f24cd49b2c89a355 --- /dev/null +++ b/src/helper/NonIntersectingPathsBuilder.h @@ -0,0 +1,115 @@ +// 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 +#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. + +private: + + XML::Node *parent; + XML::Node *after; + + ObjectSet *set; + std::vector items; + std::vector result_subitems; + std::vector result_nodes; + + bool items_intersect = false; + +public: + + NonIntersectingPathsBuilder(ObjectSet *set) : set(set) {} + void fracture(bool skip_undo = false); + void perform_fracture(); + 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; + bool items_intersected() const { return items_intersect; }; + +private: + + using SubItemOperation = std::function(SubItem &, SubItem &)>; + + void prepare_input(); + void perform_operation(SubItemOperation operation); + std::vector get_operation_result(SubItemOperation operation); + void draw_subitems(const std::vector &subitems); + 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: + + Geom::PathVector paths; + std::set items; + SPItem *top_item; + + 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, 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(); } + + 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; +}; + +} \ No newline at end of file diff --git a/src/helper/disjoint-sets.cpp b/src/helper/disjoint-sets.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7145361680a0ec575d0cde60e2a88cfafee0d8fa --- /dev/null +++ b/src/helper/disjoint-sets.cpp @@ -0,0 +1,67 @@ +// 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) +{ + int parent = parent_of(a); + int child = parent_of(b); + + if (child == parent) { return; } + + int parent_size = size_of_set(parent); + int child_size = size_of_set(child); + + if (parent_size < child_size) { + std::swap(parent, child); + } + + parents[child] = parent; + parents[parent] = -(parent_size + child_size); +} + +int DisjointSets::parent_of(int x) +{ + if (parents[x] < 0) { + return x; + } + + int parent = parents[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(); + 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 0000000000000000000000000000000000000000..28a3111e4e1962a02f9e6abc6e8dccef0bb4e894 --- /dev/null +++ b/src/helper/disjoint-sets.h @@ -0,0 +1,32 @@ +// 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 + +class DisjointSets +{ + // 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: + DisjointSets(int n) { parents.resize(n, -1); } + void merge(int a, int b); + int parent_of(int x); + 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 new file mode 100644 index 0000000000000000000000000000000000000000..56f70abc7e8808239a6aaa7a563d7b7352520cd8 --- /dev/null +++ b/src/helper/useful-functions.cpp @@ -0,0 +1,173 @@ +// 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 +#include + +#include "disjoint-sets.h" +#include "geom-pathstroke.h" +#include "ui/widget/canvas.h" + +#include "object/object-set.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_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; +} + +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 split_non_intersecting_paths(const Geom::PathVector &paths) +{ + int n = paths.size(); + + DisjointSets sets(n); + std::vector visited(n); + + for (int i = n - 1; i >= 0; i--) { + + if (visited[i]) { continue; } + visited[i] = true; + + for (int j = 0; j < n; j++) { + if (visited[j]) { continue; } + if (is_intersecting(paths[i], paths[j])) { + sets.merge(i, j); + } + } + } + + int sets_count = sets.sets_count(); // this is O(N). + std::map> map; + for (int i = 0; i < n; i++) { + int parent = sets.parent_of(i); + map[parent].push_back(i); + } + + int i = 0; + std::vector result(sets_count); + 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; +} + +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); + } + + parent->addChild(repr, after); + + 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 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 += ":"; + 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); +} + +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 new file mode 100644 index 0000000000000000000000000000000000000000..d14f27511b7e86415b178abb326980c38a9d0c35 --- /dev/null +++ b/src/helper/useful-functions.h @@ -0,0 +1,48 @@ +// 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" + +namespace Inkscape { + +class ObjectSet; + +void set_desktop_busy(SPDesktop *desktop); +void unset_desktop_busy(SPDesktop *desktop); + +std::vector get_groups_expanded(const std::vector &items); + +std::vector split_non_intersecting_paths(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); +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); +} \ No newline at end of file diff --git a/src/inkscape-application.cpp b/src/inkscape-application.cpp index 5f709b62f6582cea868c7a86919b70e2b08278a7..68780902b282a8e9e553ca61d4e90638dc7ee542 100644 --- a/src/inkscape-application.cpp +++ b/src/inkscape-application.cpp @@ -48,14 +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.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 @@ -584,14 +586,16 @@ 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_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 + 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 +1622,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 210da9d6ef9c26857323086876f2a3ced351e4ff..41f7c38bb5da86c93b8fea9ad4429333f6ced49a 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 3129bd41a804fd2425838d1dc77800aa7dc773fe..63e87cf6d176cdc36639fe9a449beceb86b63591 100644 --- a/src/inkscape-window.cpp +++ b/src/inkscape-window.cpp @@ -25,6 +25,10 @@ #include "actions/actions-canvas-mode.h" #include "actions/actions-canvas-transform.h" #include "actions/actions-dialogs.h" +#include "actions/actions-edit.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! @@ -90,10 +94,14 @@ 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_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_canvas_transform(this); // Actions to transform canvas view. + 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 ========= ink_drag_setup(_desktop_widget); diff --git a/src/livarot/ShapeMisc.cpp b/src/livarot/ShapeMisc.cpp index b76ea5875bfed1ebfc71becd0cbb91dfad29eaa0..6a72ec99c9f5d88797ad894a61a7d679e092b2de 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; diff --git a/src/object/object-set.cpp b/src/object/object-set.cpp index 1e552f7734ed387a08f9a19ee4b5e1c1e3433927..d7fe40ffa84fb7ab617a1dca6af4aab273a63e35 100644 --- a/src/object/object-set.cpp +++ b/src/object/object-set.cpp @@ -15,11 +15,24 @@ #include #include #include +#include +#include #include +#include +#include #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 { @@ -129,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); @@ -462,6 +478,151 @@ void ObjectSet::_remove3DBoxesRecursively(SPObject *obj) { } } +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, true); +} + +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. +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(true); + ungroup_all(this); + + 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; + } + } + } + + 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)); + } + } + } + + 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(bool skip_undo) +{ + // FIXME consider the case when splitting a group. + + if (isEmpty()) { + return; + } + + the_temporary_fix_for_the_transform_bug(); + + ungroup_all(this); + + std::vector items_vec(items().begin(), items().end()); + + 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 = split_non_intersecting_paths(pathvec); + for (auto paths : broken) { + result.push_back(draw_on_canvas(paths, items_vec[i])); + } + } + + 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")); + } +} + +void ObjectSet::activate() +{ + _is_active = true; +} + +void ObjectSet::deactivate() +{ + clear(); + _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 69245cfe4e27564cb93fbda2e329da7728ba3adb..3e48ae90a78bbbf5f1977a25fc20df0a60287106 100644 --- a/src/object/object-set.h +++ b/src/object/object-set.h @@ -471,7 +471,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 @@ -480,6 +480,15 @@ public: void swapFillStroke(); void fillBetweenMany(); + void the_temporary_fix_for_the_transform_bug(); + void fracture(bool skip_undo = false); + 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) {}; @@ -495,6 +504,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; diff --git a/src/object/sp-guide.cpp b/src/object/sp-guide.cpp index a463c8408a84172458116ad82510d18d13d3010a..3c779a5ca3bb194cb3e48bdb3ae83f742ad297cb 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/object/sp-item.cpp b/src/object/sp-item.cpp index db784c7307586dd3bc5c5deebefc58846098ac88..54bd4f083fdedda1e35c717d0926160f7864d624 100644 --- a/src/object/sp-item.cpp +++ b/src/object/sp-item.cpp @@ -57,6 +57,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,50 @@ 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; +} + +std::vector SPItem::get_groups_expanded() +{ + std::vector result; + if (SPGroup *group = dynamic_cast(this)) { + std::vector 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()); + } + } else { + return { this }; + } + 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 c22b41e4fdb8c6d185fd7b270184ebea3c892d4f..213efcfc13e029abab9bbf862d26f4c17601d880 100644 --- a/src/object/sp-item.h +++ b/src/object/sp-item.h @@ -171,6 +171,9 @@ public: sigc::signal _transformed_signal; + Geom::PathVector get_pathvector(); + std::vector get_groups_expanded(); + bool isLocked() const; void setLocked(bool lock); diff --git a/src/object/sp-namedview.cpp b/src/object/sp-namedview.cpp index 32c0dd958f44e2f79a9a1eaa4d471d9cda7d8aa7..c70f9b9da67d804e0ec3e167902c677b0738df25 100644 --- a/src/object/sp-namedview.cpp +++ b/src/object/sp-namedview.cpp @@ -1027,13 +1027,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/path-chemistry.cpp b/src/path-chemistry.cpp index 585b1dcff7187c757c6072ec9ecbbe9af312c2a8..30a7de81c1936eaab9b92ccb4b484606c01a2cc9 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.")); @@ -610,8 +607,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-boolop.cpp b/src/path/path-boolop.cpp index 5464957d2c41737380553f6acc6273a6058c6ac5..d5050e5b9122ce588aa5d1c0265428df23558b7b 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 @@ -850,6 +919,100 @@ 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 + std::swap(originaux[0], originaux[1]); + std::swap(origWind[0], origWind[1]); + + 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 59c53f79255367c17c8912ffb7eac3c02b0a9599..f6434f109dd3be41fa85bf5740e274163e92cdd1 100644 --- a/src/path/path-boolop.h +++ b/src/path/path-boolop.h @@ -14,7 +14,9 @@ #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 diff --git a/src/path/path-offset.cpp b/src/path/path-offset.cpp index 9fed2e11d6e34ed256a58a66a16a0abd9a758eed..192212ca54793bd5e96e02ba56d0c2db88624cb5 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/rubberband.cpp b/src/rubberband.cpp index f31aecc98aad9b2619a7861bc08017e5e50d74c2..4818cb391bdb4f0ef79177f78085440c42c063fa 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 : 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 : 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 : 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 0d93d7cd77254271ea80d4fbc45a1017f7e756a3..9d5ebabbbe469c6bb2ca285849e2d05eacc37cd7 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; }; } diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 123293d7e450c21996cce510dfe6c6fbdf94e0b5..2387a633f15f38669b0a12ddfa6b97108e236d75 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; @@ -397,8 +397,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; } } @@ -433,7 +432,7 @@ void ObjectSet::deleteItems() } if(document()) { - DocumentUndo::done(document(), SP_VERB_EDIT_DELETE, _("Delete")); + DocumentUndo::done(document(), _("Delete"), INKSCAPE_ICON("edit-delete")); } } @@ -618,8 +617,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); @@ -647,8 +645,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"), ""); } /* @@ -837,8 +834,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; } @@ -871,8 +867,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")); } @@ -950,8 +945,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 @@ -1074,15 +1068,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; } @@ -1101,8 +1094,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")); } } @@ -1155,9 +1147,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")); } @@ -1194,8 +1184,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")); } } @@ -1218,9 +1207,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) { @@ -1241,10 +1228,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 @@ -1337,7 +1323,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")); } } @@ -1345,7 +1331,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")); } } @@ -1353,8 +1339,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"), ""); } } @@ -1385,9 +1370,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"), ""); + } } void ObjectSet::removeFilter() @@ -1410,9 +1395,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"), ""); + } } @@ -1420,8 +1405,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")); } } @@ -1429,8 +1413,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")); } } @@ -1484,8 +1467,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; @@ -1530,8 +1512,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; @@ -1594,9 +1575,8 @@ void ObjectSet::toLayer(SPObject *moveto, bool skip_undo, Inkscape::XML::Node *a 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")); } } } @@ -1850,9 +1830,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"), ""); + } } void ObjectSet::setScaleAbsolute(double x0, double x1,double y0, double y1) @@ -1950,10 +1930,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) @@ -1967,13 +1948,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")); + } } /* @@ -2330,11 +2309,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) @@ -2360,11 +2336,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")); } } @@ -2388,11 +2361,10 @@ 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) +void ObjectSet::move(double dx, double dy, bool skip_undo) { if (isEmpty()) { return; @@ -2400,16 +2372,13 @@ 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", 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")); } } } @@ -2428,14 +2397,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")); } } @@ -2733,8 +2699,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); } @@ -2773,8 +2738,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")); } } @@ -2864,8 +2828,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; } @@ -2902,8 +2865,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; @@ -3095,9 +3057,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 { @@ -3180,8 +3142,7 @@ void ObjectSet::toMarker(bool apply) - DocumentUndo::done(doc, SP_VERB_EDIT_SELECTION_2_MARKER, - _("Objects to marker")); + DocumentUndo::done(doc, _("Objects to marker"), ""); } static void sp_selection_to_guides_recursive(SPItem *item, bool wholegroups) { @@ -3225,7 +3186,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"), ""); } /* @@ -3391,7 +3352,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"), ""); } /* @@ -3408,7 +3369,7 @@ void ObjectSet::unSymbol() } } } - DocumentUndo::done(document(), SP_VERB_EDIT_UNSYMBOL, _("unSymbol all selected symbols")); + DocumentUndo::done(document(), _("unSymbol all selected symbols"), ""); } void ObjectSet::tile(bool apply) @@ -3508,8 +3469,7 @@ void ObjectSet::tile(bool apply) } - DocumentUndo::done(doc, SP_VERB_EDIT_TILE, - _("Objects to pattern")); + DocumentUndo::done(doc, _("Objects to pattern"), ""); } void ObjectSet::untile() @@ -3583,8 +3543,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"), ""); setList(new_select); } } @@ -3758,8 +3717,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()) { @@ -3863,7 +3821,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"), ""); } /** @@ -4058,9 +4016,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"), ""); } else { - DocumentUndo::done(doc, SP_VERB_OBJECT_SET_MASK, _("Set mask")); + DocumentUndo::done(doc, _("Set mask"), ""); } } } @@ -4191,9 +4149,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"), ""); } else { - DocumentUndo::done(doc, SP_VERB_OBJECT_UNSET_MASK, _("Release mask")); + DocumentUndo::done(doc, _("Release mask"), ""); } } } @@ -4216,8 +4174,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"), ""); return true; } else { return false; @@ -4293,8 +4250,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"), ""); } /** @@ -4368,7 +4324,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"), ""); } /** @@ -4392,11 +4348,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"), ""); } } @@ -4416,8 +4371,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"), ""); } }; diff --git a/src/selection-chemistry.h b/src/selection-chemistry.h index 771066b817fc2c0b7855604eded3118e80669b1a..b4779db57a1670bf37773c6d3e1193c25ec0cf89 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/CMakeLists.txt b/src/ui/CMakeLists.txt index 42c2cecc03be876f10f8c3b9464b96892a3351ee..9ab563906c97e11d7a51fd19f4ab690ba9cbd884 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -47,6 +47,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 @@ -69,6 +70,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 @@ -355,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 @@ -377,6 +380,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/desktop/menubar.cpp b/src/ui/desktop/menubar.cpp index 7620ac04004fdb3ca4931d36644909b0b84b59ac..290ffc08ca6056fbf8d6620cb83eaeb5d7369765 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) { @@ -454,25 +395,43 @@ 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"; + } + else if (strcmp(name, "_Edit") == 0) { + 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"; + } + 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 { - 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 +443,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); @@ -576,13 +534,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. @@ -672,4 +623,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 f6167d4d758317cd5d09f8762cf1ec9b2b75bd35..16742bc4bf2e972d0b2f77ff444847773c666cd2 100644 --- a/src/ui/dialog/command-palette.cpp +++ b/src/ui/dialog/command-palette.cpp @@ -617,9 +617,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 +1467,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 +1585,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 fa4befd77fdcd97bced842907e022850d2b35d55..119a169be52f2a343e35fd000d175f8bfca10df3 100644 --- a/src/ui/dialog/dialog-container.cpp +++ b/src/ui/dialog/dialog-container.cpp @@ -387,7 +387,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 +394,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 +565,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())) { @@ -1185,4 +1181,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 diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index e19208a68a4136c95829c4f1a3fa61f8852d7bb2..43e4bf460741849fe4e90171478ad14555768690 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/modifiers.cpp b/src/ui/modifiers.cpp index 187d342529c828e8a6f5f7bd3943921cd8276d53..537b2644f0e107302a3237b26b0031e8bf4e6020 100644 --- a/src/ui/modifiers.cpp +++ b/src/ui/modifiers.cpp @@ -53,6 +53,12 @@ decltype(Modifier::_modifiers) Modifier::_modifiers { {Type::TRANS_SNAPPING, new Modifier("trans-snapping", _("No Transform Snapping"), _("Disable snapping when transforming object."), SHIFT, TRANSFORM, DRAG)}, // Center handle click: seltrans.cpp:734 SHIFT // Align handle click: seltrans.cpp:1365 SHIFT + + // TODO figure out the right category and trigger. + {Type::SELECT_AND_UNION, new Modifier("select-union", _("Select and union"), _("Union whatever the cursor moves over"), CTRL, DRAG, DRAG)}, + {Type::SELECT_AND_DELETE, new Modifier("select-delete", _("Select and delete"), _("Delete whatever the cursor moves over"), ALT, DRAG, DRAG)}, + {Type::SELECT_AND_INTERSECTION, new Modifier("select-intersection", _("Select and intersection"), _("Intersect whatever the cursor moves over"), CTRL | ALT, DRAG, DRAG)}, + {Type::JUST_SELECT, new Modifier("just-select", _("Just select"), _("Just select whatever the cursor moves over"), SHIFT, DRAG, DRAG)}, }; decltype(Modifier::_category_names) Modifier::_category_names { diff --git a/src/ui/modifiers.h b/src/ui/modifiers.h index 54765c45f82ade98b06259c31d7d49991123ec65..273c4a5f137e20c0fd9af6585558613530aad099 100644 --- a/src/ui/modifiers.h +++ b/src/ui/modifiers.h @@ -76,6 +76,12 @@ enum class Type { TRANS_OFF_CENTER, // Scale/Rotate/skew from opposite corner {HANDLE+SHIFT} TRANS_SNAPPING, // Disable snapping while transforming {HANDLE+SHIFT} // TODO: Alignment omitted because it's UX is not completed + + // Builder tool + SELECT_AND_UNION, + SELECT_AND_DELETE, + SELECT_AND_INTERSECTION, + JUST_SELECT, }; diff --git a/src/ui/tool-factory.cpp b/src/ui/tool-factory.cpp index 8c80c392142f4ee7741b3f766ab6cef093995db2..aa2afc32fab7c1951505b8e8ce70bfa20b251d9e 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/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp index 8624f5a355c1e36ad79d16053527b7af3c446c2c..75498311d11bc341f7ed326be238122cc301e121 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/ui/toolbar/builder-toolbar.cpp b/src/ui/toolbar/builder-toolbar.cpp new file mode 100644 index 0000000000000000000000000000000000000000..16eec591b3b0124b2f7720f4fcafb946fb50225f --- /dev/null +++ b/src/ui/toolbar/builder-toolbar.cpp @@ -0,0 +1,525 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * A toolbar for the Builder tool. + * + * Authors: + * Osama Ahmad + * + * Copyright (C) 2021 authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "builder-toolbar.h" +#include "ui/tools//builder-tool.h" + +#include + +#include +#include + +#include <2geom/rect.h> + +#include "desktop.h" +#include "document-undo.h" +#include "selection.h" +#include "message-stack.h" +#include "selection-chemistry.h" +#include "verbs.h" + + +#include "object/sp-namedview.h" + +#include "ui/icon-names.h" +#include "ui/widget/canvas.h" // Focus widget +#include "ui/widget/unit-tracker.h" + +#include "widgets/widget-sizes.h" + +namespace Inkscape { +namespace UI { +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(_command_widgets); + _command_widgets.push_back(add_label("Commands: ")); + boolop_buttons_init(); + add_separator(_command_widgets); + compound_operations_buttons_init(); + add_separator(_interactive_mode_widgets); + interactive_mode_buttons_init(); + + show_all(); +} + +void BuilderToolbar::normal_mode_setup() +{ +// std::cout << "In normal_mode_setup.\n"; + 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() +{ +// std::cout << "In interactive_mode_setup.\n"; + 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. + // if you're going to edit this, remember to edit the BuilderTool::Mode enum and + // 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_operation_union, + }, + { + .label = _("Delete"), + .tooltip_text = _("Delete whatever the mouse moves over"), + .icon_name = "path-difference", + .handler = &BuilderToolbar::set_operation_delete, + }, + { + .label = _("Intersection"), + .tooltip_text = _("Intersect whatever the mouse moves over"), + .icon_name = "path-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_operation_just_select, + }, + }; + + operation_buttons_init_create_buttons(operation_buttons_descriptors); + operation_buttons_init_set_active_button(); + operation_buttons_init_add_buttons(); +} + +void BuilderToolbar::operation_buttons_init_create_buttons(const std::vector& descriptors) +{ + Gtk::RadioToolButton::Group operation_group; + + for (auto& operation : descriptors) + { + 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::operation_buttons_init_set_active_button() +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + 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(); +} + +void BuilderToolbar::operation_buttons_init_add_buttons() +{ + int button_index = 0; + for (auto button : _operation_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); + } +} + +void BuilderToolbar::operation_changed(int operation) +{ + // each operation has its own handler so that it's + // easier to attach more logic in the future. + auto handler = _operation_handlers[operation]; + (this->*handler)(); + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + 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) +{ + auto builder_tool = dynamic_cast(_desktop->event_context); + if (builder_tool) { + builder_tool->set_current_operation(operation); + } +} + +void BuilderToolbar::set_operation_union() +{ + set_current_operation(Tools::BuilderTool::SELECT_AND_UNION); +} + +void BuilderToolbar::set_operation_delete() +{ + set_current_operation(Tools::BuilderTool::SELECT_AND_DELETE); +} + +void BuilderToolbar::set_operation_intersection() +{ + set_current_operation(Tools::BuilderTool::SELECT_AND_INTERSECT); +} + +void BuilderToolbar::set_operation_just_select() +{ + set_current_operation(Tools::BuilderTool::JUST_SELECT); +} + + +void BuilderToolbar::boolop_buttons_init() +{ + boolop_buttons_init_verbs(); + boolop_buttons_init_actions(); +} + +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)); + _command_widgets.push_back(button); + add(*button); + } +} + +void BuilderToolbar::perform_fracture() +{ + auto selection = _desktop->getSelection(); + selection->fracture(); +} + +void BuilderToolbar::perform_flatten() +{ + auto selection = _desktop->getSelection(); + selection->flatten(); +} + +void BuilderToolbar::boolop_buttons_init_verbs() +{ + _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() +{ + 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() +{ + _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() +{ + // TODO find a better way than this. Using verbs is easier. + const static std::vector interactive_mode_buttons_descriptors = { + { + .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) + { + 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)); + _interactive_mode_widgets.push_back(button); + add(*button); + } +} + +void BuilderToolbar::add_separator(std::vector &group) +{ + auto separator = Gtk::manage(new Gtk::SeparatorToolItem()); + group.push_back(separator); + add(*separator); +} + +GtkWidget * +BuilderToolbar::create(SPDesktop *desktop) +{ + auto toolbar = new BuilderToolbar(desktop); + return GTK_WIDGET(toolbar->gobj()); +} + +} +} +} diff --git a/src/ui/toolbar/builder-toolbar.h b/src/ui/toolbar/builder-toolbar.h new file mode 100644 index 0000000000000000000000000000000000000000..30f10fec7f33768f1c32c49c0a26e4d6208087e6 --- /dev/null +++ b/src/ui/toolbar/builder-toolbar.h @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * A toolbar for the Builder tool. + * + * Authors: + * Osama Ahmad + * + * Copyright (C) 2021 authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#pragma once + +#include + +#include + +#include "toolbar.h" + +class SPDesktop; + +namespace Inkscape { +class Selection; + +namespace UI { + +namespace Widget { +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 _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); + 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(); + void boolop_buttons_init_actions(); + void boolop_buttons_init_actions_add_buttons(const std::vector& descriptors); + void boolop_buttons_init_verbs(); + void perform_fracture(); + 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 interactive_mode_buttons_init(); + void interactive_mode_buttons_init_add_buttons(const std::vector &descriptors); + void interactive_mode_apply(); + void interactive_mode_reset(); + void interactive_mode_discard(); + + 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; +}; + +} +} +} \ No newline at end of file diff --git a/src/ui/toolbar/node-toolbar.cpp b/src/ui/toolbar/node-toolbar.cpp index af3a0188a54dfd43b2f0da62dbcf713a5d6ce56c..adcf1e1e8ef59feabc43441b9cb6f30e832acc25 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() diff --git a/src/ui/toolbar/select-toolbar.cpp b/src/ui/toolbar/select-toolbar.cpp index d67c3315f8e34f776d45a4b248f9a57e342abb9f..0f2da43a3064c0634c4ac808e2abb20656dee155 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()); } diff --git a/src/ui/tools/builder-tool.cpp b/src/ui/tools/builder-tool.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6e7f9dcad4788e427ee328c2b0c401dca0501d07 --- /dev/null +++ b/src/ui/tools/builder-tool.cpp @@ -0,0 +1,721 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * @file + * A tool for building shapes. + */ +/* Authors: + * Osama Ahmad + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#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" + +#include "ui/toolbar/builder-toolbar.h" + +#ifdef WITH_DBUS +#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; + +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 + +const std::string& BuilderTool::getPrefsPath() { + return BuilderTool::prefsPath; +} + +const std::string BuilderTool::prefsPath = "/tools/builder"; + +const std::vector BuilderTool::operation_cursor_filenames = { + "cursor-union.svg", + "cursor-delete.svg", + "cursor-intersect.svg", + "select.svg", +}; + +const std::vector BuilderTool::operation_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) + , moved(false) + , button_press_state(0) + , item(nullptr) + , _seltrans(nullptr) + , _describer(nullptr) +{ +} + +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(); + + end_interactive_mode(); +} + +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(); + + 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(); + } + + set_current_operation(); + + start_interactive_mode(); +} + +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() { + if (in_interactive_mode()) { + desktop->getSelection()->deactivate(); + } + 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); +} + +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); +} + +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; +} + +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); + + bool ret = false; + + auto handler = get_event_handler(event); + if (handler) { + ret = (this->*handler)(event); + } + + if (!ret) { + ret = ToolBase::root_handler(event); + } + + return ret; +} + +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; + + Geom::Point const button_pt(event->button.x, event->button.y); + Geom::Point const p(desktop->w2d(button_pt)); + + 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); + 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; + + return 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(); + } + + return false; +} + +bool BuilderTool::event_button_release_handler(GdkEvent *event) +{ + xp = yp = 0; + Inkscape::Selection *selection = desktop->getSelection(); + + + if ((event->button.button == 1) && (this->grabbed)) { + + 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(), true, false); + } + + _seltrans->resetState(); + r->stop(); + this->defaultMessageContext()->clear(); + + int operation = get_current_operation(); + + if(is_operation_add_to_selection(operation, event)) { + selection->addList (items); + } else { + if (in_interactive_mode()) selection->activate(); + selection->setList (items); + perform_operation(selection, operation); + if (in_interactive_mode()) selection->deactivate(); + } + + } else { // it was just a click, or a too small rubberband + r->stop(); + + int operation = get_current_operation(); + + if (operation == JUST_SELECT && !is_operation_add_to_selection(operation, event)) { + selection->clear(); + } + + bool in_groups = Modifier::get(Modifiers::Type::SELECT_IN_GROUPS)->active(event->button.state); + + 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, operation); + if (in_interactive_mode()) selection->deactivate(); + } else { + // clicked in an empty area + selection->clear(); + } + } + } + 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; +} + +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 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 (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 { + 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); + } + } + + return false; +} + +bool BuilderTool::event_key_press_handler(GdkEvent *event) +{ + set_current_operation(event); + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + Inkscape::Selection *selection = desktop->getSelection(); + + int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); + auto const y_dir = desktop->yaxisdir(); + + bool ret = false; + switch (get_latin_keyval (&event->key)) { + 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 operation: show outline operation moving */ + /* FIXME: Is next condition ok? (lauris) */ + if (this->dragging && this->grabbed) { + _seltrans->stamp(); + 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_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; + + 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; + } + + return ret; +} + +bool BuilderTool::event_key_release_handler(GdkEvent *event) +{ + set_current_operation(event); + guint keyval = get_latin_keyval(&event->key); + if (key_is_a_modifier (keyval)) { + this->defaultMessageContext()->clear(); + } + + return false; +} + +void BuilderTool::perform_operation(Selection *selection, int operation) +{ + int size = selection->size(); + + if (shapes_builder.is_started()) { + if (operation == SELECT_AND_UNION) { + shapes_builder.set_union(selection); + } else if (operation == SELECT_AND_DELETE) { + shapes_builder.set_delete(selection); + } + return; + } + + if (operation != JUST_SELECT && size > 1) { + if (operation == SELECT_AND_UNION) { + selection->pathUnion(); + } else if (operation == SELECT_AND_DELETE) { + selection->pathDiff(); + } else if (operation == SELECT_AND_INTERSECT) { + selection->pathIntersect(); + } + selection->clear(); + } +} + +void BuilderTool::perform_current_operation(Selection *selection) +{ + int operation = get_current_operation(); + return perform_operation(selection, operation); +} + +void BuilderTool::set_modifiers_state(GdkEvent* event) +{ + // TODO This function is deprecated. + GdkModifierType modifiers; + gdk_window_get_pointer(gdk_event_get_window(event), nullptr, nullptr, &modifiers); + + alt_on = modifiers & GDK_MOD1_MASK; + ctrl_on = modifiers & INK_GDK_PRIMARY_MASK; + shift_on = modifiers & GDK_SHIFT_MASK; +} + +int BuilderTool::get_current_operation() +{ + if (ctrl_on) { + if (alt_on && !in_interactive_mode()) return SELECT_AND_INTERSECT; + return SELECT_AND_UNION; + } + if (alt_on) return SELECT_AND_DELETE; + if (shift_on && !in_interactive_mode()) return JUST_SELECT; + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + 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) +{ + if (current_operation == -1) { + current_operation = get_current_operation(); + } + + if (current_operation == active_operation) { + return; + } + + if (in_interactive_mode() && + (current_operation == SELECT_AND_INTERSECT || current_operation == JUST_SELECT)) { + return; + } + + 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_operation(GdkEvent *event) +{ + set_modifiers_state(event); + set_current_operation(); +} + +void BuilderTool::set_cursor_operation() +{ + if (active_operation > operation_cursor_filenames.size()) { + std::cerr << "BuilderTool: operation " << active_operation << " is unknown.\n"; + return; + } + + 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_operation > operation_colors.size()) { + std::cerr << "BuilderTool: operation " << active_operation << " is unknown.\n"; + return; + } + + auto instance = Rubberband::get(desktop); + instance->setColor(operation_colors[active_operation]); +} + +bool BuilderTool::is_operation_add_to_selection(int operation, GdkEvent *event) +{ + return operation == JUST_SELECT && Modifier::get(Modifiers::Type::SELECT_ADD_TO)->active(event->button.state); +} + +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"); + 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 { +// 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() +{ + 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::apply() +{ + if (in_interactive_mode()) { + end_interactive_mode(); + } else { + std::cerr << "Applying while not in interactive mode?...\n"; + } +} + +void BuilderTool::reset() +{ + 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 new file mode 100644 index 0000000000000000000000000000000000000000..8aa54133c468c8ae89ffa35c5bbbcb73894bc468 --- /dev/null +++ b/src/ui/tools/builder-tool.h @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * @file + * A tool for building shapes. + */ +/* Authors: + * Osama Ahmad + * + * Copyright (C) 2021 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#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) + +namespace Inkscape { +class CanvasItem; +class SelTrans; +class SelectionDescriber; +} + +namespace Inkscape { +namespace UI { +namespace Tools { + +class BuilderTool : public ToolBase { +public: + + typedef bool (BuilderTool::*EventHandler)(GdkEvent*); + + enum Operation { + SELECT_AND_UNION = 0, + SELECT_AND_DELETE = 1, + SELECT_AND_INTERSECT = 2, + JUST_SELECT = 3, + }; + + BuilderTool(); + ~BuilderTool() override; + + bool dragging; + bool moved; + guint button_press_state; + + 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; + + void set_current_operation(GdkEvent* event); + void set_current_operation(int current_operation = -1); + + void start_interactive_mode(); + void end_interactive_mode(); + + bool in_interactive_mode() const; + + void apply(); + void reset(); + void discard(); + +private: + + bool sp_select_context_abort(); + + EventHandler get_event_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 perform_operation(Selection *selection, int operation); + void perform_current_operation(Selection *selection); + + void set_modifiers_state(GdkEvent* event); + int get_current_operation(); + bool is_operation_add_to_selection(int operation, GdkEvent *event); + void set_cursor_operation(); + void set_rubberband_color(); + + // 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; + static const std::vector operation_colors; + static const std::map handlers; + + InteractiveShapesBuilder shapes_builder; + + 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; +}; + +} +} +} diff --git a/src/ui/tools/select-tool.cpp b/src/ui/tools/select-tool.cpp index 1b68ea4d1770e2d2d31d7a32cf5991542c7741d4..3635ef5876370fe633d5b1da5b80e1451f24dc10 100644 --- a/src/ui/tools/select-tool.cpp +++ b/src/ui/tools/select-tool.cpp @@ -920,100 +920,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(); diff --git a/src/verbs.cpp b/src/verbs.cpp index d310eb3a2b40f2571dc9c3f491496f0437c44d95..86258efaf30e961b8dcb332c4df26b7938434ebd 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -25,6 +25,8 @@ #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. @@ -32,17 +34,27 @@ #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 "inkscape-version.h" #include "layer-fns.h" #include "layer-manager.h" + +#include "livarot/LivarotDefs.h" +#include "livarot/Shape.h" + #include "message-stack.h" #include "path-chemistry.h" #include "selection-chemistry.h" @@ -60,19 +72,27 @@ #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 "object/sp-text.h" +#include "object/sp-polygon.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 "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" @@ -85,7 +105,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" @@ -94,7 +113,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 "ui/widget/canvas.h" // Canvas area using Inkscape::DocumentUndo; using Inkscape::UI::Dialog::ActionAlign; @@ -1106,6 +1125,7 @@ void EditVerb::perform(SPAction *action, void *data) } // end of sp_verb_action_edit_perform() + /** * Decode the verb code and take appropriate action. */ @@ -1127,6 +1147,7 @@ void SelectionVerb::perform(SPAction *action, void *data) selection->removeLPESRecursive(true); selection->unlinkRecursive(true); selection->pathUnion(); + selection->the_temporary_fix_for_the_transform_bug(); break; case SP_VERB_SELECTION_INTERSECT: selection->removeLPESRecursive(true); @@ -1702,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; @@ -1829,22 +1853,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); @@ -2163,7 +2187,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); @@ -2603,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 e6fc50b2e87d20d53427537d1e35b3cb863ff682..c50e97b5d95d8f516bdcc7f4def650c311fa7e59 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 7dc933fd5d4068fd2bf5d5c3460cc41dd0ab7620..c204e8a738d4daf7ee39c1d996fd13f1279e2e88 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,6 +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::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")},