cosmetix
[dyskinesia.git] / src / keycmb.cpp
blob1886f8ca2e783de8a5433fc2a83cc0af5078f03b
1 /* coded by Ketmar // Vampire Avalon (psyc://ketmar.no-ip.org/~Ketmar)
2 * Understanding is not required. Only obedience.
4 * This program is free software. It comes without any warranty, to
5 * the extent permitted by applicable law. You can redistribute it
6 * and/or modify it under the terms of the Do What The Fuck You Want
7 * To Public License, Version 2, as published by Sam Hocevar. See
8 * http://sam.zoy.org/wtfpl/COPYING for more details.
9 */
10 #include <QDebug>
12 #include "keycmb.h"
15 typedef struct {
16 int key;
17 const char *name;
18 } tKeyCN;
21 static const tKeyCN keyList[] = {
22 {Qt::Key_Escape, "ESC"},
23 {Qt::Key_Tab, "TAB"},
24 //{Qt::Key_Backtab, "???"},
25 {Qt::Key_Backspace, "BSP"},
26 {Qt::Key_Return, "RET"},
27 //{Qt::Key_Enter, "RET"},
28 {Qt::Key_Enter, "KPRET"},
29 {Qt::Key_Insert, "INS"},
30 {Qt::Key_Delete, "DEL"},
31 {Qt::Key_Home, "HOME"},
32 {Qt::Key_End, "END"},
33 {Qt::Key_Left, "LEFT"},
34 {Qt::Key_Up, "UP"},
35 {Qt::Key_Right, "RIGHT"},
36 {Qt::Key_Down, "DOWN"},
37 {Qt::Key_PageUp, "PRIOR"},
38 {Qt::Key_PageDown, "NEXT"},
39 {Qt::Key_F1, "F1"},
40 {Qt::Key_F2, "F2"},
41 {Qt::Key_F3, "F3"},
42 {Qt::Key_F4, "F4"},
43 {Qt::Key_F5, "F5"},
44 {Qt::Key_F6, "F6"},
45 {Qt::Key_F7, "F7"},
46 {Qt::Key_F8, "F8"},
47 {Qt::Key_F9, "F9"},
48 {Qt::Key_F10, "F10"},
49 {Qt::Key_F11, "F11"},
50 {Qt::Key_F12, "F12"},
51 {Qt::Key_F13, "F13"},
52 {Qt::Key_F14, "F14"},
53 {Qt::Key_F15, "F15"},
54 {Qt::Key_F16, "F16"},
55 {Qt::Key_F17, "F17"},
56 {Qt::Key_F18, "F18"},
57 {Qt::Key_F19, "F19"},
58 {Qt::Key_F20, "F20"},
59 {Qt::Key_F21, "F21"},
60 {Qt::Key_F22, "F22"},
61 {Qt::Key_F23, "F23"},
62 {Qt::Key_F24, "F24"},
63 {Qt::Key_F25, "F25"},
64 {Qt::Key_F26, "F26"},
65 {Qt::Key_F27, "F27"},
66 {Qt::Key_F28, "F28"},
67 {Qt::Key_F29, "F29"},
68 {Qt::Key_F30, "F30"},
69 {Qt::Key_F31, "F31"},
70 {Qt::Key_F32, "F32"},
71 {Qt::Key_F33, "F33"},
72 {Qt::Key_F34, "F34"},
73 {Qt::Key_F35, "F35"},
74 {0, 0}
78 typedef enum {
79 emCtrl=0x01,
80 emMeta=0x02,
81 emShift=0x04,
82 emHyper=0x08
83 } eParserMod;
86 QString KeyComboList::normalizeKbd (const QString &kbd, bool strictShift) {
87 unsigned char mods = 0;
88 QString s(kbd.trimmed()), res, t;
89 if (s.isEmpty()) return QString();
90 int pos = 0, mFlag;
91 while (pos+1 < s.length() && s.at(pos+1) == '-') {
92 switch (s.at(pos).unicode()) {
93 case 'C': mFlag = emCtrl; break;
94 case 'M': mFlag = emMeta; break;
95 case 'S': mFlag = emShift; break;
96 case 'H': mFlag = emHyper; break;
97 default: mFlag = 0;
99 if (!mFlag || (mods & mFlag)) return QString();
100 mods |= mFlag;
101 pos += 2;
103 if (pos) s = s.mid(pos);
104 if (s.length() == 1) {
105 if (mods & emShift) {
106 mods &= ~emShift;
107 s = s.toUpper();
108 } else if (strictShift) s = s.toLower();
109 if (mods == emCtrl) s = s.toLower();
111 if (mods & emCtrl) res += "C-";
112 if (mods & emMeta) res += "M-";
113 if (mods & emHyper) res += "H-";
114 if (mods & emShift) res += "S-";
115 //qDebug() << "key:" << t << "rmod:" << res;
116 if (s.length() > 1) {
117 for (int f = 0; keyList[f].key; f++) {
118 QString kn(keyList[f].name);
119 if (kn.compare(s, Qt::CaseInsensitive) == 0) {
120 // key found!
121 res += kn;
122 //qDebug() << "res:" << res;
123 return res;
126 } else {
127 res += s;
128 //qDebug() << "res:" << res;
129 return res;
131 //qDebug() << "wtf?";
132 return QString();
136 QString KeyComboList::event2Kbd (QKeyEvent *keyev) {
137 QString res;
138 // check modifiers
139 Qt::KeyboardModifiers md = keyev->modifiers();
140 if (md & Qt::ControlModifier) res += "C-";
141 if (md & Qt::AltModifier) res += "M-";
142 if (md & Qt::MetaModifier) res += "H-";
143 if (md & Qt::ShiftModifier) res += "S-";
144 int k = keyev->key();
145 if (k >= 32 && k <= 0xff00) {
146 res += QChar((uint)k);
147 return normalizeKbd(res, true);
149 for (int f = 0; keyList[f].key; f++) {
150 if (keyList[f].key == k) {
151 res += keyList[f].name;
152 return normalizeKbd(res, true);
155 return QString();
159 ///////////////////////////////////////////////////////////////////////////////
160 KeyComboList::KeyComboList () {
161 clear();
165 KeyComboList::~KeyComboList () {
169 void KeyComboList::clear () {
170 mBinds.clear();
171 mCombos.clear();
172 reset();
176 bool KeyComboList::bind (const QString &kn, const QString &cmd) {
177 QString newK;
178 QStringList cl(kn.split(' ', QString::SkipEmptyParts));
179 //qDebug() << cl;
180 foreach (const QString &s, cl) {
181 QString t(normalizeKbd(s));
182 //qDebug() << "s:" << s << "t:" << t;
183 if (t.isEmpty()) {
184 qWarning() << "invalid keycombo:" << s;
185 return false;
187 if (!newK.isEmpty()) newK += ' ';
188 newK += t;
190 if (cmd.isEmpty()) {
191 mBinds.remove(newK);
192 int f; while ((f = mCombos.indexOf(newK)) >= 0) mCombos.removeAt(f);
193 } else {
194 mBinds[newK] = cmd;
195 if (mCombos.indexOf(newK) < 0) mCombos << newK;
197 return true;
201 QString KeyComboList::process (QKeyEvent *keyev) {
202 //qDebug() << "key:" << keyev->key();
203 QString e(event2Kbd(keyev));
204 if (e.isEmpty()) return QString();
205 qDebug() << "ev:" << e << "cmb:" << mCurCombo << "cond:" << mCurCombo+" "+e;
206 mCurComboText += keyev->text();
207 bool first = mCurCombo.isEmpty();
208 if (!first) mCurCombo += ' ';
209 mCurCombo += e;
210 bool hasMore = false;
211 QString ee(mCurCombo+' ');
212 //qDebug() << "first:" << first << "cmb:" << mCurCombo;
213 for (int f = 0, len = mCombos.length(); f < len; f++) {
214 //qDebug() << "at f:" << mCombos[f] << "cur:" << mCurCombo;
215 if (mCombos[f] == mCurCombo) {
216 // hit it!
217 mLastCombo = mCurCombo;
218 mLastComboText = mCurComboText;
219 reset();
220 return mBinds[mLastCombo];
222 if (mCombos[f].startsWith(ee)) hasMore = true;
224 if (!hasMore) {
225 // not found
226 reset();
227 if (first) return QString();
228 return QString(" ?");
230 //qDebug() << "HAS MORE";
231 return QString(" ");
235 void KeyComboList::reset () {
236 mCurCombo.clear();
237 mCurComboText.clear();
241 //FIXME: slooooow!
242 static QString quoteStr (const QString &s) {
243 QString res;
244 res.reserve(s.size()+2); // at least
245 res += '"';
246 for (int f = 0; f < s.size(); ++f) {
247 QChar ch = s[f];
248 if (ch == '"' || ch == '\\') { res += '\\'; res += ch; }
249 else if (ch == '\t') res += "\\t";
250 else if (ch == '\n') res += "\\n";
251 else if (ch.unicode() <= 32 || ch.unicode() >= 0xff00) res += ' ';
252 else res += ch;
254 res += '"';
255 return res;
259 QStringList KeyComboList::toString () const {
260 QStringList res;
261 foreach (const QString &kk, mCombos) {
262 res << quoteStr(kk);
263 res << quoteStr(mBinds[kk]);
265 return res;
269 bool KeyComboList::isProcessing () const {
270 return !(mCurCombo.isEmpty());
274 void KeyComboList::appendFrom (KeyComboList *kl) {
275 if (!kl) return;
276 foreach (const QString &kk, kl->mCombos) bind(kk, kl->mBinds[kk]);