From 22ecbe25a331c93ecbc67a1e6026ba5539c8d187 Mon Sep 17 00:00:00 2001 From: "Leroy.H.Y" Date: Wed, 27 Nov 2019 10:39:04 +0800 Subject: [PATCH] [fix] Several further fixes Former-commit-id: 12fb3bcd29a5cf6574fede9a1427fb0fa9d7c4dc --- Build.Counter | 2 +- src/QvCoreConfigOperations.hpp | 2 +- src/QvCoreConfigOperations_Convertion.cpp | 8 +- src/ui/w_ImportConfig.cpp | 77 +- src/ui/w_ImportConfig.hpp | 6 +- src/ui/w_InboundEditor.cpp | 15 +- src/ui/w_MainWindow.cpp | 105 ++- src/ui/w_MainWindow.hpp | 1 + src/ui/w_MainWindow.ui | 19 +- src/ui/w_OutboundEditor.cpp | 1 + src/ui/w_OutboundEditor.ui | 28 +- src/ui/w_RoutesEditor.cpp | 72 +- src/ui/w_RoutesEditor.hpp | 8 +- translations/en-US.ts | 919 +++++++++++++--------- 14 files changed, 759 insertions(+), 504 deletions(-) diff --git a/Build.Counter b/Build.Counter index 4fdb2984..cdfbfefd 100644 --- a/Build.Counter +++ b/Build.Counter @@ -1 +1 @@ -887 +917 diff --git a/src/QvCoreConfigOperations.hpp b/src/QvCoreConfigOperations.hpp index 90b8942c..53610f06 100644 --- a/src/QvCoreConfigOperations.hpp +++ b/src/QvCoreConfigOperations.hpp @@ -34,7 +34,7 @@ namespace Qv2ray bool RenameConnection(QString originalName, QString newName); // VMess URI Protocol QJsonObject ConvertConfigFromVMessString(QString vmess, QString *alias, QString *errMessage); - QJsonObject ConvertConfigFromFile(QString sourceFilePath, bool overrideInbounds); + QJsonObject ConvertConfigFromFile(QString sourceFilePath, bool keepInbounds); QString ConvertConfigToVMessString(const StreamSettingsObject &transfer, const VMessServerObject &serverConfig, const QString &alias); } diff --git a/src/QvCoreConfigOperations_Convertion.cpp b/src/QvCoreConfigOperations_Convertion.cpp index 64f97875..c76bdab9 100644 --- a/src/QvCoreConfigOperations_Convertion.cpp +++ b/src/QvCoreConfigOperations_Convertion.cpp @@ -1,4 +1,4 @@ -#include "QvCoreConfigOperations.hpp" +#include "QvCoreConfigOperations.hpp" namespace Qv2ray { @@ -224,7 +224,7 @@ namespace Qv2ray RROOT } - QJsonObject ConvertConfigFromFile(QString sourceFilePath, bool overrideInbounds) + QJsonObject ConvertConfigFromFile(QString sourceFilePath, bool keepInbounds) { QFile source(sourceFilePath); @@ -233,9 +233,9 @@ namespace Qv2ray return QJsonObject(); } - auto root = JsonFromString(StringFromFile(new QFile(sourceFilePath))); + auto root = JsonFromString(StringFromFile(&source)); - if (overrideInbounds) { + if (!keepInbounds) { JSON_ROOT_TRY_REMOVE("inbounds") } diff --git a/src/ui/w_ImportConfig.cpp b/src/ui/w_ImportConfig.cpp index 76f84560..c1e5ef42 100644 --- a/src/ui/w_ImportConfig.cpp +++ b/src/ui/w_ImportConfig.cpp @@ -24,6 +24,15 @@ ImportConfigWindow::ImportConfigWindow(QWidget *parent) nameTxt->setText(QDateTime::currentDateTime().toString("MM-dd_hh-mm") + "_" + tr("Imported") + "_"); } +QMap ImportConfigWindow::OpenImport(bool outboundsOnly) +{ + // if Outbound Only, set keepImported to false and disable the checkbox + // keepImportedInboundCheckBox->setChecked(!outboundsOnly); + keepImportedInboundCheckBox->setEnabled(!outboundsOnly); + this->exec(); + return this->result() == QDialog::Accepted ? connections : QMap(); +} + void ImportConfigWindow::on_importSourceCombo_currentIndexChanged(int index) { stackedWidget->setCurrentIndex(index); @@ -37,17 +46,7 @@ void ImportConfigWindow::on_selectFileBtn_clicked() void ImportConfigWindow::on_qrFromScreenBtn_clicked() { - // QRubberBand QThread::msleep(static_cast(doubleSpinBox->value() * 1000)); - //bool hasVmessDetected = false; - //for (auto screen : qApp->screens()) { - // if (!screen) { - // LOG(MODULE_UI, "Cannot even find a screen. RARE") - // QvMessageBox(this, tr("Screenshot failed"), tr("Cannot find a valid screen, it's rare.")); - // return; - // } - //auto pix = screen->grabWindow(0); - // ScreenShotWindow w; auto pix = w.DoScreenShot(); @@ -57,32 +56,25 @@ void ImportConfigWindow::on_qrFromScreenBtn_clicked() if (str.trimmed().isEmpty()) { LOG(MODULE_UI, "Cannot decode QR Code from an image, size: h=" + to_string(pix.width()) + ", v=" + to_string(pix.height())) QvMessageBox(this, tr("Capture QRCode"), tr("Cannot find a valid QRCode from this region.")); - // continue; } else { vmessConnectionStringTxt->appendPlainText(str.trimmed() + NEWLINE); - //hasVmessDetected = true; } } - - //} - //if (!hasVmessDetected) { - //QvMessageBox(this, tr("QRCode scanning failed"), tr("Cannot find any QRCode from any screens.")); - //} } void ImportConfigWindow::on_beginImportBtn_clicked() { QString aliasPrefix = nameTxt->text(); QJsonObject config; - auto conf = GetGlobalConfig(); + //auto conf = GetGlobalConfig(); switch (importSourceCombo->currentIndex()) { case 0: { // From File... - bool overrideInBound = !keepImportedInboundCheckBox->isChecked(); + bool keepInBound = keepImportedInboundCheckBox->isChecked(); QString path = fileLineTxt->text(); aliasPrefix = aliasPrefix.isEmpty() ? aliasPrefix : QFileInfo(path).fileName(); - config = ConvertConfigFromFile(path, overrideInBound); + config = ConvertConfigFromFile(path, keepInBound); if (config.isEmpty()) { QvMessageBox(this, tr("Import config file"), tr("Import from file failed, for more information, please check the log file.")); @@ -91,14 +83,7 @@ void ImportConfigWindow::on_beginImportBtn_clicked() QvMessageBox(this, tr("Import config file"), tr("Failed to check the validity of the config file.")); return; } else { - // We save first, "alias" may change to prevent override existing file. - bool alwaysFalse = SaveConnectionConfig(config, &aliasPrefix, false); - - if (alwaysFalse) { - QvMessageBox(this, tr("Assertion Failed"), tr("Assertion failed: ::SaveConnectionConfig should returns false.")); - } - - conf.configs.push_back(aliasPrefix.toStdString()); + connections[aliasPrefix] = config; break; } } @@ -125,13 +110,7 @@ void ImportConfigWindow::on_beginImportBtn_clicked() vmessErrors[vmess] = QString::number(vmessErrors.count() + 1) + ": " + errMessage; continue; } else { - bool alwaysFalse = SaveConnectionConfig(config, &aliasPrefix, false); - - if (alwaysFalse) { - QvMessageBox(this, tr("Assertion Failed"), "Assertion failed: ::SaveConnectionConfig should returns false."); - } - - conf.configs.push_back(aliasPrefix.toStdString()); + connections[aliasPrefix] = config; } } @@ -141,24 +120,19 @@ void ImportConfigWindow::on_beginImportBtn_clicked() vmessConnectionStringTxt->appendPlainText(vmessErrors.key(item)); errorsList->addItem(item); } - - // Don't quit; - return; } break; } case 2: { + QvMessageBox(this, tr("TODO"), tr("TODO")); // Subscription link. break; } } - SetGlobalConfig(conf); - // Never restart current connection after import. - emit s_reload_config(false); - close(); + accept(); } void ImportConfigWindow::on_selectImageBtn_clicked() { @@ -197,7 +171,7 @@ void ImportConfigWindow::on_errorsList_currentItemChanged(QListWidgetItem *curre return; } - // + // Select vmess string that is invalid. QTextCursor c = vmessConnectionStringTxt->textCursor(); c.setPosition(startPos); c.setPosition(endPos, QTextCursor::KeepAnchor); @@ -218,8 +192,7 @@ void ImportConfigWindow::on_editFileBtn_clicked() if (!jsonCheckingError.isEmpty()) { LOG(MODULE_FILE, "Currupted JSON file detected") - if (QvMessageBoxAsk(this, tr("Edit file as JSON"), tr("The file you selected has json syntax error. Continue editing may make you lose data. Would you like to continue?") + - NEWLINE + jsonCheckingError) != QMessageBox::Yes) { + if (QvMessageBoxAsk(this, tr("Edit file as JSON"), tr("The file you selected has json syntax error. Continue editing may make you lose data. Would you like to continue?") + NEWLINE + jsonCheckingError) != QMessageBox::Yes) { return; } else { LOG(MODULE_FILE, "Continue editing curruped json file, data loss is expected.") @@ -257,13 +230,15 @@ void ImportConfigWindow::on_connectionEditBtn_clicked() root.insert("outbounds", outboundsList); // // WARN This one will change the connection name, because of some duplicates. - SaveConnectionConfig(root, &alias, false); + connections[alias] = root; + //SaveConnectionConfig(root, &alias, false); // - auto conf = GetGlobalConfig(); - auto connectionList = conf.configs; - connectionList.push_back(alias.toStdString()); - conf.configs = connectionList; - SetGlobalConfig(conf); + // WARN Add connection here + //auto conf = GetGlobalConfig(); + //auto connectionList = conf.configs; + //connectionList.push_back(alias.toStdString()); + //conf.configs = connectionList; + //SetGlobalConfig(conf); close(); } else { return; diff --git a/src/ui/w_ImportConfig.hpp b/src/ui/w_ImportConfig.hpp index 64fa0bfd..1746901c 100644 --- a/src/ui/w_ImportConfig.hpp +++ b/src/ui/w_ImportConfig.hpp @@ -2,6 +2,8 @@ #define IMPORTCONF_H #include +#include +#include #include "ui_w_ImportConfig.h" class ImportConfigWindow : public QDialog, private Ui::ImportConfigWindow @@ -11,8 +13,7 @@ class ImportConfigWindow : public QDialog, private Ui::ImportConfigWindow public: explicit ImportConfigWindow(QWidget *parent = nullptr); ~ImportConfigWindow() { } - signals: - void s_reload_config(bool need_restart); + QMap OpenImport(bool outboundsOnly = false); private slots: void on_importSourceCombo_currentIndexChanged(int index); @@ -30,6 +31,7 @@ class ImportConfigWindow : public QDialog, private Ui::ImportConfigWindow void on_cancelImportBtn_clicked(); private: + QMap connections; QMap vmessErrors; }; diff --git a/src/ui/w_InboundEditor.cpp b/src/ui/w_InboundEditor.cpp index b66a31fc..98f0b66d 100644 --- a/src/ui/w_InboundEditor.cpp +++ b/src/ui/w_InboundEditor.cpp @@ -24,9 +24,13 @@ InboundEditor::InboundEditor(QJsonObject root, QWidget *parent) : } else if (inboundType == "mtproto") { mtSettings = root["settings"].toObject(); } else { - LOG(MODULE_UI, "Unsupported inbound type: " + inboundType.toStdString() + ", decisions should be made if to open JSONEDITOR") - QvMessageBox(this, tr("Inbound type not supported"), tr("The inbound type is not supported by Qv2ray (yet). Please use JsonEditor to change the settings") + "\r\n" + - tr("Inbound: ") + inboundType); + if (!root["protocol"].toString().isEmpty()) { + LOG(MODULE_UI, "Unsupported inbound type: " + inboundType.toStdString()) + QvMessageBox(this, tr("Inbound type not supported"), tr("The inbound type is not supported by Qv2ray (yet). Please use JsonEditor to change the settings") + "\r\n" + + tr("Inbound: ") + inboundType); + } else { + LOG(MODULE_UI, "Creating new inbound config") + } } LoadUIData(); @@ -61,7 +65,10 @@ QJsonObject InboundEditor::GenerateNewRoot() void InboundEditor::LoadUIData() { isLoading = true; - strategyCombo->setCurrentText(allocate["strategy"].toString()); + auto x = allocate["strategy"].toString(); + allocate["strategy"] = x.isEmpty() ? "always" : x; + strategyCombo->setCurrentText(x); + // refreshNumberBox->setValue(allocate["refresh"].toInt()); concurrencyNumberBox->setValue(allocate["concurrency"].toInt()); enableSniffingCB->setChecked(sniffing["enabled"].toBool()); diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index 9d1a134f..bd1aaee7 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -65,6 +65,7 @@ MainWindow::MainWindow(QWidget *parent) // QAction *action_RCM_RenameConnection = new QAction(tr("Rename"), this); QAction *action_RCM_StartThis = new QAction(tr("Connect to this"), this); + QAction *action_RCM_ConvToComplex = new QAction(tr("Edit as Complex Config"), this); QAction *action_RCM_EditJson = new QAction(QICON_R("json.png"), tr("Edit as Json"), this); QAction *action_RCM_ShareQR = new QAction(QICON_R("share.png"), tr("Share as QRCode/VMess URL"), this); // @@ -88,6 +89,7 @@ MainWindow::MainWindow(QWidget *parent) connect(action_RCM_RenameConnection, &QAction::triggered, this, &MainWindow::on_action_RenameConnection_triggered); connect(action_RCM_StartThis, &QAction::triggered, this, &MainWindow::on_action_StartThis_triggered); connect(action_RCM_EditJson, &QAction::triggered, this, &MainWindow::on_action_RCM_EditJson_triggered); + connect(action_RCM_ConvToComplex, &QAction::triggered, this, &MainWindow::on_action_RCM_ConvToComplex_triggered); // // Share optionss connect(action_RCM_ShareQR, &QAction::triggered, this, &MainWindow::on_action_RCM_ShareQR_triggered); @@ -102,6 +104,7 @@ MainWindow::MainWindow(QWidget *parent) listMenu = new QMenu(this); listMenu->addAction(action_RCM_RenameConnection); listMenu->addAction(action_RCM_StartThis); + listMenu->addAction(action_RCM_ConvToComplex); listMenu->addAction(action_RCM_EditJson); listMenu->addAction(action_RCM_ShareQR); // @@ -308,8 +311,6 @@ void MainWindow::on_startButton_clicked() bool httpEnabled = conf.inboundConfig.useHTTP; bool socksEnabled = conf.inboundConfig.useSocks; - // TODO: Set PAC proxy string - if (usePAC) { bool canStartPAC = true; QString pacProxyString; // Something like this --> SOCKS5 127.0.0.1:1080; SOCKS 127.0.0.1:1080; DIRECT; http://proxy:8080 @@ -498,35 +499,44 @@ void MainWindow::ShowAndSetConnection(QString guiConnectionName, bool SetConnect // Check empty again... if (guiConnectionName.isEmpty()) return; - // --------- BRGIN Show Connection - auto root = connections[guiConnectionName]; - auto outBoundRoot = root["outbounds"].toArray().first().toObject(); // - auto outboundType = outBoundRoot["protocol"].toString(); - _OutBoundTypeLabel->setText(outboundType); removeConfigButton->setEnabled(true); editConfigButton->setEnabled(true); editJsonBtn->setEnabled(true); duplicateBtn->setEnabled(true); + // + // --------- BRGIN Show Connection + auto root = connections[guiConnectionName]; + // + auto isComplexConfig = root["routing"].toObject()["rules"].toArray().count() > 0; + routeCountLabel->setText(isComplexConfig > 0 ? tr("Complex") : tr("Simple")); - if (outboundType == "vmess") { - auto Server = StructFromJsonString(JsonToString(outBoundRoot["settings"].toObject()["vnext"].toArray().first().toObject())); - _hostLabel->setText(QSTRING(Server.address)); - _portLabel->setText(QSTRING(to_string(Server.port))); - } else if (outboundType == "shadowsocks") { - auto x = JsonToString(outBoundRoot["settings"].toObject()["servers"].toArray().first().toObject()); - auto Server = StructFromJsonString(x); - _hostLabel->setText(QSTRING(Server.address)); - _portLabel->setText(QSTRING(to_string(Server.port))); - } else if (outboundType == "socks") { - auto x = JsonToString(outBoundRoot["settings"].toObject()["servers"].toArray().first().toObject()); - auto Server = StructFromJsonString(x); - _hostLabel->setText(QSTRING(Server.address)); - _portLabel->setText(QSTRING(to_string(Server.port))); + if (isComplexConfig) { + _OutBoundTypeLabel->setText(tr("N/A")); + _hostLabel->setText(tr("N/A")); + _portLabel->setText(tr("N/A")); + } else { + auto outBoundRoot = root["outbounds"].toArray().first().toObject(); + auto outboundType = outBoundRoot["protocol"].toString(); + _OutBoundTypeLabel->setText(outboundType); + + if (outboundType == "vmess") { + auto Server = StructFromJsonString(JsonToString(outBoundRoot["settings"].toObject()["vnext"].toArray().first().toObject())); + _hostLabel->setText(QSTRING(Server.address)); + _portLabel->setText(QSTRING(to_string(Server.port))); + } else if (outboundType == "shadowsocks") { + auto x = JsonToString(outBoundRoot["settings"].toObject()["servers"].toArray().first().toObject()); + auto Server = StructFromJsonString(x); + _hostLabel->setText(QSTRING(Server.address)); + _portLabel->setText(QSTRING(to_string(Server.port))); + } else if (outboundType == "socks") { + auto x = JsonToString(outBoundRoot["settings"].toObject()["servers"].toArray().first().toObject()); + auto Server = StructFromJsonString(x); + _hostLabel->setText(QSTRING(Server.address)); + _portLabel->setText(QSTRING(to_string(Server.port))); + } } - routeCountLabel->setText(QString::number(root["routing"].toObject()["rules"].toArray().count())); - // --------- END Show Connection // // Set Connection @@ -660,8 +670,22 @@ void MainWindow::on_removeConfigButton_clicked() } void MainWindow::on_importConfigButton_clicked() { + // BETA ImportConfigWindow *w = new ImportConfigWindow(this); - w->exec(); + auto configs = w->OpenImport(); + auto gConf = GetGlobalConfig(); + + for (auto conf : configs) { + auto name = configs.key(conf, ""); + + if (name.isEmpty()) + continue; + + SaveConnectionConfig(conf, &name, false); + gConf.configs.push_back(name.toStdString()); + } + + SetGlobalConfig(gConf); OnConfigListChanged(false); } void MainWindow::on_editConfigButton_clicked() @@ -677,7 +701,7 @@ void MainWindow::on_editConfigButton_clicked() QJsonObject root; bool isChanged = false; - if (outBoundRoot["outbounds"].toArray().count() > 1) { + if (CheckIsComplexConfig(outBoundRoot)) { LOG(MODULE_UI, "INFO: Opening route editor.") RouteEditor *routeWindow = new RouteEditor(outBoundRoot, this); root = routeWindow->OpenEditor(); @@ -705,6 +729,34 @@ void MainWindow::on_reconnectButton_clicked() on_stopButton_clicked(); on_startButton_clicked(); } + +void MainWindow::on_action_RCM_ConvToComplex_triggered() +{ + // Check if we have a connection selected... + if (connectionListWidget->currentIndex().row() < 0) { + QvMessageBox(this, tr("No Config Selected"), tr("Please Select a Config")); + return; + } + + auto alias = connectionListWidget->currentItem()->text(); + auto outBoundRoot = connections[alias]; + QJsonObject root; + bool isChanged = false; + // + LOG(MODULE_UI, "INFO: Opening route editor.") + RouteEditor *routeWindow = new RouteEditor(outBoundRoot, this); + root = routeWindow->OpenEditor(); + isChanged = routeWindow->result() == QDialog::Accepted; + + if (isChanged) { + connections[alias] = root; + // true indicates the alias will NOT change + SaveConnectionConfig(root, &alias, true); + OnConfigListChanged(alias == CurrentConnectionName); + ShowAndSetConnection(CurrentConnectionName, false, false); + } +} + void MainWindow::on_action_RCM_EditJson_triggered() { // Check if we have a connection selected... @@ -745,10 +797,9 @@ void MainWindow::on_shareBtn_clicked() auto alias = connectionListWidget->currentItem()->text(); auto root = connections[alias]; auto outBoundRoot = root["outbounds"].toArray().first().toObject(); - // auto outboundType = outBoundRoot["protocol"].toString(); - if (outboundType == "vmess") { + if (CheckIsComplexConfig(root) && outboundType == "vmess") { auto vmessServer = StructFromJsonString(JsonToString(outBoundRoot["settings"].toObject()["vnext"].toArray().first().toObject())); auto transport = StructFromJsonString(JsonToString(outBoundRoot["streamSettings"].toObject())); auto vmess = ConvertConfigToVMessString(transport, vmessServer, alias); diff --git a/src/ui/w_MainWindow.hpp b/src/ui/w_MainWindow.hpp index 753ba645..afffd753 100644 --- a/src/ui/w_MainWindow.hpp +++ b/src/ui/w_MainWindow.hpp @@ -90,6 +90,7 @@ class MainWindow : public QMainWindow, Ui::MainWindow // void on_action_StartThis_triggered(); void on_action_RCM_EditJson_triggered(); + void on_action_RCM_ConvToComplex_triggered(); void on_action_RenameConnection_triggered(); // QvHttpRequestHelper HTTPRequestHelper; diff --git a/src/ui/w_MainWindow.ui b/src/ui/w_MainWindow.ui index b6be494f..f25415cf 100644 --- a/src/ui/w_MainWindow.ui +++ b/src/ui/w_MainWindow.ui @@ -331,26 +331,12 @@ - - - - Mux - - - - - - - - - - - Actions + Operations @@ -387,6 +373,9 @@ + + false + Ping Test diff --git a/src/ui/w_OutboundEditor.cpp b/src/ui/w_OutboundEditor.cpp index 969f885c..4810c6c5 100644 --- a/src/ui/w_OutboundEditor.cpp +++ b/src/ui/w_OutboundEditor.cpp @@ -6,6 +6,7 @@ #include "w_OutboundEditor.hpp" #include "w_MainWindow.hpp" #include "w_JsonEditor.hpp" +#include "w_RoutesEditor.hpp" OutboundEditor::OutboundEditor(QWidget *parent) : QDialog(parent), diff --git a/src/ui/w_OutboundEditor.ui b/src/ui/w_OutboundEditor.ui index 3dcb8536..de0b1a8e 100644 --- a/src/ui/w_OutboundEditor.ui +++ b/src/ui/w_OutboundEditor.ui @@ -28,7 +28,7 @@ true - + @@ -327,7 +327,7 @@ - Default Value + Default @@ -428,7 +428,7 @@ - Default Value + Default @@ -1045,15 +1045,19 @@ - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + diff --git a/src/ui/w_RoutesEditor.cpp b/src/ui/w_RoutesEditor.cpp index 1033d36d..6e9c59d9 100644 --- a/src/ui/w_RoutesEditor.cpp +++ b/src/ui/w_RoutesEditor.cpp @@ -1,12 +1,12 @@ -#include "w_RoutesEditor.hpp" +#include "w_RoutesEditor.hpp" #include "QvCoreConfigOperations.hpp" #include "w_OutboundEditor.hpp" #include "w_JsonEditor.hpp" #include "w_InboundEditor.hpp" +#include "w_ImportConfig.hpp" static bool isLoading = false; #define CurrentRule this->rules[this->currentRuleIndex] -#define STATUS(msg) statusLabel->setText(tr(msg)); #define LOADINGCHECK if(isLoading) return; RouteEditor::RouteEditor(QJsonObject connection, QWidget *parent) : @@ -318,9 +318,9 @@ void RouteEditor::ShowRuleDetail(RuleObject rule) inboundsList->item(_index)->setCheckState(Qt::Checked); inboundsList->setCurrentRow(_index); - STATUS("OK") + statusLabel->setText(tr("OK")); } else { - STATUS("Cannot find inbound by a tag, possible currupted files?") + statusLabel->setText(tr("Cannot find inbound by a tag, possible currupted files?")); LOG(MODULE_UI, "An inbound could not be determined by tag.") return; } @@ -335,7 +335,7 @@ void RouteEditor::on_editOutboundBtn_clicked() int row = outboundsList->currentRow(); if (row < 0) { - STATUS("No row selected.") + statusLabel->setText(tr("No row selected.")); LOG(MODULE_UI, "No outbound row selected.") return; } @@ -348,19 +348,19 @@ void RouteEditor::on_editOutboundBtn_clicked() tr("This outbound entry is not supported by the GUI editor.") + "\r\n" + tr("We will launch Json Editor instead.")); JsonEditor *w = new JsonEditor(currentOutbound, this); - STATUS("Opening JSON editor") + statusLabel->setText(tr("Opening JSON editor")); result = w->OpenEditor(); delete w; } else { OutboundEditor *w = new OutboundEditor(currentOutbound, this); - STATUS("Opening default outbound editor.") + statusLabel->setText(tr("Opening default outbound editor.")); result = w->OpenEditor(); delete w; } outbounds[row] = result; on_outboundsList_currentRowChanged(row); - STATUS("OK") + statusLabel->setText(tr("OK")); } void RouteEditor::on_insertDirectBtn_clicked() @@ -370,7 +370,7 @@ void RouteEditor::on_insertDirectBtn_clicked() auto out = GenerateOutboundEntry("freedom", freedom, QJsonObject(), QJsonObject(), "0.0.0.0", tag); this->outbounds.append(out); outboundsList->addItem(tag); - STATUS("Added DIRECT outbound") + statusLabel->setText(tr("Added DIRECT outbound")); } void RouteEditor::on_editInboundBtn_clicked() @@ -386,20 +386,20 @@ void RouteEditor::on_editInboundBtn_clicked() if (protocol != "http" && protocol != "mtproto" && protocol != "socks" && protocol != "dokodemo-door") { QvMessageBox(this, tr("Cannot Edit"), tr("Currently, this type of outbound is not supported by the editor.") + "\r\n" + tr("We will launch Json Editor instead.")); - STATUS("Opening JSON editor") + statusLabel->setText(tr("Opening JSON editor")); JsonEditor *w = new JsonEditor(currentInbound, this); result = w->OpenEditor(); delete w; } else { InboundEditor *w = new InboundEditor(currentInbound, this); - STATUS("Opening default inbound editor") + statusLabel->setText(tr("Opening default inbound editor")); result = w->OpenEditor(); delete w; } inbounds[row] = result; on_inboundsList_currentRowChanged(row); - STATUS("OK") + statusLabel->setText(tr("OK")); } void RouteEditor::on_routeProtocolHTTPCB_stateChanged(int arg1) @@ -414,7 +414,7 @@ void RouteEditor::on_routeProtocolHTTPCB_stateChanged(int arg1) if (routeProtocolBTCB->isChecked()) protocols << "bittorrent"; CurrentRule.protocol = protocols.toStdList(); - STATUS("Protocol list changed.") + statusLabel->setText(tr("Protocol list changed.")); } void RouteEditor::on_routeProtocolTLSCB_stateChanged(int arg1) @@ -429,7 +429,7 @@ void RouteEditor::on_routeProtocolTLSCB_stateChanged(int arg1) if (routeProtocolBTCB->isChecked()) protocols << "bittorrent"; CurrentRule.protocol = protocols.toStdList(); - STATUS("Protocol list changed.") + statusLabel->setText(tr("Protocol list changed.")); } void RouteEditor::on_routeProtocolBTCB_stateChanged(int arg1) @@ -444,7 +444,7 @@ void RouteEditor::on_routeProtocolBTCB_stateChanged(int arg1) if (routeProtocolTLSCB->isChecked()) protocols << "tls"; CurrentRule.protocol = protocols.toStdList(); - STATUS("Protocol list changed.") + statusLabel->setText(tr("Protocol list changed.")); } void RouteEditor::on_balabcerAddBtn_clicked() @@ -460,9 +460,9 @@ void RouteEditor::on_balabcerAddBtn_clicked() if (!balancerTx.isEmpty()) { balancerList->addItem(balancerTx); balancerSelectionCombo->setEditText(""); - STATUS("OK") + statusLabel->setText(tr("OK")); } else { - STATUS("Balacer is empty, not processing.") + statusLabel->setText(tr("Balacer is empty, not processing.")); } } @@ -476,7 +476,7 @@ void RouteEditor::on_balancerDelBtn_clicked() Balancers[QSTRING(CurrentRule.balancerTag)].removeAt(balancerList->currentRow()); balancerList->takeItem(balancerList->currentRow()); - STATUS("Removed a balancer entry.") + statusLabel->setText(tr("Removed a balancer entry.")); } void RouteEditor::on_hostList_textChanged() @@ -569,7 +569,7 @@ void RouteEditor::on_routesTable_cellChanged(int row, int column) } rules[row].QV2RAY_RULE_ENABLED = routesTable->item(row, column)->checkState() == Qt::Checked; - STATUS((rules[row].QV2RAY_RULE_ENABLED ? "Enabled a route" : "Disabled a route")) + statusLabel->setText(tr((rules[row].QV2RAY_RULE_ENABLED ? "Enabled a route" : "Disabled a route"))); } void RouteEditor::on_netBothRB_clicked() @@ -647,7 +647,7 @@ void RouteEditor::on_inboundsList_itemChanged(QListWidgetItem *item) } CurrentRule.inboundTag = new_inbounds.toStdList(); - STATUS("OK") + statusLabel->setText(tr("OK")); } void RouteEditor::on_delRouteBtn_clicked() @@ -730,3 +730,35 @@ void RouteEditor::on_delInboundBtn_clicked() inbounds.removeAt(index); inboundsList->takeItem(index); } + +void RouteEditor::on_addInboundBtn_clicked() +{ + LOADINGCHECK + InboundEditor w(QJsonObject(), this); + auto _result = w.OpenEditor(); + inbounds.append(_result); + inboundsList->addItem(tr("New Inbound")); +} + +void RouteEditor::on_addOutboundBtn_clicked() +{ + LOADINGCHECK + ImportConfigWindow *w = new ImportConfigWindow(this); + // True here for not keep the inbounds. + auto configs = w->OpenImport(true); + + for (auto conf : configs) { + auto name = configs.key(conf, ""); + + if (name.isEmpty()) + continue; + + // conf is rootObject, needs to unwrap it. + auto confList = conf["outbounds"].toArray(); + + for (int i = 0; i < confList.count(); i++) { + outbounds.append(confList[i]); + outboundsList->addItem(name + "_" + QString::number(i)); + } + } +} diff --git a/src/ui/w_RoutesEditor.hpp b/src/ui/w_RoutesEditor.hpp index 818deb7b..61dea55d 100644 --- a/src/ui/w_RoutesEditor.hpp +++ b/src/ui/w_RoutesEditor.hpp @@ -1,4 +1,4 @@ -#ifndef W_QVOUTBOUNDEDITOR_H +#ifndef W_QVOUTBOUNDEDITOR_H #define W_QVOUTBOUNDEDITOR_H #include @@ -80,12 +80,16 @@ class RouteEditor : public QDialog, private Ui::RouteEditor void on_delInboundBtn_clicked(); + void on_addInboundBtn_clicked(); + + void on_addOutboundBtn_clicked(); + private: void ShowRuleDetail(RuleObject rule); int currentRuleIndex; - QString DomainStrategy; QMap Balancers; QList rules; + QString DomainStrategy; // QJsonArray inbounds; QJsonArray outbounds; diff --git a/translations/en-US.ts b/translations/en-US.ts index 6ab13c0b..4dd5d1bd 100644 --- a/translations/en-US.ts +++ b/translations/en-US.ts @@ -183,81 +183,80 @@ - + + Imported + + + + Select file to import - + Capture QRCode - + Cannot find a valid QRCode from this region. - - + + Import config file - + Import from file failed, for more information, please check the log file. - + Failed to check the validity of the config file. - - - Assertion Failed + + TODO - - Assertion failed: ::SaveConnectionConfig should returns false. - - - - + Select an image to import - + QRCode scanning failed - + Cannot find any QRCode from the image. + + - - Edit file as JSON - + Provided file not found: - + The file you selected has json syntax error. Continue editing may make you lose data. Would you like to continue? - + Failed to save file, please check if you have the required permissions @@ -495,41 +494,41 @@ - + Inbound type not supported - + The inbound type is not supported by Qv2ray (yet). Please use JsonEditor to change the settings - + Inbound: - - + + Removing a user - - + + You haven't selected a user yet. - - + + Add a user - - + + This user exists already. @@ -597,19 +596,19 @@ - + Connect - + Disconnect - + Reconnect @@ -679,12 +678,12 @@ - - Mux + + Operations - + Edit connection as JSON @@ -709,225 +708,285 @@ - - Actions - - - - + Edit JSON - + Ping Test - + Share - + Log - + Speed Graph - + Speed - + 0.00 B/s 0.00 B/s - + Data - + 0.00 B 0.00 B - + #ManuallyCreateConnection - + #ImportConnection - + #Exit - + #Preferences - + #Start - + #Stop - + #Restart - - + + Hide - + Quit - + Rename - + Connect to this - + + Edit as Complex Config + + + + Edit as Json - + Share as QRCode/VMess URL - - - + + + Show - - + + No connection selected! - - + + Please select a config from the list. - + Update - + Found a new version: - + Download Link: - - - + + + Connected To Server: - + Connected - + + + Configuring PAC + + + + + Could not start PAC server as it is configured to use SOCKS, but it is not enabled + + + + + Could not start PAC server as it is configured to use HTTP, but it is not enabled + + + + + PAC Processing Failed + + + + + HTTP or SOCKS inbound is not properly configured for PAC + + + + + Qv2ray will continue, but will not set system proxy. + + + + + Cannot set system proxy + + + + + HTTP inbound is not enabled + + + + Disconnected - + Complex + + + + + Simple + + + + + + + N/A + + + + + Rename a Connection - + The name cannot be empty - + The name has been used already, Please choose another. - - + + Removing this Connection - + Are you sure to remove this connection? - + Failed to delete connection file, please delete manually. - - + + + No Config Selected - - + + + Please Select a Config - + Share Connection - + There're no support of sharing configs other than vmess @@ -1041,7 +1100,7 @@ - Default Value + Default @@ -1175,9 +1234,9 @@ - - - + + + Prefrences @@ -1195,16 +1254,15 @@ - - - - - - - - - + + + + + + + + Enabled @@ -1304,88 +1362,109 @@ Listening Address + + + Set System Proxy + + - - - + + + Port - - + + Authentication - - + + Username - - + + Password - + + PAC Proxy IP + + + + + Use Proxy + + + + + Import GFWList + + + + Connection Settings - + General Connection Settings - + Enable Proxy - + Bypass Chinese Mainland - + Statistics - + API Port - + Use Local DNS - + Forward Proxy - + Status - + Type - + + HTTP @@ -1396,6 +1475,7 @@ + SOCKS @@ -1415,266 +1495,256 @@ - - GFWList Source - - - - + Github - + Mirror: Pagure - + Mirror: Repo.or.cz - + Mirror: Bitbucket - + Mirror: Gitlab - + Mirror: TuxFamily - + GFWList File - + Go - + Edit PAC - + Open PAC Folder - - Access URL - - - - + SOCKS Settings - + UDP Support - + Local IP (For UDP) - + Socks - + Host Address - + Network Toolbar Settings - + Items - - + + - - - + + + - + Page Y Offset - + Pages - + Lines - + Text Style - + Font - - + + Bold - - + + Italic - + Size - + Color - + A: - + R: - + G: - + B: - + ... - + Style - + Content - + Content Type - + Text/Tag - + You can config how the network speed toolbar looks like in this panel - + Apply Network Speed Bar UI Settings - + About - + Qv2ray - + Version: - + Built Time - + Official Repo - + Ignore Next Version - + <html><head/><body><p><a href="https://github.com/lhy0403/Qv2ray"><span style=" text-decoration: underline; color:#2980b9;">https://github.com/lhy0403/Qv2ray</span></a></p></body></html> - + Custom DNS List - + License: - + <html><head/><body><p><a href="https://www.gnu.org/licenses/gpl-3.0.txt"><span style=" text-decoration: underline; color:#2980b9;">GPLv3 (https://www.gnu.org/licenses/gpl-3.0.txt)</span></a></p></body></html> - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -1696,17 +1766,17 @@ p, li { white-space: pre-wrap; } - + Cancel - + About Qt - + Use Dark Theme @@ -1726,67 +1796,72 @@ p, li { white-space: pre-wrap; } - + Open v2ray assets folder - + Open v2ray core file - - + + Enable tProxy Support - + This will append capabilities to the v2ray executable. - + Qv2ray will copy your v2ray core to this path: - + If anything goes wrong after enabling this, please refer to issue #57 or the link below: - + Qv2ray cannot copy one or both v2ray files from: - + to this path: - - + + Failed to setcap onto v2ray executable. You may need to run `setcap` manually. - + tProxy is not supported on macOS and Windows - + Dark Mode - + Please restart Qv2ray to fully apply this feature. + + + Select GFWList in base64 + + QObject @@ -1805,31 +1880,6 @@ p, li { white-space: pre-wrap; } JSON should not be empty - - - Configuration Error - - - - - Cannot start v2ray - - - - - v2ray core file cannot be found at: - - - - - Please go to prefrence window to change the location. - - - - - Or put v2ray core file in the location above. - - Cannot Start Qv2ray @@ -1847,72 +1897,72 @@ p, li { white-space: pre-wrap; } - + Qv2ray will now exit. - + Cannot load languages - + Qv2ray will continue running, but you cannot change the UI language. - + Another instance of Qv2ray is already running. - + Qv2ray Cannot Continue - + You are running a lower version of Qv2ray compared to the current config file. - + Please report if you think this is an error. - + DependencyMissing - + Cannot find openssl libs - + This could be caused by a missing of `openssl` package in your system. Or an AppImage issue. - + If you are using AppImage, please report a bug. - + Please refer to Github Issue #65 to check for solutions. - + Github Issue Link: - + Technical Details @@ -1937,81 +1987,132 @@ p, li { white-space: pre-wrap; } - + Custom Text - + Current Time - + Current Date - + Current Qv2ray Version - + Current Connection Name - + Current Connection Status - + Total Upload Speed - + Total Download Speed - + Upload Speed for Specific Tag - + Download Speed for Specific Tag - + Total Uploaded Data - + Total Downloaded Data - + Uploaded Data for Specific Tag - + Downloaded Data for Specific Tag + + Qv2ray::Components::PACHandler + + + PAC Handler + + + + + Failed to listen PAC request on this port, please verify the permissions + + + + + Qv2ray::QvCoreInteration::ConnectionInstance + + + Configuration Error + + + + + Cannot start v2ray + + + + + v2ray core file cannot be found at: + + + + + Please go to prefrence window to change the location. + + + + + Or put v2ray core file in the location above. + + + + + API Call Failed + + + + + Failed to get statistics data, please check if v2ray is running properly + + + RouteEditor - + Route Editor @@ -2027,287 +2128,375 @@ p, li { white-space: pre-wrap; } - + Add new inbound + + + + + A - - - + + Delete inbound + + + + + + D - - + + Edit inbound + + + + + E - + + Add default inbound from global config + + + + Inbound Information - - + + Type - - - + + + Port - - + + Address - - + + Tag - + Outbound List - + + Add outbound + + + + + Delete outbound + + + + + Edit outbound + + + + + Add Freedom outbound + + + + F - + + Add blackhole outbound + + + + B - + Outbound Information - + Routes - - - + + + Enabled - + InBounds - + Domain/IP - + Outbound - - - - + + Add new route + + + + + + + ... - + + Remove route + + + + Route Details - + Target IP List - + Source IP Matching List - + Target Domain List - + Network - + TCP - + UDP - + Both - + Protocol - + HTTP - + TLS - + BitTorrent - + e.g. 80, 443, 8000-8080 - + Enable Balancers - + Balancers - + Outbound Tag - + + Outbound Tag selector + + + + Users List - - - - + + + + No Tag - - + + Any - - + + Items - + Cannot find an inbound by tag: - + Index Out Of Range - - Cannot Edit - - - - - This outbound entry is not supported by the GUI editor. + + + + + OK - + Cannot find inbound by a tag, possible currupted files? + + + + + No row selected. + + + + + + Cannot Edit + + + + + This outbound entry is not supported by the GUI editor. + + + + + We will launch Json Editor instead. - + + + Opening JSON editor + + + + + Opening default outbound editor. + + + + + Added DIRECT outbound + + + + Currently, this type of outbound is not supported by the editor. - - - - + + Opening default inbound editor + + + + + + + Protocol list changed. + + + + + Balacer is empty, not processing. + + + + + Removed a balancer entry. + + + + Changing route inbound/outbound - - You didn't select an outbound. + + One or more inbound config(s) have no tag configured, which will be ignored, do you still want to continue? - - Banlancer will be used. - - - - - - One or more inbound config(s) have no tag configured, do you still want to continue? - - - - - Are you sure to change the inbound/outbound of currently selected route? - - - - - Current inbound/outbound combinations: - - - - - Inbounds: - - - - - Outbound: + + New Inbound