update: several refactors and Import window redesign

This commit is contained in:
Qv2ray-dev 2020-05-17 22:04:03 +08:00
parent fc71624213
commit 31f6e1389f
17 changed files with 356 additions and 351 deletions

View File

@ -1 +1 @@
5460
5461

View File

@ -66,9 +66,9 @@ namespace Qv2ray::common
request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, ua);
}
QByteArray QvHttpRequestHelper::Get(const QString &url)
QByteArray QvHttpRequestHelper::Get(const QUrl &url)
{
request.setUrl({ url });
request.setUrl(url);
setAccessManagerAttributes(accessManager);
auto _reply = accessManager.get(request);
//

View File

@ -33,7 +33,11 @@ namespace Qv2ray::common
~QvHttpRequestHelper();
// get
void AsyncGet(const QString &url);
QByteArray Get(const QString &url);
QByteArray Get(const QUrl &url);
QByteArray Get(const QString &url)
{
return Get(QUrl{ url });
}
signals:
void OnRequestFinished(QByteArray &data);

View File

@ -1,5 +1,7 @@
#include "ConnectionIO.hpp"
#include "Serialization.hpp"
#include "common/HTTPRequestHelper.hpp"
#include "common/QvHelpers.hpp"
namespace Qv2ray::core::connection
@ -8,14 +10,7 @@ namespace Qv2ray::core::connection
{
CONFIGROOT ConvertConfigFromFile(const QString &sourceFilePath, bool importComplex)
{
QFile source(sourceFilePath);
if (!source.exists())
{
LOG(MODULE_FILEIO, "Trying to import from an non-existing file.") return CONFIGROOT();
}
auto root = CONFIGROOT(JsonFromString(StringFromFile(source)));
auto root = CONFIGROOT(JsonFromString(StringFromFile(sourceFilePath)));
if (!importComplex)
{
@ -29,5 +24,27 @@ namespace Qv2ray::core::connection
root.remove("dns");
return root;
}
QMultiHash<QString, CONFIGROOT> GetConnectionConfigFromSubscription(const QUrl &subscriptionUrl, const QString &groupName)
{
QMultiHash<QString, CONFIGROOT> subscriptionContent;
QvHttpRequestHelper helper;
const auto data = helper.Get(subscriptionUrl);
auto subscriptionLines = SplitLines(TryDecodeSubscriptionString(data));
for (const auto &line : subscriptionLines)
{
QString __alias;
QString __errMessage;
// Assign a group name, to pass the name check.
QString __groupName = groupName;
auto connectionConfigMap = ConvertConfigFromString(line.trimmed(), &__alias, &__errMessage, &__groupName);
if (!__errMessage.isEmpty())
LOG(MODULE_SUBSCRIPTION, "Error: " + __errMessage)
for (const auto &val : connectionConfigMap)
{
subscriptionContent.insert(connectionConfigMap.key(val), val);
}
}
return subscriptionContent;
}
} // namespace ConnectionIO
} // namespace Qv2ray::core::connection

View File

@ -6,6 +6,7 @@ namespace Qv2ray::core::connection
{
// File Protocol
CONFIGROOT ConvertConfigFromFile(const QString &sourceFilePath, bool importComplex);
QMultiHash<QString, CONFIGROOT> GetConnectionConfigFromSubscription(const QUrl &subscriptionUrl, const QString &groupName);
} // namespace ConnectionIO
} // namespace Qv2ray::core::connection

View File

@ -1,7 +1,6 @@
#include "Serialization.hpp"
#include "Generation.hpp"
#include "common/QvHelpers.hpp"
#include "components/plugins/QvPluginHost.hpp"
#include "core/CoreUtils.hpp"
#include "core/handler/ConfigHandler.hpp"
@ -49,7 +48,7 @@ namespace Qv2ray::core::connection
{
CONFIGROOT root;
auto outbound = GenerateOutboundEntry(val.first, OUTBOUNDSETTING(val.second), {});
root.insert("outbounds", QJsonArray{ outbound });
QJsonIO::SetValue(root, outbound, "outbounds", 0);
connectionConf.insert(key, root);
}
}
@ -108,14 +107,5 @@ namespace Qv2ray::core::connection
return sharelink;
}
QString DecodeSubscriptionString(const QByteArray &arr)
{
// String may start with: vmess:// and ss://
// We only process vmess:// here
// Some subscription providers may use plain vmess:// saperated by
// lines But others may use base64 of above.
auto result = QString::fromUtf8(arr).trimmed();
return result.contains("://") ? result : SafeBase64Decode(result);
}
} // namespace Serialization
} // namespace Qv2ray::core::connection

View File

@ -1,5 +1,6 @@
#pragma once
#include "base/Qv2rayBase.hpp"
#include "common/QvHelpers.hpp"
namespace Qv2ray::core::connection
{
@ -15,7 +16,11 @@ namespace Qv2ray::core::connection
const inline auto QV2RAY_SSD_DEFAULT_NAME_PATTERN = QObject::tr("%1 - %2 (rate %3)");
//
// General
QString DecodeSubscriptionString(const QByteArray &arr);
inline const QString TryDecodeSubscriptionString(const QByteArray &arr)
{
auto result = QString::fromUtf8(arr).trimmed();
return result.contains("://") ? result : SafeBase64Decode(result);
}
QMultiHash<QString, CONFIGROOT> ConvertConfigFromString(const QString &link, QString *aliasPrefix, QString *errMessage,
QString *newGroupName = nullptr);
const QString ConvertConfigToString(const ConnectionGroupPair &id, bool isSip002 = false);

View File

@ -66,7 +66,6 @@ namespace Qv2ray::core::connection
CONFIGROOT Deserialize(const QString &vmessStr, QString *alias, QString *errMessage)
{
#define default CONFIGROOT()
LOG(MODULE_SETTINGS, "Trying to convert from a vmess string.")
QString vmess = vmessStr;
if (vmess.trimmed() != vmess)

View File

@ -66,7 +66,6 @@ namespace Qv2ray::core::handlers
connect(kernelHandler, &KernelInstanceHandler::OnDisconnected, this, &QvConfigHandler::OnDisconnected);
//
tcpingHelper = new QvTCPingHelper(5, this);
httpHelper = new QvHttpRequestHelper(this);
connect(tcpingHelper, &QvTCPingHelper::OnLatencyTestCompleted, this, &QvConfigHandler::OnLatencyDataArrived_p);
//
// Save per 1 minutes.
@ -369,7 +368,6 @@ namespace Qv2ray::core::handlers
{
LOG(MODULE_CORE_HANDLER, "Triggering save settings from destructor")
delete kernelHandler;
delete httpHelper;
CHSaveConfigData();
}
@ -465,57 +463,33 @@ namespace Qv2ray::core::handlers
bool QvConfigHandler::UpdateSubscription(const GroupId &id)
{
CheckGroupExistanceEx(id, false);
if (isHttpRequestInProgress || !groups[id].isSubscription)
if (!groups[id].isSubscription)
{
return false;
}
isHttpRequestInProgress = true;
auto data = httpHelper->Get(groups[id].subscriptionOption.address);
isHttpRequestInProgress = false;
return CHUpdateSubscription_p(id, data);
return CHUpdateSubscription_p(id, groups[id].subscriptionOption.address);
}
bool QvConfigHandler::CHUpdateSubscription_p(const GroupId &id, const QByteArray &subscriptionData)
bool QvConfigHandler::CHUpdateSubscription_p(const GroupId &id, const QString &url)
{
CheckGroupExistanceEx(id, false);
if (!groups.contains(id))
{
return false;
}
QMultiHash<QString, CONFIGROOT> allSubscriptionConnections;
//
//
//
// ====================================================================================== Begin reading subscription
auto _newConnections = GetConnectionConfigFromSubscription(url, GetDisplayName(id));
if (_newConnections.count() < 5)
{
auto subscriptionLines = SplitLines(DecodeSubscriptionString(subscriptionData));
for (const auto &line : subscriptionLines)
{
QString __alias;
QString __errMessage;
// Assign a group name, to pass the name check.
QString __groupName = GetDisplayName(id);
auto connectionConfigMap = ConvertConfigFromString(line.trimmed(), &__alias, &__errMessage, &__groupName);
if (!__errMessage.isEmpty())
LOG(MODULE_SUBSCRIPTION, "Error: " + __errMessage)
for (const auto &val : connectionConfigMap)
{
allSubscriptionConnections.insert(connectionConfigMap.key(val), val);
}
}
if (allSubscriptionConnections.count() < 5)
{
LOG(MODULE_SUBSCRIPTION, "Find a subscription with less than 5 connections.")
if (QvMessageBoxAsk(nullptr, tr("Update Subscription"),
tr("%1 entrie(s) have been found from the subscription source, do you want to continue?")
.arg(allSubscriptionConnections.count())) != QMessageBox::Yes)
LOG(MODULE_SUBSCRIPTION, "Find a subscription with less than 5 connections.")
if (QvMessageBoxAsk(
nullptr, tr("Update Subscription"),
tr("%1 entrie(s) have been found from the subscription source, do you want to continue?").arg(_newConnections.count())) !=
QMessageBox::Yes)
return false;
}
return false;
}
// ====================================================================================== End reading subscription
//
//
//
// ====================================================================================== Begin Connection Data Storage
// Anyway, we try our best to preserve the connection id.
@ -540,9 +514,9 @@ namespace Qv2ray::core::handlers
auto originalConnectionIdList = groups[id].connections;
groups[id].connections.clear();
//
for (const auto &config : allSubscriptionConnections)
for (const auto &config : _newConnections)
{
const auto _alias = allSubscriptionConnections.key(config);
const auto _alias = _newConnections.key(config);
QString errMessage;
if (!errMessage.isEmpty())
@ -562,7 +536,7 @@ namespace Qv2ray::core::handlers
LOG(MODULE_CORE_HANDLER, "Reused connection id from name: " + _alias)
auto _conn = nameMap.take(_alias);
groups[id].connections << _conn;
UpdateConnection(_conn, config);
UpdateConnection(_conn, config, true);
// Remove Connection Id from the list.
originalConnectionIdList.removeAll(_conn);
typeMap.remove(typeMap.key(_conn));
@ -573,7 +547,7 @@ namespace Qv2ray::core::handlers
auto _conn = typeMap.take(outboundData);
groups[id].connections << _conn;
// Update Connection Properties
UpdateConnection(_conn, config);
UpdateConnection(_conn, config, true);
RenameConnection(_conn, _alias);
// Remove Connection Id from the list.
originalConnectionIdList.removeAll(_conn);
@ -583,7 +557,7 @@ namespace Qv2ray::core::handlers
{
// New connection id is required since nothing matched found...
LOG(MODULE_CORE_HANDLER, "Generated new connection id for connection: " + _alias)
CreateConnection(config, _alias, id);
CreateConnection(config, _alias, id, true);
}
// ====================================================================================== End guessing new ConnectionId
}

View File

@ -1,7 +1,6 @@
#pragma once
#include "base/Qv2rayBase.hpp"
#include "common/HTTPRequestHelper.hpp"
#include "components/latency/QvTCPing.hpp"
#include "core/CoreUtils.hpp"
#include "core/connection/ConnectionIO.hpp"
@ -152,7 +151,7 @@ namespace Qv2ray::core::handlers
void timerEvent(QTimerEvent *event) override;
private:
bool CHUpdateSubscription_p(const GroupId &id, const QByteArray &subscriptionData);
bool CHUpdateSubscription_p(const GroupId &id, const QString &url);
private:
int saveTimerId;
@ -163,8 +162,6 @@ namespace Qv2ray::core::handlers
QHash<ConnectionId, CONFIGROOT> connectionRootCache;
private:
QvHttpRequestHelper *httpHelper;
bool isHttpRequestInProgress = false;
QvTCPingHelper *tcpingHelper;
KernelInstanceHandler *kernelHandler;
};

View File

@ -257,7 +257,7 @@ GroupManager::~GroupManager()
void GroupManager::on_addGroupButton_clicked()
{
auto const key = tr("New Group") + " - " + GenerateRandomString(5);
auto id = ConnectionManager->CreateGroup(key, true);
auto id = ConnectionManager->CreateGroup(key, false);
//
auto item = new QListWidgetItem(key);
item->setData(Qt::UserRole, id.toString());
@ -266,7 +266,7 @@ void GroupManager::on_addGroupButton_clicked()
void GroupManager::on_updateButton_clicked()
{
if (QvMessageBoxAsk(this, tr("Reload Subscription"), tr("Would you like to reload the subscription?")) == QMessageBox::Yes)
if (QvMessageBoxAsk(this, tr("Update Subscription"), tr("Would you like to update the subscription?")) == QMessageBox::Yes)
{
this->setEnabled(false);
ConnectionManager->UpdateSubscription(currentGroupId);

View File

@ -105,7 +105,7 @@
<string/>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<iconset resource="../../../resources.qrc">
<normaloff>:/assets/icons/ui_light/add.png</normaloff>:/assets/icons/ui_light/add.png</iconset>
</property>
</widget>
@ -125,7 +125,7 @@
<string/>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<iconset resource="../../../resources.qrc">
<normaloff>:/assets/icons/ui_light/delete.png</normaloff>:/assets/icons/ui_light/delete.png</iconset>
</property>
</widget>
@ -227,6 +227,9 @@
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
@ -321,26 +324,6 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Connection Settings</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<layout class="QGridLayout" name="connectionSettingsLayout"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_4">
<attribute name="title">
<string>Route Settings</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<layout class="QGridLayout" name="routeSettingsLayout"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
@ -352,7 +335,7 @@
<tabstop>updateIntervalSB</tabstop>
</tabstops>
<resources>
<include location="../../resources.qrc"/>
<include location="../../../resources.qrc"/>
</resources>
<connections>
<connection>

View File

@ -20,12 +20,22 @@
#include <QJsonObject>
#include <QThread>
constexpr auto LINK_PAGE = 0;
constexpr auto QRCODE_PAGE = 1;
constexpr auto MANUAL_PAGE = 2;
constexpr auto ADVANCED_PAGE = 3;
ImportConfigWindow::ImportConfigWindow(QWidget *parent) : QDialog(parent)
{
setupUi(this);
// nameTxt->setText(tr("My Connection Imported at: ") + QDateTime::currentDateTime().toString("MM-dd hh:mm"));
nameTxt->setText(tr("New Connection") + QDateTime::currentDateTime().toString("MM-dd hh:mm"));
QvMessageBusConnect(ImportConfigWindow);
RESTORE_RUNTIME_CONFIG(screenShotHideQv2ray, hideQv2rayCB->setChecked)
//
for (const auto &gid : ConnectionManager->AllGroups())
{
groupCombo->addItem(GetDisplayName(gid), gid.toString());
}
}
void ImportConfigWindow::UpdateColorScheme()
@ -54,23 +64,28 @@ QMultiHash<QString, CONFIGROOT> ImportConfigWindow::SelectConnection(bool outbou
// false and disable the checkbox
keepImportedInboundCheckBox->setEnabled(!outboundsOnly);
routeEditBtn->setEnabled(!outboundsOnly);
groupCombo->setEnabled(false);
this->exec();
QMultiHash<QString, CONFIGROOT> conn;
for (const auto &connEntry : connections.values())
for (const auto &connEntry : connectionsToNewGroup.values())
{
conn += connEntry;
}
for (const auto &connEntry : connectionsToExistingGroup.values())
{
conn += connEntry;
}
return result() == Accepted ? conn : QMultiHash<QString, CONFIGROOT>{};
}
int ImportConfigWindow::ImportConnection()
int ImportConfigWindow::PerformImportConnection()
{
this->exec();
int count = 0;
for (const auto &groupName : connections.keys())
for (const auto &groupObject : connectionsToNewGroup)
{
GroupId groupId = groupName.isEmpty() ? DefaultGroupId : ConnectionManager->CreateGroup(groupName, false);
const auto groupObject = connections[groupName];
const auto groupName = connectionsToNewGroup.key(groupObject);
GroupId groupId = ConnectionManager->CreateGroup(groupName, false);
for (const auto &connConf : groupObject)
{
auto connName = groupObject.key(connConf);
@ -84,6 +99,21 @@ int ImportConfigWindow::ImportConnection()
}
}
for (const auto &groupObject : connectionsToExistingGroup)
{
const auto groupId = connectionsToExistingGroup.key(groupObject);
for (const auto &connConf : groupObject)
{
auto connName = groupObject.key(connConf);
auto [protocol, host, port] = GetConnectionInfo(connConf);
if (connName.isEmpty())
{
connName = protocol + "/" + host + ":" + QSTRN(port) + "-" + GenerateRandomString(5);
}
ConnectionManager->CreateConnection(connConf, connName, groupId, true);
}
}
return count;
}
@ -116,7 +146,7 @@ void ImportConfigWindow::on_qrFromScreenBtn_clicked()
if (_r == QDialog::Accepted)
{
auto str = DecodeQRCode(pix);
qrImageLabel->setPixmap(QPixmap::fromImage(pix));
if (str.trimmed().isEmpty())
{
LOG(MODULE_UI, "Cannot decode QR Code from an image, size: h=" + QSTRN(pix.width()) + ", v=" + QSTRN(pix.height()))
@ -124,7 +154,7 @@ void ImportConfigWindow::on_qrFromScreenBtn_clicked()
}
else
{
vmessConnectionStringTxt->appendPlainText(str.trimmed() + NEWLINE);
qrCodeLinkTxt->setText(str.trimmed());
}
}
}
@ -135,7 +165,7 @@ void ImportConfigWindow::on_beginImportBtn_clicked()
switch (tabWidget->currentIndex())
{
case 0:
case LINK_PAGE:
{
QStringList linkList = SplitLines(vmessConnectionStringTxt->toPlainText());
//
@ -165,11 +195,19 @@ void ImportConfigWindow::on_beginImportBtn_clicked()
linkErrors[link] = QSTRN(linkErrors.count() + 1) + ": " + errMessage;
continue;
}
else
else if (newGroupName.isEmpty())
{
for (const auto &conf : config)
{
AddToGroup(newGroupName, config.key(conf), conf);
connectionsToExistingGroup[GroupId{ groupCombo->currentData().toString() }].insert(config.key(conf), conf);
}
}
else
{
for (const auto &conf : config)
{
connectionsToNewGroup[newGroupName].insert(config.key(conf), conf);
}
}
}
@ -189,7 +227,26 @@ void ImportConfigWindow::on_beginImportBtn_clicked()
break;
}
case 2:
case QRCODE_PAGE:
{
QString errorMsg;
const auto root = ConvertConfigFromString(qrCodeLinkTxt->text(), &aliasPrefix, &errorMsg);
if (!errorMsg.isEmpty())
{
QvMessageBoxWarn(this, tr("Failed to import connection"), errorMsg);
break;
}
for (const auto &conf : root)
{
connectionsToExistingGroup[GroupId{ groupCombo->currentData().toString() }].insert(root.key(conf), conf);
}
break;
}
case MANUAL_PAGE:
{
break;
}
case ADVANCED_PAGE:
{
// From File...
bool ImportAsComplex = keepImportedInboundCheckBox->isChecked();
@ -203,7 +260,7 @@ void ImportConfigWindow::on_beginImportBtn_clicked()
aliasPrefix += "_" + QFileInfo(path).fileName();
CONFIGROOT config = ConvertConfigFromFile(path, ImportAsComplex);
AddToGroup("", aliasPrefix, config);
connectionsToExistingGroup[GroupId{ groupCombo->currentData().toString() }].insert(aliasPrefix, config);
break;
}
}
@ -275,7 +332,7 @@ void ImportConfigWindow::on_connectionEditBtn_clicked()
CONFIGROOT root;
root.insert("outbounds", outboundsList);
//
AddToGroup("", alias, root);
connectionsToExistingGroup[GroupId{ groupCombo->currentData().toString() }].insert(alias, root);
accept();
}
}
@ -291,12 +348,13 @@ void ImportConfigWindow::on_subscriptionButton_clicked()
GroupManager w(this);
w.exec();
auto importToComplex = !keepImportedInboundCheckBox->isEnabled();
connections.clear();
connectionsToNewGroup.clear();
connectionsToExistingGroup.clear();
if (importToComplex)
{
auto [alias, conf] = w.GetSelectedConfig();
AddToGroup("", alias, conf);
connectionsToExistingGroup[GroupId{ groupCombo->currentData().toString() }].insert(alias, conf);
}
accept();
@ -311,7 +369,7 @@ void ImportConfigWindow::on_routeEditBtn_clicked()
if (isChanged)
{
AddToGroup("", alias, result);
connectionsToExistingGroup[GroupId{ groupCombo->currentData().toString() }].insert(alias, result);
accept();
}
}
@ -331,7 +389,7 @@ void ImportConfigWindow::on_jsonEditBtn_clicked()
if (isChanged)
{
AddToGroup("", alias, CONFIGROOT(result));
connectionsToExistingGroup[GroupId{ groupCombo->currentData().toString() }].insert(alias, CONFIGROOT(result));
accept();
}
}

View File

@ -15,7 +15,7 @@ class ImportConfigWindow
public:
explicit ImportConfigWindow(QWidget *parent = nullptr);
~ImportConfigWindow();
int ImportConnection();
int PerformImportConnection();
QMultiHash<QString, CONFIGROOT> SelectConnection(bool outboundsOnly);
private:
@ -43,19 +43,8 @@ class ImportConfigWindow
private:
void UpdateColorScheme();
QMap<QString, QMultiHash<QString, CONFIGROOT>> connections;
QMap<QString, QString> linkErrors;
void AddToGroup(const QString &groupName, const QString &alias, const CONFIGROOT &root)
{
if (connections.contains(groupName))
{
connections[groupName].insert(alias, root);
}
else
{
QMultiHash<QString, CONFIGROOT> temp;
temp.insert(alias, root);
connections.insert(groupName, temp);
}
}
//
QHash<GroupId, QMultiHash<QString, CONFIGROOT>> connectionsToExistingGroup;
QHash<QString, QMultiHash<QString, CONFIGROOT>> connectionsToNewGroup;
};

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>522</width>
<height>460</height>
<width>549</width>
<height>482</height>
</rect>
</property>
<property name="minimumSize">
@ -42,15 +42,18 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Import To Group</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="groupCombo"/>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Import Source</string>
</property>
</widget>
</item>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
@ -58,197 +61,193 @@
</property>
<widget class="QWidget" name="tabWidgetPage2">
<attribute name="title">
<string>VMess / QRCode</string>
<string>Link</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_3">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Connection Share Link</string>
<string>Share Link</string>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QPlainTextEdit" name="vmessConnectionStringTxt">
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
<property name="placeholderText">
<string>Paste share link here, one line for each.</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QListWidget" name="errorsList"/>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Error List</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Share Link</string>
</property>
</widget>
</item>
</layout>
<item row="0" column="1">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Error List</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>QRCode File</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="imageFileEdit">
<property name="text">
<string/>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="selectImageBtn">
<property name="text">
<string>Select</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Screenshot</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QDoubleSpinBox" name="doubleSpinBox">
<property name="suffix">
<string> secs</string>
</property>
<property name="maximum">
<double>5.000000000000000</double>
</property>
<property name="singleStep">
<double>0.500000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Delay</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="hideQv2rayCB">
<property name="text">
<string>Hide Qv2ray</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="qrFromScreenBtn">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Go</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<item row="1" column="0">
<widget class="QPlainTextEdit" name="vmessConnectionStringTxt">
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
<property name="placeholderText">
<string>Paste share link here, one line for each.</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QListWidget" name="errorsList"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabWidgetPage4">
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Subscriptions / Manually Input</string>
<string>QR Code</string>
</attribute>
<layout class="QFormLayout" name="formLayout_4">
<item row="1" column="1">
<widget class="QPushButton" name="subscriptionButton">
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Open Subscription Manager</string>
<string>QRCode File</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="Line" name="line">
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="imageFileEdit">
<property name="text">
<string/>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="selectImageBtn">
<property name="text">
<string>Select</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Screenshot</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QCheckBox" name="hideQv2rayCB">
<property name="text">
<string>Hide Qv2ray</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="doubleSpinBox">
<property name="suffix">
<string> secs</string>
</property>
<property name="maximum">
<double>5.000000000000000</double>
</property>
<property name="singleStep">
<double>0.500000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Delay</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="qrFromScreenBtn">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Go</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Image</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="qrImageLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>10</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Manually Input Connections</string>
<string/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Detected Link</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="qrCodeLinkTxt">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Input Manually</string>
</attribute>
<layout class="QFormLayout" name="formLayout_4">
<item row="1" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Connection Editor</string>
<string>Simple Editor</string>
</property>
</widget>
</item>
<item row="4" column="1">
<item row="1" column="1">
<widget class="QPushButton" name="connectionEditBtn">
<property name="text">
<string>Open Connection Editor</string>
@ -258,51 +257,27 @@
</property>
</widget>
</item>
<item row="5" column="0">
<item row="2" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Route Editor</string>
<string>Complex Editor</string>
</property>
</widget>
</item>
<item row="5" column="1">
<item row="2" column="1">
<widget class="QPushButton" name="routeEditBtn">
<property name="text">
<string>Open Route Editor</string>
<string>Open Route / Complex Connection Editor</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Json Editor</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QPushButton" name="jsonEditBtn">
<property name="text">
<string>Open JSON Editor</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Subscription Manager</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_14">
<widget class="QLabel" name="label">
<property name="text">
<string>Subscription Link</string>
<string>You can manually input connection here.</string>
</property>
</widget>
</item>
@ -310,17 +285,24 @@
</widget>
<widget class="QWidget" name="tabWidgetPage1">
<attribute name="title">
<string>Existing File</string>
<string>Advanced</string>
</attribute>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Manually Input Connections</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="fileLabel">
<property name="text">
<string>Path</string>
</property>
</widget>
</item>
<item row="0" column="1">
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="fileLineTxt">
@ -338,13 +320,30 @@
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="keepImportedInboundCheckBox">
<property name="text">
<string>Import as Complex Config (Manually edit route rules and inbounds)</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Json Editor</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPushButton" name="jsonEditBtn">
<property name="text">
<string>Open JSON Editor</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
@ -384,21 +383,10 @@
</widget>
<tabstops>
<tabstop>nameTxt</tabstop>
<tabstop>tabWidget</tabstop>
<tabstop>vmessConnectionStringTxt</tabstop>
<tabstop>errorsList</tabstop>
<tabstop>imageFileEdit</tabstop>
<tabstop>selectImageBtn</tabstop>
<tabstop>doubleSpinBox</tabstop>
<tabstop>hideQv2rayCB</tabstop>
<tabstop>qrFromScreenBtn</tabstop>
<tabstop>subscriptionButton</tabstop>
<tabstop>connectionEditBtn</tabstop>
<tabstop>routeEditBtn</tabstop>
<tabstop>jsonEditBtn</tabstop>
<tabstop>fileLineTxt</tabstop>
<tabstop>selectFileBtn</tabstop>
<tabstop>keepImportedInboundCheckBox</tabstop>
<tabstop>beginImportBtn</tabstop>
<tabstop>cancelImportBtn</tabstop>
</tabstops>

View File

@ -576,7 +576,7 @@ void MainWindow::on_action_RCM_DeleteThese_triggered()
void MainWindow::on_importConfigButton_clicked()
{
ImportConfigWindow w(this);
w.ImportConnection();
w.PerformImportConnection();
}
void MainWindow::on_action_RCM_EditAsComplex_triggered()

View File

@ -86,7 +86,7 @@
<string/>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<iconset resource="../../../resources.qrc">
<normaloff>:/assets/icons/ui_light/locate.png</normaloff>:/assets/icons/ui_light/locate.png</iconset>
</property>
</widget>
@ -110,7 +110,7 @@
<string/>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<iconset resource="../../../resources.qrc">
<normaloff>:/assets/icons/ui_light/sort.png</normaloff>:/assets/icons/ui_light/sort.png</iconset>
</property>
<property name="popupMode">
@ -291,7 +291,7 @@
<string/>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<iconset resource="../../../resources.qrc">
<normaloff>:/assets/icons/ui_light/delete.png</normaloff>:/assets/icons/ui_light/delete.png</iconset>
</property>
</widget>
@ -354,7 +354,7 @@
<string/>
</property>
<property name="icon">
<iconset resource="../../resources.qrc">
<iconset resource="../../../resources.qrc">
<normaloff>:/assets/icons/ui_light/delete.png</normaloff>:/assets/icons/ui_light/delete.png</iconset>
</property>
</widget>
@ -538,7 +538,7 @@
<tabstop>importConfigButton</tabstop>
</tabstops>
<resources>
<include location="../../resources.qrc"/>
<include location="../../../resources.qrc"/>
</resources>
<connections/>
</ui>