[Move refactor] Reparent orphan classes (#6236)

* Move orphaned classes to their correct parent folders.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2025-10-09 14:15:19 +02:00 • committed by GitHub
parent 1ef07309d6
commit d9c65d4ae0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
143 changed files with 171 additions and 169 deletions

View file

@ -0,0 +1,368 @@
#include "remote_decklist_tree_widget.h"
#include <QFileIconProvider>
#include <QHeaderView>
#include <QSortFilterProxyModel>
#include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/pb/command_deck_list.pb.h>
#include <libcockatrice/protocol/pb/response_deck_list.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_deckstorage.pb.h>
#include <libcockatrice/protocol/pending_command.h>
RemoteDeckList_TreeModel::DirectoryNode::DirectoryNode(const QString &_name,
RemoteDeckList_TreeModel::DirectoryNode *_parent)
: RemoteDeckList_TreeModel::Node(_name, _parent)
{
}
RemoteDeckList_TreeModel::DirectoryNode::~DirectoryNode()
{
clearTree();
}
void RemoteDeckList_TreeModel::DirectoryNode::clearTree()
{
for (int i = 0; i < size(); ++i)
delete at(i);
clear();
}
QString RemoteDeckList_TreeModel::DirectoryNode::getPath() const
{
if (parent) {
QString parentPath = parent->getPath();
if (parentPath.isEmpty())
return name;
else
return parentPath + "/" + name;
} else
return name;
}
RemoteDeckList_TreeModel::DirectoryNode *RemoteDeckList_TreeModel::DirectoryNode::getNodeByPath(QStringList path)
{
QString pathItem;
if (parent) {
if (path.isEmpty())
return this;
pathItem = path.takeFirst();
if (pathItem.isEmpty() && name.isEmpty())
return this;
}
for (int i = 0; i < size(); ++i) {
DirectoryNode *node = dynamic_cast<DirectoryNode *>(at(i));
if (!node)
continue;
if (node->getName() == pathItem)
return node->getNodeByPath(path);
}
return 0;
}
RemoteDeckList_TreeModel::FileNode *RemoteDeckList_TreeModel::DirectoryNode::getNodeById(int id) const
{
for (int i = 0; i < size(); ++i) {
DirectoryNode *node = dynamic_cast<DirectoryNode *>(at(i));
if (node) {
FileNode *result = node->getNodeById(id);
if (result)
return result;
} else {
FileNode *file = dynamic_cast<FileNode *>(at(i));
if (file->getId() == id)
return file;
}
}
return 0;
}
RemoteDeckList_TreeModel::RemoteDeckList_TreeModel(AbstractClient *_client, QObject *parent)
: QAbstractItemModel(parent), client(_client)
{
QFileIconProvider fip;
dirIcon = fip.icon(QFileIconProvider::Folder);
fileIcon = fip.icon(QFileIconProvider::File);
root = new DirectoryNode;
}
RemoteDeckList_TreeModel::~RemoteDeckList_TreeModel()
{
delete root;
}
int RemoteDeckList_TreeModel::rowCount(const QModelIndex &parent) const
{
DirectoryNode *node = getNode<DirectoryNode *>(parent);
if (node)
return node->size();
else
return 0;
}
int RemoteDeckList_TreeModel::columnCount(const QModelIndex & /*parent*/) const
{
return 3;
}
QVariant RemoteDeckList_TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.column() >= 3)
return QVariant();
Node *temp = static_cast<Node *>(index.internalPointer());
FileNode *file = dynamic_cast<FileNode *>(temp);
if (!file) {
DirectoryNode *node = dynamic_cast<DirectoryNode *>(temp);
switch (role) {
case Qt::DisplayRole: {
switch (index.column()) {
case 0:
return node->getName();
default:
return QVariant();
}
}
case Qt::DecorationRole:
return index.column() == 0 ? dirIcon : QVariant();
default:
return QVariant();
}
} else {
switch (role) {
case Qt::DisplayRole: {
switch (index.column()) {
case 0:
return file->getName();
case 1:
return file->getId();
case 2:
return file->getUploadTime();
default:
return QVariant();
}
}
case Qt::DecorationRole:
return index.column() == 0 ? fileIcon : QVariant();
case Qt::TextAlignmentRole:
return index.column() == 1 ? Qt::AlignRight : Qt::AlignLeft;
default:
return QVariant();
}
}
}
QVariant RemoteDeckList_TreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation != Qt::Horizontal)
return QVariant();
switch (role) {
case Qt::TextAlignmentRole:
return section == 1 ? Qt::AlignRight : Qt::AlignLeft;
case Qt::DisplayRole: {
switch (section) {
case 0:
return tr("Name");
case 1:
return tr("ID");
case 2:
return tr("Upload time");
default:
return QVariant();
}
}
default:
return QVariant();
}
}
QModelIndex RemoteDeckList_TreeModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
DirectoryNode *parentNode = getNode<DirectoryNode *>(parent);
if (row >= parentNode->size())
return QModelIndex();
return createIndex(row, column, parentNode->at(row));
}
QModelIndex RemoteDeckList_TreeModel::parent(const QModelIndex &ind) const
{
if (!ind.isValid())
return QModelIndex();
return nodeToIndex(static_cast<Node *>(ind.internalPointer())->getParent());
}
Qt::ItemFlags RemoteDeckList_TreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid()) {
return Qt::NoItemFlags;
}
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
QModelIndex RemoteDeckList_TreeModel::nodeToIndex(Node *node) const
{
if (node == nullptr || node == root)
return QModelIndex();
return createIndex(node->getParent()->indexOf(node), 0, node);
}
void RemoteDeckList_TreeModel::addFileToTree(const ServerInfo_DeckStorage_TreeItem &file, DirectoryNode *parent)
{
const ServerInfo_DeckStorage_File &fileInfo = file.file();
QDateTime time;
time.setSecsSinceEpoch(fileInfo.creation_time());
beginInsertRows(nodeToIndex(parent), parent->size(), parent->size());
parent->append(new FileNode(QString::fromStdString(file.name()), file.id(), time, parent));
endInsertRows();
}
void RemoteDeckList_TreeModel::addFolderToTree(const ServerInfo_DeckStorage_TreeItem &folder, DirectoryNode *parent)
{
DirectoryNode *newItem = addNamedFolderToTree(QString::fromStdString(folder.name()), parent);
const ServerInfo_DeckStorage_Folder &folderInfo = folder.folder();
const int folderItemsSize = folderInfo.items_size();
for (int i = 0; i < folderItemsSize; ++i) {
const ServerInfo_DeckStorage_TreeItem &subItem = folderInfo.items(i);
if (subItem.has_folder())
addFolderToTree(subItem, newItem);
else
addFileToTree(subItem, newItem);
}
}
RemoteDeckList_TreeModel::DirectoryNode *RemoteDeckList_TreeModel::addNamedFolderToTree(const QString &name,
DirectoryNode *parent)
{
DirectoryNode *newItem = new DirectoryNode(name, parent);
beginInsertRows(nodeToIndex(parent), parent->size(), parent->size());
parent->append(newItem);
endInsertRows();
return newItem;
}
void RemoteDeckList_TreeModel::removeNode(RemoteDeckList_TreeModel::Node *node)
{
int ind = node->getParent()->indexOf(node);
beginRemoveRows(nodeToIndex(node->getParent()), ind, ind);
node->getParent()->removeAt(ind);
endRemoveRows();
delete node;
}
void RemoteDeckList_TreeModel::refreshTree()
{
PendingCommand *pend = client->prepareSessionCommand(Command_DeckList());
connect(pend, &PendingCommand::finished, this, &RemoteDeckList_TreeModel::deckListFinished);
client->sendCommand(pend);
}
void RemoteDeckList_TreeModel::clearTree()
{
beginResetModel();
root->clearTree();
endResetModel();
}
void RemoteDeckList_TreeModel::deckListFinished(const Response &r)
{
const Response_DeckList &resp = r.GetExtension(Response_DeckList::ext);
clearTree();
ServerInfo_DeckStorage_TreeItem tempRoot;
tempRoot.set_id(0);
tempRoot.mutable_folder()->CopyFrom(resp.root());
addFolderToTree(tempRoot, root);
emit treeRefreshed();
}
RemoteDeckList_TreeWidget::RemoteDeckList_TreeWidget(AbstractClient *_client, QWidget *parent) : QTreeView(parent)
{
treeModel = new RemoteDeckList_TreeModel(_client, this);
proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(treeModel);
proxyModel->setDynamicSortFilter(true);
proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
setModel(proxyModel);
connect(treeModel, &RemoteDeckList_TreeModel::treeRefreshed, this, &RemoteDeckList_TreeWidget::expandAll);
header()->setSectionResizeMode(QHeaderView::ResizeToContents);
setUniformRowHeights(true);
setSelectionMode(QAbstractItemView::ExtendedSelection);
setSortingEnabled(true);
proxyModel->sort(0, Qt::AscendingOrder);
header()->setSortIndicator(0, Qt::AscendingOrder);
}
RemoteDeckList_TreeModel::Node *RemoteDeckList_TreeWidget::getNode(const QModelIndex &ind) const
{
return treeModel->getNode<RemoteDeckList_TreeModel::Node *>(proxyModel->mapToSource(ind));
}
RemoteDeckList_TreeModel::Node *RemoteDeckList_TreeWidget::getCurrentItem() const
{
return getNode(selectionModel()->currentIndex());
}
QList<RemoteDeckList_TreeModel::Node *> RemoteDeckList_TreeWidget::getCurrentSelection() const
{
auto list = QList<RemoteDeckList_TreeModel::Node *>();
for (const auto &row : selectionModel()->selectedRows()) {
list << getNode(row);
}
return list;
}
RemoteDeckList_TreeModel::DirectoryNode *RemoteDeckList_TreeWidget::getNodeByPath(const QString &path) const
{
return treeModel->getRoot()->getNodeByPath(path.split("/"));
}
RemoteDeckList_TreeModel::FileNode *RemoteDeckList_TreeWidget::getNodeById(int id) const
{
return treeModel->getRoot()->getNodeById(id);
}
void RemoteDeckList_TreeWidget::addFileToTree(const ServerInfo_DeckStorage_TreeItem &file,
RemoteDeckList_TreeModel::DirectoryNode *parent)
{
treeModel->addFileToTree(file, parent);
}
void RemoteDeckList_TreeWidget::addFolderToTree(const ServerInfo_DeckStorage_TreeItem &folder,
RemoteDeckList_TreeModel::DirectoryNode *parent)
{
treeModel->addFolderToTree(folder, parent);
}
void RemoteDeckList_TreeWidget::addFolderToTree(const QString &name, RemoteDeckList_TreeModel::DirectoryNode *parent)
{
treeModel->addNamedFolderToTree(name, parent);
}
void RemoteDeckList_TreeWidget::removeNode(RemoteDeckList_TreeModel::Node *node)
{
treeModel->removeNode(node);
}
void RemoteDeckList_TreeWidget::refreshTree()
{
treeModel->refreshTree();
}
void RemoteDeckList_TreeWidget::clearTree()
{
treeModel->clearTree();
}

