diff --git a/Qv2ray.pro b/Qv2ray.pro index 578d6565..3268b01f 100644 --- a/Qv2ray.pro +++ b/Qv2ray.pro @@ -166,13 +166,13 @@ unix { message(" --> Generating desktop dependency.") desktop.files += ./icons/Qv2ray.desktop - desktop.path = /opt/$${TARGET}/share/applications/ + desktop.path = /usr/share/applications/ message(" --> Generating icons dependency.") icon.files += ./icons/Qv2ray.png - icon.path = /opt/$${TARGET}/share/icons/hicolor/256x256/apps/ + icon.path = /usr/share/icons/hicolor/256x256/apps/ - target.path = /opt/$${TARGET}/bin + target.path = /usr/local/bin INSTALLS += target desktop icon } diff --git a/icons/Qv2ray.desktop b/icons/Qv2ray.desktop index 5220338e..500efcaf 100755 --- a/icons/Qv2ray.desktop +++ b/icons/Qv2ray.desktop @@ -3,6 +3,6 @@ Type=Application Keywords=Internet;VPN;Proxy;v2ray;Qt; Categories=Network;Qt; Icon=Qv2ray -Exec=/opt/Qv2ray/bin/Qv2ray +Exec=Qv2ray Name=Qv2ray Comment=Cross platform v2ray Qt GUI Client. diff --git a/src/QvUtils.cpp b/src/QvUtils.cpp index d15e23c0..ddd63bb0 100644 --- a/src/QvUtils.cpp +++ b/src/QvUtils.cpp @@ -88,6 +88,7 @@ namespace Qv2ray { QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(source->toUtf8(), &error); + Q_UNUSED(doc) if (error.error == QJsonParseError::NoError) { return ""; @@ -179,5 +180,3 @@ namespace Qv2ray } } } - - diff --git a/src/ui/w_InboundEditor.cpp b/src/ui/w_InboundEditor.cpp index 4e841f62..13c486fc 100644 --- a/src/ui/w_InboundEditor.cpp +++ b/src/ui/w_InboundEditor.cpp @@ -1,14 +1,419 @@ #include "w_InboundEditor.h" #include "ui_w_InboundEditor.h" +#include "QvUtils.h" +#include "QvCoreConfigOperations.h" -InboundEditor::InboundEditor(QWidget *parent) : +static bool isLoading = false; +#define PREPARE_RETURN if(isLoading) return; + +InboundEditor::InboundEditor(QJsonObject root, QWidget *parent) : QDialog(parent), ui(new Ui::InboundEditor) { + original = root; + this->root = root; + auto inboundType = root["protocol"].toString(); + allocate = root["allocate"].toObject(); + sniffing = root["sniffing"].toObject(); + + if (inboundType == "http") { + httpSettings = root["settings"].toObject(); + } else if (inboundType == "socks") { + socksSettings = root["settings"].toObject(); + } else if (inboundType == "dokodemo-door") { + dokoSettings = root["settings"].toObject(); + } 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); + } + ui->setupUi(this); + LoadUIData(); +} + +QJsonObject InboundEditor::OpenEditor() +{ + int resultCode = this->exec(); + return resultCode == QDialog::Accepted ? GenerateNewRoot() : original; +} + +QJsonObject InboundEditor::GenerateNewRoot() +{ + QJsonObject _newRoot = root; + auto inboundType = root["protocol"].toString(); + + if (inboundType == "http") { + _newRoot["settings"] = httpSettings; + } else if (inboundType == "socks") { + _newRoot["settings"] = socksSettings; + } else if (inboundType == "dokodemo-door") { + _newRoot["settings"] = dokoSettings; + } else if (inboundType == "mtproto") { + _newRoot["settings"] = mtSettings; + } + + _newRoot["sniffing"] = sniffing; + _newRoot["allocate"] = allocate; + return _newRoot; +} + +void InboundEditor::LoadUIData() +{ + isLoading = true; + ui->strategyCombo->setCurrentText(allocate["strategy"].toString()); + ui->refreshNumberBox->setValue(allocate["refresh"].toInt()); + ui->concurrencyNumberBox->setValue(allocate["concurrency"].toInt()); + ui->enableSniffingCB->setChecked(sniffing["enabled"].toBool()); + + foreach (auto item, sniffing["destOverride"].toArray()) { + if (item.toString().toLower() == "http") ui->destOverrideList->item(0)->setCheckState(Qt::Checked); + + if (item.toString().toLower() == "tls") ui->destOverrideList->item(1)->setCheckState(Qt::Checked); + } + + // + ui->inboundTagTxt->setText(root["tag"].toString()); + ui->inboundHostTxt->setText(root["listen"].toString()); + ui->inboundPortTxt->setText(root["port"].toVariant().toString()); + ui->inboundProtocolCombo->setCurrentText(root["protocol"].toString()); + // HTTP + ui->httpTimeoutSpinBox->setValue(httpSettings["timeout"].toInt()); + ui->httpTransparentCB->setChecked(httpSettings["allowTransparent"].toBool()); + ui->httpUserLevelSB->setValue(httpSettings["userLevel"].toInt()); + ui->httpAccountListBox->clear(); + + for (auto user : httpSettings["accounts"].toArray()) { + ui->httpAccountListBox->addItem(user.toObject()["user"].toString() + ":" + user.toObject()["pass"].toString()); + } + + // SOCKS + ui->socksAuthCombo->setCurrentText(socksSettings["auth"].toString()); + ui->socksUDPCB->setChecked(socksSettings["udp"].toBool()); + ui->socksUDPIPAddrTxt->setText(socksSettings["ip"].toString()); + ui->socksUserLevelSB->setValue(socksSettings["userLevel"].toInt()); + + for (auto user : socksSettings["accounts"].toArray()) { + ui->socksAccountListBox->addItem(user.toObject()["user"].toString() + ":" + user.toObject()["pass"].toString()); + } + + // Dokodemo-Door + ui->dokoFollowRedirectCB->setChecked(dokoSettings["followRedirect"].toBool()); + ui->dokoIPAddrTxt->setText(dokoSettings["address"].toString()); + ui->dokoPortSB->setValue(dokoSettings["port"].toInt()); + ui->dokoTimeoutSB->setValue(dokoSettings["timeout"].toInt()); + ui->dokoUserLevelSB->setValue(dokoSettings["userLevel"].toInt()); + ui->dokoTCPCB->setChecked(dokoSettings["network"].toString().contains("tcp")); + ui->dokoUDPCB->setChecked(dokoSettings["network"].toString().contains("udp")); + // MTProto + ui->mtEMailTxt->setText(mtSettings["users"].toArray().first()["email"].toString()); + ui->mtUserLevelSB->setValue(mtSettings["users"].toArray().first()["level"].toInt()); + ui->mtSecretTxt->setText(mtSettings["users"].toArray().first()["secret"].toString()); + isLoading = false; } InboundEditor::~InboundEditor() { delete ui; } + +void InboundEditor::on_inboundProtocolCombo_currentIndexChanged(const QString &arg1) +{ + PREPARE_RETURN + root["protocol"] = arg1.toLower(); +} + +void InboundEditor::on_inboundProtocolCombo_currentIndexChanged(int index) +{ + ui->stackedWidget->setCurrentIndex(index); +} + +void InboundEditor::on_inboundTagTxt_textEdited(const QString &arg1) +{ + PREPARE_RETURN + root["tag"] = arg1; +} + +void InboundEditor::on_httpTimeoutSpinBox_valueChanged(int arg1) +{ + PREPARE_RETURN + httpSettings["timtout"] = arg1; +} + +void InboundEditor::on_httpTransparentCB_stateChanged(int arg1) +{ + PREPARE_RETURN + httpSettings["allowTransparent"] = arg1 == Qt::Checked; +} + +void InboundEditor::on_httpUserLevelSB_valueChanged(int arg1) +{ + PREPARE_RETURN + httpSettings["userLevel"] = arg1; +} + +void InboundEditor::on_httpRemoveUserBtn_clicked() +{ + PREPARE_RETURN + + if (ui->httpAccountListBox->currentRow() != -1) { + auto userpass = ui->httpAccountListBox->currentItem(); + auto list = httpSettings["accounts"].toArray(); + + for (int i = 0 ; i < list.count(); i++) { + auto user = list[i].toObject(); + + if (user["user"].toString() + ":" + user["pass"].toString() == userpass->text()) { + list.removeAt(i); + ui->httpAccountListBox->removeItemWidget(userpass); + return; + } + } + } else { + QvMessageBox(this, tr("Removing a user"), tr("You haven't selected a user yet,")); + } +} + +void InboundEditor::on_httpAddUserBtn_clicked() +{ + PREPARE_RETURN + auto user = ui->httpAddUserTxt->text(); + auto pass = ui->httpAddPasswordTxt->text(); + // + auto list = httpSettings["accounts"].toArray(); + + for (int i = 0 ; i < list.count(); i++) { + auto _user = list[i].toObject(); + + if (_user["user"].toString() == user) { + QvMessageBox(this, tr("Add a user"), tr("This user exists already.")); + return; + } + } + + QJsonObject entry; + entry["user"] = user; + entry["pass"] = pass; + list.append(entry); + httpSettings["accounts"] = list; +} + +void InboundEditor::on_strategyCombo_currentIndexChanged(const QString &arg1) +{ + PREPARE_RETURN + allocate["strategy"] = arg1.toLower(); +} + +void InboundEditor::on_refreshNumberBox_valueChanged(int arg1) +{ + PREPARE_RETURN + allocate["refresh"] = arg1; +} + +void InboundEditor::on_concurrencyNumberBox_valueChanged(int arg1) +{ + PREPARE_RETURN + allocate["concurrency"] = arg1; +} + +void InboundEditor::on_enableSniffingCB_stateChanged(int arg1) +{ + PREPARE_RETURN + sniffing["enabled"] = arg1 == Qt::Checked; +} + +void InboundEditor::on_destOverrideList_itemChanged(QListWidgetItem *item) +{ + PREPARE_RETURN + Q_UNUSED(item) + QJsonArray list; + + for (int i = 0; i < ui->destOverrideList->count(); i++) { + auto _item = ui->destOverrideList->item(i); + + if (item->checkState() == Qt::Checked) { + list.append(_item->text().toLower()); + } + } + + sniffing["destOverride"] = list; +} + +void InboundEditor::on_socksUDPCB_stateChanged(int arg1) +{ + PREPARE_RETURN + socksSettings["udp"] = arg1 == Qt::Checked; +} + +void InboundEditor::on_socksUDPIPAddrTxt_textEdited(const QString &arg1) +{ + PREPARE_RETURN + socksSettings["ip"] = arg1; +} + +void InboundEditor::on_socksUserLevelSB_valueChanged(int arg1) +{ + PREPARE_RETURN + socksSettings["userLevel"] = arg1; +} + +void InboundEditor::on_socksRemoveUserBtn_clicked() +{ + PREPARE_RETURN + + if (ui->socksAccountListBox->currentRow() != -1) { + auto userpass = ui->socksAccountListBox->currentItem(); + auto list = socksSettings["accounts"].toArray(); + + for (int i = 0 ; i < list.count(); i++) { + auto user = list[i].toObject(); + + if (user["user"].toString() + ":" + user["pass"].toString() == userpass->text()) { + list.removeAt(i); + ui->socksAccountListBox->removeItemWidget(userpass); + return; + } + } + } else { + QvMessageBox(this, tr("Removing a user"), tr("You haven't selected a user yet,")); + } +} + +void InboundEditor::on_socksAddUserBtn_clicked() +{ + PREPARE_RETURN + auto user = ui->socksAddUserTxt->text(); + auto pass = ui->socksAddPasswordTxt->text(); + // + auto list = socksSettings["accounts"].toArray(); + + for (int i = 0 ; i < list.count(); i++) { + auto _user = list[i].toObject(); + + if (_user["user"].toString() == user) { + QvMessageBox(this, tr("Add a user"), tr("This user exists already.")); + return; + } + } + + QJsonObject entry; + entry["user"] = user; + entry["pass"] = pass; + list.append(entry); + socksSettings["accounts"] = list; +} + +void InboundEditor::on_dokoIPAddrTxt_textEdited(const QString &arg1) +{ + PREPARE_RETURN + dokoSettings["address"] = arg1; +} + +void InboundEditor::on_dokoPortSB_valueChanged(int arg1) +{ + PREPARE_RETURN + dokoSettings["port"] = arg1; +} + +void InboundEditor::on_dokoTCPCB_stateChanged(int arg1) +{ + PREPARE_RETURN + Q_UNUSED(arg1) + bool hasTCP = ui->dokoTCPCB->checkState() == Qt::Checked; + bool hasUDP = ui->dokoUDPCB->checkState() == Qt::Checked; + QString str = ""; + str += hasTCP ? "tcp" : ""; + str += (hasTCP && hasUDP) ? "," : ""; + str += hasUDP ? "udp" : ""; + dokoSettings["network"] = str; +} + +void InboundEditor::on_dokoUDPCB_stateChanged(int arg1) +{ + PREPARE_RETURN + Q_UNUSED(arg1) + bool hasTCP = ui->dokoTCPCB->checkState() == Qt::Checked; + bool hasUDP = ui->dokoUDPCB->checkState() == Qt::Checked; + QString str = ""; + str += hasTCP ? "tcp" : ""; + str += (hasTCP && hasUDP) ? "," : ""; + str += hasUDP ? "udp" : ""; + dokoSettings["network"] = str; +} + +void InboundEditor::on_dokoTimeoutSB_valueChanged(int arg1) +{ + PREPARE_RETURN + dokoSettings["timeout"] = arg1; +} + +void InboundEditor::on_dokoFollowRedirectCB_stateChanged(int arg1) +{ + PREPARE_RETURN + dokoSettings["followRedirect"] = arg1 == Qt::Checked; +} + +void InboundEditor::on_dokoUserLevelSB_valueChanged(int arg1) +{ + PREPARE_RETURN + dokoSettings["userLevel"] = arg1; +} + +void InboundEditor::on_mtEMailTxt_textEdited(const QString &arg1) +{ + PREPARE_RETURN + + if (!mtSettings.contains("users")) mtSettings["users"] = QJsonArray(); + + QJsonObject user = mtSettings["users"].toArray().empty() ? QJsonObject() : mtSettings["users"].toArray().first().toObject(); + user["email"] = arg1; + QJsonArray list; + list.append(user); + mtSettings["users"] = list; +} + +void InboundEditor::on_mtSecretTxt_textEdited(const QString &arg1) +{ + PREPARE_RETURN + + if (!mtSettings.contains("users")) mtSettings["users"] = QJsonArray(); + + QJsonObject user = mtSettings["users"].toArray().empty() ? QJsonObject() : mtSettings["users"].toArray().first().toObject(); + user["secret"] = arg1; + QJsonArray list; + list.append(user); + mtSettings["users"] = list; +} + +void InboundEditor::on_mtUserLevelSB_valueChanged(int arg1) +{ + PREPARE_RETURN + + if (!mtSettings.contains("users")) mtSettings["users"] = QJsonArray(); + + QJsonObject user = mtSettings["users"].toArray().empty() ? QJsonObject() : mtSettings["users"].toArray().first().toObject(); + user["userLevel"] = arg1; + QJsonArray list; + list.append(user); + mtSettings["users"] = list; +} + +void InboundEditor::on_inboundHostTxt_textEdited(const QString &arg1) +{ + PREPARE_RETURN + root["listen"] = arg1; +} + +void InboundEditor::on_inboundPortTxt_textEdited(const QString &arg1) +{ + PREPARE_RETURN + root["port"] = arg1; +} + +void InboundEditor::on_socksAuthCombo_currentIndexChanged(const QString &arg1) +{ + PREPARE_RETURN + socksSettings["auth"] = arg1.toLower(); +} diff --git a/src/ui/w_InboundEditor.h b/src/ui/w_InboundEditor.h index 2024c3ec..d55e16ea 100644 --- a/src/ui/w_InboundEditor.h +++ b/src/ui/w_InboundEditor.h @@ -2,21 +2,100 @@ #define W_INBOUNDEDITOR_H #include +#include +#include -namespace Ui { -class InboundEditor; +namespace Ui +{ + class InboundEditor; } class InboundEditor : public QDialog { - Q_OBJECT + Q_OBJECT -public: - explicit InboundEditor(QWidget *parent = nullptr); - ~InboundEditor(); + public: + explicit InboundEditor(QJsonObject root, QWidget *parent = nullptr); + ~InboundEditor(); + QJsonObject OpenEditor(); -private: - Ui::InboundEditor *ui; + private slots: + void on_inboundProtocolCombo_currentIndexChanged(const QString &arg1); + + void on_inboundProtocolCombo_currentIndexChanged(int index); + + void on_inboundTagTxt_textEdited(const QString &arg1); + + void on_httpTimeoutSpinBox_valueChanged(int arg1); + + void on_httpTransparentCB_stateChanged(int arg1); + + void on_httpUserLevelSB_valueChanged(int arg1); + + void on_httpRemoveUserBtn_clicked(); + + void on_httpAddUserBtn_clicked(); + + void on_strategyCombo_currentIndexChanged(const QString &arg1); + + void on_refreshNumberBox_valueChanged(int arg1); + + void on_concurrencyNumberBox_valueChanged(int arg1); + + void on_enableSniffingCB_stateChanged(int arg1); + + void on_destOverrideList_itemChanged(QListWidgetItem *item); + + void on_socksUDPCB_stateChanged(int arg1); + + void on_socksUDPIPAddrTxt_textEdited(const QString &arg1); + + void on_socksUserLevelSB_valueChanged(int arg1); + + void on_socksRemoveUserBtn_clicked(); + + void on_socksAddUserBtn_clicked(); + + void on_dokoIPAddrTxt_textEdited(const QString &arg1); + + void on_dokoPortSB_valueChanged(int arg1); + + void on_dokoTCPCB_stateChanged(int arg1); + + void on_dokoUDPCB_stateChanged(int arg1); + + void on_dokoTimeoutSB_valueChanged(int arg1); + + void on_dokoFollowRedirectCB_stateChanged(int arg1); + + void on_dokoUserLevelSB_valueChanged(int arg1); + + void on_mtEMailTxt_textEdited(const QString &arg1); + + void on_mtSecretTxt_textEdited(const QString &arg1); + + void on_mtUserLevelSB_valueChanged(int arg1); + + void on_inboundHostTxt_textEdited(const QString &arg1); + + void on_inboundPortTxt_textEdited(const QString &arg1); + + void on_socksAuthCombo_currentIndexChanged(const QString &arg1); + + private: + QJsonObject GenerateNewRoot(); + void LoadUIData(); + Ui::InboundEditor *ui; + QJsonObject original; + QJsonObject root; + // + QJsonObject httpSettings; + QJsonObject socksSettings; + QJsonObject mtSettings; + QJsonObject dokoSettings; + // + QJsonObject sniffing; + QJsonObject allocate; }; #endif // W_INBOUNDEDITOR_H diff --git a/src/ui/w_InboundEditor.ui b/src/ui/w_InboundEditor.ui index 582b2b74..915eb3c0 100644 --- a/src/ui/w_InboundEditor.ui +++ b/src/ui/w_InboundEditor.ui @@ -2,12 +2,15 @@ InboundEditor + + Qt::ApplicationModal + 0 0 - 874 - 497 + 815 + 500 @@ -24,6 +27,9 @@ Tag + + inboundTagTxt + @@ -41,7 +47,7 @@ - + @@ -59,7 +65,7 @@ - Port: 1080|env:PORT|80-85 + Port: 1080|80-85 @@ -70,10 +76,13 @@ Protocol + + inboundProtocolCombo + - + 0 @@ -82,22 +91,22 @@ - HTTP + http - SOCKS + socks - Dokodemo + dokodemo-door - MTProto + mtproto @@ -115,6 +124,9 @@ Strategy + + strategyCombo + @@ -127,12 +139,12 @@ - Always + always - Random + random @@ -142,6 +154,9 @@ Refresh + + refreshNumberBox + @@ -165,6 +180,9 @@ Concurrency + + concurrencyNumberBox + @@ -194,9 +212,32 @@ + + + 0 + 32 + + + + + 80 + 16777215 + + - Destination -Override + Destination Override + + + Qt::RichText + + + false + + + true + + + destOverrideList @@ -204,7 +245,7 @@ Override - http + HTTP Unchecked @@ -212,7 +253,7 @@ Override - tls + TLS Unchecked @@ -232,6 +273,9 @@ Override Enabled + + enableSniffingCB + @@ -250,7 +294,7 @@ Override - 0 + 1 @@ -267,6 +311,9 @@ Override Timeout + + httpTimeoutSpinBox + @@ -277,6 +324,9 @@ Override Allow Transparent + + httpTransparentCB + @@ -291,6 +341,9 @@ Override User Level + + httpUserLevelSB + @@ -299,7 +352,7 @@ Override - + @@ -337,6 +390,9 @@ Override Add + + httpAddUserTxt + @@ -375,18 +431,21 @@ Override Auth + + socksAuthCombo + - No Auth + noauth - Password + password @@ -396,6 +455,9 @@ Override Enable UDP + + socksUDPCB + @@ -403,6 +465,9 @@ Override Local UDP IP + + socksUDPIPAddrTxt + @@ -410,6 +475,9 @@ Override User Level + + socksUserLevelSB + @@ -432,7 +500,7 @@ Override - + @@ -470,6 +538,9 @@ Override Add + + socksAddUserTxt + @@ -506,6 +577,9 @@ Override IP Address + + dokoIPAddrTxt + @@ -520,6 +594,9 @@ Override Port + + dokoPortSB + @@ -540,6 +617,9 @@ Override Network + + dokoTCPCB + @@ -571,6 +651,9 @@ Override Timeout + + dokoTimeoutSB + @@ -578,6 +661,9 @@ Override Follow Redirect + + dokoFollowRedirectCB + @@ -585,6 +671,9 @@ Override User Level + + dokoUserLevelSB + @@ -603,7 +692,7 @@ Override - CheckBox + Enabled @@ -628,6 +717,9 @@ Override EMail Address + + mtEMailTxt + @@ -642,6 +734,9 @@ Override Secret + + mtSecretTxt + @@ -656,6 +751,9 @@ Override User Level + + mtUserLevelSB + @@ -670,6 +768,44 @@ Override + + inboundTagTxt + inboundHostTxt + inboundPortTxt + inboundProtocolCombo + httpTimeoutSpinBox + httpTransparentCB + httpUserLevelSB + httpAccountListBox + httpRemoveUserBtn + httpAddUserTxt + httpAddPasswordTxt + httpAddUserBtn + socksAuthCombo + socksUDPCB + socksUDPIPAddrTxt + socksUserLevelSB + socksAccountListBox + socksRemoveUserBtn + socksAddUserTxt + socksAddPasswordTxt + socksAddUserBtn + dokoIPAddrTxt + dokoPortSB + dokoTCPCB + dokoUDPCB + dokoTimeoutSB + dokoFollowRedirectCB + dokoUserLevelSB + mtEMailTxt + mtSecretTxt + mtUserLevelSB + strategyCombo + refreshNumberBox + concurrencyNumberBox + enableSniffingCB + destOverrideList + @@ -681,8 +817,8 @@ Override accept() - 248 - 254 + 841 + 493 157 @@ -697,8 +833,8 @@ Override reject() - 316 - 260 + 841 + 493 286 diff --git a/src/ui/w_JsonEditor.cpp b/src/ui/w_JsonEditor.cpp index 25a382a6..c062c67b 100644 --- a/src/ui/w_JsonEditor.cpp +++ b/src/ui/w_JsonEditor.cpp @@ -9,7 +9,7 @@ JsonEditor::JsonEditor(QJsonObject rootObject, QWidget *parent) : { ui->setupUi(this); original = rootObject; - result = rootObject; + final = rootObject; QString jsonString = JsonToString(rootObject); if (VerifyJsonString(&jsonString).isEmpty()) { @@ -36,7 +36,7 @@ QJsonObject JsonEditor::OpenEditor() string = ui->jsonEditor->toPlainText(); } - return resultCode == QDialog::Accepted ? result : original; + return resultCode == QDialog::Accepted ? final : original; } JsonEditor::~JsonEditor() @@ -52,8 +52,8 @@ void JsonEditor::on_jsonEditor_textChanged() if (VerifyResult.isEmpty()) { BLACK(jsonEditor) - result = JsonFromString(string); - model.loadJson(QJsonDocument(result).toJson()); + final = JsonFromString(string); + model.loadJson(QJsonDocument(final).toJson()); ui->jsonTree->expandAll(); ui->jsonTree->resizeColumnToContents(0); } else { diff --git a/src/ui/w_JsonEditor.h b/src/ui/w_JsonEditor.h index 1d04c1fd..397cbb22 100644 --- a/src/ui/w_JsonEditor.h +++ b/src/ui/w_JsonEditor.h @@ -27,7 +27,7 @@ class JsonEditor : public QDialog private: QJsonModel model; QJsonObject original; - QJsonObject result; + QJsonObject final; Ui::JsonEditor *ui; }; diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index 09f8d944..388ef1f7 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -538,20 +538,25 @@ void MainWindow::on_addConfigButton_clicked() OutboundEditor *w = new OutboundEditor(this); connect(w, &OutboundEditor::s_reload_config, this, &MainWindow::OnConfigListChanged); auto outboundEntry = w->OpenEditor(); - QJsonArray outboundsList; - outboundsList.push_back(outboundEntry); - QJsonObject root; - root.insert("outbounds", outboundsList); + bool isChanged = w->result() == QDialog::Accepted; auto alias = w->Alias; delete w; - auto conf = GetGlobalConfig(); - auto connectionList = conf.configs; - connectionList.push_back(alias.toStdString()); - conf.configs = connectionList; - SetGlobalConfig(conf); - OnConfigListChanged(false); - SaveConnectionConfig(root, &alias); - ShowAndSetConnection(CurrentConnectionName, false, false); + + if (isChanged) { + QJsonArray outboundsList; + outboundsList.push_back(outboundEntry); + QJsonObject root; + root.insert("outbounds", outboundsList); + // + auto conf = GetGlobalConfig(); + auto connectionList = conf.configs; + connectionList.push_back(alias.toStdString()); + conf.configs = connectionList; + SetGlobalConfig(conf); + OnConfigListChanged(false); + SaveConnectionConfig(root, &alias); + ShowAndSetConnection(CurrentConnectionName, false, false); + } } void MainWindow::on_editConfigButton_clicked() @@ -565,24 +570,29 @@ void MainWindow::on_editConfigButton_clicked() auto alias = ui->connectionListWidget->currentItem()->text(); auto outBoundRoot = connections[alias]; QJsonObject root; + bool isChanged = false; if (outBoundRoot["outbounds"].toArray().count() > 1) { LOG(MODULE_UI, "INFO: Opening route editor.") RouteEditor *routeWindow = new RouteEditor(outBoundRoot, alias, this); root = routeWindow->OpenEditor(); + isChanged = routeWindow->result() == QDialog::Accepted; } else { LOG(MODULE_UI, "INFO: Opening single connection edit window.") OutboundEditor *w = new OutboundEditor(outBoundRoot["outbounds"].toArray().first().toObject(), &alias, this); auto outboundEntry = w->OpenEditor(); + isChanged = w->result() == QDialog::Accepted; QJsonArray outboundsList; outboundsList.push_back(outboundEntry); root.insert("outbounds", outboundsList); } - connections[alias] = root; - SaveConnectionConfig(root, &alias); - OnConfigListChanged(alias == CurrentConnectionName); - ShowAndSetConnection(CurrentConnectionName, false, false); + if (isChanged) { + connections[alias] = root; + SaveConnectionConfig(root, &alias); + OnConfigListChanged(alias == CurrentConnectionName); + ShowAndSetConnection(CurrentConnectionName, false, false); + } } void MainWindow::on_reconnectButton_clicked() @@ -602,14 +612,19 @@ void MainWindow::on_action_RCM_EditJson_triggered() auto alias = ui->connectionListWidget->currentItem()->text(); JsonEditor *w = new JsonEditor(connections[alias], this); auto root = w->OpenEditor(); + bool isChanged = w->result() == QDialog::Accepted; delete w; - connections[alias] = root; - SaveConnectionConfig(root, &alias); - ShowAndSetConnection(CurrentConnectionName, false, false); + + if (isChanged) { + connections[alias] = root; + SaveConnectionConfig(root, &alias); + ShowAndSetConnection(CurrentConnectionName, false, false); + } } void MainWindow::on_editJsonBtn_clicked() { + // See above. on_action_RCM_EditJson_triggered(); } @@ -635,6 +650,12 @@ void MainWindow::on_speedTimer_Ticked() foreach (auto inbound, inbounds) { auto tag = inbound.toObject()["tag"].toString(); + + // TODO: A proper scheme... + if (tag == API_TAG_INBOUND) { + continue; + } + totalSpeedUp += vinstance->getTagLastUplink(tag); totalSpeedDown += vinstance->getTagLastDownlink(tag); totalDataUp += vinstance->getTagTotalUplink(tag); @@ -650,7 +671,6 @@ void MainWindow::on_speedTimer_Ticked() ui->netspeedLabel->setText(speedUp + "/s\r\n" + speedDown + "/s"); ui->dataamountLabel->setText(dataUp + "\r\n" + dataDown); // - hTray->setToolTip(TRAY_TOOLTIP_PREFIX "\r\n" + tr("Connected To Server: ") + CurrentConnectionName + "\r\n" - "Up: " + speedUp + "/s Down: " + speedDown + "/s"); + hTray->setToolTip(TRAY_TOOLTIP_PREFIX "\r\n" + tr("Connected To Server: ") + CurrentConnectionName + "\r\nUp: " + speedUp + "/s Down: " + speedDown + "/s"); } diff --git a/src/ui/w_RoutesEditor.cpp b/src/ui/w_RoutesEditor.cpp index 86160356..7cc16e0e 100644 --- a/src/ui/w_RoutesEditor.cpp +++ b/src/ui/w_RoutesEditor.cpp @@ -3,6 +3,7 @@ #include "ui_w_RoutesEditor.h" #include "w_OutboundEditor.h" #include "w_JsonEditor.h" +#include "w_InboundEditor.h" RouteEditor::RouteEditor(QJsonObject connection, const QString alias, QWidget *parent) : QDialog(parent), @@ -15,6 +16,7 @@ RouteEditor::RouteEditor(QJsonObject connection, const QString alias, QWidget *p routes = StructFromJsonString(JsonToString(root["routing"].toObject())); ui->setupUi(this); ui->outboundsList->clear(); + ui->inboundsList->clear(); foreach (auto out, outbounds) { bool hasTag = out.toObject().contains("tag"); @@ -28,7 +30,7 @@ RouteEditor::RouteEditor(QJsonObject connection, const QString alias, QWidget *p foreach (auto in, inbounds) { bool hasTag = in.toObject().contains("tag"); // - auto tag = hasTag ? in.toObject()["tag"].toString() : tr("NoTag"); + auto tag = hasTag ? in.toObject()["tag"].toString() : tr("NoTag"); auto protocol = in.toObject()["protocol"].toString(); auto port = in.toObject()["port"].toVariant().toString(); // @@ -55,6 +57,9 @@ RouteEditor::RouteEditor(QJsonObject connection, const QString alias, QWidget *p QJsonObject RouteEditor::OpenEditor() { this->exec(); + root["inbounds"] = inbounds; + root["outbounds"] = outbounds; + root["routing"] = GetRootObject(routes); return root; } @@ -146,8 +151,8 @@ void RouteEditor::on_editOutboundBtn_clicked() auto protocol = currentOutbound["protocol"].toString(); if (protocol != "vmess" && protocol != "shadowsocks" && protocol != "socks") { - QvMessageBox(this, tr("Cannot Edit"), tr("Currently, this type of outbound is not supported by the editor.")); - QvMessageBox(this, tr("Cannot Edit"), tr("We will launch Json Editor instead.")); + 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.")); JsonEditor *w = new JsonEditor(currentOutbound, this); result = w->OpenEditor(); delete w; @@ -168,4 +173,23 @@ void RouteEditor::on_insertDirectBtn_clicked() void RouteEditor::on_editInboundBtn_clicked() { + QJsonObject result; + int row = ui->inboundsList->currentRow(); + auto currentInbound = inbounds[row].toObject(); + auto protocol = currentInbound["protocol"].toString(); + + 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.")); + JsonEditor *w = new JsonEditor(currentInbound, this); + result = w->OpenEditor(); + delete w; + } else { + InboundEditor *w = new InboundEditor(currentInbound, this); + result = w->OpenEditor(); + delete w; + } + + inbounds[row] = result; + on_inboundsList_currentRowChanged(row); } diff --git a/src/ui/w_RoutesEditor.ui b/src/ui/w_RoutesEditor.ui index f27d4dbf..d0c156d5 100644 --- a/src/ui/w_RoutesEditor.ui +++ b/src/ui/w_RoutesEditor.ui @@ -6,134 +6,29 @@ 0 0 - 889 + 950 560 + + + 950 + 560 + + Dialog - - - - - - Routes List - - - - - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - - InBounds - - - - - DomainOrIP - - - - - Outbound - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - ... - - - - :/icons/add_connection_btn.png:/icons/add_connection_btn.png - - - - - - - ... - - - - :/icons/remove_connection_btn.png:/icons/remove_connection_btn.png - - - - - - - Change IO - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Route Information - - - - - - Domains - - - - - - - IPs - - - - - - - + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + @@ -437,15 +332,131 @@ - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - + + + + + + Routes List + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + + Enabled + + + + + InBounds + + + + + DomainOrIP + + + + + Outbound + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + ... + + + + :/icons/add_connection_btn.png:/icons/add_connection_btn.png + + + + + + + ... + + + + :/icons/remove_connection_btn.png:/icons/remove_connection_btn.png + + + + + + + Change IO + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Route Information + + + + + + Domains + + + + + + + IPs + + + + + + + diff --git a/translations/en-US.ts b/translations/en-US.ts index 90bfec2c..b329361c 100644 --- a/translations/en-US.ts +++ b/translations/en-US.ts @@ -120,266 +120,274 @@ InboundEditor - + Inbound Editor - + Tag - + Tag of this inbound entry - + Listening - + Hostname or IP Address - + : - - Port: 1080|env:PORT|80-85 - - - - + Protocol - - HTTP - - - - - SOCKS - - - - - Dokodemo - - - - - MTProto - - - - + Allocation Settings - + Strategy - - Always - - - - - Random - - - - + Refresh - + Concurrency - + Sniffing Settings - - Destination -Override + + Port: 1080|80-85 - - - - + + always + + + + + random + + + + + Destination Override + + + + + + + + Enabled - + HTTP Inbound Settings - - + + Timeout - + Allow Transparent - - - - + + + + User Level - - - - + + + + ... - - - + + Password - - + + Add - - + + Username - - + + Accounts - + SOCKS Inbound Settings - + Auth - - No Auth - - - - + Enable UDP - + Local UDP IP - + 127.0.0.1 - + Dokodemo-Door Inbound Settings - + IP Address - + Not necessary when setting "Follow Redirect" - + Port - + Network - + TCP - + UDP - + Follow Redirect - - CheckBox - - - - + MTProto Inbound Settings - + EMail Address - + Secret - + SECRET + + + 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. + + JsonEditor @@ -434,172 +442,204 @@ Override MainWindow - + Qv2ray - + + Share + + + + + QR Code + + + + + VMess + + + + + Options + + + + + Instant + + + + + 0.00 KB/s +0.00 KB/s + + + + + Total + + + + + 0.00 KB +0.00 KB + + + + Connect - + Disconnect - + Reconnect - + Clear Log - + Prefrences - + Stopped - + Host List - + Config Details - + Type - + Host - + Port - + Detail - + Ping - - QR - - - - - :// - - - - + Json - + #AddConnection - + A - - + + #ImportConnection - + I - + #EditConnection - + ... - + #RemoveConnection - + R - + Log - + #ManuallyCreateConnection - + #Exit - + #Preferences - + #Start - + #Stop - + #Restart - + Hide @@ -625,8 +665,8 @@ Override - - + + Show @@ -670,7 +710,7 @@ Override - + Connected To Server: @@ -685,65 +725,71 @@ Override - + UUID - + AlterID - + Transport - + Email - + Encryption - + Username - - + + Rename a Connection - + The name cannot be empty + Removing this Connection - - + + Failed to delete connection file, please delete manually. + + + + + No Config Selected - - + + Please Select a Config - + The name has been used already, Please choose another. @@ -1275,27 +1321,42 @@ Override - + 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 + + + + Another instance of Qv2ray is already running. @@ -1318,150 +1379,157 @@ Override RouteEditor - + Dialog - + Routes List - + + Enabled + + + + InBounds - + DomainOrIP - + Outbound - - - - - - + + + + + + ... - + Change IO - + Route Information - + Domains - + IPs - + Inbound List - + Add Default - + Inbound Information - - + + Tag - - + + Type - - + + Address - - + + Port - + Outbound List - + Add "Direct" - + Outbound Information - - + + Edit - + Status - - - + + + NoTag - + #NoTag - - + + Cannot Edit - + + Currently, this type of outbound is not supported by the editor. - + + We will launch Json Editor instead.