2015-02-12 23:16:25 +08:00
|
|
|
// Aseprite
|
2024-04-23 05:28:03 +08:00
|
|
|
// Copyright (C) 2018-2024 Igara Studio S.A.
|
2018-03-07 06:21:09 +08:00
|
|
|
// Copyright (C) 2001-2018 David Capello
|
2015-02-12 23:16:25 +08:00
|
|
|
//
|
2016-08-27 04:02:58 +08:00
|
|
|
// This program is distributed under the terms of
|
|
|
|
// the End-User License Agreement for Aseprite.
|
2014-10-29 22:58:03 +08:00
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "app/ui/keyboard_shortcuts.h"
|
|
|
|
|
|
|
|
#include "app/app.h"
|
|
|
|
#include "app/app_menus.h"
|
|
|
|
#include "app/commands/command.h"
|
|
|
|
#include "app/commands/commands.h"
|
|
|
|
#include "app/commands/params.h"
|
2018-07-07 22:54:44 +08:00
|
|
|
#include "app/doc.h"
|
2022-01-03 16:31:46 +08:00
|
|
|
#include "app/i18n/strings.h"
|
2019-12-19 05:00:11 +08:00
|
|
|
#include "app/tools/active_tool.h"
|
2014-10-29 22:58:03 +08:00
|
|
|
#include "app/tools/ink.h"
|
|
|
|
#include "app/tools/tool.h"
|
|
|
|
#include "app/tools/tool_box.h"
|
2022-03-09 07:40:11 +08:00
|
|
|
#include "app/ui/key.h"
|
2023-04-12 06:01:33 +08:00
|
|
|
#include "app/ui/timeline/timeline.h"
|
2014-10-29 22:58:03 +08:00
|
|
|
#include "app/ui_context.h"
|
|
|
|
#include "app/xml_document.h"
|
|
|
|
#include "app/xml_exception.h"
|
2022-03-09 07:40:11 +08:00
|
|
|
#include "fmt/format.h"
|
2014-10-29 22:58:03 +08:00
|
|
|
#include "ui/accelerator.h"
|
|
|
|
#include "ui/message.h"
|
|
|
|
|
2024-04-23 05:28:03 +08:00
|
|
|
#include "tinyxml2.h"
|
|
|
|
|
2018-07-24 02:15:04 +08:00
|
|
|
#include <algorithm>
|
|
|
|
#include <set>
|
|
|
|
#include <vector>
|
|
|
|
|
2014-10-29 22:58:03 +08:00
|
|
|
#define XML_KEYBOARD_FILE_VERSION "1"
|
|
|
|
|
2022-01-03 16:31:46 +08:00
|
|
|
#define I18N_KEY(a) app::Strings::keyboard_shortcuts_##a()
|
|
|
|
|
2024-04-23 05:28:03 +08:00
|
|
|
using namespace tinyxml2;
|
|
|
|
|
2014-10-29 22:58:03 +08:00
|
|
|
namespace {
|
|
|
|
|
2022-01-03 16:31:46 +08:00
|
|
|
struct KeyShortcutAction {
|
2014-10-29 22:58:03 +08:00
|
|
|
const char* name;
|
2022-01-03 16:31:46 +08:00
|
|
|
std::string userfriendly;
|
2014-10-29 22:58:03 +08:00
|
|
|
app::KeyAction action;
|
2020-09-19 06:29:43 +08:00
|
|
|
app::KeyContext context;
|
2014-10-29 22:58:03 +08:00
|
|
|
};
|
2024-12-17 01:52:19 +08:00
|
|
|
|
2022-01-03 16:31:46 +08:00
|
|
|
static std::vector<KeyShortcutAction> g_actions;
|
2024-12-17 01:52:19 +08:00
|
|
|
|
2022-01-03 16:31:46 +08:00
|
|
|
static const std::vector<KeyShortcutAction>& actions()
|
|
|
|
{
|
|
|
|
if (g_actions.empty()) {
|
|
|
|
g_actions = std::vector<KeyShortcutAction>{
|
|
|
|
{ "CopySelection",
|
|
|
|
I18N_KEY(copy_selection),
|
|
|
|
app::KeyAction::CopySelection,
|
|
|
|
app::KeyContext::TranslatingSelection },
|
|
|
|
{ "SnapToGrid",
|
|
|
|
I18N_KEY(snap_to_grid),
|
|
|
|
app::KeyAction::SnapToGrid,
|
|
|
|
app::KeyContext::TranslatingSelection },
|
|
|
|
{ "LockAxis",
|
|
|
|
I18N_KEY(lock_axis),
|
|
|
|
app::KeyAction::LockAxis,
|
|
|
|
app::KeyContext::TranslatingSelection },
|
|
|
|
{ "FineControl",
|
|
|
|
I18N_KEY(fine_translating),
|
|
|
|
app::KeyAction::FineControl,
|
|
|
|
app::KeyContext::TranslatingSelection },
|
|
|
|
{ "MaintainAspectRatio",
|
|
|
|
I18N_KEY(maintain_aspect_ratio),
|
|
|
|
app::KeyAction::MaintainAspectRatio,
|
|
|
|
app::KeyContext::ScalingSelection },
|
|
|
|
{ "ScaleFromCenter",
|
|
|
|
I18N_KEY(scale_from_center),
|
|
|
|
app::KeyAction::ScaleFromCenter,
|
2020-09-03 05:09:19 +08:00
|
|
|
app::KeyContext::ScalingSelection },
|
2022-01-03 16:31:46 +08:00
|
|
|
{ "FineControl",
|
|
|
|
I18N_KEY(fine_scaling),
|
|
|
|
app::KeyAction::FineControl,
|
|
|
|
app::KeyContext::ScalingSelection },
|
|
|
|
{ "AngleSnap",
|
|
|
|
I18N_KEY(angle_snap),
|
|
|
|
app::KeyAction::AngleSnap,
|
|
|
|
app::KeyContext::RotatingSelection },
|
|
|
|
{ "AddSelection",
|
|
|
|
I18N_KEY(add_selection),
|
|
|
|
app::KeyAction::AddSelection,
|
|
|
|
app::KeyContext::SelectionTool },
|
|
|
|
{ "SubtractSelection",
|
|
|
|
I18N_KEY(subtract_selection),
|
|
|
|
app::KeyAction::SubtractSelection,
|
|
|
|
app::KeyContext::SelectionTool },
|
|
|
|
{ "IntersectSelection",
|
|
|
|
I18N_KEY(intersect_selection),
|
|
|
|
app::KeyAction::IntersectSelection,
|
|
|
|
app::KeyContext::SelectionTool },
|
|
|
|
{ "AutoSelectLayer",
|
|
|
|
I18N_KEY(auto_select_layer),
|
|
|
|
app::KeyAction::AutoSelectLayer,
|
|
|
|
app::KeyContext::MoveTool },
|
|
|
|
{ "StraightLineFromLastPoint",
|
|
|
|
I18N_KEY(line_from_last_point),
|
|
|
|
app::KeyAction::StraightLineFromLastPoint,
|
|
|
|
app::KeyContext::FreehandTool },
|
|
|
|
{ "AngleSnapFromLastPoint",
|
|
|
|
I18N_KEY(angle_from_last_point),
|
|
|
|
app::KeyAction::AngleSnapFromLastPoint,
|
|
|
|
app::KeyContext::FreehandTool },
|
|
|
|
{ "MoveOrigin",
|
|
|
|
I18N_KEY(move_origin),
|
|
|
|
app::KeyAction::MoveOrigin,
|
|
|
|
app::KeyContext::ShapeTool },
|
|
|
|
{ "SquareAspect",
|
|
|
|
I18N_KEY(square_aspect),
|
|
|
|
app::KeyAction::SquareAspect,
|
|
|
|
app::KeyContext::ShapeTool },
|
|
|
|
{ "DrawFromCenter",
|
|
|
|
I18N_KEY(draw_from_center),
|
|
|
|
app::KeyAction::DrawFromCenter,
|
|
|
|
app::KeyContext::ShapeTool },
|
|
|
|
{ "RotateShape",
|
|
|
|
I18N_KEY(rotate_shape),
|
|
|
|
app::KeyAction::RotateShape,
|
|
|
|
app::KeyContext::ShapeTool },
|
|
|
|
{ "LeftMouseButton",
|
|
|
|
I18N_KEY(trigger_left_mouse_button),
|
|
|
|
app::KeyAction::LeftMouseButton,
|
|
|
|
app::KeyContext::Any },
|
|
|
|
{ "RightMouseButton",
|
|
|
|
I18N_KEY(trigger_right_mouse_button),
|
|
|
|
app::KeyAction::RightMouseButton,
|
|
|
|
app::KeyContext::Any }
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return g_actions;
|
2024-12-17 01:52:19 +08:00
|
|
|
}
|
2022-01-03 16:31:46 +08:00
|
|
|
|
2020-09-03 05:09:19 +08:00
|
|
|
static struct {
|
|
|
|
const char* name;
|
|
|
|
app::KeyContext context;
|
2022-01-03 16:31:46 +08:00
|
|
|
} g_contexts[] = {
|
2020-09-03 05:09:19 +08:00
|
|
|
{ "", app::KeyContext::Any },
|
|
|
|
{ "Normal", app::KeyContext::Normal },
|
|
|
|
{ "Selection", app::KeyContext::SelectionTool },
|
|
|
|
{ "TranslatingSelection", app::KeyContext::TranslatingSelection },
|
|
|
|
{ "ScalingSelection", app::KeyContext::ScalingSelection },
|
|
|
|
{ "RotatingSelection", app::KeyContext::RotatingSelection },
|
|
|
|
{ "MoveTool", app::KeyContext::MoveTool },
|
|
|
|
{ "FreehandTool", app::KeyContext::FreehandTool },
|
|
|
|
{ "ShapeTool", app::KeyContext::ShapeTool },
|
2023-04-12 06:01:33 +08:00
|
|
|
{ "FramesSelection", app::KeyContext::FramesSelection },
|
2020-09-03 05:09:19 +08:00
|
|
|
{ NULL, app::KeyContext::Any }
|
|
|
|
};
|
2024-12-17 01:52:19 +08:00
|
|
|
|
2022-03-09 07:40:11 +08:00
|
|
|
using Vec = app::DragVector;
|
2024-12-17 01:52:19 +08:00
|
|
|
|
2022-01-03 16:31:46 +08:00
|
|
|
struct KeyShortcutWheelAction {
|
2018-07-20 10:05:14 +08:00
|
|
|
const char* name;
|
2022-01-03 16:31:46 +08:00
|
|
|
const std::string userfriendly;
|
2022-03-09 07:40:11 +08:00
|
|
|
Vec vector;
|
2018-07-20 10:05:14 +08:00
|
|
|
};
|
2024-12-17 01:52:19 +08:00
|
|
|
|
2022-01-03 16:31:46 +08:00
|
|
|
static std::vector<KeyShortcutWheelAction> g_wheel_actions;
|
2024-12-17 01:52:19 +08:00
|
|
|
|
2022-01-03 16:31:46 +08:00
|
|
|
static const std::vector<KeyShortcutWheelAction>& wheel_actions()
|
|
|
|
{
|
|
|
|
if (g_wheel_actions.empty()) {
|
|
|
|
g_wheel_actions = std::vector<KeyShortcutWheelAction>{
|
|
|
|
{ "", "", Vec(0.0, 0.0) },
|
|
|
|
{ "Zoom", I18N_KEY(zoom), Vec(8.0, 0.0) },
|
|
|
|
{ "VScroll", I18N_KEY(scroll_vertically), Vec(4.0, 0.0) },
|
|
|
|
{ "HScroll", I18N_KEY(scroll_horizontally), Vec(4.0, 0.0) },
|
|
|
|
{ "FgColor", I18N_KEY(fg_color), Vec(8.0, 0.0) },
|
|
|
|
{ "BgColor", I18N_KEY(bg_color), Vec(8.0, 0.0) },
|
|
|
|
{ "FgTile", I18N_KEY(fg_tile), Vec(8.0, 0.0) },
|
|
|
|
{ "BgTile", I18N_KEY(bg_tile), Vec(8.0, 0.0) },
|
|
|
|
{ "Frame", I18N_KEY(change_frame), Vec(16.0, 0.0) },
|
|
|
|
{ "BrushSize", I18N_KEY(change_brush_size), Vec(4.0, 0.0) },
|
|
|
|
{ "BrushAngle", I18N_KEY(change_brush_angle), Vec(-4.0, 0.0) },
|
|
|
|
{ "ToolSameGroup", I18N_KEY(change_tool_same_group), Vec(8.0, 0.0) },
|
|
|
|
{ "ToolOtherGroup", I18N_KEY(change_tool), Vec(0.0, -8.0) },
|
|
|
|
{ "Layer", I18N_KEY(change_layer), Vec(0.0, 8.0) },
|
|
|
|
{ "InkType", I18N_KEY(change_ink_type), Vec(0.0, -16.0) },
|
|
|
|
{ "InkOpacity", I18N_KEY(change_ink_opacity), Vec(0.0, 1.0) },
|
|
|
|
{ "LayerOpacity", I18N_KEY(change_layer_opacity), Vec(0.0, 1.0) },
|
|
|
|
{ "CelOpacity", I18N_KEY(change_cel_opacity), Vec(0.0, 1.0) },
|
|
|
|
{ "Alpha", I18N_KEY(color_alpha), Vec(4.0, 0.0) },
|
|
|
|
{ "HslHue", I18N_KEY(color_hsl_hue), Vec(1.0, 0.0) },
|
|
|
|
{ "HslSaturation", I18N_KEY(color_hsl_saturation), Vec(4.0, 0.0) },
|
|
|
|
{ "HslLightness", I18N_KEY(color_hsl_lightness), Vec(0.0, 4.0) },
|
|
|
|
{ "HsvHue", I18N_KEY(color_hsv_hue), Vec(1.0, 0.0) },
|
|
|
|
{ "HsvSaturation", I18N_KEY(color_hsv_saturation), Vec(4.0, 0.0) },
|
|
|
|
{ "HsvValue", I18N_KEY(color_hsv_value), Vec(0.0, 4.0) }
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return g_wheel_actions;
|
2024-12-17 01:52:19 +08:00
|
|
|
}
|
2022-01-03 16:31:46 +08:00
|
|
|
|
2024-04-23 05:28:03 +08:00
|
|
|
const char* get_shortcut(XMLElement* elem)
|
|
|
|
{
|
2014-10-29 22:58:03 +08:00
|
|
|
const char* shortcut = NULL;
|
|
|
|
|
2015-02-12 23:46:56 +08:00
|
|
|
#ifdef _WIN32
|
2014-10-29 22:58:03 +08:00
|
|
|
if (!shortcut)
|
|
|
|
shortcut = elem->Attribute("win");
|
2015-02-12 23:46:56 +08:00
|
|
|
#elif defined __APPLE__
|
2014-10-29 22:58:03 +08:00
|
|
|
if (!shortcut)
|
|
|
|
shortcut = elem->Attribute("mac");
|
2015-02-12 23:46:56 +08:00
|
|
|
#else
|
2014-10-29 22:58:03 +08:00
|
|
|
if (!shortcut)
|
|
|
|
shortcut = elem->Attribute("linux");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!shortcut)
|
|
|
|
shortcut = elem->Attribute("shortcut");
|
|
|
|
|
|
|
|
return shortcut;
|
|
|
|
}
|
|
|
|
|
2020-09-19 06:29:43 +08:00
|
|
|
std::string get_user_friendly_string_for_keyaction(app::KeyAction action, app::KeyContext context)
|
|
|
|
{
|
2022-01-03 16:31:46 +08:00
|
|
|
for (const auto& a : actions()) {
|
|
|
|
if (action == a.action && context == a.context)
|
|
|
|
return a.userfriendly;
|
2018-07-20 10:05:14 +08:00
|
|
|
}
|
2022-03-09 07:40:11 +08:00
|
|
|
return std::string();
|
2024-12-17 01:52:19 +08:00
|
|
|
}
|
2018-07-20 10:05:14 +08:00
|
|
|
|
|
|
|
std::string get_user_friendly_string_for_wheelaction(app::WheelAction wheelAction)
|
|
|
|
{
|
2022-03-09 07:40:11 +08:00
|
|
|
int c = int(wheelAction);
|
|
|
|
if (c >= int(app::WheelAction::First) && c <= int(app::WheelAction::Last)) {
|
2022-01-03 16:31:46 +08:00
|
|
|
return wheel_actions()[c].userfriendly;
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
2024-12-17 01:52:19 +08:00
|
|
|
else
|
2022-03-09 07:40:11 +08:00
|
|
|
return std::string();
|
2024-12-17 01:52:19 +08:00
|
|
|
}
|
2015-02-12 23:16:25 +08:00
|
|
|
|
2022-04-14 09:46:48 +08:00
|
|
|
void erase_accel(app::KeySourceAccelList& kvs,
|
|
|
|
const app::KeySource source,
|
|
|
|
const ui::Accelerator& accel)
|
|
|
|
{
|
|
|
|
for (auto it = kvs.begin(); it != kvs.end();) {
|
|
|
|
auto& kv = *it;
|
|
|
|
if (kv.first == source && kv.second == accel) {
|
|
|
|
it = kvs.erase(it);
|
|
|
|
}
|
2024-12-17 01:52:19 +08:00
|
|
|
else
|
|
|
|
++it;
|
2022-04-14 09:46:48 +08:00
|
|
|
}
|
2024-12-17 01:52:19 +08:00
|
|
|
}
|
2022-04-14 09:46:48 +08:00
|
|
|
|
|
|
|
void erase_accels(app::KeySourceAccelList& kvs, const app::KeySource source)
|
|
|
|
{
|
|
|
|
for (auto it = kvs.begin(); it != kvs.end();) {
|
|
|
|
auto& kv = *it;
|
|
|
|
if (kv.first == source) {
|
|
|
|
it = kvs.erase(it);
|
|
|
|
}
|
2024-12-17 01:52:19 +08:00
|
|
|
else
|
|
|
|
++it;
|
2022-04-14 09:46:48 +08:00
|
|
|
}
|
2024-12-17 01:52:19 +08:00
|
|
|
}
|
2022-04-14 09:46:48 +08:00
|
|
|
|
2014-10-29 22:58:03 +08:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
namespace base {
|
|
|
|
|
|
|
|
template<>
|
|
|
|
app::KeyAction convert_to(const std::string& from)
|
|
|
|
{
|
2022-01-03 16:31:46 +08:00
|
|
|
for (const auto& a : actions()) {
|
|
|
|
if (from == a.name)
|
|
|
|
return a.action;
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
2022-01-03 16:31:46 +08:00
|
|
|
return app::KeyAction::None;
|
2024-12-17 01:52:19 +08:00
|
|
|
}
|
2014-10-29 22:58:03 +08:00
|
|
|
|
|
|
|
template<>
|
|
|
|
std::string convert_to(const app::KeyAction& from)
|
|
|
|
{
|
2022-01-03 16:31:46 +08:00
|
|
|
for (const auto& a : actions()) {
|
|
|
|
if (from == a.action)
|
|
|
|
return a.name;
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
2020-09-03 05:09:19 +08:00
|
|
|
return std::string();
|
2024-12-17 01:52:19 +08:00
|
|
|
}
|
2015-02-12 23:16:25 +08:00
|
|
|
|
2018-07-20 10:05:14 +08:00
|
|
|
template<>
|
|
|
|
app::WheelAction convert_to(const std::string& from)
|
|
|
|
{
|
2022-03-09 07:40:11 +08:00
|
|
|
for (int c = int(app::WheelAction::First); c <= int(app::WheelAction::Last); ++c) {
|
2022-01-03 16:31:46 +08:00
|
|
|
if (from == wheel_actions()[c].name)
|
2022-03-09 07:40:11 +08:00
|
|
|
return (app::WheelAction)c;
|
2018-07-20 10:05:14 +08:00
|
|
|
}
|
2022-03-09 07:40:11 +08:00
|
|
|
return app::WheelAction::None;
|
2024-12-17 01:52:19 +08:00
|
|
|
}
|
2018-07-20 10:05:14 +08:00
|
|
|
|
|
|
|
template<>
|
|
|
|
std::string convert_to(const app::WheelAction& from)
|
|
|
|
{
|
2022-03-09 07:40:11 +08:00
|
|
|
int c = int(from);
|
|
|
|
if (c >= int(app::WheelAction::First) && c <= int(app::WheelAction::Last)) {
|
2022-01-03 16:31:46 +08:00
|
|
|
return wheel_actions()[c].name;
|
2018-07-20 10:05:14 +08:00
|
|
|
}
|
2024-12-17 01:52:19 +08:00
|
|
|
else
|
2020-09-03 05:09:19 +08:00
|
|
|
return std::string();
|
2024-12-17 01:52:19 +08:00
|
|
|
}
|
2018-07-20 10:05:14 +08:00
|
|
|
|
2020-09-03 05:09:19 +08:00
|
|
|
template<>
|
|
|
|
app::KeyContext convert_to(const std::string& from)
|
|
|
|
{
|
2022-01-03 16:31:46 +08:00
|
|
|
for (int c = 0; g_contexts[c].name; ++c) {
|
|
|
|
if (from == g_contexts[c].name)
|
|
|
|
return g_contexts[c].context;
|
2020-09-03 05:09:19 +08:00
|
|
|
}
|
|
|
|
return app::KeyContext::Any;
|
2024-12-17 01:52:19 +08:00
|
|
|
}
|
2020-09-03 05:09:19 +08:00
|
|
|
|
|
|
|
template<>
|
|
|
|
std::string convert_to(const app::KeyContext& from)
|
|
|
|
{
|
2022-01-03 16:31:46 +08:00
|
|
|
for (int c = 0; g_contexts[c].name; ++c) {
|
|
|
|
if (from == g_contexts[c].context)
|
|
|
|
return g_contexts[c].name;
|
2020-09-03 05:09:19 +08:00
|
|
|
}
|
|
|
|
return std::string();
|
2024-12-17 01:52:19 +08:00
|
|
|
}
|
2020-09-03 05:09:19 +08:00
|
|
|
|
2014-10-29 22:58:03 +08:00
|
|
|
} // namespace base
|
|
|
|
|
|
|
|
namespace app {
|
|
|
|
|
|
|
|
using namespace ui;
|
|
|
|
|
2014-11-16 05:31:12 +08:00
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
// Key
|
|
|
|
|
2022-04-14 09:46:48 +08:00
|
|
|
Key::Key(const Key& k)
|
|
|
|
: m_type(k.m_type)
|
|
|
|
, m_adds(k.m_adds)
|
|
|
|
, m_dels(k.m_dels)
|
|
|
|
, m_keycontext(k.m_keycontext)
|
|
|
|
{
|
|
|
|
switch (m_type) {
|
|
|
|
case KeyType::Command:
|
|
|
|
m_command = k.m_command;
|
|
|
|
m_params = k.m_params;
|
|
|
|
break;
|
|
|
|
case KeyType::Tool:
|
|
|
|
case KeyType::Quicktool: m_tool = k.m_tool; break;
|
|
|
|
case KeyType::Action: m_action = k.m_action; break;
|
|
|
|
case KeyType::WheelAction:
|
|
|
|
m_action = k.m_action;
|
|
|
|
m_wheelAction = k.m_wheelAction;
|
|
|
|
break;
|
2022-04-14 10:01:12 +08:00
|
|
|
case KeyType::DragAction:
|
|
|
|
m_action = k.m_action;
|
|
|
|
m_wheelAction = k.m_wheelAction;
|
|
|
|
m_dragVector = k.m_dragVector;
|
|
|
|
break;
|
2022-04-14 09:46:48 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-19 06:29:43 +08:00
|
|
|
Key::Key(Command* command, const Params& params, const KeyContext keyContext)
|
2014-10-29 22:58:03 +08:00
|
|
|
: m_type(KeyType::Command)
|
|
|
|
, m_keycontext(keyContext)
|
|
|
|
, m_command(command)
|
2015-03-12 02:40:22 +08:00
|
|
|
, m_params(params)
|
2014-10-29 22:58:03 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-09-19 06:29:43 +08:00
|
|
|
Key::Key(const KeyType type, tools::Tool* tool)
|
2014-10-29 22:58:03 +08:00
|
|
|
: m_type(type)
|
|
|
|
, m_keycontext(KeyContext::Any)
|
|
|
|
, m_tool(tool)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-09-19 06:29:43 +08:00
|
|
|
Key::Key(const KeyAction action, const KeyContext keyContext)
|
2014-10-29 22:58:03 +08:00
|
|
|
: m_type(KeyType::Action)
|
2020-09-19 06:29:43 +08:00
|
|
|
, m_keycontext(keyContext)
|
2014-10-29 22:58:03 +08:00
|
|
|
, m_action(action)
|
|
|
|
{
|
2020-09-19 06:29:43 +08:00
|
|
|
if (m_keycontext != KeyContext::Any)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Automatic key context
|
2014-11-16 05:31:12 +08:00
|
|
|
switch (action) {
|
|
|
|
case KeyAction::None: m_keycontext = KeyContext::Any; break;
|
|
|
|
case KeyAction::CopySelection:
|
|
|
|
case KeyAction::SnapToGrid:
|
2016-04-05 05:46:48 +08:00
|
|
|
case KeyAction::LockAxis:
|
2015-09-12 07:04:02 +08:00
|
|
|
case KeyAction::FineControl: m_keycontext = KeyContext::TranslatingSelection; break;
|
|
|
|
case KeyAction::AngleSnap: m_keycontext = KeyContext::RotatingSelection; break;
|
2014-11-16 05:31:12 +08:00
|
|
|
case KeyAction::MaintainAspectRatio:
|
2015-09-12 07:04:02 +08:00
|
|
|
case KeyAction::ScaleFromCenter: m_keycontext = KeyContext::ScalingSelection; break;
|
2014-11-16 05:31:12 +08:00
|
|
|
case KeyAction::AddSelection:
|
|
|
|
case KeyAction::SubtractSelection:
|
2015-09-12 07:04:02 +08:00
|
|
|
case KeyAction::IntersectSelection: m_keycontext = KeyContext::SelectionTool; break;
|
2014-11-17 05:33:31 +08:00
|
|
|
case KeyAction::AutoSelectLayer: m_keycontext = KeyContext::MoveTool; break;
|
2015-09-16 23:19:10 +08:00
|
|
|
case KeyAction::StraightLineFromLastPoint:
|
|
|
|
case KeyAction::AngleSnapFromLastPoint: m_keycontext = KeyContext::FreehandTool; break;
|
2016-04-05 05:46:48 +08:00
|
|
|
case KeyAction::MoveOrigin:
|
|
|
|
case KeyAction::SquareAspect:
|
|
|
|
case KeyAction::DrawFromCenter:
|
|
|
|
case KeyAction::RotateShape: m_keycontext = KeyContext::ShapeTool; break;
|
2014-11-16 05:31:12 +08:00
|
|
|
case KeyAction::LeftMouseButton: m_keycontext = KeyContext::Any; break;
|
|
|
|
case KeyAction::RightMouseButton: m_keycontext = KeyContext::Any; break;
|
|
|
|
}
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
|
2020-09-19 06:29:43 +08:00
|
|
|
Key::Key(const WheelAction wheelAction)
|
2018-07-20 10:05:14 +08:00
|
|
|
: m_type(KeyType::WheelAction)
|
|
|
|
, m_keycontext(KeyContext::MouseWheel)
|
|
|
|
, m_action(KeyAction::None)
|
|
|
|
, m_wheelAction(wheelAction)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-03-09 07:40:11 +08:00
|
|
|
// static
|
|
|
|
KeyPtr Key::MakeDragAction(WheelAction dragAction)
|
|
|
|
{
|
|
|
|
KeyPtr k(new Key(dragAction));
|
|
|
|
k->m_type = KeyType::DragAction;
|
|
|
|
k->m_keycontext = KeyContext::Any;
|
2022-01-03 16:31:46 +08:00
|
|
|
k->m_dragVector = wheel_actions()[(int)dragAction].vector;
|
2022-03-09 07:40:11 +08:00
|
|
|
return k;
|
|
|
|
}
|
|
|
|
|
2022-04-14 09:46:48 +08:00
|
|
|
const ui::Accelerators& Key::accels() const
|
2014-10-29 22:58:03 +08:00
|
|
|
{
|
2022-04-14 09:46:48 +08:00
|
|
|
if (!m_accels) {
|
|
|
|
m_accels = std::make_unique<ui::Accelerators>();
|
|
|
|
|
|
|
|
// Add default keys
|
|
|
|
for (const auto& kv : m_adds) {
|
|
|
|
if (kv.first == KeySource::Original)
|
|
|
|
m_accels->add(kv.second);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete/add extension-defined keys
|
|
|
|
for (const auto& kv : m_dels) {
|
|
|
|
if (kv.first == KeySource::ExtensionDefined)
|
|
|
|
m_accels->remove(kv.second);
|
|
|
|
else {
|
|
|
|
ASSERT(kv.first != KeySource::Original);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const auto& kv : m_adds) {
|
|
|
|
if (kv.first == KeySource::ExtensionDefined)
|
|
|
|
m_accels->add(kv.second);
|
|
|
|
}
|
2014-10-29 22:58:03 +08:00
|
|
|
|
2022-04-14 09:46:48 +08:00
|
|
|
// Delete/add user-defined keys
|
|
|
|
for (const auto& kv : m_dels) {
|
|
|
|
if (kv.first == KeySource::UserDefined)
|
|
|
|
m_accels->remove(kv.second);
|
|
|
|
}
|
|
|
|
for (const auto& kv : m_adds) {
|
|
|
|
if (kv.first == KeySource::UserDefined)
|
|
|
|
m_accels->add(kv.second);
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
2014-11-16 05:31:12 +08:00
|
|
|
}
|
2022-04-14 09:46:48 +08:00
|
|
|
return *m_accels;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Key::add(const ui::Accelerator& accel, const KeySource source, KeyboardShortcuts& globalKeys)
|
|
|
|
{
|
|
|
|
m_adds.emplace_back(source, accel);
|
|
|
|
m_accels.reset();
|
2014-10-29 22:58:03 +08:00
|
|
|
|
2014-11-16 05:31:12 +08:00
|
|
|
// Remove the accelerator from other commands
|
2022-04-14 09:46:48 +08:00
|
|
|
if (source == KeySource::ExtensionDefined || source == KeySource::UserDefined) {
|
|
|
|
erase_accel(m_dels, source, accel);
|
2014-10-29 22:58:03 +08:00
|
|
|
|
2022-04-14 09:46:48 +08:00
|
|
|
globalKeys.disableAccel(accel, source, m_keycontext, this);
|
|
|
|
}
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
|
2018-07-25 00:45:12 +08:00
|
|
|
const ui::Accelerator* Key::isPressed(const Message* msg,
|
2023-04-25 21:38:49 +08:00
|
|
|
const KeyboardShortcuts& globalKeys,
|
|
|
|
const KeyContext keyContext) const
|
2014-10-29 22:58:03 +08:00
|
|
|
{
|
2018-07-20 10:05:14 +08:00
|
|
|
if (auto keyMsg = dynamic_cast<const KeyMessage*>(msg)) {
|
|
|
|
for (const Accelerator& accel : accels()) {
|
|
|
|
if (accel.isPressed(keyMsg->modifiers(), keyMsg->scancode(), keyMsg->unicodeChar()) &&
|
|
|
|
(m_keycontext == KeyContext::Any || m_keycontext == keyContext)) {
|
2018-07-25 00:45:12 +08:00
|
|
|
return &accel;
|
2018-07-20 10:05:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (auto mouseMsg = dynamic_cast<const MouseMessage*>(msg)) {
|
|
|
|
for (const Accelerator& accel : accels()) {
|
|
|
|
if ((accel.modifiers() == mouseMsg->modifiers()) &&
|
|
|
|
(m_keycontext == KeyContext::Any ||
|
|
|
|
// TODO we could have multiple mouse wheel key-context,
|
|
|
|
// like "sprite editor" context, or "timeline" context,
|
|
|
|
// etc.
|
|
|
|
m_keycontext == KeyContext::MouseWheel)) {
|
2018-07-25 00:45:12 +08:00
|
|
|
return &accel;
|
2018-07-20 10:05:14 +08:00
|
|
|
}
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
}
|
2018-07-25 00:45:12 +08:00
|
|
|
return nullptr;
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
|
2023-04-25 21:38:49 +08:00
|
|
|
const ui::Accelerator* Key::isPressed(const Message* msg, const KeyboardShortcuts& globalKeys) const
|
|
|
|
{
|
|
|
|
return isPressed(msg, globalKeys, globalKeys.getCurrentKeyContext());
|
|
|
|
}
|
|
|
|
|
2015-05-21 02:02:31 +08:00
|
|
|
bool Key::isPressed() const
|
2014-10-29 22:58:03 +08:00
|
|
|
{
|
|
|
|
for (const Accelerator& accel : this->accels()) {
|
2015-05-21 02:02:31 +08:00
|
|
|
if (accel.isPressed())
|
2014-10-29 22:58:03 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-08-26 00:29:19 +08:00
|
|
|
bool Key::isLooselyPressed() const
|
|
|
|
{
|
|
|
|
for (const Accelerator& accel : this->accels()) {
|
|
|
|
if (accel.isLooselyPressed())
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-12-24 04:01:43 +08:00
|
|
|
bool Key::isCommandListed() const
|
2024-05-21 23:36:33 +08:00
|
|
|
{
|
2024-12-24 04:01:43 +08:00
|
|
|
return type() == KeyType::Command && command()->isListed(params());
|
2024-05-21 23:36:33 +08:00
|
|
|
}
|
|
|
|
|
2014-10-29 22:58:03 +08:00
|
|
|
bool Key::hasAccel(const ui::Accelerator& accel) const
|
|
|
|
{
|
2014-11-16 05:31:12 +08:00
|
|
|
return accels().has(accel);
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
|
2022-04-14 09:46:48 +08:00
|
|
|
bool Key::hasUserDefinedAccels() const
|
2014-10-29 22:58:03 +08:00
|
|
|
{
|
2022-04-14 09:46:48 +08:00
|
|
|
for (const auto& kv : m_adds) {
|
|
|
|
if (kv.first == KeySource::UserDefined)
|
|
|
|
return true;
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
2022-04-14 09:46:48 +08:00
|
|
|
return false;
|
|
|
|
}
|
2014-10-29 22:58:03 +08:00
|
|
|
|
2022-04-14 09:46:48 +08:00
|
|
|
void Key::disableAccel(const ui::Accelerator& accel, const KeySource source)
|
|
|
|
{
|
|
|
|
// It doesn't make sense that the default keyboard shortcuts file
|
|
|
|
// (gui.xml) removes some accelerator.
|
|
|
|
ASSERT(source != KeySource::Original);
|
2014-10-29 22:58:03 +08:00
|
|
|
|
2022-04-14 09:46:48 +08:00
|
|
|
erase_accel(m_adds, source, accel);
|
|
|
|
erase_accel(m_dels, source, accel);
|
2014-11-16 05:31:12 +08:00
|
|
|
|
2022-04-14 09:46:48 +08:00
|
|
|
m_dels.emplace_back(source, accel);
|
|
|
|
m_accels.reset();
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Key::reset()
|
|
|
|
{
|
2022-04-14 09:46:48 +08:00
|
|
|
erase_accels(m_adds, KeySource::UserDefined);
|
|
|
|
erase_accels(m_dels, KeySource::UserDefined);
|
|
|
|
m_accels.reset();
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
|
2018-07-20 10:05:14 +08:00
|
|
|
void Key::copyOriginalToUser()
|
|
|
|
{
|
2022-04-14 09:46:48 +08:00
|
|
|
// Erase all user-defined keys
|
|
|
|
erase_accels(m_adds, KeySource::UserDefined);
|
|
|
|
erase_accels(m_dels, KeySource::UserDefined);
|
|
|
|
|
|
|
|
// Then copy all original & extension-defined keys as user-defined
|
|
|
|
auto copy = m_adds;
|
|
|
|
for (const auto& kv : copy)
|
|
|
|
m_adds.emplace_back(KeySource::UserDefined, kv.second);
|
|
|
|
m_accels.reset();
|
2018-07-20 10:05:14 +08:00
|
|
|
}
|
|
|
|
|
2014-10-29 22:58:03 +08:00
|
|
|
std::string Key::triggerString() const
|
|
|
|
{
|
|
|
|
switch (m_type) {
|
|
|
|
case KeyType::Command: m_command->loadParams(m_params); return m_command->friendlyName();
|
|
|
|
case KeyType::Tool:
|
|
|
|
case KeyType::Quicktool: {
|
|
|
|
std::string text = m_tool->getText();
|
|
|
|
if (m_type == KeyType::Quicktool)
|
|
|
|
text += " (quick)";
|
|
|
|
return text;
|
|
|
|
}
|
2020-09-19 06:29:43 +08:00
|
|
|
case KeyType::Action: return get_user_friendly_string_for_keyaction(m_action, m_keycontext);
|
2018-07-20 10:05:14 +08:00
|
|
|
case KeyType::WheelAction:
|
|
|
|
case KeyType::DragAction: return get_user_friendly_string_for_wheelaction(m_wheelAction);
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
return "Unknown";
|
|
|
|
}
|
|
|
|
|
2014-11-16 05:31:12 +08:00
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
// KeyboardShortcuts
|
|
|
|
|
2022-06-10 04:13:51 +08:00
|
|
|
static std::unique_ptr<KeyboardShortcuts> g_singleton;
|
|
|
|
|
|
|
|
// static
|
2014-10-29 22:58:03 +08:00
|
|
|
KeyboardShortcuts* KeyboardShortcuts::instance()
|
|
|
|
{
|
2022-06-10 04:13:51 +08:00
|
|
|
if (!g_singleton)
|
|
|
|
g_singleton = std::make_unique<KeyboardShortcuts>();
|
|
|
|
return g_singleton.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
void KeyboardShortcuts::destroyInstance()
|
|
|
|
{
|
|
|
|
g_singleton.reset();
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
KeyboardShortcuts::KeyboardShortcuts()
|
|
|
|
{
|
2022-01-03 16:31:46 +08:00
|
|
|
ASSERT(Strings::instance());
|
|
|
|
Strings::instance()->LanguageChange.connect([] {
|
|
|
|
// Clear collections so they are re-constructed with the new language
|
|
|
|
g_actions.clear();
|
|
|
|
g_wheel_actions.clear();
|
|
|
|
});
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
KeyboardShortcuts::~KeyboardShortcuts()
|
|
|
|
{
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
2018-07-24 02:15:04 +08:00
|
|
|
void KeyboardShortcuts::setKeys(const KeyboardShortcuts& keys, const bool cloneKeys)
|
|
|
|
{
|
|
|
|
if (cloneKeys) {
|
|
|
|
for (const KeyPtr& key : keys)
|
|
|
|
m_keys.push_back(std::make_shared<Key>(*key));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_keys = keys.m_keys;
|
|
|
|
}
|
|
|
|
UserChange();
|
|
|
|
}
|
|
|
|
|
2014-10-29 22:58:03 +08:00
|
|
|
void KeyboardShortcuts::clear()
|
|
|
|
{
|
|
|
|
m_keys.clear();
|
|
|
|
}
|
|
|
|
|
2024-04-23 05:28:03 +08:00
|
|
|
void KeyboardShortcuts::importFile(XMLElement* rootElement, KeySource source)
|
2014-10-29 22:58:03 +08:00
|
|
|
{
|
|
|
|
// <keyboard><commands><key>
|
2024-04-23 05:28:03 +08:00
|
|
|
XMLHandle handle(rootElement);
|
|
|
|
XMLElement* xmlKey = handle.FirstChildElement("commands").FirstChildElement("key").ToElement();
|
2014-10-29 22:58:03 +08:00
|
|
|
while (xmlKey) {
|
|
|
|
const char* command_name = xmlKey->Attribute("command");
|
|
|
|
const char* command_key = get_shortcut(xmlKey);
|
2021-09-07 00:20:20 +08:00
|
|
|
bool removed = bool_attr(xmlKey, "removed", false);
|
2014-10-29 22:58:03 +08:00
|
|
|
|
2015-11-19 03:15:25 +08:00
|
|
|
if (command_name) {
|
2017-11-30 22:57:18 +08:00
|
|
|
Command* command = Commands::instance()->byId(command_name);
|
2014-10-29 22:58:03 +08:00
|
|
|
if (command) {
|
|
|
|
// Read context
|
|
|
|
KeyContext keycontext = KeyContext::Any;
|
|
|
|
const char* keycontextstr = xmlKey->Attribute("context");
|
2020-09-03 05:09:19 +08:00
|
|
|
if (keycontextstr)
|
|
|
|
keycontext = base::convert_to<KeyContext>(std::string(keycontextstr));
|
2014-10-29 22:58:03 +08:00
|
|
|
|
|
|
|
// Read params
|
|
|
|
Params params;
|
|
|
|
|
2024-04-23 05:28:03 +08:00
|
|
|
XMLElement* xmlParam = xmlKey->FirstChildElement("param");
|
2014-10-29 22:58:03 +08:00
|
|
|
while (xmlParam) {
|
|
|
|
const char* param_name = xmlParam->Attribute("name");
|
|
|
|
const char* param_value = xmlParam->Attribute("value");
|
|
|
|
|
|
|
|
if (param_name && param_value)
|
|
|
|
params.set(param_name, param_value);
|
|
|
|
|
|
|
|
xmlParam = xmlParam->NextSiblingElement();
|
|
|
|
}
|
|
|
|
|
|
|
|
// add the keyboard shortcut to the command
|
2018-07-18 10:53:08 +08:00
|
|
|
KeyPtr key = this->command(command_name, params, keycontext);
|
2015-11-19 03:15:25 +08:00
|
|
|
if (key && command_key) {
|
2014-11-16 05:31:12 +08:00
|
|
|
Accelerator accel(command_key);
|
|
|
|
|
|
|
|
if (!removed) {
|
2018-07-24 02:15:04 +08:00
|
|
|
key->add(accel, source, *this);
|
2014-11-16 05:31:12 +08:00
|
|
|
|
2015-11-30 20:34:14 +08:00
|
|
|
// Add the shortcut to the menuitems with this command
|
|
|
|
// (this is only visual, the
|
|
|
|
// "CustomizedGuiManager::onProcessMessage" is the only
|
|
|
|
// one that process keyboard shortcuts)
|
2014-11-16 05:31:12 +08:00
|
|
|
if (key->accels().size() == 1) {
|
|
|
|
AppMenus::instance()->applyShortcutToMenuitemsWithCommand(command, params, key);
|
|
|
|
}
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
2014-11-16 05:31:12 +08:00
|
|
|
else
|
2022-04-14 09:46:48 +08:00
|
|
|
key->disableAccel(accel, source);
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
xmlKey = xmlKey->NextSiblingElement();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load keyboard shortcuts for tools
|
2018-07-20 10:05:14 +08:00
|
|
|
// <keyboard><tools><key>
|
2024-04-23 05:28:03 +08:00
|
|
|
xmlKey = handle.FirstChildElement("tools").FirstChildElement("key").ToElement();
|
2014-10-29 22:58:03 +08:00
|
|
|
while (xmlKey) {
|
|
|
|
const char* tool_id = xmlKey->Attribute("tool");
|
|
|
|
const char* tool_key = get_shortcut(xmlKey);
|
2021-09-07 00:20:20 +08:00
|
|
|
bool removed = bool_attr(xmlKey, "removed", false);
|
2014-10-29 22:58:03 +08:00
|
|
|
|
2015-12-27 01:52:29 +08:00
|
|
|
if (tool_id) {
|
2016-04-23 00:19:06 +08:00
|
|
|
tools::Tool* tool = App::instance()->toolBox()->getToolById(tool_id);
|
2014-10-29 22:58:03 +08:00
|
|
|
if (tool) {
|
2018-07-18 10:53:08 +08:00
|
|
|
KeyPtr key = this->tool(tool);
|
2015-12-27 01:52:29 +08:00
|
|
|
if (key && tool_key) {
|
2020-06-18 04:05:43 +08:00
|
|
|
LOG(VERBOSE, "KEYS: Shortcut for tool %s: %s\n", tool_id, tool_key);
|
2014-11-16 05:31:12 +08:00
|
|
|
Accelerator accel(tool_key);
|
|
|
|
|
|
|
|
if (!removed)
|
2018-07-24 02:15:04 +08:00
|
|
|
key->add(accel, source, *this);
|
2014-11-16 05:31:12 +08:00
|
|
|
else
|
2022-04-14 09:46:48 +08:00
|
|
|
key->disableAccel(accel, source);
|
2014-11-16 05:31:12 +08:00
|
|
|
}
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
xmlKey = xmlKey->NextSiblingElement();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load keyboard shortcuts for quicktools
|
2018-07-20 10:05:14 +08:00
|
|
|
// <keyboard><quicktools><key>
|
2024-04-23 05:28:03 +08:00
|
|
|
xmlKey = handle.FirstChildElement("quicktools").FirstChildElement("key").ToElement();
|
2014-10-29 22:58:03 +08:00
|
|
|
while (xmlKey) {
|
|
|
|
const char* tool_id = xmlKey->Attribute("tool");
|
|
|
|
const char* tool_key = get_shortcut(xmlKey);
|
2021-09-07 00:20:20 +08:00
|
|
|
bool removed = bool_attr(xmlKey, "removed", false);
|
2014-11-16 05:31:12 +08:00
|
|
|
|
2015-12-27 01:52:29 +08:00
|
|
|
if (tool_id) {
|
2016-04-23 00:19:06 +08:00
|
|
|
tools::Tool* tool = App::instance()->toolBox()->getToolById(tool_id);
|
2014-10-29 22:58:03 +08:00
|
|
|
if (tool) {
|
2018-07-18 10:53:08 +08:00
|
|
|
KeyPtr key = this->quicktool(tool);
|
2015-12-27 01:52:29 +08:00
|
|
|
if (key && tool_key) {
|
2020-06-18 04:05:43 +08:00
|
|
|
LOG(VERBOSE, "KEYS: Shortcut for quicktool %s: %s\n", tool_id, tool_key);
|
2014-11-16 05:31:12 +08:00
|
|
|
Accelerator accel(tool_key);
|
|
|
|
|
|
|
|
if (!removed)
|
2018-07-24 02:15:04 +08:00
|
|
|
key->add(accel, source, *this);
|
2014-11-16 05:31:12 +08:00
|
|
|
else
|
2022-04-14 09:46:48 +08:00
|
|
|
key->disableAccel(accel, source);
|
2014-11-16 05:31:12 +08:00
|
|
|
}
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
xmlKey = xmlKey->NextSiblingElement();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load special keyboard shortcuts for sprite editor customization
|
2018-07-20 10:05:14 +08:00
|
|
|
// <keyboard><actions><key>
|
2024-04-23 05:28:03 +08:00
|
|
|
xmlKey = handle.FirstChildElement("actions").FirstChildElement("key").ToElement();
|
2014-10-29 22:58:03 +08:00
|
|
|
while (xmlKey) {
|
2018-07-20 10:05:14 +08:00
|
|
|
const char* action_id = xmlKey->Attribute("action");
|
|
|
|
const char* action_key = get_shortcut(xmlKey);
|
2021-09-07 00:20:20 +08:00
|
|
|
bool removed = bool_attr(xmlKey, "removed", false);
|
2014-10-29 22:58:03 +08:00
|
|
|
|
2018-07-20 10:05:14 +08:00
|
|
|
if (action_id) {
|
|
|
|
KeyAction action = base::convert_to<KeyAction, std::string>(action_id);
|
2014-10-29 22:58:03 +08:00
|
|
|
if (action != KeyAction::None) {
|
2020-09-19 06:29:43 +08:00
|
|
|
// Read context
|
|
|
|
KeyContext keycontext = KeyContext::Any;
|
|
|
|
const char* keycontextstr = xmlKey->Attribute("context");
|
|
|
|
if (keycontextstr)
|
|
|
|
keycontext = base::convert_to<KeyContext>(std::string(keycontextstr));
|
|
|
|
|
|
|
|
KeyPtr key = this->action(action, keycontext);
|
2018-07-20 10:05:14 +08:00
|
|
|
if (key && action_key) {
|
2020-09-19 06:29:43 +08:00
|
|
|
LOG(VERBOSE,
|
|
|
|
"KEYS: Shortcut for action %s/%s: %s\n",
|
|
|
|
action_id,
|
|
|
|
(keycontextstr ? keycontextstr : "Any"),
|
|
|
|
action_key);
|
2018-07-20 10:05:14 +08:00
|
|
|
Accelerator accel(action_key);
|
|
|
|
|
|
|
|
if (!removed)
|
2018-07-24 02:15:04 +08:00
|
|
|
key->add(accel, source, *this);
|
2018-07-20 10:05:14 +08:00
|
|
|
else
|
2022-04-14 09:46:48 +08:00
|
|
|
key->disableAccel(accel, source);
|
2018-07-20 10:05:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
xmlKey = xmlKey->NextSiblingElement();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load special keyboard shortcuts for mouse wheel customization
|
|
|
|
// <keyboard><wheel><key>
|
2024-04-23 05:28:03 +08:00
|
|
|
xmlKey = handle.FirstChildElement("wheel").FirstChildElement("key").ToElement();
|
2018-07-20 10:05:14 +08:00
|
|
|
while (xmlKey) {
|
|
|
|
const char* action_id = xmlKey->Attribute("action");
|
|
|
|
const char* action_key = get_shortcut(xmlKey);
|
2021-09-07 00:20:20 +08:00
|
|
|
bool removed = bool_attr(xmlKey, "removed", false);
|
2018-07-20 10:05:14 +08:00
|
|
|
|
|
|
|
if (action_id) {
|
|
|
|
WheelAction action = base::convert_to<WheelAction, std::string>(action_id);
|
|
|
|
if (action != WheelAction::None) {
|
|
|
|
KeyPtr key = this->wheelAction(action);
|
|
|
|
if (key && action_key) {
|
2020-06-18 04:05:43 +08:00
|
|
|
LOG(VERBOSE, "KEYS: Shortcut for wheel action %s: %s\n", action_id, action_key);
|
2018-07-20 10:05:14 +08:00
|
|
|
Accelerator accel(action_key);
|
2014-11-16 05:31:12 +08:00
|
|
|
|
|
|
|
if (!removed)
|
2018-07-24 02:15:04 +08:00
|
|
|
key->add(accel, source, *this);
|
2014-11-16 05:31:12 +08:00
|
|
|
else
|
2022-04-14 09:46:48 +08:00
|
|
|
key->disableAccel(accel, source);
|
2014-11-16 05:31:12 +08:00
|
|
|
}
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
xmlKey = xmlKey->NextSiblingElement();
|
|
|
|
}
|
2022-03-09 07:40:11 +08:00
|
|
|
|
|
|
|
// Load special keyboard shortcuts to simulate mouse wheel actions
|
|
|
|
// <keyboard><drag><key>
|
2024-04-23 05:28:03 +08:00
|
|
|
xmlKey = handle.FirstChildElement("drag").FirstChildElement("key").ToElement();
|
2022-03-09 07:40:11 +08:00
|
|
|
while (xmlKey) {
|
|
|
|
const char* action_id = xmlKey->Attribute("action");
|
|
|
|
const char* action_key = get_shortcut(xmlKey);
|
|
|
|
bool removed = bool_attr(xmlKey, "removed", false);
|
|
|
|
|
|
|
|
if (action_id) {
|
|
|
|
WheelAction action = base::convert_to<WheelAction, std::string>(action_id);
|
|
|
|
if (action != WheelAction::None) {
|
|
|
|
KeyPtr key = this->dragAction(action);
|
|
|
|
if (key && action_key) {
|
|
|
|
if (auto vector_str = xmlKey->Attribute("vector")) {
|
|
|
|
double x, y = 0.0;
|
|
|
|
// Parse a string like "double,double"
|
|
|
|
x = std::strtod(vector_str, (char**)&vector_str);
|
|
|
|
if (vector_str && *vector_str == ',') {
|
|
|
|
++vector_str;
|
|
|
|
y = std::strtod(vector_str, nullptr);
|
|
|
|
}
|
|
|
|
key->setDragVector(DragVector(x, y));
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG(VERBOSE, "KEYS: Shortcut for drag action %s: %s\n", action_id, action_key);
|
|
|
|
Accelerator accel(action_key);
|
|
|
|
|
|
|
|
if (!removed)
|
|
|
|
key->add(accel, source, *this);
|
|
|
|
else
|
2022-04-14 10:01:12 +08:00
|
|
|
key->disableAccel(accel, source);
|
2022-03-09 07:40:11 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
xmlKey = xmlKey->NextSiblingElement();
|
|
|
|
}
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void KeyboardShortcuts::importFile(const std::string& filename, KeySource source)
|
|
|
|
{
|
2024-04-23 05:28:03 +08:00
|
|
|
XMLDocumentRef doc = app::open_xml(filename);
|
|
|
|
XMLHandle handle(doc.get());
|
|
|
|
XMLElement* xmlKey = handle.FirstChildElement("keyboard").ToElement();
|
2014-10-29 22:58:03 +08:00
|
|
|
|
|
|
|
importFile(xmlKey, source);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KeyboardShortcuts::exportFile(const std::string& filename)
|
|
|
|
{
|
2024-04-23 05:28:03 +08:00
|
|
|
auto doc = std::make_unique<XMLDocument>();
|
|
|
|
XMLElement* keyboard = doc->NewElement("keyboard");
|
|
|
|
XMLElement* commands = keyboard->InsertNewChildElement("commands");
|
|
|
|
XMLElement* tools = keyboard->InsertNewChildElement("tools");
|
|
|
|
XMLElement* quicktools = keyboard->InsertNewChildElement("quicktools");
|
|
|
|
XMLElement* actions = keyboard->InsertNewChildElement("actions");
|
|
|
|
XMLElement* wheel = keyboard->InsertNewChildElement("wheel");
|
|
|
|
XMLElement* drag = keyboard->InsertNewChildElement("drag");
|
2014-10-29 22:58:03 +08:00
|
|
|
|
2024-04-23 05:28:03 +08:00
|
|
|
keyboard->SetAttribute("version", XML_KEYBOARD_FILE_VERSION);
|
2014-10-29 22:58:03 +08:00
|
|
|
|
|
|
|
exportKeys(commands, KeyType::Command);
|
|
|
|
exportKeys(tools, KeyType::Tool);
|
|
|
|
exportKeys(quicktools, KeyType::Quicktool);
|
|
|
|
exportKeys(actions, KeyType::Action);
|
2018-07-20 10:05:14 +08:00
|
|
|
exportKeys(wheel, KeyType::WheelAction);
|
2022-03-09 07:40:11 +08:00
|
|
|
exportKeys(drag, KeyType::DragAction);
|
2015-02-12 23:16:25 +08:00
|
|
|
|
2024-04-23 05:28:03 +08:00
|
|
|
doc->InsertEndChild(doc->NewDeclaration("xml version=\"1.0\" encoding=\"utf-8\""));
|
2014-10-29 22:58:03 +08:00
|
|
|
doc->InsertEndChild(keyboard);
|
2024-04-23 05:28:03 +08:00
|
|
|
save_xml(doc.get(), filename);
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
|
2024-04-23 05:28:03 +08:00
|
|
|
void KeyboardShortcuts::exportKeys(XMLElement* parent, KeyType type)
|
2014-10-29 22:58:03 +08:00
|
|
|
{
|
2018-07-18 10:53:08 +08:00
|
|
|
for (KeyPtr& key : m_keys) {
|
2014-10-29 22:58:03 +08:00
|
|
|
// Save only user defined accelerators.
|
2014-11-16 05:31:12 +08:00
|
|
|
if (key->type() != type)
|
2014-10-29 22:58:03 +08:00
|
|
|
continue;
|
|
|
|
|
2022-04-14 09:46:48 +08:00
|
|
|
for (const auto& kv : key->delsKeys())
|
|
|
|
if (kv.first == KeySource::UserDefined)
|
|
|
|
exportAccel(parent, key.get(), kv.second, true);
|
2014-11-16 05:31:12 +08:00
|
|
|
|
2022-04-14 09:46:48 +08:00
|
|
|
for (const auto& kv : key->addsKeys())
|
|
|
|
if (kv.first == KeySource::UserDefined)
|
|
|
|
exportAccel(parent, key.get(), kv.second, false);
|
2014-11-16 05:31:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-23 05:28:03 +08:00
|
|
|
void KeyboardShortcuts::exportAccel(XMLElement* parent,
|
|
|
|
const Key* key,
|
|
|
|
const ui::Accelerator& accel,
|
|
|
|
bool removed)
|
2014-11-16 05:31:12 +08:00
|
|
|
{
|
2024-04-23 05:28:03 +08:00
|
|
|
XMLElement* elem = parent->InsertNewChildElement("key");
|
2014-11-16 05:31:12 +08:00
|
|
|
|
|
|
|
switch (key->type()) {
|
|
|
|
case KeyType::Command: {
|
2024-04-23 05:28:03 +08:00
|
|
|
elem->SetAttribute("command", key->command()->id().c_str());
|
2014-11-16 05:31:12 +08:00
|
|
|
|
2020-09-03 05:09:19 +08:00
|
|
|
if (key->keycontext() != KeyContext::Any) {
|
|
|
|
elem->SetAttribute("context", base::convert_to<std::string>(key->keycontext()).c_str());
|
|
|
|
}
|
2014-11-16 05:31:12 +08:00
|
|
|
|
2015-03-12 02:40:22 +08:00
|
|
|
for (const auto& param : key->params()) {
|
|
|
|
if (param.second.empty())
|
|
|
|
continue;
|
2014-11-16 05:31:12 +08:00
|
|
|
|
2024-04-23 05:28:03 +08:00
|
|
|
XMLElement* paramElem = elem->InsertNewChildElement("param");
|
|
|
|
paramElem->SetAttribute("name", param.first.c_str());
|
|
|
|
paramElem->SetAttribute("value", param.second.c_str());
|
2014-11-16 05:31:12 +08:00
|
|
|
}
|
|
|
|
break;
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
2014-11-16 05:31:12 +08:00
|
|
|
|
|
|
|
case KeyType::Tool:
|
2024-04-23 05:28:03 +08:00
|
|
|
case KeyType::Quicktool: elem->SetAttribute("tool", key->tool()->getId().c_str()); break;
|
2014-11-16 05:31:12 +08:00
|
|
|
|
|
|
|
case KeyType::Action:
|
2020-09-03 05:09:19 +08:00
|
|
|
elem->SetAttribute("action", base::convert_to<std::string>(key->action()).c_str());
|
2020-09-19 06:29:43 +08:00
|
|
|
if (key->keycontext() != KeyContext::Any)
|
|
|
|
elem->SetAttribute("context", base::convert_to<std::string>(key->keycontext()).c_str());
|
2014-11-16 05:31:12 +08:00
|
|
|
break;
|
2018-07-20 10:05:14 +08:00
|
|
|
|
|
|
|
case KeyType::WheelAction:
|
2024-04-23 05:28:03 +08:00
|
|
|
elem->SetAttribute("action", base::convert_to<std::string>(key->wheelAction()).c_str());
|
2022-03-09 07:40:11 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case KeyType::DragAction:
|
2024-04-23 05:28:03 +08:00
|
|
|
elem->SetAttribute("action", base::convert_to<std::string>(key->wheelAction()).c_str());
|
|
|
|
elem->SetAttribute("vector",
|
|
|
|
fmt::format("{},{}", key->dragVector().x, key->dragVector().y).c_str());
|
2018-07-20 10:05:14 +08:00
|
|
|
break;
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
2014-11-16 05:31:12 +08:00
|
|
|
|
2024-04-23 05:28:03 +08:00
|
|
|
elem->SetAttribute("shortcut", accel.toString().c_str());
|
2014-11-16 05:31:12 +08:00
|
|
|
|
|
|
|
if (removed)
|
2024-04-23 05:28:03 +08:00
|
|
|
elem->SetAttribute("removed", "true");
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void KeyboardShortcuts::reset()
|
|
|
|
{
|
2018-07-18 10:53:08 +08:00
|
|
|
for (KeyPtr& key : m_keys)
|
2014-10-29 22:58:03 +08:00
|
|
|
key->reset();
|
|
|
|
}
|
|
|
|
|
2023-04-25 21:27:47 +08:00
|
|
|
KeyPtr KeyboardShortcuts::command(const char* commandName,
|
|
|
|
const Params& params,
|
|
|
|
const KeyContext keyContext) const
|
2014-10-29 22:58:03 +08:00
|
|
|
{
|
2017-11-30 22:57:18 +08:00
|
|
|
Command* command = Commands::instance()->byId(commandName);
|
2014-10-29 22:58:03 +08:00
|
|
|
if (!command)
|
2018-07-18 10:53:08 +08:00
|
|
|
return nullptr;
|
2014-10-29 22:58:03 +08:00
|
|
|
|
2018-07-18 10:53:08 +08:00
|
|
|
for (KeyPtr& key : m_keys) {
|
2014-10-29 22:58:03 +08:00
|
|
|
if (key->type() == KeyType::Command && key->keycontext() == keyContext &&
|
|
|
|
key->command() == command && key->params() == params) {
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-18 10:53:08 +08:00
|
|
|
KeyPtr key = std::make_shared<Key>(command, params, keyContext);
|
2014-10-29 22:58:03 +08:00
|
|
|
m_keys.push_back(key);
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
2023-04-25 21:27:47 +08:00
|
|
|
KeyPtr KeyboardShortcuts::tool(tools::Tool* tool) const
|
2014-10-29 22:58:03 +08:00
|
|
|
{
|
2018-07-18 10:53:08 +08:00
|
|
|
for (KeyPtr& key : m_keys) {
|
2014-10-29 22:58:03 +08:00
|
|
|
if (key->type() == KeyType::Tool && key->tool() == tool) {
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-18 10:53:08 +08:00
|
|
|
KeyPtr key = std::make_shared<Key>(KeyType::Tool, tool);
|
2014-10-29 22:58:03 +08:00
|
|
|
m_keys.push_back(key);
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
2023-04-25 21:27:47 +08:00
|
|
|
KeyPtr KeyboardShortcuts::quicktool(tools::Tool* tool) const
|
2014-10-29 22:58:03 +08:00
|
|
|
{
|
2018-07-18 10:53:08 +08:00
|
|
|
for (KeyPtr& key : m_keys) {
|
2014-10-29 22:58:03 +08:00
|
|
|
if (key->type() == KeyType::Quicktool && key->tool() == tool) {
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-18 10:53:08 +08:00
|
|
|
KeyPtr key = std::make_shared<Key>(KeyType::Quicktool, tool);
|
2014-10-29 22:58:03 +08:00
|
|
|
m_keys.push_back(key);
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
2023-04-25 21:27:47 +08:00
|
|
|
KeyPtr KeyboardShortcuts::action(const KeyAction action, const KeyContext keyContext) const
|
2014-10-29 22:58:03 +08:00
|
|
|
{
|
2018-07-18 10:53:08 +08:00
|
|
|
for (KeyPtr& key : m_keys) {
|
2014-10-29 22:58:03 +08:00
|
|
|
if (key->type() == KeyType::Action && key->action() == action &&
|
2020-09-19 06:29:43 +08:00
|
|
|
key->keycontext() == keyContext) {
|
2014-10-29 22:58:03 +08:00
|
|
|
return key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-19 06:29:43 +08:00
|
|
|
KeyPtr key = std::make_shared<Key>(action, keyContext);
|
2014-10-29 22:58:03 +08:00
|
|
|
m_keys.push_back(key);
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
2023-04-25 21:27:47 +08:00
|
|
|
KeyPtr KeyboardShortcuts::wheelAction(const WheelAction wheelAction) const
|
2018-07-20 10:05:14 +08:00
|
|
|
{
|
|
|
|
for (KeyPtr& key : m_keys) {
|
|
|
|
if (key->type() == KeyType::WheelAction && key->wheelAction() == wheelAction) {
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
KeyPtr key = std::make_shared<Key>(wheelAction);
|
|
|
|
m_keys.push_back(key);
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
2023-04-25 21:27:47 +08:00
|
|
|
KeyPtr KeyboardShortcuts::dragAction(const WheelAction dragAction) const
|
2022-03-09 07:40:11 +08:00
|
|
|
{
|
|
|
|
for (KeyPtr& key : m_keys) {
|
|
|
|
if (key->type() == KeyType::DragAction && key->wheelAction() == dragAction) {
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
KeyPtr key = Key::MakeDragAction(dragAction);
|
|
|
|
m_keys.push_back(key);
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
2017-12-06 21:22:32 +08:00
|
|
|
void KeyboardShortcuts::disableAccel(const ui::Accelerator& accel,
|
2022-04-14 09:46:48 +08:00
|
|
|
const KeySource source,
|
2017-12-06 21:22:32 +08:00
|
|
|
const KeyContext keyContext,
|
|
|
|
const Key* newKey)
|
2014-10-29 22:58:03 +08:00
|
|
|
{
|
2018-07-18 10:53:08 +08:00
|
|
|
for (KeyPtr& key : m_keys) {
|
2022-04-14 09:46:48 +08:00
|
|
|
if (key.get() != newKey && key->keycontext() == keyContext && key->hasAccel(accel) &&
|
2017-12-06 21:22:32 +08:00
|
|
|
// Tools can contain the same keyboard shortcut
|
2022-03-09 07:40:11 +08:00
|
|
|
(key->type() != KeyType::Tool || newKey == nullptr || newKey->type() != KeyType::Tool) &&
|
|
|
|
// DragActions can share the same keyboard shortcut (e.g. to
|
|
|
|
// change different values using different DragVectors)
|
|
|
|
(key->type() != KeyType::DragAction || newKey == nullptr ||
|
|
|
|
newKey->type() != KeyType::DragAction)) {
|
2022-04-14 09:46:48 +08:00
|
|
|
key->disableAccel(accel, source);
|
2017-12-06 21:22:32 +08:00
|
|
|
}
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-25 21:27:47 +08:00
|
|
|
KeyContext KeyboardShortcuts::getCurrentKeyContext() const
|
2014-10-29 22:58:03 +08:00
|
|
|
{
|
2023-06-09 06:00:49 +08:00
|
|
|
auto ctx = UIContext::instance();
|
|
|
|
Doc* doc = ctx->activeDocument();
|
2014-10-29 22:58:03 +08:00
|
|
|
if (doc && doc->isMaskVisible() &&
|
2019-12-19 05:00:11 +08:00
|
|
|
// The active key context will be the selectedTool() (in the
|
|
|
|
// toolbox) instead of the activeTool() (which depends on the
|
|
|
|
// quick tool shortcuts).
|
|
|
|
//
|
|
|
|
// E.g. If we have the rectangular marquee tool selected
|
|
|
|
// (selectedTool()) are going to press keys like alt+left or
|
|
|
|
// alt+right to move the selection edge in the selection
|
|
|
|
// context, the alt key switches the activeTool() to the
|
|
|
|
// eyedropper, but we want to use alt+left and alt+right in the
|
|
|
|
// original context (the selection tool).
|
2023-04-12 06:01:33 +08:00
|
|
|
App::instance()->activeToolManager()->selectedTool()->getInk(0)->isSelection()) {
|
2015-09-12 07:04:02 +08:00
|
|
|
return KeyContext::SelectionTool;
|
2023-04-12 06:01:33 +08:00
|
|
|
}
|
|
|
|
|
2023-06-09 06:00:49 +08:00
|
|
|
const view::RealRange& range = ctx->range();
|
|
|
|
if (doc && !range.selectedFrames().empty() &&
|
|
|
|
(range.type() == view::Range::kFrames || range.type() == view::Range::kCels)) {
|
2023-04-12 06:01:33 +08:00
|
|
|
return KeyContext::FramesSelection;
|
|
|
|
}
|
|
|
|
|
|
|
|
return KeyContext::Normal;
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
|
2018-07-20 10:05:14 +08:00
|
|
|
bool KeyboardShortcuts::getCommandFromKeyMessage(const Message* msg,
|
|
|
|
Command** command,
|
|
|
|
Params* params)
|
2014-10-29 22:58:03 +08:00
|
|
|
{
|
2023-04-25 21:38:49 +08:00
|
|
|
const KeyContext contexts[] = { getCurrentKeyContext(), KeyContext::Normal };
|
|
|
|
int n = (contexts[0] != contexts[1] ? 2 : 1);
|
|
|
|
for (int i = 0; i < n; ++i) {
|
|
|
|
for (KeyPtr& key : m_keys) {
|
|
|
|
if (key->type() == KeyType::Command && key->isPressed(msg, *this, contexts[i])) {
|
|
|
|
if (command)
|
|
|
|
*command = key->command();
|
|
|
|
if (params)
|
|
|
|
*params = key->params();
|
|
|
|
return true;
|
|
|
|
}
|
2014-10-29 22:58:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
tools::Tool* KeyboardShortcuts::getCurrentQuicktool(tools::Tool* currentTool)
|
|
|
|
{
|
|
|
|
if (currentTool && currentTool->getInk(0)->isSelection()) {
|
2021-04-09 06:49:40 +08:00
|
|
|
KeyPtr key = action(KeyAction::CopySelection, KeyContext::TranslatingSelection);
|
2015-05-21 02:02:31 +08:00
|
|
|
if (key && key->isPressed())
|
2014-10-29 22:58:03 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-04-23 00:19:06 +08:00
|
|
|
tools::ToolBox* toolbox = App::instance()->toolBox();
|
2014-10-29 22:58:03 +08:00
|
|
|
|
|
|
|
// Iterate over all tools
|
|
|
|
for (tools::Tool* tool : *toolbox) {
|
2018-07-18 10:53:08 +08:00
|
|
|
KeyPtr key = quicktool(tool);
|
2014-10-29 22:58:03 +08:00
|
|
|
|
|
|
|
// Collect all tools with the pressed keyboard-shortcut
|
2015-05-21 02:02:31 +08:00
|
|
|
if (key && key->isPressed()) {
|
2014-10-29 22:58:03 +08:00
|
|
|
return tool;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-08-26 00:29:19 +08:00
|
|
|
KeyAction KeyboardShortcuts::getCurrentActionModifiers(KeyContext context)
|
|
|
|
{
|
|
|
|
KeyAction flags = KeyAction::None;
|
|
|
|
|
2018-07-20 10:05:14 +08:00
|
|
|
for (const KeyPtr& key : m_keys) {
|
2015-08-26 00:29:19 +08:00
|
|
|
if (key->type() == KeyType::Action && key->keycontext() == context && key->isLooselyPressed()) {
|
|
|
|
flags = static_cast<KeyAction>(int(flags) | int(key->action()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
2018-07-20 10:05:14 +08:00
|
|
|
WheelAction KeyboardShortcuts::getWheelActionFromMouseMessage(const KeyContext context,
|
|
|
|
const ui::Message* msg)
|
|
|
|
{
|
2018-07-25 00:45:12 +08:00
|
|
|
WheelAction wheelAction = WheelAction::None;
|
|
|
|
const ui::Accelerator* bestAccel = nullptr;
|
2018-07-20 10:05:14 +08:00
|
|
|
for (const KeyPtr& key : m_keys) {
|
|
|
|
if (key->type() == KeyType::WheelAction && key->keycontext() == context) {
|
2018-07-25 00:45:12 +08:00
|
|
|
const ui::Accelerator* accel = key->isPressed(msg, *this);
|
|
|
|
if ((accel) && (!bestAccel || bestAccel->modifiers() < accel->modifiers())) {
|
|
|
|
bestAccel = accel;
|
|
|
|
wheelAction = key->wheelAction();
|
|
|
|
}
|
|
|
|
}
|
2018-07-20 10:05:14 +08:00
|
|
|
}
|
2018-07-25 00:45:12 +08:00
|
|
|
return wheelAction;
|
2018-07-20 10:05:14 +08:00
|
|
|
}
|
|
|
|
|
2022-03-09 07:40:11 +08:00
|
|
|
Keys KeyboardShortcuts::getDragActionsFromKeyMessage(const KeyContext context,
|
|
|
|
const ui::Message* msg)
|
|
|
|
{
|
|
|
|
KeyPtr bestKey = nullptr;
|
|
|
|
Keys keys;
|
|
|
|
for (const KeyPtr& key : m_keys) {
|
|
|
|
if (key->type() == KeyType::DragAction) {
|
|
|
|
const ui::Accelerator* accel = key->isPressed(msg, *this);
|
|
|
|
if (accel) {
|
|
|
|
keys.push_back(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return keys;
|
|
|
|
}
|
|
|
|
|
2018-07-20 10:05:14 +08:00
|
|
|
bool KeyboardShortcuts::hasMouseWheelCustomization() const
|
|
|
|
{
|
|
|
|
for (const KeyPtr& key : m_keys) {
|
|
|
|
if (key->type() == KeyType::WheelAction && key->hasUserDefinedAccels())
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-24 02:15:04 +08:00
|
|
|
void KeyboardShortcuts::clearMouseWheelKeys()
|
2018-07-20 10:05:14 +08:00
|
|
|
{
|
2018-07-24 02:15:04 +08:00
|
|
|
for (auto it = m_keys.begin(); it != m_keys.end();) {
|
|
|
|
if ((*it)->type() == KeyType::WheelAction)
|
|
|
|
it = m_keys.erase(it);
|
|
|
|
else
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
2018-07-20 10:05:14 +08:00
|
|
|
|
2018-07-24 02:15:04 +08:00
|
|
|
void KeyboardShortcuts::addMissingMouseWheelKeys()
|
|
|
|
{
|
2022-03-09 07:40:11 +08:00
|
|
|
for (int action = int(WheelAction::First); action <= int(WheelAction::Last); ++action) {
|
|
|
|
// Wheel actions
|
|
|
|
auto it = std::find_if(m_keys.begin(), m_keys.end(), [action](const KeyPtr& key) -> bool {
|
|
|
|
return key->type() == KeyType::WheelAction && key->wheelAction() == (WheelAction)action;
|
|
|
|
});
|
|
|
|
if (it == m_keys.end()) {
|
|
|
|
KeyPtr key = std::make_shared<Key>((WheelAction)action);
|
|
|
|
m_keys.push_back(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Drag actions
|
|
|
|
it = std::find_if(m_keys.begin(), m_keys.end(), [action](const KeyPtr& key) -> bool {
|
|
|
|
return key->type() == KeyType::DragAction && key->wheelAction() == (WheelAction)action;
|
2018-07-24 02:15:04 +08:00
|
|
|
});
|
|
|
|
if (it == m_keys.end()) {
|
2022-03-09 07:40:11 +08:00
|
|
|
KeyPtr key = Key::MakeDragAction((WheelAction)action);
|
2018-07-24 02:15:04 +08:00
|
|
|
m_keys.push_back(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KeyboardShortcuts::setDefaultMouseWheelKeys(const bool zoomWithWheel)
|
|
|
|
{
|
|
|
|
clearMouseWheelKeys();
|
|
|
|
|
|
|
|
KeyPtr key;
|
2018-07-20 10:05:14 +08:00
|
|
|
key = std::make_shared<Key>(WheelAction::Zoom);
|
2018-07-24 02:15:04 +08:00
|
|
|
key->add(Accelerator(zoomWithWheel ? kKeyNoneModifier : kKeyCtrlModifier, kKeyNil, 0),
|
|
|
|
KeySource::Original,
|
|
|
|
*this);
|
|
|
|
m_keys.push_back(key);
|
2018-07-20 10:05:14 +08:00
|
|
|
|
|
|
|
if (!zoomWithWheel) {
|
|
|
|
key = std::make_shared<Key>(WheelAction::VScroll);
|
2018-07-24 02:15:04 +08:00
|
|
|
key->add(Accelerator(kKeyNoneModifier, kKeyNil, 0), KeySource::Original, *this);
|
|
|
|
m_keys.push_back(key);
|
2018-07-20 10:05:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
key = std::make_shared<Key>(WheelAction::HScroll);
|
2018-07-24 02:15:04 +08:00
|
|
|
key->add(Accelerator(kKeyShiftModifier, kKeyNil, 0), KeySource::Original, *this);
|
|
|
|
m_keys.push_back(key);
|
2018-07-20 10:05:14 +08:00
|
|
|
|
|
|
|
key = std::make_shared<Key>(WheelAction::FgColor);
|
2018-07-24 02:15:04 +08:00
|
|
|
key->add(Accelerator(kKeyAltModifier, kKeyNil, 0), KeySource::Original, *this);
|
|
|
|
m_keys.push_back(key);
|
2018-07-20 10:05:14 +08:00
|
|
|
|
|
|
|
key = std::make_shared<Key>(WheelAction::BgColor);
|
2018-07-24 02:15:04 +08:00
|
|
|
key->add(Accelerator((KeyModifiers)(kKeyAltModifier | kKeyShiftModifier), kKeyNil, 0),
|
|
|
|
KeySource::Original,
|
|
|
|
*this);
|
|
|
|
m_keys.push_back(key);
|
2018-07-20 10:05:14 +08:00
|
|
|
|
|
|
|
if (zoomWithWheel) {
|
2018-07-25 00:32:36 +08:00
|
|
|
key = std::make_shared<Key>(WheelAction::BrushSize);
|
2018-07-24 02:15:04 +08:00
|
|
|
key->add(Accelerator(kKeyCtrlModifier, kKeyNil, 0), KeySource::Original, *this);
|
|
|
|
m_keys.push_back(key);
|
2018-07-25 00:32:36 +08:00
|
|
|
|
|
|
|
key = std::make_shared<Key>(WheelAction::Frame);
|
|
|
|
key->add(Accelerator((KeyModifiers)(kKeyCtrlModifier | kKeyShiftModifier), kKeyNil, 0),
|
|
|
|
KeySource::Original,
|
|
|
|
*this);
|
|
|
|
m_keys.push_back(key);
|
2018-07-24 02:15:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KeyboardShortcuts::addMissingKeysForCommands()
|
|
|
|
{
|
|
|
|
std::set<std::string> commandsAlreadyAdded;
|
|
|
|
for (const KeyPtr& key : m_keys) {
|
|
|
|
if (key->type() != KeyType::Command)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (key->params().empty())
|
|
|
|
commandsAlreadyAdded.insert(key->command()->id());
|
2018-07-20 10:05:14 +08:00
|
|
|
}
|
|
|
|
|
2018-07-24 02:15:04 +08:00
|
|
|
std::vector<std::string> ids;
|
|
|
|
Commands* commands = Commands::instance();
|
|
|
|
commands->getAllIds(ids);
|
|
|
|
|
|
|
|
for (const std::string& id : ids) {
|
|
|
|
Command* command = commands->byId(id.c_str());
|
|
|
|
|
|
|
|
// Don't add commands that need params (they will be added to
|
|
|
|
// the list using the list of keyboard shortcuts from gui.xml).
|
|
|
|
if (command->needsParams())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
auto it = commandsAlreadyAdded.find(command->id());
|
|
|
|
if (it != commandsAlreadyAdded.end())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Create the new Key element in KeyboardShortcuts for this
|
|
|
|
// command without params.
|
|
|
|
this->command(command->id().c_str());
|
|
|
|
}
|
2018-07-20 10:05:14 +08:00
|
|
|
}
|
|
|
|
|
2018-07-18 10:53:08 +08:00
|
|
|
std::string key_tooltip(const char* str, const app::Key* key)
|
2017-06-23 20:02:59 +08:00
|
|
|
{
|
|
|
|
std::string res;
|
|
|
|
if (str)
|
|
|
|
res += str;
|
|
|
|
if (key && !key->accels().empty()) {
|
|
|
|
res += " (";
|
|
|
|
res += key->accels().front().toString();
|
|
|
|
res += ")";
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2017-10-24 00:03:18 +08:00
|
|
|
std::string convertKeyContextToUserFriendlyString(KeyContext keyContext)
|
|
|
|
{
|
|
|
|
switch (keyContext) {
|
|
|
|
case KeyContext::Any: return std::string();
|
2022-01-03 16:31:46 +08:00
|
|
|
case KeyContext::Normal: return I18N_KEY(key_context_normal);
|
|
|
|
case KeyContext::SelectionTool: return I18N_KEY(key_context_selection);
|
|
|
|
case KeyContext::TranslatingSelection: return I18N_KEY(key_context_translating_selection);
|
|
|
|
case KeyContext::ScalingSelection: return I18N_KEY(key_context_scaling_selection);
|
|
|
|
case KeyContext::RotatingSelection: return I18N_KEY(key_context_rotating_selection);
|
|
|
|
case KeyContext::MoveTool: return I18N_KEY(key_context_move_tool);
|
|
|
|
case KeyContext::FreehandTool: return I18N_KEY(key_context_freehand_tool);
|
|
|
|
case KeyContext::ShapeTool: return I18N_KEY(key_context_shape_tool);
|
2023-04-12 06:01:33 +08:00
|
|
|
case KeyContext::FramesSelection: return I18N_KEY(key_context_frames_selection);
|
2017-10-24 00:03:18 +08:00
|
|
|
}
|
|
|
|
return std::string();
|
|
|
|
}
|
|
|
|
|
2014-10-29 22:58:03 +08:00
|
|
|
} // namespace app
|