View file

@ -0,0 +1,141 @@
/**
* @file remote_decklist_tree_widget.h
* @ingroup NetworkingWidgets
* @ingroup DeckStorageWidgets
* @brief TODO: Document this.
*/
#ifndef REMOTEDECKLIST_TREEWIDGET_H
#define REMOTEDECKLIST_TREEWIDGET_H
#include <QAbstractItemModel>
#include <QDateTime>
#include <QTreeView>
class Response;
class AbstractClient;
class QSortFilterProxyModel;
class ServerInfo_DeckStorage_TreeItem;
class RemoteDeckList_TreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
class DirectoryNode;
class FileNode;
class Node
{
protected:
DirectoryNode *parent;
QString name;
public:
explicit Node(const QString &_name, DirectoryNode *_parent = nullptr) : parent(_parent), name(_name)
{
}
virtual ~Node() = default;
DirectoryNode *getParent() const
{
return parent;
}
QString getName() const
{
return name;
}
};
class DirectoryNode : public Node, public QList<Node *>
{
public:
explicit DirectoryNode(const QString &_name = QString(), DirectoryNode *_parent = nullptr);
~DirectoryNode() override;
void clearTree();
QString getPath() const;
DirectoryNode *getNodeByPath(QStringList path);
FileNode *getNodeById(int id) const;
};
class FileNode : public Node
{
private:
int id;
QDateTime uploadTime;
public:
FileNode(const QString &_name, int _id, const QDateTime &_uploadTime, DirectoryNode *_parent = nullptr)
: Node(_name, _parent), id(_id), uploadTime(_uploadTime)
{
}
int getId() const
{
return id;
}
QDateTime getUploadTime() const
{
return uploadTime;
}
};
template <typename T> T getNode(const QModelIndex &index) const
{
if (!index.isValid())
return dynamic_cast<T>(root);
return dynamic_cast<T>(static_cast<Node *>(index.internalPointer()));
}
private:
AbstractClient *client;
DirectoryNode *root;
QIcon fileIcon, dirIcon;
QModelIndex nodeToIndex(Node *node) const;
signals:
void treeRefreshed();
private slots:
void deckListFinished(const Response &r);
public:
explicit RemoteDeckList_TreeModel(AbstractClient *_client, QObject *parent = nullptr);
~RemoteDeckList_TreeModel() override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
DirectoryNode *getRoot() const
{
return root;
}
void addFileToTree(const ServerInfo_DeckStorage_TreeItem &file, DirectoryNode *parent);
void addFolderToTree(const ServerInfo_DeckStorage_TreeItem &folder, DirectoryNode *parent);
DirectoryNode *addNamedFolderToTree(const QString &name, DirectoryNode *parent);
void removeNode(Node *node);
void refreshTree();
void clearTree();
};
class RemoteDeckList_TreeWidget : public QTreeView
{
private:
RemoteDeckList_TreeModel *treeModel;
QSortFilterProxyModel *proxyModel;
public:
explicit RemoteDeckList_TreeWidget(AbstractClient *_client, QWidget *parent = nullptr);
RemoteDeckList_TreeModel::Node *getNode(const QModelIndex &ind) const;
RemoteDeckList_TreeModel::Node *getCurrentItem() const;
QList<RemoteDeckList_TreeModel::Node *> getCurrentSelection() const;
RemoteDeckList_TreeModel::DirectoryNode *getNodeByPath(const QString &path) const;
RemoteDeckList_TreeModel::FileNode *getNodeById(int id) const;
void addFileToTree(const ServerInfo_DeckStorage_TreeItem &file, RemoteDeckList_TreeModel::DirectoryNode *parent);
void addFolderToTree(const ServerInfo_DeckStorage_TreeItem &folder,
RemoteDeckList_TreeModel::DirectoryNode *parent);
void addFolderToTree(const QString &name, RemoteDeckList_TreeModel::DirectoryNode *parent);
void removeNode(RemoteDeckList_TreeModel::Node *node);
void refreshTree();
void clearTree();
};
#endif

