From f3978cec5936d9f1b4bd8dc04d178978163fe563 Mon Sep 17 00:00:00 2001 From: "wingo.he" Date: Wed, 11 Oct 2023 21:53:50 +0800 Subject: [PATCH] =?UTF-8?q?v9.29.0=20[=E6=96=87=E6=9C=AC=E7=BB=88=E7=AB=AF?= =?UTF-8?q?]=E9=87=8D=E6=9E=84=E6=96=87=E6=9C=AC=E7=BB=88=E7=AB=AF?= =?UTF-8?q?=E6=8C=89=E9=94=AE=E6=A8=A1=E5=9D=97=EF=BC=8C=E5=85=81=E8=AE=B8?= =?UTF-8?q?=E5=9C=A8=E4=B8=8D=E5=90=8C=E5=B9=B3=E5=8F=B0=E4=B8=8B=EF=BC=8C?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=8F=8A=E4=BD=BF=E7=94=A8=E8=AF=A5?= =?UTF-8?q?=E5=B9=B3=E5=8F=B0=E7=9A=84=E6=8C=89=E9=94=AE=E4=B9=A0=E6=83=AF?= =?UTF-8?q?=E3=80=82=20[=E6=96=87=E6=9C=AC=E7=BB=88=E7=AB=AF]=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E9=BC=A0=E6=A0=87=E5=9C=A8=E4=B8=AD=E6=96=87=E7=A7=BB?= =?UTF-8?q?=E5=8A=A8=E8=BF=87=E7=A8=8B=E7=9A=84=E9=BC=A0=E6=A0=87=E6=AE=8B?= =?UTF-8?q?=E5=BD=B1=E9=97=AE=E9=A2=98=E3=80=82=20[=E6=96=87=E6=9C=AC?= =?UTF-8?q?=E7=BB=88=E7=AB=AF]=E4=BC=98=E5=8C=96=E9=94=AE=E7=9B=98?= =?UTF-8?q?=E6=8C=89=E9=94=AE=E8=87=AA=E5=8A=A8=E9=87=8D=E5=A4=8D=E7=9A=84?= =?UTF-8?q?=E9=A2=91=E7=8E=87=E7=AE=97=E6=B3=95=E3=80=82=20[=E6=96=87?= =?UTF-8?q?=E6=9C=AC=E7=BB=88=E7=AB=AF]=E5=A2=9E=E5=8A=A0bell=E7=9A=84?= =?UTF-8?q?=E5=A3=B0=E9=9F=B3=EF=BC=8C=E6=8F=90=E7=A4=BA=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=BD=93=E5=89=8D=E8=BE=93=E5=85=A5=E7=8A=B6=E6=80=81=E3=80=82?= =?UTF-8?q?=20[=E6=96=87=E6=9C=AC=E7=BB=88=E7=AB=AF]=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=AD=97=E7=AC=A6=E4=B8=B2=E5=A4=8D=E5=88=B6=E5=92=8C=E7=B2=98?= =?UTF-8?q?=E8=B4=B4=EF=BC=8C=E4=B8=BA=E7=94=A8=E6=88=B7=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E6=9B=B4=E5=A4=9A=E7=9A=84=E9=80=89=E6=8B=A9=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Text Terminal] Reconstruct the text terminal button module, allowing customization and use of button habits on different platforms. [Text terminal] Fixed the issue of mouse residue during Chinese movement. [Text Terminal] Optimize the frequency algorithm for automatic repetition of keyboard keys. [Text terminal] Add a bell sound to the text terminal to prompt the user about the current input status. [Text Terminal] Optimize string copying and pasting, providing users with more choices. --- kxterm/CMakeLists.txt | 8 + kxterm/qkxcolorschema.cpp | 12 + kxterm/qkxcolorschema.h | 2 + kxterm/qkxcombinekeyactiondialog.cpp | 501 ++++++++++++ kxterm/qkxcombinekeyactiondialog.h | 55 ++ kxterm/qkxcombinekeyactiondialog.ui | 537 +++++++++++++ kxterm/qkxkeytranslator.cpp | 456 ++++++++--- kxterm/qkxkeytranslator.h | 122 ++- kxterm/qkxkeytranslatormodel.cpp | 216 +++++ kxterm/qkxkeytranslatormodel.h | 62 ++ kxterm/qkxshortcutinputdialog.cpp | 126 +++ kxterm/qkxshortcutinputdialog.h | 40 + kxterm/qkxshortcutinputdialog.ui | 113 +++ kxterm/qkxtermitem.cpp | 495 ++++++------ kxterm/qkxtermitem.h | 65 +- kxterm/qkxtermwidget.cpp | 42 +- kxterm/qkxtermwidget.h | 5 +- kxterm/qkxutils.cpp | 71 +- kxterm/qkxutils.h | 11 +- kxterm/qkxview.cpp | 3 - kxterm/qvte.cpp | 13 +- kxterm/qvte.h | 1 + kxterm/qvtedef.h | 12 +- kxterm/qvteimpl.cpp | 4 +- private/keytabs/default.keytab | 59 +- private/keytabs/linux.keytab | 38 +- private/keytabs/macos.keytab | 222 +++++ private/keytabs/solaris.keytab | 31 +- private/languages/woterm_zh.ts | 1118 +++++++++++++++++++------- private/skins/black/desktop.qss | 7 + private/skins/black/operation.png | Bin 0 -> 4203 bytes private/skins/black/string.png | Bin 0 -> 4454 bytes private/skins/blue/desktop.qss | 7 + private/skins/blue/operation.png | Bin 0 -> 4959 bytes private/skins/blue/string.png | Bin 0 -> 5263 bytes private/skins/gold/desktop.qss | 7 + private/skins/gold/operation.png | Bin 0 -> 4753 bytes private/skins/gold/string.png | Bin 0 -> 4387 bytes private/skins/light/desktop.qss | 7 + private/skins/light/operation.png | Bin 0 -> 4955 bytes private/skins/light/string.png | Bin 0 -> 5254 bytes woterm/main.cpp | 52 +- woterm/qmorlogintermwidget.cpp | 8 +- woterm/qmosshtermwidget.cpp | 8 +- woterm/qmotermwidget.cpp | 37 +- woterm/qmotermwidget.h | 7 +- woterm/qwomainwindow.cpp | 2 + woterm/qwoplaybooktermwidget.cpp | 29 +- woterm/qwoptytermwidget.cpp | 8 +- woterm/qworlogintermwidget.cpp | 9 +- woterm/qwoserialtermwidget.cpp | 4 +- woterm/qwosessionttyproperty.cpp | 495 ++++++++++-- woterm/qwosessionttyproperty.h | 20 +- woterm/qwosessionttyproperty.ui | 200 ++++- woterm/qwosetting.cpp | 10 +- woterm/qwosetting.h | 3 +- woterm/qwosshtermwidget.cpp | 8 +- woterm/qwotelnettermwidget.cpp | 8 +- woterm/qwotermwidget.cpp | 67 +- woterm/qwotermwidget.h | 11 +- woterm/resource/keytab.template | 55 ++ woterm/version.h | 2 +- woterm/woterm.qrc | 1 + 63 files changed, 4530 insertions(+), 982 deletions(-) create mode 100644 kxterm/qkxcombinekeyactiondialog.cpp create mode 100644 kxterm/qkxcombinekeyactiondialog.h create mode 100644 kxterm/qkxcombinekeyactiondialog.ui create mode 100644 kxterm/qkxkeytranslatormodel.cpp create mode 100644 kxterm/qkxkeytranslatormodel.h create mode 100644 kxterm/qkxshortcutinputdialog.cpp create mode 100644 kxterm/qkxshortcutinputdialog.h create mode 100644 kxterm/qkxshortcutinputdialog.ui create mode 100644 private/keytabs/macos.keytab create mode 100644 private/skins/black/operation.png create mode 100644 private/skins/black/string.png create mode 100644 private/skins/blue/operation.png create mode 100644 private/skins/blue/string.png create mode 100644 private/skins/gold/operation.png create mode 100644 private/skins/gold/string.png create mode 100644 private/skins/light/operation.png create mode 100644 private/skins/light/string.png create mode 100644 woterm/resource/keytab.template diff --git a/kxterm/CMakeLists.txt b/kxterm/CMakeLists.txt index 45071d9..58900f5 100644 --- a/kxterm/CMakeLists.txt +++ b/kxterm/CMakeLists.txt @@ -15,6 +15,7 @@ find_package(Qt5 COMPONENTS Core Gui Widgets REQUIRED) set(SOURCE_FILES qkxhistory.cpp qkxkeytranslator.cpp + qkxkeytranslatormodel.cpp qkxscreen.cpp qkxtermitem.cpp qkxutils.cpp @@ -27,11 +28,14 @@ set(SOURCE_FILES wcwidth.cpp qkxechoinput.cpp qkxbackgroundimagerender.cpp + qkxcombinekeyactiondialog.cpp + qkxshortcutinputdialog.cpp ) set(HAEDER_FILES qkxhistory.h qkxkeytranslator.h + qkxkeytranslatormodel.h qkxscreen.h qkxtermitem.h qkxutils.h @@ -45,11 +49,15 @@ set(HAEDER_FILES wcwidth.h qkxechoinput.h qkxbackgroundimagerender.h + qkxcombinekeyactiondialog.h + qkxshortcutinputdialog.h qkxterm_share.h ) set(OTHER_FILES qkxsearch.ui + qkxcombinekeyactiondialog.ui + qkxshortcutinputdialog.ui ) add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${HAEDER_FILES} ${OTHER_FILES}) diff --git a/kxterm/qkxcolorschema.cpp b/kxterm/qkxcolorschema.cpp index 02fa97f..7b541f6 100644 --- a/kxterm/qkxcolorschema.cpp +++ b/kxterm/qkxcolorschema.cpp @@ -12,6 +12,7 @@ #include "qkxcolorschema.h" #include +#include QKxColorSchema::QKxColorSchema(QObject *parent) : QObject(parent) @@ -24,8 +25,17 @@ QKxColorSchema::~QKxColorSchema() } +QString QKxColorSchema::name() const +{ + return m_name; +} + bool QKxColorSchema::load(const QString &path) { + m_pretty.clear(); + m_name.clear(); + m_indexs.clear(); + QFile f(path); if(!f.open(QFile::ReadOnly|QFile::Text)) { return false; @@ -49,6 +59,8 @@ bool QKxColorSchema::load(const QString &path) } } } + QFileInfo fi(f); + m_name = fi.fileName(); m_pretty.clear(); return !m_indexs.isEmpty(); } diff --git a/kxterm/qkxcolorschema.h b/kxterm/qkxcolorschema.h index 792e1e5..5dd0395 100644 --- a/kxterm/qkxcolorschema.h +++ b/kxterm/qkxcolorschema.h @@ -23,6 +23,7 @@ class QKxColorSchema : public QObject public: explicit QKxColorSchema(QObject *parent = nullptr); virtual ~QKxColorSchema(); + QString name() const; bool load(const QString& path); QColor cursor() const; QColor background() const; @@ -37,6 +38,7 @@ class QKxColorSchema : public QObject QColor m_cursor; QVector m_indexs; QMap m_pretty; + QString m_name; }; #endif // QTHEME_H diff --git a/kxterm/qkxcombinekeyactiondialog.cpp b/kxterm/qkxcombinekeyactiondialog.cpp new file mode 100644 index 0000000..b1308f9 --- /dev/null +++ b/kxterm/qkxcombinekeyactiondialog.cpp @@ -0,0 +1,501 @@ +/******************************************************************************************* +* +* Copyright (C) 2023 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qkxcombinekeyactiondialog.h" +#include "ui_qkxcombinekeyactiondialog.h" + +#include "qkxshortcutinputdialog.h" + +#include +#include +#include +#include +#include +#include + +#define SIMULATE_MAC_KEY (false) + +QKxCombineKeyActionDialog::QKxCombineKeyActionDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::QKxCombineKeyActionDialog) +{ + ui->setupUi(this); + setWindowTitle(tr("Combine key action input")); + + QObject::connect(ui->btnApply, SIGNAL(clicked()), this, SLOT(onApplyButtonClicked())); + QObject::connect(ui->btnCancel, SIGNAL(clicked()), this, SLOT(close())); + + QStringListModel *model = new QStringListModel(this); + auto all = QKxKeyTranslator::operationDescriptions(); + model->setStringList(all.values()); + ui->op->setModel(model); + { + QButtonGroup *group = new QButtonGroup(this); + group->addButton(ui->radAnsiN); + group->addButton(ui->radAnsiO); + group->addButton(ui->radAnsiP); + ui->radAnsiO->setChecked(true); + QObject::connect(group, SIGNAL(buttonToggled(int,bool)), this, SLOT(onCombineKeyChanged())); + } + { + QButtonGroup *group = new QButtonGroup(this); + group->addButton(ui->radNewLineN); + group->addButton(ui->radNewLineO); + group->addButton(ui->radNewLineP); + ui->radNewLineO->setChecked(true); + QObject::connect(group, SIGNAL(buttonToggled(int,bool)), this, SLOT(onCombineKeyChanged())); + } + { + QButtonGroup *group = new QButtonGroup(this); + group->addButton(ui->radAppCuKeysN); + group->addButton(ui->radAppCuKeysO); + group->addButton(ui->radAppCuKeysP); + ui->radAppCuKeysO->setChecked(true); + QObject::connect(group, SIGNAL(buttonToggled(int,bool)), this, SLOT(onCombineKeyChanged())); + } + { + QButtonGroup *group = new QButtonGroup(this); + group->addButton(ui->radAppScreenN); + group->addButton(ui->radAppScreenO); + group->addButton(ui->radAppScreenP); + ui->radAppScreenO->setChecked(true); + QObject::connect(group, SIGNAL(buttonToggled(int,bool)), this, SLOT(onCombineKeyChanged())); + } + { + QButtonGroup *group = new QButtonGroup(this); + group->addButton(ui->radAppKeyPadN); + group->addButton(ui->radAppKeyPadO); + group->addButton(ui->radAppKeyPadP); + ui->radAppKeyPadO->setChecked(true); + QObject::connect(group, SIGNAL(buttonToggled(int,bool)), this, SLOT(onCombineKeyChanged())); + } + { +#if defined (Q_OS_MAC) || SIMULATE_MAC_KEY + ui->radAltN->setText("-Option"); + ui->radAltO->setText("None"); + ui->radAltP->setText("+Option"); +#endif + QButtonGroup *group = new QButtonGroup(this); + group->addButton(ui->radAltN); + group->addButton(ui->radAltO); + group->addButton(ui->radAltP); + ui->radAltO->setChecked(true); + QObject::connect(group, SIGNAL(buttonToggled(int,bool)), this, SLOT(onCombineKeyChanged())); + } + { +#if defined (Q_OS_MAC) || SIMULATE_MAC_KEY + ui->radCtrlN->setText("-Control"); + ui->radCtrlO->setText("None"); + ui->radCtrlP->setText("+Control"); +#endif + QButtonGroup *group = new QButtonGroup(this); + group->addButton(ui->radCtrlN); + group->addButton(ui->radCtrlO); + group->addButton(ui->radCtrlP); + ui->radCtrlO->setChecked(true); + QObject::connect(group, SIGNAL(buttonToggled(int,bool)), this, SLOT(onCombineKeyChanged())); + } + { + QButtonGroup *group = new QButtonGroup(this); + group->addButton(ui->radShiftN); + group->addButton(ui->radShiftO); + group->addButton(ui->radShiftP); + ui->radShiftO->setChecked(true); + QObject::connect(group, SIGNAL(buttonToggled(int,bool)), this, SLOT(onCombineKeyChanged())); + } + { +#if defined (Q_OS_MAC) || SIMULATE_MAC_KEY + ui->radMetaN->setText("-Command"); + ui->radMetaO->setText("None"); + ui->radMetaP->setText("+Command"); +#endif + QButtonGroup *group = new QButtonGroup(this); + group->addButton(ui->radMetaN); + group->addButton(ui->radMetaO); + group->addButton(ui->radMetaP); + ui->radMetaO->setChecked(true); + QObject::connect(group, SIGNAL(buttonToggled(int,bool)), this, SLOT(onCombineKeyChanged())); + } + { + QButtonGroup *group = new QButtonGroup(this); + group->addButton(ui->radString); + group->addButton(ui->radOperation); + QObject::connect(group, SIGNAL(buttonToggled(int,bool)), this, SLOT(onActionButtonClicked())); + ui->radOperation->setChecked(true); + } + QObject::connect(ui->btnQuickInput, SIGNAL(clicked()), this, SLOT(onQuickInput())); + ui->key->setReadOnly(false); + ui->key->installEventFilter(this); + QObject::connect(ui->key, SIGNAL(textChanged(QString)), this, SLOT(onCombineKeyChanged())); + adjustSizeLater(); +} + +QKxCombineKeyActionDialog::~QKxCombineKeyActionDialog() +{ + delete ui; +} + +void QKxCombineKeyActionDialog::init(const QKxKeyTranslator::KeyInfo &ki) +{ + if(ki.modes & QKxKeyTranslator::VM_ANSI_YES) { + ui->radAnsiP->setChecked(true); + }else if(ki.modes& QKxKeyTranslator::VM_ANSI_NO) { + ui->radAnsiN->setChecked(true); + }else{ + ui->radAnsiO->setChecked(true); + } + + if(ki.modes & QKxKeyTranslator::VM_NEWLINE_YES) { + ui->radNewLineP->setChecked(true); + }else if(ki.modes& QKxKeyTranslator::VM_NEWLINE_NO) { + ui->radNewLineN->setChecked(true); + }else{ + ui->radNewLineO->setChecked(true); + } + + if(ki.modes & QKxKeyTranslator::VM_APPCUKEY_YES) { + ui->radAppCuKeysP->setChecked(true); + }else if(ki.modes& QKxKeyTranslator::VM_APPCUKEY_NO) { + ui->radAppCuKeysN->setChecked(true); + }else{ + ui->radAppCuKeysO->setChecked(true); + } + + if(ki.modes & QKxKeyTranslator::VM_APPSCREEN_YES) { + ui->radAppScreenP->setChecked(true); + }else if(ki.modes& QKxKeyTranslator::VM_APPSCREEN_NO) { + ui->radAppScreenN->setChecked(true); + }else{ + ui->radAppScreenO->setChecked(true); + } + + if(ki.modes & QKxKeyTranslator::VM_APPKEYPAD_YES) { + ui->radAppKeyPadP->setChecked(true); + }else if(ki.modes& QKxKeyTranslator::VM_APPKEYPAD_NO) { + ui->radAppKeyPadN->setChecked(true); + }else{ + ui->radAppKeyPadO->setChecked(true); + } + + if(ki.modifies & QKxKeyTranslator::MK_SHIFT_YES) { + ui->radShiftP->setChecked(true); + }else if(ki.modifies & QKxKeyTranslator::MK_SHIFT_NO) { + ui->radShiftN->setChecked(true); + }else{ + ui->radShiftO->setChecked(true); + } + + if(ki.modifies & QKxKeyTranslator::MK_CTRL_YES) { + ui->radCtrlP->setChecked(true); + }else if(ki.modifies & QKxKeyTranslator::MK_CTRL_NO) { + ui->radCtrlN->setChecked(true); + }else{ + ui->radCtrlO->setChecked(true); + } + + if(ki.modifies & QKxKeyTranslator::MK_ALT_YES) { + ui->radAltP->setChecked(true); + }else if(ki.modifies & QKxKeyTranslator::MK_ALT_NO) { + ui->radAltN->setChecked(true); + }else{ + ui->radAltO->setChecked(true); + } + + if(ki.modifies & QKxKeyTranslator::MK_META_YES) { + ui->radMetaP->setChecked(true); + }else if(ki.modifies & QKxKeyTranslator::MK_META_NO) { + ui->radMetaN->setChecked(true); + }else{ + ui->radMetaO->setChecked(true); + } + + QKeySequence seq(ki.key); + ui->key->setText(seq.toString()); + + ui->radString->setChecked(!ki.out.isEmpty()); + ui->radOperation->setChecked(ki.out.isEmpty()); + ui->stringArea->setVisible(ui->radString->isChecked()); + ui->opArea->setVisible(ui->radOperation->isChecked()); + + if(ki.out.isEmpty()) { + ui->op->setCurrentIndex(ki.op - 1); + }else{ + ui->ansiText->setText(ki.action); + } + + + adjustSizeLater(); +} + +QKxKeyTranslator::KeyInfo QKxCombineKeyActionDialog::result() +{ + QKxKeyTranslator::KeyInfo ki; + if(ui->radString->isChecked()) { + ki.action = ui->ansiText->text(); + ki.out = QKxKeyTranslator::stringToAnsiSequence(ki.action); + }else if(ui->radOperation->isChecked()) { + int idx = ui->op->currentIndex(); + ki.op = QKxKeyTranslator::EOperation(idx+1); + ki.action = QKxKeyTranslator::operationToName(ki.op); + } + + if(ui->radAnsiP->isChecked()) { + ki.modes |= QKxKeyTranslator::VM_ANSI_YES; + }else if(ui->radAnsiN->isChecked()) { + ki.modes |= QKxKeyTranslator::VM_ANSI_NO; + } + if(ui->radNewLineP->isChecked()) { + ki.modes |= QKxKeyTranslator::VM_NEWLINE_YES; + }else if(ui->radNewLineN->isChecked()) { + ki.modes |= QKxKeyTranslator::VM_NEWLINE_NO; + } + if(ui->radAppCuKeysP->isChecked()) { + ki.modes |= QKxKeyTranslator::VM_APPCUKEY_YES; + }else if(ui->radAppCuKeysN->isChecked()) { + ki.modes |= QKxKeyTranslator::VM_APPCUKEY_NO; + } + if(ui->radAppScreenP->isChecked()) { + ki.modes |= QKxKeyTranslator::VM_APPSCREEN_YES; + }else if(ui->radAppScreenN->isChecked()) { + ki.modes |= QKxKeyTranslator::VM_APPSCREEN_NO; + } + if(ui->radAppKeyPadP->isChecked()) { + ki.modes |= QKxKeyTranslator::VM_APPKEYPAD_YES; + }else if(ui->radAppKeyPadN->isChecked()) { + ki.modes |= QKxKeyTranslator::VM_APPKEYPAD_NO; + } + + if(ui->radShiftP->isChecked()) { + ki.modifies |= QKxKeyTranslator::MK_SHIFT_YES; + }else if(ui->radShiftN->isChecked()) { + ki.modifies |= QKxKeyTranslator::MK_SHIFT_NO; + } + if(ui->radCtrlP->isChecked()) { + ki.modifies |= QKxKeyTranslator::MK_CTRL_YES; + }else if(ui->radCtrlN->isChecked()) { + ki.modifies |= QKxKeyTranslator::MK_CTRL_NO; + } + if(ui->radAltP->isChecked()) { + ki.modifies |= QKxKeyTranslator::MK_ALT_YES; + }else if(ui->radAltN->isChecked()) { + ki.modifies |= QKxKeyTranslator::MK_ALT_NO; + } + if(ui->radMetaP->isChecked()) { + ki.modifies |= QKxKeyTranslator::MK_META_YES; + }else if(ui->radMetaN->isChecked()) { + ki.modifies |= QKxKeyTranslator::MK_META_NO; + } + QKeySequence seq(ui->key->text()); + ki.key = seq[0]; + ki.condition = combineKey(); + return ki; +} + +void QKxCombineKeyActionDialog::onActionButtonClicked() +{ + //qDebug() << "onActionButtonClicked" << QDateTime::currentMSecsSinceEpoch(); + setUpdatesEnabled(false); + ui->opArea->setVisible(ui->radOperation->isChecked()); + ui->stringArea->setVisible(ui->radString->isChecked()); + setUpdatesEnabled(true); + adjustSizeLater(); +} + +void QKxCombineKeyActionDialog::onApplyButtonClicked() +{ + QString key = ui->key->text(); + if(key.isEmpty()) { + emit messageArrived(tr("Parameter error"), tr("The key parameter should not be empty.")); + return; + } + if(ui->radString->isChecked()) { + QString out = ui->ansiText->text(); + if(out.isEmpty()) { + emit messageArrived(tr("Parameter error"), tr("The string parameter should not be empty.")); + return; + } + } + done(QDialog::Accepted+1); +} + +void QKxCombineKeyActionDialog::onCombineKeyChanged() +{ + ui->ckey->setText(combineKey()); +} + +void QKxCombineKeyActionDialog::onQuickInput() +{ + QKxShortcutInputDialog dlg(this); + QString key = ui->key->text(); + if(!key.isEmpty()) { + QKeySequence seq(key); + int ckey = seq[0]; + if(ui->radShiftP->isChecked()) { + ckey += Qt::SHIFT; + } + if(ui->radCtrlP->isChecked()) { + ckey += Qt::CTRL; + } + if(ui->radAltP->isChecked()) { + ckey += Qt::ALT; + } + if(ui->radMetaP->isChecked()) { + ckey += Qt::META; + } + + dlg.init(QKeySequence(ckey)); + } + if(dlg.exec() == QDialog::Accepted+1) { + QString key = dlg.result(); + QKeySequence seq(key); + setShortcut(seq); + } +} + +void QKxCombineKeyActionDialog::adjustSizeLater() +{ + QPointer that(this); + QTimer::singleShot(0, this, [=](){ + if(that == nullptr) { + return ; + } + adjustSize(); + }); +} + +QString QKxCombineKeyActionDialog::combineKey() +{ + QString ckey = ui->key->text(); + ckey+= " "; + if(ui->radShiftP->isChecked()) { + ckey += "+Shift"; + }else if(ui->radShiftN->isChecked()) { + ckey += "-Shift"; + } + if(ui->radCtrlP->isChecked()) { +#if defined (Q_OS_MAC) || SIMULATE_MAC_KEY + ckey += "+Control"; +#else + ckey += "+Ctrl"; +#endif + }else if(ui->radCtrlN->isChecked()) { +#if defined (Q_OS_MAC) || SIMULATE_MAC_KEY + ckey += "-Control"; +#else + ckey += "-Ctrl"; +#endif + } + if(ui->radAltP->isChecked()) { +#if defined (Q_OS_MAC) || SIMULATE_MAC_KEY + ckey += "+Option"; +#else + ckey += "+Alt"; +#endif + }else if(ui->radAltN->isChecked()) { +#if defined (Q_OS_MAC) || SIMULATE_MAC_KEY + ckey += "-Option"; +#else + ckey += "-Alt"; +#endif + } + if(ui->radMetaP->isChecked()) { +#if defined (Q_OS_MAC) || SIMULATE_MAC_KEY + ckey += "+Command"; +#else + ckey += "+Meta"; +#endif + }else if(ui->radMetaN->isChecked()) { +#if defined (Q_OS_MAC) || SIMULATE_MAC_KEY + ckey += "-Command"; +#else + ckey += "-Meta"; +#endif + } + + if(ui->radAnsiP->isChecked()) { + ckey += "+Ansi"; + }else if(ui->radAnsiN->isChecked()) { + ckey += "-Ansi"; + } + if(ui->radNewLineP->isChecked()) { + ckey += "+NewLine"; + }else if(ui->radNewLineN->isChecked()) { + ckey += "-NewLine"; + } + if(ui->radAppCuKeysP->isChecked()) { + ckey += "+AppCuKeys"; + }else if(ui->radAppCuKeysN->isChecked()) { + ckey += "-AppCuKeys"; + } + if(ui->radAppScreenP->isChecked()) { + ckey += "+AppScreen"; + }else if(ui->radAppScreenN->isChecked()) { + ckey += "-AppScreen"; + } + if(ui->radAppKeyPadP->isChecked()) { + ckey += "+AppKeyPad"; + }else if(ui->radAppKeyPadN->isChecked()) { + ckey += "-AppKeyPad"; + } + return ckey; +} + +void QKxCombineKeyActionDialog::setShortcut(const QKeySequence &seq) +{ + int ckey = seq[0]; + int vkey = ckey &~(Qt::MODIFIER_MASK); + QKeySequence key(vkey); + QString tmp = key.toString(); + ui->key->setText(tmp); + if(ckey & Qt::SHIFT) { + ui->radShiftP->setChecked(true); + }else{ + ui->radShiftN->setChecked(true); + } + if(ckey & Qt::CTRL) { + ui->radCtrlP->setChecked(true); + }else{ + ui->radCtrlN->setChecked(true); + } + if(ckey & Qt::ALT) { + ui->radAltP->setChecked(true); + }else{ + ui->radAltN->setChecked(true); + } + if(ckey & Qt::META) { + ui->radMetaP->setChecked(true); + }else{ + ui->radMetaN->setChecked(true); + } +} + +bool QKxCombineKeyActionDialog::eventFilter(QObject *watched, QEvent *ev) +{ + QEvent::Type t = ev->type(); + if(watched == ui->key) { + if(t == QEvent::KeyPress) { + QKeyEvent *ke = reinterpret_cast(ev); + int key = ke->key(); + if(key == Qt::Key_Shift|| + key == Qt::Key_Control|| + key == Qt::Key_Alt|| + key == Qt::Key_Meta) { + return true; + } + QKeySequence seq(key); + ui->key->setText(seq.toString()); + return true; + }else if(t == QEvent::KeyRelease) { + return true; + } + } + return false; +} diff --git a/kxterm/qkxcombinekeyactiondialog.h b/kxterm/qkxcombinekeyactiondialog.h new file mode 100644 index 0000000..fef3456 --- /dev/null +++ b/kxterm/qkxcombinekeyactiondialog.h @@ -0,0 +1,55 @@ +/******************************************************************************************* +* +* Copyright (C) 2023 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#ifndef QKXCOMBINEKEYACTIONDIALOG_H +#define QKXCOMBINEKEYACTIONDIALOG_H + + +#include "qvtedef.h" +#include "qkxkeytranslator.h" + +#include +#include + +namespace Ui { +class QKxCombineKeyActionDialog; +} + +class QTERM_EXPORT QKxCombineKeyActionDialog : public QDialog +{ + Q_OBJECT + +public: + explicit QKxCombineKeyActionDialog(QWidget *parent = nullptr); + ~QKxCombineKeyActionDialog(); + + void init(const QKxKeyTranslator::KeyInfo& ki); + QKxKeyTranslator::KeyInfo result(); +signals: + void messageArrived(const QString& title, const QString& msg); +private slots: + void onActionButtonClicked(); + void onApplyButtonClicked(); + void onCombineKeyChanged(); + void onQuickInput(); +private: + void adjustSizeLater(); + QString combineKey(); + void setShortcut(const QKeySequence &seq); + +private: + virtual bool eventFilter(QObject *watched, QEvent *ev); + +private: + Ui::QKxCombineKeyActionDialog *ui; +}; + +#endif // QKXCOMBINEKEYACTIONDIALOG_H diff --git a/kxterm/qkxcombinekeyactiondialog.ui b/kxterm/qkxcombinekeyactiondialog.ui new file mode 100644 index 0000000..932e325 --- /dev/null +++ b/kxterm/qkxcombinekeyactiondialog.ui @@ -0,0 +1,537 @@ + + + QKxCombineKeyActionDialog + + + + 0 + 0 + 618 + 712 + + + + Dialog + + + + + + VT mode + + + + + + + + +Ansi + + + + + + + -Ansi + + + + + + + None + + + + + + + + + + + +NewLine + + + + + + + -NewLine + + + + + + + None + + + + + + + + + + + +AppCuKeys + + + + + + + -AppCuKeys + + + + + + + None + + + + + + + + + + + +AppScreen + + + + + + + -AppScreen + + + + + + + None + + + + + + + + + + + +AppKeyPad + + + + + + + -AppKeyPad + + + + + + + None + + + + + + + + + + + + + 0 + 0 + + + + Shortcut + + + + + + + + +Shift + + + + + + + -Shift + + + + + + + None + + + + + + + + + + + +Ctrl + + + + + + + -Ctrl + + + + + + + None + + + + + + + + + + + +Alt + + + + + + + -Alt + + + + + + + None + + + + + + + + + + + +Meta + + + + + + + -Meta + + + + + + + None + + + + + + + + + + + Key: + + + + + + + + 100 + 16777215 + + + + + + + + Quick input + + + + ../private/skins/black/login.png../private/skins/black/login.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + Combine key: + + + + + + + + 0 + 0 + + + + color: rgb(224, 27, 36); + + + + + + + + + + + + Action + + + + + + + + String to remote server + + + + + + + Operation on local terminal + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Operation: + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + String: + + + + + + + + + + + + + + + + + + + 24 + 24 + + + + + + + ../private/skins/black/tips.png + + + true + + + + + + + + + [+]:Preceding a modename means the key/mode is pressed/active respectively. + + + false + + + + + + + [-]:Preceding a modename means the key/mode isn't pressed/active respectively. + + + false + + + + + + + [None]:ignore this key/mode. + + + false + + + + + + + Executing a shortcut in the specified VT mode will trigger the corresponding action. + + + false + + + + + + + + + + + Qt::Horizontal + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Apply + + + + + + + Cancel + + + + + + + + + + diff --git a/kxterm/qkxkeytranslator.cpp b/kxterm/qkxkeytranslator.cpp index 408d1fe..16bbda0 100644 --- a/kxterm/qkxkeytranslator.cpp +++ b/kxterm/qkxkeytranslator.cpp @@ -12,154 +12,187 @@ #include "qkxkeytranslator.h" #include +#include #include +#include -static QRegExp rgxTitle("keyboard\\s+\"(.*)\""); -static QRegExp rgxKey("key\\s+([\\w\\+\\s\\-\\*\\.]+)\\s*:\\s*(\"(.*)\"|\\w+)"); - QKxKeyTranslator::QKxKeyTranslator(QObject *parent) : QObject(parent) { } -QString QKxKeyTranslator::path() const -{ - return m_path; -} - QString QKxKeyTranslator::name() const { - return m_title; -} - -void QKxKeyTranslator::setName(const QString &name) -{ - m_title = name; + return m_name; } bool QKxKeyTranslator::load(const QString &file) { - QFile f(file); - if(!f.open(QFile::ReadOnly|QFile::Text)) { + m_name.clear(); + m_keys.clear(); + + QList all; + if(!load(file, all)) { return false; } + for(auto it = all.begin(); it != all.end(); it++) { + const KeyInfo& ki = *it; - while(!f.atEnd()){ - QString line = f.readLine().simplified(); - if(line.isEmpty()) { - continue; - } - if(rgxTitle.exactMatch(line)) { - m_title = rgxTitle.capturedTexts().at(1); - } else if(rgxKey.exactMatch(line)) { - QStringList lsz = rgxKey.capturedTexts(); - if(!lsz.last().isEmpty()) { - parseOutput(lsz.at(1).simplified(), lsz.at(3).simplified()); - } - } + QList lski = m_keys.take(ki.key); + lski.insert(ki.key, ki); + m_keys.insert(ki.key, lski); } + QFileInfo fi(file); + m_name = fi.fileName(); return !m_keys.isEmpty(); } -QByteArray QKxKeyTranslator::match(int key, Qt::KeyboardModifiers modifiers, VTModes states) +bool QKxKeyTranslator::match(int key, Qt::KeyboardModifiers modifiers, VTModes modes, EOperation &op, QByteArray &out) { //skip keypad effect. - modifiers &= ~Qt::KeypadModifier; - modifiers &= ~Qt::MetaModifier; + //modifiers &= ~Qt::KeypadModifier; + //modifiers &= ~Qt::MetaModifier; QList kis = m_keys.value(key); if(kis.isEmpty()) { - return QByteArray(); + return false; } QMap hit; for(int i = 0; i < kis.length(); i++) { KeyInfo ki = kis.at(i); - if((ki.mode & states) == ki.mode) { - if(modifierEqual(ki.modify, modifiers)) { - hit.insert(ki.modify, ki); + if((ki.modes & modes) == ki.modes) { + if(modifierMatch(ki.modifies, modifiers)) { + if(!ki.out.isEmpty() || !hit.contains(ki.modifies)){ + // make sure the ansi control sequence has more proirity. + hit.insert(ki.modifies, ki); + } } } } if(hit.isEmpty()) { - return QByteArray(); + return false; } - return hit.last().out; + KeyInfo& ki = hit.last(); + op = ki.op; + out = ki.out; + return true; } -bool QKxKeyTranslator::parseKey(const QString &key, KeyInfo &ki) +QKeySequence QKxKeyTranslator::shortcut(EOperation op, VTModes modes) { - QStringList tokens = tokenArray(key); - qDebug() << key << tokens; - QString code = tokens.takeFirst(); - QKeySequence ks = QKeySequence::fromString(code); - ki.key = ks[0]; - ModifyKeys modify = 0; - VTModes mode = 0; - while(!tokens.isEmpty()) { - QString t1 = tokens.takeFirst().toLower(); - bool wanted = t1.at(0) == '+'; - QString t = t1.mid(1); - if(t == "shift") { - modify |= wanted ? MK_SHIFT_YES : MK_SHIFT_NO; - }else if(t == "alt") { - modify |= wanted ? MK_ALT_YES : MK_ALT_NO; - }else if(t == "ctrl" || t == "control") { - modify |= wanted ? MK_CTRL_YES : MK_CTRL_NO; - }else if(t == "meta") { - modify |= wanted ? MK_META_YES : MK_META_NO; - }else if(t == "anymod" || t == "anymodifier") { - modify |= wanted ? MK_ANY_YES : MK_ANY_NO; - }else if(t == "keypad") { - modify |= wanted ? MK_KEYPAD_YES : MK_KEYPAD_NO; - }else if(t == "appcukeys" || t == "appcursorkeys") { - mode |= wanted ? VM_APPCUKEY_YES : VM_APPCUKEY_NO; - }else if(t == "ansi") { - mode |= wanted ? VM_ANSI_YES : VM_ANSI_NO; - }else if(t == "newline") { - mode |= wanted ? VM_NEWLINE_YES : VM_NEWLINE_NO; - }else if(t == "appscreen") { - mode |= wanted ? VM_APPSCREEN_YES : VM_APPSCREEN_NO; - }else if(t == "appkeypad") { - mode |= wanted ? VM_APPKEYPAD_YES : VM_APPKEYPAD_NO; + for(auto it = m_keys.begin(); it != m_keys.end(); it++) { + QList kis = m_keys.value(it.key()); + if(kis.isEmpty()) { + return false; + } + for(int i = 0; i < kis.length(); i++) { + KeyInfo ki = kis.at(i); + if((ki.modes & modes) == ki.modes) { + if(ki.op == op) { + int key = ki.key; + if(ki.modifies & MK_CTRL_YES) { + key += Qt::CTRL; + } + if(ki.modifies & MK_SHIFT_YES) { + key += Qt::SHIFT; + } + if(ki.modifies & MK_ALT_YES) { + key += Qt::ALT; + } + if(ki.modifies & MK_META_YES) { + key += Qt::META; + } + return QKeySequence(key); + } + } } } - ki.raw = key.toUtf8(); - ki.modify = modify; - ki.mode = mode; - return true; + return QKeySequence(); } -QStringList QKxKeyTranslator::tokenArray(const QString &keys) +QString QKxKeyTranslator::operationToName(EOperation op) { - QStringList all; - QString part; - for(int i = 0; i < keys.length(); i++) { - QChar ch = keys.at(i); - if(ch == QChar::Space || ch == '-' || ch == '+') { - part = part.simplified(); - if(!part.isEmpty()) { - all.append(part); - } - part.clear(); - } - part.append(ch); + switch (op) { + case ECopy: + return "copy"; + case EPaste: + return "paste"; + case ESelectLineUp: + return "selectLineUp"; + case ESelectLineDown: + return "selectLineDown"; + case ESelectLineLeft: + return "selectLineLeft"; + case ESelectLineRight: + return "selectLineRight"; + case ESelectLineHome: + return "selectLineHome"; + case ESelectLineEnd: + return "selectLineEnd"; + case ESelectAll: + return "selectAll"; + case EScrollLineUp: + return "scrollLineUp"; + case EScrollPageUp: + return "scrollPageUp"; + case EScrollUpToTop: + return "scrollUpToTop"; + case EScrollLineDown: + return "scrollLineDown"; + case EScrollPageDown: + return "scrollPageDown"; + case EScrollDownToBottom: + return "scrollDownToBottom"; + case EFind: + return "find"; + default: + return ""; } - if(!part.isEmpty()) { - all.append(part); - } - return all; } -void QKxKeyTranslator::parseOutput(const QString &key, const QString &output) +QKxKeyTranslator::EOperation QKxKeyTranslator::operationFromName(const QString &code) { - //qDebug() << "output" << key << output; - KeyInfo ki; - if(!parseKey(key, ki)) { - return; + if(code == "scrollLineUp") { + return EScrollLineUp; + }else if(code == "scrollPageUp") { + return EScrollPageUp; + }else if(code == "scrollUpToTop") { + return EScrollUpToTop; + }else if(code == "scrollLineDown") { + return EScrollLineDown; + }else if(code == "scrollPageDown") { + return EScrollPageDown; + }else if(code == "scrollDownToBottom") { + return EScrollDownToBottom; + }else if(code == "selectLineUp") { + return ESelectLineUp; + }else if(code == "selectLineDown") { + return ESelectLineDown; + }else if(code == "selectLineLeft") { + return ESelectLineLeft; + }else if(code == "selectLineRight") { + return ESelectLineRight; + }else if(code == "selectLineHome") { + return ESelectLineHome; + }else if(code == "selectLineEnd") { + return ESelectLineEnd; + }else if(code == "selectAll") { + return ESelectAll; + }else if(code == "copy") { + return ECopy; + }else if(code == "paste") { + return EPaste; + }else if(code == "find") { + return EFind; } + return ENotDefined; +} + +QByteArray QKxKeyTranslator::stringToAnsiSequence(const QString &code) +{ QByteArray out; - QByteArray left = output.toLatin1(); + QByteArray left = code.toLatin1(); while(left.length() > 0) { char ch = left.at(0); if(ch != '\\') { @@ -203,20 +236,235 @@ void QKxKeyTranslator::parseOutput(const QString &key, const QString &output) break; } } + return out; +} +bool QKxKeyTranslator::load(const QString &file, QList &all) +{ + QFile f(file); + if(!f.open(QFile::ReadOnly|QFile::Text)) { + return false; + } + + //static const QRegularExpression rgxTitle(R"(keyboard\s+\"(.*)\")"); + static const QRegularExpression rgxKey(R"(key\s+(.+?)\s*:\s*(\"(.*)\"|\w+))"); + + while(!f.atEnd()){ + QString line = f.readLine(); + if(line.isEmpty()) { + continue; + } + line = removeComment(line); + QRegularExpressionMatch key = rgxKey.match(line); + if(key.hasMatch()) { + QString k = key.captured(1); + QString w = key.captured(2); + QString v = key.captured(3); + //QStringView vt = key.capturedView(3); + //qDebug() << "QStringView" << vt; + if(v.isEmpty()) { + // shortcut map to + parseOperation(k, w, all); + }else{ + // key map to + parseString(k, v, all); + } + } + } + return !all.isEmpty(); +} + +bool QKxKeyTranslator::save(const QString &filePath, const QString &fileTemplate, const QList &keys) +{ + QFile fread(fileTemplate); + if(!fread.open(QFile::ReadOnly)) { + return false; + } + int cLength = maxMaxConditionLength(keys); + QByteArray tempSave = fread.readAll(); + QByteArrayList lineAll; + for(auto it = keys.begin(); it != keys.end(); it++) { + const QKxKeyTranslator::KeyInfo& ki = *it; + QByteArray action = ki.out.isEmpty() ? ki.action.toUtf8() : ("\"" + ki.action.toUtf8() + "\""); + QByteArray condition = ki.condition.toUtf8(); + condition = condition.append(cLength+20, QChar::Space); + condition.resize(cLength+10); + QByteArray line = "key " + condition + " : " + action; + lineAll.append(line); + } + fread.close(); + + QFile fsave(filePath); + if(!fsave.open(QFile::WriteOnly)) { + qInfo() << "save failure:" << fsave.errorString(); + return false; + } + QFileInfo fi(fsave); + QByteArray content = lineAll.join("\r\n"); + tempSave = tempSave.replace("{{name}}", fi.baseName().toUtf8()); + QByteArray keysAll = tempSave.replace("{{key}}", content); + fsave.write(keysAll); + return true; +} + +int QKxKeyTranslator::maxMaxConditionLength(const QList &keys) +{ + int count = 0; + for(auto it = keys.begin(); it != keys.end(); it++) { + const QKxKeyTranslator::KeyInfo& ki = *it; + if(ki.condition.length() > count) { + count = ki.condition.length(); + } + } + return count; +} + +QMap QKxKeyTranslator::operationDescriptions() +{ + static QMap descs; + if(descs.isEmpty()) { + descs.insert(ECopy, tr("Copy")); + descs.insert(EPaste, tr("Paste")); + descs.insert(EFind, tr("Find")); + descs.insert(ESelectLineUp, tr("Select to the previous row")); + descs.insert(ESelectLineDown, tr("Select to the next row")); + descs.insert(ESelectLineLeft, tr("Select to the left of the row")); + descs.insert(ESelectLineRight, tr("Select to the right of the row")); + descs.insert(ESelectLineHome, tr("Select to the beginning of row")); + descs.insert(ESelectLineEnd, tr("Select to the end of row")); + descs.insert(ESelectAll, tr("Select all")); + descs.insert(EScrollLineUp, tr("Scroll up one row")); + descs.insert(EScrollPageUp, tr("Scroll up one page")); + descs.insert(EScrollUpToTop, tr("Scroll to the top")); + descs.insert(EScrollLineDown, tr("Scroll down one row")); + descs.insert(EScrollPageDown, tr("Scroll down one page")); + descs.insert(EScrollDownToBottom, tr("Scroll down to bottom")); + } + return descs; +} + +QString QKxKeyTranslator::removeComment(const QString &_line) +{ + QString line = _line; + bool inQuotes = false; + int pos = -1; + for (int i = line.length() - 1; i >= 0; i--) { + QChar ch = line[i]; + if (ch == '\"') { + inQuotes = !inQuotes; + } else if (ch == '#' && !inQuotes) { + pos = i; + } + } + + if (pos != -1) { + line = line.left(pos); + } + line = line.simplified(); + return line; +} + +void QKxKeyTranslator::parseOperation(const QString &key, const QString &code, QList& all) +{ + KeyInfo ki; + if(!parseKey(key, ki)) { + return; + } + ki.op = operationFromName(code); + if(ki.op == ENotDefined) { + return; + } + ki.action = code; + all.append(ki); +} + +void QKxKeyTranslator::parseString(const QString &key, const QString &code, QList& all) +{ + KeyInfo ki; + if(!parseKey(key, ki)) { + return; + } + QByteArray out = stringToAnsiSequence(code); + if(out.isEmpty()) { + return; + } + ki.action = code; ki.out = out; - QList lki = m_keys.value(ki.key); - lki.append(ki); - m_keys.insert(ki.key, lki); + all.append(ki); +} + +bool QKxKeyTranslator::parseKey(const QString &key, KeyInfo &ki) +{ + QStringList tokens = tokenArray(key); + //qDebug() << key << tokens; + QString code = tokens.takeFirst(); + QKeySequence ks = QKeySequence::fromString(code); + ki.key = ks[0]; + ModifyKeys modifies = 0; + VTModes modes = 0; + while(!tokens.isEmpty()) { + QString t1 = tokens.takeFirst().toLower(); + bool wanted = t1.at(0) == '+'; + QString t = t1.mid(1); + if(t == "shift") { + modifies |= wanted ? MK_SHIFT_YES : MK_SHIFT_NO; + }else if(t == "alt" || t == "option") { + modifies |= wanted ? MK_ALT_YES : MK_ALT_NO; + }else if(t == "ctrl" || t == "control") { + modifies |= wanted ? MK_CTRL_YES : MK_CTRL_NO; + }else if(t == "meta" || t == "command") { + modifies |= wanted ? MK_META_YES : MK_META_NO; + }else if(t == "anymod" || t == "anymodifier") { + modifies |= wanted ? MK_ANY_YES : MK_ANY_NO; + }else if(t == "keypad") { + modifies |= wanted ? MK_KEYPAD_YES : MK_KEYPAD_NO; + }else if(t == "appcukeys" || t == "appcursorkeys") { + modes |= wanted ? VM_APPCUKEY_YES : VM_APPCUKEY_NO; + }else if(t == "ansi") { + modes |= wanted ? VM_ANSI_YES : VM_ANSI_NO; + }else if(t == "newline") { + modes |= wanted ? VM_NEWLINE_YES : VM_NEWLINE_NO; + }else if(t == "appscreen") { + modes |= wanted ? VM_APPSCREEN_YES : VM_APPSCREEN_NO; + }else if(t == "appkeypad") { + modes |= wanted ? VM_APPKEYPAD_YES : VM_APPKEYPAD_NO; + } + } + ki.condition = key; + ki.modifies = modifies; + ki.modes = modes; + return true; +} + +QStringList QKxKeyTranslator::tokenArray(const QString &keys) +{ + QStringList all; + QString part; + for(int i = 0; i < keys.length(); i++) { + QChar ch = keys.at(i); + if(ch == QChar::Space || ch == '-' || ch == '+') { + part = part.simplified(); + if(!part.isEmpty()) { + all.append(part); + } + part.clear(); + } + part.append(ch); + } + if(!part.isEmpty()) { + all.append(part); + } + return all; } -bool QKxKeyTranslator::modifierEqual(ModifyKeys mk, Qt::KeyboardModifiers modifier) +bool QKxKeyTranslator::modifierMatch(ModifyKeys mk, Qt::KeyboardModifiers modifier) { ModifyKeys flags; - int anyModify = Qt::ControlModifier|Qt::ShiftModifier|Qt::AltModifier; + int anyModify = Qt::ControlModifier|Qt::ShiftModifier|Qt::AltModifier|Qt::MetaModifier; flags |= modifier & Qt::ControlModifier ? MK_CTRL_YES : MK_CTRL_NO; flags |= modifier & Qt::ShiftModifier ? MK_SHIFT_YES : MK_SHIFT_NO; flags |= modifier & Qt::AltModifier ? MK_ALT_YES : MK_ALT_NO; + flags |= modifier & Qt::MetaModifier ? MK_META_YES : MK_META_NO; flags |= modifier & anyModify ? MK_ANY_YES : MK_ANY_NO; return (mk & flags) == mk; diff --git a/kxterm/qkxkeytranslator.h b/kxterm/qkxkeytranslator.h index f621439..a4e8e1b 100644 --- a/kxterm/qkxkeytranslator.h +++ b/kxterm/qkxkeytranslator.h @@ -12,15 +12,68 @@ #ifndef QKEYTRANSLATOR_H #define QKEYTRANSLATOR_H +#include "qvtedef.h" + #include #include #include #include -class QKxKeyTranslator : public QObject + +/*** + * + * "key" Keyname { ("+"|"-") Modename } ":" (String|Operation) + * + * extract from ${woterm}/private/keytabs/keyinfo.txt. + * ${woterm}/private/keytabs/README-KeyTab + * 需要认真理解keyinfo.txt和README-KeyTab的描述。 + * 需要区分Ansi模式,VT52 escape和ANSI escape的区别。 + * 需要区分AnyMod和Alt, Ctrl, Shift的区别。 + * 需要区分NewLine和发送\r\n和\r的关系。 + * + * + * AnyModifier: If this mode is set, the key combination uses any modifier key (any of the previous three modifier keys: alt/ctrl/shift); and vice versa if it's reset + * Ansi: If this mode is set, &konsole; will send ANSI escape and control sequences + * If this mode is reset &konsole; will send VT52 escape and control sequences + * AppScreen: If this mode is set, the key combination will only affect interactive programs that use the Alternate Screen buffer, like vim. + * &konsole; makes use of two screen buffers: + * The Normal Screen buffer (default): allows you to scroll back to view previous lines of output, this is the default buffer you usually use to execute commands... &etc; + * The Alternate Screen buffer: the terminal switches to this buffer when you run an interactive program like vim tumx etc. + * KeyPad: If this mode is set, the key combination uses a key on the Keypad (Number Pad),This mode is useful to distinguish between keys on the keyboard and keys on the Keypad + * AppCursorKeys: DECCKM. + * AppKeyPad: DECKPAM + * + * + * + * + **/ + + + +class QTERM_EXPORT QKxKeyTranslator : public QObject { Q_OBJECT -public: +public: + enum EOperation { + ENotDefined = 0, + ECopy, + EPaste, + ESelectLineUp, + ESelectLineDown, + ESelectLineLeft, + ESelectLineRight, + ESelectLineHome, + ESelectLineEnd, + ESelectAll, + EScrollLineUp, + EScrollPageUp, + EScrollUpToTop, + EScrollLineDown, + EScrollPageDown, + EScrollDownToBottom, + EFind + }; + enum VTMode { //YES VM_ANSI_YES = 1 << 1, // vt100/vt102 is Ansi mode,but vt52 not Ansi mode. @@ -37,6 +90,7 @@ class QKxKeyTranslator : public QObject }; Q_DECLARE_FLAGS(VTModes, VTMode) Q_FLAGS(VTModes) + Q_ENUM(EOperation) enum ModifyKey { //YES @@ -55,32 +109,62 @@ class QKxKeyTranslator : public QObject MK_KEYPAD_NO = 1 << 13 }; Q_DECLARE_FLAGS(ModifyKeys, ModifyKey) - Q_FLAGS(ModifyKeys) + Q_FLAGS(ModifyKeys) - typedef struct KeyInfo { - QByteArray raw; - ModifyKeys modify; - VTModes mode; - QByteArray out; + struct KeyInfo { + QString condition; + QString action; + + ModifyKeys modifies; + VTModes modes; int key; - } KeyInfo; + // operation or key. + QByteArray out; + EOperation op; + + KeyInfo() { + op = ENotDefined; + modes = 0; + modifies = 0; + } + + friend bool operator==(const KeyInfo& ki, const KeyInfo& hit) { + return ki.action == hit.action + && ki.condition == hit.condition + && ki.modifies == hit.modifies + && ki.modes == hit.modes + && ki.key == hit.key + && ki.out == hit.out + && ki.op == hit.op; + } + }; public: explicit QKxKeyTranslator(QObject *parent = nullptr); - QString path() const; QString name() const; - void setName(const QString& name); bool load(const QString& file); - QByteArray match(int key, Qt::KeyboardModifiers modifiers, VTModes modes); -private: - bool parseKey(const QString&key, KeyInfo &ki); - QStringList tokenArray(const QString& keys); - void parseOutput(const QString& key, const QString& output); - bool modifierEqual(ModifyKeys mk, Qt::KeyboardModifiers modifier); + bool match(int key, Qt::KeyboardModifiers modifiers, VTModes modes, EOperation &op, QByteArray &out); + + QKeySequence shortcut(EOperation op, VTModes modes=0); + + static QString operationToName(EOperation op); + static EOperation operationFromName(const QString& name); + static QByteArray stringToAnsiSequence(const QString& code); + static QMap operationDescriptions(); + static QString removeComment(const QString& line); + static void parseOperation(const QString& key, const QString& code, QList& all); + static void parseString(const QString&key, const QString& code, QList& all); + static bool parseKey(const QString&key, KeyInfo &ki); + static QStringList tokenArray(const QString& keys); + static bool modifierMatch(ModifyKeys mk, Qt::KeyboardModifiers modifier); + + static bool load(const QString& file, QList& all); + static bool save(const QString& file, const QString& fileTemplate, const QList& all); + static int maxMaxConditionLength(const QList& all); private: - QString m_path; - QString m_title; + QString m_name; QMap> m_keys; + }; #endif // QKEYTRANSLATOR_H diff --git a/kxterm/qkxkeytranslatormodel.cpp b/kxterm/qkxkeytranslatormodel.cpp new file mode 100644 index 0000000..4f636f3 --- /dev/null +++ b/kxterm/qkxkeytranslatormodel.cpp @@ -0,0 +1,216 @@ +/******************************************************************************************* +* +* Copyright (C) 2023 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qkxkeytranslatormodel.h" +#include "qkxutils.h" + +#include +#include +#include +#include +#include +#include +#include + +QKxKeyTranslatorModel::QKxKeyTranslatorModel(const QString &pathPrv, const QFont& font, QObject *parent) + : QAbstractListModel(parent) + , m_font(font) + , m_hasModified(false) +{ + m_pathPrivate = QDir::cleanPath(pathPrv); + m_opIcon = QIcon("../private/skins/black/operation.png"); + m_strIcon = QIcon("../private/skins/black/string.png"); +} + +QString QKxKeyTranslatorModel::name() const +{ + QFileInfo fi(m_filePath); + return fi.fileName(); +} + +bool QKxKeyTranslatorModel::save(const QString& fileTemplate) +{ + if(m_filePath.startsWith(m_pathPrivate)) { + return false; + } + if(!QKxKeyTranslator::save(m_filePath, fileTemplate, m_keys)) { + return false; + } + m_hasModified = false; + return true; +} + +bool QKxKeyTranslatorModel::load(const QString &name) +{ + QString path = QKxUtils::keytabPath(name); + m_keys.clear(); + beginResetModel(); + bool ok = QKxKeyTranslator::load(path, m_keys); + mysort(); + endResetModel(); + m_filePath = path; + m_hasModified = false; + return ok; +} + +bool QKxKeyTranslatorModel::hasModified() +{ + return m_hasModified; +} + +QModelIndex QKxKeyTranslatorModel::modify(const QModelIndex& idx, QKxKeyTranslator::KeyInfo &ki) +{ + if(!idx.isValid()) { + return idx; + } + int row = idx.row(); + beginResetModel(); + m_keys[row] = ki; + mysort(); + int i = 0; + for(i = 0; i < m_keys.length(); i++) { + const QKxKeyTranslator::KeyInfo& hit = m_keys.at(i); + if(hit == ki) { + break; + } + } + endResetModel(); + m_hasModified = true; + return index(i); +} + +QModelIndex QKxKeyTranslatorModel::add(QKxKeyTranslator::KeyInfo &ki) +{ + beginResetModel(); + m_keys.append(ki); + mysort(); + int i = 0; + for(i = 0; i < m_keys.length(); i++) { + const QKxKeyTranslator::KeyInfo& hit = m_keys.at(i); + if(hit == ki) { + break; + } + } + endResetModel(); + m_hasModified = true; + return index(i); +} + +void QKxKeyTranslatorModel::remove(const QModelIndex &idx) +{ + if(!idx.isValid()) { + return; + } + int row = idx.row(); + beginResetModel(); + m_keys.removeAt(row); + mysort(); + endResetModel(); + m_hasModified = true; +} + +bool QKxKeyTranslatorModel::data(int idx, QKxKeyTranslator::KeyInfo &ki) const +{ + if(idx < 0 || idx >= m_keys.length()) { + return false; + } + ki = m_keys.at(idx); + return true; +} + +QVariant QKxKeyTranslatorModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(role == Qt::DisplayRole) { + switch (section) { + case 0: + return tr("Combine key"); + case 1: + return tr("Action"); + } + } + return QAbstractListModel::headerData(section, orientation, role); +} + +int QKxKeyTranslatorModel::rowCount(const QModelIndex &parent) const +{ + // For list models only the root node (an invalid parent) should return the list's size. For all + // other (valid) parents, rowCount() should return 0 so that it does not become a tree model. + if (parent.isValid()) { + return 0; + } + + + return m_keys.length(); +} + +QVariant QKxKeyTranslatorModel::data(const QModelIndex &idx, int role) const +{ + int row = idx.row(); + int column = idx.column(); + if (!idx.isValid() || row < 0 || row >= m_keys.length()) { + return QVariant(); + } + + const QKxKeyTranslator::KeyInfo& ki = m_keys.at(row); + if(role == Qt::DisplayRole) { + if(column == 0) { + return ki.condition; + }else if(column == 1) { + auto all = QKxKeyTranslator::operationDescriptions(); + if(ki.out.isEmpty()) { + return all.value(ki.op); + } + return ki.action; + } + return QVariant(); + }else if(role == Qt::DecorationRole) { + if(column == 0) { + return ki.out.isEmpty() ? m_opIcon : m_strIcon; + } + return QVariant(); + } + if(role == Qt::SizeHintRole) { + QFontMetrics fm(m_font); + if(column == 0) { + int w = fm.width(ki.condition) + 50; + return QSize(w, 28); + } + if(column == 1) { + int w = fm.width(ki.action) + 30; + return QSize(w, 28); + } + return QVariant(); + } + return QVariant(); +} + +int QKxKeyTranslatorModel::columnCount(const QModelIndex &parent) const +{ + return 2; +} + +void QKxKeyTranslatorModel::mysort() +{ + std::sort(m_keys.begin(), m_keys.end(), [=](const QKxKeyTranslator::KeyInfo& v1, const QKxKeyTranslator::KeyInfo& v2){ + if(v1.out.isEmpty() && v2.out.isEmpty()) { + return v1.op < v2.op; + }else if(!v1.out.isEmpty() && !v2.out.isEmpty()) { + int vkey1 = v1.key + (v1.out.isEmpty() ? 0 : 1 << 30); + int vkey2 = v2.key + (v2.out.isEmpty() ? 0 : 1 << 30); + QString key1 = QString::number(vkey1) + v1.out; + QString key2 = QString::number(vkey2) + v2.out; + return key1 < key2; + } + int vkey1 = v1.key + (v1.out.isEmpty() ? 0 : 1 << 30); + int vkey2 = v2.key + (v2.out.isEmpty() ? 0 : 1 << 30); + return vkey1 < vkey2; + }); +} diff --git a/kxterm/qkxkeytranslatormodel.h b/kxterm/qkxkeytranslatormodel.h new file mode 100644 index 0000000..914f98b --- /dev/null +++ b/kxterm/qkxkeytranslatormodel.h @@ -0,0 +1,62 @@ +/******************************************************************************************* +* +* Copyright (C) 2023 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#ifndef QKXKEYTRANSLATORMODEL_H +#define QKXKEYTRANSLATORMODEL_H + +#include "qvtedef.h" +#include "qkxkeytranslator.h" +#include +#include +#include +#include + +class QTERM_EXPORT QKxKeyTranslatorModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit QKxKeyTranslatorModel(const QString& pathPrv, const QFont& font, QObject *parent = nullptr); + + QString name() const; + bool save(const QString& fileTemplate); + bool load(const QString& name = DEFAULT_KEY_TRANSLATOR); + int count() { + return m_keys.length(); + }; + + bool hasModified(); + QModelIndex modify(const QModelIndex& idx, QKxKeyTranslator::KeyInfo& ki); + QModelIndex add(QKxKeyTranslator::KeyInfo& ki); + void remove(const QModelIndex& idx); + bool data(int idx, QKxKeyTranslator::KeyInfo& ki) const; +protected: + // Header: + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + // Basic functionality: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + int columnCount(const QModelIndex &parent) const override; +private: + void mysort(); +private: + QList m_keys; + QString m_filePath; + QString m_pathPrivate; + QFont m_font; + QIcon m_opIcon, m_strIcon; + bool m_hasModified; +}; + +#endif // QKXKEYTRANSLATORMODEL_H diff --git a/kxterm/qkxshortcutinputdialog.cpp b/kxterm/qkxshortcutinputdialog.cpp new file mode 100644 index 0000000..c3d6e64 --- /dev/null +++ b/kxterm/qkxshortcutinputdialog.cpp @@ -0,0 +1,126 @@ +/******************************************************************************************* +* +* Copyright (C) 2023 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#include "qkxshortcutinputdialog.h" +#include "ui_qkxshortcutinputdialog.h" + +#include +#include + +QKxShortcutInputDialog::QKxShortcutInputDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::QKxShortcutInputDialog) +{ + ui->setupUi(this); + setWindowTitle(tr("Shortcut input")); + QObject::connect(ui->btnCancel, SIGNAL(clicked()), this, SLOT(close())); + QObject::connect(ui->btnApply, SIGNAL(clicked()), this, SLOT(onApplyButtonClicked())); + QObject::connect(ui->shortcut, SIGNAL(keySequenceChanged(QKeySequence)), this, SLOT(onKeySequenceChanged())); + QObject::connect(ui->shortcut, SIGNAL(editingFinished()), this, SLOT(onKeySequenceChanged())); +} + +QKxShortcutInputDialog::~QKxShortcutInputDialog() +{ + delete ui; +} + +void QKxShortcutInputDialog::init(const QKeySequence &key) +{ + ui->shortcut->setKeySequence(key); +} + +QString QKxShortcutInputDialog::result() const +{ + QKeySequence key = ui->shortcut->keySequence(); + QString name = key.toString(); + QStringList all = name.split(","); + return all.last(); +} + +void QKxShortcutInputDialog::onApplyButtonClicked() +{ + QKeySequence key = ui->shortcut->keySequence(); + QString txt = key.toString(); + if(txt.isEmpty()) { + QMessageBox::information(this, tr("Parameter error"), tr("Please input a shortcut.")); + return; + } + done(QDialog::Accepted+1); +} + +void QKxShortcutInputDialog::onKeySequenceChanged() +{ + QKeySequence seq = ui->shortcut->keySequence(); + QString txt = seq.toString(); + qDebug() << "onKeySequenceChanged" << txt; + if(txt.isEmpty()) { + return; + } + QStringList all = txt.split(","); + if(all.length() > 1) { + QString last = all.last(); + QKeySequence keyLast(last.simplified()); + ui->shortcut->clear(); + ui->shortcut->setKeySequence(keyLast); + showTip(keyLast); + }else{ + showTip(seq); + } +} + +void QKxShortcutInputDialog::showTip(const QKeySequence &seq) +{ + int ckey = seq[0]; + int vkey = ckey &~(Qt::MODIFIER_MASK); + QKeySequence key(vkey); + QString tip = key.toString() + " "; + + qDebug() << "showTip" << QString::number(ckey, 16); + if(ckey & Qt::SHIFT) { + tip += "+Shift"; + }else{ + tip += "-Shift"; + } +#if defined (Q_OS_MAC) + if(ckey & Qt::CTRL) { + tip += "+Control"; + }else{ + tip += "-Control"; + } + if(ckey & Qt::ALT) { + tip += "+Option"; + }else{ + tip += "-Option"; + } + if(ckey & Qt::META) { + tip += "+Command"; + }else{ + tip += "-Command"; + } +#else + if(ckey & Qt::CTRL) { + tip += "+Ctrl"; + }else{ + tip += "-Ctrl"; + } + if(ckey & Qt::ALT) { + tip += "+Alt"; + }else{ + tip += "-Alt"; + } + if(ckey & Qt::META) { + tip += "+Meta"; + }else{ + tip += "-Meta"; + } +#endif + ui->tip->setText(tip); +} diff --git a/kxterm/qkxshortcutinputdialog.h b/kxterm/qkxshortcutinputdialog.h new file mode 100644 index 0000000..1042a61 --- /dev/null +++ b/kxterm/qkxshortcutinputdialog.h @@ -0,0 +1,40 @@ +/******************************************************************************************* +* +* Copyright (C) 2023 Guangzhou AoYiDuo Network Technology Co.,Ltd. All Rights Reserved. +* +* Contact: http://www.aoyiduo.com +* +* this file is used under the terms of the GPLv3[GNU GENERAL PUBLIC LICENSE v3] +* more information follow the website: https://www.gnu.org/licenses/gpl-3.0.en.html +* +*******************************************************************************************/ + +#ifndef QKXSHORTCUTINPUTDIALOG_H +#define QKXSHORTCUTINPUTDIALOG_H + +#include + +namespace Ui { +class QKxShortcutInputDialog; +} + +class QKxShortcutInputDialog : public QDialog +{ + Q_OBJECT + +public: + explicit QKxShortcutInputDialog(QWidget *parent = nullptr); + ~QKxShortcutInputDialog(); + + void init(const QKeySequence& key); + QString result() const; +private slots: + void onApplyButtonClicked(); + void onKeySequenceChanged(); +private: + void showTip(const QKeySequence& seq); +private: + Ui::QKxShortcutInputDialog *ui; +}; + +#endif // QKXSHORTCUTINPUTDIALOG_H diff --git a/kxterm/qkxshortcutinputdialog.ui b/kxterm/qkxshortcutinputdialog.ui new file mode 100644 index 0000000..1902602 --- /dev/null +++ b/kxterm/qkxshortcutinputdialog.ui @@ -0,0 +1,113 @@ + + + QKxShortcutInputDialog + + + + 0 + 0 + 444 + 138 + + + + Dialog + + + + + + Please directly enter the desired shortcut key on the keyboard. + + + + + + + + + Shortcut: + + + + + + + + + + + + + + + 20 + 20 + + + + + + + ../private/skins/black/tips.png + + + true + + + + + + + color: rgb(224, 27, 36); + + + + + + + + + + + + Qt::Horizontal + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Apply + + + + + + + Cancel + + + + + + + + + + diff --git a/kxterm/qkxtermitem.cpp b/kxterm/qkxtermitem.cpp index b78f224..3e6f5e9 100644 --- a/kxterm/qkxtermitem.cpp +++ b/kxterm/qkxtermitem.cpp @@ -45,6 +45,7 @@ #define REPAINT_TIMEOUT2 (40) #define BLINK_MAX_COUNT (9) + /*** * Windows only AlignBaseline can be better. * https://www.freesion.com/article/83151447592/ @@ -82,8 +83,9 @@ QKxTermItem::QKxTermItem(QWidget* parent) , m_blinkAlway(false) , m_bEchoInputEnabled(false) , m_readOnly(false) - , m_dragActived(false) + , m_dragMode(DTM_NotDefined) , m_ptDraged(-1,-1) + , m_keyRepeat(QEvent::None, 0, Qt::NoModifier) { static int idx = 0; setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -93,23 +95,13 @@ QKxTermItem::QKxTermItem(QWidget* parent) setObjectName(QString("QKxTermItem:%1").arg(idx++)); setInputEnable(true); - - m_keyCopy = QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_C); - m_keyPaste = QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_V); - m_keyUpSelect = QKeySequence(Qt::SHIFT + Qt::Key_Up); - m_keyDownSelect = QKeySequence(Qt::SHIFT + Qt::Key_Down); - m_keyLeftSelect = QKeySequence(Qt::SHIFT + Qt::Key_Left); - m_keyRightSelect = QKeySequence(Qt::SHIFT + Qt::Key_Right); - m_keyHomeSelect = QKeySequence(Qt::SHIFT + Qt::Key_Home); - m_keyEndSelect = QKeySequence(Qt::SHIFT + Qt::Key_End); - m_keySelectAll = QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_A); - initTitle(); m_vte = new QVteImpl(this); m_view = new QKxView(this); m_view->setScreen(m_vte->screen()); QObject::connect(m_vte, SIGNAL(contentChanged(bool)), this, SLOT(onContentChanged(bool))); + QObject::connect(m_view, SIGNAL(selectChanged()), this, SIGNAL(selectChanged())); QObject::connect(m_view, SIGNAL(selectChanged()), this, SLOT(onSelectChanged())); QObject::connect(m_vte, SIGNAL(screenChanged()), this, SLOT(onScreenChanged())); QObject::connect(m_vte, SIGNAL(sendData(QByteArray)), this, SIGNAL(sendData(QByteArray))); @@ -131,7 +123,7 @@ QKxTermItem::QKxTermItem(QWidget* parent) QObject::connect(m_ticker, SIGNAL(timeout()), this, SLOT(onRepaintTimeout())); QObject::connect(m_ticker2, SIGNAL(timeout()), this, SLOT(onRepaintTimeout())); - setKeyLayoutByName(DEFAULT_KEY_LAYOUT); + setKeyTranslatorByName(DEFAULT_KEY_TRANSLATOR); setColorSchema(DEFAULT_COLOR_SCHEMA); QFont ft = QFontDatabase::systemFont(QFontDatabase::FixedFont); @@ -258,15 +250,19 @@ void QKxTermItem::setReadOnly(bool on) } } -bool QKxTermItem::dragCopyAndPaste() const +QKxTermItem::DragTextMode QKxTermItem::dragTextMode() const { - return m_dragActived; + return m_dragMode; } -void QKxTermItem::setDragCopyAndPaste(bool on) +void QKxTermItem::setDragTextMode(DragTextMode mode) { - m_dragActived = on; - setAcceptDrops(on); + m_dragMode = mode; + if(mode != DTM_NotDefined) { + setAcceptDrops(true); + }else{ + setAcceptDrops(false); + } } bool QKxTermItem::isOverSelection(const QPoint &_pt) @@ -352,11 +348,6 @@ void QKxTermItem::setCursorType(const QKxTermItem::CursorType &t) m_cursorType = t; } -QString QKxTermItem::keyTable() const -{ - return m_keyTranslator->path(); -} - QCursor QKxTermItem::mouseCursor() const { return cursor(); @@ -424,41 +415,73 @@ bool QKxTermItem::scrollTo(int y) return false; } -QKxKeyTranslator* QKxTermItem::keyLayout() const +QKxKeyTranslator *QKxTermItem::keyTranslator() const { return m_keyTranslator; } -void QKxTermItem::setKeyLayoutByName(const QString &name) +void QKxTermItem::setKeyTranslator(QKxKeyTranslator *translator) { - QKxKeyTranslator *translator = QKxUtils::keyboardLayout(name); - setKeyLayout(translator); + m_keyTranslator = translator; } -void QKxTermItem::setKeyLayout(QKxKeyTranslator *translator) +QString QKxTermItem::keyTranslatorName() const { - m_keyTranslator = translator; + if(m_keyTranslator) { + return m_keyTranslator->name(); + } + return QString(); } -QString QKxTermItem::keyLayoutName() const +void QKxTermItem::setKeyTranslatorByName(const QString &name) { - return m_keyTranslator->name(); + QString path = QKxUtils::keytabPath(name); + if(path.isEmpty()) { + QString err = QString("\r\nkey translator file named of %1 is not exist").arg(name); + QMetaObject::invokeMethod(this, "parseError", Qt::QueuedConnection, Q_ARG(QByteArray, err.toUtf8())); + return; + } + + if(!loadKeyTranslator(path)) { + QString err = QString("\r\nThe file format is incorrect, please check its content and try to load the default configure."); + err += "\r\n"; + err += path; + QByteArray msg = err.toUtf8(); + QMetaObject::invokeMethod(this, "parseError", Qt::QueuedConnection, Q_ARG(QByteArray, msg)); + + QString path = QKxUtils::keytabPath(DEFAULT_KEY_TRANSLATOR); + if(QFile::exists(path)) { + loadKeyTranslator(path); + } + return; + } } QString QKxTermItem::colorSchema() const { - return m_schema; + if(m_colorSchema) { + return m_colorSchema->name(); + } + return QString(); } void QKxTermItem::setColorSchema(const QString &name) { QString path = QKxUtils::colorSchemaPath(name); - if(!path.isEmpty()) { - m_schema = name; - if(loadColorSchema(path)) { - setBackgroundColor(m_colorSchema->background()); - } + if(path.isEmpty()) { + QString err = QString("failed to load the color schema named of %1").arg(name); + QMetaObject::invokeMethod(this, "parseError", Qt::QueuedConnection, Q_ARG(QByteArray, err.toUtf8())); + return; } + if(!loadColorSchema(path)) { + QString err = QString("The file format is incorrect, please check its content."); + err += "\r\n"; + err += path; + QByteArray msg = err.toUtf8(); + QMetaObject::invokeMethod(this, "parseError", Qt::QueuedConnection, Q_ARG(QByteArray, msg)); + return; + } + setBackgroundColor(m_colorSchema->background()); } QString QKxTermItem::termName() const @@ -608,96 +631,9 @@ void QKxTermItem::simulateKeyRelease(QKeyEvent *ev) keyReleaseEvent(ev); } -void QKxTermItem::bindShortCut(QKxTermItem::ShortCutKey sck, QKeySequence key) +QStringList QKxTermItem::availableKeytabs() const { - switch(sck) - { - case SCK_Copy: - m_keyCopy = key; - break; - case SCK_Paste: - m_keyPaste = key; - break; - case SCK_SelectAll: - m_keySelectAll = key; - break; - case SCK_SelectDown: - m_keyDownSelect = key; - break; - case SCK_SelectEnd: - m_keyEndSelect = key; - break; - case SCK_SelectHome: - m_keyHomeSelect = key; - break; - case SCK_SelectLeft: - m_keyLeftSelect = key; - break; - case SCK_SelectRight: - m_keyRightSelect = key; - break; - case SCK_SelectUp: - m_keyUpSelect = key; - break; - } -} - - -QKeySequence QKxTermItem::defaultShortCutKey(QKxTermItem::ShortCutKey sck) -{ - switch(sck) - { - case SCK_Copy: - return QKeySequence(Qt::ALT + Qt::Key_C); - case SCK_Paste: - return QKeySequence(Qt::ALT + Qt::Key_V); - case SCK_SelectAll: - return QKeySequence(Qt::ALT + Qt::Key_A); - case SCK_SelectDown: - return QKeySequence(Qt::SHIFT + Qt::Key_Down); - case SCK_SelectEnd: - return QKeySequence(Qt::SHIFT + Qt::Key_End); - case SCK_SelectHome: - return QKeySequence(Qt::SHIFT + Qt::Key_Home); - case SCK_SelectLeft: - return QKeySequence(Qt::SHIFT + Qt::Key_Left); - case SCK_SelectRight: - return QKeySequence(Qt::SHIFT + Qt::Key_Right); - case SCK_SelectUp: - return QKeySequence(Qt::SHIFT + Qt::Key_Up); - } - return QKeySequence(); -} - -QKeySequence QKxTermItem::currentShortCutKey(QKxTermItem::ShortCutKey sck) -{ - switch(sck) - { - case SCK_Copy: - return m_keyCopy; - case SCK_Paste: - return m_keyPaste; - case SCK_SelectAll: - return m_keySelectAll; - case SCK_SelectDown: - return m_keyDownSelect; - case SCK_SelectEnd: - return m_keyEndSelect; - case SCK_SelectHome: - return m_keyHomeSelect; - case SCK_SelectLeft: - return m_keyLeftSelect; - case SCK_SelectRight: - return m_keyRightSelect; - case SCK_SelectUp: - return m_keyUpSelect; - } - return QKeySequence(); -} - -QStringList QKxTermItem::availableKeyLayouts() const -{ - return QKxUtils::availableKeyLayouts(); + return QKxUtils::availableKeytabs(); } QStringList QKxTermItem::availableColorSchemas() const @@ -763,6 +699,17 @@ void QKxTermItem::tryToPaste() if(clipTxt.isEmpty()) { return; } + +#define NEVER_TRY_TO_FIX_CRLF_PROBLEM_AND_LEFT_FOR_USER_FIX_OUTSIZE +#if defined(NEVER_TRY_TO_FIX_CRLF_PROBLEM_AND_LEFT_FOR_USER_FIX_OUTSIZE) + /*** + * No longer trying to convert line breaks for different platforms. + * If there are any issues, the user will fix them themselves. + ***/ + // clipTxt = clipTxt.replace("\r\n", "\n"); + pastePlainText(clipTxt); +#else + //vim / vi: it is very bad on local terminal. clipTxt = clipTxt.replace("\r\n", "\n"); clipTxt = clipTxt.replace("\r", "\n"); QStringList lines = clipTxt.split("\n"); @@ -774,6 +721,7 @@ void QKxTermItem::tryToPaste() handleKeyEvent(&ev); } pastePlainText(last); +#endif } void QKxTermItem::pastePlainText(const QString &txt) @@ -782,7 +730,8 @@ void QKxTermItem::pastePlainText(const QString &txt) if(m_echoInput != nullptr && m_bEchoInputEnabled) { m_echoInput->tryToPaste(txt); }else{ - QByteArray buf = txt.toUtf8(); + QTextCodec *m_codec = m_vte->getTextCodec(); + QByteArray buf = m_codec->fromUnicode(txt); handleSendData(buf); } } @@ -796,6 +745,11 @@ void QKxTermItem::selectAll() m_view->setSelection(m_selectStart, m_selectEnd); } +void QKxTermItem::tryToShowFindTool() +{ + emit showFindTool(); +} + void QKxTermItem::resetTermSize() { updateTermSize(); @@ -958,19 +912,21 @@ void QKxTermItem::updateTermSize(int rows, int cols) m_vte->resize(rows, cols); } -bool QKxTermItem::loadKeyLayout(const QString &path) +bool QKxTermItem::loadKeyTranslator(const QString &path) { - if(m_keyTranslator == nullptr) { - m_keyTranslator = new QKxKeyTranslator(this); + if(m_keyTranslator) { + m_keyTranslator->deleteLater(); } + m_keyTranslator = new QKxKeyTranslator(this); return m_keyTranslator->load(path); } bool QKxTermItem::loadColorSchema(const QString &path) { - if(m_colorSchema == nullptr) { - m_colorSchema = new QKxColorSchema(this); + if(m_colorSchema) { + m_colorSchema->deleteLater(); } + m_colorSchema = new QKxColorSchema(this); if(m_colorSchema->load(path)) { setBackgroundColor(m_colorSchema->background()); updateView(PF_FullScreen); @@ -1190,6 +1146,18 @@ void QKxTermItem::onSetActive() setFocus(); } +void QKxTermItem::onKeyAutoRepeat() +{ + qint64 now = QDateTime::currentMSecsSinceEpoch(); + if(now - m_timeLast > 1000) { + QTimer *timer = qobject_cast(sender()); + timer->stop(); + } + if(m_keyRepeat.type() != QEvent::None) { + handleKeyEvent(&m_keyRepeat); + } +} + void QKxTermItem::paint(QPainter *p) { if(m_image.isEmpty()) { @@ -1253,97 +1221,46 @@ void QKxTermItem::keyPressEvent(QKeyEvent *ev) m_echoInput->onKeyPressEvent(ev); return; } - int key = ev->key(); Qt::KeyboardModifiers modifier = ev->modifiers(); - if(modifier & Qt::ShiftModifier) { - key += Qt::SHIFT; - } - if(modifier & Qt::ControlModifier) { - key += Qt::CTRL; - } - if(modifier & Qt::MetaModifier) { - key += Qt::META; - } - if(modifier & Qt::AltModifier) { - key += Qt::ALT; + if(modifier == Qt::NoModifier){ + clearSelection(); } - QKeySequence seq(key); - if(seq == m_keyCopy) { - tryToCopy(); - }else if(seq == m_keyPaste) { - tryToPaste(); - }else if(seq == m_keyUpSelect){ - if(m_selectStart == QPoint(-1,-1)) { - TermCursor tc = m_view->cursor(); - m_selectStart = m_selectEnd = QPoint(tc.x, tc.y + m_scrollValue); - } - int y = m_selectEnd.y() - 1; - m_selectEnd.setY(y > 0 ? y : 0); - qDebug() << "start:" << m_selectStart << "end:" << m_selectEnd; - m_view->setSelection(m_selectStart, m_selectEnd); - }else if(seq == m_keyDownSelect){ - if(m_selectStart == QPoint(-1,-1)) { - TermCursor tc = m_view->cursor(); - m_selectStart = m_selectEnd = QPoint(tc.x, tc.y + m_scrollValue); - } - int y = m_selectEnd.y() + 1; - m_selectEnd.setY(y >= m_view->lineCount() ? m_view->lineCount() - 1 : y); - qDebug() << "start:" << m_selectStart << "end:" << m_selectEnd; - m_view->setSelection(m_selectStart, m_selectEnd); - }else if(seq == m_keyLeftSelect){ - if(m_selectStart == QPoint(-1,-1)) { - TermCursor tc = m_view->cursor(); - m_selectStart = m_selectEnd = QPoint(tc.x, tc.y + m_scrollValue); - } - int x = m_selectEnd.x() - 1; - m_selectEnd.setX(x > 0 ? x : 0); - qDebug() << "start:" << m_selectStart << "end:" << m_selectEnd; - m_view->setSelection(m_selectStart, m_selectEnd); - }else if(seq == m_keyRightSelect){ - if(m_selectStart == QPoint(-1,-1)) { - TermCursor tc = m_view->cursor(); - m_selectStart = m_selectEnd = QPoint(tc.x, tc.y + m_scrollValue); - } - int x = m_selectEnd.x() + 1; - m_selectEnd.setX(x >= m_columns ? m_columns - 1 : x); - qDebug() << "start:" << m_selectStart << "end:" << m_selectEnd; - m_view->setSelection(m_selectStart, m_selectEnd); - }else if(seq == m_keyHomeSelect){ - if(m_selectStart == QPoint(-1,-1)) { - TermCursor tc = m_view->cursor(); - m_selectStart = m_selectEnd = QPoint(tc.x, tc.y + m_scrollValue); - } - m_selectEnd.setX(0); - qDebug() << "start:" << m_selectStart << "end:" << m_selectEnd; - m_view->setSelection(m_selectStart, m_selectEnd); - }else if(seq == m_keyEndSelect){ - if(m_selectStart == QPoint(-1,-1)) { - TermCursor tc = m_view->cursor(); - m_selectStart = m_selectEnd = QPoint(tc.x, tc.y + m_scrollValue); + +#if defined (Q_OS_MAC) + if(!ev->isAutoRepeat()) { + m_keyRepeat = QKeyEvent(QEvent::None, 0, Qt::NoModifier); + if(m_keyAutoRepeat) { + m_keyAutoRepeat->stop(); } - m_selectEnd.setX(m_columns-1); - qDebug() << "start:" << m_selectStart << "end:" << m_selectEnd; - m_view->setSelection(m_selectStart, m_selectEnd); - }else if(seq == m_keySelectAll){ - m_selectStart = QPoint(0, 0); - m_selectEnd = QPoint(m_columns-1, m_view->lineCount()); - qDebug() << "start:" << m_selectStart << "end:" << m_selectEnd; - m_view->setSelection(m_selectStart, m_selectEnd); + handleKeyEvent(ev); }else{ - if(modifier == Qt::NoModifier){ - clearSelection(); + m_keyRepeat = QKeyEvent(ev->type(), ev->key(), ev->modifiers(), ev->text(), true); + if(m_keyAutoRepeat == nullptr) { + m_keyAutoRepeat = new QTimer(this); + m_keyAutoRepeat->setTimerType(Qt::PreciseTimer); + QObject::connect(m_keyAutoRepeat, SIGNAL(timeout()), this, SLOT(onKeyAutoRepeat())); } - handleKeyEvent(ev); + if(!m_keyAutoRepeat->isActive()) { + m_keyAutoRepeat->start(10); + } + m_timeLast = QDateTime::currentMSecsSinceEpoch(); } +#else + handleKeyEvent(ev); +#endif } void QKxTermItem::keyReleaseEvent(QKeyEvent *ev) -{ +{ ev->accept(); if(m_bEchoInputEnabled && m_echoInput != nullptr) { m_echoInput->onKeyReleaseEvent(ev); return; } + m_keyRepeat = QKeyEvent(QEvent::None, 0, Qt::NoModifier); + if(m_keyAutoRepeat) { + m_keyAutoRepeat->stop(); + } } void QKxTermItem::mousePressEvent(QMouseEvent *ev) @@ -1380,7 +1297,7 @@ void QKxTermItem::mousePressEvent(QMouseEvent *ev) } m_ptClicked = QPoint(-1,-1); QPoint pt = widgetPointToTermViewPosition(ev->pos()); - if(m_dragActived && m_view->isOverSelection(pt)) { + if(m_dragMode != DTM_NotDefined && m_view->isOverSelection(pt)) { m_ptDraged = ev->pos(); return; } @@ -1393,7 +1310,7 @@ void QKxTermItem::mouseMoveEvent(QMouseEvent *ev) { if(ev->buttons() & Qt::LeftButton) { QPoint pt = ev->pos(); - if(m_dragActived && m_ptDraged != QPoint(-1,-1)) { + if(m_dragMode != DTM_NotDefined && m_ptDraged != QPoint(-1,-1)) { int x = qAbs(pt.x() - m_ptDraged.x()); int y = qAbs(pt.y() - m_ptDraged.y()); if( x < 5 && y < 5) { @@ -1404,8 +1321,10 @@ void QKxTermItem::mouseMoveEvent(QMouseEvent *ev) drag.setMimeData(mimeData); drag.exec(Qt::CopyAction|Qt::MoveAction); if(!txtSel.isEmpty()) { - QClipboard *clip = QGuiApplication::clipboard(); - clip->setText(txtSel); + if(m_dragMode == DTM_DragCopyAndPaste) { + QClipboard *clip = QGuiApplication::clipboard(); + clip->setText(txtSel); + } handleSendData(txtSel.toUtf8()); } m_ptDraged = QPoint(-1,-1); @@ -1427,7 +1346,7 @@ void QKxTermItem::mouseMoveEvent(QMouseEvent *ev) void QKxTermItem::mouseReleaseEvent(QMouseEvent *ev) { QPoint pt = ev->pos(); - if(m_dragActived && pt == m_ptDraged) { + if(m_dragMode != DTM_NotDefined && pt == m_ptDraged) { clearSelection(); } m_ptDraged = QPoint(-1,-1); @@ -1443,6 +1362,8 @@ void QKxTermItem::mouseReleaseEvent(QMouseEvent *ev) case Qt::RightButton: key = 2; break; + default: + break; } int xoffset = pt.x() - m_ptClicked.x(); @@ -1842,11 +1763,13 @@ void QKxTermItem::updateImage() dst.cs.resize(col); } if(src.xcur >= 0) { - QRect rt(src.xcur * m_fontWidth, r * (m_fontHeight + m_spaceLine + m_lineWidth), m_fontWidth, m_fontHeight + m_spaceLine + m_lineWidth); + const TermChar& c = src.cs.at(src.xcur); + QRect rt(src.xcur * m_fontWidth, r * (m_fontHeight + m_spaceLine + m_lineWidth), m_fontWidth * c.count, m_fontHeight + m_spaceLine + m_lineWidth); clip |= rt.adjusted(-2, -2, 2, 2); } if(dst.xcur >= 0) { - QRect rt(dst.xcur * m_fontWidth, r * (m_fontHeight + m_spaceLine + m_lineWidth), m_fontWidth, m_fontHeight + m_spaceLine + m_lineWidth); + const TermChar& c = dst.cs.at(dst.xcur); + QRect rt(dst.xcur * m_fontWidth, r * (m_fontHeight + m_spaceLine + m_lineWidth), m_fontWidth * c.count, m_fontHeight + m_spaceLine + m_lineWidth); clip |= rt.adjusted(-2, -2, 2, 2); } int y = r * (m_fontHeight + m_spaceLine + m_lineWidth); @@ -2070,18 +1993,102 @@ void QKxTermItem::handleKeyEvent(QKeyEvent *ev) { unsigned int states = m_vte->states(); QKxKeyTranslator::VTModes vtmode; - vtmode |= states & TF_DECANM ? QKxKeyTranslator::VM_ANSI_NO : QKxKeyTranslator::VM_ANSI_YES; + vtmode |= states & TF_DECANM ? QKxKeyTranslator::VM_ANSI_YES : QKxKeyTranslator::VM_ANSI_NO; vtmode |= states & TF_LNM ? QKxKeyTranslator::VM_NEWLINE_YES : QKxKeyTranslator::VM_NEWLINE_NO; vtmode |= states & TF_DECCKM ? QKxKeyTranslator::VM_APPCUKEY_YES : QKxKeyTranslator::VM_APPCUKEY_NO; vtmode |= states & TF_SCREEN ? QKxKeyTranslator::VM_APPSCREEN_YES : QKxKeyTranslator::VM_APPSCREEN_NO; vtmode |= states & TF_DECKPAM ? QKxKeyTranslator::VM_APPKEYPAD_YES : QKxKeyTranslator::VM_APPKEYPAD_NO; int key = ev->key(); Qt::KeyboardModifiers modifiers = ev->modifiers(); - QByteArray buf = m_keyTranslator->match(key, modifiers, vtmode); - if(!buf.isEmpty()) { + + //if(modifiers & Qt::MetaModifier && key == Qt::Key_C) { + // qDebug() << modifiers << ev->key(); + //} + + QByteArray buf; + QKxKeyTranslator::EOperation op = QKxKeyTranslator::ENotDefined; + m_keyTranslator->match(key, modifiers, vtmode, op, buf); + + // operation first. + if(op == QKxKeyTranslator::ECopy) { + tryToCopy(); + }else if(op == QKxKeyTranslator::EPaste) { + tryToPaste(); + }else if(op == QKxKeyTranslator::EFind){ + tryToShowFindTool(); + }else if(op == QKxKeyTranslator::EScrollLineUp) { + scrollLine(-3); + }else if(op == QKxKeyTranslator::EScrollPageUp) { + scrollPage(-1); + }else if(op == QKxKeyTranslator::EScrollUpToTop) { + scrollToTop(); + }else if(op == QKxKeyTranslator::EScrollLineDown) { + scrollLine(3); + }else if(op == QKxKeyTranslator::EScrollPageDown) { + scrollPage(1); + }else if(op == QKxKeyTranslator::EScrollDownToBottom) { + scrollToBottom(); + }else if(op == QKxKeyTranslator::ESelectLineUp) { + if(m_selectStart == QPoint(-1,-1)) { + TermCursor tc = m_view->cursor(); + m_selectStart = m_selectEnd = QPoint(tc.x, tc.y + m_scrollValue); + } + int y = m_selectEnd.y() - 1; + m_selectEnd.setY(y > 0 ? y : 0); + qDebug() << "start:" << m_selectStart << "end:" << m_selectEnd; + m_view->setSelection(m_selectStart, m_selectEnd); + }else if(op == QKxKeyTranslator::ESelectLineDown) { + if(m_selectStart == QPoint(-1,-1)) { + TermCursor tc = m_view->cursor(); + m_selectStart = m_selectEnd = QPoint(tc.x, tc.y + m_scrollValue); + } + int y = m_selectEnd.y() + 1; + m_selectEnd.setY(y >= m_view->lineCount() ? m_view->lineCount() - 1 : y); + qDebug() << "start:" << m_selectStart << "end:" << m_selectEnd; + m_view->setSelection(m_selectStart, m_selectEnd); + }else if(op == QKxKeyTranslator::ESelectLineLeft) { + if(m_selectStart == QPoint(-1,-1)) { + TermCursor tc = m_view->cursor(); + m_selectStart = m_selectEnd = QPoint(tc.x, tc.y + m_scrollValue); + } + int x = m_selectEnd.x() - 1; + m_selectEnd.setX(x > 0 ? x : 0); + qDebug() << "start:" << m_selectStart << "end:" << m_selectEnd; + m_view->setSelection(m_selectStart, m_selectEnd); + }else if(op == QKxKeyTranslator::ESelectLineRight) { + if(m_selectStart == QPoint(-1,-1)) { + TermCursor tc = m_view->cursor(); + m_selectStart = m_selectEnd = QPoint(tc.x, tc.y + m_scrollValue); + } + int x = m_selectEnd.x() + 1; + m_selectEnd.setX(x >= m_columns ? m_columns - 1 : x); + qDebug() << "start:" << m_selectStart << "end:" << m_selectEnd; + m_view->setSelection(m_selectStart, m_selectEnd); + }else if(op == QKxKeyTranslator::ESelectLineHome) { + if(m_selectStart == QPoint(-1,-1)) { + TermCursor tc = m_view->cursor(); + m_selectStart = m_selectEnd = QPoint(tc.x, tc.y + m_scrollValue); + } + m_selectEnd.setX(0); + qDebug() << "start:" << m_selectStart << "end:" << m_selectEnd; + m_view->setSelection(m_selectStart, m_selectEnd); + }else if(op == QKxKeyTranslator::ESelectLineEnd) { + if(m_selectStart == QPoint(-1,-1)) { + TermCursor tc = m_view->cursor(); + m_selectStart = m_selectEnd = QPoint(tc.x, tc.y + m_scrollValue); + } + m_selectEnd.setX(m_columns-1); + qDebug() << "start:" << m_selectStart << "end:" << m_selectEnd; + m_view->setSelection(m_selectStart, m_selectEnd); + }else if(op == QKxKeyTranslator::ESelectAll) { + m_selectStart = QPoint(0, 0); + m_selectEnd = QPoint(m_columns-1, m_view->lineCount()); + qDebug() << "start:" << m_selectStart << "end:" << m_selectEnd; + m_view->setSelection(m_selectStart, m_selectEnd); + }else if(!buf.isEmpty()) { char tmp[100]; sprintf(tmp, "0x%x", ev->key()); - qDebug() << "keyPressEvent:" << tmp << "buf:" << buf << "text:" << ev->text(); + qDebug() << "from match:" << tmp << "buf:" << buf << "text:" << ev->text(); handleSendData(buf); }else{ if(ev->key() >= 0x40 && ev->key() < 0x5f && (ev->modifiers() & Qt::ControlModifier)) { @@ -2094,13 +2101,13 @@ void QKxTermItem::handleKeyEvent(QKeyEvent *ev) buf.append("\033[6~"); }else { QTextCodec *m_codec = m_vte->getTextCodec(); - QByteArray tmp = m_codec->fromUnicode(ev->text()); + QByteArray tmp = m_codec->fromUnicode(ev->text()); buf.append(tmp); } if(!buf.isEmpty()) { - char tmp[100]; - sprintf(tmp, "0x%x", ev->key()); - qDebug() << "keyPressEvent key:" << tmp << " UnicodeText:" << ev->text() << " RemoteText:" << buf; + char str[100]; + sprintf(str, "0x%x", ev->key()); + qDebug() << "keyPressEvent key:" << str << " UnicodeText:" << ev->text() << " RemoteText:" << buf; handleSendData(buf); } } @@ -2118,7 +2125,7 @@ void QKxTermItem::scroll(int yoffset) { int lineToScroll = yoffset / m_fontHeight; int vmax = m_view->historyLineCount(); - if(vmax) { + if(vmax > 0) { int value = m_scrollValue; value += lineToScroll; if(value < 0) { @@ -2136,6 +2143,44 @@ void QKxTermItem::scroll(int yoffset) } } +void QKxTermItem::scrollLine(int lineToScroll) +{ + int vmax = m_view->historyLineCount(); + if(vmax > 0) { + int value = m_scrollValue; + value += lineToScroll; + if(value < 0) { + value = 0; + }else if(value > vmax) { + value = vmax; + } + updateScrollValue(value); + }else{ + int key = lineToScroll > 0 ? Qt::Key_Up : Qt::Key_Down; + QKeyEvent event(QEvent::KeyPress, key, Qt::NoModifier); + for (int i=0; i < qAbs(lineToScroll);i++) { + handleKeyEvent(&event); + } + } +} + +void QKxTermItem::scrollPage(int n) +{ + int line = n > 0 ? (n*m_rows - 1) : (n*m_rows + 1); + scrollLine(line); +} + +void QKxTermItem::scrollToTop() +{ + updateScrollValue(0); +} + +void QKxTermItem::scrollToBottom() +{ + int vmax = m_view->historyLineCount(); + updateScrollValue(vmax); +} + bool QKxTermItem::isLineVisible(int y) { if(y < m_scrollValue || y >= m_scrollValue + m_rows) { diff --git a/kxterm/qkxtermitem.h b/kxterm/qkxtermitem.h index e1c848c..92962d2 100644 --- a/kxterm/qkxtermitem.h +++ b/kxterm/qkxtermitem.h @@ -45,20 +45,15 @@ class QTERM_EXPORT QKxTermItem : public QWidget PF_Scroll = 4, PF_FullScreen = 8 }; - enum ShortCutKey{ - SCK_Copy = 1, - SCK_Paste, - SCK_SelectAll, - SCK_SelectLeft, - SCK_SelectRight, - SCK_SelectUp, - SCK_SelectDown, - SCK_SelectHome, - SCK_SelectEnd, + enum DragTextMode{ + DTM_NotDefined = 0, + DTM_DragToInput, + DTM_DragCopyAndPaste }; + Q_ENUM(CursorType) Q_ENUM(SelectionMode) - Q_ENUM(ShortCutKey) + Q_ENUM(DragTextMode) Q_DECLARE_FLAGS(PaintFlags, PaintFlag) Q_PROPERTY(QFont font READ font WRITE setFont) @@ -70,7 +65,6 @@ class QTERM_EXPORT QKxTermItem : public QWidget Q_PROPERTY(int scrollMaxValue READ scrollMaxValue) Q_PROPERTY(bool highContrast READ highContrast WRITE setHighContrast) Q_PROPERTY(CursorType cursorType READ cursorType WRITE setCursorType) - Q_PROPERTY(QString keyTable READ keyTable) Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode) Q_PROPERTY(QColor background READ backgroundColor) Q_PROPERTY(QString name READ termName WRITE setTermName) @@ -95,8 +89,8 @@ class QTERM_EXPORT QKxTermItem : public QWidget bool readOnly() const; void setReadOnly(bool on); - bool dragCopyAndPaste() const; - void setDragCopyAndPaste(bool on); + DragTextMode dragTextMode() const; + void setDragTextMode(DragTextMode mode); bool isOverSelection(const QPoint& pt); @@ -122,8 +116,6 @@ class QTERM_EXPORT QKxTermItem : public QWidget CursorType cursorType() const; void setCursorType(const CursorType& t); - QString keyTable() const; - QCursor mouseCursor() const; void setMouseCursor(const QCursor& cur); @@ -141,10 +133,10 @@ class QTERM_EXPORT QKxTermItem : public QWidget void scrollToEnd(); bool scrollTo(int y); - QKxKeyTranslator *keyLayout() const; - void setKeyLayout(QKxKeyTranslator *translator); - QString keyLayoutName() const; - void setKeyLayoutByName(const QString& name); + QKxKeyTranslator *keyTranslator() const; + void setKeyTranslator(QKxKeyTranslator *translator); + QString keyTranslatorName() const; + void setKeyTranslatorByName(const QString& name); QString colorSchema() const; void setColorSchema(const QString& name); @@ -180,12 +172,8 @@ class QTERM_EXPORT QKxTermItem : public QWidget void simulateKeyPress(QKeyEvent *ev); void simulateKeyRelease(QKeyEvent *ev); - // shortcut - void bindShortCut(ShortCutKey sck, QKeySequence key); - QKeySequence defaultShortCutKey(ShortCutKey sck); - QKeySequence currentShortCutKey(ShortCutKey sck); // theme - Q_INVOKABLE QStringList availableKeyLayouts() const; + Q_INVOKABLE QStringList availableKeytabs() const; Q_INVOKABLE QStringList availableColorSchemas() const; Q_INVOKABLE QStringList availableFontFamilies() const; Q_INVOKABLE void clearAll(); @@ -197,6 +185,7 @@ class QTERM_EXPORT QKxTermItem : public QWidget Q_INVOKABLE void tryToPaste(); Q_INVOKABLE void pastePlainText(const QString& txt); Q_INVOKABLE void selectAll(); + Q_INVOKABLE void tryToShowFindTool(); void resetTermSize(); signals: @@ -207,7 +196,9 @@ class QTERM_EXPORT QKxTermItem : public QWidget void scrollValueChanged(int lines, int position); void backgroundChanged(const QColor &clr); void titleChanged(const QString& title); - void activePathArrived(const QString& path); + void activePathArrived(const QString& path); + void showFindTool(); + void selectChanged(); public slots: void onScreenChanged(); @@ -219,7 +210,12 @@ public slots: void onSelectClickTimeout(); void onGuessActivePathChanged(const QString& path); void onSetActive(); + void onKeyAutoRepeat(); public: + Q_INVOKABLE void scrollLine(int n); + Q_INVOKABLE void scrollPage(int n); + Q_INVOKABLE void scrollToTop(); + Q_INVOKABLE void scrollToBottom(); Q_INVOKABLE void resetState(); Q_INVOKABLE void echoInput(const QByteArray& data); Q_INVOKABLE void parse(const QByteArray& data); @@ -230,7 +226,7 @@ public slots: Q_INVOKABLE void updateTermSize(); Q_INVOKABLE void updateTermSize(int rows, int cols); Q_INVOKABLE void updateScrollPosition(qreal position); - Q_INVOKABLE bool loadKeyLayout(const QString &path); + Q_INVOKABLE bool loadKeyTranslator(const QString &path); Q_INVOKABLE bool loadColorSchema(const QString &path); Q_INVOKABLE bool find(const QString& key, bool match, bool regular); Q_INVOKABLE bool findPrev(bool match, bool regular); @@ -278,7 +274,7 @@ public slots: void drawCursor(QPainter *p, const QRect& rt, const QColor& bg, const QColor& fg, bool &inverse); void handleKeyEvent(QKeyEvent *ev); void handleSendData(const QByteArray& buf); - void scroll(int offsety); + void scroll(int offsety); bool isLineVisible(int y); // -1: timeout. // 1: title changed. @@ -338,8 +334,6 @@ public slots: SelectionMode m_selectMode; bool m_tripleClick; - QString m_schema; - QString m_preeditText; QRect m_preeditRect; @@ -353,10 +347,6 @@ public slots: QString m_pathActive; - QKeySequence m_keyCopy; - QKeySequence m_keyPaste; - QKeySequence m_keyUpSelect, m_keyDownSelect, m_keyLeftSelect, m_keyRightSelect; - QKeySequence m_keyHomeSelect, m_keyEndSelect, m_keySelectAll; QPoint m_ptClicked; bool m_bEchoInputEnabled; @@ -364,7 +354,7 @@ public slots: bool m_readOnly; bool m_dragEnabled; - bool m_dragActived; + DragTextMode m_dragMode; QPoint m_ptDraged; @@ -373,6 +363,11 @@ public slots: /* background image */ QPointer m_bkImageRender; + + // auto key repeat + QPointer m_keyAutoRepeat; + QKeyEvent m_keyRepeat; + qint64 m_timeLast; }; #endif // QTERM_H diff --git a/kxterm/qkxtermwidget.cpp b/kxterm/qkxtermwidget.cpp index d99df03..964103a 100644 --- a/kxterm/qkxtermwidget.cpp +++ b/kxterm/qkxtermwidget.cpp @@ -117,9 +117,11 @@ QKxTermWidget::QKxTermWidget(QWidget* parent) QObject::connect(m_term, SIGNAL(activePathArrived(QString)), this, SIGNAL(activePathArrived(QString))); QObject::connect(m_term, SIGNAL(sendData(QByteArray)), this, SIGNAL(sendData(QByteArray))); QObject::connect(m_term, SIGNAL(readOnlyChanged()), this, SIGNAL(readOnlyChanged())); + QObject::connect((QKxTermItem*)m_term, &QKxTermItem::showFindTool, this, [=](){ + setFindBarVisible(true); + }); QObject::connect(m_vscroll, SIGNAL(valueChanged(int)), this, SLOT(onScrollValueChanged(int))); m_term->installEventFilter(this); - m_keyFind = QKeySequence(Qt::CTRL + Qt::Key_F); } QKxTermWidget::~QKxTermWidget() @@ -180,11 +182,6 @@ void QKxTermWidget::sendInput(const QByteArray &cmd) emit m_term->sendData(cmd); } -void QKxTermWidget::setFindShortCut(QKeySequence key) -{ - m_keyFind = key; -} - QString QKxTermWidget::textCodec() const { return m_term->textCodec(); @@ -230,13 +227,15 @@ void QKxTermWidget::pastePlainText(const QString &txt) m_term->pastePlainText(txt); } -bool QKxTermWidget::pasteWhenOverSelectionText(const QPoint& pt) +bool QKxTermWidget::copyWhenOverSelectionText(const QPoint& pt, bool paste) { if(m_term->isOverSelection(pt)) { QString txtSel = m_term->selectedText(); QClipboard *clip = QGuiApplication::clipboard(); clip->setText(txtSel); - m_term->directSendData(txtSel.toUtf8()); + if(paste) { + m_term->directSendData(txtSel.toUtf8()); + } return true; } return false; @@ -283,36 +282,11 @@ void QKxTermWidget::resizeEvent(QResizeEvent *ev) QWidget::resizeEvent(ev); QSize sz = ev->size(); QSize szh = m_vscroll->sizeHint(); - //m_vscroll->setGeometry(sz.width() - szh.width(), 0, szh.width(), sz.height()); - //m_term->setGeometry(0, 0, sz.width() - szh.width(), sz.height()); } bool QKxTermWidget::eventFilter(QObject *watched, QEvent *event) { - if(watched == m_term) { - if(event->type() == QEvent::KeyPress) { - QKeyEvent *ev = (QKeyEvent*)event; - int key = ev->key(); - Qt::KeyboardModifiers modifier = ev->modifiers(); - if(modifier & Qt::ShiftModifier) { - key += Qt::SHIFT; - } - if(modifier & Qt::ControlModifier) { - key += Qt::CTRL; - } - if(modifier & Qt::MetaModifier) { - key += Qt::META; - } - if(modifier & Qt::AltModifier) { - key += Qt::ALT; - } - QKeySequence seq(key); - if(seq == m_keyFind) { - setFindBarVisible(true); - } - } - } - return false; + return QWidget::eventFilter(watched, event); } void QKxTermWidget::onTermScrollValueChanged(int lines, int position) diff --git a/kxterm/qkxtermwidget.h b/kxterm/qkxtermwidget.h index 20b25ce..380fb92 100644 --- a/kxterm/qkxtermwidget.h +++ b/kxterm/qkxtermwidget.h @@ -38,8 +38,6 @@ class QTERM_EXPORT QKxTermWidget : public QWidget void sendInput(const QByteArray& cmd); - void setFindShortCut(QKeySequence key); - QString textCodec() const; void setTextCodec(const QString& codec); @@ -52,7 +50,7 @@ class QTERM_EXPORT QKxTermWidget : public QWidget Q_INVOKABLE void tryToCopy(); Q_INVOKABLE void tryToPaste(); Q_INVOKABLE void pastePlainText(const QString& txt); - Q_INVOKABLE bool pasteWhenOverSelectionText(const QPoint& pt); + Q_INVOKABLE bool copyWhenOverSelectionText(const QPoint& pt, bool paste); QString selectedText() const; void selectAllText(); @@ -77,7 +75,6 @@ private slots: QPointer m_term; QPointer m_find; QPointer m_vscroll; - QKeySequence m_keyFind; }; #endif // QTERM_H diff --git a/kxterm/qkxutils.cpp b/kxterm/qkxutils.cpp index 19d47af..e26739f 100644 --- a/kxterm/qkxutils.cpp +++ b/kxterm/qkxutils.cpp @@ -293,25 +293,19 @@ QVector initDefault() { return vcs; } -static QMap layouts; -static QStringList m_keyboardPaths; -static QStringList m_colorPaths; -void QKxUtils::addCustomKeyboardLayoutPaths(const QStringList &paths) +static QMap layouts; +static QString gkeytabCustomPath; +void QKxUtils::setCustomKeytabPath(const QString &path) { - m_keyboardPaths = paths; + gkeytabCustomPath = path; } -void QKxUtils::addCustomColorSchemaPaths(const QStringList &paths) -{ - m_colorPaths = paths; -} - -QStringList QKxUtils::availableKeyLayouts() +QStringList QKxUtils::availableKeytabs() { if(layouts.isEmpty()) { - QStringList paths = m_keyboardPaths; + QStringList paths(gkeytabCustomPath); QString path = QDir::cleanPath(QCoreApplication::applicationDirPath() + "/../private/keytabs"); - paths.append(path); + paths.prepend(path); QStringList filters; filters << "*.keytab"; for(auto it = paths.begin(); it != paths.end(); it++) { @@ -322,11 +316,7 @@ QStringList QKxUtils::availableKeyLayouts() for(int i = 0; i < fis.length(); i++) { QFileInfo& fi = fis[i]; QString fileName = fi.baseName(); - QKxKeyTranslator *keyLayout = new QKxKeyTranslator(); - if(keyLayout->load(fi.absoluteFilePath())){ - keyLayout->setName(fileName); - layouts.insert(fileName, keyLayout); - } + layouts.insert(fileName, fi.absoluteFilePath()); } } } @@ -334,28 +324,35 @@ QStringList QKxUtils::availableKeyLayouts() return layouts.keys(); } -QKxKeyTranslator *QKxUtils::keyboardLayout(const QString &name) +QString QKxUtils::keytabPath(const QString &name) { if(layouts.isEmpty()) { - availableKeyLayouts(); + availableKeytabs(); } - if(layouts.isEmpty()) { - return nullptr; - } - QKxKeyTranslator *key = layouts.value(name); - if(key != nullptr) { - return key; - } - return layouts.first(); + return layouts.value(name); +} + +void QKxUtils::cleanupKeytabs() +{ + layouts.clear(); } static QMap schemas; +static QString gschemaCustomPath; +void QKxUtils::setCustomColorSchemaPath(const QString &path) +{ + if(gschemaCustomPath.contains(path)) { + return; + } + gschemaCustomPath.append(path); +} + QStringList QKxUtils::availableColorSchemas() { if(schemas.isEmpty()) { - QStringList paths = m_colorPaths; + QStringList paths(gschemaCustomPath); QString path = QDir::cleanPath(QCoreApplication::applicationDirPath() + "/../private/themes"); - paths.append(path); + paths.prepend(path); QStringList filters; filters << "*.theme"; for(auto it = paths.begin(); it != paths.end(); it++) { @@ -389,16 +386,16 @@ struct FontCustomInfo { #define FONT_FILE_DELETE ("fontDels.txt") static QList gsysFamilies; -static QString gfontBackupPath; +static QString gfontCustomPath; static QMap gcustomFonts; void QKxUtils::setCustomFontPath(const QString &path) { - gfontBackupPath = path; + gfontCustomPath = path; } QStringList QKxUtils::customFontFamilies() { - QDir d(gfontBackupPath); + QDir d(gfontCustomPath); systemFontFamilies(); @@ -406,7 +403,7 @@ QStringList QKxUtils::customFontFamilies() QStringList fs = d.entryList(QDir::Files|QDir::NoDotAndDotDot); for(auto it = fs.begin(); it != fs.end(); it++) { QString name = *it; - QString fileName = gfontBackupPath + "/" + name; + QString fileName = gfontCustomPath + "/" + name; if(name == FONT_FILE_DELETE) { continue; } @@ -500,7 +497,7 @@ bool QKxUtils::isFixedPitch(const QFont &font) { QStringList QKxUtils::fontRemoveList() { - QString fileName = gfontBackupPath + "/" + FONT_FILE_DELETE; + QString fileName = gfontCustomPath + "/" + FONT_FILE_DELETE; QStringList fileDels; QFile file(fileName); if(!file.open(QFile::ReadOnly)) { @@ -525,7 +522,7 @@ QStringList QKxUtils::removeFontList() } } if(fileDels.isEmpty()) { - QString fileName = gfontBackupPath + "/" + FONT_FILE_DELETE; + QString fileName = gfontCustomPath + "/" + FONT_FILE_DELETE; QFile::remove(fileName); } return fileDels; @@ -544,7 +541,7 @@ void QKxUtils::appentFontRemoveList(const QStringList &_dels) } } fileDels.append(dels); - QString fileName = gfontBackupPath + "/" + FONT_FILE_DELETE; + QString fileName = gfontCustomPath + "/" + FONT_FILE_DELETE; QFile file(fileName); if(file.open(QFile::WriteOnly)) { QDataStream ds(&file); diff --git a/kxterm/qkxutils.h b/kxterm/qkxutils.h index 04c6bb4..604f012 100644 --- a/kxterm/qkxutils.h +++ b/kxterm/qkxutils.h @@ -22,12 +22,15 @@ class QKxKeyTranslator; class QTERM_EXPORT QKxUtils { public: - static void addCustomKeyboardLayoutPaths(const QStringList& paths); - static void addCustomColorSchemaPaths(const QStringList& paths); - static QStringList availableKeyLayouts(); - static QKxKeyTranslator *keyboardLayout(const QString& name); + static void setCustomKeytabPath(const QString& path); + static QStringList availableKeytabs(); + static QString keytabPath(const QString& name); + static void cleanupKeytabs(); + + static void setCustomColorSchemaPath(const QString& path); static QStringList availableColorSchemas(); static QString colorSchemaPath(const QString& name); + static void setCustomFontPath(const QString& path); static QStringList customFontFamilies(); static void removeCustomFontFamily(const QString& name, QStringList& fileErrs); diff --git a/kxterm/qkxview.cpp b/kxterm/qkxview.cpp index 2304cfd..bee6622 100644 --- a/kxterm/qkxview.cpp +++ b/kxterm/qkxview.cpp @@ -517,9 +517,6 @@ QList QKxView::copyImage(int y, int rows, int cols) TermLine &last = img.last(); if(r == tcy && visible) { last.xcur = tc.x; - if(last.cs.length() < tc.x - 2) { - int i = tc.x - 2; - } while(last.cs.length() <= tc.x) { last.cs.append(TermChar()); } diff --git a/kxterm/qvte.cpp b/kxterm/qvte.cpp index 805dda7..55b7b59 100644 --- a/kxterm/qvte.cpp +++ b/kxterm/qvte.cpp @@ -77,6 +77,8 @@ static inline void resetFlag(unsigned int& mode, unsigned int flag) { typedef void (*ModeModifier)(unsigned int& mode, unsigned int flag); + +#define MODES_DEFAULT (TF_DECANM | TF_DECAWM | TF_DECTCEM | TF_DECARM | TF_DECSCLM | TF_UTF8) QVte::QVte() { columns = 80; @@ -88,7 +90,7 @@ QVte::QVte() // according to the datasheet of DEC, DECAWM is default OFF. // but the linux console defaults to ON. // http://vt100.net/docs/vt510-rm/chapter2.html#S2.13 - flags = TF_DECAWM | TF_DECTCEM | TF_DECARM | TF_DECSCLM | TF_UTF8; + flags = MODES_DEFAULT; cursor_hidden = true; } @@ -129,7 +131,7 @@ void QVte::reset() { resize(rows, columns); mode = TM_Ground; - flags = TF_DECAWM | TF_DECTCEM | TF_DECARM | TF_DECSCLM | TF_UTF8; + flags = MODES_DEFAULT; cursor_hidden = true; cursor = TermCursor(); csi_data = ControlData(); @@ -142,6 +144,11 @@ unsigned int QVte::states() return flags; } +bool QVte::isAnsiMode() const +{ + return flags & TF_DECANM; +} + void QVte::RIS() { top = 0; @@ -156,7 +163,7 @@ void QVte::RIS() // according to the datasheet of DEC, DECAWM is default OFF. // but the linux console defaults to ON. // http://vt100.net/docs/vt510-rm/chapter2.html#S2.13 - flags = TF_DECAWM | TF_DECTCEM | TF_DECARM | TF_DECSCLM | TF_UTF8; + flags = MODES_DEFAULT; saveCursor(cursor); resetEscape(); diff --git a/kxterm/qvte.h b/kxterm/qvte.h index f235416..570c75c 100644 --- a/kxterm/qvte.h +++ b/kxterm/qvte.h @@ -24,6 +24,7 @@ class QVte void resize(int rows, int cols); void reset(); unsigned int states(); + bool isAnsiMode() const; protected: // to implement virtual void screenResize(int rows, int cols) = 0; diff --git a/kxterm/qvtedef.h b/kxterm/qvtedef.h index ee7d3de..0d3f548 100644 --- a/kxterm/qvtedef.h +++ b/kxterm/qvtedef.h @@ -31,7 +31,13 @@ #endif -#define DEFAULT_KEY_LAYOUT ("default") +#if defined (Q_OS_MAC) +#define DEFAULT_KEY_TRANSLATOR ("macos") +#elif defined(Q_OS_LINUX) +#define DEFAULT_KEY_TRANSLATOR ("linux") +#else +#define DEFAULT_KEY_TRANSLATOR ("default") +#endif #define DEFAULT_TEXT_CODEC ("UTF-8") #define DEFAULT_FONT_SIZE (12) #define DEFAULT_COLOR_SCHEMA ("Ubuntu") @@ -176,7 +182,9 @@ enum TermFlags { TF_LNM = (1 << 3), // line feedback/new line mode TF_DECTCEM = (1 << 4), // text cursor enable mode TF_DECCKM = (1 << 5), // cursor keys mode - TF_DECANM = (1 << 6), // ANSI/VT52 mode //Designate VT52 mode (DECANM) + TF_DECANM = (1 << 6), // ANSI/VT52 mode //Designate VT52 mode (DECANM), + // true: ANSI + // false: vt52 TF_DECCOLM = (1 << 7), // column mode (80/132) TF_DECSCLM = (1 << 8), // scolling mode (soft scroll) TF_DECSCNM = (1 << 9), // screen mode diff --git a/kxterm/qvteimpl.cpp b/kxterm/qvteimpl.cpp index c390192..ade9140 100644 --- a/kxterm/qvteimpl.cpp +++ b/kxterm/qvteimpl.cpp @@ -20,6 +20,7 @@ #include #include #include +#include static const int graphic_size = 32; //static quint16 vt100_graphics_std[graphic_size] = @@ -338,7 +339,8 @@ void QVteImpl::scrollDown(int y1, int y2, int n) void QVteImpl::bell() { - qDebug() << "bell"; + //qDebug() << "bell"; + QApplication::beep(); } void QVteImpl::setLineTextSize(int y, TermTextSize t) diff --git a/private/keytabs/default.keytab b/private/keytabs/default.keytab index 035fdc3..e043103 100644 --- a/private/keytabs/default.keytab +++ b/private/keytabs/default.keytab @@ -28,6 +28,11 @@ key Tab +Shift-Ansi : "\t" key Backtab +Ansi : "\E[Z" key Backtab -Ansi : "\t" +key Tab +Control+Ansi : "\E[27;5;9~" +key Backtab +Control+Ansi : "\E[27;6;9~" +key Tab +Control-Ansi : "\t" +key Backtab +Control-Ansi : "\t" + key Return-Shift-NewLine : "\r" key Return-Shift+NewLine : "\r\n" @@ -76,8 +81,19 @@ key Down +Shift+AppScreen : "\E[1;*B" key Left +Shift+AppScreen : "\E[1;*D" key Right +Shift+AppScreen : "\E[1;*C" +key Up +Shift+Alt-AppScreen : "\E[1;*A" +key Down +Shift+Alt-AppScreen : "\E[1;*B" +key Left +Shift+Alt-AppScreen : "\E[1;*D" +key Right +Shift+Alt-AppScreen : "\E[1;*C" + +key Up +Shift+Ctrl-AppScreen : "\E[1;*A" +key Down +Shift+Ctrl-AppScreen : "\E[1;*B" +key Left +Shift+Ctrl-AppScreen : "\E[1;*D" +key Right +Shift+Ctrl-AppScreen : "\E[1;*C" + # Keypad keys with NumLock ON -# (see "Numeric Keypad" section at http://www.nw.com/nw/WWW/products/wizcon/vt100.html ) +# (see https://web.archive.org/web/20070807181942/http://www.nw.com/nw/WWW/products/wizcon/vt100.html +# https://vt100.net/docs/vt100-ug/chapter3.html) # # Not enabled for now because it breaks the keypad in Vim. # @@ -113,9 +129,9 @@ key End +AppCuKeys+KeyPad : "\EOF" key Home -AppCuKeys+KeyPad : "\E[H" key End -AppCuKeys+KeyPad : "\E[F" -key Insert +KeyPad : "\E[2~" -key Delete +KeyPad : "\E[3~" -key PgUp -Shift+KeyPad : "\E[5~" +key Insert +KeyPad : "\E[2~" +key Delete +KeyPad : "\E[3~" +key PgUp -Shift+KeyPad : "\E[5~" key PgDown -Shift+KeyPad : "\E[6~" # the key labelled 5 on the Keypad, is Qt::Key_Clear (a very intuitive @@ -139,11 +155,14 @@ key Delete -AnyMod : "\E[3~" key Insert +AnyMod : "\E[2;*~" key Delete +AnyMod : "\E[3;*~" -key PgUp -Shift-AnyMod : "\E[5~" +key PgUp -Shift-AnyMod : "\E[5~" key PgDown -Shift-AnyMod : "\E[6~" -key PgUp -Shift+AnyMod : "\E[5;*~" +key PgUp -Shift+AnyMod : "\E[5;*~" key PgDown -Shift+AnyMod : "\E[6;*~" +key PgUp +Shift+AppScreen : "\E[5;*~" +key PgDown +Shift+AppScreen : "\E[6;*~" + # Function keys key F1 -AnyMod : "\EOP" key F2 -AnyMod : "\EOQ" @@ -159,30 +178,42 @@ key F11 -AnyMod : "\E[23~" key F12 -AnyMod : "\E[24~" key F1 +AnyMod : "\EO*P" +key F7 +AnyMod : "\E[18;*~" key F2 +AnyMod : "\EO*Q" key F3 +AnyMod : "\EO*R" key F4 +AnyMod : "\EO*S" key F5 +AnyMod : "\E[15;*~" key F6 +AnyMod : "\E[17;*~" -key F7 +AnyMod : "\E[18;*~" key F8 +AnyMod : "\E[19;*~" key F9 +AnyMod : "\E[20;*~" key F10 +AnyMod : "\E[21;*~" key F11 +AnyMod : "\E[23;*~" key F12 +AnyMod : "\E[24;*~" -# Work around dead keys key Space +Control : "\x00" # Some keys are used by konsole to cause operations. # The scroll* operations refer to the history buffer. -key Up +Shift-AppScreen : scrollLineUp -key PgUp +Shift-AppScreen : scrollPageUp -key Home +Shift-AppScreen : scrollUpToTop -key Down +Shift-AppScreen : scrollLineDown -key PgDown +Shift-AppScreen : scrollPageDown -key End +Shift-AppScreen : scrollDownToBottom +key Up -Alt+Ctrl+Shift-AppScreen : scrollLineUp +key PgUp -Alt+Ctrl+Shift-AppScreen : scrollPageUp +key Down -Alt+Ctrl+Shift-AppScreen : scrollLineDown +key PgDown -Alt+Ctrl+Shift-AppScreen : scrollPageDown +key Home -Alt+Ctrl+Shift-AppScreen : scrollUpToTop +key End -Alt+Ctrl+Shift-AppScreen : scrollDownToBottom + +key Left -Alt-Ctrl+Shift : selectLineLeft +key Right -Alt-Ctrl+Shift : selectLineRight +key Up -Alt-Ctrl+Shift : selectLineUp +key Down -Alt-Ctrl+Shift : selectLineDown +key Home -Alt-Ctrl+Shift : selectLineHome +key End -Alt-Ctrl+Shift : selectLineEnd + +key A -Alt+Ctrl+Shift : selectAll +key C -Alt+Ctrl+Shift : copy +key V -Alt+Ctrl+Shift : paste +key F -Alt+Ctrl+Shift : find + diff --git a/private/keytabs/linux.keytab b/private/keytabs/linux.keytab index c93c300..8d85268 100644 --- a/private/keytabs/linux.keytab +++ b/private/keytabs/linux.keytab @@ -103,9 +103,9 @@ key F12 : "\E[24~" key Home : "\E[1~" key End : "\E[4~" -key PgUp -Shift : "\E[5~" -key PgDown -Shift : "\E[6~" -key Insert-Shift : "\E[2~" +key PgUp -Shift : "\E[5~" +key PgDown -Shift : "\E[6~" +key Insert -Shift : "\E[2~" # Keypad-Enter. See comment on Return above. @@ -116,17 +116,23 @@ key Space +Control : "\x00" # some of keys are used by konsole. -key Up +Shift : scrollLineUp -key PgUp +Shift : scrollPageUp -key Down +Shift : scrollLineDown -key PgDown +Shift : scrollPageDown +key Up -Alt+Ctrl+Shift-AppScreen : scrollLineUp +key PgUp -Alt+Ctrl+Shift-AppScreen : scrollPageUp +key Down -Alt+Ctrl+Shift-AppScreen : scrollLineDown +key PgDown -Alt+Ctrl+Shift-AppScreen : scrollPageDown +key Home -Alt+Ctrl+Shift-AppScreen : scrollUpToTop +key End -Alt+Ctrl+Shift-AppScreen : scrollDownToBottom + +# linux no such shortcut. +#key Left -Alt-Ctrl+Shift : selectLineLeft +#key Right -Alt-Ctrl+Shift : selectLineRight +#key Up -Alt-Ctrl+Shift : selectLineUp +#key Down -Alt-Ctrl+Shift : selectLineDown +#key Home -Alt-Ctrl+Shift : selectLineHome +#key End -Alt-Ctrl+Shift : selectLineEnd + +key A -Alt+Ctrl+Shift : selectAll +key C -Alt+Ctrl+Shift : copy +key V -Alt+Ctrl+Shift : paste +key F -Alt+Ctrl+Shift : find - -#---------------------------------------------------------- - -# keypad characters as offered by Qt -# cannot be recognized as such. - -#---------------------------------------------------------- - -# Following other strings as emitted by konsole. diff --git a/private/keytabs/macos.keytab b/private/keytabs/macos.keytab new file mode 100644 index 0000000..7617e35 --- /dev/null +++ b/private/keytabs/macos.keytab @@ -0,0 +1,222 @@ +# [macos.keytab] Konsole Keyboard Table +# -------------------------------------------------------------- + +keyboard "macOS" + +# -------------------------------------------------------------- + +# TODO: There's something wrong in Konsole that these are +# required. +# Konsole's files are in the wrong location in macOS. +# All Konsole files should be installed into +# ~/Library/Application Support/konsole/ + +key A+Control : "\x01" # goto start of line +key C+Control : "\x03" # interrupt +key D+Control : "\x04" # logout "exit" +key E+Control : "\x05" # goto end of line +key L+Control : "\x0C" # clear terminal screen +key R+Control : "\x12" # invoke shell history +key U+Control : "\x15" # clear current line +key V+Control : "\x16" # +key W+Control : "\x17" # erase the word preceding to the cursor position +#key Z+Control : "\x1a" + +key Delete +Shift : "\x7f" + +# -------------------------------------------------------------- + +# common keys - below is same as default.keytab + +key Escape : "\E" + +key Tab -Shift : "\t" +key Tab +Shift+Ansi : "\E[Z" +key Tab +Shift-Ansi : "\t" +key Backtab +Ansi : "\E[Z" +key Backtab -Ansi : "\t" + +key Return-Shift-NewLine : "\r" +key Return-Shift+NewLine : "\r\n" + +key Return+Shift : "\EOM" + +# Backspace and Delete codes are preserving Control-H. +# +# Backspace without Control sends '^?'; this matches XTerm behaviour, so that +# pressing Option+Backspace will send \E + Del, which is the expected behaviour +# in some apps (e.g. emacs), and it was the behaviour before the commit +# that add the Backspace +Control rule +key Backspace -Control : "\x7f" + +# Match xterm behaviour: Backspace sends '^H' when Control is pressed +# BS, hex \x08, \b +key Backspace +Control : "\b" + +# Arrow keys in VT52 mode +# shift up/down are reserved for scrolling. +# shift left/right are reserved for switching between tabs (this is hardcoded). + +key Up -Shift-Ansi : "\EA" +key Down -Shift-Ansi : "\EB" +key Right-Shift-Ansi : "\EC" +key Left -Shift-Ansi : "\ED" + +# Arrow keys in ANSI mode with Application - and Normal Cursor Mode) + +key Up -Shift-AnyMod+Ansi+AppCuKeys : "\EOA" +key Down -Shift-AnyMod+Ansi+AppCuKeys : "\EOB" +key Right -Shift-AnyMod+Ansi+AppCuKeys : "\EOC" +key Left -Shift-AnyMod+Ansi+AppCuKeys : "\EOD" + +key Up -Shift-AnyMod+Ansi-AppCuKeys : "\E[A" +key Down -Shift-AnyMod+Ansi-AppCuKeys : "\E[B" +key Right -Shift-AnyMod+Ansi-AppCuKeys : "\E[C" +key Left -Shift-AnyMod+Ansi-AppCuKeys : "\E[D" + +key Up -Shift+AnyMod+Ansi : "\E[1;*A" +key Down -Shift+AnyMod+Ansi : "\E[1;*B" +key Right -Shift+AnyMod+Ansi : "\E[1;*C" +key Left -Shift+AnyMod+Ansi : "\E[1;*D" + +key Up +Shift+AppScreen : "\E[1;*A" +key Down +Shift+AppScreen : "\E[1;*B" +key Left +Shift+AppScreen : "\E[1;*D" +key Right +Shift+AppScreen : "\E[1;*C" + +key Up +Shift+Option-AppScreen : "\E[1;*A" +key Down +Shift+Option-AppScreen : "\E[1;*B" +key Left +Shift+Option-AppScreen : "\E[1;*D" +key Right +Shift+Option-AppScreen : "\E[1;*C" + +key Up +Shift+Control-AppScreen : "\E[1;*A" +key Down +Shift+Control-AppScreen : "\E[1;*B" +key Left +Shift+Control-AppScreen : "\E[1;*D" +key Right +Shift+Control-AppScreen : "\E[1;*C" + +# Keypad keys with NumLock ON +# (see https://web.archive.org/web/20070807181942/http://www.nw.com/nw/WWW/products/wizcon/vt100.html +# https://vt100.net/docs/vt100-ug/chapter3.html) +# +# Not enabled for now because it breaks the keypad in Vim. +# +#key 0 +KeyPad+AppKeyPad : "\EOp" +#key 1 +KeyPad+AppKeyPad : "\EOq" +#key 2 +KeyPad+AppKeyPad : "\EOr" +#key 3 +KeyPad+AppKeyPad : "\EOs" +#key 4 +KeyPad+AppKeyPad : "\EOt" +#key 5 +KeyPad+AppKeyPad : "\EOu" +#key 6 +KeyPad+AppKeyPad : "\EOv" +#key 7 +KeyPad+AppKeyPad : "\EOw" +#key 8 +KeyPad+AppKeyPad : "\EOx" +#key 9 +KeyPad+AppKeyPad : "\EOy" +#key + +KeyPad+AppKeyPad : "\EOl" +#key - +KeyPad+AppKeyPad : "\EOm" +#key . +KeyPad+AppKeyPad : "\EOn" +#key * +KeyPad+AppKeyPad : "\EOM" +#key Enter +KeyPad+AppKeyPad : "\r" + +# Keypad keys with NumLock Off +key Up -Shift+Ansi+AppCuKeys+KeyPad : "\EOA" +key Down -Shift+Ansi+AppCuKeys+KeyPad : "\EOB" +key Right -Shift+Ansi+AppCuKeys+KeyPad : "\EOC" +key Left -Shift+Ansi+AppCuKeys+KeyPad : "\EOD" + +key Up -Shift+Ansi-AppCuKeys+KeyPad : "\E[A" +key Down -Shift+Ansi-AppCuKeys+KeyPad : "\E[B" +key Right -Shift+Ansi-AppCuKeys+KeyPad : "\E[C" +key Left -Shift+Ansi-AppCuKeys+KeyPad : "\E[D" + +key Home +AppCuKeys+KeyPad : "\EOH" +key End +AppCuKeys+KeyPad : "\EOF" +key Home -AppCuKeys+KeyPad : "\E[H" +key End -AppCuKeys+KeyPad : "\E[F" + +key Insert +KeyPad : "\E[2~" +key Delete +KeyPad : "\E[3~" +key PgUp -Shift+KeyPad : "\E[5~" +key PgDown -Shift+KeyPad : "\E[6~" + +# the key labelled 5 on the Keypad, is Qt::Key_Clear (a very intuitive +# and discoverable name...) +key Clear +KeyPad : "\E[E" + +# other grey PC keys + +key Enter+NewLine : "\r\n" +key Enter-NewLine : "\r" + +key Home -AnyMod-AppCuKeys : "\E[H" +key End -AnyMod-AppCuKeys : "\E[F" +key Home -AnyMod+AppCuKeys : "\EOH" +key End -AnyMod+AppCuKeys : "\EOF" +key Home +AnyMod : "\E[1;*H" +key End +AnyMod : "\E[1;*F" + +key Insert -AnyMod : "\E[2~" +key Delete -AnyMod : "\E[3~" +key Insert +AnyMod : "\E[2;*~" +key Delete +AnyMod : "\E[3;*~" + +key PgUp -Shift-AnyMod : "\E[5~" +key PgDown -Shift-AnyMod : "\E[6~" +key PgUp -Shift+AnyMod : "\E[5;*~" +key PgDown -Shift+AnyMod : "\E[6;*~" + +key PgUp +Shift+AppScreen : "\E[5;*~" +key PgDown +Shift+AppScreen : "\E[6;*~" + +# Function keys +key F1 -AnyMod : "\EOP" +key F2 -AnyMod : "\EOQ" +key F3 -AnyMod : "\EOR" +key F4 -AnyMod : "\EOS" +key F5 -AnyMod : "\E[15~" +key F6 -AnyMod : "\E[17~" +key F7 -AnyMod : "\E[18~" +key F8 -AnyMod : "\E[19~" +key F9 -AnyMod : "\E[20~" +key F10 -AnyMod : "\E[21~" +key F11 -AnyMod : "\E[23~" +key F12 -AnyMod : "\E[24~" + +key F1 +AnyMod : "\EO*P" +key F2 +AnyMod : "\EO*Q" +key F3 +AnyMod : "\EO*R" +key F4 +AnyMod : "\EO*S" +key F5 +AnyMod : "\E[15;*~" +key F6 +AnyMod : "\E[17;*~" +key F7 +AnyMod : "\E[18;*~" +key F8 +AnyMod : "\E[19;*~" +key F9 +AnyMod : "\E[20;*~" +key F10 +AnyMod : "\E[21;*~" +key F11 +AnyMod : "\E[23;*~" +key F12 +AnyMod : "\E[24;*~" + +# Work around dead keys + +key Space +Control : "\x00" + +# Some keys are used by konsole to cause operations. +# The scroll* operations refer to the history buffer. + +key Up -Option-Control-Shift+Command-AppScreen : scrollLineUp +key PgUp -Option-Control-Shift+Command-AppScreen : scrollPageUp +key Down -Option-Control-Shift+Command-AppScreen : scrollLineDown +key PgDown -Option-Control-Shift+Command-AppScreen : scrollPageDown +key Home -Option-Control-Shift+Command-AppScreen : scrollUpToTop +key End -Option-Control-Shift+Command-AppScreen : scrollDownToBottom + +# mac no such shortcut +#key Left -Option-Control+Shift : selectLineLeft +#key Right -Option-Control+Shift : selectLineRight +#key Up -Option-Control+Shift : selectLineUp +#key Down -Option-Control+Shift : selectLineDown +#key Home -Option-Control+Shift : selectLineHome +#key End -Option-Control+Shift : selectLineEnd + +key A -Option-Control-Shift+Command : selectAll +key C -Option-Control-Shift+Command : copy +key V -Option-Control-Shift+Command : paste +key F -Option-Control-Shift+Command : find + diff --git a/private/keytabs/solaris.keytab b/private/keytabs/solaris.keytab index fe77e85..b5e4f10 100644 --- a/private/keytabs/solaris.keytab +++ b/private/keytabs/solaris.keytab @@ -72,8 +72,8 @@ key Home : "\E[1~" key Insert-Shift : "\E[2~" key Delete : "\E[3~" key End : "\E[4~" -key PgUp -Shift : "\E[5~" -key PgDown -Shift : "\E[6~" +key PgUp -Shift : "\E[5~" +key PgDown-Shift : "\E[6~" # function keys @@ -97,12 +97,23 @@ key Space +Control : "\x00" # Some keys are used by konsole to cause operations. # The scroll* operations refer to the history buffer. -#key Left +Shift : prevSession -#key Right +Shift : nextSession -key Up +Shift : scrollLineUp -key PgUp +Shift : scrollPageUp -key Down +Shift : scrollLineDown -key PgDown +Shift : scrollPageDown -#key Insert+Shift : emitSelection +key Up -Alt+Ctrl+Shift-AppScreen : scrollLineUp +key PgUp -Alt+Ctrl+Shift-AppScreen : scrollPageUp +key Down -Alt+Ctrl+Shift-AppScreen : scrollLineDown +key PgDown -Alt+Ctrl+Shift-AppScreen : scrollPageDown +key Home -Alt+Ctrl+Shift-AppScreen : scrollUpToTop +key End -Alt+Ctrl+Shift-AppScreen : scrollDownToBottom + +#key Left -Alt-Ctrl+Shift : selectLineLeft +#key Right -Alt-Ctrl+Shift : selectLineRight +#key Up -Alt-Ctrl+Shift : selectLineUp +#key Down -Alt-Ctrl+Shift : selectLineDown +#key Home -Alt-Ctrl+Shift : selectLineHome +#key End -Alt-Ctrl+Shift : selectLineEnd + +key A -Alt+Ctrl+Shift : selectAll +key C -Alt+Ctrl+Shift : copy +key V -Alt+Ctrl+Shift : paste +key F -Alt+Ctrl+Shift : find + -# keypad characters are not offered differently by Qt. diff --git a/private/languages/woterm_zh.ts b/private/languages/woterm_zh.ts index 18083bb..bc75eb2 100644 --- a/private/languages/woterm_zh.ts +++ b/private/languages/woterm_zh.ts @@ -4,7 +4,7 @@ English - + English 简体中文 @@ -1091,6 +1091,222 @@ 校验失败 + + QKxCombineKeyActionDialog + + + Dialog + 对话框 + + + + VT mode + VT模式 + + + + +Ansi + + + + + -Ansi + + + + + + + + + + + + + None + + + + + +NewLine + + + + + -NewLine + + + + + +AppCuKeys + + + + + -AppCuKeys + + + + + +AppScreen + + + + + -AppScreen + + + + + +AppKeyPad + + + + + -AppKeyPad + + + + + Shortcut + 快捷键 + + + + +Shift + + + + + -Shift + + + + + +Alt + + + + + -Alt + + + + + +Ctrl + + + + + -Ctrl + + + + + +Meta + + + + + -Meta + + + + Ctrl + Ctrl + + + + Key: + 键值: + + + + Quick input + 快捷输入 + + + + Combine key: + 组合键 + + + + Action + 功能 + + + + String to remote server + 发送至远程服务器的字符串 + + + + Operation on local terminal + 本地终端内的操作 + + + + Operation: + 操作: + + + + String: + 字符串: + + + + [+]:Preceding a modename means the key/mode is pressed/active respectively. + [+]:在模式名称之前表示按键或模式分别被按下或激活。 + + + + [-]:Preceding a modename means the key/mode isn't pressed/active respectively. + [-]:在模式名称之前表示键或模式分别没有被按下或激活。 + + + + [None]:ignore this key/mode. + [None]:忽略此按键或模式。 + + + + Executing a shortcut in the specified VT mode will trigger the corresponding action. + 在指定的VT模式下执行快捷键将触发相应的功能。 + + + + Apply + 应用 + + + + Cancel + 取消 + + + + Combine key action input + 组合键输入 + + + + + Parameter error + 参数错误 + + + + The key parameter should not be empty. + 按键参数不能为空。 + + + + The string parameter should not be empty. + 字符串参数不能为空。 + + QKxConfirmWidget @@ -1395,6 +1611,102 @@ 无法连接远程服务。 + + QKxKeyTranslator + + + Copy + 复制 + + + + Paste + 粘贴 + + + + Find + 查找 + + + + Select to the previous row + 选择到上一行的内容 + + + + Select to the next row + 选择到下一行的内容 + + + + Select to the left of the row + 选择当前位置的左则内容 + + + + Select to the right of the row + 选择当前位置的右则内容 + + + + Select to the beginning of row + 选择到行的开头 + + + + Select to the end of row + 选择到行的末尾 + + + + Select all + 选择所有 + + + + Scroll up one row + 向上滚动一行 + + + + Scroll up one page + 向上滚动一页 + + + + Scroll to the top + 滚动至顶部 + + + + Scroll down one row + 向下滚动一行 + + + + Scroll down one page + 向下滚动一页 + + + + Scroll down to bottom + 向下滚动至底部 + + + + QKxKeyTranslatorModel + + + Combine key + 组合键 + + + + Action + 功能 + + QKxLicense @@ -1608,15 +1920,58 @@ 正则 + + QKxShortcutInputDialog + + + Dialog + 对话框 + + + + Please directly enter the desired shortcut key on the keyboard. + 请直接在键盘上输入所需的快捷键。 + + + + Shortcut: + 快捷鍵: + + + + Apply + 应用 + + + + Cancel + 取消 + + + + Shortcut input + 快捷键输入 + + + + Parameter error + 参数错误 + + + + Please input a shortcut. + 请输入一个快捷键 + + QKxTermItem - + In read-only mode, no commands can be executed. 在只读模式下,不能执行任何命令。 - + There are no conditions for executing commands in the current state. 在当前状态下不存在执行命令的条件。 @@ -1991,107 +2346,107 @@ QMoRLoginTermWidget - + Error 错误 - + can't find the session, maybe it had been delete ago 没有查找到相应会话,是否已被删除。 - + Select Files 选择文件 - + Open Directory 打开文件夹 - + Copy 复制 - + Paste 粘贴 - + Force Reconnect 强制重新联网 - + Split Vertical 垂直分割 - + Split Horizontal 水平分割 - + Close Session 关闭会话 - + Float This Tab 浮动此选项卡 - + Find... 查找... - + Edit 编辑 - + Duplicate in new window 在新窗口中打开 - + Clean history 清空历史 - + Output history to file 将历史输出至文件 - + Stop history to file 停止输出历史文件 - + Zmodem upload Zmodem上传 - + Zmodem receive Zmodem下载 - + Zmodem abort Zmodem终止 - + Files are transfering... 文件正在传输中... @@ -2134,152 +2489,152 @@ QMoSshTermWidget - + Reconnection confirmation 重新连接确认 - + Continue to connect to the server? 是否继续连接到此服务器? - + Restore 恢复 - + Error 错误 - + can't find the session, maybe it had been delete ago 无法查找到此会话,或许已删除。 - + warning 警告 - + failed to find rz program, please install lrzsz. 找不到rz程序,请安装lrzsz。 - + Select Files 选择文件 - + Open Directory 打开文件夹 - + Configure error 配置错误 - + The session proxy had been removed. 会话代理已经被删除。 - + Copy 复制 - + Paste 粘贴 - + Force Reconnect 强制重新联网 - + Split Vertical 垂直分割 - + Split Horizontal 水平分割 - + Sftp Assistant SFTP助手 - + Find... 查找... - + Edit 编辑 - + Duplicate in new window 在新窗口中打开 - + New session multiplex 新建连接复用 - + Clean history 清空历史 - + Output history to file 将历史输出至文件 - + Stop history to file 停止输出历史文件 - + Zmodem upload Zmodem上传 - + Zmodem receive Zmodem下载 - + Zmodem abort - + Close Session 关闭会话 - + Test font 测试字体 - + Files are transfering... 文件正在传输中... @@ -2320,22 +2675,22 @@ QMoTermWidget - + Save history to file 保存历史至文件 - + log (*.log) log (*.log) - + Warning 警告 - + Failed to create file. @@ -5154,8 +5509,8 @@ - - + + Open remote session 打开远程会话 @@ -5166,8 +5521,8 @@ - - + + Open local session 打开本地会话 @@ -5178,8 +5533,8 @@ - - + + Open serialport session 打开串口会话 @@ -5230,7 +5585,7 @@ - + Playbooks 剧本 @@ -5396,7 +5751,7 @@ - + Clear all 清空所有 @@ -5461,7 +5816,7 @@ 官网 - + Open 打开 @@ -5560,22 +5915,22 @@ - + Error Information 错误信息 - + The license code has been invalidated as follow reason 许可证代码已失效,原因如下 - + Merge information 合并信息 - + No tabs to merge. 没有可合并的选项。 @@ -5607,17 +5962,17 @@ 请购买许可证支持我们 - + Configure information 配置信息 - + The configure has been changed, restart application to take effect right now. 配置已更改,请立即重新启动应用程序以生效。 - + Password input 密码输入 @@ -5626,80 +5981,80 @@ 首次使用管理员配置需要输入新密码激活它。 - + Please input password to verify. 请输入密码进行验证。 - + Login to the configuration of administrator for the first time, Please input password to activate it. 首次使用管理员配置需要输入新密码激活它。 - + Password error 参数错误 - + the password is not right. 密码不正确。 - + Toolbar 工具栏 - + History 历史 - + Merge 合并 - + Tunnel 隧道 - + Enter keyword to search 关键字搜索 - + filterBox 过滤框 - + Administrator login 管理员登录 - + Please input password to login application. 请输入密码登录应用 - + Login failure 登录失败 - + The password is wrong, %1 times left to try. 密码错误,剩余%1次尝试机会。 - + New 新建 @@ -5708,7 +6063,7 @@ Please input password to activate it. 管理 - + List 列表 @@ -6157,7 +6512,7 @@ Please input password to activate it. QWoPtyTermWidget - + Reconnection confirmation 重连确认 @@ -6166,8 +6521,8 @@ Please input password to activate it. 是否继续连接服务器? - - + + session list 会话列表 @@ -6176,67 +6531,67 @@ Please input password to activate it. 错误 - + Continue to connect to this session? 是否继续连接本会话? - + Copy 复制 - + Paste 粘贴 - + Force reload 强制重新加载 - + Split vertical 垂直分割 - + Split horizontal 水平分割 - + Close session 关闭会话 - + Float this tab 浮动此选项 - + Find... 查找... - + Edit 编辑 - + Output history to file 将历史输出至文件 - + Stop history to file 停止输出历史文件 - + Test font 测试字体 @@ -6252,28 +6607,28 @@ Please input password to activate it. QWoRLoginTermWidget - - + + session list 会话列表 - + Error 错误 - + can't find the session, maybe it had been delete ago 会话信息已经不存在 - + Select Files 选择文件 - + Open Directory 打开文件夹 @@ -6313,62 +6668,62 @@ Please input password to activate it. 浮动此选项卡 - + Find... 查找... - + Edit 编辑 - + Zmodem upload Zmodem上传 - + Zmodem receive Zmodem下载 - + Zmodem abort ZModem中止传输 - + Files are transfering... 文件正在传输中... - + Clean history 清空历史 - + Reconnection confirmation 重新连接 - + Continue to connect to the server? 继续连接至服务器? - + Duplicate in new window 在新窗口中打开 - + Output history to file 将历史输出至文件 - + Stop history to file 停止输出历史文件 @@ -6924,87 +7279,87 @@ Please input password to activate it. QWoSerialTermWidget - + Copy 复制 - + Edit 编辑 - + Clean history 清空历史 - + Output history to file 将历史输出至文件 - + Stop history to file 停止输出历史文件 - + Open interactive mode 开启交互模式 - + Close interactive mode 关闭交互模式 - + Find... 查找... - + Zmodem upload Zmodem上传 - + Zmodem receive Zmodem下载 - + Zmodem abort Zmodem终止 - + Interactive mode 交互模式 - + The current terminal is in read-only mode. Do you need to temporarily set it to interactive mode. 当前终端处于只读模式。你需要临时将其设置为交互式模式吗。 - + warning 警告 - + failed to find rz program, please install lrzsz. 找不到rz程序,请安装lrzsz。 - + Select Files 选择文件 - + Open Directory 打开文件夹 @@ -8178,330 +8533,527 @@ Please input password to activate it. 对话框 - + Color Schema 配色选项 - + Schema 选项 - + Font 字体 - + Font: 字体: - + Import 导入 - + Use the recommended font when other font sizes are blurred or incomplete. 当其他字体大小模糊或不完整时,使用推荐的字体。 - + Cursor 鼠标类型 - + Block Cursor BLOCK鼠标 - + Underline Cursor UNDERLINE鼠标 - + IBeam Cursor IBEAM鼠标 - Keyboard Layout - 键盘布局 + 键盘布局 - + Server Code Page 服务器编码 - + Scroll Buffer 屏幕滚动行数 - + BufferSize: 缓冲区大小 - + lines 行数 - + Local shell 本地终端 - + Shell path: Shell路径: - + ... ... - + Copy and paste 复制与粘贴 - Drag the selected text to copy and paste - 拖动所选文本进行复制和粘贴 + 拖动所选文本进行复制和粘贴 - + Right click the selected text to copy and paste it. 右键单击所选文本进行复制和粘贴。 - + image path: 图像路径: - + Terminal Position: 终端位置: - + No-repeat 不平铺 - + Tile horizontally 水平平铺 - + Tile vertically 垂直平铺 - + Tile all 全部平铺 - + Render mode 渲染模式 - + Edge smoothing 边缘平滑 - + Image transparency 图像透明度 - + 30 30 - + 220 220 - + Save to all sessions 保存至所有会话 - + + Save 保存 - + + Key profile + 按键配置 + + + + Reload + 重新加载 + + + + Clone + 克隆 + + + + Delete + 删除 + + + + Rename + 重命名 + + + + Help + 帮助 + + + + Add + 增加 + + + + Modify + 修改 + + + + Remove + 移除 + + + + Keyboard events that are not listed will be handled by default. + 没有列出的键盘事件,将进行默认处理。 + + + + Drag the selected text to input. + 拖拽选中的文本至输入。 + + + + Drag the selected text to copy and paste it. + 拖拽选定的文本进行复制和粘贴。 + + + + Auto copy when selecting text. + 自动复制选中的字符串。 + + + + Right click the selected text to copy. + 右键单击所选文本进行复制。 + + + Cancel 取消 - + TTY Properties TTY属性 - + Pattern 风格 - + Shortcut 快捷键 - + Other 其它 - + Background image 背景图 - + System recommand: 系统推荐: - + + + + + + + File information + 文件信息 + + + + File is not exist. + 文件不存在。 + + + + + File name + 文件名称 + + + + Input a file name to save. + 输入保存的文件名字 + + + + A file with the same name already exists,Please input a new one. + 已经存在同名的文件,请输入新的名称。 + + + + Failed to create file. Please check if the file name is a valid file name. + 无法创建文件。请检查文件名是否为有效的文件名。 + + + + + + Rename information + 重命名信息 + + + + + Internal private directory does not support this operation. + 内部私有文件夹不支持此操作。 + + + + Input a new file name + 输入新的文件名 + + + + The name already exist, please input a new one. + 名字已存在,请重新输入。 + + + + Failed to rename file + 无法重命名文件 + + + + Delete information + 删除信息 + + + + Operation information + 操作信息 + + + + Modifying internal private files is not supported. Please clone a file before editing. + 不支持修改内部私有文件。编辑前请先克隆该文件。 + + + + + + + + Parameter error + 参数错误 + + + + + + + + No selection to modify + 没有可修改的选中项 + + + + Shortcut information + 快捷键信息 + + + + Failed to load keytab file + 无法加载按键配置文件 + + + Program Files (*.exe) 执行程序文件(*.exe) - + Program Files 程序文件 - + Font file 字体文件 - + Image file 图像文件 - + Image information 图像信息 - + Image size cannot be smaller than 128*128 图像尺寸不能小于128*128 - + + Modify information + 修改信息 + + + + The shortcut file[%1] has been modified, do you need to save it? + 快捷方式文件[%1]已被修改,是否需要保存? + + + + The current profile does not support modification, a new profile will be created. + 当前配置文件不支持修改,将创建一个新的配置文件。 + + + + Please input a profile name. + 请输入配置文件名称。 + + + + Modifying internal private files is not supported. Please input a new one. + 不支持修改内部专用文件。请输入一个新名字。 + + + + Found a profile with the same name. Do you want to switch to that profile? + 找到一个同名的配置文件。是否要切换到该配置文件? + + Edit - 编辑 + 编辑 - + Copy 复制 - Paste - 粘贴 + 粘贴 - SelectAll - 选择全部 + 选择全部 - Select text from right to left - 从右到左选择文本内容 + 从右到左选择文本内容 - Select text from left to right - 从左到右选择文本内容 + 从左到右选择文本内容 - Select text from bottom to up - 从下向上选择文本内容 + 从下向上选择文本内容 - Select text from top to bottom - 从上向下选择文本内容 + 从上向下选择文本内容 - Select text from from line start to end - 从行开始选择文本内容 + 从行开始选择文本内容 - Select text from current to line end - 从当前至行尾选择文本内容 + 从当前至行尾选择文本内容 - + Input does not match actual 输入与实际使用不一致。 - + input 输入 - + output 输出 - - - - + + + + Import errors 导入错误 - + The font format is not supported. 字体格式不支持。 - + The font to be imported does not meet the requirements for terminal use. 要导入的字体不符合终端使用要求。 - + Failed to open the font file. 无法打开字体文件。 - + Failed to backup the font file. 无法备份字体文件。 @@ -9901,88 +10453,88 @@ Continue To Close It? QWoSshTermWidget - + Reconnection confirmation 重连确认 - + Continue to connect to the server? 继续连接远程服务器? - + Restore 恢复 - - + + session list 会话列表 - + Error 错误 - + can't find the session, maybe it had been delete ago 无法找到相应会话信息 - + warning 警告 - + failed to find rz program, please install lrzsz. 找不到rz程序,请安装lrzsz。 - + Select Files 选择文件 - + Open Directory 打开文件夹 - + Configure error 配置错误 - + The session proxy had been removed. 会话代理已经被删除。 - + Copy 复制 - + Paste 粘贴 - + Force Reconnect 强制重新联网 - + Split Vertical 垂直分割 - + Split Horizontal 水平分割 @@ -9995,22 +10547,22 @@ Continue To Close It? 创建水平窗口 - + Sftp Assistant SFTP助手 - + Find... 查找... - + Edit 编辑 - + Zmodem abort Zmodem中止传输 @@ -10019,17 +10571,17 @@ Continue To Close It? 新建连接复用 - + Clean history 清空历史 - + Output history to file 将历史输出至文件 - + Stop history to file 停止输出历史文件 @@ -10046,47 +10598,47 @@ Continue To Close It? ZModem中断传输 - + Close Session 关闭会话 - + Float This Tab 浮动此选项卡 - + Sftp independent tab SFTP独立选项卡 - + Duplicate in new window 在新窗口中打开 - + New session multiplex 新建连接复用 - + Zmodem upload Zmodem上传 - + Zmodem receive Zmodem下载 - + Test font 测试字体 - + Files are transfering... 文件正在传输中... @@ -10160,118 +10712,118 @@ Continue To Close It? QWoTelnetTermWidget - + Reconnection confirmation 连接确认 - + Continue to connect to the server? 继续连接服务器? - - + + session list 会话列表 - + Error 错误 - + can't find the session, maybe it had been delete ago 无法查找相应会话 - + Select Files 选择文件 - + Open Directory 打开文件夹 - + Copy 复制 - + Paste 粘贴 - + Force Reconnect 强制重新联网 - + Split Vertical 垂直分割 - + Split Horizontal 水平分割 - + Close Session 关闭会话 - + Float This Tab 浮动此选项卡 - + Find... 查找... - + Edit 编辑 - + Duplicate in new window 在新窗口中打开 - + Zmodem upload Zmodem上传 - + Zmodem receive Zmodem下载 - + Zmodem abort Zmodem中止传输 - + Clean history 清空历史 - + Output history to file 将历史输出至文件 - + Stop history to file 停止输出历史文件 @@ -10284,7 +10836,7 @@ Continue To Close It? ZModem下载 - + Files are transfering... 文件正在传输中... @@ -10318,22 +10870,22 @@ Continue To Close It? QWoTermWidget - + Save history to file - + log (*.log) - + Warning 警告 - + Failed to create file. diff --git a/private/skins/black/desktop.qss b/private/skins/black/desktop.qss index c53d885..08e5f04 100644 --- a/private/skins/black/desktop.qss +++ b/private/skins/black/desktop.qss @@ -400,3 +400,10 @@ QTabWidget { QTabWidget > QWidget { border: 1px solid #313131; } + +QWidget[objectName="shortcut"] QPushButton { + padding-left: 4px; + padding-right: 4px; + padding-top: 0px; + padding-bottom: 0px; +} \ No newline at end of file diff --git a/private/skins/black/operation.png b/private/skins/black/operation.png new file mode 100644 index 0000000000000000000000000000000000000000..bda6d2c6e3eb6db39a1db490d13d3da8238d647b GIT binary patch literal 4203 zcmd5=i8mD7|DGAk*kx(R5^vG)3fXtXOWF5*m#rZTgCSxR$!p2JFX<~w3`s+>%%rR_ zvNSR>CQFlSgD{x*O}{_ld%pLa&%NjQoO{o6Kj+@_oaf$j8>?H~93mV50D#-n#K?{@ z4*v-@Rz{C@X{lli%m_nM2R24aVDo>>!0d1nR|EjS$^R#SQwxc7Mp7)w*d@w7JTMCB z8yNsF@DINoAbArO8GsCr3_(c#W3T?B=T#;Ez`JT{WZ)26vAJ+R^SQIZ(9ZUT+g0O* zLc!Pj0cnzXMV~7-wHlaB>Yp;J`rkr-7#y~0r2nUF;dqMXjHC_u*lh?4T1v%0l%$9$ zu8Y@1rribqx>u!aj!Q8dcDPjXZy>?0X>-ftOp_ARI=#@2@T#JaD5OPb7mV$&;$&lM zFCPC9QMGrKEA1bnm0cc-V}MazDd-Y^4eGIxjN~Ogj>je`|64HOeJ!ug^|HF7Kr)1! zglDq=Rse_StdU$CU1-ZIjdD1mkt^CFok-wKK!BVe9uwc3% z{N=Pnv3@t;E&z#~D~saKNo85Qp-CZeRRF_`(2r_Gqq|9A==)qxH}^W*-@Qa^>686% z$ft0VDi->{em=eniQIh{3VjJHD+XXW>0D)%dG{1RNo@3la?chgCPs0-`XGdQC8Q*O4Fn=$%sw$d6N0a=)n?B zHMy|$mEYH>59JAp%3A=qC^fkb_Cf)NP6TOKBCf-A${O>+!~rChp7y}!j@>Sn(>)Lx zGu#qm=6we&oH7A<8GzPXT-r7TQojD>Bb!k4o&wLfe#G_CKQpf9X2530d$Imb0y996?rxB0T6WC*;vvy3zQFL}{{>-M^CSV=+)-8Qbz^&N+n|-_E z1rV=@Ut-tmXMY1>W#8p--ZqEq1D50h%t89U(W8Qqc=31MY|ei>#RD~iGmjEXr3ZlQ zo=vQ=(YD6L8-S*tX5-bL_K8ouLUy(M#wPPPg2!$zu=Knz)FXoz%sGYS1IFSeIqwX3b)C4OFNsgDT#t{1_vNAR>7IDaR0JX_-+N0QA zb5x;Qh`Y8 z+V%V@AF}CyoxSbk8Zx0xi5YhP^aiyE^<9hv*}b|JVPn&whVKfXd(KPy@M(5DQ3g;@ zq4WikWk@L6IL0};5HPEnCG-$@WWF43b`)uKqh&%yMOjA1y$}(`a#q{}5*uy)?n0{o zSm{vk%iRD`0?{CAIn$+odu8%1TuC@im5xO5PDs5SW7=hk^cyWkfb5iSY;SPHOXIs7 zeYiEJ4ENJe`8ZR`S1o$f!A(xAifL!|nTCg;IyH%~nVns;qI+Caph)4G@-DCq^uG;^ zz{~4UI+D}wK*i$4nJz}+LsJ#LHfyJN$Gl#xa9{tSVUGV#Ew^zbC{OR9TH~50<*Rm| z&Xn=B18r7a{%qeO@qVNV*f(LOtbb2ttx!~@x2+tp^%4<=!uDGd^=i~$`zxiOP7pe+ zR!&7Uhsvu;wyTa45o7A8M0k$rTTUs}X-JvsJuT%LcF}Cn_S6BUQK1vMDYb_SqorJI z)1ukAwkCBu9PZO2ebzP(3qOz|niTa0(d=7?^ljsxTn8ax7T3lZ#Rc`M+5_1&&a5C# zD)%&l&Xv#dIuA}Bl+J00R3g?1M1`s>t%ulkti&>Mt$6rs4=5IaM_c@Obi1vT-Q-8h zT|tKkR+sth@fLV}0-%CJLt=JBCwD6X9~uLv=$&jO3M*D#I$LTtvleQ^PYFvMGVAev z4Mcm6_X>z@6)7&cSBu%}m0$>At|89J%QS4O1N7%bTV&uRNqm>_Zc7*iH6Y>~9 z6ZPy6IC57HzuYMn9B={2HbRGr?P zgzK4vTAsJ@T9ta7KvzrpsQ`gU01x=$s*iP`_oDXT*VdSR7`8I;zjbcZV43|XF>-#D zIil;pz@*O$oboFl_UnT;v|kt+{j*bOIVVmj95`2j+xy&3boQ?L1&pfM2Ab-XvWAL% zaI9tZFB5XjHV<86K%@ENi#ap-uup>qA5tK4u=39^Y$GIFaeq6J7;|&3n8Yf@^(jB< zO(V{$(S9_8v4EQ;gmk6P_f|HEl|d`BKj>+x!k5zqp0q1IsowTc3X}^BRBL?=WnOlm ztz2c>Ts8ChfxJ;9fW1C)B@s|zMLUh;9D4aWi0RF5`sr<2f&MJk)+O=wu~?2nub7n6 zFLFxa;bdP->(fQQb8BgzO(kxyvltPhwwZ%?nrv>=$_Kr55hSWJQt&If0@Lw(f;j_P zzc*Wc6Pi<)j`cD=*O!MkV)gEP6DA%opURHEtL3e&(+5jM6Jy%49{zY2-nq$67bp|W z*UwS^r6oC^zF7UCiT5UivA^;4BK-1`@Eb#|mE|cU)(>vomGB^8YLg9ghQ*{;U}4a5 zyrA;@t&XA5jj?;e3iX{pc6@Hf#=X+)lS3`@S$B>Ip>4|wt^3sjX1&iWmm&x=ixvx3 zx7=n9QC$4kq`RQ))shV8cA(=TQ(k-K+l*W~ zStT&}ofFE*aT&_>^E!%IYyG}@u*3&1e3yM~=S7a8g3?sf-Mqd53OclJS|<|`S%v-5 zkRW5%aLAC><_fv(HFYnobNx!gGQ@RoMxf(16Mu6+QM^o4>I-4cTX@8L2qycVpQY)j zl(a}vzAN2;@-=X+Zt|}?OuPLx1V6KJqZ<#R`8Do{B0yha@aX5P16rLM6{6%Ju`b1K zkMXNe`SlaKe2QxbvcF_0ckFMTZ^(;Un-w}4;oKbuUUUCw1oL6<2Bhv9=kz9B#RNT) z^~SRgpK}?Onbi*2#AzK~oa*?}RUz>0pPvtgje>%{warwSa+p{N$*qA0wJ2t!&R#_V zQEivR_XUG!2q}C{)g@Z~kza+tf(vW@;tuaJRJ*4}IgKbLBXPex{>bXC#m&-OJcPq4 z!bC7TQkpVp|Noq`%A=WAg$gVohLPdL*tqjSHk!dM3Nj~!3{7QT*~wJC{E;P~J;i_; zx?&BSgUSD=zlfM?~l zVg8tmHIiH#Vap|Cwi%O_EHP9)jOmYryb+)g&Sh?z?Q=|6Z?`}kb_A&d1t8A%zQ>_i zbuc_|vD$;0rxp^rMQn6R(OFqWj(i*K&aHUAt@}z`p9%duxEisg8jzt49xR!+<5*!` z(I@w=EZQyti<&X{Vw&bpG%49+jWwAINzEdD`UQ3}$hIZcbPbOKO$$;!N*PNS;RWNS zan<#ZQI5w--%Uq5>s?DfWETCh*0eT*B2OoU_o^FH&5F9ut^~BZ0z_-p1iPByiFXB^ z5PY=#iU-m_El#?-K^)cc`**1zjQ_pVz1#V(TF%o-o05iQOT^Fljj7O_q~fE)b56KA z$NGS)>xVJPhpBsNu7s-ulWA4O_fL$&hFezhupUC05$$&my8;f@b~iNq?`fD2&7OQ9vYZ`(W@dg0D+>pm^2h3Nq`#(IO6;DK zP#M&9@7^U?dH9XZCQA(P66Z~8#=0|b)xhx3%VM8)3-wY~^V=H;8(3onL?4@oG>511L>2VMmfcnB#^X(@Xr!rpa&@I6x`5<=l^nY;6iSX^k>!40{tf9fBqX{BQ z4|f!4t7g3JA!nD}e2Q4toR=$f*m~G$YGiJl+MR3Hd1H3w=Mp($uTy6U#QH;fcngTZ zjg*IjsS0FaxOks=1Mb)2xg_rm7`=*B0;C^|wj^~1hcW$`#hs`~)>s;n*8pmO6yJ3! zFZ;=|+_{(e)?#AmJ+&1_L0JPNzYz(g&ShTb(`{}TT@pN(%f@0PWeDW^ eufxY@Oi`E)>;7diJmX^kU}|h-)MDuK;Qs*RCEtz! literal 0 HcmV?d00001 diff --git a/private/skins/black/string.png b/private/skins/black/string.png new file mode 100644 index 0000000000000000000000000000000000000000..cdaac04e0950509d191abe300a9785ce0f639991 GIT binary patch literal 4454 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 KdKjH&k^ckFy8}i5 literal 0 HcmV?d00001 diff --git a/private/skins/gold/string.png b/private/skins/gold/string.png new file mode 100644 index 0000000000000000000000000000000000000000..f57d1d0210533f5e032cdf18672ff02a2a96625b GIT binary patch literal 4387 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 literal 0 HcmV?d00001 diff --git a/private/skins/light/desktop.qss b/private/skins/light/desktop.qss index 5ca669e..b3d4060 100644 --- a/private/skins/light/desktop.qss +++ b/private/skins/light/desktop.qss @@ -398,3 +398,10 @@ QTabWidget { QTabWidget > QWidget { border: 1px solid #313131; } + +QWidget[objectName="shortcut"] QPushButton { + padding-left: 4px; + padding-right: 4px; + padding-top: 0px; + padding-bottom: 0px; +} \ No newline at end of file diff --git a/private/skins/light/operation.png b/private/skins/light/operation.png new file mode 100644 index 0000000000000000000000000000000000000000..4c0b6fea86692b3630fd3c0cafc33e479e0533c3 GIT binary patch literal 4955 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 literal 0 HcmV?d00001 diff --git a/private/skins/light/string.png b/private/skins/light/string.png new file mode 100644 index 0000000000000000000000000000000000000000..a5d8588e1f5fa32409b07b89240dedbe6100c880 GIT binary patch literal 5254 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 { + if(m_rkeyMode != ERKM_NotDefined) { + if(copyWhenOverSelectionText(ev->pos(), m_rkeyMode == ERKM_CopyPaste)) { return; } } @@ -418,7 +419,8 @@ void QMoRLoginTermWidget::contextMenuEvent(QContextMenuEvent *ev) m_menu->addAction(tr("Float This Tab"), this, SLOT(onFloatThisTab())); } - m_menu->addAction(QIcon("../private/skins/black/find.png"), tr("Find..."), this, SLOT(onShowFindBar()), QKeySequence(Qt::CTRL + Qt::Key_F)); + QKeySequence ksFind = m_term->keyTranslator()->shortcut(QKxKeyTranslator::EFind); + m_menu->addAction(QIcon("../private/skins/black/find.png"), tr("Find..."), this, SLOT(onShowFindBar()), ksFind); m_menu->addAction(QIcon("../private/skins/black/palette.png"), tr("Edit"), this, SLOT(onModifyThisSession())); //m_menu->addAction(QIcon("../private/skins/black/history.png"), tr("History"), this, SLOT(onSessionCommandHistory())); m_menu->addAction(tr("Duplicate in new window"), this, SLOT(onDuplicateInNewWindow())); diff --git a/woterm/qmosshtermwidget.cpp b/woterm/qmosshtermwidget.cpp index f656df6..d39595c 100644 --- a/woterm/qmosshtermwidget.cpp +++ b/woterm/qmosshtermwidget.cpp @@ -13,6 +13,7 @@ #include "qkxtermwidget.h" #include "qkxtermitem.h" +#include "qkxkeytranslator.h" #include "qkxmessagebox.h" #include "qwossh.h" #include "qwosshconf.h" @@ -484,8 +485,8 @@ void QMoSshTermWidget::resizeEvent(QResizeEvent *ev) void QMoSshTermWidget::contextMenuEvent(QContextMenuEvent *ev) { - if(m_rkeyPaste) { - if(pasteWhenOverSelectionText(ev->pos())) { + if(m_rkeyMode != ERKM_NotDefined) { + if(copyWhenOverSelectionText(ev->pos(), m_rkeyMode == ERKM_CopyPaste)) { return; } } @@ -511,7 +512,8 @@ void QMoSshTermWidget::contextMenuEvent(QContextMenuEvent *ev) QObject::connect(hsplit, SIGNAL(triggered()), this, SLOT(onHorizontalSplitView())); menu.addAction(QIcon("../private/skins/black/sftp.png"), tr("Sftp Assistant"), this, SLOT(onSftpConnectReady())); - menu.addAction(QIcon("../private/skins/black/find.png"), tr("Find..."), this, SLOT(onShowFindBar()), QKeySequence(Qt::CTRL + Qt::Key_F)); + QKeySequence ksFind = m_term->keyTranslator()->shortcut(QKxKeyTranslator::EFind); + menu.addAction(QIcon("../private/skins/black/find.png"), tr("Find..."), this, SLOT(onShowFindBar()), ksFind); menu.addAction(QIcon("../private/skins/black/palette.png"), tr("Edit"), this, SLOT(onModifyThisSession())); menu.addAction(tr("Duplicate in new window"), this, SLOT(onDuplicateInNewWindow())); menu.addAction(tr("New session multiplex"), this, SLOT(onNewSessionMultiplex())); diff --git a/woterm/qmotermwidget.cpp b/woterm/qmotermwidget.cpp index 7e1672c..8e599c9 100644 --- a/woterm/qmotermwidget.cpp +++ b/woterm/qmotermwidget.cpp @@ -53,18 +53,6 @@ QMoTermWidget::QMoTermWidget(const QString& target, ETermType ttype, QWidget *pa showTouchPoint(true, true); m_term->showTermName(false); - QString val = QWoSetting::value("property/shortcut").toString(); - QVariantMap mdata = QWoUtils::qBase64ToVariant(val).toMap(); - m_term->bindShortCut(QKxTermItem::SCK_Copy, mdata.value("SCK_Copy", m_term->defaultShortCutKey(QKxTermItem::SCK_Copy)).value()); - m_term->bindShortCut(QKxTermItem::SCK_Paste, mdata.value("SCK_Paste", m_term->defaultShortCutKey(QKxTermItem::SCK_Paste)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectAll, mdata.value("SCK_SelectAll", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectAll)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectLeft, mdata.value("SCK_SelectLeft", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectLeft)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectRight, mdata.value("SCK_SelectRight", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectRight)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectUp, mdata.value("SCK_SelectUp", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectUp)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectDown, mdata.value("SCK_SelectDown", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectDown)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectHome, mdata.value("SCK_SelectHome", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectHome)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectEnd, mdata.value("SCK_SelectEnd", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectEnd)).value()); - // only for android. m_term->setBlinkAlway(true); } @@ -188,10 +176,10 @@ void QMoTermWidget::resetProperty(QVariantMap mdata) QString schema = mdata.value("colorSchema", DEFAULT_COLOR_SCHEMA).toString(); m_term->setColorSchema(schema); - QString keyboard = mdata.value("keyboard", DEFAULT_KEY_LAYOUT).toString(); - m_term->setKeyLayoutByName(keyboard); + QString keyboard = mdata.value("keyTranslator", DEFAULT_KEY_TRANSLATOR).toString(); + m_term->setKeyTranslatorByName(keyboard); - QString codec = mdata.value("textcodec", DEFAULT_TEXT_CODEC).toString(); + QString codec = mdata.value("textCodec", DEFAULT_TEXT_CODEC).toString(); m_term->setTextCodec(codec); QFont ft = QFontDatabase::systemFont(QFontDatabase::FixedFont); @@ -211,8 +199,23 @@ void QMoTermWidget::resetProperty(QVariantMap mdata) int lines = mdata.value("historyLength", DEFAULT_HISTORY_LINE_LENGTH).toInt(); m_term->setHistorySize(lines); bool dragPaste = mdata.value("dragPaste", false).toBool(); - m_term->setDragCopyAndPaste(dragPaste); - m_rkeyPaste = mdata.value("rkeyPaste", false).toBool(); + bool dragInput = mdata.value("dragInput", false).toBool(); + if(dragPaste) { + m_term->setDragTextMode(QKxTermItem::DTM_DragCopyAndPaste); + }else if(dragInput) { + m_term->setDragTextMode(QKxTermItem::DTM_DragToInput); + }else{ + m_term->setDragTextMode(QKxTermItem::DTM_NotDefined); + } + bool rkeyCopy = mdata.value("rkeyCopy", false).toBool(); + bool rkeyPaste = mdata.value("rkeyPaste", false).toBool(); + if(rkeyCopy) { + m_rkeyMode = ERKM_Copy; + }else if(rkeyPaste) { + m_rkeyMode = ERKM_CopyPaste; + }else{ + m_rkeyMode = ERKM_NotDefined; + } } void QMoTermWidget::showTouchPoint(bool show, bool async) diff --git a/woterm/qmotermwidget.h b/woterm/qmotermwidget.h index 7923872..ddd3d48 100644 --- a/woterm/qmotermwidget.h +++ b/woterm/qmotermwidget.h @@ -26,6 +26,11 @@ class QMoTermWidget : public QKxTermWidget ETTLocalShell, ETTSerialPort }; + enum ERightKeyMode { + ERKM_NotDefined, + ERKM_Copy, + ERKM_CopyPaste + }; public: explicit QMoTermWidget(const QString& target, ETermType ttype, QWidget *parent=nullptr); virtual ~QMoTermWidget(); @@ -56,7 +61,7 @@ protected slots: QString m_target; ETermType m_ttype; QPointer m_loading; - bool m_rkeyPaste; + ERightKeyMode m_rkeyMode; QString m_historyFile; QPointer m_touchPoint; }; diff --git a/woterm/qwomainwindow.cpp b/woterm/qwomainwindow.cpp index cc9fc2d..52fbabb 100644 --- a/woterm/qwomainwindow.cpp +++ b/woterm/qwomainwindow.cpp @@ -383,6 +383,8 @@ void QWoMainWindow::onAppStart() int level = QWoSetting::windownOpacity(); resetWindowOpacity(turnOn, level); } + + //QMetaObject::invokeMethod(this, "onActionTTYOptionsTriggered", Qt::QueuedConnection); } diff --git a/woterm/qwoplaybooktermwidget.cpp b/woterm/qwoplaybooktermwidget.cpp index cea9d51..400e44f 100644 --- a/woterm/qwoplaybooktermwidget.cpp +++ b/woterm/qwoplaybooktermwidget.cpp @@ -46,19 +46,7 @@ QWoPlaybookTermWidget::QWoPlaybookTermWidget(QWidget *parent) m_term->showTermName(false); - QString val = QWoSetting::value("property/shortcut").toString(); - QVariantMap mdata = QWoUtils::qBase64ToVariant(val).toMap(); - m_term->bindShortCut(QKxTermItem::SCK_Copy, mdata.value("SCK_Copy", m_term->defaultShortCutKey(QKxTermItem::SCK_Copy)).value()); - m_term->bindShortCut(QKxTermItem::SCK_Paste, mdata.value("SCK_Paste", m_term->defaultShortCutKey(QKxTermItem::SCK_Paste)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectAll, mdata.value("SCK_SelectAll", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectAll)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectLeft, mdata.value("SCK_SelectLeft", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectLeft)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectRight, mdata.value("SCK_SelectRight", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectRight)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectUp, mdata.value("SCK_SelectUp", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectUp)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectDown, mdata.value("SCK_SelectDown", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectDown)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectHome, mdata.value("SCK_SelectHome", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectHome)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectEnd, mdata.value("SCK_SelectEnd", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectEnd)).value()); - - // only for android. + // only for android. m_term->setBlinkAlway(true); } @@ -125,10 +113,10 @@ void QWoPlaybookTermWidget::resetProperty(QVariantMap mdata) QString schema = mdata.value("colorSchema", DEFAULT_COLOR_SCHEMA).toString(); m_term->setColorSchema(schema); - QString keyboard = mdata.value("keyboard", DEFAULT_KEY_LAYOUT).toString(); - m_term->setKeyLayoutByName(keyboard); + QString keyboard = mdata.value("keyTranslator", DEFAULT_KEY_TRANSLATOR).toString(); + m_term->setKeyTranslatorByName(keyboard); - QString codec = mdata.value("textcodec", DEFAULT_TEXT_CODEC).toString(); + QString codec = mdata.value("textCodec", DEFAULT_TEXT_CODEC).toString(); m_term->setTextCodec(codec); QFont ft = QFontDatabase::systemFont(QFontDatabase::FixedFont); @@ -148,5 +136,12 @@ void QWoPlaybookTermWidget::resetProperty(QVariantMap mdata) int lines = mdata.value("historyLength", DEFAULT_HISTORY_LINE_LENGTH).toInt(); m_term->setHistorySize(lines); bool dragPaste = mdata.value("dragPaste", false).toBool(); - m_term->setDragCopyAndPaste(dragPaste); + bool dragInput = mdata.value("dragInput", false).toBool(); + if(dragPaste) { + m_term->setDragTextMode(QKxTermItem::DTM_DragCopyAndPaste); + }else if(dragInput) { + m_term->setDragTextMode(QKxTermItem::DTM_DragToInput); + }else{ + m_term->setDragTextMode(QKxTermItem::DTM_NotDefined); + } } diff --git a/woterm/qwoptytermwidget.cpp b/woterm/qwoptytermwidget.cpp index ca1468b..35e74f1 100644 --- a/woterm/qwoptytermwidget.cpp +++ b/woterm/qwoptytermwidget.cpp @@ -29,6 +29,7 @@ #include "qkxtermwidget.h" #include "qkxtermitem.h" +#include "qkxkeytranslator.h" #include "qkxmessagebox.h" @@ -245,8 +246,8 @@ void QWoPtyTermWidget::resizeEvent(QResizeEvent *ev) void QWoPtyTermWidget::contextMenuEvent(QContextMenuEvent *ev) { - if(m_rkeyPaste) { - if(pasteWhenOverSelectionText(ev->pos())) { + if(m_rkeyMode != ERKM_NotDefined) { + if(copyWhenOverSelectionText(ev->pos(), m_rkeyMode == ERKM_CopyPaste)) { return; } } @@ -269,7 +270,8 @@ void QWoPtyTermWidget::contextMenuEvent(QContextMenuEvent *ev) m_menu->addAction(tr("Float this tab"), this, SLOT(onFloatThisTab())); } - m_menu->addAction(QIcon("../private/skins/black/find.png"), tr("Find..."), this, SLOT(onShowFindBar()), QKeySequence(Qt::CTRL + Qt::Key_F)); + QKeySequence ksFind = m_term->keyTranslator()->shortcut(QKxKeyTranslator::EFind); + m_menu->addAction(QIcon("../private/skins/black/find.png"), tr("Find..."), this, SLOT(onShowFindBar()), ksFind); m_menu->addAction(QIcon("../private/skins/black/palette.png"), tr("Edit"), this, SLOT(onModifyThisSession())); m_output = m_menu->addAction(tr("Output history to file"), this, SLOT(onOutputHistoryToFile())); m_stop = m_menu->addAction(tr("Stop history to file"), this, SLOT(onStopOutputHistoryFile())); diff --git a/woterm/qworlogintermwidget.cpp b/woterm/qworlogintermwidget.cpp index 172882a..b31d3a2 100644 --- a/woterm/qworlogintermwidget.cpp +++ b/woterm/qworlogintermwidget.cpp @@ -28,6 +28,7 @@ #include "qkxtermwidget.h" #include "qkxtermitem.h" +#include "qkxkeytranslator.h" #include "qkxmessagebox.h" @@ -500,12 +501,11 @@ void QWoRLoginTermWidget::resizeEvent(QResizeEvent *ev) void QWoRLoginTermWidget::contextMenuEvent(QContextMenuEvent *ev) { - if(m_rkeyPaste) { - if(pasteWhenOverSelectionText(ev->pos())) { + if(m_rkeyMode != ERKM_NotDefined) { + if(copyWhenOverSelectionText(ev->pos(), m_rkeyMode == ERKM_CopyPaste)) { return; } } - if(m_menu == nullptr) { m_menu = new QMenu(this); m_copy = m_menu->addAction(tr("Copy")); @@ -529,7 +529,8 @@ void QWoRLoginTermWidget::contextMenuEvent(QContextMenuEvent *ev) m_menu->addAction(tr("Float This Tab"), this, SLOT(onFloatThisTab())); } - m_menu->addAction(QIcon("../private/skins/black/find.png"), tr("Find..."), this, SLOT(onShowFindBar()), QKeySequence(Qt::CTRL + Qt::Key_F)); + QKeySequence ksFind = m_term->keyTranslator()->shortcut(QKxKeyTranslator::EFind); + m_menu->addAction(QIcon("../private/skins/black/find.png"), tr("Find..."), this, SLOT(onShowFindBar()), ksFind); m_menu->addAction(QIcon("../private/skins/black/palette.png"), tr("Edit"), this, SLOT(onModifyThisSession())); //m_menu->addAction(QIcon("../private/skins/black/history.png"), tr("History"), this, SLOT(onSessionCommandHistory())); m_menu->addAction(tr("Duplicate in new window"), this, SLOT(onDuplicateInNewWindow())); diff --git a/woterm/qwoserialtermwidget.cpp b/woterm/qwoserialtermwidget.cpp index 85709a0..8e324a5 100644 --- a/woterm/qwoserialtermwidget.cpp +++ b/woterm/qwoserialtermwidget.cpp @@ -18,6 +18,7 @@ #include "qwoevent.h" #include "qkxtermwidget.h" #include "qkxtermitem.h" +#include "qkxkeytranslator.h" #include "qwosessionttyproperty.h" #include "qkxmessagebox.h" #include "qwosetting.h" @@ -75,7 +76,8 @@ void QWoSerialTermWidget::contextMenuEvent(QContextMenuEvent *ev) }else{ menu.addAction(tr("Close interactive mode"), this, SLOT(onCloseInteractiveMode())); } - menu.addAction(QIcon("../private/skins/black/find.png"), tr("Find..."), this, SLOT(onShowFindBar()), QKeySequence(Qt::CTRL + Qt::Key_F)); + QKeySequence ksFind = m_term->keyTranslator()->shortcut(QKxKeyTranslator::EFind); + menu.addAction(QIcon("../private/skins/black/find.png"), tr("Find..."), this, SLOT(onShowFindBar()), ksFind); #if 0 menu.addSeparator(); menu.addAction(QIcon("../private/skins/black/upload.png"), tr("Zmodem upload"), this, SLOT(onZmodemSend())); diff --git a/woterm/qwosessionttyproperty.cpp b/woterm/qwosessionttyproperty.cpp index 4e8e23e..d86a5f8 100644 --- a/woterm/qwosessionttyproperty.cpp +++ b/woterm/qwosessionttyproperty.cpp @@ -16,13 +16,13 @@ #include "qwoutils.h" #include "qkxutils.h" #include "qwosetting.h" -#include "qwoshortcutmodel.h" -#include "qwoshortcutdelegate.h" +#include "qkxkeytranslatormodel.h" #include "qkxmessagebox.h" #include "qwofontlistmodel.h" #include "qkxpositionitem.h" #include "qkxbuttonassist.h" #include "qwosshconf.h" +#include "qkxcombinekeyactiondialog.h" #include #include @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include #define BUTTON_REMOVE_SIZE (10) #define BUTTON_MARGIN (3) @@ -139,6 +142,8 @@ QWoSessionTTYProperty::QWoSessionTTYProperty(ETTYType type, QWidget *parent) m_tabBar->addTab(tr("Other")); m_tabBar->addTab(tr("Background image")); QObject::connect(m_tabBar, SIGNAL(currentChanged(int)), this, SLOT(onPageCurrentChanged(int))); + m_tabBar->setCurrentIndex(0); + ui->stacked->setCurrentWidget(ui->pattern); m_preview = new QKxTermWidget(this); m_term = m_preview->termItem(); @@ -154,11 +159,17 @@ QWoSessionTTYProperty::QWoSessionTTYProperty(ETTYType type, QWidget *parent) QStringList schemas = m_term->availableColorSchemas(); schemas.sort(); ui->schema->setModel(new QStringListModel(schemas, this)); - QObject::connect(ui->schema, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(onColorCurrentIndexChanged(const QString &))); + QObject::connect(ui->schema, SIGNAL(currentIndexChanged(QString)), this, SLOT(onColorCurrentIndexChanged(QString))); + m_keysModel = new QKxKeyTranslatorModel(QWoSetting::privatePath(), ui->keymap->font(), this); + ui->keymap->setModel(m_keysModel); + ui->keymap->setIconSize(QSize(32, 32)); - QStringList kbly = QKxUtils::availableKeyLayouts(); - ui->kblayout->setModel(new QStringListModel(kbly, this)); + QStringList kbly = QKxUtils::availableKeytabs(); + ui->keytab->setModel(new QStringListModel(kbly, this)); + QObject::connect(ui->keytab, SIGNAL(currentIndexChanged(QString)), this, SLOT(onKeytabCurrentIndexChanged(QString))); + ui->keytab->setCurrentIndex(-1); + ui->keytab->setCurrentText(DEFAULT_KEY_TRANSLATOR); QList codec; foreach(QByteArray c, QTextCodec::availableCodecs()) { @@ -174,8 +185,15 @@ QWoSessionTTYProperty::QWoSessionTTYProperty(ETTYType type, QWidget *parent) QWoFontListModel *model = new QWoFontListModel(this); ui->fontChooser->setModel(model); + QObject::connect(ui->keymap, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(onKeymapItemDBClicked(QModelIndex))); + QHeaderView *hdrView = ui->keymap->header(); + if(hdrView != nullptr) { + hdrView->setStretchLastSection(true); + hdrView->setSectionResizeMode(QHeaderView::ResizeToContents); + } + QObject::connect(m_delegate, SIGNAL(removeArrived(QString)), this, SLOT(onFontFamilyRemove(QString))); - QObject::connect(ui->fontChooser, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(onFontCurrentIndexChanged(const QString&))); + QObject::connect(ui->fontChooser, SIGNAL(currentIndexChanged(QString)), this, SLOT(onFontCurrentIndexChanged(QString))); QObject::connect(ui->fontSize, SIGNAL(valueChanged(int)), this, SLOT(onFontValueChanged(int))); QObject::connect(ui->blockCursor, SIGNAL(toggled(bool)), this, SLOT(onBlockCursorToggled())); QObject::connect(ui->underlineCursor, SIGNAL(toggled(bool)), this, SLOT(onUnderlineCursorToggled())); @@ -185,6 +203,32 @@ QWoSessionTTYProperty::QWoSessionTTYProperty(ETTYType type, QWidget *parent) QObject::connect(ui->btnSaveToAll, SIGNAL(clicked()), this, SLOT(onButtonSaveToAllClicked())); QObject::connect(ui->btnCancel, SIGNAL(clicked()), this, SLOT(close())); + { + QObject::connect(ui->chkDragCopyPaste, &QRadioButton::clicked, this, [=](){ + if(ui->chkDragCopyPaste->isChecked()) { + ui->chkDragToInput->setChecked(false); + } + }); + QObject::connect(ui->chkDragToInput, &QRadioButton::clicked, this, [=](){ + if(ui->chkDragToInput->isChecked()) { + ui->chkDragCopyPaste->setChecked(false); + } + }); + } + + { + QObject::connect(ui->chkRKeyCopyPaste, &QRadioButton::clicked, this, [=](){ + if(ui->chkRKeyCopyPaste->isChecked()) { + ui->chkRKeyCopy->setChecked(false); + } + }); + QObject::connect(ui->chkRKeyCopy, &QRadioButton::clicked, this, [=](){ + if(ui->chkRKeyCopy->isChecked()) { + ui->chkRKeyCopyPaste->setChecked(false); + } + }); + } + if(m_ttyType == ETTY_LocalShell) { ui->shellPathGroup->setVisible(true); ui->shellPath->setReadOnly(true); @@ -235,6 +279,19 @@ QWoSessionTTYProperty::QWoSessionTTYProperty(ETTYType type, QWidget *parent) m_item->setPositionAsString(pos); } + { + QObject::connect(ui->btnClone, SIGNAL(clicked()), this, SLOT(onCloneButtonClicked())); + QObject::connect(ui->btnReload, SIGNAL(clicked()), this, SLOT(onReloadButtonClicked())); + QObject::connect(ui->btnRename, SIGNAL(clicked()), this, SLOT(onRenameButtonClicked())); + QObject::connect(ui->btnRemove, SIGNAL(clicked()), this, SLOT(onDeleteButtonClicked())); + QObject::connect(ui->btnWrite, SIGNAL(clicked()), this, SLOT(onWriteButtonClicked())); + + QObject::connect(ui->btnKeyAdd, SIGNAL(clicked()), this, SLOT(onKeyAddButtonClicked())); + QObject::connect(ui->btnKeyCopy, SIGNAL(clicked()), this, SLOT(onKeyCopyButtonClicked())); + QObject::connect(ui->btnKeyModify, SIGNAL(clicked()), this, SLOT(onKeyModifyButtonClicked())); + QObject::connect(ui->btnKeyRemove, SIGNAL(clicked()), this, SLOT(onKeyRemoveButtonClicked())); + } + QObject::connect(ui->chkTileNone, SIGNAL(clicked()), this, SLOT(onTileButtonClicked())); QObject::connect(ui->chkTileH, SIGNAL(clicked()), this, SLOT(onTileButtonClicked())); QObject::connect(ui->chkTileV, SIGNAL(clicked()), this, SLOT(onTileButtonClicked())); @@ -243,6 +300,11 @@ QWoSessionTTYProperty::QWoSessionTTYProperty(ETTYType type, QWidget *parent) QObject::connect(ui->bkImgAlpha, SIGNAL(valueChanged(int)), this, SLOT(onBkImageAlphaValueChanged(int))); + QObject::connect(ui->btnKeyHelp, &QAbstractButton::clicked, this, [=](){ + QString url = QWoSetting::isChineseLanguageFile() ? "http://cn.woterm.com" : "http://en.woterm.com"; + url += "/docs/manual/termopts/ttyopts/"; + QDesktopServices::openUrl(url); + }); initDefault(); adjustSize(); } @@ -258,21 +320,21 @@ void QWoSessionTTYProperty::setCustom(const QVariantMap &_mdata) QVariantMap mdata = HostInfo::merge(global, _mdata); m_bCustom = true; - if(mdata.contains("colorSchema")){ + { QString schema = mdata.value("colorSchema", DEFAULT_COLOR_SCHEMA).toString(); ui->schema->setCurrentText(schema); m_term->setColorSchema(schema); } - if(mdata.contains("keyboard")) { - QString name = mdata.value("keyboard", DEFAULT_KEY_LAYOUT).toString(); - ui->kblayout->setCurrentText(name); + { + QString name = mdata.value("keyTranslator", DEFAULT_KEY_TRANSLATOR).toString(); + ui->keytab->setCurrentText(name); } - if(mdata.contains("textcodec")) { - QString name = mdata.value("textcodec", DEFAULT_TEXT_CODEC).toString(); + { + QString name = mdata.value("textCodec", DEFAULT_TEXT_CODEC).toString(); ui->codepage->setCurrentText(name); m_term->setTextCodec(name); } - if(mdata.contains("fontName")) { + { QFont ft = QFontDatabase::systemFont(QFontDatabase::FixedFont); QString fontName = mdata.value("fontName", ft.family()).toString(); int fontSize = mdata.value("fontSize", ft.pointSize()).toInt(); @@ -281,7 +343,7 @@ void QWoSessionTTYProperty::setCustom(const QVariantMap &_mdata) m_term->setTerminalFont(fontName, fontSize); refleshFontPreview(); } - if(mdata.contains("cursorType")) { + { QString cursorType = mdata.value("cursorType", "block").toString(); if(cursorType.isEmpty() || cursorType == "block") { ui->blockCursor->setChecked(true); @@ -291,26 +353,42 @@ void QWoSessionTTYProperty::setCustom(const QVariantMap &_mdata) ui->beamCursor->setChecked(true); } } - if(mdata.contains("historyLength")){ + { QString line = mdata.value("historyLength", QString("%1").arg(DEFAULT_HISTORY_LINE_LENGTH)).toString(); ui->lineSize->setText(line); } - if(mdata.contains("dragPaste")) { + { bool checked = mdata.value("dragPaste", false).toBool(); ui->chkDragCopyPaste->setChecked(checked); } - if(mdata.contains("rkeyPaste")) { + { + bool checked = mdata.value("dragInput", false).toBool(); + ui->chkDragToInput->setChecked(checked); + } + { + bool checked = mdata.value("selectCopy", false).toBool(); + ui->chkSelectToCopy->setChecked(checked); + } + { + bool checked = mdata.value("rkeyCopy", false).toBool(); + ui->chkRKeyCopy->setChecked(checked); + } + { bool checked = mdata.value("rkeyPaste", false).toBool(); ui->chkRKeyCopyPaste->setChecked(checked); } - if(mdata.contains("keySequence")) { - setShortCut(mdata.value("keySequence").toMap()); - } - if(mdata.contains("shellPath")) { + + { QString path = mdata.value("shellPath").toString(); ui->shellPath->setText(path); } + { + QString name = mdata.value("keyTranslator", DEFAULT_KEY_TRANSLATOR).toString(); + if(!m_keysModel->load(name)) { + reloadKeytabsLater(name); + } + } } QVariantMap QWoSessionTTYProperty::result() const @@ -318,6 +396,189 @@ QVariantMap QWoSessionTTYProperty::result() const return m_result; } +void QWoSessionTTYProperty::onCloneButtonClicked() +{ + QString name = ui->keytab->currentText(); + QString path = QKxUtils::keytabPath(name); + QFileInfo fi(path); + if(!fi.exists()) { + QKxMessageBox::information(this, tr("File information"), tr("File is not exist.")); + return; + } + int tryLeft = 3; + QString fileName = name; + QStringList keytabsAll = QKxUtils::availableKeytabs(); + while(tryLeft-- > 0) { + fileName = QInputDialog::getText(this, tr("File name"), tr("Input a file name to save."), QLineEdit::Normal, fileName); + if(fileName.isEmpty()) { + return; + } + if(keytabsAll.contains(fileName)) { + QKxMessageBox::information(this, tr("File information"), tr("A file with the same name already exists,Please input a new one.")); + continue; + } + QString pathCustom = QWoSetting::customKeytabPath(); + pathCustom = pathCustom+"/"+fileName; + if(!fileName.endsWith(".keytab")) { + pathCustom += ".keytab"; + } + if(QFile::copy(path, pathCustom)) { + reloadKeytabsLater(fileName); + return; + } + QKxMessageBox::information(this, tr("File information"), tr("Failed to create file. Please check if the file name is a valid file name.")); + } +} + +void QWoSessionTTYProperty::onReloadButtonClicked() +{ + reloadKeytabsLater(); +} + +void QWoSessionTTYProperty::onRenameButtonClicked() +{ + QString name = ui->keytab->currentText(); + if(isPrivateKeytab(name)) { + QKxMessageBox::information(this, tr("Rename information"), tr("Internal private directory does not support this operation.")); + return; + } + QString path = QKxUtils::keytabPath(name); + QFileInfo fi(path); + QString fileName = QInputDialog::getText(this, tr("File information"), tr("Input a new file name"), QLineEdit::Normal, name); + if(fileName.isEmpty()) { + return; + } + QStringList all = QKxUtils::availableKeytabs(); + if(all.contains(fileName)) { + QKxMessageBox::information(this, tr("Rename information"), tr("The name already exist, please input a new one.")); + return; + } + QString pathNew = fi.absolutePath(); + pathNew += "/" + fileName; + if(!pathNew.endsWith(".keytab")) { + pathNew += ".keytab"; + } + if(!QFile::rename(fi.absoluteFilePath(), pathNew)) { + QKxMessageBox::information(this, tr("Rename information"), tr("Failed to rename file")+":"+name); + return; + } + reloadKeytabsLater(fileName); +} + +void QWoSessionTTYProperty::onDeleteButtonClicked() +{ + QString name = ui->keytab->currentText(); + if(isPrivateKeytab(name)) { + QKxMessageBox::information(this, tr("Delete information"), tr("Internal private directory does not support this operation.")); + return; + } + QString path = QKxUtils::keytabPath(name); + if(QFile::remove(path)) { + reloadKeytabsLater(); + } +} + +void QWoSessionTTYProperty::onWriteButtonClicked() +{ + QString name = ui->keytab->currentText(); + if(isPrivateKeytab(name)) { + QKxMessageBox::information(this, tr("Operation information"), tr("Modifying internal private files is not supported. Please clone a file before editing.")); + return; + } + m_keysModel->save(":/woterm/resource/keytab.template"); +} + +void QWoSessionTTYProperty::onKeyAddButtonClicked() +{ + if(!switchToModify()) { + return; + } + QKxCombineKeyActionDialog dlg(this); + QObject::connect(&dlg, &QKxCombineKeyActionDialog::messageArrived, this, [=](const QString& title, const QString& msg){ + QKxMessageBox::information(this, title, msg); + }); + if(dlg.exec() == QDialog::Accepted+1) { + QKxKeyTranslator::KeyInfo ki = dlg.result(); + QModelIndex idx = m_keysModel->add(ki); + ui->keymap->setCurrentIndex(idx); + ui->keymap->scrollTo(idx); + } +} + +void QWoSessionTTYProperty::onKeyCopyButtonClicked() +{ + if(!switchToModify()) { + return; + } + + QModelIndex idx = ui->keymap->currentIndex(); + if(!idx.isValid()) { + QKxMessageBox::information(this, tr("Parameter error"), tr("No selection to modify")); + return; + } + QKxKeyTranslator::KeyInfo ki; + if(!m_keysModel->data(idx.row(), ki)) { + QKxMessageBox::information(this, tr("Parameter error"), tr("No selection to modify")); + return; + } + QModelIndex idx2 = m_keysModel->add(ki); + ui->keymap->setCurrentIndex(idx2); +} + +void QWoSessionTTYProperty::onKeyModifyButtonClicked() +{ + if(!switchToModify()) { + return; + } + QModelIndex idx = ui->keymap->currentIndex(); + if(!idx.isValid()) { + QKxMessageBox::information(this, tr("Parameter error"), tr("No selection to modify")); + return; + } + onKeymapItemDBClicked(idx); +} + +void QWoSessionTTYProperty::onKeyRemoveButtonClicked() +{ + if(!switchToModify()) { + return; + } + QModelIndex idx = ui->keymap->currentIndex(); + if(!idx.isValid()) { + QKxMessageBox::information(this, tr("Parameter error"), tr("No selection to modify")); + return; + } + m_keysModel->remove(idx); + if(idx.row() >= m_keysModel->count()) { + idx = idx.siblingAtRow(idx.row()-1); + } + ui->keymap->setCurrentIndex(idx); + ui->keymap->scrollTo(idx); +} + +void QWoSessionTTYProperty::onKeymapItemDBClicked(const QModelIndex &idx) +{ + if(!switchToModify()) { + return; + } + QKxCombineKeyActionDialog dlg(this); + QObject::connect(&dlg, &QKxCombineKeyActionDialog::messageArrived, this, [=](const QString& title, const QString& msg){ + QKxMessageBox::information(this, title, msg); + }); + QKxKeyTranslator::KeyInfo ki; + if(!m_keysModel->data(idx.row(), ki)) { + QKxMessageBox::information(this, tr("Parameter error"), tr("No selection to modify")); + return; + } + dlg.init(ki); + if(dlg.exec() == QDialog::Accepted+1) { + QKxKeyTranslator::KeyInfo ki = dlg.result(); + QModelIndex idx2 = m_keysModel->modify(idx, ki); + ui->keymap->setCurrentIndex(idx2); + ui->keymap->scrollTo(idx2); + } +} + void QWoSessionTTYProperty::onPageCurrentChanged(int idx) { switch (idx) { @@ -341,6 +602,32 @@ void QWoSessionTTYProperty::onColorCurrentIndexChanged(const QString &txt) m_term->setColorSchema(txt); } +void QWoSessionTTYProperty::onKeytabCurrentIndexChanged(const QString &txt) +{ + if(txt.isEmpty()) { + return; + } + + + QString path = QDir::cleanPath(QKxUtils::keytabPath(txt)); + QString pathPrv = QDir::cleanPath(QWoSetting::privatePath()); + bool isPrv = path.startsWith(pathPrv); + ui->btnRemove->setVisible(!isPrv); + ui->btnRename->setVisible(!isPrv); + ui->btnWrite->setVisible(!isPrv); + + //ui->btnKeyAdd->setEnabled(!isPrv); + //ui->btnKeyCopy->setEnabled(!isPrv); + //ui->btnKeyModify->setEnabled(!isPrv); + //ui->btnKeyRemove->setEnabled(!isPrv); + + saveShortcut(); + if(!m_keysModel->load(txt)) { + QKxMessageBox::information(this, tr("Shortcut information"), tr("Failed to load keytab file")+":"+txt); + reloadKeytabsLater(DEFAULT_KEY_TRANSLATOR); + } +} + void QWoSessionTTYProperty::onFontCurrentIndexChanged(const QString &family) { QStringList styles = QKxUtils::familyStyles(family); @@ -418,18 +705,6 @@ void QWoSessionTTYProperty::onShellPathButtonClicked() } } -void QWoSessionTTYProperty::onItemDoubleClicked(const QModelIndex &index) -{ - QAbstractItemModel *model = ui->keymap->model(); - if(index.column() == 1) { - return; - } - int row = index.row(); - QModelIndex idx = model->index(row, 1, index.parent()); - QRect rt = ui->keymap->visualRect(idx); - ui->keymap->edit(idx); -} - void QWoSessionTTYProperty::onButtonImportClicked() { QString pathLast = QWoSetting::value("fontImport/lastPath", QDir::homePath()).toString(); @@ -494,41 +769,45 @@ void QWoSessionTTYProperty::onSelectButtonClicked() ui->bkImage->setText(file); } +void QWoSessionTTYProperty::saveShortcut(bool force) +{ + if(m_keysModel->hasModified()) { + if(force) { + m_keysModel->save(":/woterm/resource/keytab.template"); + return; + } + int code = QKxMessageBox::information(this, tr("Modify information"), tr("The shortcut file[%1] has been modified, do you need to save it?").arg(m_keysModel->name()), QMessageBox::Yes|QMessageBox::No); + if(code == QMessageBox::Yes) { + m_keysModel->save(":/woterm/resource/keytab.template"); + } + } +} + QVariantMap QWoSessionTTYProperty::save() { QVariantMap mvar; + saveShortcut(); + QString schema = ui->schema->currentText(); - QString keyboard = ui->kblayout->currentText(); + QString keytab = ui->keytab->currentText(); QString textcodec = ui->codepage->currentText(); QString fontName = ui->fontChooser->currentText(); int fontSize = ui->fontSize->value(); QString cursorType = ui->blockCursor->isChecked() ? "block" : (ui->beamCursor->isChecked() ? "beam": "underline"); QString lineSize = ui->lineSize->text(); mvar.insert("colorSchema", schema); - mvar.insert("keyboard", keyboard); + mvar.insert("keyTranslator", keytab); mvar.insert("textcodec", textcodec); mvar.insert("fontName", fontName); mvar.insert("fontSize", fontSize); mvar.insert("cursorType", cursorType); mvar.insert("historyLength", lineSize); + mvar.insert("rkeyCopy", ui->chkRKeyCopy->isChecked()); mvar.insert("rkeyPaste", ui->chkRKeyCopyPaste->isChecked()); mvar.insert("dragPaste", ui->chkDragCopyPaste->isChecked()); - - QWoShortCutModel *model = qobject_cast(ui->keymap->model()); - QMap all = model->toMap(); - QVariantMap mdata; - mdata.insert("SCK_Copy", all.value(QKxTermItem::SCK_Copy)); - mdata.insert("SCK_Paste", all.value(QKxTermItem::SCK_Paste)); - mdata.insert("SCK_SelectAll", all.value(QKxTermItem::SCK_SelectAll)); - mdata.insert("SCK_SelectLeft", all.value(QKxTermItem::SCK_SelectLeft)); - mdata.insert("SCK_SelectRight", all.value(QKxTermItem::SCK_SelectRight)); - mdata.insert("SCK_SelectUp", all.value(QKxTermItem::SCK_SelectUp)); - mdata.insert("SCK_SelectDown", all.value(QKxTermItem::SCK_SelectDown)); - mdata.insert("SCK_SelectHome", all.value(QKxTermItem::SCK_SelectHome)); - mdata.insert("SCK_SelectEnd", all.value(QKxTermItem::SCK_SelectEnd)); - - mvar.insert("keySequence", mdata); + mvar.insert("dragInput", ui->chkDragToInput->isChecked()); + mvar.insert("selectCopy", ui->chkSelectToCopy->isChecked()); if(m_ttyType == ETTY_LocalShell) { mvar.insert("shellPath", ui->shellPath->text()); @@ -557,11 +836,11 @@ void QWoSessionTTYProperty::initDefault() ui->schema->setCurrentText(schema); m_term->setColorSchema(schema); - QString keyboard = mdata.value("keyboard", DEFAULT_KEY_LAYOUT).toString(); - ui->kblayout->setCurrentText(keyboard); - m_term->setKeyLayoutByName(keyboard); + QString keytab = mdata.value("keyTranslator", DEFAULT_KEY_TRANSLATOR).toString(); + ui->keytab->setCurrentText(keytab); + m_term->setKeyTranslatorByName(keytab); - QString codec = mdata.value("textcodec", DEFAULT_TEXT_CODEC).toString(); + QString codec = mdata.value("textCodec", DEFAULT_TEXT_CODEC).toString(); ui->codepage->setCurrentText(codec); m_term->setTextCodec(codec); @@ -583,41 +862,38 @@ void QWoSessionTTYProperty::initDefault() } QString line = mdata.value("historyLength", QString("%1").arg(DEFAULT_HISTORY_LINE_LENGTH)).toString(); ui->lineSize->setText(line); - { bool checked = mdata.value("dragPaste", false).toBool(); ui->chkDragCopyPaste->setChecked(checked); } + { + bool checked = mdata.value("dragInput", false).toBool(); + ui->chkDragToInput->setChecked(checked); + } + { + bool checked = mdata.value("rkeyCopy", false).toBool(); + ui->chkRKeyCopy->setChecked(checked); + } { bool checked = mdata.value("rkeyPaste", false).toBool(); ui->chkRKeyCopyPaste->setChecked(checked); } + { + bool checked = mdata.value("selectCopy", false).toBool(); + ui->chkSelectToCopy->setChecked(checked); + } if(m_ttyType == ETTY_LocalShell){ QString shellPath = mdata.value("shellPath", QWoUtils::findShellPath()).toString(); ui->shellPath->setText(shellPath); } - setShortCut(mdata.value("keySequence").toMap()); -} - -void QWoSessionTTYProperty::setShortCut(const QVariantMap &mdata) -{ - QWoShortCutModel *model = new QWoShortCutModel(ui->keymap, this); - ui->keymap->setModel(model); - model->append(tr("Edit")); - model->append(QKxTermItem::SCK_Copy, tr("Copy"), mdata.value("SCK_Copy", m_term->defaultShortCutKey(QKxTermItem::SCK_Copy)).value()); - model->append(QKxTermItem::SCK_Paste, tr("Paste"), mdata.value("SCK_Paste", m_term->defaultShortCutKey(QKxTermItem::SCK_Paste)).value()); - model->append(QKxTermItem::SCK_SelectAll, tr("SelectAll"), mdata.value("SCK_SelectAll", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectAll)).value()); - model->append(QKxTermItem::SCK_SelectLeft, tr("Select text from right to left"), mdata.value("SCK_SelectLeft", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectLeft)).value()); - model->append(QKxTermItem::SCK_SelectRight, tr("Select text from left to right"), mdata.value("SCK_SelectRight", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectRight)).value()); - model->append(QKxTermItem::SCK_SelectUp, tr("Select text from bottom to up"), mdata.value("SCK_SelectUp", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectUp)).value()); - model->append(QKxTermItem::SCK_SelectDown, tr("Select text from top to bottom"), mdata.value("SCK_SelectDown", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectDown)).value()); - model->append(QKxTermItem::SCK_SelectHome, tr("Select text from from line start to end"), mdata.value("SCK_SelectHome", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectHome)).value()); - model->append(QKxTermItem::SCK_SelectEnd, tr("Select text from current to line end"), mdata.value("SCK_SelectEnd", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectEnd)).value()); - ui->keymap->setColumnWidth(0, model->widthColumn(ui->keymap->font(), 0)); - ui->keymap->setItemDelegate(new QWoKeySequenceDelegate(this)); - QObject::connect(ui->keymap, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(onItemDoubleClicked(QModelIndex))); + { + QString name = mdata.value("keyTranslator", DEFAULT_KEY_TRANSLATOR).toString(); + if(!m_keysModel->load(name)) { + reloadKeytabsLater(name); + } + } } void QWoSessionTTYProperty::setFixPreviewString() @@ -642,6 +918,13 @@ void QWoSessionTTYProperty::refleshFontPreview() } } +bool QWoSessionTTYProperty::isPrivateKeytab(const QString &name) +{ + QString path = QDir::cleanPath(QKxUtils::keytabPath(name)); + QString pathPrv = QDir::cleanPath(QWoSetting::privatePath()); + return path.startsWith(pathPrv); +} + bool QWoSessionTTYProperty::importFont(const QStringList& allFamilies, const QString &fileName) { QFileInfo fi(fileName); @@ -678,7 +961,7 @@ bool QWoSessionTTYProperty::importFont(const QStringList& allFamilies, const QSt QKxMessageBox::warning(this, tr("Import errors"), tr("Failed to open the font file.")+QString("[%1,%2]").arg(fileName).arg(f.errorString())); return false; } - QString path = QWoSetting::fontBackupPath(); + QString path = QWoSetting::customFontPath(); QString fileNew = path + "/" + fi.fileName(); if(!f.copy(fileNew)) { QKxMessageBox::warning(this, tr("Import errors"), tr("Failed to backup the font file.")+QString("[%1,%2]").arg(fileName).arg(f.errorString())); @@ -686,3 +969,63 @@ bool QWoSessionTTYProperty::importFont(const QStringList& allFamilies, const QSt } return true; } + +void QWoSessionTTYProperty::reloadKeytabsLater(const QString& name) +{ + QString nameNow = ui->keytab->currentText(); + QKxUtils::cleanupKeytabs(); + QStringList all = QKxUtils::availableKeytabs(); + QStringListModel *model = qobject_cast(ui->keytab->model()); + model->setStringList(all); + + QString target = name; + if(name.isEmpty() || !all.contains(name)) { + target = nameNow; + } + if(target.isEmpty() || !all.contains(target)) { + target = DEFAULT_KEY_TRANSLATOR; + } + ui->keytab->setCurrentText(target); +} + +bool QWoSessionTTYProperty::switchToModify() +{ + QString name = ui->keytab->currentText(); + if(isPrivateKeytab(name)) { + QStringList keytabsAll = QKxUtils::availableKeytabs(); + for(int i = 0; i < 3; i++) { + QStringList tips; + tips.append(tr("The current profile does not support modification, a new profile will be created.")); + tips.append(tr("Please input a profile name.")); + QString fileName = QInputDialog::getText(this, tr("File name"), tips.join("\r\n"), QLineEdit::Normal, name); + if(fileName.isEmpty()) { + return false; + } + if(keytabsAll.contains(fileName)) { + if(isPrivateKeytab(fileName)) { + QKxMessageBox::information(this, tr("File information"), tr("Modifying internal private files is not supported. Please input a new one.")); + continue; + } + if(QKxMessageBox::information(this, tr("File information"), tr("Found a profile with the same name. Do you want to switch to that profile?"), QMessageBox::Yes|QMessageBox::No) != QMessageBox::Yes) { + return false; + } + ui->keytab->setCurrentText(fileName); + reloadKeytabsLater(fileName); + return true; + }else{ + QString pathCustom = QWoSetting::customKeytabPath(); + pathCustom = pathCustom+"/"+fileName; + if(!fileName.endsWith(".keytab")) { + pathCustom += ".keytab"; + } + QString path = QDir::cleanPath(QKxUtils::keytabPath(name)); + if(!QFile::copy(path, pathCustom)) { + return false; + } + reloadKeytabsLater(fileName); + return true; + } + } + } + return true; +} diff --git a/woterm/qwosessionttyproperty.h b/woterm/qwosessionttyproperty.h index cf3e826..c4df006 100644 --- a/woterm/qwosessionttyproperty.h +++ b/woterm/qwosessionttyproperty.h @@ -55,6 +55,7 @@ class QFontCleanDelegate : public QStyledItemDelegate QEvent::Type m_event; }; +class QKxKeyTranslatorModel; class QWoSessionTTYProperty : public QDialog { Q_OBJECT @@ -71,8 +72,19 @@ class QWoSessionTTYProperty : public QDialog void setCustom(const QVariantMap& prop); QVariantMap result() const; private slots: + void onCloneButtonClicked(); + void onReloadButtonClicked(); + void onRenameButtonClicked(); + void onDeleteButtonClicked(); + void onWriteButtonClicked(); + void onKeyAddButtonClicked(); + void onKeyCopyButtonClicked(); + void onKeyModifyButtonClicked(); + void onKeyRemoveButtonClicked(); + void onKeymapItemDBClicked(const QModelIndex& idx); void onPageCurrentChanged(int idx); void onColorCurrentIndexChanged(const QString & txt); + void onKeytabCurrentIndexChanged(const QString& txt); void onFontCurrentIndexChanged(const QString& family); void onStyleCurrentIndexChanged(const QString& style); void onFontValueChanged(int v); @@ -84,19 +96,21 @@ private slots: void onButtonSaveClicked(); void onButtonSaveToAllClicked(); void onShellPathButtonClicked(); - void onItemDoubleClicked(const QModelIndex &index); void onButtonImportClicked(); void onFontFamilyRemove(const QString& family); void onTileButtonClicked(); void onSelectButtonClicked(); private: + void saveShortcut(bool force = false); QVariantMap save(); void initDefault(); - void setShortCut(const QVariantMap& dm); void setFixPreviewString(); void refleshFontPreview(); + bool isPrivateKeytab(const QString& name); bool importFont(const QStringList& families, const QString& fileName); + void reloadKeytabsLater(const QString& name = QString()); + bool switchToModify(); private: Ui::QWoSessionTTYProperty *ui; bool m_bCustom; @@ -107,7 +121,7 @@ private slots: QPointer m_term; QPointer m_delegate; QPointer m_item; - + QPointer m_keysModel; }; #endif // QWOSESSIONTTYPROPERTY_H diff --git a/woterm/qwosessionttyproperty.ui b/woterm/qwosessionttyproperty.ui index 2b26807..9418324 100644 --- a/woterm/qwosessionttyproperty.ui +++ b/woterm/qwosessionttyproperty.ui @@ -6,8 +6,8 @@ 0 0 - 584 - 438 + 616 + 447 @@ -16,6 +16,9 @@ + + 2 + @@ -245,20 +248,17 @@ 0 - - - - Keyboard Layout + Key profile - + 0 @@ -267,6 +267,169 @@ + + + + Reload + + + + ../private/skins/black/reload.png../private/skins/black/reload.png + + + + + + + Clone + + + + ../private/skins/black/copy.png../private/skins/black/copy.png + + + + + + + Save + + + + ../private/skins/black/save.png../private/skins/black/save.png + + + + + + + Delete + + + + ../private/skins/black/remove.png../private/skins/black/remove.png + + + + + + + Rename + + + + + + + + + + 0 + 0 + + + + + + + + + + Help + + + + ../private/skins/black/help2.png../private/skins/black/help2.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Add + + + + ../private/skins/black/add2.png../private/skins/black/add2.png + + + + + + + Copy + + + + ../private/skins/black/ftp.png../private/skins/black/ftp.png + + + + + + + Modify + + + + ../private/skins/black/edit.png../private/skins/black/edit.png + + + + + + + Remove + + + + ../private/skins/black/remove.png../private/skins/black/remove.png + + + + + + + + + + + + 24 + 24 + + + + + + + ../private/skins/black/tips.png + + + true + + + + + + + Keyboard events that are not listed will be handled by default. + + + @@ -389,10 +552,31 @@ Copy and paste + + + + Drag the selected text to input. + + + - Drag the selected text to copy and paste + Drag the selected text to copy and paste it. + + + + + + + Auto copy when selecting text. + + + + + + + Right click the selected text to copy. diff --git a/woterm/qwosetting.cpp b/woterm/qwosetting.cpp index c2ab1f6..1022ff7 100644 --- a/woterm/qwosetting.cpp +++ b/woterm/qwosetting.cpp @@ -139,7 +139,7 @@ QString QWoSetting::tempPath() return path; } -QString QWoSetting::fontBackupPath() +QString QWoSetting::customFontPath() { QString path = QWoSetting::ensurePath("font"); path = QDir::cleanPath(path); @@ -147,6 +147,14 @@ QString QWoSetting::fontBackupPath() return path; } +QString QWoSetting::customKeytabPath() +{ + QString path = QWoSetting::ensurePath("keytab"); + path = QDir::cleanPath(path); + path = QDir::toNativeSeparators(path); + return path; +} + QString QWoSetting::cachePath() { QString path = QWoSetting::ensurePath("cache"); diff --git a/woterm/qwosetting.h b/woterm/qwosetting.h index a08d0ea..39516dd 100644 --- a/woterm/qwosetting.h +++ b/woterm/qwosetting.h @@ -49,7 +49,8 @@ class QWoSetting : public QKxSetting static QString sftpTaskDbPath(); static QString sftpTaskLogPath(); static QString tempPath(); - static QString fontBackupPath(); + static QString customFontPath(); + static QString customKeytabPath(); static QString cachePath(); static QString viewPath(); static QString fileIconCachePath(); diff --git a/woterm/qwosshtermwidget.cpp b/woterm/qwosshtermwidget.cpp index 318f469..ce7101b 100644 --- a/woterm/qwosshtermwidget.cpp +++ b/woterm/qwosshtermwidget.cpp @@ -28,6 +28,7 @@ #include "qkxtermwidget.h" #include "qkxtermitem.h" +#include "qkxkeytranslator.h" #include "qwoshower.h" @@ -561,8 +562,8 @@ void QWoSshTermWidget::resizeEvent(QResizeEvent *ev) void QWoSshTermWidget::contextMenuEvent(QContextMenuEvent *ev) { - if(m_rkeyPaste) { - if(pasteWhenOverSelectionText(ev->pos())) { + if(m_rkeyMode != ERKM_NotDefined) { + if(copyWhenOverSelectionText(ev->pos(), m_rkeyMode == ERKM_CopyPaste)) { return; } } @@ -589,7 +590,8 @@ void QWoSshTermWidget::contextMenuEvent(QContextMenuEvent *ev) menu.addAction(QIcon("../private/skins/black/sftp.png"), tr("Sftp Assistant"), this, SLOT(onSftpConnectReady())); menu.addAction(QIcon("../private/skins/black/sftp.png"), tr("Sftp independent tab"), this, SLOT(onSftpTabConnectReady())); - menu.addAction(QIcon("../private/skins/black/find.png"), tr("Find..."), this, SLOT(onShowFindBar()), QKeySequence(Qt::CTRL + Qt::Key_F)); + QKeySequence ksFind = m_term->keyTranslator()->shortcut(QKxKeyTranslator::EFind); + menu.addAction(QIcon("../private/skins/black/find.png"), tr("Find..."), this, SLOT(onShowFindBar()), ksFind); menu.addAction(QIcon("../private/skins/black/palette.png"), tr("Edit"), this, SLOT(onModifyThisSession())); menu.addAction(tr("Duplicate in new window"), this, SLOT(onDuplicateInNewWindow())); menu.addAction(tr("New session multiplex"), this, SLOT(onNewSessionMultiplex())); diff --git a/woterm/qwotelnettermwidget.cpp b/woterm/qwotelnettermwidget.cpp index 6a01481..b3b2f7e 100644 --- a/woterm/qwotelnettermwidget.cpp +++ b/woterm/qwotelnettermwidget.cpp @@ -27,6 +27,7 @@ #include "qkxtermwidget.h" #include "qkxtermitem.h" +#include "qkxkeytranslator.h" #include "qkxmessagebox.h" @@ -399,8 +400,8 @@ void QWoTelnetTermWidget::resizeEvent(QResizeEvent *ev) void QWoTelnetTermWidget::contextMenuEvent(QContextMenuEvent *ev) { - if(m_rkeyPaste) { - if(pasteWhenOverSelectionText(ev->pos())) { + if(m_rkeyMode != ERKM_NotDefined) { + if(copyWhenOverSelectionText(ev->pos(), m_rkeyMode == ERKM_CopyPaste)) { return; } } @@ -427,7 +428,8 @@ void QWoTelnetTermWidget::contextMenuEvent(QContextMenuEvent *ev) m_menu->addAction(tr("Float This Tab"), this, SLOT(onFloatThisTab())); } - m_menu->addAction(QIcon("../private/skins/black/find.png"), tr("Find..."), this, SLOT(onShowFindBar()), QKeySequence(Qt::CTRL + Qt::Key_F)); + QKeySequence ksFind = m_term->keyTranslator()->shortcut(QKxKeyTranslator::EFind); + m_menu->addAction(QIcon("../private/skins/black/find.png"), tr("Find..."), this, SLOT(onShowFindBar()), ksFind); m_menu->addAction(QIcon("../private/skins/black/palette.png"), tr("Edit"), this, SLOT(onModifyThisSession())); //m_menu->addAction(QIcon("../private/skins/black/history.png"), tr("History"), this, SLOT(onSessionCommandHistory())); m_menu->addAction(tr("Duplicate in new window"), this, SLOT(onDuplicateInNewWindow())); diff --git a/woterm/qwotermwidget.cpp b/woterm/qwotermwidget.cpp index 9938933..4c4fc14 100644 --- a/woterm/qwotermwidget.cpp +++ b/woterm/qwotermwidget.cpp @@ -53,7 +53,8 @@ QWoTermWidget::QWoTermWidget(const QString& target, int gid, ETermType ttype, QW , m_target(target) , m_gid(gid) , m_bexit(false) - , m_rkeyPaste(false) + , m_rkeyMode(ERKM_NotDefined) + , m_selectCopy(false) , m_ttype(ttype) { static int idx = 0; @@ -69,20 +70,17 @@ QWoTermWidget::QWoTermWidget(const QString& target, int gid, ETermType ttype, QW m_loading = new QWoLoadingWidget(QColor("#1296DB"), this); - QString val = QWoSetting::value("property/shortcut").toString(); - QVariantMap mdata = QWoUtils::qBase64ToVariant(val).toMap(); - m_term->bindShortCut(QKxTermItem::SCK_Copy, mdata.value("SCK_Copy", m_term->defaultShortCutKey(QKxTermItem::SCK_Copy)).value()); - m_term->bindShortCut(QKxTermItem::SCK_Paste, mdata.value("SCK_Paste", m_term->defaultShortCutKey(QKxTermItem::SCK_Paste)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectAll, mdata.value("SCK_SelectAll", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectAll)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectLeft, mdata.value("SCK_SelectLeft", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectLeft)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectRight, mdata.value("SCK_SelectRight", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectRight)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectUp, mdata.value("SCK_SelectUp", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectUp)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectDown, mdata.value("SCK_SelectDown", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectDown)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectHome, mdata.value("SCK_SelectHome", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectHome)).value()); - m_term->bindShortCut(QKxTermItem::SCK_SelectEnd, mdata.value("SCK_SelectEnd", m_term->defaultShortCutKey(QKxTermItem::SCK_SelectEnd)).value()); - m_term->installEventFilter(this); m_gsTermsAll.append(this); + + QTimer *timer = new QTimer(this); + QObject::connect(timer, SIGNAL(timeout()), this, SLOT(onTimeoutToSelectCopy())); + QObject::connect((QKxTermItem*)m_term, &QKxTermItem::selectChanged, this, [=]{ + if(!m_selectCopy) { + return; + } + timer->start(300); + }); } QWoTermWidget::~QWoTermWidget() @@ -272,6 +270,23 @@ void QWoTermWidget::onFloatThisTab() } } +void QWoTermWidget::onTimeoutToSelectCopy() +{ + QTimer *timer = qobject_cast(sender()); + if(timer) { + timer->stop(); + } + if(!m_selectCopy) { + return; + } + QString txt = m_term->selectedText(); + if(txt.isEmpty()) { + return; + } + QClipboard *clip = QGuiApplication::clipboard(); + clip->setText(txt); +} + void QWoTermWidget::resetProperty(QVariantMap mdata, bool force) { if(mdata.isEmpty() && !force) { @@ -280,10 +295,10 @@ void QWoTermWidget::resetProperty(QVariantMap mdata, bool force) QString schema = mdata.value("colorSchema", DEFAULT_COLOR_SCHEMA).toString(); m_term->setColorSchema(schema); - QString keyboard = mdata.value("keyboard", DEFAULT_KEY_LAYOUT).toString(); - m_term->setKeyLayoutByName(keyboard); + QString keyboard = mdata.value("keyTranslator", DEFAULT_KEY_TRANSLATOR).toString(); + m_term->setKeyTranslatorByName(keyboard); - QString codec = mdata.value("textcodec", DEFAULT_TEXT_CODEC).toString(); + QString codec = mdata.value("textCodec", DEFAULT_TEXT_CODEC).toString(); m_term->setTextCodec(codec); QFont ft = QFontDatabase::systemFont(QFontDatabase::FixedFont); @@ -304,8 +319,24 @@ void QWoTermWidget::resetProperty(QVariantMap mdata, bool force) int lines = mdata.value("historyLength", DEFAULT_HISTORY_LINE_LENGTH).toInt(); m_term->setHistorySize(lines); bool dragPaste = mdata.value("dragPaste", false).toBool(); - m_term->setDragCopyAndPaste(dragPaste); - m_rkeyPaste = mdata.value("rkeyPaste", false).toBool(); + bool dragInput = mdata.value("dragInput", false).toBool(); + if(dragPaste) { + m_term->setDragTextMode(QKxTermItem::DTM_DragCopyAndPaste); + }else if(dragInput) { + m_term->setDragTextMode(QKxTermItem::DTM_DragToInput); + }else{ + m_term->setDragTextMode(QKxTermItem::DTM_NotDefined); + } + bool rkeyCopy = mdata.value("rkeyCopy", false).toBool(); + bool rkeyPaste = mdata.value("rkeyPaste", false).toBool(); + if(rkeyCopy) { + m_rkeyMode = ERKM_Copy; + }else if(rkeyPaste) { + m_rkeyMode = ERKM_CopyPaste; + }else{ + m_rkeyMode = ERKM_NotDefined; + } + m_selectCopy = mdata.value("selectCopy", false).toBool(); QString path = QWoSetting::terminalBackgroundImage(); diff --git a/woterm/qwotermwidget.h b/woterm/qwotermwidget.h index 78ab11d..645252a 100644 --- a/woterm/qwotermwidget.h +++ b/woterm/qwotermwidget.h @@ -53,6 +53,13 @@ class QWoTermWidget : public QKxTermWidget ETTSerialPort, ETTConsole }; + + enum ERightKeyMode { + ERKM_NotDefined, + ERKM_Copy, + ERKM_CopyPaste + }; + public: explicit QWoTermWidget(const QString& target, int gid, ETermType ttype, QWidget *parent=nullptr); virtual ~QWoTermWidget(); @@ -101,6 +108,7 @@ protected slots: void onOutputHistoryToFile(); void onStopOutputHistoryFile(); void onFloatThisTab(); + void onTimeoutToSelectCopy(); private: void resetProperty(QVariantMap data, bool force=false); @@ -119,7 +127,8 @@ protected slots: QString m_target; int m_gid; bool m_bexit; - bool m_rkeyPaste; + ERightKeyMode m_rkeyMode; + bool m_selectCopy; ETermType m_ttype; static QList> m_gsTermsAll; diff --git a/woterm/resource/keytab.template b/woterm/resource/keytab.template new file mode 100644 index 0000000..559c98d --- /dev/null +++ b/woterm/resource/keytab.template @@ -0,0 +1,55 @@ +# -------------------------------------------------------------- +# document help +# https://cn.woterm.com/docs/manual/termopts/ttyopts/ +# https://en.woterm.com/docs/manual/termopts/ttyopts/ +# private/keytabs/readme.docbook +# private/keytabs/keyinfo.txt +# -------------------------------------------------------------- + +# -------------------------------------------------------------- +# +# This configuration table allows to customize the +# meaning of the keys. +# +# The syntax is that each entry has the form : +# +# "key" Keyname { ("+"|"-") Modename } ":" (String|Operation) +# +# Keynames are those defined in with the +# "Qt::Key_" removed. (We'd better insert the list here) +# +# Mode names are : +# +# - Shift +# - Alt +# - Control +# +# The VT100 emulation has two modes that can affect the +# sequences emitted by certain keys. These modes are +# under control of the client program. +# +# - Newline : effects Return and Enter key. +# - Application : effects Up and Down key. +# +# - Ansi : effects Up and Down key (This is for VT52, really). +# Some keys are used by konsole to cause operations. +# scrollLineUp +# scrollPageUp +# scrollPromptUp +# scrollUpToTop +# scrollLineDown +# scrollPageDown +# scrollPromptDown +# scrollDownToBottom + +# selectLineLeft +# selectLineRight +# selectLineUp +# selectLineDown +# selectLineHome +# selectLineEnd +# selectAll + +keyboard "{{name}}" + +{{key}} diff --git a/woterm/version.h b/woterm/version.h index 34e6db2..d63af4a 100644 --- a/woterm/version.h +++ b/woterm/version.h @@ -1,4 +1,4 @@ #pragma once -#define WOTERM_VERSION ("9.28.13") +#define WOTERM_VERSION ("9.29.0") #define NOISE diff --git a/woterm/woterm.qrc b/woterm/woterm.qrc index df86fdd..78bc642 100644 --- a/woterm/woterm.qrc +++ b/woterm/woterm.qrc @@ -48,5 +48,6 @@ resource/images/exit.png resource/images/reload.png resource/images/edit.png + resource/keytab.template