diff --git a/Build.Counter b/Build.Counter index c9ac2c6b..922b8183 100644 --- a/Build.Counter +++ b/Build.Counter @@ -1 +1 @@ -1907 +1922 diff --git a/Qv2ray.pro b/Qv2ray.pro index 23956f6a..8dc846bd 100644 --- a/Qv2ray.pro +++ b/Qv2ray.pro @@ -42,6 +42,7 @@ SOURCES += \ src/QvCoreConfigOperations_Generation.cpp \ src/QvUtils.cpp \ src/ui/w_PreferencesWindow.cpp \ + src/utils/QvHelpers.cpp \ src/utils/QJsonModel.cpp \ src/ui/w_ExportConfig.cpp \ src/ui/w_InboundEditor.cpp \ @@ -87,6 +88,7 @@ HEADERS += \ src/ui/w_RoutesEditor.hpp \ src/ui/w_SubscriptionEditor.hpp \ src/ui/w_ScreenShot_Core.hpp \ + src/utils/QvHelpers.hpp \ src/utils/QvTinyLog.hpp \ src/utils/QJsonModel.hpp \ src/utils/QJsonObjectInsertMacros.h \ diff --git a/src/QvConfigUpgrade.cpp b/src/QvConfigUpgrade.cpp index 01fd69b2..d6a5a86d 100644 --- a/src/QvConfigUpgrade.cpp +++ b/src/QvConfigUpgrade.cpp @@ -5,7 +5,7 @@ #include "QvUtils.hpp" -#define UPDATELOG(msg) LOG(MODULE_CONFIG, " [" + to_string(fromVersion) + "-" + to_string(fromVersion + 1) + "] --> " msg) +#define UPDATELOG(msg) LOG(MODULE_CONFIG, " [" + to_string(fromVersion) + "-" + to_string(fromVersion + 1) + "] --> " + msg) namespace Qv2ray { diff --git a/src/QvUtils.cpp b/src/QvUtils.cpp index 09c5b19c..75f6a19d 100644 --- a/src/QvUtils.cpp +++ b/src/QvUtils.cpp @@ -1,25 +1,4 @@ #include "QvUtils.hpp" -#include - -// Forwarded from QvTinyLog -static QQueue __loggerBuffer; -void _LOG(const std::string &module, const std::string &log) -{ - string logString = "[" + module + "]: " + log; - cout << logString << endl; - __loggerBuffer.enqueue((logString + NEWLINE).c_str()); -} - -const QString readLastLog() -{ - QString result; - - while (!__loggerBuffer.isEmpty()) { - result += __loggerBuffer.dequeue(); - } - - return result; -} namespace Qv2ray { @@ -35,21 +14,6 @@ namespace Qv2ray StringToFile(&str, &config); } - const QString GenerateRandomString(int len) - { - const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - QString randomString; - - for (int i = 0; i < len; ++i) { - uint rand = QRandomGenerator::system()->generate(); - uint max = static_cast(possibleCharacters.length()); - QChar nextChar = possibleCharacters[rand % max]; - randomString.append(nextChar); - } - - return randomString; - } - Qv2rayConfig GetGlobalConfig() { return GlobalConfig; @@ -69,124 +33,6 @@ namespace Qv2ray } } - QString Stringify(list list, QString saperator) - { - QString out; - - for (auto item : list) { - out.append(QSTRING(item)); - out.append(saperator); - } - - if (out.length() >= 1) - out = out.remove(out.length() - 1, 1); - - return out; - } - - QString Stringify(QList list, QString saperator) - { - QString out; - - for (auto item : list) { - out.append(item); - out.append(saperator); - } - - if (out.length() >= 1) - out = out.remove(out.length() - 1, 1); - - return out; - } - - QString StringFromFile(QFile *source) - { - source->open(QFile::ReadOnly); - QTextStream stream(source); - QString str = stream.readAll(); - source->close(); - return str; - } - - bool StringToFile(const QString *text, QFile *targetFile) - { - bool override = targetFile->exists(); - targetFile->open(QFile::WriteOnly); - QTextStream stream(targetFile); - stream << *text << endl; - stream.flush(); - targetFile->close(); - return override; - } - - QJsonObject JSONFromFile(QFile *sourceFile) - { - QString json = StringFromFile(sourceFile); - return JsonFromString(json); - } - - QString JsonToString(QJsonObject json, QJsonDocument::JsonFormat format) - { - QJsonDocument doc; - doc.setObject(json); - return doc.toJson(format); - } - - QString JsonToString(QJsonArray array, QJsonDocument::JsonFormat format) - { - QJsonDocument doc; - doc.setArray(array); - return doc.toJson(format); - } - - QString VerifyJsonString(const QString &source) - { - QJsonParseError error; - QJsonDocument doc = QJsonDocument::fromJson(source.toUtf8(), &error); - Q_UNUSED(doc) - - if (error.error == QJsonParseError::NoError) { - return ""; - } else { - LOG(MODULE_UI, "WARNING: Json parse returns: " + error.errorString().toStdString()) - return error.errorString(); - } - } - - QJsonObject JsonFromString(QString string) - { - QJsonDocument doc = QJsonDocument::fromJson(string.toUtf8()); - return doc.object(); - } - - QString Base64Encode(QString string) - { - QByteArray ba = string.toUtf8(); - return ba.toBase64(); - } - - QString Base64Decode(QString string) - { - QByteArray ba = string.toUtf8(); - return QString(QByteArray::fromBase64(ba)); - } - - QStringList SplitLines(const QString &_string) - { - return _string.split(QRegExp("[\r\n]"), QString::SkipEmptyParts); - } - - list SplitLines_std(const QString &_string) - { - list list; - - for (auto line : _string.split(QRegExp("[\r\n]"), QString::SkipEmptyParts)) { - list.push_back(line.toStdString()); - } - - return list; - } - void LoadGlobalConfig() { QFile file(QV2RAY_CONFIG_FILE); @@ -197,91 +43,5 @@ namespace Qv2ray SetGlobalConfig(config); file.close(); } - - QStringList GetFileList(QDir dir) - { - return dir.entryList(QStringList() << "*" << "*.*", QDir::Hidden | QDir::Files); - } - - bool CheckFile(QDir dir, QString fileName) - { - return GetFileList(dir).indexOf(fileName) >= 0; - } - - void QvMessageBox(QWidget *parent, QString title, QString text) - { - QMessageBox::warning(parent, title, text, QMessageBox::Ok | QMessageBox::Default, 0); - } - - int QvMessageBoxAsk(QWidget *parent, QString title, QString text, QMessageBox::StandardButton extraButtons) - { - return QMessageBox::information(parent, title, text, QMessageBox::Yes | QMessageBox::No | extraButtons); - } - - QString FormatBytes(long long bytes) - { - char str[64]; - const char *sizes[5] = { "B", "KB", "MB", "GB", "TB" }; - int i; - double dblByte = bytes; - - for (i = 0; i < 5 && bytes >= 1024; i++, bytes /= 1024) - dblByte = bytes / 1024.0; - - sprintf(str, "%.2f", dblByte); - return strcat(strcat(str, " "), sizes[i]); - } - - - QTranslator *getTranslator(const QString &lang) - { - QTranslator *translator = new QTranslator(); - translator->load(lang + ".qm", ":/translations/"); - return translator; - } - - /// This returns a file name without extensions. - void DeducePossibleFileName(const QString &baseDir, QString *fileName, const QString &extension) - { - int i = 1; - - if (!QDir(baseDir).exists()) { - QDir(baseDir).mkpath(baseDir); - LOG(MODULE_FILE, "Making path: " + baseDir.toStdString()) - } - - while (true) { - if (!QFile(baseDir + "/" + fileName + "_" + QString::number(i) + extension).exists()) { - *fileName = *fileName + "_" + QString::number(i); - return; - } else { - //LOG(MODULE_FILE, "File with name: " << (fileName + "_" + QString::number(i) + extension).toStdString() << " already exists") - } - - i++; - } - } - - void QFastAppendTextDocument(const QString &message, QTextDocument *doc) - { - QTextCursor cursor(doc); - cursor.movePosition(QTextCursor::End); - cursor.beginEditBlock(); - cursor.insertBlock(); - cursor.insertHtml(message); - cursor.endEditBlock(); - } - - QStringList ConvertQStringList(const QList &stdListString) - { - QStringList listQt; - listQt.reserve(stdListString.size()); - - for (const std::string &s : stdListString) { - listQt.append(QString::fromStdString(s)); - } - - return listQt; - } } } diff --git a/src/QvUtils.hpp b/src/QvUtils.hpp index d20680c5..2dcc211e 100644 --- a/src/QvUtils.hpp +++ b/src/QvUtils.hpp @@ -2,25 +2,12 @@ #define UTILS_H #include "Qv2rayBase.hpp" -#include -#include +#include "QvHelpers.hpp" namespace Qv2ray { namespace Utils { - QTranslator *getTranslator(const QString &lang); - - QStringList GetFileList(QDir dir); - - QString Base64Encode(QString string); - QString Base64Decode(QString string); - QStringList SplitLines(const QString &str); - list SplitLines_std(const QString &_string); - - bool CheckFile(QDir dir, QString fileName); - - const QString GenerateRandomString(int len = 12); void SetConfigDirPath(const QString *path); QString GetConfigDirPath(); @@ -29,95 +16,6 @@ namespace Qv2ray Qv2rayConfig GetGlobalConfig(); void LoadGlobalConfig(); - - void QvMessageBox(QWidget *parent, QString title, QString text); - int QvMessageBoxAsk(QWidget *parent, QString title, QString text, QMessageBox::StandardButton extraButtons = QMessageBox::NoButton); - // - QString StringFromFile(QFile *source); - bool StringToFile(const QString *text, QFile *target); - // - QJsonObject JsonFromString(QString string); - QString JsonToString(QJsonObject json, QJsonDocument::JsonFormat format = QJsonDocument::JsonFormat::Indented); - QString JsonToString(QJsonArray array, QJsonDocument::JsonFormat format = QJsonDocument::JsonFormat::Indented); - // - QString VerifyJsonString(const QString &source); - // - QString Stringify(list list, QString saperator = ";"); - QString Stringify(QList list, QString saperator = ";"); - // - // - template - QString StructToJsonString(const TYPE t) - { - return QString::fromStdString(X::tojson(t, "", 4, ' ')); - } - // - // - template - TYPE StructFromJsonString(const QString &str) - { - TYPE v; - X::loadjson(str.toStdString(), v, false); - return v; - } - // - // Misc - template - QJsonObject GetRootObject(const T &t) - { - auto json = StructToJsonString(t); - QJsonDocument doc = QJsonDocument::fromJson(QByteArray::fromStdString(json.toStdString())); - return doc.object(); - } - template QJsonObject GetRootObject(const RuleObject &t); - template QJsonObject GetRootObject(const StreamSettingsObject &t); - template QJsonObject GetRootObject(const VMessServerObject &t); - // - // - template - void RemoveItem(std::vector &vec, size_t pos) - { - auto it = vec.begin(); - std::advance(it, pos); - vec.erase(it); - } - - QString FormatBytes(long long bytes); - void DeducePossibleFileName(const QString &baseDir, QString *fileName, const QString &extension); - // - // - QString ConvertGFWToPAC(const QString &rawContent, const QString &customProxyString); - void QFastAppendTextDocument(const QString &message, QTextDocument *doc); - QStringList ConvertQStringList(const QList &stdListString); - inline bool IsValidFileName(const QString &str) - { - // If no match, we are good. - return QRegExp(R"([\/\\\"?%*:|><]|(^\.{1,2}$))").indexIn(str) == -1; - } - - - // These functions a sugers to prevent warnings from deprecated Qt 5.14 functions. - template - std::list toStdList(QList list) - { -#if QT_VERSION >= QT_VERSION_CHECK(5,14,0) - std::list _list{list.begin(), list.end()}; - return _list; -#else - return list.toStdList(); -#endif - } - - template - QList toQList(std::list list) - { -#if QT_VERSION >= QT_VERSION_CHECK(5,14,0) - QList _list{list.begin(), list.end()}; - return _list; -#else - return QList::fromStdList(list); -#endif - } } } diff --git a/src/ui/w_MainWindow.cpp b/src/ui/w_MainWindow.cpp index fa84611d..8f4f1658 100644 --- a/src/ui/w_MainWindow.cpp +++ b/src/ui/w_MainWindow.cpp @@ -103,31 +103,36 @@ MainWindow::MainWindow(QWidget *parent): updownImageBox->setStyleSheet("image: url(" + QV2RAY_UI_RESOURCES_ROOT + "netspeed_arrow.png)"); updownImageBox_2->setStyleSheet("image: url(" + QV2RAY_UI_RESOURCES_ROOT + "netspeed_arrow.png)"); // + // Setup System tray icons and menus + // hTray->setToolTip(TRAY_TOOLTIP_PREFIX); + // Basic actions + action_Tray_ShowHide = new QAction(this->windowIcon(), tr("Hide"), this); + action_Tray_Quit = new QAction(tr("Quit"), this); + action_Tray_Start = new QAction(tr("Connect"), this); + action_Tray_Reconnect = new QAction(tr("Reconnect"), this); + action_Tray_Stop = new QAction(tr("Disconnect"), this); // - 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_Reconnect = 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("Connect to this"), this); - QAction *action_RCM_ConvToComplex = new QAction(tr("Edit as Complex Config"), this); - QAction *action_RCM_EditJson = new QAction(QICON_R("json.png"), tr("Edit as Json"), this); - QAction *action_RCM_ShareQR = new QAction(QICON_R("share.png"), tr("Share as QRCode/VMess URL"), this); + action_Tray_SetSystemProxy = new QAction(tr("Enable System Proxy"), this); + action_Tray_ClearSystemProxy = new QAction(tr("Disable System Proxy"), this); // action_Tray_Start->setEnabled(true); action_Tray_Stop->setEnabled(false); action_Tray_Reconnect->setEnabled(false); // - trayMenu->addAction(action_Tray_ShowHide); - trayMenu->addSeparator(); - trayMenu->addAction(action_Tray_Start); - trayMenu->addAction(action_Tray_Stop); - trayMenu->addAction(action_Tray_Reconnect); - trayMenu->addSeparator(); - trayMenu->addAction(action_Tray_Quit); + tray_SystemProxyMenu->addAction(action_Tray_SetSystemProxy); + tray_SystemProxyMenu->addAction(action_Tray_ClearSystemProxy); + tray_SystemProxyMenu->setTitle(tr("System Proxy")); + // + tray_RootMenu->addAction(action_Tray_ShowHide); + tray_RootMenu->addSeparator(); + tray_RootMenu->addMenu(tray_SystemProxyMenu); + tray_RootMenu->addSeparator(); + tray_RootMenu->addAction(action_Tray_Start); + tray_RootMenu->addAction(action_Tray_Stop); + tray_RootMenu->addAction(action_Tray_Reconnect); + tray_RootMenu->addSeparator(); + tray_RootMenu->addAction(action_Tray_Quit); // connect(action_Tray_ShowHide, &QAction::triggered, this, &MainWindow::ToggleVisibility); connect(action_Tray_Start, &QAction::triggered, this, &MainWindow::on_startButton_clicked); @@ -135,6 +140,15 @@ MainWindow::MainWindow(QWidget *parent): connect(action_Tray_Reconnect, &QAction::triggered, this, &MainWindow::on_reconnectButton_clicked); connect(action_Tray_Quit, &QAction::triggered, this, &MainWindow::quit); connect(hTray, &QSystemTrayIcon::activated, this, &MainWindow::on_activatedTray); + // + // Actions for right click the connection list + // + QAction *action_RCM_RenameConnection = new QAction(tr("Rename"), this); + QAction *action_RCM_StartThis = new QAction(tr("Connect to this"), this); + QAction *action_RCM_ConvToComplex = new QAction(tr("Edit as Complex Config"), this); + QAction *action_RCM_EditJson = new QAction(QICON_R("json.png"), tr("Edit as Json"), this); + QAction *action_RCM_ShareQR = new QAction(QICON_R("share.png"), tr("Share as QRCode/VMess URL"), this); + // connect(action_RCM_RenameConnection, &QAction::triggered, this, &MainWindow::on_action_RCM_RenameConnection_triggered); connect(action_RCM_StartThis, &QAction::triggered, this, &MainWindow::on_action_StartThis_triggered); connect(action_RCM_EditJson, &QAction::triggered, this, &MainWindow::on_action_RCM_EditJson_triggered); @@ -147,15 +161,15 @@ MainWindow::MainWindow(QWidget *parent): connect(this, &MainWindow::DisConnect, this, &MainWindow::on_stopButton_clicked); connect(this, &MainWindow::ReConnect, this, &MainWindow::on_reconnectButton_clicked); // - hTray->setContextMenu(trayMenu); + hTray->setContextMenu(tray_RootMenu); hTray->show(); // - listMenu = new QMenu(this); - listMenu->addAction(action_RCM_RenameConnection); - listMenu->addAction(action_RCM_StartThis); - listMenu->addAction(action_RCM_ConvToComplex); - listMenu->addAction(action_RCM_EditJson); - listMenu->addAction(action_RCM_ShareQR); + connectionListMenu = new QMenu(this); + connectionListMenu->addAction(action_RCM_RenameConnection); + connectionListMenu->addAction(action_RCM_StartThis); + connectionListMenu->addAction(action_RCM_ConvToComplex); + connectionListMenu->addAction(action_RCM_EditJson); + connectionListMenu->addAction(action_RCM_ShareQR); // ReloadConnections(); // @@ -208,7 +222,7 @@ MainWindow::MainWindow(QWidget *parent): connectionListWidget->setCurrentItem(item); on_connectionListWidget_itemChanged(item, 0); connectionListWidget->scrollToItem(item); - trayMenu->actions()[0]->setText(tr("Show")); + tray_RootMenu->actions()[0]->setText(tr("Show")); on_startButton_clicked(); } else { QvMessageBox(this, tr("Autostarting a config"), tr("Could not find a specified config named: ") + NEWLINE + @@ -222,7 +236,7 @@ MainWindow::MainWindow(QWidget *parent): } // If we are not connected to anything, show the MainWindow. - if(vinstance->ConnectionStatus != STARTED){ + if (vinstance->ConnectionStatus != STARTED) { this->show(); } @@ -315,6 +329,7 @@ void MainWindow::VersionUpdate(QByteArray &data) void MainWindow::ReloadConnections() { LOG(MODULE_UI, "Loading new GlobalConfig") + SetEditWidgetEnable(false); currentConfig = GetGlobalConfig(); // // Store the latency test value. @@ -325,10 +340,10 @@ void MainWindow::ReloadConnections() } connections.clear(); - SetEditWidgetEnable(false); // connectionListWidget->clear(); auto _regularConnections = GetRegularConnections(currentConfig.configs); + auto _subsConnections = GetSubscriptionConnections(toStdList(QMap(currentConfig.subscribes).keys())); for (auto i = 0; i < _regularConnections.count(); i++) { ConnectionObject _o; @@ -342,8 +357,6 @@ void MainWindow::ReloadConnections() connectionListWidget->addTopLevelItem(new QTreeWidgetItem(QStringList() << _o.connectionName)); } - auto _subsConnections = GetSubscriptionConnections(toStdList(QMap(currentConfig.subscribes).keys())); - for (auto i = 0; i < _subsConnections.count(); i++) { auto subName = _subsConnections.keys()[i]; auto subTopLevel = new QTreeWidgetItem(QStringList() << tr("Subscription:") + " " + subName); @@ -537,9 +550,9 @@ void MainWindow::on_startButton_clicked() this->show(); } - trayMenu->actions()[2]->setEnabled(!startFlag); - trayMenu->actions()[3]->setEnabled(startFlag); - trayMenu->actions()[4]->setEnabled(startFlag); + action_Tray_Start->setEnabled(!startFlag); + action_Tray_Stop->setEnabled(startFlag); + action_Tray_Reconnect->setEnabled(startFlag); // startButton->setEnabled(!startFlag); stopButton->setEnabled(startFlag); @@ -559,9 +572,9 @@ void MainWindow::on_stopButton_clicked() QFile(QV2RAY_GENERATED_FILE_PATH).remove(); statusLabel->setText(tr("Disconnected")); vCoreLogBrowser->clear(); - trayMenu->actions()[2]->setEnabled(true); - trayMenu->actions()[3]->setEnabled(false); - trayMenu->actions()[4]->setEnabled(false); + action_Tray_Start->setEnabled(true); + action_Tray_Stop->setEnabled(false); + action_Tray_Reconnect->setEnabled(false); // startButton->setEnabled(true); stopButton->setEnabled(false); @@ -587,7 +600,7 @@ void MainWindow::on_stopButton_clicked() void MainWindow::closeEvent(QCloseEvent *event) { this->hide(); - trayMenu->actions()[0]->setText(tr("Show")); + tray_RootMenu->actions()[0]->setText(tr("Show")); event->ignore(); } void MainWindow::on_activatedTray(QSystemTrayIcon::ActivationReason reason) @@ -631,10 +644,10 @@ void MainWindow::ToggleVisibility() QThread::msleep(20); SetWindowPos(HWND(this->winId()), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); #endif - trayMenu->actions()[0]->setText(tr("Hide")); + tray_RootMenu->actions()[0]->setText(tr("Hide")); } else { this->hide(); - trayMenu->actions()[0]->setText(tr("Show")); + tray_RootMenu->actions()[0]->setText(tr("Show")); } } void MainWindow::quit() @@ -655,7 +668,7 @@ void MainWindow::ShowAndSetConnection(QString guiConnectionName, bool SetConnect SetEditWidgetEnable(true); // // --------- BRGIN Show Connection - currentGUIShownConnectionName = guiConnectionName; + currentSelectedName = guiConnectionName; auto conf = connections[guiConnectionName]; auto root = conf.config; // @@ -766,7 +779,7 @@ void MainWindow::on_connectionListWidget_customContextMenuRequested(const QPoint auto item = connectionListWidget->itemAt(connectionListWidget->mapFromGlobal(_pos)); if (IsConnectableItem(item)) { - listMenu->popup(_pos); + connectionListMenu->popup(_pos); } } void MainWindow::on_action_RCM_RenameConnection_triggered() @@ -775,7 +788,7 @@ void MainWindow::on_action_RCM_RenameConnection_triggered() SUBSCRIPTION_CONFIG_MODIFY_DENY(item->text(0)) item->setFlags(item->flags() | Qt::ItemIsEditable); connectionListWidget->editItem(item); - originalName = item->text(0); + renameOriginalName = item->text(0); isRenamingInProgress = true; } void MainWindow::on_connectionListWidget_itemChanged(QTreeWidgetItem *item, int) @@ -785,11 +798,11 @@ void MainWindow::on_connectionListWidget_itemChanged(QTreeWidgetItem *item, int) if (isRenamingInProgress) { // Should not rename a config from subscription? // In this case it's after we entered the name. - LOG(MODULE_CONNECTION, "RENAME: " + originalName.toStdString() + " -> " + item->text(0).toStdString()) + LOG(MODULE_CONNECTION, "RENAME: " + renameOriginalName.toStdString() + " -> " + item->text(0).toStdString()) auto newName = item->text(0); // If I really did some changes. - if (originalName != newName) { + if (renameOriginalName != newName) { bool canGo = true; if (newName.trimmed().isEmpty()) { @@ -808,25 +821,25 @@ void MainWindow::on_connectionListWidget_itemChanged(QTreeWidgetItem *item, int) } if (!canGo) { - item->setText(0, originalName); + item->setText(0, renameOriginalName); return; } // // Change auto start config. - if (originalName.toStdString() == currentConfig.autoStartConfig.connectionName && currentConfig.autoStartConfig.subscriptionName.empty()) { + if (renameOriginalName.toStdString() == currentConfig.autoStartConfig.connectionName && currentConfig.autoStartConfig.subscriptionName.empty()) { currentConfig.autoStartConfig.connectionName = newName.toStdString(); } //configList[configList.indexOf(originalName.toStdString())] = newName.toStdString(); - currentConfig.configs.remove(originalName.toStdString()); + currentConfig.configs.remove(renameOriginalName.toStdString()); currentConfig.configs.push_back(newName.toStdString()); // - RenameConnection(originalName, newName); + RenameConnection(renameOriginalName, newName); // LOG(MODULE_UI, "Saving a global config") SetGlobalConfig(currentConfig); - bool running = CurrentConnectionName == originalName; + bool running = CurrentConnectionName == renameOriginalName; if (running) CurrentConnectionName = newName; @@ -1185,7 +1198,7 @@ void MainWindow::onPingFinished(QvTCPingData data) connections[data.connectionName].latency = data.avg; - if (data.connectionName == currentGUIShownConnectionName) { - ShowAndSetConnection(currentGUIShownConnectionName, false, false); + if (data.connectionName == currentSelectedName) { + ShowAndSetConnection(currentSelectedName, false, false); } } diff --git a/src/ui/w_MainWindow.hpp b/src/ui/w_MainWindow.hpp index 7fcd26a9..42b3119e 100644 --- a/src/ui/w_MainWindow.hpp +++ b/src/ui/w_MainWindow.hpp @@ -104,8 +104,11 @@ class MainWindow : public QMainWindow, Ui::MainWindow void on_connectionListWidget_itemSelectionChanged(); private: - // void SetEditWidgetEnable(bool enabled); + void ShowAndSetConnection(QString currentText, bool SetConnection, bool Apply); + void ReloadConnections(); + Qv2rayConfig currentConfig; + // // Charts QChartView *speedChartView; QChart *speedChartObj; @@ -114,23 +117,20 @@ class MainWindow : public QMainWindow, Ui::MainWindow QList uploadList; QList downloadList; // - // - QMenu *trayMenu = new QMenu(this); - QMenu *listMenu; + QMenu *connectionListMenu; /// Key --> ListWidget.item.text QMap connections; // - QString originalName; + QString renameOriginalName; bool isRenamingInProgress; // + // ID for QTimers + // int logTimerId; int speedTimerId; int pingTimerId; // - void ShowAndSetConnection(QString currentText, bool SetConnection, bool Apply); - void ReloadConnections(); - // // QvHttpRequestHelper *requestHelper; QSystemTrayIcon *hTray; @@ -139,11 +139,25 @@ class MainWindow : public QMainWindow, Ui::MainWindow SyntaxHighlighter *vCoreLogHighlighter; SyntaxHighlighter *qvAppLogHighlighter; // - Qv2rayConfig currentConfig; - QList logTextBrowsers; int currentLogBrowserId = 0; - QString currentGUIShownConnectionName; + QString currentSelectedName; + // + // Actions in the system tray menu + // + QMenu *tray_RootMenu = new QMenu(this); + QAction *action_Tray_ShowHide; + QAction *action_Tray_Quit; + // --> Connectivities + QAction *action_Tray_Start; + QAction *action_Tray_Reconnect ; + QAction *action_Tray_Stop; + // --> System proxy settings + QMenu *tray_SystemProxyMenu = new QMenu(this); + QAction *action_Tray_SetSystemProxy; + QAction *action_Tray_ClearSystemProxy; + // + }; #endif // MAINWINDOW_H diff --git a/src/utils/QvHelpers.cpp b/src/utils/QvHelpers.cpp new file mode 100644 index 00000000..5aa6da61 --- /dev/null +++ b/src/utils/QvHelpers.cpp @@ -0,0 +1,248 @@ +#include "QvHelpers.hpp" +#include "QvUtils.hpp" +#include + +// Forwarded from QvTinyLog +static QQueue __loggerBuffer; + +void _LOG(const std::string &module, const std::string &log) +{ + string logString = "[" + module + "]: " + log; + cout << logString << endl; + __loggerBuffer.enqueue((logString + NEWLINE).c_str()); +} + +const QString readLastLog() +{ + QString result; + + while (!__loggerBuffer.isEmpty()) { + result += __loggerBuffer.dequeue(); + } + + return result; +} + +namespace Qv2ray +{ + namespace Utils + { + const QString GenerateRandomString(int len) + { + const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + QString randomString; + + for (int i = 0; i < len; ++i) { + uint rand = QRandomGenerator::system()->generate(); + uint max = static_cast(possibleCharacters.length()); + QChar nextChar = possibleCharacters[rand % max]; + randomString.append(nextChar); + } + + return randomString; + } + + QString Stringify(list list, QString saperator) + { + QString out; + + for (auto item : list) { + out.append(QSTRING(item)); + out.append(saperator); + } + + if (out.length() >= 1) + out = out.remove(out.length() - 1, 1); + + return out; + } + + QString Stringify(QList list, QString saperator) + { + QString out; + + for (auto item : list) { + out.append(item); + out.append(saperator); + } + + if (out.length() >= 1) + out = out.remove(out.length() - 1, 1); + + return out; + } + + QString StringFromFile(QFile *source) + { + source->open(QFile::ReadOnly); + QTextStream stream(source); + QString str = stream.readAll(); + source->close(); + return str; + } + + bool StringToFile(const QString *text, QFile *targetFile) + { + bool override = targetFile->exists(); + targetFile->open(QFile::WriteOnly); + QTextStream stream(targetFile); + stream << *text << endl; + stream.flush(); + targetFile->close(); + return override; + } + + QJsonObject JSONFromFile(QFile *sourceFile) + { + QString json = StringFromFile(sourceFile); + return JsonFromString(json); + } + + QString JsonToString(QJsonObject json, QJsonDocument::JsonFormat format) + { + QJsonDocument doc; + doc.setObject(json); + return doc.toJson(format); + } + + QString JsonToString(QJsonArray array, QJsonDocument::JsonFormat format) + { + QJsonDocument doc; + doc.setArray(array); + return doc.toJson(format); + } + + QString VerifyJsonString(const QString &source) + { + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(source.toUtf8(), &error); + Q_UNUSED(doc) + + if (error.error == QJsonParseError::NoError) { + return ""; + } else { + LOG(MODULE_UI, "WARNING: Json parse returns: " + error.errorString().toStdString()) + return error.errorString(); + } + } + + QJsonObject JsonFromString(QString string) + { + QJsonDocument doc = QJsonDocument::fromJson(string.toUtf8()); + return doc.object(); + } + + QString Base64Encode(QString string) + { + QByteArray ba = string.toUtf8(); + return ba.toBase64(); + } + + QString Base64Decode(QString string) + { + QByteArray ba = string.toUtf8(); + return QString(QByteArray::fromBase64(ba)); + } + + QStringList SplitLines(const QString &_string) + { + return _string.split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + } + + list SplitLines_std(const QString &_string) + { + list list; + + for (auto line : _string.split(QRegExp("[\r\n]"), QString::SkipEmptyParts)) { + list.push_back(line.toStdString()); + } + + return list; + } + + QStringList GetFileList(QDir dir) + { + return dir.entryList(QStringList() << "*" << "*.*", QDir::Hidden | QDir::Files); + } + + bool FileExistsIn(QDir dir, QString fileName) + { + return GetFileList(dir).contains(fileName); + } + + void QvMessageBox(QWidget *parent, QString title, QString text) + { + QMessageBox::warning(parent, title, text, QMessageBox::Ok | QMessageBox::Default, 0); + } + + int QvMessageBoxAsk(QWidget *parent, QString title, QString text, QMessageBox::StandardButton extraButtons) + { + return QMessageBox::information(parent, title, text, QMessageBox::Yes | QMessageBox::No | extraButtons); + } + + QString FormatBytes(long long bytes) + { + char str[64]; + const char *sizes[5] = { "B", "KB", "MB", "GB", "TB" }; + int i; + double dblByte = bytes; + + for (i = 0; i < 5 && bytes >= 1024; i++, bytes /= 1024) + dblByte = bytes / 1024.0; + + sprintf(str, "%.2f", dblByte); + return strcat(strcat(str, " "), sizes[i]); + } + + QTranslator *getTranslator(const QString &lang) + { + QTranslator *translator = new QTranslator(); + translator->load(lang + ".qm", ":/translations/"); + return translator; + } + + /// This returns a file name without extensions. + void DeducePossibleFileName(const QString &baseDir, QString *fileName, const QString &extension) + { + int i = 1; + + if (!QDir(baseDir).exists()) { + QDir(baseDir).mkpath(baseDir); + LOG(MODULE_FILE, "Making path: " + baseDir.toStdString()) + } + + while (true) { + if (!QFile(baseDir + "/" + fileName + "_" + QString::number(i) + extension).exists()) { + *fileName = *fileName + "_" + QString::number(i); + return; + } else { + DEBUG(MODULE_FILE, "File with name: " + fileName->toStdString() + "_" + to_string(i) + extension.toStdString() + " already exists") + } + + i++; + } + } + + void QFastAppendTextDocument(const QString &message, QTextDocument *doc) + { + QTextCursor cursor(doc); + cursor.movePosition(QTextCursor::End); + cursor.beginEditBlock(); + cursor.insertBlock(); + cursor.insertHtml(message); + cursor.endEditBlock(); + } + + QStringList ConvertQStringList(const QList &stdListString) + { + QStringList listQt; + listQt.reserve(stdListString.size()); + + for (const std::string &s : stdListString) { + listQt.append(QString::fromStdString(s)); + } + + return listQt; + } + } +} diff --git a/src/utils/QvHelpers.hpp b/src/utils/QvHelpers.hpp new file mode 100644 index 00000000..b4fd7877 --- /dev/null +++ b/src/utils/QvHelpers.hpp @@ -0,0 +1,100 @@ +#ifndef QVHELPERS_H +#define QVHELPERS_H + +#include "Qv2rayBase.hpp" +#include +#include + +namespace Qv2ray +{ + namespace Utils + { + + QTranslator *getTranslator(const QString &lang); + QStringList GetFileList(QDir dir); + QString Base64Encode(QString string); + QString Base64Decode(QString string); + QStringList SplitLines(const QString &str); + list SplitLines_std(const QString &_string); + bool FileExistsIn(QDir dir, QString fileName); + const QString GenerateRandomString(int len = 12); + void QvMessageBox(QWidget *parent, QString title, QString text); + int QvMessageBoxAsk(QWidget *parent, QString title, QString text, QMessageBox::StandardButton extraButtons = QMessageBox::NoButton); + QString StringFromFile(QFile *source); + bool StringToFile(const QString *text, QFile *target); + QJsonObject JsonFromString(QString string); + QString JsonToString(QJsonObject json, QJsonDocument::JsonFormat format = QJsonDocument::JsonFormat::Indented); + QString JsonToString(QJsonArray array, QJsonDocument::JsonFormat format = QJsonDocument::JsonFormat::Indented); + QString VerifyJsonString(const QString &source); + QString Stringify(list list, QString saperator = ";"); + QString Stringify(QList list, QString saperator = ";"); + QString FormatBytes(long long bytes); + void DeducePossibleFileName(const QString &baseDir, QString *fileName, const QString &extension); + QString ConvertGFWToPAC(const QString &rawContent, const QString &customProxyString); + void QFastAppendTextDocument(const QString &message, QTextDocument *doc); + QStringList ConvertQStringList(const QList &stdListString); + // + template + QString StructToJsonString(const TYPE t) + { + return QString::fromStdString(X::tojson(t, "", 4, ' ')); + } + // + template + TYPE StructFromJsonString(const QString &str) + { + TYPE v; + X::loadjson(str.toStdString(), v, false); + return v; + } + // Misc + template + QJsonObject GetRootObject(const T &t) + { + auto json = StructToJsonString(t); + QJsonDocument doc = QJsonDocument::fromJson(QByteArray::fromStdString(json.toStdString())); + return doc.object(); + } + template QJsonObject GetRootObject(const RuleObject &t); + template QJsonObject GetRootObject(const StreamSettingsObject &t); + template QJsonObject GetRootObject(const VMessServerObject &t); + // + template + void RemoveItem(std::vector &vec, size_t pos) + { + auto it = vec.begin(); + std::advance(it, pos); + vec.erase(it); + } + + inline bool IsValidFileName(const QString &str) + { + // If no match, we are good. + return QRegExp(R"([\/\\\"?%*:|><]|(^\.{1,2}$))").indexIn(str) == -1; + } + + // These functions a sugers to prevent warnings from deprecated Qt 5.14 functions. + template + std::list toStdList(QList list) + { +#if QT_VERSION >= QT_VERSION_CHECK(5,14,0) + std::list _list {list.begin(), list.end()}; + return _list; +#else + return list.toStdList(); +#endif + } + + template + QList toQList(std::list list) + { +#if QT_VERSION >= QT_VERSION_CHECK(5,14,0) + QList _list {list.begin(), list.end()}; + return _list; +#else + return QList::fromStdList(list); +#endif + } + } +} +#endif // QVHELPERS_H