summaryrefslogtreecommitdiffstats
path: root/src/actions/actions-tools.cpp
blob: c6214c7bb2831fe18afc5463ffa9a3bcb0849cf9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
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 :