Merge branch 'master' into remote-decks-multi

This commit is contained in:
RickyRister 2024-12-23 17:44:01 -08:00 committed by GitHub
commit 9c914e6217
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 1669 additions and 1158 deletions

View file

@ -41,6 +41,7 @@ TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor, AbstractClient *_c
localDirView->setColumnHidden(1, true);
localDirView->setRootIndex(localDirModel->index(localDirModel->rootPath(), 0));
localDirView->setSortingEnabled(true);
localDirView->setSelectionMode(QAbstractItemView::ExtendedSelection);
localDirView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
localDirView->header()->setSortIndicator(0, Qt::AscendingOrder);
@ -149,41 +150,50 @@ QString TabDeckStorage::getTargetPath() const
void TabDeckStorage::actOpenLocalDeck()
{
QModelIndex curLeft = localDirView->selectionModel()->currentIndex();
if (localDirModel->isDir(curLeft))
return;
QString filePath = localDirModel->filePath(curLeft);
QModelIndexList curLefts = localDirView->selectionModel()->selectedRows();
for (const auto &curLeft : curLefts) {
if (localDirModel->isDir(curLeft))
return;
QString filePath = localDirModel->filePath(curLeft);
DeckLoader deckLoader;
if (!deckLoader.loadFromFile(filePath, DeckLoader::CockatriceFormat))
return;
DeckLoader deckLoader;
if (!deckLoader.loadFromFile(filePath, DeckLoader::CockatriceFormat))
return;
emit openDeckEditor(&deckLoader);
emit openDeckEditor(&deckLoader);
}
}
void TabDeckStorage::actUpload()
{
QModelIndex curLeft = localDirView->selectionModel()->currentIndex();
if (localDirModel->isDir(curLeft))
QModelIndexList curLefts = localDirView->selectionModel()->selectedRows();
if (curLefts.isEmpty()) {
return;
}
QString targetPath = getTargetPath();
if (targetPath.length() > MAX_NAME_LENGTH) {
qCritical() << "target path to upload to is too long" << targetPath;
return;
}
QString filePath = localDirModel->filePath(curLeft);
for (const auto &curLeft : curLefts) {
if (localDirModel->isDir(curLeft)) {
continue;
}
QString filePath = localDirModel->filePath(curLeft);
uploadDeck(filePath, targetPath);
}
}
void TabDeckStorage::uploadDeck(const QString &filePath, const QString &targetPath)
{
QFile deckFile(filePath);
QFileInfo deckFileInfo(deckFile);
QString deckString;
DeckLoader deck;
bool error = !deck.loadFromFile(filePath, DeckLoader::CockatriceFormat);
if (!error) {
deckString = deck.writeToString_Native();
error = deckString.length() > MAX_FILE_LENGTH;
}
if (error) {
if (!deck.loadFromFile(filePath, DeckLoader::CockatriceFormat)) {
QMessageBox::critical(this, tr("Error"), tr("Invalid deck file"));
return;
}
@ -202,6 +212,12 @@ void TabDeckStorage::actUpload()
deck.setName(deck.getName().left(MAX_NAME_LENGTH));
}
QString deckString = deck.writeToString_Native();
if (deckString.length() > MAX_FILE_LENGTH) {
QMessageBox::critical(this, tr("Error"), tr("Invalid deck file"));
return;
}
Command_DeckUpload cmd;
cmd.set_path(targetPath.toStdString());
cmd.set_deck_list(deckString.toStdString());
@ -228,16 +244,22 @@ void TabDeckStorage::uploadFinished(const Response &r, const CommandContainer &c
void TabDeckStorage::actDeleteLocalDeck()
{
QModelIndex curLeft = localDirView->selectionModel()->currentIndex();
if (localDirModel->isDir(curLeft))
return;
const QModelIndexList curLefts = localDirView->selectionModel()->selectedRows();
if (QMessageBox::warning(this, tr("Delete local file"),
tr("Are you sure you want to delete \"%1\"?").arg(localDirModel->fileName(curLeft)),
auto isDir = [&](const auto &curLeft) { return localDirModel->isDir(curLeft); };
if (curLefts.isEmpty() || std::all_of(curLefts.begin(), curLefts.end(), isDir)) {
return;
}
if (QMessageBox::warning(this, tr("Delete local file"), tr("Are you sure you want to delete the selected files?"),
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
return;
localDirModel->remove(curLeft);
for (const auto &curLeft : curLefts) {
if (!localDirModel->isDir(curLeft)) {
localDirModel->remove(curLeft);
}
}
}
void TabDeckStorage::actOpenRemoteDeck()

View file

@ -29,6 +29,7 @@ private:
QAction *aOpenLocalDeck, *aUpload, *aDeleteLocalDeck, *aOpenRemoteDeck, *aDownload, *aNewFolder, *aDeleteRemoteDeck;
QString getTargetPath() const;
void uploadDeck(const QString &filePath, const QString &targetPath);
void deleteRemoteDeck(const RemoteDeckList_TreeModel::Node *node);
private slots:

View file

@ -36,6 +36,7 @@ TabReplays::TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client) :
localDirView->setColumnHidden(1, true);
localDirView->setRootIndex(localDirModel->index(localDirModel->rootPath(), 0));
localDirView->setSortingEnabled(true);
localDirView->setSelectionMode(QAbstractItemView::ExtendedSelection);
localDirView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
localDirView->header()->setSortIndicator(0, Qt::AscendingOrder);
@ -126,50 +127,62 @@ void TabReplays::retranslateUi()
void TabReplays::actOpenLocalReplay()
{
QModelIndex curLeft = localDirView->selectionModel()->currentIndex();
if (localDirModel->isDir(curLeft))
return;
QString filePath = localDirModel->filePath(curLeft);
QModelIndexList curLefts = localDirView->selectionModel()->selectedRows();
for (const auto &curLeft : curLefts) {
if (localDirModel->isDir(curLeft))
continue;
QString filePath = localDirModel->filePath(curLeft);
QFile f(filePath);
if (!f.open(QIODevice::ReadOnly))
return;
QByteArray _data = f.readAll();
f.close();
QFile f(filePath);
if (!f.open(QIODevice::ReadOnly))
continue;
QByteArray _data = f.readAll();
f.close();
GameReplay *replay = new GameReplay;
replay->ParseFromArray(_data.data(), _data.size());
GameReplay *replay = new GameReplay;
replay->ParseFromArray(_data.data(), _data.size());
emit openReplay(replay);
emit openReplay(replay);
}
}
void TabReplays::actDeleteLocalReplay()
{
QModelIndex curLeft = localDirView->selectionModel()->currentIndex();
if (!curLeft.isValid())
return;
QModelIndexList curLefts = localDirView->selectionModel()->selectedRows();
if (QMessageBox::warning(this, tr("Delete local file"),
tr("Are you sure you want to delete \"%1\"?").arg(localDirModel->fileName(curLeft)),
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
if (curLefts.isEmpty()) {
return;
}
localDirModel->remove(curLeft);
if (QMessageBox::warning(this, tr("Delete local file"), tr("Are you sure you want to delete the selected files?"),
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
return;
}
for (const auto &curLeft : curLefts) {
if (curLeft.isValid()) {
localDirModel->remove(curLeft);
}
}
}
void TabReplays::actOpenRemoteReplay()
{
ServerInfo_Replay const *curRight = serverDirView->getCurrentReplay();
if (!curRight)
return;
auto const curRights = serverDirView->getSelectedReplays();
Command_ReplayDownload cmd;
cmd.set_replay_id(curRight->replay_id());
for (const auto curRight : curRights) {
if (!curRight) {
continue;
}
PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
SLOT(openRemoteReplayFinished(const Response &)));
client->sendCommand(pend);
Command_ReplayDownload cmd;
cmd.set_replay_id(curRight->replay_id());
PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
SLOT(openRemoteReplayFinished(const Response &)));
client->sendCommand(pend);
}
}
void TabReplays::openRemoteReplayFinished(const Response &r)
@ -186,34 +199,37 @@ void TabReplays::openRemoteReplayFinished(const Response &r)
void TabReplays::actDownload()
{
QString filePath;
QString dirPath;
QModelIndex curLeft = localDirView->selectionModel()->currentIndex();
if (!curLeft.isValid())
filePath = localDirModel->rootPath();
dirPath = localDirModel->rootPath();
else {
while (!localDirModel->isDir(curLeft))
curLeft = curLeft.parent();
filePath = localDirModel->filePath(curLeft);
dirPath = localDirModel->filePath(curLeft);
}
ServerInfo_Replay const *curRight = serverDirView->getCurrentReplay();
const auto curRights = serverDirView->getSelectedReplays();
if (!curRight) {
const auto isNull = [](const auto *replay) { return !replay; };
if (std::any_of(curRights.begin(), curRights.end(), isNull)) {
QMessageBox::information(this, tr("Downloading Replays"),
tr("Folder download is not yet supported. Please download replays individually."));
return;
}
filePath += QString("/replay_%1.cor").arg(curRight->replay_id());
for (const auto curRight : curRights) {
const QString filePath = dirPath + QString("/replay_%1.cor").arg(curRight->replay_id());
Command_ReplayDownload cmd;
cmd.set_replay_id(curRight->replay_id());
Command_ReplayDownload cmd;
cmd.set_replay_id(curRight->replay_id());
PendingCommand *pend = client->prepareSessionCommand(cmd);
pend->setExtraData(filePath);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
SLOT(downloadFinished(Response, CommandContainer, QVariant)));
client->sendCommand(pend);
PendingCommand *pend = client->prepareSessionCommand(cmd);
pend->setExtraData(filePath);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
SLOT(downloadFinished(Response, CommandContainer, QVariant)));
client->sendCommand(pend);
}
}
void TabReplays::downloadFinished(const Response &r,
@ -235,18 +251,22 @@ void TabReplays::downloadFinished(const Response &r,
void TabReplays::actKeepRemoteReplay()
{
ServerInfo_ReplayMatch const *curRight = serverDirView->getCurrentReplayMatch();
if (!curRight)
const auto curRights = serverDirView->getSelectedReplayMatches();
if (curRights.isEmpty()) {
return;
}
Command_ReplayModifyMatch cmd;
cmd.set_game_id(curRight->game_id());
cmd.set_do_not_hide(!curRight->do_not_hide());
for (const auto curRight : curRights) {
Command_ReplayModifyMatch cmd;
cmd.set_game_id(curRight->game_id());
cmd.set_do_not_hide(!curRight->do_not_hide());
PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
SLOT(keepRemoteReplayFinished(Response, CommandContainer)));
client->sendCommand(pend);
PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
SLOT(keepRemoteReplayFinished(Response, CommandContainer)));
client->sendCommand(pend);
}
}
void TabReplays::keepRemoteReplayFinished(const Response &r, const CommandContainer &commandContainer)
@ -265,21 +285,27 @@ void TabReplays::keepRemoteReplayFinished(const Response &r, const CommandContai
void TabReplays::actDeleteRemoteReplay()
{
ServerInfo_ReplayMatch const *curRight = serverDirView->getCurrentReplayMatch();
if (!curRight)
const auto curRights = serverDirView->getSelectedReplayMatches();
if (curRights.isEmpty()) {
return;
}
if (QMessageBox::warning(this, tr("Delete remote replay"),
tr("Are you sure you want to delete the replay of game %1?").arg(curRight->game_id()),
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
tr("Are you sure you want to delete the selected replays?"),
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
return;
}
Command_ReplayDeleteMatch cmd;
cmd.set_game_id(curRight->game_id());
for (const auto curRight : curRights) {
Command_ReplayDeleteMatch cmd;
cmd.set_game_id(curRight->game_id());
PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
SLOT(deleteRemoteReplayFinished(Response, CommandContainer)));
client->sendCommand(pend);
PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
SLOT(deleteRemoteReplayFinished(Response, CommandContainer)));
client->sendCommand(pend);
}
}
void TabReplays::deleteRemoteReplayFinished(const Response &r, const CommandContainer &commandContainer)

View file

@ -306,14 +306,41 @@ RemoteReplayList_TreeWidget::RemoteReplayList_TreeWidget(AbstractClient *_client
setSortingEnabled(true);
proxyModel->sort(0, Qt::AscendingOrder);
header()->setSortIndicator(0, Qt::AscendingOrder);
setSelectionMode(QAbstractItemView::ExtendedSelection);
}
ServerInfo_Replay const *RemoteReplayList_TreeWidget::getCurrentReplay() const
/**
* 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
{
return treeModel->getReplay(proxyModel->mapToSource(selectionModel()->currentIndex()));
const auto selection = selectionModel()->selectedRows();
auto replays = QList<ServerInfo_Replay const *>();
for (const auto &row : selection) {
replays << treeModel->getReplay(proxyModel->mapToSource(row));
}
return replays;
}
ServerInfo_ReplayMatch const *RemoteReplayList_TreeWidget::getCurrentReplayMatch() const
/**
* Gets all currently selected replayMatches.
*
* @return A Set of pointers to the selected replayMatches.
*/
QSet<ServerInfo_ReplayMatch const *> RemoteReplayList_TreeWidget::getSelectedReplayMatches() const
{
return treeModel->getReplayMatch(proxyModel->mapToSource(selectionModel()->currentIndex()));
const auto selection = selectionModel()->selectedRows();
auto replayMatches = QSet<ServerInfo_ReplayMatch const *>();
for (const auto &row : selection) {
if (const auto replayMatch = treeModel->getReplayMatch(proxyModel->mapToSource(row))) {
replayMatches << replayMatch;
}
}
return replayMatches;
}

View file

@ -111,8 +111,8 @@ private:
public:
RemoteReplayList_TreeWidget(AbstractClient *_client, QWidget *parent = nullptr);
ServerInfo_Replay const *getCurrentReplay() const;
ServerInfo_ReplayMatch const *getCurrentReplayMatch() const;
QList<ServerInfo_Replay const *> getSelectedReplays() const;
QSet<ServerInfo_ReplayMatch const *> getSelectedReplayMatches() const;
void refreshTree();
void addMatchInfo(const ServerInfo_ReplayMatch &matchInfo)
{

View file

@ -4,7 +4,22 @@
#include "shortcuts_settings.h"
#include <QHeaderView>
#include <QSortFilterProxyModel>
ShortcutFilterProxyModel::ShortcutFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
{
}
/**
* @return True if this row or its parent matches the search string
*/
bool ShortcutFilterProxyModel::filterAcceptsRow(const int sourceRow, const QModelIndex &sourceParent) const
{
QModelIndex nameIndex = sourceModel()->index(sourceRow, filterKeyColumn(), sourceParent);
QModelIndex parentIndex = sourceModel()->index(sourceParent.row(), filterKeyColumn(), sourceParent.parent());
return sourceModel()->data(nameIndex).toString().contains(filterRegularExpression()) ||
sourceModel()->data(parentIndex).toString().contains(filterRegularExpression());
}
ShortcutTreeView::ShortcutTreeView(QWidget *parent) : QTreeView(parent)
{
@ -14,7 +29,7 @@ ShortcutTreeView::ShortcutTreeView(QWidget *parent) : QTreeView(parent)
populateShortcutsModel();
// filter proxy
proxyModel = new QSortFilterProxyModel(this);
proxyModel = new ShortcutFilterProxyModel(this);
proxyModel->setRecursiveFilteringEnabled(true);
proxyModel->setSourceModel(shortcutsModel);
proxyModel->setDynamicSortFilter(true);

View file

@ -2,10 +2,23 @@
#define SHORTCUT_TREEVIEW_H
#include <QModelIndex>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include <QTreeView>
class QSortFilterProxyModel;
/**
* Custom implementation of QSortFilterProxyModel that also searches in the parent's string when filtering
*/
class ShortcutFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit ShortcutFilterProxyModel(QObject *parent = nullptr);
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
};
class ShortcutTreeView : public QTreeView
{
Q_OBJECT
@ -21,7 +34,7 @@ public slots:
private:
QStandardItemModel *shortcutsModel;
QSortFilterProxyModel *proxyModel;
ShortcutFilterProxyModel *proxyModel;
void populateShortcutsModel();
private slots:

File diff suppressed because it is too large Load diff