#include "ui/base/accelerators/accelerator.h"
#include <stdint.h>
#include <tuple>
#include "base/check_op.h"
#include "base/i18n/rtl.h"
#include "base/notreached.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/types/cxx23_to_underlying.h"
#include "build/build_config.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/events/event.h"
#include "ui/events/keycodes/keyboard_code_conversion.h"
#include "ui/strings/grit/ui_strings.h"
#if BUILDFLAG(IS_MAC)
#include "base/mac/mac_util.h"
#endif
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#endif
#if !BUILDFLAG(IS_WIN) && (defined(USE_AURA) || BUILDFLAG(IS_MAC))
#include "ui/events/keycodes/keyboard_code_conversion.h"
#endif
#if BUILDFLAG(IS_CHROMEOS)
#include "ui/base/accelerators/ash/quick_insert_event_property.h"
#include "ui/base/ui_base_features.h"
#endif
#if BUILDFLAG(USE_BLINK)
#include "ui/base/accelerators/media_keys_listener.h"
#endif
namespace ui {
Accelerator::Accelerator(const KeyEvent& key_event)
: key_code_(key_event.key_code()),
key_state_(key_event.type() == EventType::kKeyPressed
? KeyState::PRESSED
: KeyState::RELEASED),
modifiers_(key_event.flags() & kInterestingFlagsMask),
time_stamp_(key_event.time_stamp()),
interrupted_by_mouse_event_(false),
source_device_id_(key_event.source_device_id()) {
#if BUILDFLAG(IS_CHROMEOS)
if (features::IsImprovedKeyboardShortcutsEnabled()) {
code_ = key_event.code();
}
if (key_event.key_code() == VKEY_ASSISTANT &&
HasQuickInsertProperty(key_event)) {
key_code_ = VKEY_QUICK_INSERT;
}
#endif
}
KeyEvent Accelerator::ToKeyEvent() const {
return KeyEvent(key_state() == Accelerator::KeyState::PRESSED
? EventType::kKeyPressed
: EventType::kKeyReleased,
key_code(),
#if BUILDFLAG(IS_CHROMEOS)
code(),
#endif
modifiers(), time_stamp());
}
#if BUILDFLAG(USE_BLINK)
bool Accelerator::IsMediaKey() const {
if (modifiers_ != EF_NONE) {
return false;
}
return ui::MediaKeysListener::IsMediaKeycode(key_code_);
}
#endif
std::vector<std::u16string> Accelerator::GetShortcutVectorRepresentation()
const {
std::vector<std::u16string> shortcut_vector;
if (IsEmpty()) {
return shortcut_vector;
}
std::u16string key_code = GetKeyCodeStringForShortcut();
#if BUILDFLAG(IS_MAC)
shortcut_vector = GetShortFormModifiers();
shortcut_vector.push_back(key_code);
#else
std::vector<std::u16string> modifiers = GetLongFormModifiers();
if (base::i18n::IsRTL() && key_code.length() == 1 &&
!base::IsAsciiAlphaNumeric(key_code[0])) {
shortcut_vector.push_back(key_code);
shortcut_vector.insert(shortcut_vector.end(), modifiers.begin(),
modifiers.end());
} else {
shortcut_vector.insert(shortcut_vector.end(), modifiers.begin(),
modifiers.end());
shortcut_vector.push_back(key_code);
}
#endif
return shortcut_vector;
}
std::u16string Accelerator::GetShortcutText() const {
std::u16string shortcut;
std::vector<std::u16string> shortcut_vector =
GetShortcutVectorRepresentation();
#if BUILDFLAG(IS_MAC)
shortcut = base::JoinString(shortcut_vector, u"");
#else
shortcut = base::JoinString(shortcut_vector, u"+");
#endif
return shortcut;
}
std::u16string Accelerator::GetKeyCodeStringForShortcut() const {
std::u16string key_string;
#if BUILDFLAG(IS_MAC)
key_string = KeyCodeToMacSymbol();
#else
key_string = KeyCodeToName();
#endif
if (key_string.empty()) {
#if BUILDFLAG(IS_WIN)
wchar_t key;
if (base::IsAsciiDigit(base::to_underlying(key_code_))) {
key = static_cast<wchar_t>(key_code_);
} else {
key = LOWORD(::MapVirtualKeyW(key_code_, MAPVK_VK_TO_CHAR));
}
if (key != 0) {
key_string += key;
}
#elif defined(USE_AURA) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
const uint16_t c = DomCodeToUsLayoutCharacter(
UsLayoutKeyboardCodeToDomCode(key_code_), false);
if (c != 0) {
key_string +=
static_cast<std::u16string::value_type>(base::ToUpperASCII(c));
}
#endif
}
return key_string;
}
#if BUILDFLAG(IS_MAC)
std::u16string Accelerator::KeyCodeToMacSymbol() const {
switch (key_code_) {
case VKEY_CAPITAL:
return u"⇪";
case VKEY_RETURN:
return u"⌤";
case VKEY_BACK:
return u"⌫";
case VKEY_ESCAPE:
return u"⎋";
case VKEY_RIGHT:
return u"→";
case VKEY_LEFT:
return u"←";
case VKEY_UP:
return u"↑";
case VKEY_DOWN:
return u"↓";
case VKEY_PRIOR:
return u"⇞";
case VKEY_NEXT:
return u"⇟";
case VKEY_HOME:
return base::i18n::IsRTL() ? u"↗"
: u"↖";
case VKEY_END:
return base::i18n::IsRTL() ? u"↙"
: u"↘";
case VKEY_TAB:
return u"⇥";
default:
return KeyCodeToName();
}
}
#endif
std::u16string Accelerator::KeyCodeToName() const {
int string_id = 0;
switch (key_code_) {
case VKEY_TAB:
string_id = IDS_APP_TAB_KEY;
break;
case VKEY_RETURN:
string_id = IDS_APP_ENTER_KEY;
break;
case VKEY_SPACE:
string_id = IDS_APP_SPACE_KEY;
break;
case VKEY_PRIOR:
string_id = IDS_APP_PAGEUP_KEY;
break;
case VKEY_NEXT:
string_id = IDS_APP_PAGEDOWN_KEY;
break;
case VKEY_END:
string_id = IDS_APP_END_KEY;
break;
case VKEY_HOME:
string_id = IDS_APP_HOME_KEY;
break;
case VKEY_INSERT:
string_id = IDS_APP_INSERT_KEY;
break;
case VKEY_DELETE:
string_id = IDS_APP_DELETE_KEY;
break;
case VKEY_LEFT:
string_id = IDS_APP_LEFT_ARROW_KEY;
break;
case VKEY_RIGHT:
string_id = IDS_APP_RIGHT_ARROW_KEY;
break;
case VKEY_UP:
string_id = IDS_APP_UP_ARROW_KEY;
break;
case VKEY_DOWN:
string_id = IDS_APP_DOWN_ARROW_KEY;
break;
case VKEY_ESCAPE:
string_id = IDS_APP_ESC_KEY;
break;
case VKEY_BACK:
string_id = IDS_APP_BACKSPACE_KEY;
break;
case VKEY_F1:
string_id = IDS_APP_F1_KEY;
break;
case VKEY_F6:
string_id = IDS_APP_F6_KEY;
break;
case VKEY_F11:
string_id = IDS_APP_F11_KEY;
break;
#if !BUILDFLAG(IS_MAC)
case VKEY_OEM_COMMA:
string_id = IDS_APP_COMMA_KEY;
break;
case VKEY_OEM_PERIOD:
string_id = IDS_APP_PERIOD_KEY;
break;
#endif
case VKEY_MEDIA_NEXT_TRACK:
string_id = IDS_APP_MEDIA_NEXT_TRACK_KEY;
break;
case VKEY_MEDIA_PLAY_PAUSE:
string_id = IDS_APP_MEDIA_PLAY_PAUSE_KEY;
break;
case VKEY_MEDIA_PREV_TRACK:
string_id = IDS_APP_MEDIA_PREV_TRACK_KEY;
break;
case VKEY_MEDIA_STOP:
string_id = IDS_APP_MEDIA_STOP_KEY;
break;
default:
break;
}
return string_id ? l10n_util::GetStringUTF16(string_id) : std::u16string();
}
std::vector<std::u16string> Accelerator::GetLongFormModifiers() const {
std::vector<std::u16string> modifiers;
if (IsCmdDown()) {
#if BUILDFLAG(IS_MAC)
modifiers.push_back(l10n_util::GetStringUTF16(IDS_APP_COMMAND_KEY));
#elif BUILDFLAG(IS_CHROMEOS)
modifiers.push_back(l10n_util::GetStringUTF16(IDS_APP_SEARCH_KEY));
#elif BUILDFLAG(IS_WIN)
modifiers.push_back(l10n_util::GetStringUTF16(IDS_APP_WINDOWS_KEY));
#elif BUILDFLAG(IS_LINUX)
modifiers.push_back(l10n_util::GetStringUTF16(IDS_APP_SUPER_KEY));
#else
NOTREACHED();
#endif
}
if (IsAltDown()) {
modifiers.push_back(l10n_util::GetStringUTF16(IDS_APP_ALT_KEY));
}
if (IsCtrlDown()) {
modifiers.push_back(l10n_util::GetStringUTF16(IDS_APP_CTRL_KEY));
}
if (IsShiftDown()) {
modifiers.push_back(l10n_util::GetStringUTF16(IDS_APP_SHIFT_KEY));
}
return modifiers;
}
std::vector<std::u16string> Accelerator::GetShortFormModifiers() const {
std::vector<std::u16string> modifiers;
if (IsCtrlDown()) {
modifiers.push_back(u"⌃");
}
if (IsAltDown()) {
modifiers.push_back(u"⌥");
}
if (IsShiftDown()) {
modifiers.push_back(u"⇧");
}
if (IsCmdDown()) {
modifiers.push_back(u"⌘");
}
if (IsFunctionDown()) {
modifiers.push_back(u"(fn) ");
}
return modifiers;
}
}