mirror of https://github.com/procxx/kepka.git
divided shortcuts file to default and custom
This commit is contained in:
parent
bfa8075acf
commit
26ffbbc34f
|
@ -133,6 +133,76 @@ inline bool qMapLessThanKey(const ShortcutCommands::Handler &a, const ShortcutCo
|
||||||
|
|
||||||
namespace Shortcuts {
|
namespace Shortcuts {
|
||||||
|
|
||||||
|
// inspired by https://github.com/sindresorhus/strip-json-comments
|
||||||
|
QByteArray _stripJsonComments(const QByteArray &json) {
|
||||||
|
enum InsideComment {
|
||||||
|
InsideCommentNone,
|
||||||
|
InsideCommentSingleLine,
|
||||||
|
InsideCommentMultiLine,
|
||||||
|
};
|
||||||
|
InsideComment insideComment = InsideCommentNone;
|
||||||
|
bool insideString = false;
|
||||||
|
|
||||||
|
QByteArray result;
|
||||||
|
|
||||||
|
const char *b = json.cbegin(), *e = json.cend(), *offset = b;
|
||||||
|
for (const char *ch = offset; ch != e; ++ch) {
|
||||||
|
char currentChar = *ch;
|
||||||
|
char nextChar = (ch + 1 == e) ? 0 : *(ch + 1);
|
||||||
|
|
||||||
|
if (insideComment == InsideCommentNone && currentChar == '"') {
|
||||||
|
bool escaped = ((ch > b) && *(ch - 1) == '\\') && ((ch - 1 < b) || *(ch - 2) != '\\');
|
||||||
|
if (!escaped) {
|
||||||
|
insideString = !insideString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insideString) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insideComment == InsideCommentNone && currentChar == '/' && nextChar == '/') {
|
||||||
|
if (ch > offset) {
|
||||||
|
if (result.isEmpty()) result.reserve(json.size() - 2);
|
||||||
|
result.append(offset, ch - offset);
|
||||||
|
offset = ch;
|
||||||
|
}
|
||||||
|
insideComment = InsideCommentSingleLine;
|
||||||
|
++ch;
|
||||||
|
} else if (insideComment == InsideCommentSingleLine && currentChar == '\r' && nextChar == '\n') {
|
||||||
|
if (ch > offset) {
|
||||||
|
offset = ch;
|
||||||
|
}
|
||||||
|
++ch;
|
||||||
|
insideComment = InsideCommentNone;
|
||||||
|
} else if (insideComment == InsideCommentSingleLine && currentChar == '\n') {
|
||||||
|
if (ch > offset) {
|
||||||
|
offset = ch;
|
||||||
|
}
|
||||||
|
insideComment = InsideCommentNone;
|
||||||
|
} else if (insideComment == InsideCommentNone && currentChar == '/' && nextChar == '*') {
|
||||||
|
if (ch > offset) {
|
||||||
|
if (result.isEmpty()) result.reserve(json.size() - 2);
|
||||||
|
result.append(offset, ch - offset);
|
||||||
|
offset = ch;
|
||||||
|
}
|
||||||
|
insideComment = InsideCommentMultiLine;
|
||||||
|
++ch;
|
||||||
|
} else if (insideComment == InsideCommentMultiLine && currentChar == '*' && nextChar == '/') {
|
||||||
|
if (ch > offset) {
|
||||||
|
offset = ch;
|
||||||
|
}
|
||||||
|
++ch;
|
||||||
|
insideComment = InsideCommentNone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insideComment == InsideCommentNone && e > offset && !result.isEmpty()) {
|
||||||
|
result.append(offset, e - offset);
|
||||||
|
}
|
||||||
|
return result.isEmpty() ? json : result;
|
||||||
|
}
|
||||||
|
|
||||||
struct DataStruct;
|
struct DataStruct;
|
||||||
DataStruct *DataPtr = nullptr;
|
DataStruct *DataPtr = nullptr;
|
||||||
|
|
||||||
|
@ -253,57 +323,40 @@ namespace Shortcuts {
|
||||||
|
|
||||||
new DataStruct();
|
new DataStruct();
|
||||||
|
|
||||||
QJsonArray shortcuts;
|
// write default shortcuts to a file if they are not there already
|
||||||
OrderedSet<QKeySequence> notfound;
|
bool defaultValid = false;
|
||||||
QFile f(cWorkingDir() + qsl("tdata/shortcuts.json"));
|
QFile defaultFile(cWorkingDir() + qsl("tdata/shortcuts-default.json"));
|
||||||
if (f.exists()) {
|
if (defaultFile.open(QIODevice::ReadOnly)) {
|
||||||
if (f.open(QIODevice::ReadOnly)) {
|
|
||||||
QJsonParseError error = { 0, QJsonParseError::NoError };
|
QJsonParseError error = { 0, QJsonParseError::NoError };
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(f.readAll(), &error);
|
QJsonDocument doc = QJsonDocument::fromJson(_stripJsonComments(defaultFile.readAll()), &error);
|
||||||
if (error.error != QJsonParseError::NoError) {
|
defaultFile.close();
|
||||||
DataPtr->errors.push_back(qsl("Failed to parse '%1'! Error: %2").arg(f.fileName()).arg(error.errorString()));
|
|
||||||
} else if (!doc.isArray()) {
|
|
||||||
DataPtr->errors.push_back(qsl("Failed to parse '%1'! Error: array expected").arg(f.fileName()));
|
|
||||||
} else {
|
|
||||||
for (QMap<QKeySequence, QShortcut*>::const_iterator i = DataPtr->sequences.cbegin(), e = DataPtr->sequences.cend(); i != e; ++i) {
|
|
||||||
notfound.insert(i.key());
|
|
||||||
}
|
|
||||||
|
|
||||||
shortcuts = doc.array();
|
if (error.error == QJsonParseError::NoError && doc.isArray()) {
|
||||||
int limit = ShortcutsCountLimit;
|
QJsonArray shortcuts(doc.array());
|
||||||
for (QJsonArray::const_iterator i = shortcuts.constBegin(), e = shortcuts.constEnd(); i != e; ++i) {
|
if (!shortcuts.isEmpty() && shortcuts.constBegin()->isObject()) {
|
||||||
if (!i->isObject()) {
|
QJsonObject versionObject(shortcuts.constBegin()->toObject());
|
||||||
DataPtr->errors.push_back(qsl("Bad entry in '%1'! Error: object expected").arg(f.fileName()));
|
QJsonObject::const_iterator version = versionObject.constFind(qsl("version"));
|
||||||
} else {
|
if (version != versionObject.constEnd() && version->isString() && version->toString() == QString::number(AppVersion)) {
|
||||||
QKeySequence seq;
|
defaultValid = true;
|
||||||
QJsonObject entry(i->toObject());
|
|
||||||
QJsonObject::const_iterator keys = entry.constFind(qsl("keys")), command = entry.constFind(qsl("command"));
|
|
||||||
if (keys == entry.constEnd() || command == entry.constEnd() || !keys->isString() || (!command->isString() && !command->isNull())) {
|
|
||||||
DataPtr->errors.push_back(qsl("Bad entry in '%1'! Error: {\"keys\": \"..\", \"command\": [ \"..\" | null ]} expected").arg(f.fileName()));
|
|
||||||
} else if (command->isNull()) {
|
|
||||||
seq = _removeShortcut(keys->toString());
|
|
||||||
} else {
|
|
||||||
seq = _setShortcut(keys->toString(), command->toString());
|
|
||||||
}
|
|
||||||
if (!--limit) {
|
|
||||||
DataPtr->errors.push_back(qsl("Too many entries in '%1'!").arg(f.fileName()));
|
|
||||||
break;
|
|
||||||
} else if (DataPtr->errors.isEmpty()) {
|
|
||||||
notfound.remove(seq);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.close();
|
if (!defaultValid && defaultFile.open(QIODevice::WriteOnly)) {
|
||||||
} else {
|
const char *defaultHeader = "\
|
||||||
DataPtr->errors.push_back(qsl("Could not read '") + f.fileName() + qsl("'!"));
|
// This is a list of default shortcuts for Telegram Desktop\n\
|
||||||
}
|
// Please don't modify it, its content is not used in any way\n\
|
||||||
}
|
// You can place your own shortcuts in the 'shortcuts-custom.json' file\n\n";
|
||||||
if (DataPtr->errors.isEmpty() && (shortcuts.isEmpty() || !notfound.isEmpty()) && f.open(QIODevice::WriteOnly)) {
|
defaultFile.write(defaultHeader);
|
||||||
for (OrderedSet<QKeySequence>::const_iterator i = notfound.cbegin(), e = notfound.cend(); i != e; ++i) {
|
|
||||||
QMap<QKeySequence, QShortcut*>::const_iterator s = DataPtr->sequences.constFind(i.key());
|
QJsonArray shortcuts;
|
||||||
if (s != DataPtr->sequences.cend()) {
|
|
||||||
QMap<int, ShortcutCommands::Handler>::const_iterator h = DataPtr->handlers.constFind(s.value()->id());
|
QJsonObject version;
|
||||||
|
version.insert(qsl("version"), QString::number(AppVersion));
|
||||||
|
shortcuts.push_back(version);
|
||||||
|
|
||||||
|
for (QMap<QKeySequence, QShortcut*>::const_iterator i = DataPtr->sequences.cbegin(), e = DataPtr->sequences.cend(); i != e; ++i) {
|
||||||
|
QMap<int, ShortcutCommands::Handler>::const_iterator h = DataPtr->handlers.constFind(i.value()->id());
|
||||||
if (h != DataPtr->handlers.cend()) {
|
if (h != DataPtr->handlers.cend()) {
|
||||||
QMap<ShortcutCommands::Handler, QString>::const_iterator n = DataPtr->commandnames.constFind(h.value());
|
QMap<ShortcutCommands::Handler, QString>::const_iterator n = DataPtr->commandnames.constFind(h.value());
|
||||||
if (n != DataPtr->commandnames.cend()) {
|
if (n != DataPtr->commandnames.cend()) {
|
||||||
|
@ -314,12 +367,72 @@ namespace Shortcuts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
QJsonDocument doc;
|
QJsonDocument doc;
|
||||||
doc.setArray(shortcuts);
|
doc.setArray(shortcuts);
|
||||||
f.write(doc.toJson(QJsonDocument::Indented));
|
defaultFile.write(doc.toJson(QJsonDocument::Indented));
|
||||||
f.close();
|
defaultFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// read custom shortcuts from file if it exists or write an empty custom shortcuts file
|
||||||
|
QFile customFile(cWorkingDir() + qsl("tdata/shortcuts-custom.json"));
|
||||||
|
if (customFile.exists()) {
|
||||||
|
if (customFile.open(QIODevice::ReadOnly)) {
|
||||||
|
QJsonParseError error = { 0, QJsonParseError::NoError };
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(_stripJsonComments(customFile.readAll()), &error);
|
||||||
|
customFile.close();
|
||||||
|
|
||||||
|
if (error.error != QJsonParseError::NoError) {
|
||||||
|
DataPtr->errors.push_back(qsl("Failed to parse! Error: %2").arg(error.errorString()));
|
||||||
|
} else if (!doc.isArray()) {
|
||||||
|
DataPtr->errors.push_back(qsl("Failed to parse! Error: array expected"));
|
||||||
|
} else {
|
||||||
|
QJsonArray shortcuts = doc.array();
|
||||||
|
int limit = ShortcutsCountLimit;
|
||||||
|
for (QJsonArray::const_iterator i = shortcuts.constBegin(), e = shortcuts.constEnd(); i != e; ++i) {
|
||||||
|
if (!i->isObject()) {
|
||||||
|
DataPtr->errors.push_back(qsl("Bad entry! Error: object expected"));
|
||||||
|
} else {
|
||||||
|
QKeySequence seq;
|
||||||
|
QJsonObject entry(i->toObject());
|
||||||
|
QJsonObject::const_iterator keys = entry.constFind(qsl("keys")), command = entry.constFind(qsl("command"));
|
||||||
|
if (keys == entry.constEnd() || command == entry.constEnd() || !keys->isString() || (!command->isString() && !command->isNull())) {
|
||||||
|
DataPtr->errors.push_back(qsl("Bad entry! {\"keys\": \"...\", \"command\": [ \"...\" | null ]} expected"));
|
||||||
|
} else if (command->isNull()) {
|
||||||
|
seq = _removeShortcut(keys->toString());
|
||||||
|
} else {
|
||||||
|
seq = _setShortcut(keys->toString(), command->toString());
|
||||||
|
}
|
||||||
|
if (!--limit) {
|
||||||
|
DataPtr->errors.push_back(qsl("Too many entries! Limit is %1").arg(ShortcutsCountLimit));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DataPtr->errors.push_back(qsl("Could not read the file!"));
|
||||||
|
}
|
||||||
|
if (!DataPtr->errors.isEmpty()) {
|
||||||
|
DataPtr->errors.push_front(qsl("While reading file '%1'...").arg(customFile.fileName()));
|
||||||
|
}
|
||||||
|
} else if (customFile.open(QIODevice::WriteOnly)) {
|
||||||
|
const char *customContent = "\
|
||||||
|
// This is a list of your own shortcuts for Telegram Desktop\n\
|
||||||
|
// You can see full list of commands in the 'shortcuts-default.json' file\n\
|
||||||
|
// Place a null value instead of a command string to switch the shortcut off\n\n\
|
||||||
|
[\n\
|
||||||
|
// {\n\
|
||||||
|
// \"command\": \"close_telegram\",\n\
|
||||||
|
// \"keys\": \"ctrl+f4\"\n\
|
||||||
|
// },\n\
|
||||||
|
// {\n\
|
||||||
|
// \"command\": \"quit_telegram\",\n\
|
||||||
|
// \"keys\": \"ctrl+q\"\n\
|
||||||
|
// }\n\
|
||||||
|
]\n";
|
||||||
|
customFile.write(customContent);
|
||||||
|
customFile.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,22 +441,26 @@ namespace Shortcuts {
|
||||||
return DataPtr->errors;
|
return DataPtr->errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
void launch(int shortcutId) {
|
bool launch(int shortcutId) {
|
||||||
t_assert(DataPtr != nullptr);
|
t_assert(DataPtr != nullptr);
|
||||||
|
|
||||||
QMap<int, ShortcutCommands::Handler>::const_iterator it = DataPtr->handlers.constFind(shortcutId);
|
QMap<int, ShortcutCommands::Handler>::const_iterator it = DataPtr->handlers.constFind(shortcutId);
|
||||||
if (it != DataPtr->handlers.cend()) {
|
if (it == DataPtr->handlers.cend()) {
|
||||||
(*it.value())();
|
return false;
|
||||||
}
|
}
|
||||||
|
(*it.value())();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void launch(const QString &command) {
|
bool launch(const QString &command) {
|
||||||
t_assert(DataPtr != nullptr);
|
t_assert(DataPtr != nullptr);
|
||||||
|
|
||||||
QMap<QString, ShortcutCommands::Handler>::const_iterator it = DataPtr->commands.constFind(command);
|
QMap<QString, ShortcutCommands::Handler>::const_iterator it = DataPtr->commands.constFind(command);
|
||||||
if (it != DataPtr->commands.cend()) {
|
if (it == DataPtr->commands.cend()) {
|
||||||
(*it.value())();
|
return false;
|
||||||
}
|
}
|
||||||
|
(*it.value())();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void finish() {
|
void finish() {
|
||||||
|
|
|
@ -25,8 +25,8 @@ namespace Shortcuts {
|
||||||
void start();
|
void start();
|
||||||
const QStringList &errors();
|
const QStringList &errors();
|
||||||
|
|
||||||
void launch(int shortcutId);
|
bool launch(int shortcutId);
|
||||||
void launch(const QString &command);
|
bool launch(const QString &command);
|
||||||
|
|
||||||
void finish();
|
void finish();
|
||||||
|
|
||||||
|
|
|
@ -1011,24 +1011,43 @@ QRect Window::iconRect() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Window::eventFilter(QObject *obj, QEvent *e) {
|
bool Window::eventFilter(QObject *obj, QEvent *e) {
|
||||||
QEvent::Type t = e->type();
|
switch (e->type()) {
|
||||||
if (t == QEvent::MouseButtonPress || t == QEvent::KeyPress || t == QEvent::TouchBegin || t == QEvent::Wheel) {
|
case QEvent::MouseButtonPress:
|
||||||
|
case QEvent::KeyPress:
|
||||||
|
case QEvent::TouchBegin:
|
||||||
|
case QEvent::Wheel:
|
||||||
psUserActionDone();
|
psUserActionDone();
|
||||||
} else if (t == QEvent::MouseMove) {
|
break;
|
||||||
|
|
||||||
|
case QEvent::MouseMove:
|
||||||
if (main && main->isIdle()) {
|
if (main && main->isIdle()) {
|
||||||
psUserActionDone();
|
psUserActionDone();
|
||||||
main->checkIdleFinish();
|
main->checkIdleFinish();
|
||||||
}
|
}
|
||||||
} else if (t == QEvent::MouseButtonRelease) {
|
break;
|
||||||
|
|
||||||
|
case QEvent::MouseButtonRelease:
|
||||||
Ui::hideStickerPreview();
|
Ui::hideStickerPreview();
|
||||||
} else if (t == QEvent::Shortcut) {
|
break;
|
||||||
Shortcuts::launch(static_cast<QShortcutEvent*>(e)->shortcutId());
|
|
||||||
|
case QEvent::ShortcutOverride: // handle shortcuts ourselves
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case QEvent::Shortcut:
|
||||||
|
if (Shortcuts::launch(static_cast<QShortcutEvent*>(e)->shortcutId())) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QEvent::ApplicationActivate:
|
||||||
if (obj == Application::instance()) {
|
if (obj == Application::instance()) {
|
||||||
if (t == QEvent::ApplicationActivate) {
|
|
||||||
psUserActionDone();
|
psUserActionDone();
|
||||||
QTimer::singleShot(1, this, SLOT(checkHistoryActivation()));
|
QTimer::singleShot(1, this, SLOT(checkHistoryActivation()));
|
||||||
} else if (t == QEvent::FileOpen) {
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QEvent::FileOpen:
|
||||||
|
if (obj == Application::instance()) {
|
||||||
QString url = static_cast<QFileOpenEvent*>(e)->url().toEncoded();
|
QString url = static_cast<QFileOpenEvent*>(e)->url().toEncoded();
|
||||||
if (!url.trimmed().midRef(0, 5).compare(qsl("tg://"), Qt::CaseInsensitive)) {
|
if (!url.trimmed().midRef(0, 5).compare(qsl("tg://"), Qt::CaseInsensitive)) {
|
||||||
cSetStartUrl(url);
|
cSetStartUrl(url);
|
||||||
|
@ -1039,14 +1058,23 @@ bool Window::eventFilter(QObject *obj, QEvent *e) {
|
||||||
}
|
}
|
||||||
activate();
|
activate();
|
||||||
}
|
}
|
||||||
} else if (obj == this) {
|
break;
|
||||||
if (t == QEvent::WindowStateChange) {
|
|
||||||
|
case QEvent::WindowStateChange:
|
||||||
|
if (obj == this) {
|
||||||
Qt::WindowState state = (windowState() & Qt::WindowMinimized) ? Qt::WindowMinimized : ((windowState() & Qt::WindowMaximized) ? Qt::WindowMaximized : ((windowState() & Qt::WindowFullScreen) ? Qt::WindowFullScreen : Qt::WindowNoState));
|
Qt::WindowState state = (windowState() & Qt::WindowMinimized) ? Qt::WindowMinimized : ((windowState() & Qt::WindowMaximized) ? Qt::WindowMaximized : ((windowState() & Qt::WindowFullScreen) ? Qt::WindowFullScreen : Qt::WindowNoState));
|
||||||
stateChanged(state);
|
stateChanged(state);
|
||||||
} else if (t == QEvent::Move || t == QEvent::Resize) {
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QEvent::Move:
|
||||||
|
case QEvent::Resize:
|
||||||
|
if (obj == this) {
|
||||||
psUpdatedPosition();
|
psUpdatedPosition();
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PsMainWindow::eventFilter(obj, e);
|
return PsMainWindow::eventFilter(obj, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue