aseprite/src/ui/accelerator.cpp

498 lines
13 KiB
C++

// Aseprite UI Library
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "ui/accelerator.h"
#include "base/debug.h"
#include "base/replace_string.h"
#include "base/split_string.h"
#include "base/string.h"
#include "base/unique_ptr.h"
#include <cctype>
#include <cstdlib>
#include <string>
#include <vector>
#include <algorithm>
// #define REPORT_KEYS
#define PREPROCESS_KEYS
namespace ui {
#ifdef _WIN32
const char* kWinKeyName = "Win";
#else
const char* kWinKeyName = "Super";
#endif
static KeyModifiers get_pressed_modifiers_from_she()
{
KeyModifiers mods = kKeyNoneModifier;
if (she::is_key_pressed(kKeyLShift) ) mods = KeyModifiers(int(mods) | int(kKeyShiftModifier));
if (she::is_key_pressed(kKeyRShift) ) mods = KeyModifiers(int(mods) | int(kKeyShiftModifier));
if (she::is_key_pressed(kKeyLControl)) mods = KeyModifiers(int(mods) | int(kKeyCtrlModifier));
if (she::is_key_pressed(kKeyRControl)) mods = KeyModifiers(int(mods) | int(kKeyCtrlModifier));
if (she::is_key_pressed(kKeyAlt) ) mods = KeyModifiers(int(mods) | int(kKeyAltModifier));
if (she::is_key_pressed(kKeyCommand) ) mods = KeyModifiers(int(mods) | int(kKeyCmdModifier));
if (she::is_key_pressed(kKeyLWin) ) mods = KeyModifiers(int(mods) | int(kKeyWinModifier));
if (she::is_key_pressed(kKeyRWin) ) mods = KeyModifiers(int(mods) | int(kKeyWinModifier));
return mods;
}
Accelerator::Accelerator()
: m_modifiers(kKeyNoneModifier)
, m_scancode(kKeyNil)
, m_unicodeChar(0)
{
}
Accelerator::Accelerator(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar)
: m_modifiers(modifiers)
, m_scancode(scancode)
, m_unicodeChar(unicodeChar)
{
}
Accelerator::Accelerator(const std::string& str)
: m_modifiers(kKeyNoneModifier)
, m_scancode(kKeyNil)
, m_unicodeChar(0)
{
// Special case: plus sign
if (str == "+") {
m_unicodeChar = '+';
return;
}
std::size_t i, j;
for (i=0; i<str.size(); i=j+1) {
// i+1 because the first character can be '+' sign
for (j=i+1; j<str.size() && str[j] != '+'; ++j)
;
std::string tok = base::string_to_lower(str.substr(i, j - i));
if (m_scancode == kKeySpace) {
m_modifiers = (KeyModifiers)((int)m_modifiers | (int)kKeySpaceModifier);
m_scancode = kKeyNil;
}
// Modifiers
if (tok == "shift") {
m_modifiers = (KeyModifiers)((int)m_modifiers | (int)kKeyShiftModifier);
}
else if (tok == "alt") {
m_modifiers = (KeyModifiers)((int)m_modifiers | (int)kKeyAltModifier);
}
else if (tok == "ctrl") {
m_modifiers = (KeyModifiers)((int)m_modifiers | (int)kKeyCtrlModifier);
}
else if (tok == "cmd") {
m_modifiers = (KeyModifiers)((int)m_modifiers | (int)kKeyCmdModifier);
}
else if (tok == base::string_to_lower(kWinKeyName)) {
m_modifiers = (KeyModifiers)((int)m_modifiers | (int)kKeyWinModifier);
}
// Scancode
// Word with one character
else if (base::utf8_length(tok) == 1) {
std::wstring wstr = base::from_utf8(tok);
if (wstr.size() != 1) {
ASSERT(false && "Something wrong converting utf-8 to wchar string");
continue;
}
wchar_t wchr = wstr[0];
wchr = tolower(wchr);
if ((wchr >= 'a') && (wchr <= 'z')) {
m_unicodeChar = wchr;
m_scancode = (KeyScancode)((int)kKeyA + wchr - 'a');
}
else {
m_unicodeChar = wchr;
if ((wchr >= '0') && (wchr <= '9'))
m_scancode = (KeyScancode)((int)kKey0 + wchr - '0');
else {
switch (wchr) {
case '~': m_scancode = kKeyTilde; break;
case '-': m_scancode = kKeyMinus; break;
case '=': m_scancode = kKeyEquals; break;
case '[': m_scancode = kKeyOpenbrace; break;
case ']': m_scancode = kKeyClosebrace; break;
case ';': m_scancode = kKeyColon; break;
case '\'': m_scancode = kKeyQuote; break;
case '\\': m_scancode = kKeyBackslash; break;
case ',': m_scancode = kKeyComma; break;
case '.': m_scancode = kKeyStop; break;
case '/': m_scancode = kKeySlash; break;
case '*': m_scancode = kKeyAsterisk; break;
}
}
}
}
// Other ones
else {
// F1, F2, ..., F11, F12
if (tok[0] == 'f' && (tok.size() <= 3)) {
int num = std::strtol(tok.c_str()+1, NULL, 10);
if ((num >= 1) && (num <= 12))
m_scancode = (KeyScancode)((int)kKeyF1 + num - 1);
}
else if ((tok == "escape") || (tok == "esc"))
m_scancode = kKeyEsc;
else if (tok == "backspace")
m_scancode = kKeyBackspace;
else if (tok == "tab")
m_scancode = kKeyTab;
else if (tok == "enter")
m_scancode = kKeyEnter;
else if (tok == "space")
m_scancode = kKeySpace;
else if ((tok == "insert") || (tok == "ins"))
m_scancode = kKeyInsert;
else if ((tok == "delete") || (tok == "del"))
m_scancode = kKeyDel;
else if (tok == "home")
m_scancode = kKeyHome;
else if (tok == "end")
m_scancode = kKeyEnd;
else if ((tok == "page up") || (tok == "pgup"))
m_scancode = kKeyPageUp;
else if ((tok == "page down") || (tok == "pgdn"))
m_scancode = kKeyPageDown;
else if (tok == "left")
m_scancode = kKeyLeft;
else if (tok == "right")
m_scancode = kKeyRight;
else if (tok == "up")
m_scancode = kKeyUp;
else if (tok == "down")
m_scancode = kKeyDown;
else if (tok == "0 pad")
m_scancode = kKey0Pad;
else if (tok == "1 pad")
m_scancode = kKey1Pad;
else if (tok == "2 pad")
m_scancode = kKey2Pad;
else if (tok == "3 pad")
m_scancode = kKey3Pad;
else if (tok == "4 pad")
m_scancode = kKey4Pad;
else if (tok == "5 pad")
m_scancode = kKey5Pad;
else if (tok == "6 pad")
m_scancode = kKey6Pad;
else if (tok == "7 pad")
m_scancode = kKey7Pad;
else if (tok == "8 pad")
m_scancode = kKey8Pad;
else if (tok == "9 pad")
m_scancode = kKey9Pad;
else if (tok == "/ pad" || tok == "slash pad")
m_scancode = kKeySlashPad;
else if (tok == "* pad" || tok == "asterisk pad" || tok == "asterisk")
m_scancode = kKeyAsterisk;
else if (tok == "- pad" || tok == "minus pad")
m_scancode = kKeyMinusPad;
else if (tok == "+ pad" || tok == "plus pad")
m_scancode = kKeyPlusPad;
else if (tok == "del pad" || tok == "delete pad")
m_scancode = kKeyDelPad;
else if (tok == "enter pad")
m_scancode = kKeyEnterPad;
}
}
}
bool Accelerator::operator==(const Accelerator& other) const
{
if (m_modifiers != other.m_modifiers)
return false;
if (m_scancode == other.m_scancode) {
if (m_scancode != kKeyNil)
return true;
else if (m_unicodeChar != 0)
return (std::tolower(m_unicodeChar) == std::tolower(other.m_unicodeChar));
else // Only comparing modifiers, and they are equal
return true;
}
return false;
}
bool Accelerator::isEmpty() const
{
return
(m_modifiers == kKeyNoneModifier &&
m_scancode == kKeyNil &&
m_unicodeChar == 0);
}
std::string Accelerator::toString() const
{
// Same order that she::KeyScancode
static const char* table[] = {
NULL,
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"0 Pad",
"1 Pad",
"2 Pad",
"3 Pad",
"4 Pad",
"5 Pad",
"6 Pad",
"7 Pad",
"8 Pad",
"9 Pad",
"F1",
"F2",
"F3",
"F4",
"F5",
"F6",
"F7",
"F8",
"F9",
"F10",
"F11",
"F12",
"Esc",
"~",
"-",
"=",
"Backspace",
"Tab",
"[",
"]",
"Enter",
";",
"\'",
"\\",
"KEY_BACKSLASH2",
",",
".",
"/",
"Space",
"Ins",
"Del",
"Home",
"End",
"PgUp",
"PgDn",
"Left",
"Right",
"Up",
"Down",
"/ Pad",
"* Pad",
"- Pad",
"+ Pad",
"Del Pad",
"Enter Pad",
"PrtScr",
"Pause",
"KEY_ABNT_C1",
"Yen",
"Kana",
"KEY_CONVERT",
"KEY_NOCONVERT",
"KEY_AT",
"KEY_CIRCUMFLEX",
"KEY_COLON2",
"Kanji",
};
static std::size_t table_size = sizeof(table) / sizeof(table[0]);
std::string buf;
// Shifts
if (m_modifiers & kKeyCtrlModifier) buf += "Ctrl+";
if (m_modifiers & kKeyCmdModifier) buf += "Cmd+";
if (m_modifiers & kKeyAltModifier) buf += "Alt+";
if (m_modifiers & kKeyShiftModifier) buf += "Shift+";
if (m_modifiers & kKeySpaceModifier) buf += "Space+";
if (m_modifiers & kKeyWinModifier) {
buf += kWinKeyName;
buf += "+";
}
// Key
if (m_unicodeChar) {
std::wstring wideUnicodeChar;
wideUnicodeChar.push_back((wchar_t)toupper(m_unicodeChar));
buf += base::to_utf8(wideUnicodeChar);
}
else if (m_scancode && m_scancode > 0 && m_scancode < (int)table_size)
buf += table[m_scancode];
else if (!buf.empty() && buf[buf.size()-1] == '+')
buf.erase(buf.size()-1);
return buf;
}
bool Accelerator::isPressed(KeyModifiers modifiers, KeyScancode scancode, int unicodeChar) const
{
// Preprocess the character to be compared with the accelerator
#ifdef PREPROCESS_KEYS
// Directly scancode
if ((scancode >= kKeyF1 && scancode <= kKeyF12) ||
(scancode == kKeyEsc) ||
(scancode == kKeyBackspace) ||
(scancode == kKeyTab) ||
(scancode == kKeyEnter) ||
(scancode == kKeyBackslash) ||
(scancode == kKeyBackslash2) ||
(scancode >= kKeySpace && scancode <= kKeyDown) ||
(scancode >= kKeyEnterPad && scancode <= kKeyNoconvert) ||
(scancode == kKeyKanji)) {
unicodeChar = 0;
}
// For Ctrl+number
/* scancode unicodeChar
Ctrl+0 27 0
Ctrl+1 28 2
Ctrl+2 29 0
Ctrl+3 30 27
Ctrl+4 31 28
Ctrl+5 32 29
Ctrl+6 33 30
Ctrl+7 34 31
Ctrl+8 35 127
Ctrl+9 36 2
*/
else if ((scancode >= kKey0 && scancode <= kKey9) &&
(unicodeChar < 32 || unicodeChar == 127)) {
unicodeChar = '0' + scancode - kKey0;
scancode = kKeyNil;
}
// For Ctrl+letter
else if (unicodeChar >= 1 && unicodeChar <= 'z'-'a'+1) {
unicodeChar = 'a'+unicodeChar-1;
scancode = kKeyNil;
}
// For any other legal Unicode code
else if (unicodeChar >= ' ') {
unicodeChar = std::tolower(unicodeChar);
/* without shift (because characters like '*' can be trigger with
"Shift+8", so we don't want "Shift+*") */
if (!(unicodeChar >= 'a' && unicodeChar <= 'z'))
modifiers = (KeyModifiers)((int)modifiers & ((int)~kKeyShiftModifier));
scancode = kKeyNil;
}
#endif
#ifdef REPORT_KEYS
printf("%3d==%3d %3d==%3d %s==%s ",
m_scancode, scancode,
m_unicodeChar, unicodeChar,
toString().c_str(),
Accelerator(modifiers, scancode, unicodeChar).toString().c_str());
#endif
if ((m_modifiers == modifiers) &&
((m_scancode != kKeyNil && m_scancode == scancode) ||
(m_unicodeChar && m_unicodeChar == unicodeChar) ||
(m_scancode == kKeyNil && scancode == kKeyNil && !m_unicodeChar && !unicodeChar))) {
#ifdef REPORT_KEYS
printf("true\n");
fflush(stdout);
#endif
return true;
}
#ifdef REPORT_KEYS
printf("false\n");
fflush(stdout);
#endif
return false;
}
bool Accelerator::isPressed() const
{
KeyModifiers modifiers = get_pressed_modifiers_from_she();
return ((m_scancode == 0 || she::is_key_pressed(m_scancode)) &&
(m_modifiers == modifiers));
}
bool Accelerator::isLooselyPressed() const
{
KeyModifiers modifiers = get_pressed_modifiers_from_she();
return ((m_scancode == 0 || she::is_key_pressed(m_scancode)) &&
(int(m_modifiers & modifiers) == m_modifiers));
}
//////////////////////////////////////////////////////////////////////
// Accelerators
bool Accelerators::has(const Accelerator& accel) const
{
return (std::find(begin(), end(), accel) != end());
}
void Accelerators::add(const Accelerator& accel)
{
if (!has(accel))
m_list.push_back(accel);
}
void Accelerators::remove(const Accelerator& accel)
{
auto it = std::find(begin(), end(), accel);
if (it != end())
m_list.erase(it);
}
} // namespace ui