1// Aseprite
2// Copyright (C) 2020 Igara Studio S.A.
3// Copyright (C) 2001-2018 David Capello
4//
5// This program is distributed under the terms of
6// the End-User License Agreement for Aseprite.
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "app/ui/select_accelerator.h"
13
14#include "app/ui/key.h"
15#include "app/ui/keyboard_shortcuts.h"
16#include "obs/signal.h"
17#include "ui/entry.h"
18#include "ui/message.h"
19
20#include <cctype>
21
22namespace app {
23
24using namespace ui;
25
26class SelectAccelerator::KeyField : public ui::Entry {
27public:
28 KeyField(const Accelerator& accel) : ui::Entry(256, "") {
29 setTranslateDeadKeys(false);
30 setExpansive(true);
31 setFocusMagnet(true);
32 setAccel(accel);
33 }
34
35 void setAccel(const Accelerator& accel) {
36 m_accel = accel;
37 updateText();
38 }
39
40 obs::signal<void(const ui::Accelerator*)> AccelChange;
41
42protected:
43 bool onProcessMessage(Message* msg) override {
44 switch (msg->type()) {
45
46 case kKeyDownMessage:
47 if (hasFocus() && !isReadOnly()) {
48 KeyMessage* keymsg = static_cast<KeyMessage*>(msg);
49 if (!keymsg->scancode() && keymsg->unicodeChar() < 32)
50 break;
51
52 KeyModifiers modifiers = keymsg->modifiers();
53
54 if (keymsg->scancode() == kKeySpace)
55 modifiers = (KeyModifiers)(modifiers & ~kKeySpaceModifier);
56
57 m_accel = Accelerator(
58 modifiers,
59 keymsg->scancode(),
60 keymsg->unicodeChar() > 32 ?
61 std::tolower(keymsg->unicodeChar()): 0);
62
63 // Convert the accelerator to a string, and parse it
64 // again. Just to obtain the exact accelerator we'll read
65 // when we import the gui.xml file or an .aseprite-keys file.
66 m_accel = Accelerator(m_accel.toString());
67
68 updateText();
69
70 AccelChange(&m_accel);
71 return true;
72 }
73 break;
74 }
75 return Entry::onProcessMessage(msg);
76 }
77
78 void updateText() {
79 setText(
80 Accelerator(
81 kKeyNoneModifier,
82 m_accel.scancode(),
83 m_accel.unicodeChar()).toString().c_str());
84 }
85
86 Accelerator m_accel;
87};
88
89SelectAccelerator::SelectAccelerator(const ui::Accelerator& accel,
90 const KeyContext keyContext,
91 const KeyboardShortcuts& currentKeys)
92 : m_keyField(new KeyField(accel))
93 , m_keyContext(keyContext)
94 , m_currentKeys(currentKeys)
95 , m_accel(accel)
96 , m_ok(false)
97 , m_modified(false)
98{
99 updateModifiers();
100 updateAssignedTo();
101
102 keyPlaceholder()->addChild(m_keyField);
103
104 alt()->Click.connect([this]{ onModifierChange(kKeyAltModifier, alt()); });
105 cmd()->Click.connect([this]{ onModifierChange(kKeyCmdModifier, cmd()); });
106 ctrl()->Click.connect([this]{ onModifierChange(kKeyCtrlModifier, ctrl()); });
107 shift()->Click.connect([this]{ onModifierChange(kKeyShiftModifier, shift()); });
108 space()->Click.connect([this]{ onModifierChange(kKeySpaceModifier, space()); });
109 win()->Click.connect([this]{ onModifierChange(kKeyWinModifier, win()); });
110
111 m_keyField->AccelChange.connect(&SelectAccelerator::onAccelChange, this);
112 clearButton()->Click.connect([this]{ onClear(); });
113 okButton()->Click.connect([this]{ onOK(); });
114 cancelButton()->Click.connect([this]{ onCancel(); });
115
116 addChild(&m_tooltipManager);
117}
118
119void SelectAccelerator::onModifierChange(KeyModifiers modifier, CheckBox* checkbox)
120{
121 bool state = (checkbox->isSelected());
122 KeyModifiers modifiers = m_accel.modifiers();
123 KeyScancode scancode = m_accel.scancode();
124 int unicodeChar = m_accel.unicodeChar();
125
126 modifiers = (KeyModifiers)((modifiers & ~modifier) | (state ? modifier : 0));
127 if (modifiers == kKeySpaceModifier && scancode == kKeySpace)
128 modifiers = kKeyNoneModifier;
129
130 m_accel = Accelerator(modifiers, scancode, unicodeChar);
131
132 m_keyField->setAccel(m_accel);
133 m_keyField->requestFocus();
134 updateAssignedTo();
135}
136
137void SelectAccelerator::onAccelChange(const ui::Accelerator* accel)
138{
139 m_accel = *accel;
140 updateModifiers();
141 updateAssignedTo();
142}
143
144void SelectAccelerator::onClear()
145{
146 m_accel = Accelerator(kKeyNoneModifier, kKeyNil, 0);
147
148 m_keyField->setAccel(m_accel);
149 updateModifiers();
150 updateAssignedTo();
151
152 m_keyField->requestFocus();
153}
154
155void SelectAccelerator::onOK()
156{
157 m_ok = true;
158 m_modified = (m_origAccel != m_accel);
159 closeWindow(NULL);
160}
161
162void SelectAccelerator::onCancel()
163{
164 closeWindow(NULL);
165}
166
167void SelectAccelerator::updateModifiers()
168{
169 alt()->setSelected(m_accel.modifiers() & kKeyAltModifier ? true: false);
170 ctrl()->setSelected(m_accel.modifiers() & kKeyCtrlModifier ? true: false);
171 shift()->setSelected(m_accel.modifiers() & kKeyShiftModifier ? true: false);
172 space()->setSelected(m_accel.modifiers() & kKeySpaceModifier ? true: false);
173#if __APPLE__
174 win()->setVisible(false);
175 cmd()->setSelected(m_accel.modifiers() & kKeyCmdModifier ? true: false);
176#else
177 #if __linux__
178 win()->setText(kWinKeyName);
179 m_tooltipManager.addTooltipFor(win(), "Also known as Windows key, logo key,\ncommand key, or system key.", TOP);
180 #endif
181 win()->setSelected(m_accel.modifiers() & kKeyWinModifier ? true: false);
182 cmd()->setVisible(false);
183#endif
184}
185
186void SelectAccelerator::updateAssignedTo()
187{
188 std::string res = "None";
189
190 for (const KeyPtr& key : m_currentKeys) {
191 if (key->keycontext() == m_keyContext &&
192 key->hasAccel(m_accel)) {
193 res = key->triggerString();
194 break;
195 }
196 }
197
198 assignedTo()->setText(res);
199}
200
201} // namespace app
202