View file

@ -0,0 +1,383 @@
#include "remote_replay_list_tree_widget.h"
#include <QFileIconProvider>
#include <QHeaderView>
#include <QSortFilterProxyModel>
#include <libcockatrice/network/client/abstract/abstract_client.h>
#include <libcockatrice/protocol/pb/command_replay_list.pb.h>
#include <libcockatrice/protocol/pb/response_replay_list.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_replay.pb.h>
#include <libcockatrice/protocol/pending_command.h>
const int RemoteReplayList_TreeModel::numberOfColumns = 6;
RemoteReplayList_TreeModel::MatchNode::MatchNode(const ServerInfo_ReplayMatch &_matchInfo)
: RemoteReplayList_TreeModel::Node(QString::fromStdString(_matchInfo.game_name())), matchInfo(_matchInfo)
{
for (int i = 0; i < matchInfo.replay_list_size(); ++i)
append(new ReplayNode(matchInfo.replay_list(i), this));
}
RemoteReplayList_TreeModel::MatchNode::~MatchNode()
{
for (int i = 0; i < size(); ++i)
delete at(i);
}
void RemoteReplayList_TreeModel::MatchNode::updateMatchInfo(const ServerInfo_ReplayMatch &_matchInfo)
{
matchInfo.MergeFrom(_matchInfo);
}
RemoteReplayList_TreeModel::RemoteReplayList_TreeModel(AbstractClient *_client, QObject *parent)
: QAbstractItemModel(parent), client(_client)
{
QFileIconProvider fip;
dirIcon = fip.icon(QFileIconProvider::Folder);
fileIcon = fip.icon(QFileIconProvider::File);
lockIcon = QPixmap("theme:icons/lock");
}
RemoteReplayList_TreeModel::~RemoteReplayList_TreeModel()
{
clearAll();
}
int RemoteReplayList_TreeModel::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid())
return replayMatches.size();
MatchNode *matchNode = dynamic_cast<MatchNode *>(static_cast<Node *>(parent.internalPointer()));
if (matchNode)
return matchNode->size();
else
return 0;
}
QVariant RemoteReplayList_TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.column() >= numberOfColumns)
return QVariant();
ReplayNode *replayNode = dynamic_cast<ReplayNode *>(static_cast<Node *>(index.internalPointer()));
if (replayNode) {
const ServerInfo_Replay &replayInfo = replayNode->getReplayInfo();
switch (role) {
case Qt::TextAlignmentRole:
return index.column() == 0 ? Qt::AlignRight : Qt::AlignLeft;
case Qt::DisplayRole: {
switch (index.column()) {
case 0:
return replayInfo.replay_id();
case 1:
return QString::fromStdString(replayInfo.replay_name());
case 5:
return replayInfo.duration();
default:
return QVariant();
}
}
case Qt::DecorationRole:
return index.column() == 0 ? fileIcon : QVariant();
}
} else {
MatchNode *matchNode = dynamic_cast<MatchNode *>(static_cast<Node *>(index.internalPointer()));
const ServerInfo_ReplayMatch &matchInfo = matchNode->getMatchInfo();
switch (role) {
case Qt::TextAlignmentRole:
switch (index.column()) {
case 0:
case 5:
return Qt::AlignRight;
default:
return Qt::AlignLeft;
}
case Qt::DisplayRole: {
switch (index.column()) {
case 0:
return matchInfo.game_id();
case 1:
return QString::fromStdString(matchInfo.game_name());
case 2: {
QStringList playerList;
for (int i = 0; i < matchInfo.player_names_size(); ++i)
playerList.append(QString::fromStdString(matchInfo.player_names(i)));
return playerList.join(", ");
}
case 4:
return QDateTime::fromSecsSinceEpoch(matchInfo.time_started());
case 5:
return matchInfo.length();
default:
return QVariant();
}
}
case Qt::DecorationRole:
switch (index.column()) {
case 0:
return dirIcon;
case 3:
return matchInfo.do_not_hide() ? lockIcon : QVariant();
default:
return QVariant();
}
}
}
return QVariant();
}
QVariant RemoteReplayList_TreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation != Qt::Horizontal)
return QVariant();
switch (role) {
case Qt::TextAlignmentRole:
switch (section) {
case 0:
case 5:
return Qt::AlignRight;
default:
return Qt::AlignLeft;
}
case Qt::DisplayRole: {
switch (section) {
case 0:
return tr("ID");
case 1:
return tr("Name");
case 2:
return tr("Players");
case 3:
return tr("Keep");
case 4:
return tr("Time started");
case 5:
return tr("Duration (sec)");
default:
return QVariant();
}
}
default:
return QVariant();
}
}
QModelIndex RemoteReplayList_TreeModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
MatchNode *matchNode = dynamic_cast<MatchNode *>(static_cast<Node *>(parent.internalPointer()));
if (matchNode) {
if (row >= matchNode->size())
return QModelIndex();
return createIndex(row, column, (void *)matchNode->at(row));
} else {
if (row >= replayMatches.size())
return QModelIndex();
return createIndex(row, column, (void *)replayMatches[row]);
}
}
QModelIndex RemoteReplayList_TreeModel::parent(const QModelIndex &ind) const
{
MatchNode const *matchNode = dynamic_cast<MatchNode *>(static_cast<Node *>(ind.internalPointer()));
if (matchNode)
return QModelIndex();
else {
ReplayNode *replayNode = dynamic_cast<ReplayNode *>(static_cast<Node *>(ind.internalPointer()));
return createIndex(replayNode->getParent()->indexOf(replayNode), 0, replayNode->getParent());
}
}
Qt::ItemFlags RemoteReplayList_TreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::NoItemFlags;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
ServerInfo_Replay const *RemoteReplayList_TreeModel::getReplay(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
ReplayNode *node = dynamic_cast<ReplayNode *>(static_cast<Node *>(index.internalPointer()));
if (!node)
return 0;
return &node->getReplayInfo();
}
ServerInfo_ReplayMatch const *RemoteReplayList_TreeModel::getReplayMatch(const QModelIndex &index) const
{
if (!index.isValid())
return nullptr;
auto *node = dynamic_cast<MatchNode *>(static_cast<Node *>(index.internalPointer()));
if (!node)
return nullptr;
return &node->getMatchInfo();
}
ServerInfo_ReplayMatch const *RemoteReplayList_TreeModel::getEnclosingReplayMatch(const QModelIndex &index) const
{
if (!index.isValid())
return nullptr;
auto *node = dynamic_cast<MatchNode *>(static_cast<Node *>(index.internalPointer()));
if (!node) {
auto *_node = dynamic_cast<ReplayNode *>(static_cast<Node *>(index.internalPointer()));
if (!_node)
return nullptr;
return &_node->getParent()->getMatchInfo();
}
return &node->getMatchInfo();
}
/**
* Deletes all items in the model
*/
void RemoteReplayList_TreeModel::clearAll()
{
for (int i = 0; i < replayMatches.size(); ++i)
delete replayMatches[i];
replayMatches.clear();
}
void RemoteReplayList_TreeModel::refreshTree()
{
PendingCommand *pend = client->prepareSessionCommand(Command_ReplayList());
connect(pend, &PendingCommand::finished, this, &RemoteReplayList_TreeModel::replayListFinished);
client->sendCommand(pend);
}
void RemoteReplayList_TreeModel::clearTree()
{
beginResetModel();
clearAll();
endResetModel();
}
void RemoteReplayList_TreeModel::addMatchInfo(const ServerInfo_ReplayMatch &matchInfo)
{
beginInsertRows(QModelIndex(), replayMatches.size(), replayMatches.size());
replayMatches.append(new MatchNode(matchInfo));
endInsertRows();
emit treeRefreshed();
}
void RemoteReplayList_TreeModel::updateMatchInfo(int gameId, const ServerInfo_ReplayMatch &matchInfo)
{
for (int i = 0; i < replayMatches.size(); ++i)
if (replayMatches[i]->getMatchInfo().game_id() == gameId) {
replayMatches[i]->updateMatchInfo(matchInfo);
emit dataChanged(createIndex(i, 0, (void *)replayMatches[i]),
createIndex(i, numberOfColumns - 1, (void *)replayMatches[i]));
break;
}
}
void RemoteReplayList_TreeModel::removeMatchInfo(int gameId)
{
for (int i = 0; i < replayMatches.size(); ++i)
if (replayMatches[i]->getMatchInfo().game_id() == gameId) {
beginRemoveRows(QModelIndex(), i, i);
replayMatches.removeAt(i);
endRemoveRows();
break;
}
}
void RemoteReplayList_TreeModel::replayListFinished(const Response &r)
{
const Response_ReplayList &resp = r.GetExtension(Response_ReplayList::ext);
beginResetModel();
clearAll();
for (int i = 0; i < resp.match_list_size(); ++i)
replayMatches.append(new MatchNode(resp.match_list(i)));
endResetModel();
emit treeRefreshed();
}
RemoteReplayList_TreeWidget::RemoteReplayList_TreeWidget(AbstractClient *_client, QWidget *parent) : QTreeView(parent)
{
treeModel = new RemoteReplayList_TreeModel(_client, this);
proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(treeModel);
proxyModel->setDynamicSortFilter(true);
proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
setModel(proxyModel);
header()->setSectionResizeMode(QHeaderView::ResizeToContents);
header()->setStretchLastSection(false);
setUniformRowHeights(true);
setSortingEnabled(true);
proxyModel->sort(0, Qt::AscendingOrder);
header()->setSortIndicator(0, Qt::AscendingOrder);
setSelectionMode(QAbstractItemView::ExtendedSelection);
}
/**
* Gets the replay at the given index
* @return The replay. Returns nullptr if there is no replay at the index.
*/
ServerInfo_Replay const *RemoteReplayList_TreeWidget::getReplay(const QModelIndex &ind) const
{
return treeModel->getReplay(proxyModel->mapToSource(ind));
}
/**
* Gets the replay match at the given index
* @return The replay match. Returns nullptr if there is no replay match at the index.
*/
ServerInfo_ReplayMatch const *RemoteReplayList_TreeWidget::getReplayMatch(const QModelIndex &ind) const
{
return treeModel->getReplayMatch(proxyModel->mapToSource(ind));
}
/**
* Gets all currently selected replays.
* Any selection that isn't a replay file (e.g. a folder) will appear as a nullptr in the list.
* Make sure to check the list for nullptr before using it.
*
* @return A List of pointers to the selected replays, as well as nullptr for any selection that isn't a replay.
*/
QList<ServerInfo_Replay const *> RemoteReplayList_TreeWidget::getSelectedReplays() const
{
const auto selection = selectionModel()->selectedRows();
auto replays = QList<ServerInfo_Replay const *>();
for (const auto &row : selection) {
replays << treeModel->getReplay(proxyModel->mapToSource(row));
}
return replays;
}
/**
* Gets all currently selected replayMatches.
* If a non-folder node is selected, it will return the parent folder of that node.
*
* @return A Set of pointers to the selected replayMatches.
*/
QSet<ServerInfo_ReplayMatch const *> RemoteReplayList_TreeWidget::getSelectedReplayMatches() const
{
const auto selection = selectionModel()->selectedRows();
auto replayMatches = QSet<ServerInfo_ReplayMatch const *>();
for (const auto &row : selection) {
if (const auto replayMatch = treeModel->getEnclosingReplayMatch(proxyModel->mapToSource(row))) {
replayMatches << replayMatch;
}
}
return replayMatches;
}

