diff --git a/Qv2ray.pro b/Qv2ray.pro index 87cdc972..3d394b4a 100644 --- a/Qv2ray.pro +++ b/Qv2ray.pro @@ -40,12 +40,13 @@ SOURCES += \ HEADERS += \ ignored_cpp_structs.hpp \ src/QJsonObjectInsertMacros.h \ + src/Qv2rayBase.h \ src/QvCoreConfigObjects.h \ src/QvCoreConfigOperations.h \ src/QvCoreInteractions.h \ - src/QvGUIConfigObjects.h \ src/QvHTTPRequestHelper.h \ src/QvRunguard.h \ + src/QvTinyLog.h \ src/QvUtils.h \ src/w_MainWindow.h \ src/w_ConnectionEditWindow.h \ diff --git a/icons/add_connection_btn.png b/icons/add_connection_btn.png new file mode 100644 index 00000000..abc2b175 Binary files /dev/null and b/icons/add_connection_btn.png differ diff --git a/icons/edit_connection_btn.png b/icons/edit_connection_btn.png new file mode 100644 index 00000000..5984dea6 Binary files /dev/null and b/icons/edit_connection_btn.png differ diff --git a/icons/import_connection_btn.png b/icons/import_connection_btn.png new file mode 100644 index 00000000..b5f3dec6 Binary files /dev/null and b/icons/import_connection_btn.png differ diff --git a/icons/remove_connection_btn.png b/icons/remove_connection_btn.png new file mode 100644 index 00000000..3e4a0411 Binary files /dev/null and b/icons/remove_connection_btn.png differ diff --git a/resources.qrc b/resources.qrc index 7ef2d9f4..51786e22 100644 --- a/resources.qrc +++ b/resources.qrc @@ -3,5 +3,9 @@ translations/zh-CN.qm translations/en-US.qm icons/Qv2ray.ico + icons/add_connection_btn.png + icons/import_connection_btn.png + icons/remove_connection_btn.png + icons/edit_connection_btn.png diff --git a/src/QvGUIConfigObjects.h b/src/Qv2rayBase.h similarity index 79% rename from src/QvGUIConfigObjects.h rename to src/Qv2rayBase.h index a6b43a34..40d184b8 100644 --- a/src/QvGUIConfigObjects.h +++ b/src/Qv2rayBase.h @@ -2,14 +2,11 @@ #define HCONFIGOBJECTS_H #include -#include +#include "QvTinyLog.h" #include "QvCoreConfigObjects.h" -using namespace std; -#define LOG(arg) cout << arg << endl; - -#define QV2RAY_VERSION 1 -#define QV2RAY_VERSION_STRING "v1.3.0" +#define QV2RAY_VERSION_STRING "v1.3.2" +#define QV2RAY_CONFIG_VERSION "1.1" #define QV2RAY_CONFIG_PATH (Qv2ray::Utils::GetConfigDirPath() + "/") #define QV2RAY_GUI_CONFIG_PATH (QV2RAY_CONFIG_PATH + "Qv2ray.conf") #define QV2RAY_GENERATED_CONFIG_FILE_PATH (QV2RAY_CONFIG_PATH + "generated/config.gen.json") @@ -22,14 +19,16 @@ using namespace std; // GUI TOOLS #define RED(obj) \ - auto p = ui->obj->palette(); \ - p.setColor(QPalette::Text, Qt::red); \ - ui->obj->setPalette(p); + auto _p = ui->obj->palette(); \ + _p.setColor(QPalette::Text, Qt::red); \ + ui->obj->setPalette(_p); #define BLACK(obj) \ - auto p = ui->obj->palette(); \ - p.setColor(QPalette::Text, Qt::black); \ - ui->obj->setPalette(p); + auto _p = ui->obj->palette(); \ + _p.setColor(QPalette::Text, Qt::black); \ + ui->obj->setPalette(_p); + +#define QSTRING(std_string) QString::fromStdString(std_string) #ifdef __WIN32 #define NEWLINE "\r\n" @@ -66,7 +65,7 @@ namespace Qv2ray }; struct Qv2Config { - string v = "1.1"; + string config_version; bool runAsRoot; int logLevel; // @@ -86,7 +85,7 @@ namespace Qv2ray list configs; map subscribes; MuxObject mux; - Qv2Config(): runAsRoot(false), logLevel(), proxyDefault(), proxyCN(), withLocalDNS(), inBoundSettings(), configs(), subscribes(), mux() { } + Qv2Config(): config_version(QV2RAY_CONFIG_VERSION), runAsRoot(false), logLevel(), proxyDefault(), proxyCN(), withLocalDNS(), inBoundSettings(), configs(), subscribes(), mux() { } Qv2Config(string lang, string exePath, string assetsPath, int log, QvBasicInboundSetting _inBoundSettings): Qv2Config() { ignoredVersion = ""; @@ -105,7 +104,7 @@ namespace Qv2ray proxyDefault = true; withLocalDNS = true; } - XTOSTRUCT(O(v, runAsRoot, logLevel, language, autoStartConfig, ignoredVersion, v2CorePath, v2AssetsPath, proxyDefault, proxyCN, withLocalDNS, dnsList, inBoundSettings, mux, configs, subscribes)) + XTOSTRUCT(O(config_version, runAsRoot, logLevel, language, autoStartConfig, ignoredVersion, v2CorePath, v2AssetsPath, proxyDefault, proxyCN, withLocalDNS, dnsList, inBoundSettings, mux, configs, subscribes)) }; } } diff --git a/src/QvCoreConfigObjects.h b/src/QvCoreConfigObjects.h index 8ec2b59b..859e003a 100644 --- a/src/QvCoreConfigObjects.h +++ b/src/QvCoreConfigObjects.h @@ -2,13 +2,15 @@ #include #include -using namespace x2struct; #ifndef V2CONFIG_H #define V2CONFIG_H +using namespace x2struct; using namespace std; -/*------------------------------------------------------------------------------------------------------------*/ + +/* ----------------------------------------- * --------------------- + * --------------------- * ----------------------------------------- */ namespace Qv2ray { @@ -18,29 +20,31 @@ namespace Qv2ray string v, ps, add, port, id, aid, net, type, host, path, tls; XTOSTRUCT(O(v, ps, add, port, id, aid, net, type, host, path, tls)) }; - - /// Used in config generation + // + // Used in config generation struct AccountObject { string user; string pass; XTOSTRUCT(O(user, pass)) }; - + // + // struct ApiObject { - string tag = "api"; + string tag; list services; - ApiObject() : tag(), services() {} + ApiObject() : tag("api"), services() {} XTOSTRUCT(O(tag, services)) }; - - + // + // struct SystemPolicyObject { bool statsInboundUplink; bool statsInboundDownlink; SystemPolicyObject() : statsInboundUplink(), statsInboundDownlink() {} XTOSTRUCT(O(statsInboundUplink, statsInboundDownlink)) }; - + // + // struct LevelPolicyObject { int handshake; int connIdle; @@ -52,33 +56,38 @@ namespace Qv2ray LevelPolicyObject(): handshake(), connIdle(), uplinkOnly(), downlinkOnly(), statsUserUplink(), statsUserDownlink(), bufferSize() {} XTOSTRUCT(O(handshake, connIdle, uplinkOnly, downlinkOnly, statsUserUplink, statsUserDownlink, bufferSize)) }; - + // + // struct PolicyObject { map level; list system; PolicyObject(): level(), system() {} XTOSTRUCT(O(level, system)) }; - + // + // namespace TransferSettingObjects { struct HTTPRequestObject { - string version ; - string method ; + string version; + string method; list path; map> headers; HTTPRequestObject(): version("1.1"), method("GET"), path(), headers() {} XTOSTRUCT(O(version, method, path, headers)) }; - + // + // struct HTTPResponseObject { string version; - string status ; - string reason ; + string status; + string reason; map> headers; HTTPResponseObject(): version("1.1"), status("200"), reason("OK"), headers() {} XTOSTRUCT(O(version, status, reason, headers)) }; + // + // struct TCPHeader_M_Object { string type; HTTPRequestObject request; @@ -86,20 +95,22 @@ namespace Qv2ray TCPHeader_M_Object(): type("none"), request(), response() {} XTOSTRUCT(O(type, request, response)) }; + // + // struct HeaderObject { string type; HeaderObject(): type("none") {} XTOSTRUCT(O(type)) }; - - + // + // struct TCPObject { TCPHeader_M_Object header; TCPObject(): header() {} XTOSTRUCT(O(header)) }; - - + // + // struct KCPObject { int mtu = 1350; int tti = 20; @@ -112,27 +123,31 @@ namespace Qv2ray KCPObject(): header() {} XTOSTRUCT(O(mtu, tti, uplinkCapacity, downlinkCapacity, congestion, readBufferSize, writeBufferSize, header)) }; - + // + // struct WebSocketObject { string path; map headers; WebSocketObject(): path("/"), headers() {} XTOSTRUCT(O(path, headers)) }; - + // + // struct HttpObject { list host; string path; HttpObject() : host(), path("/") {} XTOSTRUCT(O(host, path)) }; - + // + // struct DomainSocketObject { string path; DomainSocketObject(): path("/") {} XTOSTRUCT(O(path)) }; - + // + // struct QuicObject { string security; string key; @@ -140,7 +155,8 @@ namespace Qv2ray QuicObject(): security(""), key(""), header() {} XTOSTRUCT(O(security, key, header)) }; - + // + // struct SockoptObject { int mark; bool tcpFastOpen; @@ -148,7 +164,8 @@ namespace Qv2ray SockoptObject(): mark(0), tcpFastOpen(false), tproxy("off") {} XTOSTRUCT(O(mark, tcpFastOpen, tproxy)) }; - + // + // struct CertificateObject { string usage; string certificateFile; @@ -158,7 +175,8 @@ namespace Qv2ray CertificateObject(): usage(), certificateFile(), keyFile(), certificate(), key() {} XTOSTRUCT(O(usage, certificateFile, keyFile, certificate, key)) }; - + // + // struct TLSObject { string serverName; bool allowInsecure; @@ -170,14 +188,15 @@ namespace Qv2ray }; } // + // struct SniffingObject { bool enabled = false; list destOverride; SniffingObject(): enabled(), destOverride() {} XTOSTRUCT(O(enabled, destOverride)) }; - - + // + // struct StreamSettingsObject { string network; string security; @@ -192,61 +211,58 @@ namespace Qv2ray StreamSettingsObject(): network("tcp"), security(), sockopt(), tlsSettings(), tcpSettings(), kcpSettings(), wsSettings(), httpSettings(), dsSettings(), quicSettings() {} XTOSTRUCT(O(network, security, sockopt, tcpSettings, tlsSettings, kcpSettings, wsSettings, httpSettings, dsSettings, quicSettings)) }; - + // + // struct MuxObject { bool enabled; int concurrency; MuxObject(): enabled(), concurrency() {} XTOSTRUCT(O(enabled, concurrency)) }; - - /// Some protocols from: https://v2ray.com/chapter_02/02_protocols.html + // + // Some protocols from: https://v2ray.com/chapter_02/02_protocols.html namespace Protocols { - struct XOutBoundsType {}; - struct XInBoundsType {}; - - /// DNS, OutBound - struct DNSOut: XOutBoundsType { + // DNS, OutBound + struct DNSOut { string network; string address; int port; + DNSOut(): network(""), address("0.0.0.0"), port(0) {} XTOSTRUCT(O(network, address, port)) }; - - - /// MTProto, InBound || OutBound - struct MTProtoIn: XInBoundsType, XOutBoundsType { + // + // MTProto, InBound || OutBound + struct MTProtoIn { struct UserObject { string email; int level; string secret; + UserObject() : email("user@domain.com"), level(0), secret("") {} XTOSTRUCT(O(email, level, secret)) }; list users; XTOSTRUCT(O(users)) }; - - /// Socks, OutBound - struct SocksOut: XOutBoundsType { - struct ServerObject { - struct UserObject { - string user; - string pass; - int level; - XTOSTRUCT(O(user, pass, level)) - }; - - string address; - int port; - list users; - - XTOSTRUCT(O(address, port, users)) + // + // Socks, OutBound + struct SocksServerObject { + struct UserObject { + string user; + string pass; + int level; + UserObject(): user("username"), pass("password"), level(0) {} + XTOSTRUCT(O(user, pass, level)) }; - list servers; - XTOSTRUCT(O(servers)) - }; + string address; + int port; + list users; + SocksServerObject(): address("0.0.0.0"), port(0), users() {} + XTOSTRUCT(O(address, port, users)) + }; + // + // VMess Server struct VMessServerObject { struct UserObject { string id; @@ -256,29 +272,30 @@ namespace Qv2ray UserObject() : id(""), alterId(64), security("auto"), level(0) {} XTOSTRUCT(O(id, alterId, security, level)) }; - // OUTBound; + string address; int port; list users; VMessServerObject(): address(""), port(0), users() {} XTOSTRUCT(O(address, port, users)) }; - - struct ShadowSocksServerObject { + // + // ShadowSocks Server + struct ShadowSocksServer { string email; string address; - int port; string method; string password; bool ota; int level; - ShadowSocksServerObject(): email(""), address("0.0.0.0"), port(0), method("aes-256-cfb"), password(""), ota(false), level(0) - {} + int port; + ShadowSocksServer(): email("user@domain.com"), address("0.0.0.0"), method("aes-256-cfb"), password(""), ota(false), level(0), port(0) {} XTOSTRUCT(O(email, address, port, method, password, ota, level)) }; } } } + using namespace Qv2ray::V2ConfigModels; using namespace Qv2ray::V2ConfigModels::Protocols; diff --git a/src/QvCoreConfigOperations.h b/src/QvCoreConfigOperations.h index 351a6006..a32c5d2a 100644 --- a/src/QvCoreConfigOperations.h +++ b/src/QvCoreConfigOperations.h @@ -54,6 +54,7 @@ namespace Qv2ray // -------------------------- BEGIN CONFIG CONVERSIONS --------------------------------------------- // Save Connection Config bool SaveConnectionConfig(QJsonObject obj, const QString *alias); + bool RenameConnection(QString originalName, QString newName); // VMess Protocol QJsonObject ConvertConfigFromVMessString(QString vmess); QJsonObject ConvertConfigFromFile(QString sourceFilePath, bool overrideInbounds); diff --git a/src/QvCoreConfigOperations_Convertion.cpp b/src/QvCoreConfigOperations_Convertion.cpp index 8f34e7f0..4a7549f4 100644 --- a/src/QvCoreConfigOperations_Convertion.cpp +++ b/src/QvCoreConfigOperations_Convertion.cpp @@ -117,6 +117,11 @@ namespace Qv2ray return list; } + bool RenameConnection(QString originalName, QString newName) + { + return QFile(QV2RAY_CONFIG_PATH + originalName + QV2RAY_CONNECTION_FILE_EXTENSION).rename(QV2RAY_CONFIG_PATH + newName + QV2RAY_CONNECTION_FILE_EXTENSION); + } + int StartPreparation(QJsonObject fullConfig) { QString json = JSONToString(fullConfig); diff --git a/src/QvCoreConfigOperations_Verification.cpp b/src/QvCoreConfigOperations_Verification.cpp index 8c1d5943..94f910ff 100644 --- a/src/QvCoreConfigOperations_Verification.cpp +++ b/src/QvCoreConfigOperations_Verification.cpp @@ -18,7 +18,7 @@ namespace Qv2ray auto vmessConf = StructFromJSONString(vmessString); return 0; } catch (exception *e) { - LOG(QObject::tr("#VMessDecodeError").toStdString() << e->what()) + LOG(MODULE_CONNECTION, QObject::tr("#VMessDecodeError").toStdString() << e->what()) return -2; } } diff --git a/src/QvCoreInteractions.h b/src/QvCoreInteractions.h index 4cad4b6e..32003fc2 100644 --- a/src/QvCoreInteractions.h +++ b/src/QvCoreInteractions.h @@ -2,7 +2,7 @@ #define VINTERACT_H #include #include -#include "QvGUIConfigObjects.h" +#include "Qv2rayBase.h" namespace Qv2ray { diff --git a/src/QvTinyLog.h b/src/QvTinyLog.h new file mode 100644 index 00000000..bbe27b6f --- /dev/null +++ b/src/QvTinyLog.h @@ -0,0 +1,19 @@ +#ifndef QVTINYLOG_H +#define QVTINYLOG_H + +#include +using namespace std; +/* + * Tiny log module. + */ +#define LOG(module, msg) cout << "[" << module << "]: " << msg << endl; + +#define MODULE_INIT "INIT" +#define MODULE_UPDATE "UPDATE" +#define MODULE_VCORE "VCORE" +#define MODULE_CONNECTION_VMESS "CONNETION-VMESS" +#define MODULE_CONNECTION "CONNECTION" +#define MODULE_UI "UI" +#define MODULE_FILE "FILE" + +#endif // QVTINYLOG_H diff --git a/src/QvUtils.cpp b/src/QvUtils.cpp index 89485cea..4e1ec40a 100644 --- a/src/QvUtils.cpp +++ b/src/QvUtils.cpp @@ -98,14 +98,14 @@ namespace Qv2ray file.close(); } - QStringList GetFileList(QDir *dir) + QStringList getFileList(QDir *dir) { return dir->entryList(QStringList() << "*" << "*.*", QDir::Hidden | QDir::Files); } bool CheckFile(QDir *dir, QString fileName) { - return GetFileList(dir).indexOf(fileName) >= 0; + return getFileList(dir).indexOf(fileName) >= 0; } void QvMessageBox(QWidget *parent, QString title, QString text) diff --git a/src/QvUtils.h b/src/QvUtils.h index a3dc3a50..3c5db765 100644 --- a/src/QvUtils.h +++ b/src/QvUtils.h @@ -1,7 +1,7 @@ #ifndef UTILS_H #define UTILS_H -#include "QvGUIConfigObjects.h" +#include "Qv2rayBase.h" #include #include @@ -11,7 +11,7 @@ namespace Qv2ray { QTranslator *getTranslator(QString lang); - QStringList GetFileList(QDir *dir); + QStringList getFileList(QDir *dir); QString Base64Encode(QString string); QString Base64Decode(QString string); diff --git a/src/main.cpp b/src/main.cpp index 5c87afbc..94ad6af1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,7 @@ #include #include "QvUtils.h" -#include "QvGUIConfigObjects.h" +#include "Qv2rayBase.h" #include "QvRunguard.h" #include "w_MainWindow.h" @@ -14,24 +14,16 @@ using namespace Qv2ray::QvConfigModels; bool initializeQv() { /// Qv2ray Config Path and ends with "/" - QString configPath; - QString exeDefaultPath; - QString v2AssetsPath; + QString configPath = QDir::homePath() + "/.qv2ray"; + QString exeDefaultPath = configPath + "/vcore/v2ray"; + QString v2AssetsPath = configPath + "/vcore"; // #if defined(__WIN32) // For Windows, there's no such 'installation' of a software // package, So as what ShadowSocks and v2rayX does, save config files next to // the executable. - configPath = QDir::homePath() + "/.qv2ray"; - exeDefaultPath = configPath + "/vcore/v2ray.exe"; - v2AssetsPath = configPath + "/vcore"; -#else // NOT WINDOWS (*nix) - // Global config dir. - configPath = QDir::homePath() + "/.qv2ray"; - exeDefaultPath = configPath + "/vcore/v2ray"; - v2AssetsPath = configPath + "/vcore"; -#endif -#ifdef __linux__ + exeDefaultPath = exeDefaultPath + ".exe"; +#elif defined(__linux__) // Special case for GNU/Linux // Unused these values Q_UNUSED(v2AssetsPath) @@ -39,6 +31,7 @@ bool initializeQv() v2AssetsPath = "/etc/v2ray"; exeDefaultPath = "/bin/v2ray"; #endif + // SetConfigDirPath(configPath); auto ConfigDir = new QDir(configPath); @@ -46,9 +39,9 @@ bool initializeQv() auto result = QDir().mkdir(QV2RAY_CONFIG_PATH); if (result) { - LOG("Created Qv2ray config dir at: " + QV2RAY_CONFIG_PATH.toStdString()) + LOG(MODULE_INIT, "Created Qv2ray config dir at: " + QV2RAY_CONFIG_PATH.toStdString()) } else { - LOG("Failed to create config dir at: " + QV2RAY_CONFIG_PATH.toStdString()) + LOG(MODULE_INIT, "Failed to create config dir at: " + QV2RAY_CONFIG_PATH.toStdString()) return false; } } @@ -59,9 +52,9 @@ bool initializeQv() auto result2 = QDir().mkdir(genPath); if (result2) { - LOG("Created config generation dir at: " + genPath.toStdString()) + LOG(MODULE_INIT, "Created config generation dir at: " + genPath.toStdString()) } else { - LOG("Failed to create config generation dir at: " + genPath.toStdString()) + LOG(MODULE_INIT, "Failed to create config generation dir at: " + genPath.toStdString()) return false; } } @@ -77,10 +70,10 @@ bool initializeQv() SetGlobalConfig(conf); SaveGlobalConfig(); // - LOG("Created initial default config file.") + LOG(MODULE_INIT, "Created initial default config file.") } else { LoadGlobalConfig(); - LOG("Loaded config file.") + LOG(MODULE_INIT, "Loaded config file.") } return true; @@ -88,13 +81,21 @@ bool initializeQv() int main(int argc, char *argv[]) { - LOG("Hv2ray Copyright (C) 2019 aliyuchang33 \r\n" - "Hv2ray/Qv2ray (partial) Copyright (C) SoneWinstone (jianwenzhen@qq.com) \r\n" - "Qv2ray Copyright (C) 2019 Leroy.H.Y \r\n" - "\r\n" - "This program comes with ABSOLUTELY NO WARRANTY.\r\n" + LOG("LICENCE", "\r\nThis program comes with ABSOLUTELY NO WARRANTY.\r\n" "This is free software, and you are welcome to redistribute it\r\n" - "under certain conditions.\r\n") + "under certain conditions.\r\n" + "\r\n" + "Hv2ray Copyright (C) 2019 aliyuchang33\r\n" + "Hv2ray/Qv2ray (partial) Copyright 2019 (C) SoneWinstone\r\n" + "Qv2ray Copyright (C) 2019 Leroy.H.Y\r\n" + "\r\n" + "Qv2ray Version: " QV2RAY_VERSION_STRING + "\r\n" + "OS: " + QSysInfo::prettyProductName().toStdString() + + "\r\n" + "Arch: " + QSysInfo::currentCpuArchitecture().toStdString()) + LOG("DEBUG", "============================== This is a debug build ==============================") + // QApplication _qApp(argc, argv); // // Qv2ray Initialize @@ -109,9 +110,9 @@ int main(int argc, char *argv[]) #endif if (_qApp.installTranslator(getTranslator(QString::fromStdString(GetGlobalConfig().language)))) { - LOG("Loaded translations " + GetGlobalConfig().language) + LOG(MODULE_UI, "Loaded translations " + GetGlobalConfig().language) } else if (_qApp.installTranslator(getTranslator("en-US"))) { - LOG("Loaded default translations") + LOG(MODULE_UI, "Loaded default translations") } else { QvMessageBox( nullptr, "Failed to load translations 无法加载语言文件", @@ -119,18 +120,17 @@ int main(int argc, char *argv[]) "无法加载语言文件,用户体验可能会降级."); } -#ifndef QT_NO_DEBUG - QvMessageBox(nullptr, "Warning", "This is a debug build."); - LOG("DEBUG BUILD!") -#else - RunGuard guard("Qv2ray-Instance-Identifier"); + RunGuard guard("Qv2ray-Instance-Identifier" +#ifdef QT_DEBUG + "DEBUG_VERSION" +#endif + ); if (!guard.isSingleInstance()) { - Utils::QvMessageBox(nullptr, QObject::tr("Qv2ray"), QObject::tr("#AnotherInstanceRunning")); + Utils::QvMessageBox(nullptr, "Qv2ray", QObject::tr("#AnotherInstanceRunning")); return -1; } -#endif // Show MainWindow MainWindow w; return _qApp.exec(); diff --git a/src/w_ConnectionEditWindow.cpp b/src/w_ConnectionEditWindow.cpp index 6bfc9af3..0003c138 100644 --- a/src/w_ConnectionEditWindow.cpp +++ b/src/w_ConnectionEditWindow.cpp @@ -19,7 +19,9 @@ ConnectionEditWindow::ConnectionEditWindow(QWidget *parent) ui->setupUi(this); ui->portLineEdit->setValidator(new QIntValidator()); ui->alterLineEdit->setValidator(new QIntValidator()); - shadowsocks = ShadowSocksServerObject(); + shadowsocks = ShadowSocksServer(); + socks = SocksServerObject(); + socks.users.push_back(SocksServerObject::UserObject()); vmess = VMessServerObject(); vmess.users.push_back(VMessServerObject::UserObject()); stream = StreamSettingsObject(); @@ -40,10 +42,20 @@ ConnectionEditWindow::ConnectionEditWindow(QJsonObject editRootObject, QString a stream = StructFromJSONString(JSONToString(outBoundRoot["streamSettings"].toObject())); shadowsocks.port = vmess.port; shadowsocks.address = vmess.address; + socks.address = vmess.address; + socks.port = vmess.port; } else if (OutboundType == "shadowsocks") { - shadowsocks = StructFromJSONString(JSONToString(outBoundRoot["settings"].toObject()["servers"].toArray().first().toObject())); + shadowsocks = StructFromJSONString(JSONToString(outBoundRoot["settings"].toObject()["servers"].toArray().first().toObject())); vmess.address = shadowsocks.address; vmess.port = shadowsocks.port; + socks.address = shadowsocks.address; + socks.port = shadowsocks.port; + } else if (OutboundType == "socks") { + socks = StructFromJSONString(JSONToString(outBoundRoot["settings"].toObject()["servers"].toArray().first().toObject())); + vmess.address = socks.address; + vmess.port = socks.port; + shadowsocks.address = socks.address; + shadowsocks.port = socks.port; } ReLoad_GUI_JSON_ModelContent(); @@ -60,64 +72,70 @@ void ConnectionEditWindow::ReLoad_GUI_JSON_ModelContent() { if (OutboundType == "vmess") { ui->outBoundTypeCombo->setCurrentIndex(0); - ui->ipLineEdit->setText(QString::fromStdString(vmess.address)); + ui->ipLineEdit->setText(QSTRING(vmess.address)); ui->portLineEdit->setText(QString::number(vmess.port)); - ui->idLineEdit->setText(QString::fromStdString(vmess.users.front().id)); + ui->idLineEdit->setText(QSTRING(vmess.users.front().id)); ui->alterLineEdit->setText(QString::number(vmess.users.front().alterId)); - ui->securityCombo->setCurrentText(QString::fromStdString(vmess.users.front().security)); - ui->tranportCombo->setCurrentText(QString::fromStdString(stream.network)); + ui->securityCombo->setCurrentText(QSTRING(vmess.users.front().security)); + ui->tranportCombo->setCurrentText(QSTRING(stream.network)); ui->tlsCB->setChecked(stream.security == "tls"); // TCP - ui->tcpHeaderTypeCB->setCurrentText(QString::fromStdString(stream.tcpSettings.header.type)); + ui->tcpHeaderTypeCB->setCurrentText(QSTRING(stream.tcpSettings.header.type)); ui->tcpRequestTxt->setPlainText(StructToJSONString(stream.tcpSettings.header.request)); ui->tcpRespTxt->setPlainText(StructToJSONString(stream.tcpSettings.header.response)); // HTTP QString allHosts; foreach (auto host, stream.httpSettings.host) { - allHosts = allHosts + QString::fromStdString(host) + "\r\n"; + allHosts = allHosts + QSTRING(host) + "\r\n"; } ui->httpHostTxt->setPlainText(allHosts); - ui->httpPathTxt->setText(QString::fromStdString(stream.httpSettings.path)); + ui->httpPathTxt->setText(QSTRING(stream.httpSettings.path)); // WS - ui->wsPathTxt->setText(QString::fromStdString(stream.wsSettings.path)); + ui->wsPathTxt->setText(QSTRING(stream.wsSettings.path)); QString wsHeaders; foreach (auto _, stream.wsSettings.headers) { - wsHeaders = wsHeaders + QString::fromStdString(_.first + "|" + _.second) + "\r\n"; + wsHeaders = wsHeaders + QSTRING(_.first + "|" + _.second) + "\r\n"; } ui->wsHeadersTxt->setPlainText(wsHeaders); // mKCP ui->kcpMTU->setValue(stream.kcpSettings.mtu); ui->kcpTTI->setValue(stream.kcpSettings.tti); - ui->kcpHeaderType->setCurrentText(QString::fromStdString(stream.kcpSettings.header.type)); + ui->kcpHeaderType->setCurrentText(QSTRING(stream.kcpSettings.header.type)); ui->kcpCongestionCB->setChecked(stream.kcpSettings.congestion); ui->kcpReadBufferSB->setValue(stream.kcpSettings.readBufferSize); ui->kcpUploadCapacSB->setValue(stream.kcpSettings.uplinkCapacity); ui->kcpDownCapacitySB->setValue(stream.kcpSettings.downlinkCapacity); ui->kcpWriteBufferSB->setValue(stream.kcpSettings.writeBufferSize); // DS - ui->dsPathTxt->setText(QString::fromStdString(stream.dsSettings.path)); + ui->dsPathTxt->setText(QSTRING(stream.dsSettings.path)); // QUIC - ui->quicKeyTxt->setText(QString::fromStdString(stream.quicSettings.key)); - ui->quicSecurityCB->setCurrentText(QString::fromStdString(stream.quicSettings.security)); - ui->quicHeaderTypeCB->setCurrentText(QString::fromStdString(stream.quicSettings.header.type)); + ui->quicKeyTxt->setText(QSTRING(stream.quicSettings.key)); + ui->quicSecurityCB->setCurrentText(QSTRING(stream.quicSettings.security)); + ui->quicHeaderTypeCB->setCurrentText(QSTRING(stream.quicSettings.header.type)); // SOCKOPT - ui->tProxyCB->setCurrentText(QString::fromStdString(stream.sockopt.tproxy)); + ui->tProxyCB->setCurrentText(QSTRING(stream.sockopt.tproxy)); ui->tcpFastOpenCB->setChecked(stream.sockopt.tcpFastOpen); ui->soMarkSpinBox->setValue(stream.sockopt.mark); } else if (OutboundType == "shadowsocks") { ui->outBoundTypeCombo->setCurrentIndex(1); // ShadowSocks Configs - ui->ipLineEdit->setText(QString::fromStdString(shadowsocks.address)); + ui->ipLineEdit->setText(QSTRING(shadowsocks.address)); ui->portLineEdit->setText(QString::number(shadowsocks.port)); - ui->ss_emailTxt->setText(QString::fromStdString(shadowsocks.email)); + ui->ss_emailTxt->setText(QSTRING(shadowsocks.email)); ui->ss_levelSpin->setValue(shadowsocks.level); ui->ss_otaCheckBox->setChecked(shadowsocks.ota); - ui->ss_passwordTxt->setText(QString::fromStdString(shadowsocks.password)); - ui->ss_encryptionMethod->setCurrentText(QString::fromStdString(shadowsocks.method)); + ui->ss_passwordTxt->setText(QSTRING(shadowsocks.password)); + ui->ss_encryptionMethod->setCurrentText(QSTRING(shadowsocks.method)); + } else if (OutboundType == "socks") { + ui->outBoundTypeCombo->setCurrentIndex(2); + ui->ipLineEdit->setText(QSTRING(socks.address)); + ui->portLineEdit->setText(QString::number(socks.port)); + ui->socks_PasswordTxt->setText(QSTRING(socks.users.front().pass)); + ui->socks_UserNameTxt->setText(QSTRING(socks.users.front().user)); } } @@ -152,6 +170,7 @@ void ConnectionEditWindow::on_ipLineEdit_textEdited(const QString &arg1) { vmess.address = arg1.toStdString(); shadowsocks.address = arg1.toStdString(); + socks.address = arg1.toStdString(); GEN_JSON // // No thanks. @@ -171,6 +190,7 @@ void ConnectionEditWindow::on_portLineEdit_textEdited(const QString &arg1) if (arg1 != "") { vmess.port = stoi(arg1.toStdString()); shadowsocks.port = stoi(arg1.toStdString()); + socks.port = stoi(arg1.toStdString()); GEN_JSON } } @@ -318,6 +338,11 @@ QJsonObject ConnectionEditWindow::GenerateConnectionJson() QJsonArray servers; servers.append(GetRootObject(shadowsocks)); settings["servers"] = servers; + } else if (OutboundType == "socks") { + streaming = QJsonObject(); + QJsonArray servers; + servers.append(GetRootObject(socks)); + settings["servers"] = servers; } auto root = GenerateOutboundEntry(OutboundType, settings, streaming, mux, "0.0.0.0", OUTBOUND_TAG_PROXY); @@ -491,3 +516,15 @@ void ConnectionEditWindow::on_ss_otaCheckBox_stateChanged(int arg1) shadowsocks.ota = arg1 == Qt::Checked; GEN_JSON } + +void ConnectionEditWindow::on_socks_UserNameTxt_textEdited(const QString &arg1) +{ + socks.users.front().user = arg1.toStdString(); + GEN_JSON +} + +void ConnectionEditWindow::on_socks_PasswordTxt_textEdited(const QString &arg1) +{ + socks.users.front().pass = arg1.toStdString(); + GEN_JSON +} diff --git a/src/w_ConnectionEditWindow.h b/src/w_ConnectionEditWindow.h index ca16c781..83b5ce3e 100644 --- a/src/w_ConnectionEditWindow.h +++ b/src/w_ConnectionEditWindow.h @@ -108,6 +108,10 @@ class ConnectionEditWindow : public QDialog void on_ss_otaCheckBox_stateChanged(int arg1); + void on_socks_UserNameTxt_textEdited(const QString &arg1); + + void on_socks_PasswordTxt_textEdited(const QString &arg1); + private: int rootJsonCursorPos; QString _alias; @@ -120,7 +124,8 @@ class ConnectionEditWindow : public QDialog QString OutboundType; StreamSettingsObject stream; VMessServerObject vmess; - ShadowSocksServerObject shadowsocks; + ShadowSocksServer shadowsocks; + SocksServerObject socks; // }; #endif // CONFEDIT_H diff --git a/src/w_ConnectionEditWindow.ui b/src/w_ConnectionEditWindow.ui index c5ac730e..9a044916 100644 --- a/src/w_ConnectionEditWindow.ui +++ b/src/w_ConnectionEditWindow.ui @@ -6,24 +6,24 @@ 0 0 - 960 - 750 + 911 + 770 - + 0 0 - 960 - 750 + 0 + 0 - #ConnectionSettings + #EditConnectionSettings true @@ -80,29 +80,15 @@ ShadowSocks + + + Socks + + - - - - #JsonPreview - - - - - - QTextEdit::NoWrap - - - true - - - - - - @@ -111,6 +97,12 @@ + + + 0 + 0 + + 0 @@ -255,7 +247,7 @@ 0 - + @@ -288,6 +280,13 @@ + + + + #Prettify + + + @@ -295,6 +294,13 @@ + + + + #Prettify + + + @@ -318,34 +324,20 @@ - + #InsertDefaultContent - - - - #Prettify - - - - + #InsertDefaultContent - - - - #Prettify - - - @@ -577,63 +569,54 @@ - - - - - - #Header - - - - + + + + #Type + + + + + + + + 0 + 0 + + + - #Type + none - - - - - - - 0 - 0 - + + + + srtp - - - none - - - - - srtp - - - - - utp - - - - - wechat-video - - - - - dtls - - - - - wireguard - - - - - - + + + + utp + + + + + wechat-video + + + + + dtls + + + + + wireguard + + + + + @@ -693,91 +676,65 @@ - + #Key - + - + #Headers - - + + - + 0 - 1 + 0 - - #Header - - - - - - - - #Type - - - - - - - - 0 - 0 - - - - - none - - - - - srtp - - - - - utp - - - - - wechat-video - - - - - dtls - - - - - wireguard - - - - - - - + + + none + + + + + srtp + + + + + utp + + + + + wechat-video + + + + + dtls + + + + + wireguard + + @@ -943,12 +900,61 @@ + + + + + + + + + #Password + + + + + + + + + + #Username + + + + + - + + + + + 0 + 0 + + + + #JsonPreview + + + + + + QTextEdit::NoWrap + + + true + + + + + + + Qt::Horizontal diff --git a/src/w_ImportConfig.cpp b/src/w_ImportConfig.cpp index 914c536a..4e32b3d2 100644 --- a/src/w_ImportConfig.cpp +++ b/src/w_ImportConfig.cpp @@ -79,6 +79,6 @@ void ImportConfigWindow::on_buttonBox_accepted() conf.configs.push_back(alias.toStdString()); SetGlobalConfig(conf); auto needReload = SaveConnectionConfig(config, &alias); - LOG("WARNING: POSSIBLE LOSS OF DATA") + LOG(MODULE_CONNECTION_VMESS, "WARNING: POSSIBLE LOSS OF DATA") emit s_reload_config(needReload); } diff --git a/src/w_MainWindow.cpp b/src/w_MainWindow.cpp index c487338f..eaf9c61d 100644 --- a/src/w_MainWindow.cpp +++ b/src/w_MainWindow.cpp @@ -20,41 +20,54 @@ #include "w_ConnectionEditWindow.h" #include "w_MainWindow.h" +#define TRAY_TOOLTIP_PREFIX "Qv2ray " QV2RAY_VERSION_STRING "\r\n" + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), - HTTPRequestHelper(this), ui(new Ui::MainWindow), + HTTPRequestHelper(this), hTray(new QSystemTrayIcon(this)), vinstance(new Qv2Instance(this)) { ui->setupUi(this); this->setWindowIcon(QIcon(":/icons/Qv2ray.ico")); hTray->setIcon(this->windowIcon()); + hTray->setToolTip(TRAY_TOOLTIP_PREFIX); // - QAction *actionShowHide = new QAction(this->windowIcon(), tr("#Hide"), this); - QAction *actionQuit = new QAction(tr("#Quit"), this); - QAction *actionStart = new QAction(tr("#Start"), this); - QAction *actionRestart = new QAction(tr("#Restart"), this); - QAction *actionStop = new QAction(tr("#Stop"), this); - actionStart->setEnabled(true); - actionStop->setEnabled(false); - actionRestart->setEnabled(false); - trayMenu->addAction(actionShowHide); + QAction *action_Tray_ShowHide = new QAction(this->windowIcon(), tr("#Hide"), this); + QAction *action_Tray_Quit = new QAction(tr("#Quit"), this); + QAction *action_Tray_Start = new QAction(tr("#Connect"), this); + QAction *action_Tray_Restart = new QAction(tr("#Reconnect"), this); + QAction *action_Tray_Stop = new QAction(tr("#Disconnect"), this); + // + QAction *action_RCM_RenameConnection = new QAction(tr("#Rename"), this); + QAction *action_RCM_StartThis = new QAction(tr("#ConnectSelected"), this); + action_Tray_Start->setEnabled(true); + action_Tray_Stop->setEnabled(false); + action_Tray_Restart->setEnabled(false); + trayMenu->addAction(action_Tray_ShowHide); trayMenu->addSeparator(); - trayMenu->addAction(actionStart); - trayMenu->addAction(actionStop); - trayMenu->addAction(actionRestart); + trayMenu->addAction(action_Tray_Start); + trayMenu->addAction(action_Tray_Stop); + trayMenu->addAction(action_Tray_Restart); trayMenu->addSeparator(); - trayMenu->addAction(actionQuit); - connect(actionShowHide, &QAction::triggered, this, &MainWindow::ToggleVisibility); - connect(actionStart, &QAction::triggered, this, &MainWindow::on_startButton_clicked); - connect(actionStop, &QAction::triggered, this, &MainWindow::on_stopButton_clicked); - connect(actionRestart, &QAction::triggered, this, &MainWindow::on_restartButton_clicked); - connect(actionQuit, &QAction::triggered, this, &MainWindow::quit); + trayMenu->addAction(action_Tray_Quit); + connect(action_Tray_ShowHide, &QAction::triggered, this, &MainWindow::ToggleVisibility); + connect(action_Tray_Start, &QAction::triggered, this, &MainWindow::on_startButton_clicked); + connect(action_Tray_Stop, &QAction::triggered, this, &MainWindow::on_stopButton_clicked); + connect(action_Tray_Restart, &QAction::triggered, this, &MainWindow::on_restartButton_clicked); + connect(action_Tray_Quit, &QAction::triggered, this, &MainWindow::quit); connect(hTray, &QSystemTrayIcon::activated, this, &MainWindow::on_activatedTray); connect(ui->logText, &QTextBrowser::textChanged, this, &MainWindow::QTextScrollToBottom); + connect(action_RCM_RenameConnection, &QAction::triggered, this, &MainWindow::on_action_RenameConnection_triggered); + connect(action_RCM_StartThis, &QAction::triggered, this, &MainWindow::on_action_StartThis_triggered); + // hTray->setContextMenu(trayMenu); hTray->show(); + // + listMenu.addAction(action_RCM_StartThis); + listMenu.addAction(action_RCM_RenameConnection); + // LoadConnections(); QObject::connect(&HTTPRequestHelper, &QvHttpRequestHelper::httpRequestFinished, this, &MainWindow::VersionUpdate); HTTPRequestHelper.get("https://api.github.com/repos/lhy0403/Qv2ray/releases/latest"); @@ -67,8 +80,8 @@ MainWindow::MainWindow(QWidget *parent) auto conf = GetGlobalConfig(); if (conf.autoStartConfig != "" && QList::fromStdList(conf.configs).contains(conf.autoStartConfig)) { - CurrentConnectionName = QString::fromStdString(conf.autoStartConfig); - auto item = ui->connectionListWidget->findItems(QString::fromStdString(conf.autoStartConfig), Qt::MatchExactly).front(); + CurrentConnectionName = QSTRING(conf.autoStartConfig); + auto item = ui->connectionListWidget->findItems(QSTRING(conf.autoStartConfig), Qt::MatchExactly).front(); item->setSelected(true); ui->connectionListWidget->setCurrentItem(item); on_connectionListWidget_itemClicked(item); @@ -81,6 +94,12 @@ MainWindow::MainWindow(QWidget *parent) } } +void MainWindow::on_action_StartThis_triggered() +{ + CurrentConnectionName = ui->connectionListWidget->currentItem()->text(); + on_restartButton_clicked(); +} + void MainWindow::VersionUpdate(QByteArray &data) { auto conf = GetGlobalConfig(); @@ -88,14 +107,14 @@ void MainWindow::VersionUpdate(QByteArray &data) QJsonObject root = JSONFromString(jsonString); // QVersionNumber newversion = QVersionNumber::fromString(root["tag_name"].toString("").remove(0, 1)); - QVersionNumber current = QVersionNumber::fromString(QString::fromStdString(QV2RAY_VERSION_STRING).remove(0, 1)); - QVersionNumber ignored = QVersionNumber::fromString(QString::fromStdString(conf.ignoredVersion)); - LOG("Received update info, Latest: " + newversion.toString().toStdString() + " Current: " + current.toString().toStdString() + " Ignored: " + ignored.toString().toStdString()) + QVersionNumber current = QVersionNumber::fromString(QSTRING(QV2RAY_VERSION_STRING).remove(0, 1)); + QVersionNumber ignored = QVersionNumber::fromString(QSTRING(conf.ignoredVersion)); + LOG(MODULE_UPDATE, "Received update info, Latest: " + newversion.toString().toStdString() + " Current: " + current.toString().toStdString() + " Ignored: " + ignored.toString().toStdString()) // If the version is newer than us. // And new version is newer than the ignored version. if (newversion > current && newversion > ignored) { - LOG("New version detected.") + LOG(MODULE_UPDATE, "New version detected.") auto link = root["html_url"].toString(""); auto result = QvMessageBoxAsk(this, tr("#NewReleaseVersionFound"), tr("#NewReleaseVersionFound") + ": " + root["tag_name"].toString("") + @@ -126,11 +145,15 @@ void MainWindow::LoadConnections() for (int i = 0; i < connections.count(); i++) { ui->connectionListWidget->addItem(connections.keys()[i]); } + + ui->connectionListWidget->sortItems(); } void MainWindow::save_reload_globalconfig(bool need_restart) { + auto statusText = ui->statusLabel->text(); ui->retranslateUi(this); + ui->statusLabel->setText(statusText); bool isRunning = vinstance->Status == STARTED; SaveGlobalConfig(); @@ -140,6 +163,7 @@ void MainWindow::save_reload_globalconfig(bool need_restart) if (isRunning && need_restart) on_startButton_clicked(); } + MainWindow::~MainWindow() { hTray->hide(); @@ -160,15 +184,16 @@ void MainWindow::on_startButton_clicked() return; } - LOG(("Now start a connection: " + CurrentConnectionName).toStdString()) + LOG(MODULE_VCORE, ("Connecting to: " + CurrentConnectionName).toStdString()) ui->logText->clear(); auto full_conf = GenerateRuntimeConfig(connections.value(CurrentConnectionName)); StartPreparation(full_conf); bool startFlag = this->vinstance->Start(); if (startFlag) { - this->hTray->showMessage(tr("Qv2ray"), tr("#ConnectedToServer ") + CurrentConnectionName); - ui->statusLabel->setText(tr("#Started") + ": " + CurrentConnectionName); + this->hTray->showMessage("Qv2ray", tr("#ConnectedToServer") + " " + CurrentConnectionName, hTray->icon()); + hTray->setToolTip(TRAY_TOOLTIP_PREFIX + tr("#ConnectedToServer") + ": " + CurrentConnectionName); + ui->statusLabel->setText(tr("#Connected") + ": " + CurrentConnectionName); } trayMenu->actions()[2]->setEnabled(!startFlag); @@ -177,23 +202,24 @@ void MainWindow::on_startButton_clicked() // ui->startButton->setEnabled(!startFlag); ui->stopButton->setEnabled(startFlag); - ui->restartButton->setEnabled(startFlag); } void MainWindow::on_stopButton_clicked() { - LOG("Stop connection!") - this->vinstance->Stop(); - QFile(QV2RAY_GENERATED_CONFIG_FILE_PATH).remove(); - ui->statusLabel->setText(tr("#Stopped")); - ui->logText->clear(); - trayMenu->actions()[2]->setEnabled(true); - trayMenu->actions()[3]->setEnabled(false); - trayMenu->actions()[4]->setEnabled(false); - // - ui->startButton->setEnabled(true); - ui->stopButton->setEnabled(false); - ui->restartButton->setEnabled(false); + if (vinstance->Status != STOPPED) { + LOG(MODULE_VCORE, "Disconnected: " + CurrentConnectionName.toStdString()) + this->vinstance->Stop(); + hTray->setToolTip(TRAY_TOOLTIP_PREFIX); + QFile(QV2RAY_GENERATED_CONFIG_FILE_PATH).remove(); + ui->statusLabel->setText(tr("#Disconnected")); + ui->logText->clear(); + trayMenu->actions()[2]->setEnabled(true); + trayMenu->actions()[3]->setEnabled(false); + trayMenu->actions()[4]->setEnabled(false); + // + ui->startButton->setEnabled(true); + ui->stopButton->setEnabled(false); + } } void MainWindow::on_restartButton_clicked() @@ -286,23 +312,30 @@ void MainWindow::ShowAndSetConnection(int index, bool SetConnection, bool ApplyC if (outboundType == "vmess") { auto Server = StructFromJSONString(JSONToString(outBoundRoot["settings"].toObject()["vnext"].toArray().first().toObject())); - ui->_hostLabel->setText(QString::fromStdString(Server.address)); - ui->_portLabel->setText(QString::fromStdString(to_string(Server.port))); + ui->_hostLabel->setText(QSTRING(Server.address)); + ui->_portLabel->setText(QSTRING(to_string(Server.port))); auto user = QList::fromStdList(Server.users).first(); - auto _configString = tr("#UUID") + ": " + QString::fromStdString(user.id) + auto _configString = tr("#UUID") + ": " + QSTRING(user.id) + "\r\n" - + tr("#AlterID") + ": " + QString::fromStdString(to_string(user.alterId)) + + tr("#AlterID") + ": " + QSTRING(to_string(user.alterId)) + "\r\n" + tr("#Transport") + ": " + outBoundRoot["streamSettings"].toObject()["network"].toString(); ui->detailInfoTxt->setPlainText(_configString); } else if (outboundType == "shadowsocks") { auto x = JSONToString(outBoundRoot["settings"].toObject()["servers"].toArray().first().toObject()); - auto Server = StructFromJSONString(x); - ui->_hostLabel->setText(QString::fromStdString(Server.address)); - ui->_portLabel->setText(QString::fromStdString(to_string(Server.port))); - auto _configString = tr("#Email") + ": " + QString::fromStdString(Server.email) + auto Server = StructFromJSONString(x); + ui->_hostLabel->setText(QSTRING(Server.address)); + ui->_portLabel->setText(QSTRING(to_string(Server.port))); + auto _configString = tr("#Email") + ": " + QSTRING(Server.email) + "\r\n" - + tr("#Encryption") + ": " + QString::fromStdString(Server.method); + + tr("#Encryption") + ": " + QSTRING(Server.method); + ui->detailInfoTxt->setPlainText(_configString); + } else if (outboundType == "socks") { + auto x = JSONToString(outBoundRoot["settings"].toObject()["servers"].toArray().first().toObject()); + auto Server = StructFromJSONString(x); + ui->_hostLabel->setText(QSTRING(Server.address)); + ui->_portLabel->setText(QSTRING(to_string(Server.port))); + auto _configString = tr("#Username") + ": " + QSTRING(Server.users.front().user); ui->detailInfoTxt->setPlainText(_configString); } @@ -323,41 +356,7 @@ void MainWindow::on_connectionListWidget_itemClicked(QListWidgetItem *item) { Q_UNUSED(item) int currentRow = ui->connectionListWidget->currentRow(); - ShowAndSetConnection(currentRow, vinstance->Status != STARTED, false); -} - -void MainWindow::on_importConfigBtn_clicked() -{ - ImportConfigWindow *w = new ImportConfigWindow(this); - connect(w, &ImportConfigWindow::s_reload_config, this, &MainWindow::save_reload_globalconfig); - w->show(); -} - -void MainWindow::on_addConfigBtn_clicked() -{ - ConnectionEditWindow *w = new ConnectionEditWindow(this); - connect(w, &ConnectionEditWindow::s_reload_config, this, &MainWindow::save_reload_globalconfig); - w->show(); -} - -void MainWindow::on_delConfigBtn_clicked() -{ - auto conf = GetGlobalConfig(); - QList list = QList::fromStdList(conf.configs); - auto currentSelected = ui->connectionListWidget->currentIndex().row(); - - if (currentSelected < 0) return; - - bool isRemovingItemRunning = ui->connectionListWidget->item(currentSelected)->text() == CurrentConnectionName; - - if (isRemovingItemRunning) { - CurrentConnectionName = ""; - } - - list.removeOne(ui->connectionListWidget->item(currentSelected)->text().toStdString()); - conf.configs = list.toStdList(); - SetGlobalConfig(conf); - save_reload_globalconfig(isRemovingItemRunning); + ShowAndSetConnection(currentRow, !isRenamingInProgress && (vinstance->Status != STARTED), false); } void MainWindow::on_prefrencesBtn_clicked() @@ -372,7 +371,116 @@ void MainWindow::on_connectionListWidget_doubleClicked(const QModelIndex &index) ShowAndSetConnection(index.row(), true, true); } -void MainWindow::on_editConnectionSettingsBtn_clicked() +void MainWindow::on_clearlogButton_clicked() +{ + ui->logText->clear(); +} + +void MainWindow::on_connectionListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) +{ + Q_UNUSED(previous) + isRenamingInProgress = true; + on_connectionListWidget_itemClicked(current); +} + +void MainWindow::on_connectionListWidget_customContextMenuRequested(const QPoint &pos) +{ + Q_UNUSED(pos) + listMenu.popup(QCursor::pos()); +} + +void MainWindow::on_action_RenameConnection_triggered() +{ + auto item = ui->connectionListWidget->currentItem(); + item->setFlags(item->flags() | Qt::ItemIsEditable); + ui->connectionListWidget->editItem(item); + originalName = item->text(); + isRenamingInProgress = false; +} + +void MainWindow::on_connectionListWidget_itemChanged(QListWidgetItem *item) +{ + LOG(MODULE_UI, "A connection ListViewItem is changed.") + + if (!isRenamingInProgress) { + // In this case it's after we entered the name. + LOG(MODULE_CONNECTION, "RENAME: " + originalName.toStdString() + " -> " + item->text().toStdString()) + auto newName = item->text(); + auto config = GetGlobalConfig(); + auto configList = QList::fromStdList(config.configs); + + if (newName.trimmed().isEmpty()) { + QvMessageBox(this, tr("#RenameConnection"), tr("#CannotUseEmptyName")); + return; + } + + // If I really did some changes. + LOG("RENAME", "ORIGINAL: " + originalName.toStdString() + ", NEW: " + newName.toStdString()) + + if (originalName != newName) { + if (configList.contains(newName.toStdString())) { + QvMessageBox(this, tr("#RenameConnection"), tr("#DuplicatedConnectionName")); + return; + } + + // + // Change auto start config. + if (originalName.toStdString() == config.autoStartConfig) config.autoStartConfig = newName.toStdString(); + + configList[configList.indexOf(originalName.toStdString())] = newName.toStdString(); + config.configs = configList.toStdList(); + // + RenameConnection(originalName, newName); + // + SetGlobalConfig(config); + bool running = CurrentConnectionName == originalName; + + if (running) CurrentConnectionName = newName; + + save_reload_globalconfig(running); + auto newItem = ui->connectionListWidget->findItems(newName, Qt::MatchExactly).front(); + ui->connectionListWidget->setCurrentItem(newItem); + } + } +} + +void MainWindow::on_removeConfigButton_clicked() +{ + if (QvMessageBoxAsk(this, tr("#RemoveConnection"), tr("#RemoveConnectionConfirm")) == QMessageBox::Yes) { + auto conf = GetGlobalConfig(); + QList list = QList::fromStdList(conf.configs); + auto currentSelected = ui->connectionListWidget->currentIndex().row(); + + if (currentSelected < 0) return; + + bool isRemovingItemRunning = ui->connectionListWidget->item(currentSelected)->text() == CurrentConnectionName; + + if (isRemovingItemRunning) { + CurrentConnectionName = ""; + } + + list.removeOne(ui->connectionListWidget->item(currentSelected)->text().toStdString()); + conf.configs = list.toStdList(); + SetGlobalConfig(conf); + save_reload_globalconfig(isRemovingItemRunning); + } +} + +void MainWindow::on_importConfigButton_clicked() +{ + ImportConfigWindow *w = new ImportConfigWindow(this); + connect(w, &ImportConfigWindow::s_reload_config, this, &MainWindow::save_reload_globalconfig); + w->show(); +} + +void MainWindow::on_addConfigButton_clicked() +{ + ConnectionEditWindow *w = new ConnectionEditWindow(this); + connect(w, &ConnectionEditWindow::s_reload_config, this, &MainWindow::save_reload_globalconfig); + w->show(); +} + +void MainWindow::on_editConfigButton_clicked() { // Check if we have a connection selected... auto index = ui->connectionListWidget->currentIndex().row(); @@ -386,8 +494,3 @@ void MainWindow::on_editConnectionSettingsBtn_clicked() connect(w, &ConnectionEditWindow::s_reload_config, this, &MainWindow::save_reload_globalconfig); w->show(); } - -void MainWindow::on_clearlogButton_clicked() -{ - ui->logText->clear(); -} diff --git a/src/w_MainWindow.h b/src/w_MainWindow.h index 97436c09..6d2e310d 100644 --- a/src/w_MainWindow.h +++ b/src/w_MainWindow.h @@ -25,8 +25,8 @@ class MainWindow : public QMainWindow public slots: void save_reload_globalconfig(bool need_restart); void UpdateLog(); - void VersionUpdate(QByteArray &data); private slots: + void VersionUpdate(QByteArray &data); void on_restartButton_clicked(); void on_startButton_clicked(); void on_stopButton_clicked(); @@ -38,32 +38,45 @@ class MainWindow : public QMainWindow void on_connectionListWidget_itemClicked(QListWidgetItem *item); - void on_importConfigBtn_clicked(); - - void on_addConfigBtn_clicked(); - - void on_delConfigBtn_clicked(); - void on_prefrencesBtn_clicked(); void on_connectionListWidget_doubleClicked(const QModelIndex &index); - void on_editConnectionSettingsBtn_clicked(); - void on_clearlogButton_clicked(); + void on_connectionListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); + + void on_connectionListWidget_customContextMenuRequested(const QPoint &pos); + + void on_action_RenameConnection_triggered(); + void on_action_StartThis_triggered(); + + void on_connectionListWidget_itemChanged(QListWidgetItem *item); + + void on_removeConfigButton_clicked(); + + void on_importConfigButton_clicked(); + + void on_addConfigButton_clicked(); + + void on_editConfigButton_clicked(); + private: - QvHttpRequestHelper HTTPRequestHelper; - void ShowAndSetConnection(int index, bool SetConnection, bool Apply); - void LoadConnections(); - QString CurrentConnectionName; Ui::MainWindow *ui; + QvHttpRequestHelper HTTPRequestHelper; QSystemTrayIcon *hTray; QMenu *trayMenu = new QMenu(this); Qv2Instance *vinstance; - // - void closeEvent(QCloseEvent *); + QMenu listMenu; QMap connections; + QString CurrentConnectionName; + // + QString originalName; + bool isRenamingInProgress; + // + void ShowAndSetConnection(int index, bool SetConnection, bool Apply); + void LoadConnections(); + void closeEvent(QCloseEvent *); }; #endif // MAINWINDOW_H diff --git a/src/w_MainWindow.ui b/src/w_MainWindow.ui index 907952fd..ebfb0baf 100644 --- a/src/w_MainWindow.ui +++ b/src/w_MainWindow.ui @@ -9,8 +9,8 @@ 0 0 - 700 - 550 + 680 + 553 @@ -19,12 +19,6 @@ 0 - - - 700 - 550 - - Qv2ray @@ -41,16 +35,16 @@ 530 - + - + 5 - #Start + #Connect @@ -60,20 +54,7 @@ false - #Stop - - - - - - - false - - - #Restart - - - false + #Disconnect @@ -101,8 +82,8 @@ - 999999 - 20 + 999999999 + 0 @@ -123,97 +104,26 @@ - - - - QFrame::StyledPanel - - - QFrame::Sunken - - - 1 - - - 0 - - - Qt::ScrollBarAsNeeded - - - Qt::ScrollBarAsNeeded - - - Qt::CopyAction - - - QAbstractItemView::ScrollPerPixel - - - QAbstractItemView::ScrollPerPixel - - - QListView::ListMode - - - - - - - - - #ImportConfig - - - - - - - #AddConfig - - - - - - - #RemoveConfig - - - - - - - - - Qt::ScrollBarAlwaysOn - - - QTextEdit::NoWrap - - - false - - - - - - - #Log - - - - #ConfigDetail + #CurrentConfigDetail - - QFormLayout::ExpandingFieldsGrow - - - 10 - + + + + #OutBoundType + + + + + + + + + + @@ -242,20 +152,6 @@ - - - - #OutBoundType - - - - - - - - - - @@ -265,49 +161,178 @@ - - - 0 - 0 - - - - - 0 - 0 - - QTextEdit::NoWrap - - - - #EditConnection - - - - - - - - 0 - 0 - - - - #ConnectionSettings - - - false - - - + + + + Qt::CustomContextMenu + + + QFrame::StyledPanel + + + QFrame::Sunken + + + 1 + + + 0 + + + Qt::ScrollBarAsNeeded + + + Qt::ScrollBarAsNeeded + + + QAbstractItemView::NoEditTriggers + + + Qt::CopyAction + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + QListView::ListMode + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + #AddConnection + + + A + + + + :/icons/add_connection_btn.png:/icons/add_connection_btn.png + + + + + + + + 0 + 0 + + + + #ImportConnection + + + I + + + + :/icons/import_connection_btn.png:/icons/import_connection_btn.png + + + + + + + + 0 + 0 + + + + #RemoveConnection + + + R + + + + :/icons/remove_connection_btn.png:/icons/remove_connection_btn.png + + + + + + + #EditConnection + + + ... + + + + :/icons/edit_connection_btn.png:/icons/edit_connection_btn.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + #Log + + + + + + + Qt::ScrollBarAlwaysOn + + + QTextEdit::NoWrap + + + false + + + @@ -351,9 +376,10 @@ startButton stopButton - restartButton clearlogButton - + + + diff --git a/src/w_PrefrencesWindow.cpp b/src/w_PrefrencesWindow.cpp index f96c07a7..ba20b69c 100644 --- a/src/w_PrefrencesWindow.cpp +++ b/src/w_PrefrencesWindow.cpp @@ -182,7 +182,7 @@ void PrefrencesWindow::on_languageComboBox_currentTextChanged(const QString &arg CurrentConfig.language = arg1.toStdString(); if (QApplication::installTranslator(getTranslator(QString::fromStdString(arg1.toStdString())))) { - LOG("Loaded translations " + arg1.toStdString()) + LOG(MODULE_UI, "Loaded translations " + arg1.toStdString()) ui->retranslateUi(this); } else { //QvMessageBox(this, tr("#Prefrences"), tr("#SwitchTranslationError")); diff --git a/src/w_PrefrencesWindow.h b/src/w_PrefrencesWindow.h index cb7bb057..6d0873e4 100644 --- a/src/w_PrefrencesWindow.h +++ b/src/w_PrefrencesWindow.h @@ -3,7 +3,7 @@ #include #include -#include "QvGUIConfigObjects.h" +#include "Qv2rayBase.h" namespace Ui { diff --git a/translations/en-US.ts b/translations/en-US.ts index 20d70b69..2826af16 100644 --- a/translations/en-US.ts +++ b/translations/en-US.ts @@ -4,13 +4,12 @@ ConnectionEditWindow - #ConnectionSettings - Connection Settings + Connection Settings - + #Host Host @@ -25,58 +24,64 @@ Outbound Type - + + Socks + Socks + + + #OutBoundSettings OutBound Settings - + #UUID UUID - + #AlterID Alter ID - - + + #Security Security Settings - + #Transport Transport Settings - + #TransportSettings Transport Settings - + #Email Email - + + #Password Password - + #EncryptionMethod Encryption Method - + #Level User Level - + #OTA One Time Auth (Outdated) @@ -85,30 +90,29 @@ TCP - - - + + #Type Type - + #Request Request - - + + #InsertDefaultContent Insert Default Content - + #Response Response - + #UseTLS Use TLS @@ -117,8 +121,8 @@ Generate JSON - - + + #Prettify Prettify JSON @@ -127,9 +131,9 @@ HTTP - - - + + + #Path Path @@ -138,13 +142,13 @@ WebSocket - - + + #Headers Headers - + #key|value format Format: KEY|VALUE @@ -153,22 +157,22 @@ mKCP - + #MTU MTU - + #TTI (ms) TTI (ms) - + #UplinkCapacity (MB/s) Uplink Capacity (MB/s) - + #Congestion Congestion Control @@ -177,53 +181,61 @@ SockOpt - + #Mark Mark - + #TCPFastOpen TCP Fast Open - + #TProxy Transparent Proxy Mode - - - - + + + + #Enabled Enabled - + + #EditConnectionSettings + Edit Connection Settings + + + + #Username + Username + + + #JsonPreview Preview Generated JSON - + #DownlinkCapacity (MB/s) Downlink Capacity (MB/s) - + #ReadBufferSize (MB) Read Buffer Size (MB) - + #WriteBufferSize (MB) Write Buffer Size (MB) - - #Header - Headers + Headers DomainSocket @@ -234,19 +246,19 @@ QUIC - + #Key Key - - + + #JsonPrettify Prettify JSON - - + + #JsonContainsError JSON is in a bad syntax @@ -358,34 +370,75 @@ MainWindow - - + Qv2ray - - - + + + #Connect + Connect + + + + + #Disconnect + Disconnect + + + + #CurrentConfigDetail + Current Connection Details + + + + #AddConnection + Add a connection + + + + A + + + + + I + + + + + + #RemoveConnection + Remove a connection + + + + R + + + + + ... + + + + #Start Start - - - + #Stop Stop - - - + #Restart Restart - + #ClearLog Clear Log @@ -394,165 +447,215 @@ Status - - + #Stopped Stopped - + #HostList Host List - + #DetailInfo Detail Connection Info - + #EditConnection Edit Connection - + #OutBoundType Outbound Type - #ImportConfig - Import Config + Import Config - #AddConfig - Add Config + Add Config - #RemoveConfig - Remove Config + Remove Config - #ConfigDetail - Detailed Config Info + Detailed Config Info - + #Host Host - + #Port Port - + + #Rename + Rename + + + + #Connected + Connected + + + + #Disconnected + Disconnected + + + #UUID UUID - + #AlterID Alter ID - + #Transport Transport Settings - #ConnectionSettings - Connection Settings + Connection Settings - + #Log Log - + #ManuallyCreateConnection Manually Create Connection - + + #ImportConnection Import Config File - + #Exit Exit - - + + #Preferences Preferences - - + + #Hide Hide - + #Quit Quit - - + + #Reconnect + Reconnect + + + + #ConnectSelected + Connect selected item + + + + #NewReleaseVersionFound Found a newer version - + #ReleaseDownloadLink New version download link - - + + #NoConfigSelected No Config Selected - - + + #PleaseSelectAConfig Please select a config from the list - + + + #ConnectedToServer + Connected to server + + + + + #RenameConnection + Rename a connection + + + + #CannotUseEmptyName + You cannot use an empty connection name + + + + #DuplicatedConnectionName + Connection name is duplicated + + + + #RemoveConnectionConfirm + Are you sure want to remove this connection + + #ConnectedToServer - Connected to server + Connected to server - #Started - Started + Started - + #Email Email - + #Encryption Encryption - - - + + #Username + Username + + + + + #Show Show @@ -830,12 +933,7 @@ Another instance is already running - - Qv2ray - - - - + #AnotherInstanceRunning Another instance is already running diff --git a/translations/zh-CN.ts b/translations/zh-CN.ts index 02c8aecb..a93b2aca 100644 --- a/translations/zh-CN.ts +++ b/translations/zh-CN.ts @@ -4,13 +4,12 @@ ConnectionEditWindow - #ConnectionSettings - 连接设置 + 连接设置 - + #Host 域名 @@ -25,58 +24,64 @@ 出站类型 - + + Socks + Socks + + + #OutBoundSettings 出站设置 - + #UUID UUID - + #AlterID Alter ID - - + + #Security 安全设置 - + #Transport 传输设置 - + #TransportSettings 传输设置 - + #Email 邮箱 - + + #Password 密码 - + #EncryptionMethod 加密方法 - + #Level 用户等级 - + #OTA 一次性验证 (过时的设置) @@ -85,30 +90,29 @@ TCP - - - + + #Type 类型 - + #Request 请求 - - + + #InsertDefaultContent 默认值 - + #Response 相应 - + #UseTLS 使用 TLS @@ -117,8 +121,8 @@ 生成 JSON - - + + #Prettify 格式化 JSON @@ -127,9 +131,9 @@ HTTP - - - + + + #Path 路径 @@ -138,13 +142,13 @@ WebSocket - - + + #Headers 头伪装 - + #key|value format 格式:键|值 @@ -153,22 +157,22 @@ mKCP - + #MTU 最大传输单元 - + #TTI (ms) 传输时间间隔 - + #UplinkCapacity (MB/s) 上行链路容量 (MB/s) - + #Congestion 拥塞控制 @@ -177,53 +181,61 @@ SockOpt - + #Mark Mark - + #TCPFastOpen TCP Fast Open - + #TProxy 透明代理模式 - - - - + + + + #Enabled 启用 - + + #EditConnectionSettings + 编辑连接设置 + + + + #Username + 用户名 + + + #JsonPreview 生成JSON预览 - + #DownlinkCapacity (MB/s) 下行链路容量 (MB/s) - + #ReadBufferSize (MB) 读取缓冲区大小 (MB) - + #WriteBufferSize (MB) 写入缓冲区大小 (MB) - - #Header - 头伪装 + 头伪装 DomainSocket @@ -234,19 +246,19 @@ QUIC - + #Key 密钥 - - + + #JsonPrettify 美化 JSON - - + + #JsonContainsError JSON 格式错误 @@ -358,34 +370,75 @@ MainWindow - - + Qv2ray - - - + + + #Connect + 连接 + + + + + #Disconnect + 断开连接 + + + + #CurrentConfigDetail + 当前连接详细信息 + + + + #AddConnection + 添加连接 + + + + A + + + + + I + + + + + + #RemoveConnection + 删除连接 + + + + R + + + + + ... + + + + #Start 启动 - - - + #Stop 停止 - - - + #Restart 重新启动 - + #ClearLog 清除日志 @@ -394,165 +447,215 @@ 状态 - - + #Stopped 已停止 - + #HostList 服务器列表 - + #DetailInfo 配置详细信息 - + #EditConnection 编辑连接 - + #OutBoundType 出站类型 - #ImportConfig - 导入配置 + 导入配置 - #AddConfig - 添加配置 + 添加配置 - #RemoveConfig - 移除配置 + 移除配置 - #ConfigDetail - 配置详细信息 + 配置详细信息 - + #Host 域名 - + #Port 端口 - + + #Rename + 重命名 + + + + #Connected + 已连接 + + + + #Disconnected + 已断开连接 + + + #UUID UUID - + #AlterID Alter ID - + #Transport 传输设置 - #ConnectionSettings - 连接设置 + 连接设置 - + #Log 日志 - + #ManuallyCreateConnection 手动添加配置 - + + #ImportConnection 导入配置文件 - + #Exit 退出 - - + + #Preferences 首选项 - - + + #Hide 隐藏 - + #Quit 退出 - - + + #Reconnect + 重新连接 + + + + #ConnectSelected + 连接当前选择的配置 + + + + #NewReleaseVersionFound 发现新版本 - + #ReleaseDownloadLink 新版本下载链接 - - + + #NoConfigSelected 没有选择配置文件 - - + + #PleaseSelectAConfig 请从列表中选择配置 - + + + #ConnectedToServer + 已连接到服务器 + + + + + #RenameConnection + 重命名连接 + + + + #CannotUseEmptyName + 不能使用空连接名 + + + + #DuplicatedConnectionName + 连接重名 + + + + #RemoveConnectionConfirm + 确定要删除连接吗 + + #ConnectedToServer - 已连接到服务器 + 已连接到服务器 - #Started - 已启动 + 已启动 - + #Email 邮箱 - + #Encryption 加密方法 - - - + + #Username + 用户名 + + + + + #Show 显示 @@ -830,12 +933,7 @@ 另一个实例正在运行 - - Qv2ray - - - - + #AnotherInstanceRunning 另一个实例正在运行