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
另一个实例正在运行