update: use model/view for ConnectionList

This commit is contained in:
QxQ 2020-10-01 01:20:33 +08:00
parent 7def1c8854
commit b33c76a556
9 changed files with 414 additions and 361 deletions

View File

@ -68,6 +68,9 @@ set(_QV2RAY_UI_SOURCES
${QV2RAY_QWIDGETS_UI_BASEDIR}/styles/StyleManager.cpp
${QV2RAY_QWIDGETS_UI_BASEDIR}/styles/StyleManager.cpp
${QV2RAY_QWIDGETS_UI_BASEDIR}/styles/StyleManager.hpp
# Models
${QV2RAY_QWIDGETS_UI_BASEDIR}/models/ConnectionModelHelper.cpp
${QV2RAY_QWIDGETS_UI_BASEDIR}/models/ConnectionModelHelper.hpp
# UI Widgets
${QV2RAY_QWIDGETS_UI_BASEDIR}/widgets/ConnectionInfoWidget.hpp
${QV2RAY_QWIDGETS_UI_BASEDIR}/widgets/ConnectionInfoWidget.cpp

View File

@ -1 +1 @@
5946
5947

View File

@ -35,12 +35,15 @@
#define VIEWABLE 120
// table of supposed nice steps for grid marks to get nice looking quarters of scale
const static double roundingTable[] = { 1.2, 1.6, 2, 2.4, 2.8, 3.2, 4, 6, 8 };
SpeedWidget::SpeedWidget(QWidget *parent) : QGraphicsView(parent)
{
UpdateSpeedPlotSettings();
}
void SpeedWidget::AddPointData(QMap<SpeedWidget::GraphID, long> data)
void SpeedWidget::AddPointData(QMap<SpeedWidget::GraphType, long> data)
{
SpeedWidget::PointData point;
point.x = QDateTime::currentMSecsSinceEpoch() / 1000;
@ -50,11 +53,11 @@ void SpeedWidget::AddPointData(QMap<SpeedWidget::GraphID, long> data)
point.y[id] = data;
}
m_datahalfMin.push_back(point);
dataCollection.push_back(point);
while (m_datahalfMin.length() > VIEWABLE)
while (dataCollection.length() > VIEWABLE)
{
m_datahalfMin.removeFirst();
dataCollection.removeFirst();
}
replot();
}
@ -93,73 +96,66 @@ int friendlyUnitPrecision(const SizeUnit unit)
default: return 3;
}
}
namespace
struct SplittedValue
{
// table of supposed nice steps for grid marks to get nice looking quarters
// of scale
const static double roundingTable[] = { 1.2, 1.6, 2, 2.4, 2.8, 3.2, 4, 6, 8 };
struct SplittedValue
double arg;
SizeUnit unit;
qint64 sizeInBytes() const
{
double arg;
SizeUnit unit;
qint64 sizeInBytes() const
auto size = arg;
for (int i = 0; i < static_cast<int>(unit); ++i)
{
auto size = arg;
for (int i = 0; i < static_cast<int>(unit); ++i)
{
size *= 1024;
}
return size;
size *= 1024;
}
};
return size;
}
};
SplittedValue getRoundedYScale(double value)
SplittedValue getRoundedYScale(double value)
{
if (value == 0.0)
return { 0, SizeUnit::Byte };
if (value <= 12.0)
return { 12, SizeUnit::Byte };
auto calculatedUnit = SizeUnit::Byte;
while (value > 1000)
{
if (value == 0.0)
return { 0, SizeUnit::Byte };
if (value <= 12.0)
return { 12, SizeUnit::Byte };
auto calculatedUnit = SizeUnit::Byte;
while (value > 1000)
{
value /= 1000;
calculatedUnit = static_cast<SizeUnit>(static_cast<int>(calculatedUnit) + 1);
}
if (value > 100.0)
{
int roundedValue = static_cast<int>(value / 40) * 40;
while (roundedValue < value) roundedValue += 40;
return { static_cast<double>(roundedValue), calculatedUnit };
}
if (value > 10.0)
{
int roundedValue = static_cast<int>(value / 4) * 4;
while (roundedValue < value) roundedValue += 4;
return { static_cast<double>(roundedValue), calculatedUnit };
}
for (const auto &roundedValue : roundingTable)
{
if (value <= roundedValue)
return { roundedValue, calculatedUnit };
}
return { 10.0, calculatedUnit };
value /= 1000;
calculatedUnit = static_cast<SizeUnit>(static_cast<int>(calculatedUnit) + 1);
}
QString formatLabel(const double argValue, const SizeUnit unit)
if (value > 100.0)
{
// check is there need for digits after decimal separator
const int precision = (argValue < 10) ? friendlyUnitPrecision(unit) : 0;
return QLocale::system().toString(argValue, 'f', precision) + " " + unitString(unit, true);
int roundedValue = static_cast<int>(value / 40) * 40;
while (roundedValue < value) roundedValue += 40;
return { static_cast<double>(roundedValue), calculatedUnit };
}
} // namespace
if (value > 10.0)
{
int roundedValue = static_cast<int>(value / 4) * 4;
while (roundedValue < value) roundedValue += 4;
return { static_cast<double>(roundedValue), calculatedUnit };
}
for (const auto &roundedValue : roundingTable)
{
if (value <= roundedValue)
return { roundedValue, calculatedUnit };
}
return { 10.0, calculatedUnit };
}
QString formatLabel(const double argValue, const SizeUnit unit)
{
// check is there need for digits after decimal separator
const int precision = (argValue < 10) ? friendlyUnitPrecision(unit) : 0;
return QLocale::system().toString(argValue, 'f', precision) + " " + unitString(unit, true);
}
void SpeedWidget::UpdateSpeedPlotSettings()
{
@ -168,9 +164,12 @@ void SpeedWidget::UpdateSpeedPlotSettings()
if (!Graph.colorConfig.contains(x)) \
Graph.colorConfig[x] = y;
_X_(API_INBOUND, (QvPair<QvGraphPenConfig>{ { 134, 196, 63, 1.5f, Qt::SolidLine }, { 50, 153, 255, 1.5f, Qt::SolidLine } }));
const static QvPair<QvGraphPenConfig> defaultPen{ { 134, 196, 63, 1.5f, Qt::SolidLine }, { 50, 153, 255, 1.5f, Qt::SolidLine } };
const static QvPair<QvGraphPenConfig> directPen{ { 0, 210, 240, 1.5f, Qt::DotLine }, { 235, 220, 42, 1.5f, Qt::DotLine } };
_X_(API_INBOUND, defaultPen);
_X_(API_OUTBOUND_PROXY, Graph.colorConfig[API_INBOUND]);
_X_(API_OUTBOUND_DIRECT, (QvPair<QvGraphPenConfig>{ { 0, 210, 240, 1.5f, Qt::DotLine }, { 235, 220, 42, 1.5f, Qt::DotLine } }));
_X_(API_OUTBOUND_DIRECT, directPen);
const auto getPen = [](const QvGraphPenConfig &conf) {
QPen p{ { conf.R, conf.G, conf.B } };
@ -200,7 +199,7 @@ void SpeedWidget::UpdateSpeedPlotSettings()
void SpeedWidget::Clear()
{
m_datahalfMin.clear();
dataCollection.clear();
m_properties.clear();
UpdateSpeedPlotSettings();
replot();
@ -215,9 +214,9 @@ quint64 SpeedWidget::maxYValue()
quint64 maxYValue = 0;
for (int id = 0; id < NB_GRAPHS; ++id)
for (int i = m_datahalfMin.size() - 1, j = 0; (i >= 0) && (j <= VIEWABLE); --i, ++j)
if (m_datahalfMin[i].y[id] > maxYValue)
maxYValue = m_datahalfMin[i].y[id];
for (int i = dataCollection.size() - 1, j = 0; (i >= 0) && (j <= VIEWABLE); --i, ++j)
if (dataCollection[i].y[id] > maxYValue)
maxYValue = dataCollection[i].y[id];
return maxYValue;
}
@ -288,10 +287,10 @@ void SpeedWidget::paintEvent(QPaintEvent *)
{
QVector<QPoint> points;
for (int i = static_cast<int>(m_datahalfMin.size()) - 1, j = 0; (i >= 0) && (j <= VIEWABLE); --i, ++j)
for (int i = static_cast<int>(dataCollection.size()) - 1, j = 0; (i >= 0) && (j <= VIEWABLE); --i, ++j)
{
const int newX = rect.right() - j * xTickSize;
const int newY = rect.bottom() - m_datahalfMin[i].y[id] * yMultiplier;
const int newY = rect.bottom() - dataCollection[i].y[id] * yMultiplier;
points.push_back({ newX, newY });
}

View File

@ -35,7 +35,7 @@ class SpeedWidget : public QGraphicsView
{
Q_OBJECT
public:
enum GraphID
enum GraphType
{
INBOUND_UP,
INBOUND_DOWN,
@ -59,7 +59,7 @@ class SpeedWidget : public QGraphicsView
explicit SpeedWidget(QWidget *parent = nullptr);
void UpdateSpeedPlotSettings();
void AddPointData(QMap<SpeedWidget::GraphID, long> data);
void AddPointData(QMap<SpeedWidget::GraphType, long> data);
void Clear();
void replot();
@ -76,7 +76,7 @@ class SpeedWidget : public QGraphicsView
};
quint64 maxYValue();
QList<PointData> m_datahalfMin;
QList<PointData> dataCollection;
QMap<GraphID, GraphProperties> m_properties;
QMap<GraphType, GraphProperties> m_properties;
};

View File

@ -0,0 +1,163 @@
#include "ConnectionModelHelper.hpp"
#include "core/handler/ConfigHandler.hpp"
#include "ui/widgets/widgets/ConnectionItemWidget.hpp"
#define NumericString(i) (QString("%1").arg(i, 30, 10, QLatin1Char('0')))
ConnectionListHelper::ConnectionListHelper(QTreeView *view, QObject *parent) : QObject(parent)
{
parentView = view;
model = new QStandardItemModel();
view->setModel(model);
for (const auto &group : ConnectionManager->AllGroups())
{
addGroupItem(group);
for (const auto &connection : ConnectionManager->Connections(group))
{
addConnectionItem({ connection, group });
}
}
const auto renamedLambda = [&](const ConnectionId &id, const QString &, const QString &newName) {
for (const auto &gid : ConnectionManager->GetGroupId(id))
{
ConnectionGroupPair pair{ id, gid };
if (pairs.contains(pair))
pairs[pair]->setData(newName, ROLE_DISPLAYNAME);
}
};
const auto latencyLambda = [&](const ConnectionId &id, const int avg) {
for (const auto &gid : ConnectionManager->GetGroupId(id))
{
ConnectionGroupPair pair{ id, gid };
if (pairs.contains(pair))
pairs[pair]->setData(NumericString(avg), ROLE_LATENCY);
}
};
const auto statsLambda = [&](const ConnectionGroupPair &id, const QMap<StatisticsType, QvStatsSpeedData> &data) {
if (connections.contains(id.connectionId))
{
for (const auto &index : connections[id.connectionId])
index->setData(NumericString(GetConnectionTotalData(id.connectionId)), ROLE_DATA_USAGE);
}
};
connect(ConnectionManager, &QvConfigHandler::OnConnectionRemovedFromGroup, this, &ConnectionListHelper::OnConnectionDeleted);
connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, this, &ConnectionListHelper::OnConnectionCreated);
connect(ConnectionManager, &QvConfigHandler::OnConnectionLinkedWithGroup, this, &ConnectionListHelper::OnConnectionLinkedWithGroup);
connect(ConnectionManager, &QvConfigHandler::OnGroupCreated, this, &ConnectionListHelper::OnGroupCreated);
connect(ConnectionManager, &QvConfigHandler::OnGroupDeleted, this, &ConnectionListHelper::OnGroupDeleted);
connect(ConnectionManager, &QvConfigHandler::OnConnectionRenamed, renamedLambda);
connect(ConnectionManager, &QvConfigHandler::OnLatencyTestFinished, latencyLambda);
connect(ConnectionManager, &QvConfigHandler::OnStatsAvailable, statsLambda);
}
ConnectionListHelper::~ConnectionListHelper()
{
delete model;
}
void ConnectionListHelper::Sort(ConnectionInfoRole role, Qt::SortOrder order)
{
model->setSortRole(role);
model->sort(0, order);
}
void ConnectionListHelper::Filter(const QString &key)
{
for (const auto &groupId : ConnectionManager->AllGroups())
{
const auto groupItem = model->indexFromItem(groups[groupId]);
bool isTotallyHide = true;
for (const auto &connectionId : ConnectionManager->Connections(groupId))
{
const auto connectionItem = model->indexFromItem(pairs[{ connectionId, groupId }]);
const auto willTotallyHide = static_cast<ConnectionItemWidget *>(parentView->indexWidget(connectionItem))->NameMatched(key);
parentView->setRowHidden(connectionItem.row(), connectionItem.parent(), !willTotallyHide);
isTotallyHide &= willTotallyHide;
}
parentView->indexWidget(groupItem)->setHidden(isTotallyHide);
if (!isTotallyHide)
parentView->expand(groupItem);
}
}
QStandardItem *ConnectionListHelper::addConnectionItem(const ConnectionGroupPair &id)
{
// Create Standard Item
auto connectionItem = new QStandardItem();
connectionItem->setData(GetDisplayName(id.connectionId), ConnectionInfoRole::ROLE_DISPLAYNAME);
connectionItem->setData(NumericString(GetConnectionLatency(id.connectionId)), ConnectionInfoRole::ROLE_LATENCY);
connectionItem->setData(NumericString(GetConnectionTotalData(id.connectionId)), ConnectionInfoRole::ROLE_DATA_USAGE);
//
// Find groups
const auto groupIndex = groups.contains(id.groupId) ? groups[id.groupId] : addGroupItem(id.groupId);
// Append into model
groupIndex->appendRow(connectionItem);
const auto connectionIndex = connectionItem->index();
//
auto widget = new ConnectionItemWidget(id, parentView);
connect(widget, &ConnectionItemWidget::RequestWidgetFocus, [widget, connectionIndex, this]() {
parentView->setCurrentIndex(connectionIndex);
parentView->scrollTo(connectionIndex);
});
//
parentView->setIndexWidget(connectionIndex, widget);
pairs[id] = connectionItem;
connections[id.connectionId].append(connectionItem);
return connectionItem;
}
QStandardItem *ConnectionListHelper::addGroupItem(const GroupId &groupId)
{
// Create Item
const auto item = new QStandardItem();
// Set item into model
model->appendRow(item);
// Get item index
const auto index = item->index();
parentView->setIndexWidget(index, new ConnectionItemWidget(groupId, parentView));
groups[groupId] = item;
return item;
}
void ConnectionListHelper::OnConnectionCreated(const ConnectionGroupPair &id, const QString &)
{
addConnectionItem(id);
}
void ConnectionListHelper::OnConnectionDeleted(const ConnectionGroupPair &id)
{
auto item = pairs.take(id);
const auto index = model->indexFromItem(item);
if (!index.isValid())
return;
model->removeRow(index.row(), index.parent());
connections[id.connectionId].removeAll(item);
}
void ConnectionListHelper::OnConnectionLinkedWithGroup(const ConnectionGroupPair &pairId)
{
addConnectionItem(pairId);
}
void ConnectionListHelper::OnGroupCreated(const GroupId &id, const QString &)
{
addGroupItem(id);
}
void ConnectionListHelper::OnGroupDeleted(const GroupId &id, const QList<ConnectionId> &connections)
{
for (const auto &conn : connections)
{
const ConnectionGroupPair pair{ conn, id };
OnConnectionDeleted(pair);
}
const auto item = groups.take(id);
const auto index = model->indexFromItem(item);
if (!index.isValid())
return;
model->removeRow(index.row(), index.parent());
}

View File

@ -0,0 +1,59 @@
#pragma once
#include "base/Qv2rayBase.hpp"
#include <QStandardItem>
#include <QStandardItemModel>
#include <QTreeView>
namespace Qv2ray::ui::widgets::models
{
enum ConnectionInfoRole
{
// 10 -> Magic value.
ROLE_DISPLAYNAME = Qt::UserRole + 10,
ROLE_LATENCY,
ROLE_IMPORTTIME,
ROLE_LAST_CONNECTED_TIME,
ROLE_DATA_USAGE
};
class ConnectionListHelper : public QObject
{
Q_OBJECT
public:
ConnectionListHelper(QTreeView *parentView, QObject *parent = nullptr);
~ConnectionListHelper();
void Sort(ConnectionInfoRole, Qt::SortOrder);
void Filter(const QString &);
inline QModelIndex GetConnectionPairIndex(const ConnectionGroupPair &id) const
{
return model->indexFromItem(pairs[id]);
}
inline QModelIndex GetGroupIndex(const GroupId &id) const
{
return model->indexFromItem(groups[id]);
}
private:
QStandardItem *addConnectionItem(const ConnectionGroupPair &id);
QStandardItem *addGroupItem(const GroupId &groupId);
void OnGroupCreated(const GroupId &id, const QString &displayName);
void OnGroupDeleted(const GroupId &id, const QList<ConnectionId> &connections);
void OnConnectionCreated(const ConnectionGroupPair &Id, const QString &displayName);
void OnConnectionDeleted(const ConnectionGroupPair &Id);
void OnConnectionLinkedWithGroup(const ConnectionGroupPair &id);
private:
QTreeView *parentView;
QStandardItemModel *model;
//
QHash<GroupId, QStandardItem *> groups;
QHash<ConnectionGroupPair, QStandardItem *> pairs;
QHash<ConnectionId, QList<QStandardItem *>> connections;
};
} // namespace Qv2ray::ui::widgets::models
using namespace Qv2ray::ui::widgets::models;

View File

@ -20,12 +20,13 @@
#include <QScrollBar>
#define TRAY_TOOLTIP_PREFIX "Qv2ray " QV2RAY_VERSION_STRING
#define CheckCurrentWidget \
auto widget = GetItemWidget(connectionListWidget->currentItem()); \
auto widget = GetIndexWidget(connectionTreeView->currentIndex()); \
if (widget == nullptr) \
return;
#define GetItemWidget(item) (qobject_cast<ConnectionItemWidget *>(connectionListWidget->itemWidget(item, 0)))
#define GetIndexWidget(item) (qobject_cast<ConnectionItemWidget *>(connectionTreeView->indexWidget(item)))
#define NumericString(i) (QString("%1").arg(i, 30, 10, QLatin1Char('0')))
#define PLUGIN_BUTTON_PROPERTY_KEY "plugin_list_index"
@ -46,43 +47,9 @@ QvMessageBusSlotImpl(MainWindow)
}
}
void MainWindow::MWAddConnectionItem_p(const ConnectionGroupPair &id)
void MainWindow::SortConnectionList(ConnectionInfoRole byCol, bool asending)
{
if (!groupNodes.contains(id.groupId))
{
MWAddGroupItem_p(id.groupId);
}
auto groupItem = groupNodes.value(id.groupId);
auto connectionItem = std::make_shared<QTreeWidgetItem>(QStringList{
"", //
GetDisplayName(id.connectionId), //
NumericString(GetConnectionLatency(id.connectionId)), //
"IMPORTTIME_NOT_SUPPORTED", //
"LAST_CONNECTED_NOT_SUPPORTED", //
NumericString(GetConnectionTotalData(id.connectionId)) //
});
connectionNodes.insert(id, connectionItem);
groupItem->addChild(connectionItem.get());
auto widget = new ConnectionItemWidget(id, connectionListWidget);
connect(widget, &ConnectionItemWidget::RequestWidgetFocus, this, &MainWindow::OnConnectionWidgetFocusRequested);
connectionListWidget->setItemWidget(connectionItem.get(), 0, widget);
}
void MainWindow::MWAddGroupItem_p(const GroupId &groupId)
{
auto groupItem = std::make_shared<QTreeWidgetItem>(QStringList{ "", GetDisplayName(groupId) });
groupNodes.insert(groupId, groupItem);
connectionListWidget->addTopLevelItem(groupItem.get());
connectionListWidget->setItemWidget(groupItem.get(), 0, new ConnectionItemWidget(groupId, connectionListWidget));
}
void MainWindow::SortConnectionList(MW_ITEM_COL byCol, bool asending)
{
connectionListWidget->sortByColumn(MW_ITEM_COL_NAME, Qt::AscendingOrder);
for (auto i = 0; i < connectionListWidget->topLevelItemCount(); i++)
{
connectionListWidget->topLevelItem(i)->sortChildren(byCol, asending ? Qt::AscendingOrder : Qt::DescendingOrder);
}
modelHelper->Sort(byCol, asending ? Qt::AscendingOrder : Qt::DescendingOrder);
on_locateBtn_clicked();
}
@ -108,8 +75,10 @@ void MainWindow::OnRecentConnectionsMenuReadyToShow()
for (const auto &conn : GlobalConfig.uiConfig.recentConnections)
{
if (ConnectionManager->IsValidId(conn))
tray_RecentConnectionsMenu->addAction(GetDisplayName(conn.connectionId) + " (" + GetDisplayName(conn.groupId) + ")",
[=]() { emit ConnectionManager->StartConnection(conn); });
{
const auto name = GetDisplayName(conn.connectionId) + " (" + GetDisplayName(conn.groupId) + ")";
tray_RecentConnectionsMenu->addAction(name, [=]() { emit ConnectionManager->StartConnection(conn); });
}
}
}
@ -127,6 +96,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
speedChartWidget = new SpeedWidget(this);
speedChart->addWidget(speedChartWidget);
//
modelHelper = new ConnectionListHelper(connectionTreeView);
//
this->setWindowIcon(QIcon(":/assets/icons/qv2ray.png"));
updateColorScheme();
UpdateActionTranslations();
@ -144,34 +115,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
connect(ConnectionManager, &QvConfigHandler::OnStatsAvailable, this, &MainWindow::OnStatsAvailable);
connect(ConnectionManager, &QvConfigHandler::OnKernelLogAvailable, this, &MainWindow::OnVCoreLogAvailable);
//
connect(ConnectionManager, &QvConfigHandler::OnConnectionRemovedFromGroup, this, &MainWindow::OnConnectionDeleted);
connect(ConnectionManager, &QvConfigHandler::OnConnectionCreated, this, &MainWindow::OnConnectionCreated);
connect(ConnectionManager, &QvConfigHandler::OnConnectionLinkedWithGroup, this, &MainWindow::OnConnectionLinkedWithGroup);
//
connect(ConnectionManager, &QvConfigHandler::OnGroupCreated, this, &MainWindow::OnGroupCreated);
connect(ConnectionManager, &QvConfigHandler::OnGroupDeleted, this, &MainWindow::OnGroupDeleted);
//
connect(ConnectionManager, &QvConfigHandler::OnSubscriptionAsyncUpdateFinished, [](const GroupId &gid) {
QvWidgetApplication->ShowTrayMessage(tr("Subscription \"%1\" has been updated").arg(GetDisplayName(gid))); //
});
//
connect(ConnectionManager, &QvConfigHandler::OnConnectionRenamed, [this](const ConnectionId &id, const QString &, const QString &newName) {
for (const auto &gid : ConnectionManager->GetGroupId(id))
{
ConnectionGroupPair pair{ id, gid };
if (connectionNodes.contains(pair))
connectionNodes.value(pair)->setText(MW_ITEM_COL_NAME, newName);
}
});
connect(ConnectionManager, &QvConfigHandler::OnLatencyTestFinished, [this](const ConnectionId &id, const int avg) {
for (const auto &gid : ConnectionManager->GetGroupId(id))
{
ConnectionGroupPair pair{ id, gid };
if (connectionNodes.contains(pair))
connectionNodes.value(pair)->setText(MW_ITEM_COL_PING, NumericString(avg)); //
}
});
//
connect(infoWidget, &ConnectionInfoWidget::OnEditRequested, this, &MainWindow::OnEditRequested);
connect(infoWidget, &ConnectionInfoWidget::OnJsonEditRequested, this, &MainWindow::OnEditJsonRequested);
//
@ -209,7 +156,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
//
connect(tray_action_ToggleVisibility, &QAction::triggered, this, &MainWindow::MWToggleVisibility);
connect(tray_action_Preferences, &QAction::triggered, this, &MainWindow::on_preferencesBtn_clicked);
connect(tray_action_Start, &QAction::triggered, [this] { ConnectionManager->StartConnection(lastConnectedIdentifier); });
connect(tray_action_Start, &QAction::triggered, [this] { ConnectionManager->StartConnection(lastConnected); });
connect(tray_action_Stop, &QAction::triggered, ConnectionManager, &QvConfigHandler::StopConnection);
connect(tray_action_Restart, &QAction::triggered, ConnectionManager, &QvConfigHandler::RestartConnection);
connect(tray_action_Quit, &QAction::triggered, this, &MainWindow::Action_Exit);
@ -300,39 +247,38 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
sortMenu->addAction(sortAction_SortByPing_Asc);
sortMenu->addAction(sortAction_SortByPing_Dsc);
//
connect(sortAction_SortByName_Asc, &QAction::triggered, [this] { SortConnectionList(MW_ITEM_COL_NAME, true); });
connect(sortAction_SortByName_Dsc, &QAction::triggered, [this] { SortConnectionList(MW_ITEM_COL_NAME, false); });
connect(sortAction_SortByData_Asc, &QAction::triggered, [this] { SortConnectionList(MW_ITEM_COL_DATA, true); });
connect(sortAction_SortByData_Dsc, &QAction::triggered, [this] { SortConnectionList(MW_ITEM_COL_DATA, false); });
connect(sortAction_SortByPing_Asc, &QAction::triggered, [this] { SortConnectionList(MW_ITEM_COL_PING, true); });
connect(sortAction_SortByPing_Dsc, &QAction::triggered, [this] { SortConnectionList(MW_ITEM_COL_PING, false); });
connect(sortAction_SortByName_Asc, &QAction::triggered, [this] { SortConnectionList(ROLE_DISPLAYNAME, true); });
connect(sortAction_SortByName_Dsc, &QAction::triggered, [this] { SortConnectionList(ROLE_DISPLAYNAME, false); });
connect(sortAction_SortByData_Asc, &QAction::triggered, [this] { SortConnectionList(ROLE_DATA_USAGE, true); });
connect(sortAction_SortByData_Dsc, &QAction::triggered, [this] { SortConnectionList(ROLE_DATA_USAGE, false); });
connect(sortAction_SortByPing_Asc, &QAction::triggered, [this] { SortConnectionList(ROLE_LATENCY, true); });
connect(sortAction_SortByPing_Dsc, &QAction::triggered, [this] { SortConnectionList(ROLE_LATENCY, false); });
//
sortBtn->setMenu(sortMenu);
//
graphWidgetMenu->addAction(action_RCM_CopyGraph);
connect(action_RCM_CopyGraph, &QAction::triggered, this, &MainWindow::Action_CopyGraphAsImage);
//
LOG(MODULE_UI, "Loading data...")
for (const auto &group : ConnectionManager->AllGroups())
{
MWAddGroupItem_p(group);
for (const auto &connection : ConnectionManager->Connections(group)) MWAddConnectionItem_p({ connection, group });
}
//
// Find and start if there is an auto-connection
const auto connectionStarted = StartAutoConnectionEntry();
if (!connectionStarted && connectionListWidget->topLevelItemCount() > 0)
if (!connectionStarted && !ConnectionManager->Connections().isEmpty())
{
ReloadRecentConnectionList();
// Select the first connection.
const auto &topLevelItem = connectionListWidget->topLevelItem(0);
const auto &item = (topLevelItem->childCount() > 0) ? topLevelItem->child(0) : topLevelItem;
connectionListWidget->setCurrentItem(item);
on_connectionListWidget_itemClicked(item, 0);
const auto groups = ConnectionManager->AllGroups();
if (!groups.isEmpty())
{
const auto connections = ConnectionManager->Connections(groups.first());
if (!connections.empty())
{
const auto index = modelHelper->GetConnectionPairIndex({ connections.first(), groups.first() });
on_connectionTreeView_clicked(index);
}
}
}
ReloadRecentConnectionList();
//
//
tray_action_ToggleVisibility->setText(!connectionStarted ? tr("Hide") : tr("Show"));
if (!connectionStarted)
MWShowWindow();
else
@ -423,7 +369,7 @@ void MainWindow::timerEvent(QTimerEvent *event)
void MainWindow::keyPressEvent(QKeyEvent *e)
{
if (focusWidget() == connectionListWidget)
if (focusWidget() == connectionTreeView)
{
CheckCurrentWidget;
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return)
@ -435,7 +381,7 @@ void MainWindow::keyPressEvent(QKeyEvent *e)
}
else
{
connectionListWidget->expandItem(connectionListWidget->currentItem());
connectionTreeView->expand(connectionTreeView->currentIndex());
}
}
else if (e->key() == Qt::Key_F2)
@ -450,7 +396,7 @@ void MainWindow::keyPressEvent(QKeyEvent *e)
if (e->key() == Qt::Key_Escape)
{
auto widget = GetItemWidget(connectionListWidget->currentItem());
auto widget = GetIndexWidget(connectionTreeView->currentIndex());
// Check if this key was accpted by the ConnectionItemWidget
if (widget && widget->IsRenaming())
{
@ -481,10 +427,10 @@ void MainWindow::keyReleaseEvent(QKeyEvent *e)
// Workaround of QtWidget not grabbing KeyDown and KeyUp in keyPressEvent
if (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down)
{
if (focusWidget() == connectionListWidget)
if (focusWidget() == connectionTreeView)
{
CheckCurrentWidget;
on_connectionListWidget_itemClicked(connectionListWidget->currentItem(), 0);
on_connectionTreeView_clicked(connectionTreeView->currentIndex());
}
}
}
@ -500,6 +446,7 @@ void MainWindow::Action_Start()
MainWindow::~MainWindow()
{
delete modelHelper;
for (auto &widget : pluginWidgets) widget->accept();
}
@ -534,14 +481,14 @@ void MainWindow::on_clearlogButton_clicked()
{
masterLogBrowser->document()->clear();
}
void MainWindow::on_connectionListWidget_customContextMenuRequested(const QPoint &pos)
void MainWindow::on_connectionTreeView_customContextMenuRequested(const QPoint &pos)
{
Q_UNUSED(pos)
auto _pos = QCursor::pos();
auto item = connectionListWidget->itemAt(connectionListWidget->mapFromGlobal(_pos));
if (item != nullptr)
auto item = connectionTreeView->indexAt(connectionTreeView->mapFromGlobal(_pos));
if (item.isValid())
{
bool isConnection = GetItemWidget(item)->IsConnection();
bool isConnection = GetIndexWidget(item)->IsConnection();
// Disable connection-specific settings.
action_RCM_Start->setEnabled(isConnection);
action_RCM_SetAutoConnection->setEnabled(isConnection);
@ -551,7 +498,7 @@ void MainWindow::on_connectionListWidget_customContextMenuRequested(const QPoint
action_RCM_RenameConnection->setEnabled(isConnection);
action_RCM_DuplicateConnection->setEnabled(isConnection);
action_RCM_UpdateSubscription->setEnabled(!isConnection);
action_RCM_RealLatencyTest->setEnabled(isConnection && ConnectionManager->IsConnected(GetItemWidget(item)->Identifier()));
action_RCM_RealLatencyTest->setEnabled(isConnection && ConnectionManager->IsConnected(GetIndexWidget(item)->Identifier()));
connectionListRCM_Menu->popup(_pos);
}
}
@ -560,9 +507,9 @@ void MainWindow::Action_DeleteConnections()
{
QList<ConnectionGroupPair> connlist;
for (const auto &item : connectionListWidget->selectedItems())
for (const auto &item : connectionTreeView->selectionModel()->selectedIndexes())
{
auto widget = GetItemWidget(item);
auto widget = GetIndexWidget(item);
if (widget)
{
const auto identifier = widget->Identifier();
@ -640,18 +587,6 @@ void MainWindow::on_subsButton_clicked()
GroupManager().exec();
}
void MainWindow::on_connectionListWidget_itemDoubleClicked(QTreeWidgetItem *item, int column)
{
Q_UNUSED(column)
auto widget = GetItemWidget(item);
if (widget == nullptr)
return;
if (widget->IsConnection())
{
widget->BeginConnection();
}
}
void MainWindow::OnDisconnected(const ConnectionGroupPair &id)
{
Q_UNUSED(id)
@ -660,7 +595,7 @@ void MainWindow::OnDisconnected(const ConnectionGroupPair &id)
tray_action_Stop->setEnabled(false);
tray_action_Restart->setEnabled(false);
tray_SystemProxyMenu->setEnabled(false);
lastConnectedIdentifier = id;
lastConnected = id;
locateBtn->setEnabled(false);
if (!GlobalConfig.uiConfig.quietMode)
{
@ -684,7 +619,7 @@ void MainWindow::OnConnected(const ConnectionGroupPair &id)
tray_action_Stop->setEnabled(true);
tray_action_Restart->setEnabled(true);
tray_SystemProxyMenu->setEnabled(true);
lastConnectedIdentifier = id;
lastConnected = id;
locateBtn->setEnabled(true);
on_clearlogButton_clicked();
speedChartWidget->Clear();
@ -710,68 +645,9 @@ void MainWindow::OnConnected(const ConnectionGroupPair &id)
}
}
void MainWindow::OnConnectionWidgetFocusRequested(const ConnectionItemWidget *_widget)
{
if (_widget == nullptr)
{
return;
}
for (auto _item_ : connectionListWidget->findItems(QString("*"), Qt::MatchWrap | Qt::MatchWildcard | Qt::MatchRecursive))
{
if (GetItemWidget(_item_) == _widget)
{
LOG(MODULE_UI, "Setting current item.")
connectionListWidget->setCurrentItem(_item_);
connectionListWidget->scrollToItem(_item_);
// Click it to show details.
on_connectionListWidget_itemClicked(_item_, 0);
}
}
}
void MainWindow::on_connectionFilterTxt_textEdited(const QString &arg1)
{
// No recursive since we only need top level item
for (auto _top_item_ : connectionListWidget->findItems(QString("*"), Qt::MatchWrap | Qt::MatchWildcard))
{
// auto topWidget = GetItemWidget(_top_item_);
bool isTotallyHide = true;
for (auto i = 0; i < _top_item_->childCount(); i++)
{
auto _child_ = _top_item_->child(i);
if (GetItemWidget(_child_)->NameMatched(arg1))
{
LOG(MODULE_UI, "Setting current item.")
// Show the child
_child_->setHidden(false);
// If any one of the children matches, the parent should not be hidden.
isTotallyHide = false;
}
else
{
_child_->setHidden(true);
}
}
_top_item_->setHidden(isTotallyHide);
if (!isTotallyHide)
{
connectionListWidget->expandItem(_top_item_);
}
}
}
void MainWindow::on_connectionListWidget_itemClicked(QTreeWidgetItem *item, int column)
{
Q_UNUSED(column)
auto widget = GetItemWidget(item);
if (widget == nullptr)
return;
infoWidget->ShowDetails(widget->Identifier());
modelHelper->Filter(arg1);
}
void MainWindow::OnStatsAvailable(const ConnectionGroupPair &id, const QMap<StatisticsType, QvStatsSpeedData> &data)
@ -781,13 +657,13 @@ void MainWindow::OnStatsAvailable(const ConnectionGroupPair &id, const QMap<Stat
// This may not be, or may not precisely be, speed per second if the backend
// has "any" latency. (Hope not...)
//
QMap<SpeedWidget::GraphID, long> pointData;
QMap<SpeedWidget::GraphType, long> pointData;
bool isOutbound = GlobalConfig.uiConfig.graphConfig.useOutboundStats;
bool hasDirect = isOutbound && GlobalConfig.uiConfig.graphConfig.hasDirectStats;
for (const auto &type : data.keys())
for (const auto &[type, data] : data.toStdMap())
{
const auto upSpeed = data[type].first.first;
const auto downSpeed = data[type].first.second;
const auto upSpeed = data.first.first;
const auto downSpeed = data.first.second;
switch (type)
{
case API_INBOUND:
@ -829,12 +705,6 @@ void MainWindow::OnStatsAvailable(const ConnectionGroupPair &id, const QMap<Stat
//
qvAppTrayIcon->setToolTip(TRAY_TOOLTIP_PREFIX NEWLINE + tr("Connected: ") + GetDisplayName(id.connectionId) + //
NEWLINE "Up: " + totalSpeedUp + " Down: " + totalSpeedDown);
//
// Set data accordingly
if (connectionNodes.contains(id))
{
connectionNodes.value(id)->setText(MW_ITEM_COL_DATA, NumericString(GetConnectionTotalData(id.connectionId)));
}
}
void MainWindow::OnVCoreLogAvailable(const ConnectionGroupPair &id, const QString &log)
@ -903,34 +773,6 @@ void MainWindow::OnEditJsonRequested(const ConnectionId &id)
}
}
void MainWindow::OnConnectionCreated(const ConnectionGroupPair &id, const QString &displayName)
{
Q_UNUSED(displayName)
MWAddConnectionItem_p(id);
}
void MainWindow::OnConnectionDeleted(const ConnectionGroupPair &id)
{
auto child = connectionNodes.take(id);
groupNodes.value(id.groupId)->removeChild(child.get());
}
void MainWindow::OnConnectionLinkedWithGroup(const ConnectionGroupPair &pairId)
{
MWAddConnectionItem_p(pairId);
}
void MainWindow::OnGroupCreated(const GroupId &id, const QString &displayName)
{
Q_UNUSED(displayName)
MWAddGroupItem_p(id);
}
void MainWindow::OnGroupDeleted(const GroupId &id, const QList<ConnectionId> &connections)
{
for (const auto &conn : connections)
{
groupNodes.value(id)->removeChild(connectionNodes.value({ conn, id }).get());
}
groupNodes.remove(id);
}
void MainWindow::OnLogScrollbarValueChanged(int value)
{
if (masterLogBrowser->verticalScrollBar()->maximum() == value)
@ -944,9 +786,10 @@ void MainWindow::on_locateBtn_clicked()
auto id = KernelInstance->CurrentConnection();
if (!id.isEmpty())
{
connectionListWidget->setCurrentItem(connectionNodes.value(id).get());
connectionListWidget->scrollToItem(connectionNodes.value(id).get());
on_connectionListWidget_itemClicked(connectionNodes.value(id).get(), 0);
const auto index = modelHelper->GetConnectionPairIndex(id);
connectionTreeView->setCurrentIndex(index);
connectionTreeView->scrollTo(index);
on_connectionTreeView_clicked(index);
}
}
@ -960,9 +803,9 @@ void MainWindow::Action_DuplicateConnection()
{
QList<ConnectionGroupPair> connlist;
for (const auto &item : connectionListWidget->selectedItems())
for (const auto &item : connectionTreeView->selectionModel()->selectedIndexes())
{
auto widget = GetItemWidget(item);
auto widget = GetIndexWidget(item);
if (widget->IsConnection())
{
connlist.append(widget->Identifier());
@ -1012,14 +855,14 @@ void MainWindow::on_clearChartBtn_clicked()
speedChartWidget->Clear();
}
void MainWindow::on_connectionListWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
{
Q_UNUSED(previous)
if (current != nullptr && !isExiting)
{
on_connectionListWidget_itemClicked(current, 0);
}
}
// void MainWindow::on_connectionListWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
//{
// Q_UNUSED(previous)
// if (current != nullptr && !isExiting)
// {
// on_connectionListWidget_itemClicked(current, 0);
// }
//}
void MainWindow::on_masterLogBrowser_textChanged()
{
@ -1031,10 +874,10 @@ void MainWindow::on_masterLogBrowser_textChanged()
void MainWindow::Action_SetAutoConnection()
{
auto current = connectionListWidget->currentItem();
if (current != nullptr)
auto current = connectionTreeView->currentIndex();
if (current.isValid())
{
auto widget = GetItemWidget(current);
auto widget = GetIndexWidget(current);
const auto identifier = widget->Identifier();
GlobalConfig.autoStartId = identifier;
GlobalConfig.autoStartBehavior = AUTO_CONNECTION_FIXED;
@ -1048,10 +891,10 @@ void MainWindow::Action_SetAutoConnection()
void MainWindow::Action_ResetStats()
{
auto current = connectionListWidget->currentItem();
if (current != nullptr)
auto current = connectionTreeView->currentIndex();
if (current.isValid())
{
auto widget = GetItemWidget(current);
auto widget = GetIndexWidget(current);
if (widget)
{
if (widget->IsConnection())
@ -1064,10 +907,10 @@ void MainWindow::Action_ResetStats()
void MainWindow::Action_UpdateSubscription()
{
auto current = connectionListWidget->currentItem();
if (current != nullptr)
auto current = connectionTreeView->currentIndex();
if (current.isValid())
{
auto widget = GetItemWidget(current);
auto widget = GetIndexWidget(current);
if (widget)
{
if (widget->IsConnection())
@ -1083,11 +926,11 @@ void MainWindow::Action_UpdateSubscription()
void MainWindow::Action_TestLatency()
{
for (const auto &current : connectionListWidget->selectedItems())
for (const auto &current : connectionTreeView->selectionModel()->selectedIndexes())
{
if (!current)
if (!current.isValid())
continue;
const auto widget = GetItemWidget(current);
const auto widget = GetIndexWidget(current);
if (!widget)
continue;
if (widget->IsConnection())
@ -1099,11 +942,11 @@ void MainWindow::Action_TestLatency()
void MainWindow::Action_TestRealLatency()
{
for (const auto &current : connectionListWidget->selectedItems())
for (const auto &current : connectionTreeView->selectionModel()->selectedIndexes())
{
if (!current)
if (!current.isValid())
continue;
const auto widget = GetItemWidget(current);
const auto widget = GetIndexWidget(current);
if (!widget)
continue;
if (widget->IsConnection())
@ -1134,8 +977,8 @@ void MainWindow::on_newConnectionBtn_clicked()
outboundsList.push_back(outboundEntry);
CONFIGROOT root;
root.insert("outbounds", outboundsList);
const auto item = connectionListWidget->currentItem();
const auto id = item ? DefaultGroupId : GetItemWidget(item)->Identifier().groupId;
const auto item = connectionTreeView->currentIndex();
const auto id = item.isValid() ? DefaultGroupId : GetIndexWidget(item)->Identifier().groupId;
ConnectionManager->CreateConnection(root, alias, id);
}
}
@ -1147,15 +990,15 @@ void MainWindow::on_newComplexConnectionBtn_clicked()
bool isChanged = w.result() == QDialog::Accepted;
if (isChanged)
{
const auto item = connectionListWidget->currentItem();
const auto id = item ? DefaultGroupId : GetItemWidget(item)->Identifier().groupId;
const auto item = connectionTreeView->currentIndex();
const auto id = item.isValid() ? DefaultGroupId : GetIndexWidget(item)->Identifier().groupId;
ConnectionManager->CreateConnection(root, QJsonIO::GetValue(root, "outbounds", 0, "tag").toString(), id);
}
}
void MainWindow::on_collapseGroupsBtn_clicked()
{
connectionListWidget->collapseAll();
connectionTreeView->collapseAll();
}
void MainWindow::Action_CopyRecentLogs()
@ -1174,3 +1017,20 @@ void MainWindow::Action_CopyRecentLogs()
}
qApp->clipboard()->setText(result.join(NEWLINE));
}
void MainWindow::on_connectionTreeView_doubleClicked(const QModelIndex &index)
{
auto widget = GetIndexWidget(index);
if (widget == nullptr)
return;
if (widget->IsConnection())
widget->BeginConnection();
}
void MainWindow::on_connectionTreeView_clicked(const QModelIndex &index)
{
auto widget = GetIndexWidget(index);
if (widget == nullptr)
return;
infoWidget->ShowDetails(widget->Identifier());
}

View File

@ -3,6 +3,7 @@
#include "ui/common/QvMessageBus.hpp"
#include "ui/common/speedchart/speedwidget.hpp"
#include "ui/widgets/common/WidgetUIBase.hpp"
#include "ui/widgets/models/ConnectionModelHelper.hpp"
#include "ui/widgets/widgets/ConnectionInfoWidget.hpp"
#include "ui/widgets/widgets/ConnectionItemWidget.hpp"
#include "ui_w_MainWindow.h"
@ -17,15 +18,6 @@ namespace Qv2rayPlugin
class QvPluginMainWindowWidget;
}
enum MW_ITEM_COL
{
MW_ITEM_COL_NAME = 1,
MW_ITEM_COL_PING = 2,
MW_ITEM_COL_IMPORTTIME = 3,
MW_ITEM_COL_LASTCONNETED = 4,
MW_ITEM_COL_DATA = 5
};
class MainWindow
: public QMainWindow
, Ui::MainWindow
@ -47,25 +39,29 @@ class MainWindow
void on_activatedTray(QSystemTrayIcon::ActivationReason reason);
void on_preferencesBtn_clicked();
void on_clearlogButton_clicked();
void on_connectionListWidget_customContextMenuRequested(const QPoint &pos);
void on_connectionTreeView_customContextMenuRequested(const QPoint &pos);
void on_importConfigButton_clicked();
void on_subsButton_clicked();
//
void on_connectionListWidget_itemDoubleClicked(QTreeWidgetItem *item, int column);
void on_connectionFilterTxt_textEdited(const QString &arg1);
void on_connectionListWidget_itemClicked(QTreeWidgetItem *item, int column);
void on_locateBtn_clicked();
//
void on_chartVisibilityBtn_clicked();
void on_logVisibilityBtn_clicked();
void on_clearChartBtn_clicked();
void on_connectionListWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void on_masterLogBrowser_textChanged();
//
void on_pluginsBtn_clicked();
void on_collapseGroupsBtn_clicked();
void on_newConnectionBtn_clicked();
void on_newComplexConnectionBtn_clicked();
//
// void on_connectionListWidget_itemDoubleClicked(QTreeWidgetItem *item, int column);
// void on_connectionListWidget_itemClicked(QTreeWidgetItem *item, int column);
// void on_connectionListWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void on_connectionTreeView_doubleClicked(const QModelIndex &index);
void on_connectionTreeView_clicked(const QModelIndex &index);
private:
// Do not declare as slots, we connect them manually.
@ -86,8 +82,6 @@ class MainWindow
void Action_CopyGraphAsImage();
void Action_CopyRecentLogs();
void OnConnectionWidgetFocusRequested(const ConnectionItemWidget *widget);
private:
void MWToggleVisibility();
void OnEditRequested(const ConnectionId &id);
@ -98,14 +92,7 @@ class MainWindow
void OnStatsAvailable(const ConnectionGroupPair &id, const QMap<StatisticsType, QvStatsSpeedData> &data);
void OnVCoreLogAvailable(const ConnectionGroupPair &id, const QString &log);
//
void OnConnectionCreated(const ConnectionGroupPair &Id, const QString &displayName);
void OnConnectionDeleted(const ConnectionGroupPair &Id);
void OnConnectionLinkedWithGroup(const ConnectionGroupPair &id);
//
void OnGroupCreated(const GroupId &id, const QString &displayName);
void OnGroupDeleted(const GroupId &id, const QList<ConnectionId> &connections);
//
void SortConnectionList(MW_ITEM_COL byCol, bool asending);
void SortConnectionList(ConnectionInfoRole byCol, bool asending);
//
void ReloadRecentConnectionList();
void OnRecentConnectionsMenuReadyToShow();
@ -122,8 +109,6 @@ class MainWindow
void closeEvent(QCloseEvent *) override;
private:
QHash<GroupId, std::shared_ptr<QTreeWidgetItem>> groupNodes;
QHash<ConnectionGroupPair, std::shared_ptr<QTreeWidgetItem>> connectionNodes;
// Charts
SpeedWidget *speedChartWidget;
SyntaxHighlighter *vCoreLogHighlighter;
@ -179,7 +164,7 @@ class MainWindow
int qvLogTimerId = -1;
bool qvLogAutoScoll = true;
//
ConnectionGroupPair lastConnectedIdentifier;
ConnectionGroupPair lastConnected;
void MWSetSystemProxy();
void MWClearSystemProxy();
void MWShowWindow();
@ -189,8 +174,7 @@ class MainWindow
//
void updateColorScheme();
//
void MWAddConnectionItem_p(const ConnectionGroupPair &id);
void MWAddGroupItem_p(const GroupId &groupId);
//
QList<Qv2rayPlugin::QvPluginMainWindowWidget *> pluginWidgets;
//
Qv2ray::ui::widgets::models::ConnectionListHelper *modelHelper;
};

View File

@ -135,7 +135,7 @@
</layout>
</item>
<item>
<widget class="QTreeWidget" name="connectionListWidget">
<widget class="QTreeView" name="connectionTreeView">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
@ -154,26 +154,12 @@
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="animated">
<bool>true</bool>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
<property name="headerHidden">
<bool>true</bool>
</property>
<property name="expandsOnDoubleClick">
<bool>true</bool>
</property>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
<item>
@ -568,7 +554,6 @@
</widget>
<layoutdefault spacing="6" margin="11"/>
<tabstops>
<tabstop>connectionListWidget</tabstop>
<tabstop>importConfigButton</tabstop>
</tabstops>
<resources>