View file

@ -0,0 +1,148 @@
/**
* @file remote_replay_list_tree_widget.h
* @ingroup DeckStorageWidgets
* @ingroup Replays
* @ingroup NetworkingWidgets
* @brief TODO: Document this.
*/
#ifndef REMOTEREPLAYLIST_TREEWIDGET_H
#define REMOTEREPLAYLIST_TREEWIDGET_H
#include <QAbstractItemModel>
#include <QDateTime>
#include <QTreeView>
#include <libcockatrice/protocol/pb/serverinfo_replay.pb.h>
#include <libcockatrice/protocol/pb/serverinfo_replay_match.pb.h>
class Response;
class AbstractClient;
class QSortFilterProxyModel;
class RemoteReplayList_TreeModel : public QAbstractItemModel
{
Q_OBJECT
private:
class MatchNode;
class ReplayNode;
class Node
{
protected:
QString name;
public:
explicit Node(const QString &_name) : name(_name)
{
}
virtual ~Node() = default;
QString getName() const
{
return name;
}
};
class MatchNode : public Node, public QList<ReplayNode *>
{
private:
ServerInfo_ReplayMatch matchInfo;
public:
explicit MatchNode(const ServerInfo_ReplayMatch &_matchInfo);
~MatchNode() override;
void clearTree();
const ServerInfo_ReplayMatch &getMatchInfo()
{
return matchInfo;
}
void updateMatchInfo(const ServerInfo_ReplayMatch &_matchInfo);
};
class ReplayNode : public Node
{
private:
MatchNode *parent;
ServerInfo_Replay replayInfo;
public:
ReplayNode(const ServerInfo_Replay &_replayInfo, MatchNode *_parent)
: Node(QString::fromStdString(_replayInfo.replay_name())), parent(_parent), replayInfo(_replayInfo)
{
}
MatchNode *getParent() const
{
return parent;
}
const ServerInfo_Replay &getReplayInfo()
{
return replayInfo;
}
};
AbstractClient *client;
QList<MatchNode *> replayMatches;
QIcon dirIcon, fileIcon, lockIcon;
void clearAll();
static const int numberOfColumns;
signals:
void treeRefreshed();
private slots:
void replayListFinished(const Response &r);
public:
explicit RemoteReplayList_TreeModel(AbstractClient *_client, QObject *parent = nullptr);
~RemoteReplayList_TreeModel() override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const override
{
return numberOfColumns;
}
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
void clearTree();
void refreshTree();
ServerInfo_Replay const *getReplay(const QModelIndex &index) const;
ServerInfo_ReplayMatch const *getReplayMatch(const QModelIndex &index) const;
ServerInfo_ReplayMatch const *getEnclosingReplayMatch(const QModelIndex &index) const;
void addMatchInfo(const ServerInfo_ReplayMatch &matchInfo);
void updateMatchInfo(int gameId, const ServerInfo_ReplayMatch &matchInfo);
void removeMatchInfo(int gameId);
};
class RemoteReplayList_TreeWidget : public QTreeView
{
private:
RemoteReplayList_TreeModel *treeModel;
QSortFilterProxyModel *proxyModel;
public:
explicit RemoteReplayList_TreeWidget(AbstractClient *_client, QWidget *parent = nullptr);
ServerInfo_Replay const *getReplay(const QModelIndex &ind) const;
ServerInfo_ReplayMatch const *getReplayMatch(const QModelIndex &ind) const;
QList<ServerInfo_Replay const *> getSelectedReplays() const;
QSet<ServerInfo_ReplayMatch const *> getSelectedReplayMatches() const;
void clearTree()
{
treeModel->clearTree();
}
void refreshTree()
{
treeModel->refreshTree();
}
void addMatchInfo(const ServerInfo_ReplayMatch &matchInfo)
{
treeModel->addMatchInfo(matchInfo);
}
void updateMatchInfo(int gameId, const ServerInfo_ReplayMatch &matchInfo)
{
treeModel->updateMatchInfo(gameId, matchInfo);
}
void removeMatchInfo(int gameId)
{
treeModel->removeMatchInfo(gameId);
}
};
#endif