Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
modifiers.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
/*
9 * Authors:
10 * 2020 Martin Owens <doctormo@geek-2.com>
11 *
12 * Copyright (C) 2018 Authors
13 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
14 */
15
16#include <cstring>
17#include <string>
18#include <bitset>
19#include <glibmm/i18n.h>
20
21#include "modifiers.h"
22#include "ui/tools/tool-base.h"
23
25
26namespace {
27using ModifierIdToTypeMap = std::map<std::string, Type>;
28ModifierIdToTypeMap const &modifier_type_from_id()
29{
30 static ModifierIdToTypeMap const static_id_to_type_map {
31 {"canvas-pan-y", Type::CANVAS_PAN_Y},
32 {"canvas-pan-x", Type::CANVAS_PAN_X},
33 {"canvas-zoom", Type::CANVAS_ZOOM},
34 {"canvas-rotate", Type::CANVAS_ROTATE},
35 {"select-add-to", Type::SELECT_ADD_TO},
36 {"select-in-groups", Type::SELECT_IN_GROUPS},
37 {"select-touch-path", Type::SELECT_TOUCH_PATH},
38 {"select-always-box", Type::SELECT_ALWAYS_BOX},
39 {"select-remove-from", Type::SELECT_REMOVE_FROM},
40 {"select-force-drag", Type::SELECT_FORCE_DRAG},
41 {"select-cycle", Type::SELECT_CYCLE},
42 {"move-confine", Type::MOVE_CONFINE},
43 {"move-increment", Type::MOVE_INCREMENT},
44 {"move-snapping", Type::MOVE_SNAPPING},
45 {"trans-confine", Type::TRANS_CONFINE},
46 {"trans-increment", Type::TRANS_INCREMENT},
47 {"trans-off-center", Type::TRANS_OFF_CENTER},
48 {"trans-snapping", Type::TRANS_SNAPPING},
49 {"bool-shift", Type::BOOL_SHIFT},
50 {"node-grow-linear", Type::NODE_GROW_LINEAR},
51 {"node-invert", Type::NODE_INVERT},
52 {"node-remove-from", Type::NODE_REMOVE_FROM},
53 {"node-grow-spatial", Type::NODE_GROW_SPATIAL}
54 };
55 return static_id_to_type_map;
56}
57
58inline std::pair<Modifiers::Type, Modifier> make_modifier(char const *id,
59 char const *name,
60 char const *desc,
61 KeyMask and_mask,
62 Trigger category,
63 Trigger trigger)
64{
65 return {modifier_type_from_id().at(id), Modifier(id, name, desc, and_mask, category, trigger)};
66}
67
68std::map<int, int> const &key_map()
69{
70 static const std::map<int, int> static_key_map = {
71 {GDK_KEY_Alt_L, GDK_ALT_MASK},
72 {GDK_KEY_Alt_R, GDK_ALT_MASK},
73 {GDK_KEY_Control_L, GDK_CONTROL_MASK},
74 {GDK_KEY_Control_R, GDK_CONTROL_MASK},
75 {GDK_KEY_Shift_L, GDK_SHIFT_MASK},
76 {GDK_KEY_Shift_R, GDK_SHIFT_MASK},
77 {GDK_KEY_Meta_L, GDK_META_MASK},
78 {GDK_KEY_Meta_R, GDK_META_MASK},
79 };
80 return static_key_map;
81}
82} // end anonymous namespace
83
85{
86 // these must be in the same order as the * enum in "modifiers.h"
87 static Modifier::Container static_modifiers {
88 // Canvas modifiers
89 make_modifier("canvas-pan-y", _("Vertical pan"), _("Pan/Scroll up and down"), ALWAYS, CANVAS, SCROLL),
90 make_modifier("canvas-pan-x", _("Horizontal pan"), _("Pan/Scroll left and right"), SHIFT, CANVAS, SCROLL),
91 make_modifier("canvas-zoom", _("Canvas zoom"), _("Zoom in and out with scroll wheel"), CTRL, CANVAS, SCROLL),
92 make_modifier("canvas-rotate", _("Canvas rotate"), _("Rotate the canvas with scroll wheel"), SHIFT | CTRL, CANVAS, SCROLL),
93
94 // Select tool modifiers (minus transforms)
95 make_modifier("select-add-to", _("Add to selection"), _("Add items to existing selection"), SHIFT, SELECT, CLICK),
96 make_modifier("select-in-groups", _("Select inside groups"), _("Ignore groups when selecting items"), CTRL, SELECT, CLICK),
97 make_modifier("select-touch-path", _("Select with touch-path"), _("Draw a band around items to select them"), ALT, SELECT, DRAG),
98 make_modifier("select-always-box", _("Select with box"), _("Don't drag items, select more with a box"), SHIFT, SELECT, DRAG),
99 make_modifier("select-remove-from", _("Remove from selection"), _("Remove items from existing selection"), CTRL, SELECT, DRAG),
100 make_modifier("select-force-drag", _("Forced Drag"), _("Drag objects even if the mouse isn't over them"), ALT, SELECT, DRAG),
101 make_modifier("select-cycle", _("Cycle through objects"), _("Scroll through objects under the cursor"), ALT, SELECT, SCROLL),
102
103 // Transform handle modifiers (applies to multiple tools)
104 make_modifier("move-confine", _("Move one axis only"), _("When dragging items, confine to either x or y axis"), CTRL, MOVE, DRAG),
105 make_modifier("move-increment", _("Move in increments"), _("Move the objects by set increments when dragging"), ALT, MOVE, DRAG),
106 make_modifier("move-snapping", _("No Move Snapping"), _("Disable snapping when moving objects"), SHIFT, MOVE, DRAG),
107 make_modifier("trans-confine", _("Keep aspect ratio"), _("When resizing objects, confine the aspect ratio"), CTRL, TRANSFORM, DRAG),
108 make_modifier("trans-increment", _("Transform in increments"), _("Scale, rotate or skew by set increments"), ALT, TRANSFORM, DRAG),
109 make_modifier("trans-off-center", _("Transform around center"), _("When scaling, scale selection symmetrically around its rotation center. When rotating/skewing, transform relative to opposite corner/edge."), SHIFT, TRANSFORM, DRAG),
110 make_modifier("trans-snapping", _("No Transform Snapping"), _("Disable snapping when transforming object."), SHIFT, TRANSFORM, DRAG),
111 // Center handle click: seltrans.cpp:734 SHIFT
112 // Align handle click: seltrans.cpp:1365 SHIFT
113 make_modifier("bool-shift", _("Switch mode"), _("Change shape builder mode temporarily by holding a modifier key."), SHIFT, BOOLEANS_TOOL, DRAG),
114
115 make_modifier("node-grow-linear", _("Linear node selection"), _("Select the next nodes with scroll wheel or keyboard"), CTRL, NODE_TOOL, SCROLL),
116 make_modifier("node-invert", _("Inverted node selection"), _("Select nodes outside the selection area"), CTRL, NODE_TOOL, DRAG),
117 make_modifier("node-remove-from", _("Remove nodes from selection"), _("Remove selected nodes from the selection"), SHIFT | CTRL, NODE_TOOL, DRAG),
118 make_modifier("node-grow-spatial", _("Spatial node selection"), _("Select more nodes with scroll wheel or keyboard"), ALWAYS, NODE_TOOL, SCROLL),
119 };
120 return static_modifiers;
121}
122
124{
125 static Modifier::CategoryNames const static_category_names {
126 {NO_CATEGORY, _("No Category")},
127 {CANVAS, _("Canvas")},
128 {SELECT, _("Selection")},
129 {MOVE, _("Movement")},
130 {TRANSFORM, _("Transformations")},
131 {NODE_TOOL, _("Node Tool")},
132 {BOOLEANS_TOOL, _("Shape Builder")},
133 };
134 return static_category_names;
135}
136
144Type Modifier::which(Trigger trigger, int button_state)
145{
146 // Record each active modifier with it's weight
147 std::map<Type, unsigned long> scales;
148 for (auto const &[key, val] : _modifiers()) {
149 if (val.get_trigger() == trigger && val.active(button_state)) {
150 scales[key] = val.get_weight();
151 }
152 }
153 // Sort the weightings
154 using pair_type = decltype(scales)::value_type;
155 auto sorted = std::max_element
156 (
157 std::begin(scales), std::end(scales),
158 [] (const pair_type & p1, const pair_type & p2) {
159 return p1.second < p2.second;
160 }
161 );
162 return sorted->first;
163}
164
170std::vector<Modifier const *> Modifier::getList()
171{
172 std::vector<Modifier const *> modifiers;
173 // Go through the dynamic modifier table
174 for (auto const &[_, val] : _modifiers()) {
175 modifiers.push_back(&val);
176 }
177
178 return modifiers;
179}
180
181Modifier *Modifier::get(char const *id)
182{
183 auto const type_it = modifier_type_from_id().find(id);
184 if (type_it == modifier_type_from_id().end()) {
185 return nullptr;
186 }
187 auto const modifier_it = _modifiers().find(type_it->second);
188 if (modifier_it == _modifiers().end()) {
189 return nullptr;
190 }
191 return &(modifier_it->second);
192}
193
200bool Modifier::active(int state) const
201{
202 // TODO:
203 // * ALT key is sometimes MOD1, MOD2 etc, if we find other ALT keys, set the ALT bit
204 // * SUPER key could be HYPER or META, these cases need to be considered.
205 auto and_mask = get_and_mask();
206 auto not_mask = get_not_mask();
207 auto active = Key::ALL_MODS & state;
208 // Check that all keys in AND mask are pressed, and NONE of the NOT mask are.
209 return and_mask != NEVER && ((active & and_mask) == and_mask) && (not_mask == NOT_SET || (active & not_mask) == 0);
210}
211
222bool Modifier::active(int state, int keyval, bool release) const
223{
224 return active(add_keyval(state, keyval, release));
225}
226
233std::string generate_label(KeyMask mask, std::string sep)
234{
235 auto ret = std::string();
236 if(mask == NOT_SET) {
237 return "-";
238 }
239 if(mask == NEVER) {
240 ret.append(_("[NEVER]"));
241 return ret;
242 }
243 if(mask & CTRL) ret.append(_("Ctrl"));
244 if(mask & SHIFT) {
245 if(!ret.empty()) ret.append(sep);
246 ret.append(_("Shift"));
247 }
248 if(mask & ALT) {
249 if(!ret.empty()) ret.append(sep);
250 ret.append(_("Alt"));
251 }
252 if(mask & SUPER) {
253 if(!ret.empty()) ret.append(sep);
254 ret.append(_("Super"));
255 }
256 if(mask & HYPER) {
257 if(!ret.empty()) ret.append(sep);
258 ret.append(_("Hyper"));
259 }
260 if(mask & META) {
261 if(!ret.empty()) ret.append(sep);
262 ret.append(_("Meta"));
263 }
264 return ret;
265}
266
273unsigned long calculate_weight(KeyMask mask)
274{
275
276 if (mask < 0)
277 return 0;
278 std::bitset<sizeof(mask)> bit_mask(mask);
279 return bit_mask.count();
280}
281
290void responsive_tooltip(MessageContext *message_context, KeyEvent const &event, int num_args, ...)
291{
292 std::string ctrl_msg = "<b>Ctrl</b>: ";
293 std::string shift_msg = "<b>Shift</b>: ";
294 std::string alt_msg = "<b>Alt</b>: ";
295
296 // NOTE: This will hide any keys changed to SUPER or multiple keys such as CTRL+SHIFT
297 va_list args;
298 va_start(args, num_args);
299 for(int i = 0; i < num_args; i++) {
300 auto modifier = Modifier::get(va_arg(args, Type));
301 auto name = std::string(_(modifier->get_name()));
302 switch (modifier->get_and_mask()) {
303 case CTRL:
304 ctrl_msg += name + ", ";
305 break;
306 case SHIFT:
307 shift_msg += name + ", ";
308 break;
309 case ALT:
310 alt_msg += name + ", ";
311 break;
312 default:
313 g_warning("Unhandled responsivle tooltip: %s", name.c_str());
314 }
315 }
316 va_end(args);
317 ctrl_msg.erase(ctrl_msg.size() - 2);
318 shift_msg.erase(shift_msg.size() - 2);
319 alt_msg.erase(alt_msg.size() - 2);
320
321 UI::Tools::sp_event_show_modifier_tip(message_context, event,
322 ctrl_msg.c_str(), shift_msg.c_str(), alt_msg.c_str());
323}
324
335int add_keyval(int state, int keyval, bool release)
336{
337 if (auto it = key_map().find(keyval); it != key_map().end()) {
338 if (release)
339 state &= ~it->second;
340 else
341 state |= it->second;
342 }
343 return state;
344}
345
346} // namespace Inkscape::Modifiers
347
348/*
349 Local Variables:
350 mode:c++
351 c-file-style:"stroustrup"
352 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
353 indent-tabs-mode:nil
354 fill-column:99
355 End:
356*/
357// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
A convenience class for working with MessageStacks.
A class to represent ways functionality is driven by shift modifiers.
Definition modifiers.h:103
unsigned long get_weight() const
Definition modifiers.h:166
static CategoryNames const & _category_names()
bool active(int button_state) const
Test if this modifier is currently active.
KeyMask get_not_mask() const
Definition modifiers.h:159
static Type which(Trigger trigger, int button_state)
Given a Trigger, find which modifier is active (category lookup)
static Container & _modifiers()
A table of all the created modifiers and their ID lookups.
Definition modifiers.cpp:84
std::map< Trigger, std::string > CategoryNames
Definition modifiers.h:107
KeyMask get_and_mask() const
Definition modifiers.h:154
static Modifier * get(Type index)
A function to turn an enum index into a modifier object.
Definition modifiers.h:215
std::map< Type, Modifier > Container
An easy to use definition of the table of modifiers by Type and ID.
Definition modifiers.h:106
static std::vector< Modifier const * > getList()
List all the modifiers available.
Geom::Point end
Type
This anonymous enum is used to provide a list of the Shifts.
Definition modifiers.h:56
int add_keyval(int state, int keyval, bool release)
Add or remove the GDK keyval to the button state if it's one of the keys that define the key mask.
unsigned long calculate_weight(KeyMask mask)
Calculate the weight of this mask based on how many bits are set.
void responsive_tooltip(MessageContext *message_context, KeyEvent const &event, int num_args,...)
Set the responsive tooltip for this tool, given the selected types.
std::string generate_label(KeyMask mask, std::string sep)
Generate a label for any modifier keys based on the mask.
void sp_event_show_modifier_tip(MessageContext *message_context, KeyEvent const &event, char const *ctrl_tip, char const *shift_tip, char const *alt_tip)
Show tool context specific modifier tip.
static cairo_user_data_key_t key
va_end(args)
int const char va_start(args, fmt)
A key has been pressed.
Glib::ustring name
Definition toolbars.cpp:55