ultimatepp/uppsrc/CtrlLib/AKeys.cpp
2025-01-23 00:47:44 +01:00

385 lines
8.8 KiB
C++

#include "CtrlLib.h"
namespace Upp {
struct KeyBinding : Moveable<KeyBinding> {
KeyInfo *key;
const char *id;
dword def[4];
};
static VectorMap<String, Vector<KeyBinding> >& sKeys()
{
static VectorMap<String, Vector<KeyBinding> > k;
return k;
}
void RegisterKeyBinding(const char *group, const char *id, KeyInfo& (*info)())
{
VectorMap<String, Vector<KeyBinding> >& keys = sKeys();
KeyBinding& k = keys.GetAdd(group).Add();
k.id = id;
KeyInfo& f = (*info)();
k.key = &f;
memcpy(k.def, f.key, sizeof(f.key));
}
KeyInfo& AK_NULL()
{
static KeyInfo x = { "" };
return x;
}
void SetDefaultKeys() {
VectorMap<String, Vector<KeyBinding> >& g = sKeys();
for(int i = 0; i < g.GetCount(); i++) {
Vector<KeyBinding>& k = g[i];
for(int i = 0; i < k.GetCount(); i++) {
KeyBinding& b = k[i];
KeyInfo& f = *b.key;
memcpy(f.key, b.def, sizeof(f.key));
}
}
}
struct KeyDisplay : Display {
virtual void Paint(Draw& w, const Rect& r, const Value& q,
Color ink, Color paper, dword flags) const {
w.DrawRect(r, paper);
String txt = GetKeyDesc(int(q));
int tcy = GetTextSize(txt, StdFont()).cy;
w.DrawText(r.left + 2, r.top + (r.Height() - tcy) / 2, txt, StdFont(), ink);
}
};
struct KeyCtrl : Ctrl {
int key;
virtual void SetData(const Value& v) { key = v; Refresh(); }
virtual Value GetData() const { return key; }
virtual void GotFocus() { Refresh(); }
virtual void LostFocus() { Refresh(); }
virtual void LeftDown(Point p, dword) { SetFocus(); }
virtual void Paint(Draw& w) {
Single<KeyDisplay>().Paint(w, GetSize(), key,
HasFocus() ? SColorLight : SColorText,
HasFocus() ? SColorHighlight : SColorPaper, 0);
}
virtual bool Key(dword _key, int) {
if(_key == K_ENTER || _key == K_ESCAPE)
return false;
if((_key & K_KEYUP) || _key < K_CHAR_LIM || _key == K_SHIFT_KEY || _key == K_ALT_KEY || _key == K_CTRL_KEY)
return true;
if(_key == K_SPACE || _key == K_DELETE || _key == K_BACKSPACE)
key = 0;
else
key = _key;
UpdateActionRefresh();
return true;
}
KeyCtrl() {
Transparent();
SetFrame(EditFieldFrame());
key = 0;
}
};
struct KeysDlg : WithKeysLayout<TopWindow> {
virtual bool Key(dword key, int);
void EnterGroup();
void KeyEdit();
void Defaults();
void CopyKeys();
void Perform();
typedef KeysDlg CLASSNAME;
KeysDlg() {
CtrlLayoutOKCancel(*this, t_("Configure keyboard shortcuts"));
group.AddColumn(t_("Group"));
group.WhenEnterRow = THISBACK(EnterGroup);
group.NoGrid();
keys.AddColumn(t_("Action"));
keys.AddColumn(t_("Primary")).Ctrls<KeyCtrl>();
keys.AddColumn(t_("Secondary")).Ctrls<KeyCtrl>();
keys.SetLineCy(EditField::GetStdHeight() + 2);
keys.NoHorzGrid().NoCursor().EvenRowColor();
keys.ColumnWidths("182 132 133");
keys.WhenCtrlsAction = THISBACK(KeyEdit);
defaults <<= THISBACK(Defaults);
}
};
bool KeysDlg::Key(dword key, int count)
{
if(key == K_F3) { // 'hidden trick' to retrieve the document
String qtf;
for(int i = 0; i < sKeys().GetCount(); i++) {
qtf << "&&&" << DeQtf(sKeys().GetKey(i)) << "&&{{1:1 ";
const Vector<KeyBinding>& b = sKeys()[i];
for(int i = 0; i < b.GetCount(); i++) {
const KeyInfo& a = *b[i].key;
const char *text = a.name;
if(*text == '\3')
text++;
if(a.key[0]) {
if(i)
qtf << ":: ";
qtf << DeQtf(GetKeyDesc(a.key[0]));
if(a.key[1])
qtf << ", " << DeQtf(GetKeyDesc(a.key[1]));
qtf << ":: " << DeQtf(text);
}
}
qtf << "}}";
}
WriteClipboard("QTF", qtf);
BeepExclamation();
return true;
}
return TopWindow::Key(key, count);
}
void KeysDlg::EnterGroup()
{
keys.Clear();
String g = group.GetKey();
int q = sKeys().Find(g);
if(q < 0)
return;
const Vector<KeyBinding>& b = sKeys()[q];
for(int i = 0; i < b.GetCount(); i++) {
const KeyInfo& a = *b[i].key;
const char *text = a.name;
if(*text == '\3')
text = t_GetLngString(text + 1);
keys.Add(text, (int)a.key[0], (int)a.key[1], (int)a.key[2]);
}
keys.GoBegin();
}
void KeysDlg::KeyEdit()
{
String g = group.GetKey();
int q = sKeys().Find(g);
if(q < 0)
return;
for(int i = 0; i < keys.GetCount(); i++) {
KeyInfo& a = *(sKeys()[q][i].key);
a.key[0] = (dword)(int)keys.Get(i, 1);
a.key[1] = (dword)(int)keys.Get(i, 2);
}
}
void KeysDlg::Defaults()
{
if(PromptYesNo("Restore default keys?")) {
SetDefaultKeys();
EnterGroup();
}
}
void KeysDlg::Perform()
{
String bkup = StoreKeys();
for(int i = 0; i < sKeys().GetCount(); i++)
group.Add(sKeys().GetKey(i));
group.Sort();
group.GoBegin();
if(Run() != IDOK)
RestoreKeys(bkup);
}
void EditKeys()
{
KeysDlg().Perform();
}
int CharFilterNoSpace(int c)
{
return c == ' ' ? 0 : c;
}
String StoreKeys()
{
String out;
const VectorMap<String, Vector<KeyBinding> >& g = sKeys();
for(int i = 0; i < g.GetCount(); i++) {
out << "- " << AsCString(g.GetKey(i)) << ";\r\n";
const Vector<KeyBinding>& k = g[i];
for(int i = 0; i < k.GetCount(); i++) {
const KeyBinding& b = k[i];
const KeyInfo& f = *b.key;
for(int i = 0; i < 4; i++)
if(f.key[i]) {
out << Format("%-25s", b.id);
bool b = false;
for(int i = 0; i < 4; i++)
if(f.key[i]) {
if(b)
out << ", ";
out << ' ' << Filter(GetKeyDesc(f.key[i]), CharFilterNoSpace);
b = true;
}
out << ";\r\n";
break;
}
}
out << "\r\n";
}
return out;
}
dword ParseKeyDesc(CParser& p)
{
dword f = 0;
for(;;) {
if(p.Id("Ctrl"))
f |= K_CTRL;
else
if(p.Id("Shift"))
f |= K_SHIFT;
else
if(p.Id("Alt"))
f |= K_ALT;
else
break;
p.PassChar('+');
}
if(p.IsNumber()) {
uint32 q = p.ReadNumber(16);
if(q <= 9)
return f | (K_0 + q);
return f | q;
}
if(p.Char('[')) {
int q = p.GetChar();
p.PassChar(']');
switch(q) {
case '`': return f | K_CTRL_GRAVE;
case '-': return f | K_CTRL_MINUS;
case '=': return f | K_CTRL_EQUAL;
case '\\': return f | K_CTRL_BACKSLASH;
case ';': return f | K_CTRL_SEMICOLON;
case '\'': return f | K_CTRL_APOSTROPHE;
case ',': return f | K_CTRL_COMMA;
case '.': return f | K_CTRL_PERIOD;
case '/': return f | K_CTRL_SLASH;
case '[': return f | K_CTRL_LBRACKET;
case ']': return f | K_CTRL_RBRACKET;
}
throw CParser::Error("");
}
String kid = p.ReadId();
/*
if(kid.GetLength() == 1 && *kid >= 'A' && *kid <= 'Z')
return f | (K_A + *kid - 'A');
if(kid.GetLength() == 2 && kid[0] == 'F' && IsDigit(kid[1]) && kid[1] != '0')
return f | (K_F1 + (kid[1] - '1'));
static struct {
int key;
const char *name;
} nkey[] = {
{ K_TAB, "Tab" }, { K_SPACE, "Space" }, { K_RETURN, "Enter" }, { K_BACKSPACE, "Backspace" },
{ K_CAPSLOCK, "Caps Lock" }, { K_ESCAPE, "Esc" },
{ K_END, "End" }, { K_HOME, "Home" },
{ K_LEFT, "Left" }, { K_UP, "Up" }, { K_RIGHT, "Right" }, { K_DOWN, "Down" },
{ K_INSERT, "Insert" }, { K_DELETE, "Delete" },
{ K_ALT_KEY, "Alt" }, { K_SHIFT_KEY, "Shift" }, { K_CTRL_KEY, "Ctrl" },
{ K_F10, "F10" }, { K_F11, "F11" }, { K_F12, "F12" }, { K_PAGEUP, "PageUp" }, { K_PAGEDOWN, "PageDown" },
{ 0, NULL }
};
*/
static VectorMap<String, int> map;
ONCELOCK {
extern Tuple<dword, const char *> KeyNames__[];
for(int i = 0; KeyNames__[i].a; i++) {
String n = Filter(KeyNames__[i].b, CharFilterNoSpace);
int q = n.Find('\v');
if(q)
n = n.Mid(q + 1);
map.Add(n, KeyNames__[i].a);
}
}
int q = map.Find(kid);
if(q >= 0)
return f | map[q];
if(kid == "Num") {
p.PassChar('[');
int q = p.GetChar();
p.PassChar(']');
if(q == '*') return f | K_MULTIPLY;
if(q == '+') return f | K_ADD;
if(q == '-') return f | K_SUBTRACT;
if(q == '/') return f | K_DIVIDE;
throw CParser::Error("");
}
return f | p.ReadNumber(16);
}
void RestoreKeys(const String& data)
{
SetDefaultKeys();
CParser p(data);
try {
while(!p.IsEof()) {
try {
p.PassChar('-');
String group;
if(p.IsId())
group = p.ReadId();
else
group = p.ReadString();
p.PassChar(';');
int q = sKeys().Find(group);
if(q < 0)
return;
const Vector<KeyBinding>& b = sKeys()[q];
while(!p.IsEof() && p.IsId())
try {
String id = p.ReadId();
int i;
for(i = 0; i < b.GetCount(); i++)
if(b[i].id == id)
break;
int q = 0;
do {
dword key = ParseKeyDesc(p);
if(i < b.GetCount() && q < 4)
b[i].key->key[q++] = key;
}
while(p.Char(','));
p.PassChar(';');
}
catch(CParser::Error) {
while(!p.IsEof() && !p.Char(';'))
p.SkipTerm();
}
}
catch(CParser::Error) {
while(!p.IsEof() && !p.Char(';'))
p.SkipTerm();
}
}
}
catch(CParser::Error) {}
}
String GetDesc(const KeyInfo& f, bool parenthesis)
{
return f.key[0] ? parenthesis ? "(" + GetKeyDesc(f.key[0]) + ")" : GetKeyDesc(f.key[0]) : String();
}
bool Match(const KeyInfo& k, dword key)
{
return findarg(key, k.key[0], k.key[1], k.key[2], k.key[3]) >= 0;
}
}