1 /*
2  * SPDX-FileCopyrightText: 2015-2015 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 
8 #ifndef _FCITX_UTILS_KEY_H_
9 #define _FCITX_UTILS_KEY_H_
10 
11 /// \addtogroup FcitxUtils
12 /// \{
13 /// \file
14 /// \brief Class to represent a key.
15 
16 #include <algorithm>
17 #include <cstddef>
18 #include <cstdint>
19 #include <string>
20 #include <vector>
21 #include <fcitx-utils/flags.h>
22 #include <fcitx-utils/keysym.h>
23 #include "fcitxutils_export.h"
24 
25 namespace fcitx {
26 class Key;
27 typedef FcitxKeySym KeySym;
28 typedef Flags<KeyState> KeyStates;
29 typedef std::vector<Key> KeyList;
30 
31 /// Control the behavior of toString function.
32 enum class KeyStringFormat {
33     /// Can be used to parse from a string.
34     Portable,
35     /// Return the human readable string in localized format.
36     Localized,
37 };
38 
39 /// Describe a Key in fcitx.
40 class FCITXUTILS_EXPORT Key {
41 public:
42     explicit Key(KeySym sym = FcitxKey_None, KeyStates states = KeyStates(),
43                  int code = 0)
sym_(sym)44         : sym_(sym), states_(states), code_(code) {}
45 
46     /// Parse a key from string. If string is invalid, it will be set to
47     /// FcitxKey_None
48     explicit Key(const char *keyString);
49 
50     /// Parse a key from std::string.
51     /// \see fcitx::Key::Key(const char *)
Key(const std::string & keyString)52     explicit Key(const std::string &keyString) : Key(keyString.c_str()) {}
53 
FCITX_INLINE_DEFINE_DEFAULT_DTOR_COPY_AND_MOVE(Key)54     FCITX_INLINE_DEFINE_DEFAULT_DTOR_COPY_AND_MOVE(Key)
55 
56     /// Create a key code based key with empty key symbol.
57     static Key fromKeyCode(int code = 0, KeyStates states = KeyStates()) {
58         return Key(FcitxKey_None, states, code);
59     }
60 
61     /// Check if key is exactly same.
62     bool operator==(const Key &key) const {
63         return sym_ == key.sym_ && states_ == key.states_ && code_ == key.code_;
64     }
65 
66     /// Check if key is not same;
67     bool operator!=(const Key &key) const { return !operator==(key); }
68 
69     /// Check if current key match the key.
70     bool check(const Key &key) const;
71 
72     /// Check if current key match the sym and states.
73     /// \see fcitx::Key::check(const Key &key)
74     bool check(KeySym sym = FcitxKey_None,
75                KeyStates states = KeyStates()) const {
76         return check(Key(sym, states));
77     }
78 
79     /**
80      * Check if current key is a key release of given modifier only key.
81      *
82      * This is a very specialized check for modifier release case.
83      * And it's designed to handle modifier only key.
84      *
85      * For example, if Alt+Shift_L is pressed, then the following release key of
86      * this event can be either: Alt+Shift+Shift_L, or Alt+Shift+Meta_{L,R}.
87      * This is because: Alt -> Meta_{L,R}, if alt is released first, then it
88      * will produce Alt+Shift+Meta_{L,R}. If shift is released first, then it
89      * will produce Alt+Shift+Shift_L.
90      *
91      * Return false if key is not a modifier.
92      */
93     bool isReleaseOfModifier(const Key &key) const;
94 
95     /// Check if key is digit key.
96     bool isDigit() const;
97 
98     /// Check if key is upper case.
99     bool isUAZ() const;
100 
101     /// Check if key is lower case.
102     bool isLAZ() const;
103 
104     /// Check if key is in the range of ascii and has no states.
105     bool isSimple() const;
106 
107     /// Check if the key is a modifier press.
108     bool isModifier() const;
109 
110     /// Check if this key will cause cursor to move, e.g. arrow key and page up/
111     /// down.
112     bool isCursorMove() const;
113 
114     /// Check if this key is a key pad key.
115     bool isKeyPad() const;
116 
117     /// Check if states has modifier.
118     bool hasModifier() const;
119 
120     /// \brief Normalize a key, usually used when key is from frontend.
121     ///
122     /// states will be filtered to have only ctrl alt shift and super.
123     /// Shift will be removed if it is key symbol is a-z/A-Z.
124     /// Shift + any other modifier and a-z will be reset to A-Z. So
125     /// key in configuration does not need to bother the case.
126     Key normalize() const;
127 
128     /// \brief Convert key to a string.
129     ///
130     /// \arg format will control the format of return value.
131     std::string
132     toString(KeyStringFormat format = KeyStringFormat::Portable) const;
133 
134     /// Check if the sym is not FcitxKey_None or FcitxKey_VoidSymbol.
135     bool isValid() const;
136 
sym()137     inline KeySym sym() const { return sym_; }
states()138     inline KeyStates states() const { return states_; }
code()139     inline int code() const { return code_; }
140 
141     /// Convert the modifier symbol to its corresponding states.
142     static KeyStates keySymToStates(KeySym sym);
143 
144     /// Convert a key symbol string to KeySym.
145     static KeySym keySymFromString(const std::string &keyString);
146 
147     /// \brief Convert keysym to a string.
148     ///
149     /// \arg format will control the format of return value.
150     static std::string
151     keySymToString(KeySym sym,
152                    KeyStringFormat format = KeyStringFormat::Portable);
153 
154     /// Convert unicode to key symbol. Useful when you want to create a
155     /// synthetic key event.
156     static KeySym keySymFromUnicode(uint32_t unicode);
157 
158     /// Convert keysym to a unicode. Will return a valid value UCS-4 value if
159     /// this key may produce a character.
160     static uint32_t keySymToUnicode(KeySym sym);
161 
162     /// Convert keysym to a unicode string. Will return a non empty value UTF-8
163     /// string if this key may produce a character.
164     /// \see fcitx::Key::keySymToUnicode
165     static std::string keySymToUTF8(KeySym sym);
166 
167     /// Parse a list of key string into a KeyList.
168     static KeyList keyListFromString(const std::string &str);
169 
170     /// Convert a key list to string.
171     template <typename Container>
172     static std::string
173     keyListToString(Container container,
174                     KeyStringFormat format = KeyStringFormat::Portable) {
175         std::string result;
176         bool first = true;
177         for (auto k : container) {
178             if (first) {
179                 first = false;
180             } else {
181                 result += " ";
182             }
183             result += k.toString(format);
184         }
185         return result;
186     }
187 
188     /// Check the current key against a key list.
189     /// \see fcitx::Key::check
190     template <typename Container>
checkKeyList(const Container & c)191     bool checkKeyList(const Container &c) const {
192         return std::find_if(c.begin(), c.end(), [this](const Key &toCheck) {
193                    return check(toCheck);
194                }) != c.end();
195     }
196 
197     /// Check the current key against a key list and get the matched key index.
198     /// \return Returns the matched key index or -1 if there is no match.
199     /// \see fcitx::Key::check
200     template <typename Container>
keyListIndex(const Container & c)201     int keyListIndex(const Container &c) const {
202         size_t idx = 0;
203         for (auto &toCheck : c) {
204             if (check(toCheck)) {
205                 break;
206             }
207             idx++;
208         }
209         if (idx == c.size()) {
210             return -1;
211         }
212         return idx;
213     }
214 
215 private:
216     KeySym sym_;
217     KeyStates states_;
218     int code_;
219 };
220 } // namespace fcitx
221 
222 #endif //  _FCITX_UTILS_KEY_H_
223