diff --git a/po/POTFILES.src.in b/po/POTFILES.src.in
index 12a8a9ab4bcb24f70be5094a85fe17fbe6f67548..b06e7b3443e7e8a70b92035d486c53adfd97c3e3 100644
--- a/po/POTFILES.src.in
+++ b/po/POTFILES.src.in
@@ -12,6 +12,8 @@
../src/actions/actions-edit-window.cpp
../src/actions/actions-edit.cpp
../src/actions/actions-effect.cpp
+../src/actions/actions-element-a.cpp
+../src/actions/actions-element-image.cpp
../src/actions/actions-extra-data.cpp
../src/actions/actions-file-window.cpp
../src/actions/actions-file.cpp
diff --git a/share/keys/carbon.xml b/share/keys/carbon.xml
index 93667a8a332e7cb6c18e7ed2e29dc9358e79733f..24aab1d0a8ddf4c7f7ec9b1037eb74c6d9addde4 100644
--- a/share/keys/carbon.xml
+++ b/share/keys/carbon.xml
@@ -238,8 +238,8 @@ See "inkscape.xml" for information about file structure.
-
-
+
+
diff --git a/share/keys/inkscape.xml b/share/keys/inkscape.xml
index 58c2b3f8e12597467676cac660cc23c6c442ffda..6c0bf3c8dfa5daf9daeda74146114c49a068a30f 100644
--- a/share/keys/inkscape.xml
+++ b/share/keys/inkscape.xml
@@ -307,8 +307,8 @@ override) the bindings in the main default.xml.
-
-
+
+
diff --git a/share/ui/menus.ui b/share/ui/menus.ui
index a01e9f7902103dfdd2592e1547abe86386b187f5..27bb2faeb83fed32b6c310ffc0d2faa4e0d43b67 100644
--- a/share/ui/menus.ui
+++ b/share/ui/menus.ui
@@ -665,11 +665,11 @@
-
_Show/Hide Current Layer
- win.layer-toggle-hide
+ win.layer-hide-toggle
-
_Lock/Unlock Current Layer
- win.layer-toggle-lock
+ win.layer-lock-toggle
@@ -917,11 +917,11 @@
-
Unhide All
- win.unhide-all
+ app.unhide-all
-
Unlock All
- win.unlock-all
+ app.unlock-all
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 49fd8f3743997822645189c74354db2d11c85a38..7e0cbc0501d57551e9392bfba005e6b6a19d5b40 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -217,6 +217,10 @@ set(inkscape_SRC
actions/actions-edit.cpp
actions/actions-effect.h
actions/actions-effect.cpp
+ actions/actions-element-a.h
+ actions/actions-element-a.cpp
+ actions/actions-element-image.h
+ actions/actions-element-image.cpp
actions/actions-file-window.h
actions/actions-file-window.cpp
actions/actions-file.h
diff --git a/src/actions/actions-element-a.cpp b/src/actions/actions-element-a.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b5a3bfb8f2e2639595b9c1e7e5b9c2858f61e10c
--- /dev/null
+++ b/src/actions/actions-element-a.cpp
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Gio::Actions for use with (for anchor or hyper link).
+ *
+ * Copyright (C) 2022 Tavmjong Bah
+ *
+ * The contents of this file may be used under the GNU General Public License Version 2 or later.
+ *
+ */
+
+#include "actions-element-a.h"
+
+#include
+
+#include // Not ! To eventually allow a headless version!
+#include
+
+#include "inkscape-application.h"
+#include "inkscape-window.h"
+#include "preferences.h"
+
+#include "selection.h" // Selection
+#include "object/sp-anchor.h"
+#include "ui/dialog/dialog-container.h"
+
+// void anchor_add(InkscapeApplication *app)
+// {
+// auto selection = app->get_active_selection();
+// auto anchor = selection->group(1);
+// selection->set(anchor);
+
+// if (app->get_active_window()) {
+// app->get_active_window()->get_desktop()->getContainer()->new_dialog("ObjectAttributes");
+// }
+// }
+
+// XML not modified. Requires GUI.
+void anchor_open_link(InkscapeApplication* app)
+{
+ auto window = app->get_active_window();
+ if (window) {
+ auto selection = app->get_active_selection();
+ for (auto item : selection->items()) {
+ auto anchor = dynamic_cast(item);
+ if (anchor) {
+ const char* href = anchor->href;
+ if (href) {
+ try {
+ window->show_uri(href, GDK_CURRENT_TIME);
+ } catch (const Glib::Error &e) {
+ std::cerr << "anchor_open_link: cannot open " << href << " " << e.what() << std::endl;
+ }
+ }
+ }
+ }
+ }
+}
+
+std::vector> raw_data_element_a =
+{
+ // clang-format off
+ {"app.element-a-open-link", N_("Open link"), "Anchor", N_("Add an anchor to an object.") },
+ // clang-format on
+};
+
+void
+add_actions_element_a(InkscapeApplication* app)
+{
+ auto *gapp = app->gio_app();
+
+ // clang-format off
+ gapp->add_action( "element-a-open-link", sigc::bind(sigc::ptr_fun(&anchor_open_link), app));
+ // clang-format on
+
+ app->get_action_extra_data().add_data(raw_data_element_a);
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/actions/actions-element-a.h b/src/actions/actions-element-a.h
new file mode 100644
index 0000000000000000000000000000000000000000..9285a1115547b0179b0ffa9001a7c1b243e84e5e
--- /dev/null
+++ b/src/actions/actions-element-a.h
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Gio::Actions for use with (for anchor or hyper link).
+ *
+ * Copyright (C) 2022 Tavmjong Bah
+ *
+ * The contents of this file may be used under the GNU General Public License Version 2 or later.
+ *
+ */
+
+#ifndef INK_ACTIONS_ELEMENT_A_H
+#define INK_ACTIONS_ELEMENT_A_H
+
+class InkscapeApplication;
+
+void add_actions_element_a(InkscapeApplication* app);
+
+#endif // INK_ACTIONS_ELEMENT_A_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/actions/actions-element-image.cpp b/src/actions/actions-element-image.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a85d459c0c71dc5d7a4837032b300c393e0cd4f9
--- /dev/null
+++ b/src/actions/actions-element-image.cpp
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Gio::Actions for use with .
+ *
+ * Copyright (C) 2022 Tavmjong Bah
+ *
+ * The contents of this file may be used under the GNU General Public License Version 2 or later.
+ *
+ */
+
+#include "actions-element-image.h"
+
+#include
+
+#include // Not ! To eventually allow a headless version!
+#include // OK, we lied. We pop-up an message dialog if external editor not found and if we have a GUI.
+#include
+
+#include "inkscape-application.h"
+#include "inkscape-window.h"
+#include "preferences.h"
+
+#include "selection.h" // Selection
+#include "object/sp-image.h"
+
+Glib::ustring image_get_editor_name(bool is_svg)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
+ Glib::ustring editor;
+ if (is_svg) {
+ editor = prefs->getString("/options/svgeditor/value", "inkscape");
+ } else {
+ editor = prefs->getString("/options/bitmapeditor/value", "gimp");
+ }
+ return editor;
+}
+
+// Note that edits are external to Inkscape and thus we cannot undo them!
+void image_edit(InkscapeApplication *app)
+{
+ auto selection = app->get_active_selection();
+ if (selection->isEmpty()) {
+ // Nothing to do.
+ return;
+ }
+
+ auto document = selection->document();
+
+ for (auto item : selection->items()) {
+ auto image = dynamic_cast(item);
+ if (image) {
+
+ Inkscape::XML::Node *node = item->getRepr();
+ const gchar *href = node->attribute("xlink:href");
+ if (!href) {
+ std::cerr << "image_edit: no xlink:href" << std::endl;
+ continue;
+ }
+
+ if (strncmp (href, "data", 4) == 0) {
+ std::cerr << "image_edit: cannot edit embedded image" << std::endl;
+ continue;
+ }
+
+ // Find filename.
+ std::string filename = href;
+ if (strncmp (href, "file", 4) == 0) {
+ filename = Glib::filename_from_uri(href);
+ }
+
+ if (Glib::path_is_absolute(filename)) {
+ // Do nothing
+ } else if (document->getDocumentBase()) {
+ filename = Glib::build_filename(document->getDocumentBase(), filename);
+ } else {
+ filename = Glib::build_filename(Glib::get_current_dir(), filename);
+ }
+
+ // Bitmap or SVG?
+ bool is_svg = false;
+ if (filename.substr(filename.find_last_of(".") + 1) == "SVG" ||
+ filename.substr(filename.find_last_of(".") + 1) == "svg") {
+ is_svg = true;
+ }
+
+ // Get editor.
+ auto editor = image_get_editor_name(is_svg);
+
+#ifdef _WIN32
+ // Parsing is done according to Unix shell rules, need to enclose editor path by single
+ // quotes (everything before file extension).
+ int index = editor.find(".exe");
+ if (index < 0) index = editor.find(".bat");
+ if (index < 0) index = editor.find(".com");
+ if (index < 0) index = editor.length();
+
+ editor.insert(index, "'");
+ editor.insert(0, "'");
+#endif
+ Glib::ustring command = editor + " '" + filename + "'";
+
+ GError* error = nullptr;
+ g_spawn_command_line_async(command.c_str(), &error);
+ if (error) {
+ Glib::ustring message = _("Failed to edit external image.\nNote: Path to editor can be set in Preferences dialog.");
+ Glib::ustring message2 = Glib::ustring::compose(_("System error message: %1"), error->message);
+ auto window = app->get_active_window();
+ if (window) {
+ Gtk::MessageDialog dialog(*window, message, true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_OK);
+ dialog.property_destroy_with_parent() = true;
+ dialog.set_name("SetEditorDialog");
+ dialog.set_title(_("External Edit Image:"));
+ dialog.set_secondary_text(message2);
+ dialog.run();
+ } else {
+ std::cerr << "image_edit: " << message << std::endl;
+ }
+ g_error_free(error);
+ error = nullptr;
+ }
+ }
+ }
+}
+
+std::vector> raw_data_element_image =
+{
+ // clang-format off
+ {"app.element-image-edit", N_("Edit externally"), "Image", N_("Edit image externally (image must be selected and not embeded).") },
+ // clang-format on
+};
+
+void
+add_actions_element_image(InkscapeApplication* app)
+{
+ auto *gapp = app->gio_app();
+
+ // clang-format off
+ gapp->add_action( "element-image-edit", sigc::bind(sigc::ptr_fun(&image_edit), app));
+ // clang-format on
+
+ app->get_action_extra_data().add_data(raw_data_element_image);
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/actions/actions-element-image.h b/src/actions/actions-element-image.h
new file mode 100644
index 0000000000000000000000000000000000000000..32a71a571914639626e679d5675075a9ea4d3c5d
--- /dev/null
+++ b/src/actions/actions-element-image.h
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Gio::Actions for use with .
+ *
+ * Copyright (C) 2022 Tavmjong Bah
+ *
+ * The contents of this file may be used under the GNU General Public License Version 2 or later.
+ *
+ */
+
+#ifndef INK_ACTIONS_ELEMENT_IMAGE_H
+#define INK_ACTIONS_ELEMENT_IMAGE_H
+
+class InkscapeApplication;
+
+void add_actions_element_image(InkscapeApplication* app);
+
+#endif // INK_ACTIONS_ELEMENT_IMAGE_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/actions/actions-hide-lock.cpp b/src/actions/actions-hide-lock.cpp
index b108f1ff4abb510d6406894e1bc24f78bd5292af..adcbfc85b042e92e64deb7ad521d88bad12f50fd 100644
--- a/src/actions/actions-hide-lock.cpp
+++ b/src/actions/actions-hide-lock.cpp
@@ -5,60 +5,233 @@
*
* Authors:
* Sushant A A
+ * Tavmjong Bah
*
- * Copyright (C) 2021 Authors
+ * Copyright (C) 2021-2022 Authors
*
* Released under GNU GPL v2+, read the file 'COPYING' for more information.
*/
+#include "actions-hide-lock.h"
+
#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"
+#include "object/sp-root.h"
+
+// Helper to unlock/unhide everything. (Could also be used to lock/hide everything but that isn't very useful.)
+static bool
+hide_lock_recurse(bool (*f)(SPItem*, bool), SPItem *item, bool hide_or_lock)
+{
+ bool changed = false;
+
+ if (f(item, hide_or_lock)) {
+ changed = true;
+ }
+
+ for (auto& child : item->children) {
+ auto item = dynamic_cast(&child);
+ if (item && hide_lock_recurse(f, item, hide_or_lock)) {
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+// Helper to hide/unhide one item.
+bool
+hide_lock_hide(SPItem* item, bool hide)
+{
+ bool changed = false;
+ if (item->isHidden() != hide) {
+ item->setHidden(hide);
+ changed = true;
+ }
+ return changed;
+}
+
+// Helper to lock/unlock one item.
+bool
+hide_lock_lock(SPItem* item, bool lock)
+{
+ bool changed = false;
+ if (item->isLocked() != lock) {
+ item->setLocked(lock);
+ changed = true;
+ }
+ return changed;
+}
+
+// Unhide all
void
-hide_lock_unhide_all(InkscapeWindow* win)
+hide_lock_unhide_all(InkscapeApplication* app)
{
- SPDesktop* dt = win->get_desktop();
- SPDocument *doc = dt->getDocument();
- unhide_all(dt);
- Inkscape::DocumentUndo::done(doc, _("Unhide all objects in the current layer"), "");
+ auto document = app->get_active_document();
+ auto root = document->getRoot();
+
+ bool changed = hide_lock_recurse(&hide_lock_hide, root, false); // Unhide
+
+ if (changed) {
+ Inkscape::DocumentUndo::done(document, _("Unhid all objects in the current layer"), "");
+ }
}
+// Unlock all
void
-hide_lock_unlock_all(InkscapeWindow* win)
+hide_lock_unlock_all(InkscapeApplication* app)
{
- SPDesktop* dt = win->get_desktop();
- SPDocument *doc = dt->getDocument();
- unlock_all(dt);
- Inkscape::DocumentUndo::done(doc, _("Unlock all objects in the current layer"), "");
+ auto document = app->get_active_document();
+ auto root = document->getRoot();
+
+ bool changed = hide_lock_recurse(&hide_lock_lock, root, false); // Unlock
+
+ if (changed) {
+ Inkscape::DocumentUndo::done(document, _("Unlocked all objects in the current layer"), "");
+ }
+}
+
+// Unhide selected items and their descendents.
+void
+hide_lock_unhide_below(InkscapeApplication *app)
+{
+ auto selection = app->get_active_selection();
+ if (!selection) {
+ std::cerr << "hide_lock_unhide_below: no selection!" << std::endl;
+ return;
+ }
+
+ bool changed = false;
+ for (auto item : selection->items()) {
+ if (hide_lock_recurse(&hide_lock_hide, item, false)) {
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ auto document = app->get_active_document();
+ Inkscape::DocumentUndo::done(document, _("Unhid selected items and their descendents."), "");
+ }
+}
+
+// Unlock selected items and their descendents.
+void
+hide_lock_unlock_below(InkscapeApplication *app)
+{
+ auto selection = app->get_active_selection();
+ if (!selection) {
+ std::cerr << "hide_lock_unhide_below: no selection!" << std::endl;
+ return;
+ }
+
+ bool changed = false;
+ for (auto item : selection->items()) {
+ if (hide_lock_recurse(&hide_lock_lock, item, false)) {
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ auto document = app->get_active_document();
+ Inkscape::DocumentUndo::done(document, _("Unlocked selected items and their descendents."), "");
+ }
+}
+
+// Hide/unhide selected items.
+void
+hide_lock_hide_selected(InkscapeApplication* app, bool hide)
+{
+ auto selection = app->get_active_selection();
+ if (!selection) {
+ std::cerr << "hide_lock_hide_selected: no selection!" << std::endl;
+ return;
+ }
+
+ bool changed = false;
+ for (auto item : selection->items()) {
+ if (hide_lock_hide(item, hide)) {
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ auto document = app->get_active_document();
+ Inkscape::DocumentUndo::done(document, (hide ? _("Hid selected items.") : _("Unhid selected items.")), "");
+ selection->clear();
+ }
+}
+
+// Lock/Unlock selected items.
+void
+hide_lock_lock_selected(InkscapeApplication* app, bool lock)
+{
+ auto selection = app->get_active_selection();
+ if (!selection) {
+ std::cerr << "hide_lock_lock_selected: no selection!" << std::endl;
+ return;
+ }
+
+ bool changed = false;
+ for (auto item : selection->items()) {
+ if (hide_lock_lock(item, lock)) {
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ auto document = app->get_active_document();
+ Inkscape::DocumentUndo::done(document, (lock ? _("Locked selected items.") : _("Unlocked selected items.")), "");
+ selection->clear();
+ }
}
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") }
+ {"app.unhide-all", N_("Unhide All"), "Hide and Lock", N_("Unhide all objects in the current layer") },
+ {"app.unlock-all", N_("Unlock All"), "Hide and Lock", N_("Unlock all objects in the current layer") },
+
+ {"app.selection-hide", N_("Hide selection"), "Hide and Lock", N_("Hide all selected objects") },
+ {"app.selection-unhide", N_("Unhide selection"), "Hide and Lock", N_("Unhide all selected objects") },
+ {"app.selection-unhide-below", N_("Unhide descendents"), "Hide and Lock", N_("Unhide all items inside selected objects") },
+
+ {"app.selection-lock", N_("Lock selection"), "Hide and Lock", N_("Lock all selected objects") },
+ {"app.selection-unlock", N_("Unlock selection"), "Hide and Lock", N_("Unlock all selected objects") },
+ {"app.selection-unlock-below", N_("Unlock descendents"), "Hide and Lock", N_("Unlock all items inside selected objects") },
// clang-format on
};
void
-add_actions_hide_lock(InkscapeWindow* win)
+add_actions_hide_lock(InkscapeApplication* app)
{
+ auto *gapp = app->gio_app();
+
// 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));
+ gapp->add_action( "unhide-all", sigc::bind( sigc::ptr_fun(&hide_lock_unhide_all), app));
+ gapp->add_action( "unlock-all", sigc::bind( sigc::ptr_fun(&hide_lock_unlock_all), app));
+
+ gapp->add_action( "selection-hide", sigc::bind(sigc::ptr_fun(&hide_lock_hide_selected), app, true ));
+ gapp->add_action( "selection-unhide", sigc::bind(sigc::ptr_fun(&hide_lock_hide_selected), app, false));
+ gapp->add_action( "selection-unhide-below", sigc::bind( sigc::ptr_fun(&hide_lock_unhide_below), app));
+
+ gapp->add_action( "selection-lock", sigc::bind(sigc::ptr_fun(&hide_lock_lock_selected), app, true ));
+ gapp->add_action( "selection-unlock", sigc::bind(sigc::ptr_fun(&hide_lock_lock_selected), app, false));
+ gapp->add_action( "selection-unlock-below", sigc::bind( sigc::ptr_fun(&hide_lock_unlock_below), app));
// 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
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/actions/actions-hide-lock.h b/src/actions/actions-hide-lock.h
index fc7fb3eb4f5471bc64ee7e3aef946e1410719afd..457de146f06ea446590dec5dfe4cc3189b985e83 100644
--- a/src/actions/actions-hide-lock.h
+++ b/src/actions/actions-hide-lock.h
@@ -12,8 +12,8 @@
#ifndef INK_ACTIONS_HIDE_LOCK_H
#define INK_ACTIONS_HIDE_LOCK_H
-class InkscapeWindow;
+class InkscapeApplication;
-void add_actions_hide_lock(InkscapeWindow* win);
+void add_actions_hide_lock(InkscapeApplication* app);
-#endif // INK_ACTIONS_HIDE_LOCK_H
\ No newline at end of file
+#endif // INK_ACTIONS_HIDE_LOCK_H
diff --git a/src/actions/actions-layer.cpp b/src/actions/actions-layer.cpp
index 175dd4c1488f56f446a4e149896ac90faf86c262..78504d749d31f50265210b22b3ff29d0ac8eb59a 100644
--- a/src/actions/actions-layer.cpp
+++ b/src/actions/actions-layer.cpp
@@ -1,7 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/** \file
*
- * Actions for Layers
+ * Actions for Layers.
+ *
+ * These all require a window. To do: remove this requirement.
*
* Authors:
* Sushant A A
@@ -23,6 +25,16 @@
#include "ui/icon-names.h"
#include "document-undo.h"
+/*
+ * A layer is a group element with a special Inkscape attribute (Inkscape:groupMode) set to
+ * "layer". It is typically directly placed in the