mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-07-19 08:52:15 -07:00
Merge branch 'master' into remote-decks-multi
This commit is contained in:
commit
9c914e6217
8 changed files with 1669 additions and 1158 deletions
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue