diff options
Diffstat (limited to 'src/actions/actions-tools.cpp')
-rw-r--r-- | src/actions/actions-tools.cpp | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/src/actions/actions-tools.cpp b/src/actions/actions-tools.cpp new file mode 100644 index 0000000..c6214c7 --- /dev/null +++ b/src/actions/actions-tools.cpp @@ -0,0 +1,413 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Gio::Actions for switching tools. + * + * Copyright (C) 2020 Tavmjong Bah + * + * The contents of this file may be used under the GNU General Public License Version 2 or later. + * + */ + +#include <iostream> +#include <map> + +#include <giomm.h> // Not <gtkmm.h>! To eventually allow a headless version! +#include <glibmm/i18n.h> + +#include "actions-tools.h" + +#include "inkscape-application.h" +#include "inkscape-window.h" +#include "inkscape.h" +#include "message-context.h" + +#include "object/box3d.h" +#include "object/sp-ellipse.h" +#include "object/sp-flowtext.h" +#include "object/sp-offset.h" +#include "object/sp-path.h" +#include "object/sp-rect.h" +#include "object/sp-spiral.h" +#include "object/sp-star.h" +#include "object/sp-text.h" +#include "object/sp-marker.h" + +#include "ui/dialog/dialog-container.h" +#include "ui/dialog/dialog-manager.h" +#include "ui/dialog/inkscape-preferences.h" +#include "ui/tools/connector-tool.h" +#include "ui/tools/text-tool.h" + +class ToolData { +public: + int tool = TOOLS_INVALID; // TODO: Switch to named enum + int pref = TOOLS_INVALID; + Glib::ustring pref_path; +}; + +static std::map<Glib::ustring, ToolData> const &get_tool_data() +{ + static std::map<Glib::ustring, ToolData> tool_data; + + if (tool_data.empty()) { + tool_data = { + // clang-format off + {"Select", {TOOLS_SELECT, PREFS_PAGE_TOOLS_SELECTOR, "/tools/select", }}, + {"Node", {TOOLS_NODES, PREFS_PAGE_TOOLS_NODE, "/tools/nodes", }}, + {"Marker", {TOOLS_MARKER, PREFS_PAGE_TOOLS,/*No Page*/ "/tools/marker", }}, + {"Rect", {TOOLS_SHAPES_RECT, PREFS_PAGE_TOOLS_SHAPES_RECT, "/tools/shapes/rect", }}, + {"Arc", {TOOLS_SHAPES_ARC, PREFS_PAGE_TOOLS_SHAPES_ELLIPSE, "/tools/shapes/arc", }}, + {"Star", {TOOLS_SHAPES_STAR, PREFS_PAGE_TOOLS_SHAPES_STAR, "/tools/shapes/star", }}, + {"3DBox", {TOOLS_SHAPES_3DBOX, PREFS_PAGE_TOOLS_SHAPES_3DBOX, "/tools/shapes/3dbox", }}, + {"Spiral", {TOOLS_SHAPES_SPIRAL, PREFS_PAGE_TOOLS_SHAPES_SPIRAL, "/tools/shapes/spiral", }}, + {"Pencil", {TOOLS_FREEHAND_PENCIL, PREFS_PAGE_TOOLS_PENCIL, "/tools/freehand/pencil", }}, + {"Pen", {TOOLS_FREEHAND_PEN, PREFS_PAGE_TOOLS_PEN, "/tools/freehand/pen", }}, + {"Calligraphic", {TOOLS_CALLIGRAPHIC, PREFS_PAGE_TOOLS_CALLIGRAPHY, "/tools/calligraphic", }}, + {"Text", {TOOLS_TEXT, PREFS_PAGE_TOOLS_TEXT, "/tools/text", }}, + {"Gradient", {TOOLS_GRADIENT, PREFS_PAGE_TOOLS_GRADIENT, "/tools/gradient", }}, + {"Mesh", {TOOLS_MESH, PREFS_PAGE_TOOLS, /* No Page */ "/tools/mesh", }}, + {"Zoom", {TOOLS_ZOOM, PREFS_PAGE_TOOLS_ZOOM, "/tools/zoom", }}, + {"Measure", {TOOLS_MEASURE, PREFS_PAGE_TOOLS_MEASURE, "/tools/measure", }}, + {"Dropper", {TOOLS_DROPPER, PREFS_PAGE_TOOLS_DROPPER, "/tools/dropper", }}, + {"Tweak", {TOOLS_TWEAK, PREFS_PAGE_TOOLS_TWEAK, "/tools/tweak", }}, + {"Spray", {TOOLS_SPRAY, PREFS_PAGE_TOOLS_SPRAY, "/tools/spray", }}, + {"Connector", {TOOLS_CONNECTOR, PREFS_PAGE_TOOLS_CONNECTOR, "/tools/connector", }}, + {"PaintBucket", {TOOLS_PAINTBUCKET, PREFS_PAGE_TOOLS_PAINTBUCKET, "/tools/paintbucket", }}, + {"Eraser", {TOOLS_ERASER, PREFS_PAGE_TOOLS_ERASER, "/tools/eraser", }}, + {"LPETool", {TOOLS_LPETOOL, PREFS_PAGE_TOOLS, /* No Page */ "/tools/lpetool", }}, + {"Pages", {TOOLS_PAGES, PREFS_PAGE_TOOLS, "/tools/pages", }} + // clang-format on + }; + } + return tool_data; +} + +static std::map<Glib::ustring, Glib::ustring> const &get_tool_msg() +{ + static std::map<Glib::ustring, Glib::ustring> tool_msg; + + if (tool_msg.empty()) { + tool_msg = { + // clang-format off + {"Select", _("<b>Click</b> to Select and Transform objects, <b>Drag</b> to select many objects.") }, + {"Node", _("Modify selected path points (nodes) directly.") }, + {"Rect", _("<b>Drag</b> to create a rectangle. <b>Drag controls</b> to round corners and resize. <b>Click</b> to select.") }, + {"Arc", _("<b>Drag</b> to create an ellipse. <b>Drag controls</b> to make an arc or segment. <b>Click</b> to select.") }, + {"Star", _("<b>Drag</b> to create a star. <b>Drag controls</b> to edit the star shape. <b>Click</b> to select.") }, + {"3DBox", _("<b>Drag</b> to create a 3D box. <b>Drag controls</b> to resize in perspective. <b>Click</b> to select (with <b>Ctrl+Alt</b> for single faces).") }, + {"Spiral", _("<b>Drag</b> to create a spiral. <b>Drag controls</b> to edit the spiral shape. <b>Click</b> to select.") }, + {"Marker", _("<b>Click</b> a shape to start editing its markers. <b>Drag controls</b> to change orientation, scale, and position.") }, + {"Pencil", _("<b>Drag</b> to create a freehand line. <b>Shift</b> appends to selected path, <b>Alt</b> activates sketch mode.") }, + {"Pen", _("<b>Click</b> or <b>click and drag</b> to start a path; with <b>Shift</b> to append to selected path. <b>Ctrl+click</b> to create single dots (straight line modes only).") }, + {"Calligraphic", _("<b>Drag</b> to draw a calligraphic stroke; with <b>Ctrl</b> to track a guide path. <b>Arrow keys</b> adjust width (left/right) and angle (up/down).") }, + {"Text", _("<b>Click</b> to select or create text, <b>drag</b> to create flowed text; then type.") }, + {"Gradient", _("<b>Drag</b> or <b>double click</b> to create a gradient on selected objects, <b>drag handles</b> to adjust gradients.") }, + {"Mesh", _("<b>Drag</b> or <b>double click</b> to create a mesh on selected objects, <b>drag handles</b> to adjust meshes.") }, + {"Zoom", _("<b>Click</b> or <b>drag around an area</b> to zoom in, <b>Shift+click</b> to zoom out.") }, + {"Measure", _("<b>Drag</b> to measure the dimensions of objects.") }, + {"Dropper", _("<b>Click</b> to set fill, <b>Shift+click</b> to set stroke; <b>drag</b> to average color in area; with <b>Alt</b> to pick inverse color; <b>Ctrl+C</b> to copy the color under mouse to clipboard") }, + {"Tweak", _("To tweak a path by pushing, select it and drag over it.") }, + {"Spray", _("<b>Drag</b>, <b>click</b> or <b>click and scroll</b> to spray the selected objects.") }, + {"Connector", _("<b>Click and drag</b> between shapes to create a connector.") }, + {"PaintBucket", _("<b>Click</b> to paint a bounded area, <b>Shift+click</b> to union the new fill with the current selection, <b>Ctrl+click</b> to change the clicked object's fill and stroke to the current setting.") }, + {"Eraser", _("<b>Drag</b> to erase.") }, + {"LPETool", _("Choose a subtool from the toolbar") }, + {"Pages", _("Create and manage pages.")} + // clang-format on + }; + } + return tool_msg; +} + +Glib::ustring +get_active_tool(InkscapeWindow *win) +{ + Glib::ustring state; + + auto action = win->lookup_action("tool-switch"); + if (!action) { + std::cerr << "git_active_tool: action 'tool-switch' missing!" << std::endl; + return state; + } + + auto saction = Glib::RefPtr<Gio::SimpleAction>::cast_dynamic(action); + if (!saction) { + std::cerr << "git_active_tool: action 'tool-switch' not SimpleAction!" << std::endl; + return state; + } + + saction->get_state(state); + + return state; +} + +int +get_active_tool_enum(InkscapeWindow *win) +{ + return get_tool_data().at(get_active_tool(win)).tool; +} + +void tool_switch(Glib::ustring const &tool, InkscapeWindow *win); + +void +set_active_tool(InkscapeWindow *win, Glib::ustring const &tool) +{ + // Seems silly to have a function to just flip argument order... but it's consistent with other + // external functions. + tool_switch(tool, win); +} + +void +open_tool_preferences(InkscapeWindow *win, Glib::ustring const &tool) +{ + tool_preferences(tool, win); +} + +/** + * Set tool to appropriate one to edit 'item'. + */ +void +set_active_tool(InkscapeWindow *win, SPItem *item, Geom::Point const p) +{ + if (dynamic_cast<SPRect *>(item)) { + tool_switch("Rect", win); + } else if (dynamic_cast<SPGenericEllipse *>(item)) { + tool_switch("Arc", win); + } else if (dynamic_cast<SPStar *>(item)) { + tool_switch("Star", win); + } else if (dynamic_cast<SPBox3D *>(item)) { + tool_switch("3DBox", win); + } else if (dynamic_cast<SPSpiral *>(item)) { + tool_switch("Spiral", win); + } else if (dynamic_cast<SPMarker *>(item)) { + tool_switch("Marker", win); + } else if (dynamic_cast<SPPath *>(item)) { + if (Inkscape::UI::Tools::cc_item_is_connector(item)) { + tool_switch("Connector", win); + } + else { + tool_switch("Node", win); + } + } else if (dynamic_cast<SPText *>(item) || dynamic_cast<SPFlowtext *>(item)) { + tool_switch("Text", win); + SPDesktop* dt = win->get_desktop(); + if (!dt) { + std::cerr << "set_active_tool: no desktop!" << std::endl; + return; + } + sp_text_context_place_cursor_at (SP_TEXT_CONTEXT(dt->event_context), item, p); + } else if (dynamic_cast<SPOffset *>(item)) { + tool_switch("Node", win); + } +} + +/** + * Set display mode. Callback for 'tool-switch' action. + */ +void +tool_switch(Glib::ustring const &tool, InkscapeWindow *win) +{ + auto const &tool_data = get_tool_data(); + // Valid tool? + auto tool_it = tool_data.find(tool); + if (tool_it == tool_data.end()) { + std::cerr << "tool-switch: invalid tool name: " << tool << std::endl; + return; + } + + // Have desktop? + SPDesktop* dt = win->get_desktop(); + if (!dt) { + std::cerr << "tool_switch: no desktop!" << std::endl; + return; + } + + auto action = win->lookup_action("tool-switch"); + if (!action) { + std::cerr << "tool-switch: action 'tool-switch' missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr<Gio::SimpleAction>::cast_dynamic(action); + if (!saction) { + std::cerr << "tool-switch: action 'tool-switch' not SimpleAction!" << std::endl; + return; + } + + // Update button states. + saction->set_enabled(false); // Avoid infinite loop when called by tool_toogle(). + saction->change_state(tool); + saction->set_enabled(true); + + // Switch to new tool. TODO: Clean this up. This should be one window function. + // Setting tool via preference path is a bit strange. + dt->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, get_tool_msg().at(tool).c_str()); + dt->setEventContext(tool_data.at(tool).pref_path); +} + +/** + * Open preferences page for tool. Could be turned into actions if need be. + */ +void +tool_preferences(Glib::ustring const &tool, InkscapeWindow *win) +{ + auto const &tool_data = get_tool_data(); + // Valid tool? + auto tool_it = tool_data.find(tool); + if (tool_it == tool_data.end()) { + std::cerr << "tool-preferences: invalid tool name: " << tool << std::endl; + return; + } + + // Have desktop? + SPDesktop* dt = win->get_desktop(); + if (!dt) { + std::cerr << "tool-preferences: no desktop!" << std::endl; + return; + } + + auto prefs = Inkscape::Preferences::get(); + prefs->setInt("/dialogs/preferences/page", tool_it->second.pref); + Inkscape::UI::Dialog::DialogContainer* container = dt->getContainer(); + + // Create dialog if it doesn't exist (also sets page if dialog not already in opened tab). + container->new_floating_dialog("Preferences"); + + // Find dialog and explicitly set page (in case not set in previous line). + auto dialog = Inkscape::UI::Dialog::DialogManager::singleton().find_floating_dialog("Preferences"); + if (dialog) { + auto pref_dialog = dynamic_cast<Inkscape::UI::Dialog::InkscapePreferences *>(dialog); + if (pref_dialog) { + pref_dialog->showPage(); // Switch to page indicated in preferences file (set above). + } + } +} + +/** + * Toggle between "Selector" and last used tool. + */ +void +tool_toggle(InkscapeWindow *win) +{ + SPDesktop* dt = win->get_desktop(); + if (!dt) { + std::cerr << "tool_toggle: no desktop!" << std::endl; + return; + } + + auto action = win->lookup_action("tool-switch"); + if (!action) { + std::cerr << "tool_toggle: action 'tool_switch' missing!" << std::endl; + return; + } + + auto saction = Glib::RefPtr<Gio::SimpleAction>::cast_dynamic(action); + if (!saction) { + std::cerr << "tool_toogle: action 'tool_switch' not SimpleAction!" << std::endl; + return; + } + + static Glib::ustring old_tool = "Select"; + + Glib::ustring tool; + saction->get_state(tool); + if (tool == "Select") { + tool = old_tool; + } else { + old_tool = tool; + tool = "Select"; + } + + tool_switch(tool, win); +} + +Glib::ustring get_active_tool(SPDesktop *desktop) +{ + InkscapeWindow* win = desktop->getInkscapeWindow(); + return get_active_tool(win); +} + +int get_active_tool_enum(SPDesktop *desktop) +{ + InkscapeWindow* win = desktop->getInkscapeWindow(); + return get_active_tool_enum(win); +} + +void set_active_tool(SPDesktop *desktop, Glib::ustring const &tool) +{ + InkscapeWindow* win = desktop->getInkscapeWindow(); + set_active_tool(win, tool); +} + +void set_active_tool(SPDesktop *desktop, SPItem *item, Geom::Point const p) +{ + InkscapeWindow* win = desktop->getInkscapeWindow(); + set_active_tool(win, item, p); +} + +std::vector<std::vector<Glib::ustring>> raw_data_tools = +{ + // clang-format off + {"win.tool-switch('Select')", N_("Select Tool"), "Tool Switch", N_("Select and transform objects") }, + {"win.tool-switch('Node')", N_("Node Tool"), "Tool Switch", N_("Edit paths by nodes") }, + + {"win.tool-switch('Rect')", N_("Rectangle Tool"), "Tool Switch", N_("Create rectangles and squares") }, + {"win.tool-switch('Arc')", N_("Ellipse/Arc Tool"), "Tool Switch", N_("Create circles, ellipses and arcs") }, + {"win.tool-switch('Star')", N_("Star/Polygon Tool"), "Tool Switch", N_("Create stars and polygons") }, + {"win.tool-switch('3DBox')", N_("3D Box Tool"), "Tool Switch", N_("Create 3D Boxes") }, + {"win.tool-switch('Spiral')", N_("Spiral Tool"), "Tool Switch", N_("Create spirals") }, + {"win.tool-switch('Marker')", N_("Marker Tool"), "Tool Switch", N_("Edit markers") }, + + {"win.tool-switch('Pen')", N_("Pen Tool"), "Tool Switch", N_("Draw Bezier curves and straight lines") }, + {"win.tool-switch('Pencil')", N_("Pencil Tool"), "Tool Switch", N_("Draw freehand lines") }, + {"win.tool-switch('Calligraphic')", N_("Calligraphy Tool"), "Tool Switch", N_("Draw calligraphic or brush strokes") }, + {"win.tool-switch('Text')", N_("Text Tool"), "Tool Switch", N_("Create and edit text objects") }, + + {"win.tool-switch('Gradient')", N_("Gradient Tool"), "Tool Switch", N_("Create and edit gradients") }, + {"win.tool-switch('Mesh')", N_("Mesh Tool"), "Tool Switch", N_("Create and edit meshes") }, + {"win.tool-switch('Dropper')", N_("Dropper Tool"), "Tool Switch", N_("Pick colors from image") }, + {"win.tool-switch('PaintBucket')", N_("Paint Bucket Tool"), "Tool Switch", N_("Fill bounded areas") }, + + {"win.tool-switch('Tweak')", N_("Tweak Tool"), "Tool Switch", N_("Tweak objects by sculpting or painting") }, + {"win.tool-switch('Spray')", N_("Spray Tool"), "Tool Switch", N_("Spray copies or clones of objects") }, + {"win.tool-switch('Eraser')", N_("Eraser Tool"), "Tool Switch", N_("Erase objects or paths") }, + {"win.tool-switch('Connector')", N_("Connector Tool"), "Tool Switch", N_("Create diagram connectors") }, + {"win.tool-switch('LPETool')", N_("LPE Tool"), "Tool Switch", N_("Do geometric constructions") }, + + {"win.tool-switch('Zoom')", N_("Zoom Tool"), "Tool Switch", N_("Zoom in or out") }, + {"win.tool-switch('Measure')", N_("Measure Tool"), "Tool Switch", N_("Measure objects") }, + {"win.tool-switch('Pages')", N_("Pages Tool"), "Tool Switch", N_("Create and edit document pages") }, + + {"win.tool-toggle", N_("Toggle Tool"), "Tool Switch", N_("Toggle between Select tool and last used tool") }, + // clang-format on +}; + + +void +add_actions_tools(InkscapeWindow* win) +{ + // clang-format off + win->add_action_radio_string ( "tool-switch", sigc::bind<InkscapeWindow*>(sigc::ptr_fun(&tool_switch), win), "Select"); + win->add_action( "tool-toggle", sigc::bind<InkscapeWindow*>(sigc::ptr_fun(&tool_toggle), win) ); + // clang-format on + + auto app = InkscapeApplication::instance(); + if (!app) { + std::cerr << "add_actions_tools: no app!" << std::endl; + return; + } + + app->get_action_extra_data().add_data(raw_data_tools); +} + + +/* + 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 : |