summaryrefslogtreecommitdiffstats
path: root/src/ui/modifiers.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/ui/modifiers.h249
1 files changed, 249 insertions, 0 deletions
diff --git a/src/ui/modifiers.h b/src/ui/modifiers.h
new file mode 100644
index 0000000..54765c4
--- /dev/null
+++ b/src/ui/modifiers.h
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#ifndef SEEN_SP_MODIFIERS_H
+#define SEEN_SP_MODIFIERS_H
+/*
+ * Copyright (C) 2020 Martin Owens <doctormo@gmail.com>
+
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <cstring>
+#include <cstdarg>
+#include <string>
+#include <vector>
+#include <map>
+
+#include <gdk/gdk.h>
+
+#include "message-context.h"
+
+namespace Inkscape {
+namespace Modifiers {
+
+using KeyMask = int;
+using Trigger = int;
+
+enum Key : KeyMask {
+ NEVER = -2, // Never happen, switch off
+ NOT_SET = -1, // Not set (user or keys)
+ ALWAYS = 0, // Always happens, no modifier needed
+ SHIFT = GDK_SHIFT_MASK,
+ CTRL = GDK_CONTROL_MASK,
+ ALT = GDK_MOD1_MASK,
+ SUPER = GDK_SUPER_MASK,
+ HYPER = GDK_HYPER_MASK,
+ META = GDK_META_MASK,
+ ALL_MODS = SHIFT | CTRL | ALT | SUPER | HYPER | META,
+};
+
+// Triggers used for collision warnings, two tools are using the same trigger
+enum Triggers : Trigger {
+ NO_CATEGORY, CANVAS, SELECT, MOVE, TRANSFORM,
+ // Action taken to trigger this modifier, starts at
+ // bit 6 so categories and triggers can be combined.
+ CLICK = 32,
+ DRAG = 64,
+ SCROLL = 128,
+};
+
+/**
+ * This anonymous enum is used to provide a list of the Shifts
+ */
+enum class Type {
+ // {TOOL_NAME}_{ACTION_NAME}
+
+ // Canvas tools (applies to any tool selection)
+ CANVAS_PAN_Y, // Pan up and down {NOTHING+SCROLL}
+ CANVAS_PAN_X, // Pan left and right {SHIFT+SCROLL}
+ CANVAS_ZOOM, // Zoom in and out {CTRL+SCROLL}
+ CANVAS_ROTATE, // Rotate CW and CCW {CTRL+SHIFT+SCROLL}
+
+ // Select tool (minus transform)
+ SELECT_ADD_TO, // Add selection {SHIFT+CLICK}
+ SELECT_IN_GROUPS, // Select within groups {CTRL+CLICK}
+ SELECT_TOUCH_PATH, // Draw band to select {ALT+DRAG+Nothing selected}
+ SELECT_ALWAYS_BOX, // Draw box to select {SHIFT+DRAG}
+ SELECT_FIRST_HIT, // Start dragging first item hit {CTRL+DRAG} (Is this an actual feature?)
+ SELECT_FORCE_DRAG, // Drag objects even if the mouse isn't over them {ALT+DRAG+Selected}
+ SELECT_CYCLE, // Cycle through objects under cursor {ALT+SCROLL}
+
+ // Transform handles (applies to multiple tools)
+ MOVE_CONFINE, // Limit dragging to X OR Y only {DRAG+CTRL}
+ MOVE_INCREMENT, // Move objects by fixed amounts {DRAG+ALT}
+ MOVE_SNAPPING, // Disable snapping while moving {DRAG+SHIFT}
+ TRANS_CONFINE, // Confine resize aspect ratio {HANDLE+CTRL}
+ TRANS_INCREMENT, // Scale/Rotate/skew by fixed ratio angles {HANDLE+ALT}
+ 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
+};
+
+
+// Generate a label such as Shift+Ctrl from any KeyMask
+std::string generate_label(KeyMask mask, std::string sep = "+");
+unsigned long calculate_weight(KeyMask mask);
+
+// Generate a responsivle tooltip set
+void responsive_tooltip(Inkscape::MessageContext *message_context, GdkEvent *event, int num_args, ...);
+
+/**
+ * A class to represent ways functionality is driven by shift modifiers
+ */
+class Modifier {
+private:
+ /** An easy to use definition of the table of modifiers by Type and ID. */
+ typedef std::map<Type, Modifier *> Container;
+ typedef std::map<std::string, Modifier *> Lookup;
+ typedef std::map<Trigger, std::string> CategoryNames;
+
+ /** A table of all the created modifiers and their ID lookups. */
+ static Container _modifiers;
+ static Lookup _modifier_lookup;
+ static CategoryNames _category_names;
+
+ char const * _id; // A unique id used by keys.xml to identify it
+ char const * _name; // A descriptive name used in preferences UI
+ char const * _desc; // A more verbose description used in preferences UI
+
+ Trigger _category; // The category of tool, what it might conflict with
+ Trigger _trigger; // The type of trigger/action
+
+ // Default values if nothing is set in keys.xml
+ KeyMask _and_mask_default; // The pressed keys must have these bits set
+ unsigned long _weight_default = 0;
+
+ // User set data, set by keys.xml (or other included file)
+ KeyMask _and_mask_keys = NOT_SET;
+ KeyMask _not_mask_keys = NOT_SET;
+ unsigned long _weight_keys = 0;
+ KeyMask _and_mask_user = NOT_SET;
+ KeyMask _not_mask_user = NOT_SET;
+ unsigned long _weight_user = 0;
+
+protected:
+
+public:
+
+ char const * get_id() const { return _id; }
+ char const * get_name() const { return _name; }
+ char const * get_description() const { return _desc; }
+ Trigger get_trigger() const { return _category | _trigger; }
+
+ // Set user value
+ void set_keys(KeyMask and_mask, KeyMask not_mask) {
+ _and_mask_keys = and_mask;
+ _not_mask_keys = not_mask;
+ _weight_keys = calculate_weight(and_mask) + calculate_weight(not_mask);
+ }
+ void set_user(KeyMask and_mask, KeyMask not_mask) {
+ _and_mask_user = and_mask;
+ _not_mask_user = not_mask;
+ _weight_user = calculate_weight(and_mask) + calculate_weight(not_mask);
+ }
+ void unset_keys() { set_keys(NOT_SET, NOT_SET); }
+ void unset_user() { set_user(NOT_SET, NOT_SET); }
+ bool is_set_user() const { return _and_mask_user != NOT_SET; }
+
+ // Get value, either user defined value or default
+ KeyMask get_and_mask() {
+ if(_and_mask_user != NOT_SET) return _and_mask_user;
+ if(_and_mask_keys != NOT_SET) return _and_mask_keys;
+ return _and_mask_default;
+ }
+ KeyMask get_not_mask() {
+ // The not mask is enabled by the AND mask being set first.
+ if(_and_mask_user != NOT_SET) return _not_mask_user;
+ if(_and_mask_keys != NOT_SET) return _not_mask_keys;
+ return NOT_SET;
+ }
+ // Return number of bits set for the keys
+ unsigned long get_weight() {
+ if(_and_mask_user != NOT_SET) return _weight_user;
+ if(_and_mask_keys != NOT_SET) return _weight_keys;
+ return _weight_default;
+ }
+
+ // Generate labels such as "Shift+Ctrl" for the active modifier
+ std::string get_label() { return generate_label(get_and_mask()); }
+ std::string get_category() { return _category_names[_category]; }
+
+ // Configurations for saving the xml file
+ bool get_config_user_disabled() { return (_and_mask_user == NEVER); }
+ std::string get_config_user_and() { return generate_label(_and_mask_user, ","); }
+ std::string get_config_user_not() { return generate_label(_not_mask_keys, ","); }
+
+ /**
+ * Inititalizes the Modifier with the parameters.
+ *
+ * @param id Goes to \c _id.
+ * @param name Goes to \c _name.
+ * @param desc Goes to \c _desc.
+ * @param default_ Goes to \c _default.
+ */
+ Modifier(char const * id,
+ char const * name,
+ char const * desc,
+ const KeyMask and_mask,
+ const Trigger category,
+ const Trigger trigger) :
+ _id(id),
+ _name(name),
+ _desc(desc),
+ _and_mask_default(and_mask),
+ _category(category),
+ _trigger(trigger)
+ {
+ _modifier_lookup.emplace(_id, this);
+ _weight_default = calculate_weight(and_mask);
+ }
+ // Delete the destructor, because we are eternal
+ ~Modifier() = delete;
+
+ static Type which(Trigger trigger, int button_state);
+ static std::vector<Modifier *>getList ();
+ bool active(int button_state);
+
+ /**
+ * A function to turn an enum index into a modifier object.
+ *
+ * @param index The enum index to be translated
+ * @return A pointer to a modifier object or a NULL if not found.
+ */
+ static Modifier * get(Type index) {
+ return _modifiers[index];
+ }
+ /**
+ * A function to turn a string id into a modifier object.
+ *
+ * @param id The id string to be translated
+ * @return A pointer to a modifier object or a NULL if not found.
+ */
+ static Modifier * get(char const * id) {
+ Modifier *modifier = nullptr;
+ Lookup::iterator mod_found = _modifier_lookup.find(id);
+
+ if (mod_found != _modifier_lookup.end()) {
+ modifier = mod_found->second;
+ }
+
+ return modifier;
+ }
+
+}; // Modifier class
+
+} // namespace Modifiers
+} // namespace Inkscape
+
+
+#endif // SEEN_SP_MODIFIERS_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :