add: added copy existing config, link existing config, edit existing config

This commit is contained in:
QwQ 2020-07-23 20:12:14 +08:00
parent b26f455166
commit cdc87d61d7
No known key found for this signature in database
GPG Key ID: E7FAEFAFCD031D4B
10 changed files with 172 additions and 114 deletions

View File

@ -1 +1 @@
5820 5821

View File

@ -107,6 +107,7 @@ namespace Qv2ray::base::objects::complex
{ {
OutboundObjectMeta meta; OutboundObjectMeta meta;
meta.metaType = METAOUTBOUND_ORIGINAL; meta.metaType = METAOUTBOUND_ORIGINAL;
meta.object.mode = MODE_CONNECTIONID;
meta.object.connectionId = id; meta.object.connectionId = id;
meta.object.displayName = tag; meta.object.displayName = tag;
return meta; return meta;

View File

@ -71,7 +71,6 @@ void RouteEditor::SetupNodeWidget()
nodeScene = new FlowScene(this); nodeScene = new FlowScene(this);
flowView = new FlowView(nodeScene, nodeGraphWidget); flowView = new FlowView(nodeScene, nodeGraphWidget);
flowView->scaleDown(); flowView->scaleDown();
flowView->scaleDown();
if (!nodeGraphWidget->layout()) if (!nodeGraphWidget->layout())
{ {
// The QWidget will take ownership of layout. // The QWidget will take ownership of layout.
@ -416,79 +415,18 @@ void RouteEditor::on_delBtn_clicked()
return; return;
} }
auto firstNode = nodeScene->selectedNodes()[0]; const auto selecteNodes = nodeScene->selectedNodes();
auto isInbound = false; // inboundNodes.values().contains(firstNode->id()); if (selecteNodes.empty())
auto isOutbound = false; // outboundNodes.values().contains(firstNode->id());
auto isRule = false; // ruleNodes.values().contains(firstNode->id());
// Get the tag first, and call inbounds/outbounds/rules container variable
// remove() Remove the node last since some events may trigger. Then remove
// the node container.
if (isInbound)
{ {
// currentInboundOutboundTag = GetFirstNodeData(firstNode->id(), InboundNode)->GetInbound(); QvMessageBoxWarn(this, tr("Deleting a node"), tr("You need to select a node first"));
// nodeScene->removeNode(*nodeScene->node(inboundNodes[currentInboundOutboundTag])); return;
// inboundNodes.remove(currentInboundOutboundTag);
//
// // Remove corresponded inbound tags from the rules.
// for (auto k : rules.keys())
// {
// auto v = rules[k];
// v.inboundTag.removeAll(currentInboundOutboundTag);
// rules[k] = v;
// }
//
// inbounds.remove(currentInboundOutboundTag);
}
else if (isOutbound)
{
// currentInboundOutboundTag = GetFirstNodeData(firstNode->id(), OutboundNode)->GetOutbound();
// outbounds.remove(currentInboundOutboundTag);
// ResolveDefaultOutboundTag(currentInboundOutboundTag, "");
//
//// Remove corresponded outbound tags from the rules.
// for (auto k : rules.keys())
//{
// auto v = rules[k];
//
// if (v.outboundTag == currentInboundOutboundTag)
// v.outboundTag.clear();
//
// rules[k] = v;
//}
//
// nodeScene->removeNode(*nodeScene->node(outboundNodes[currentInboundOutboundTag]));
// outboundNodes.remove(currentInboundOutboundTag);
}
else if (isRule)
{
// ruleEnableCB->setEnabled(false);
// ruleTagLineEdit->setEnabled(false);
// ruleRenameBtn->setEnabled(false);
// auto RuleTag = GetFirstNodeData(firstNode->id(), RuleNode)->GetRuleTag();
// currentRuleTag.clear();
// routeRuleGroupBox->setEnabled(false);
// routeEditGroupBox->setEnabled(false);
// rules.remove(RuleTag);
// nodeScene->removeNode(*nodeScene->node(ruleNodes[RuleTag]));
// ruleNodes.remove(RuleTag);
////
//// Remove item from the rule order list widget.
// ruleListWidget->takeItem(ruleListWidget->row(ruleListWidget->findItems(RuleTag, Qt::MatchExactly).first()));
// CHECKEMPTYRULES
//// currentRuleTag = rules.firstKey();
// ShowCurrentRuleDetail();
}
else
{
LOG(MODULE_UI, "Unknown node selected.")
QvMessageBoxWarn(this, tr("Error"), tr("Qv2ray entered an unknown state."));
} }
nodeDispatcher->DeleteNode(selecteNodes.front()->id());
} }
void RouteEditor::on_addRouteBtn_clicked() void RouteEditor::on_addRouteBtn_clicked()
{ {
auto ruleName = nodeDispatcher->CreateRule({}); const auto _ = nodeDispatcher->CreateRule({});
Q_UNUSED(ruleName)
} }
void RouteEditor::on_domainStrategyCombo_currentIndexChanged(const QString &arg1) void RouteEditor::on_domainStrategyCombo_currentIndexChanged(const QString &arg1)
@ -540,3 +478,9 @@ void RouteEditor::on_debugPainterCB_clicked(bool checked)
flowView->repaint(); flowView->repaint();
#endif #endif
} }
void RouteEditor::on_linkExistingBtn_clicked()
{
const auto cid = ConnectionId{ importConnBtn->currentData(Qt::UserRole).toString() };
auto _ = nodeDispatcher->CreateOutbound(make_outbound(cid, GetDisplayName(cid)));
}

View File

@ -72,6 +72,8 @@ class RouteEditor
void on_debugPainterCB_clicked(bool checked); void on_debugPainterCB_clicked(bool checked);
void on_linkExistingBtn_clicked();
private: private:
// NOTE: Self managed pointer. // NOTE: Self managed pointer.
std::shared_ptr<NodeDispatcher> nodeDispatcher; std::shared_ptr<NodeDispatcher> nodeDispatcher;

View File

@ -123,11 +123,11 @@
</layout> </layout>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,1,1,0"> <layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,1,1,0,0">
<item> <item>
<widget class="QLabel" name="label_18"> <widget class="QLabel" name="label_18">
<property name="text"> <property name="text">
<string>Existed Connection</string> <string>Existing Connection</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -138,9 +138,16 @@
<widget class="QComboBox" name="importConnBtn"/> <widget class="QComboBox" name="importConnBtn"/>
</item> </item>
<item> <item>
<widget class="QToolButton" name="importExistingBtn"> <widget class="QPushButton" name="importExistingBtn">
<property name="text"> <property name="text">
<string>OK</string> <string>Copy</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="linkExistingBtn">
<property name="text">
<string>Reference</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -83,6 +83,38 @@ void NodeDispatcher::RestoreConnections()
isConstructing = false; isConstructing = false;
} }
void NodeDispatcher::DeleteNode(const QUuid &nodeId)
{
bool isInbound = inboundNodes.values().contains(nodeId);
bool isOutbound = outboundNodes.values().contains(nodeId);
bool isRule = ruleNodes.values().contains(nodeId);
// Lots of duplicated checks.
{
Q_ASSERT_X(isInbound && !isOutbound, "Delete Node", "Node type error.");
Q_ASSERT_X(isInbound && !isRule, "Delete Node", "Node type error.");
Q_ASSERT_X(isOutbound && !isInbound, "Delete Node", "Node type error.");
Q_ASSERT_X(isOutbound && !isRule, "Delete Node", "Node type error.");
Q_ASSERT_X(isRule && !isInbound, "Delete Node", "Node type error.");
Q_ASSERT_X(isRule && !isOutbound, "Delete Node", "Node type error.");
}
if (isInbound)
{
if (!inboundNodes.values().contains(nodeId))
{
LOG(MODULE_NODE, "Could not find a inbound with id: " + nodeId.toString())
}
}
else if (isOutbound)
{
}
else if (isRule)
{
}
else
Q_UNREACHABLE();
}
QString NodeDispatcher::CreateInbound(INBOUND in) QString NodeDispatcher::CreateInbound(INBOUND in)
{ {
auto tag = getTag(in); auto tag = getTag(in);

View File

@ -50,6 +50,9 @@ class NodeDispatcher
return outbounds.count(); return outbounds.count();
} }
public:
void DeleteNode(const QUuid &nodeId);
signals: signals:
void OnInboundCreated(std::shared_ptr<INBOUND>, QtNodes::Node &); void OnInboundCreated(std::shared_ptr<INBOUND>, QtNodes::Node &);
void OnOutboundCreated(std::shared_ptr<OutboundObjectMeta>, QtNodes::Node &); void OnOutboundCreated(std::shared_ptr<OutboundObjectMeta>, QtNodes::Node &);

View File

@ -2,6 +2,8 @@
#include "base/Qv2rayBase.hpp" #include "base/Qv2rayBase.hpp"
#include "core/CoreUtils.hpp" #include "core/CoreUtils.hpp"
#include "core/handler/ConfigHandler.hpp"
#include "ui/common/UIBase.hpp"
#include "ui/editors/w_InboundEditor.hpp" #include "ui/editors/w_InboundEditor.hpp"
#include "ui/editors/w_JsonEditor.hpp" #include "ui/editors/w_JsonEditor.hpp"
#include "ui/editors/w_OutboundEditor.hpp" #include "ui/editors/w_OutboundEditor.hpp"
@ -10,6 +12,8 @@ InboundOutboundWidget::InboundOutboundWidget(WidgetMode mode, std::shared_ptr<No
{ {
workingMode = mode; workingMode = mode;
setupUi(this); setupUi(this);
editBtn->setIcon(QICON_R("edit"));
editJsonBtn->setIcon(QICON_R("code"));
} }
void InboundOutboundWidget::setValue(std::shared_ptr<INBOUND> data) void InboundOutboundWidget::setValue(std::shared_ptr<INBOUND> data)
@ -23,7 +27,10 @@ void InboundOutboundWidget::setValue(std::shared_ptr<OutboundObjectMeta> data)
{ {
assert(workingMode == MODE_OUTBOUND); assert(workingMode == MODE_OUTBOUND);
outboundObject = data; outboundObject = data;
tagTxt->setText(getTag(outboundObject->realOutbound)); tagTxt->setText(outboundObject->getTag());
isExternalOutbound = outboundObject->object.mode == MODE_CONNECTIONID;
statusLabel->setText(isExternalOutbound ? tr("External Config") : "");
tagTxt->setEnabled(!isExternalOutbound);
} }
void InboundOutboundWidget::changeEvent(QEvent *e) void InboundOutboundWidget::changeEvent(QEvent *e)
@ -56,13 +63,37 @@ void InboundOutboundWidget::on_editBtn_clicked()
} }
case MODE_OUTBOUND: case MODE_OUTBOUND:
{ {
OutboundEditor editor{ outboundObject->realOutbound, parentWidget() }; if (isExternalOutbound)
outboundObject->realOutbound = editor.OpenEditor(); {
// Set tag if (QvMessageBoxAsk(parentWidget(), tr("Edit Outbound"), editExternalMsg) != QMessageBox::Yes)
const auto newTag = getTag(outboundObject->realOutbound); {
tagTxt->setText(newTag); return;
outboundObject->realOutbound["tag"] = newTag; }
break; const auto externalId = outboundObject->object.connectionId;
const auto root = ConnectionManager->GetConnectionRoot(externalId);
if (IsComplexConfig(root))
{
if (QvMessageBoxAsk(parentWidget(), tr("Trying to edit an Complex Config"), editExternalComplexMsg) != QMessageBox::Yes)
{
return;
}
}
OUTBOUND outbound{ QJsonIO::GetValue(root, "outbounds", 0).toObject() };
OutboundEditor editor{ outbound, parentWidget() };
outbound = editor.OpenEditor();
//
ConnectionManager->UpdateConnection(externalId, CONFIGROOT{ QJsonObject{ { "outbounds", QJsonArray{ outbound } } } });
}
else
{
OutboundEditor editor{ outboundObject->realOutbound, parentWidget() };
outboundObject->realOutbound = editor.OpenEditor();
// Set tag
const auto newTag = getTag(outboundObject->realOutbound);
tagTxt->setText(newTag);
outboundObject->realOutbound["tag"] = newTag;
break;
}
} }
} }
} }
@ -83,13 +114,33 @@ void InboundOutboundWidget::on_editJsonBtn_clicked()
} }
case MODE_OUTBOUND: case MODE_OUTBOUND:
{ {
JsonEditor editor{ outboundObject->realOutbound, parentWidget() }; if (isExternalOutbound)
outboundObject->realOutbound = OUTBOUND{ editor.OpenEditor() }; {
const auto newTag = getTag(outboundObject->realOutbound); if (QvMessageBoxAsk(parentWidget(), tr("Edit Outbound"), editExternalMsg) != QMessageBox::Yes)
// Set tag {
tagTxt->setText(newTag); return;
outboundObject->realOutbound["tag"] = newTag; }
break; const auto externalId = outboundObject->object.connectionId;
const auto root = ConnectionManager->GetConnectionRoot(externalId);
OUTBOUND outbound{ QJsonIO::GetValue(root, "outbounds", 0).toObject() };
JsonEditor editor{ outbound, parentWidget() };
outbound = OUTBOUND{ editor.OpenEditor() };
//
ConnectionManager->UpdateConnection(externalId, CONFIGROOT{ QJsonObject{ { "outbounds", QJsonArray{ outbound } } } });
}
else
{
// Open Editor
JsonEditor editor{ outboundObject->realOutbound, parentWidget() };
outboundObject->realOutbound = OUTBOUND{ editor.OpenEditor() };
//
// Set tag (only for local connections)
const auto newTag = getTag(outboundObject->realOutbound);
tagTxt->setText(newTag);
outboundObject->realOutbound["tag"] = newTag;
break;
}
} }
} }
} }

View File

@ -33,4 +33,11 @@ class InboundOutboundWidget
WidgetMode workingMode; WidgetMode workingMode;
std::shared_ptr<INBOUND> inboundObject; std::shared_ptr<INBOUND> inboundObject;
std::shared_ptr<OutboundObjectMeta> outboundObject; std::shared_ptr<OutboundObjectMeta> outboundObject;
bool isExternalOutbound = false;
private:
const QString editExternalMsg = tr("You are trying to edit an external connection config, is this what you want?");
const QString editExternalComplexMsg = tr("You have selected an complex config as outbound.") + NEWLINE +
tr("continuing editing this configuration will make you LOSS ALL INBOUND AND ROUTING settings.") +
NEWLINE + tr("Is this what you want?");
}; };

View File

@ -6,14 +6,14 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>152</width> <width>181</width>
<height>64</height> <height>66</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
@ -26,30 +26,41 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item row="0" column="0" colspan="2"> <item>
<widget class="QLineEdit" name="tagTxt"/> <widget class="QLineEdit" name="tagTxt"/>
</item> </item>
<item row="1" column="0"> <item>
<widget class="QPushButton" name="editBtn"> <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,1">
<property name="text"> <item>
<string/> <widget class="QPushButton" name="editBtn">
</property> <property name="text">
<property name="icon"> <string/>
<iconset resource="../../../../resources.new.qrc"> </property>
<normaloff>:/assets/icons/ui_dark/edit.svg</normaloff>:/assets/icons/ui_dark/edit.svg</iconset> <property name="icon">
</property> <iconset resource="../../../../resources.new.qrc">
</widget> <normaloff>:/assets/icons/ui_light/edit.svg</normaloff>:/assets/icons/ui_light/edit.svg</iconset>
</item> </property>
<item row="1" column="1"> </widget>
<widget class="QPushButton" name="editJsonBtn"> </item>
<property name="text"> <item>
<string/> <widget class="QPushButton" name="editJsonBtn">
</property> <property name="text">
<property name="icon"> <string/>
<iconset resource="../../../../resources.new.qrc"> </property>
<normaloff>:/assets/icons/ui_dark/code.svg</normaloff>:/assets/icons/ui_dark/code.svg</iconset> <property name="icon">
</property> <iconset resource="../../../../resources.new.qrc">
</widget> <normaloff>:/assets/icons/ui_light/code.svg</normaloff>:/assets/icons/ui_light/code.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="statusLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>