From 0eee6d7ab925144b65ed48abf8ec7a23595f911e Mon Sep 17 00:00:00 2001 From: Max-Wilhelm Bruker Date: Wed, 9 May 2012 21:52:09 +0200 Subject: [PATCH] fixed issue #49: when editing a remote deck, be able to directly save it there --- cockatrice/CMakeLists.txt | 2 + cockatrice/src/deck_loader.cpp | 101 ++++++++++++++++++ cockatrice/src/deck_loader.h | 33 ++++++ cockatrice/src/decklistmodel.cpp | 9 +- cockatrice/src/decklistmodel.h | 7 +- cockatrice/src/deckview.cpp | 6 +- cockatrice/src/deckview.h | 4 +- .../src/dlg_load_deck_from_clipboard.cpp | 4 +- cockatrice/src/dlg_load_deck_from_clipboard.h | 6 +- cockatrice/src/player.cpp | 8 +- cockatrice/src/player.h | 8 +- cockatrice/src/tab_deck_editor.cpp | 65 +++++++---- cockatrice/src/tab_deck_editor.h | 9 +- cockatrice/src/tab_deck_storage.cpp | 33 +++--- cockatrice/src/tab_deck_storage.h | 5 +- cockatrice/src/tab_game.cpp | 25 +++-- cockatrice/src/tab_game.h | 6 +- cockatrice/src/tab_supervisor.cpp | 8 +- cockatrice/src/tab_supervisor.h | 3 +- common/decklist.cpp | 75 ++++--------- common/decklist.h | 19 +--- common/pb/command_deck_upload.proto | 6 +- servatrice/src/serversocketinterface.cpp | 63 +++++++---- 23 files changed, 324 insertions(+), 181 deletions(-) create mode 100644 cockatrice/src/deck_loader.cpp create mode 100644 cockatrice/src/deck_loader.h diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index cc8ae9a9..f04e714a 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -28,6 +28,7 @@ SET(cockatrice_SOURCES src/gameview.cpp src/gameselector.cpp src/decklistmodel.cpp + src/deck_loader.cpp src/dlg_load_deck_from_clipboard.cpp src/dlg_load_remote_deck.cpp src/cardinfowidget.cpp @@ -101,6 +102,7 @@ SET(cockatrice_HEADERS src/gameview.h src/gameselector.h src/decklistmodel.h + src/deck_loader.h src/dlg_load_deck_from_clipboard.h src/dlg_load_remote_deck.h src/cardinfowidget.h diff --git a/cockatrice/src/deck_loader.cpp b/cockatrice/src/deck_loader.cpp new file mode 100644 index 00000000..b0e34d80 --- /dev/null +++ b/cockatrice/src/deck_loader.cpp @@ -0,0 +1,101 @@ +#include +#include +#include "deck_loader.h" +#include "decklist.h" + +const QStringList DeckLoader::fileNameFilters = QStringList() + << QObject::tr("Cockatrice decks (*.cod)") + << QObject::tr("Plain text decks (*.dec *.mwDeck)") + << QObject::tr("All files (*.*)"); + +DeckLoader::DeckLoader() + : DeckList(), + lastFileName(QString()), + lastFileFormat(CockatriceFormat), + lastRemoteDeckId(-1) +{ +} + +DeckLoader::DeckLoader(const QString &nativeString) + : DeckList(nativeString), + lastFileName(QString()), + lastFileFormat(CockatriceFormat), + lastRemoteDeckId(-1) +{ +} + +DeckLoader::DeckLoader(const DeckList &other) + : DeckList(other), + lastFileName(QString()), + lastFileFormat(CockatriceFormat), + lastRemoteDeckId(-1) +{ +} + +DeckLoader::DeckLoader(const DeckLoader &other) + : DeckList(other), + lastFileName(other.lastFileName), + lastFileFormat(other.lastFileFormat), + lastRemoteDeckId(other.lastRemoteDeckId) +{ +} + +bool DeckLoader::loadFromFile(const QString &fileName, FileFormat fmt) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + return false; + + bool result = false; + switch (fmt) { + case PlainTextFormat: result = loadFromFile_Plain(&file); break; + case CockatriceFormat: result = loadFromFile_Native(&file); break; + } + if (result) { + lastFileName = fileName; + lastFileFormat = fmt; + + emit deckLoaded(); + } + return result; +} + +bool DeckLoader::loadFromRemote(const QString &nativeString, int remoteDeckId) +{ + bool result = loadFromString_Native(nativeString); + if (result) { + lastFileName = QString(); + lastFileFormat = CockatriceFormat; + lastRemoteDeckId = remoteDeckId; + + emit deckLoaded(); + } + return result; +} + +bool DeckLoader::saveToFile(const QString &fileName, FileFormat fmt) +{ + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + return false; + + bool result = false; + switch (fmt) { + case PlainTextFormat: result = saveToFile_Plain(&file); break; + case CockatriceFormat: result = saveToFile_Native(&file); break; + } + if (result) { + lastFileName = fileName; + lastFileFormat = fmt; + } + return result; +} + +DeckLoader::FileFormat DeckLoader::getFormatFromNameFilter(const QString &selectedNameFilter) +{ + switch (fileNameFilters.indexOf(selectedNameFilter)) { + case 0: return CockatriceFormat; + case 1: return PlainTextFormat; + } + return PlainTextFormat; +} diff --git a/cockatrice/src/deck_loader.h b/cockatrice/src/deck_loader.h new file mode 100644 index 00000000..53f9e583 --- /dev/null +++ b/cockatrice/src/deck_loader.h @@ -0,0 +1,33 @@ +#ifndef DECK_LOADER_H +#define DECK_LOADER_H + +#include "decklist.h" + +class DeckLoader : public DeckList { + Q_OBJECT +signals: + void deckLoaded(); +public: + enum FileFormat { PlainTextFormat, CockatriceFormat }; + static const QStringList fileNameFilters; +private: + QString lastFileName; + FileFormat lastFileFormat; + int lastRemoteDeckId; +public: + DeckLoader(); + DeckLoader(const QString &nativeString); + DeckLoader(const DeckList &other); + DeckLoader(const DeckLoader &other); + const QString &getLastFileName() const { return lastFileName; } + FileFormat getLastFileFormat() const { return lastFileFormat; } + int getLastRemoteDeckId() const { return lastRemoteDeckId; } + + static FileFormat getFormatFromNameFilter(const QString &selectedNameFilter); + + bool loadFromFile(const QString &fileName, FileFormat fmt); + bool loadFromRemote(const QString &nativeString, int remoteDeckId); + bool saveToFile(const QString &fileName, FileFormat fmt); +}; + +#endif diff --git a/cockatrice/src/decklistmodel.cpp b/cockatrice/src/decklistmodel.cpp index c46ebe2e..6abbba86 100644 --- a/cockatrice/src/decklistmodel.cpp +++ b/cockatrice/src/decklistmodel.cpp @@ -11,11 +11,12 @@ #include "decklistmodel.h" #include "carddatabase.h" #include "settingscache.h" +#include "deck_loader.h" DeckListModel::DeckListModel(QObject *parent) : QAbstractItemModel(parent) { - deckList = new DeckList; + deckList = new DeckLoader; connect(deckList, SIGNAL(deckLoaded()), this, SLOT(rebuildTree())); connect(deckList, SIGNAL(deckHashChanged()), this, SIGNAL(deckHashChanged())); root = new InnerDecklistNode; @@ -304,12 +305,10 @@ void DeckListModel::sort(int /*column*/, Qt::SortOrder order) void DeckListModel::cleanList() { - root->clearTree(); - deckList->cleanList(); - reset(); + setDeckList(new DeckLoader); } -void DeckListModel::setDeckList(DeckList *_deck) +void DeckListModel::setDeckList(DeckLoader *_deck) { delete deckList; deckList = _deck; diff --git a/cockatrice/src/decklistmodel.h b/cockatrice/src/decklistmodel.h index bab46233..4d81d007 100644 --- a/cockatrice/src/decklistmodel.h +++ b/cockatrice/src/decklistmodel.h @@ -5,6 +5,7 @@ #include #include "decklist.h" +class DeckLoader; class CardDatabase; class QProgressDialog; class QPrinter; @@ -47,11 +48,11 @@ public: QModelIndex addCard(const QString &cardName, const QString &zoneName); void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); void cleanList(); - DeckList *getDeckList() const { return deckList; } - void setDeckList(DeckList *_deck); + DeckLoader *getDeckList() const { return deckList; } + void setDeckList(DeckLoader *_deck); void pricesUpdated(InnerDecklistNode *node = 0); private: - DeckList *deckList; + DeckLoader *deckList; InnerDecklistNode *root; InnerDecklistNode *createNodeIfNeeded(const QString &name, InnerDecklistNode *parent); QModelIndex nodeToIndex(AbstractDecklistNode *node) const; diff --git a/cockatrice/src/deckview.cpp b/cockatrice/src/deckview.cpp index 5a0fa1fc..a073cf2d 100644 --- a/cockatrice/src/deckview.cpp +++ b/cockatrice/src/deckview.cpp @@ -285,12 +285,12 @@ void DeckViewScene::clearContents() cardContainers.clear(); } -void DeckViewScene::setDeck(DeckList *_deck) +void DeckViewScene::setDeck(const DeckList &_deck) { if (deck) delete deck; - deck = _deck; + deck = new DeckList(_deck); rebuildTree(); applySideboardPlan(deck->getCurrentSideboardPlan()); rearrangeItems(); @@ -475,7 +475,7 @@ void DeckView::updateSceneRect(const QRectF &rect) fitInView(rect, Qt::KeepAspectRatio); } -void DeckView::setDeck(DeckList *_deck) +void DeckView::setDeck(const DeckList &_deck) { deckViewScene->setDeck(_deck); } diff --git a/cockatrice/src/deckview.h b/cockatrice/src/deckview.h index 6f919e3e..a7aeb2f1 100644 --- a/cockatrice/src/deckview.h +++ b/cockatrice/src/deckview.h @@ -89,7 +89,7 @@ public: ~DeckViewScene(); void setLocked(bool _locked) { locked = _locked; } bool getLocked() const { return locked; } - void setDeck(DeckList *_deck); + void setDeck(const DeckList &_deck); void setOptimalAspectRatio(qreal _optimalAspectRatio) { optimalAspectRatio = _optimalAspectRatio; } void rearrangeItems(); void updateContents(); @@ -110,7 +110,7 @@ signals: void sideboardPlanChanged(); public: DeckView(QWidget *parent = 0); - void setDeck(DeckList *_deck); + void setDeck(const DeckList &_deck); void setLocked(bool _locked) { deckViewScene->setLocked(_locked); } QList getSideboardPlan() const { return deckViewScene->getSideboardPlan(); } void resetSideboardPlan(); diff --git a/cockatrice/src/dlg_load_deck_from_clipboard.cpp b/cockatrice/src/dlg_load_deck_from_clipboard.cpp index 682b6bbc..0744ff7e 100644 --- a/cockatrice/src/dlg_load_deck_from_clipboard.cpp +++ b/cockatrice/src/dlg_load_deck_from_clipboard.cpp @@ -9,7 +9,7 @@ #include #include #include "dlg_load_deck_from_clipboard.h" -#include "decklist.h" +#include "deck_loader.h" DlgLoadDeckFromClipboard::DlgLoadDeckFromClipboard(QWidget *parent) : QDialog(parent), deckList(0) @@ -47,7 +47,7 @@ void DlgLoadDeckFromClipboard::actOK() QString buffer = contentsEdit->toPlainText(); QTextStream stream(&buffer); - DeckList *l = new DeckList; + DeckLoader *l = new DeckLoader; if (l->loadFromStream_Plain(stream)) { deckList = l; accept(); diff --git a/cockatrice/src/dlg_load_deck_from_clipboard.h b/cockatrice/src/dlg_load_deck_from_clipboard.h index dad4a960..5fcd759e 100644 --- a/cockatrice/src/dlg_load_deck_from_clipboard.h +++ b/cockatrice/src/dlg_load_deck_from_clipboard.h @@ -3,7 +3,7 @@ #include -class DeckList; +class DeckLoader; class QPlainTextEdit; class QPushButton; @@ -13,10 +13,10 @@ private slots: void actOK(); void actRefresh(); private: - DeckList *deckList; + DeckLoader *deckList; public: DlgLoadDeckFromClipboard(QWidget *parent = 0); - DeckList *getDeckList() const { return deckList; } + DeckLoader *getDeckList() const { return deckList; } private: QPlainTextEdit *contentsEdit; QPushButton *refreshButton; diff --git a/cockatrice/src/player.cpp b/cockatrice/src/player.cpp index 58681cbf..6fd7250a 100644 --- a/cockatrice/src/player.cpp +++ b/cockatrice/src/player.cpp @@ -18,7 +18,7 @@ #include "dlg_create_token.h" #include "carddatabase.h" #include "color.h" -#include "decklist.h" +#include "deck_loader.h" #include "main.h" #include #include @@ -747,9 +747,9 @@ void Player::initSayMenu() } } -void Player::setDeck(DeckList *_deck) +void Player::setDeck(const DeckLoader &_deck) { - deck = _deck; + deck = new DeckLoader(_deck); aOpenDeckInDeckEditor->setEnabled(deck); createPredefinedTokenMenu->clear(); @@ -792,7 +792,7 @@ void Player::actAlwaysRevealTopCard() void Player::actOpenDeckInDeckEditor() { - emit openDeckEditor(new DeckList(deck)); + emit openDeckEditor(*deck); } void Player::actViewGraveyard() diff --git a/cockatrice/src/player.h b/cockatrice/src/player.h index 7808e336..c04e2c42 100644 --- a/cockatrice/src/player.h +++ b/cockatrice/src/player.h @@ -10,7 +10,7 @@ namespace google { namespace protobuf { class Message; } } class CardDatabase; -class DeckList; +class DeckLoader; class QMenu; class QAction; class ZoneViewZone; @@ -77,7 +77,7 @@ public: class Player : public QObject, public QGraphicsItem { Q_OBJECT signals: - void openDeckEditor(DeckList *deck); + void openDeckEditor(const DeckLoader &deck); void newCardAdded(AbstractCardItem *card); // Log events void logSay(Player *player, QString message); @@ -191,7 +191,7 @@ private: bool clearCardsToDelete(); QList cardsToDelete; - DeckList *deck; + DeckLoader *deck; QStringList predefinedTokens; PlayerArea *playerArea; @@ -263,7 +263,7 @@ public: void retranslateUi(); void clear(); TabGame *getGame() const { return game; } - void setDeck(DeckList *_deck); + void setDeck(const DeckLoader &_deck); QMenu *getPlayerMenu() const { return playerMenu; } int getId() const { return id; } QString getName() const; diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index 3e5ee00f..42fc6f74 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -27,6 +27,11 @@ #include "main.h" #include "settingscache.h" #include "priceupdater.h" +#include "tab_supervisor.h" +#include "abstractclient.h" +#include "pending_command.h" +#include "pb/response.pb.h" +#include "pb/command_deck_upload.pb.h" void SearchLineEdit::keyPressEvent(QKeyEvent *event) { @@ -351,7 +356,7 @@ void TabDeckEditor::actNewDeck() deckModel->cleanList(); nameEdit->setText(QString()); commentsEdit->setText(QString()); - lastFileName = QString(); + hashLabel->setText(QString()); setWindowModified(false); } @@ -362,24 +367,44 @@ void TabDeckEditor::actLoadDeck() QFileDialog dialog(this, tr("Load deck")); dialog.setDirectory(settingsCache->getDeckPath()); - dialog.setNameFilters(DeckList::fileNameFilters); + dialog.setNameFilters(DeckLoader::fileNameFilters); if (!dialog.exec()) return; QString fileName = dialog.selectedFiles().at(0); - DeckList::FileFormat fmt = DeckList::getFormatFromNameFilter(dialog.selectedNameFilter()); - DeckList *l = new DeckList; + DeckLoader::FileFormat fmt = DeckLoader::getFormatFromNameFilter(dialog.selectedNameFilter()); + + DeckLoader *l = new DeckLoader; if (l->loadFromFile(fileName, fmt)) - setDeck(l, fileName, fmt); + setDeck(l); else delete l; } +void TabDeckEditor::saveDeckRemoteFinished(const Response &response) +{ + if (response.response_code() != Response::RespOk) + QMessageBox::critical(this, tr("Error"), tr("The deck could not be saved.")); + else + setWindowModified(false); +} + bool TabDeckEditor::actSaveDeck() { - if (lastFileName.isEmpty()) + DeckLoader *const deck = deckModel->getDeckList(); + if (deck->getLastRemoteDeckId() != -1) { + Command_DeckUpload cmd; + cmd.set_deck_id(deck->getLastRemoteDeckId()); + cmd.set_deck_list(deck->writeToString_Native().toStdString()); + + PendingCommand *pend = AbstractClient::prepareSessionCommand(cmd); + connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(saveDeckRemoteFinished(Response))); + tabSupervisor->getClient()->sendCommand(pend); + + return true; + } else if (deck->getLastFileName().isEmpty()) return actSaveDeckAs(); - else if (deckModel->getDeckList()->saveToFile(lastFileName, lastFileFormat)) { + else if (deck->saveToFile(deck->getLastFileName(), deck->getLastFileFormat())) { setWindowModified(false); return true; } @@ -394,22 +419,20 @@ bool TabDeckEditor::actSaveDeckAs() dialog.setAcceptMode(QFileDialog::AcceptSave); dialog.setConfirmOverwrite(true); dialog.setDefaultSuffix("cod"); - dialog.setNameFilters(DeckList::fileNameFilters); + dialog.setNameFilters(DeckLoader::fileNameFilters); dialog.selectFile(deckModel->getDeckList()->getName()); if (!dialog.exec()) return false; QString fileName = dialog.selectedFiles().at(0); - DeckList::FileFormat fmt = DeckList::getFormatFromNameFilter(dialog.selectedNameFilter()); + DeckLoader::FileFormat fmt = DeckLoader::getFormatFromNameFilter(dialog.selectedNameFilter()); - if (deckModel->getDeckList()->saveToFile(fileName, fmt)) { - lastFileName = fileName; - lastFileFormat = fmt; - setWindowModified(false); - return true; + if (!deckModel->getDeckList()->saveToFile(fileName, fmt)) { + QMessageBox::critical(this, tr("Error"), tr("The deck could not be saved.\nPlease check that the directory is writable and try again.")); + return false; } - QMessageBox::critical(this, tr("Error"), tr("The deck could not be saved.\nPlease check that the directory is writable and try again.")); - return false; + setWindowModified(false); + return true; } void TabDeckEditor::actLoadDeckFromClipboard() @@ -550,20 +573,18 @@ void TabDeckEditor::finishedUpdatingPrices() aUpdatePrices->setDisabled(false); } -void TabDeckEditor::setDeck(DeckList *_deck, const QString &_lastFileName, DeckList::FileFormat _lastFileFormat) +void TabDeckEditor::setDeck(DeckLoader *_deck) { deckModel->setDeckList(_deck); - lastFileName = _lastFileName; - lastFileFormat = _lastFileFormat; - nameEdit->setText(_deck->getName()); - commentsEdit->setText(_deck->getComments()); + nameEdit->setText(deckModel->getDeckList()->getName()); + commentsEdit->setText(deckModel->getDeckList()->getComments()); updateHash(); deckModel->sort(1); deckView->expandAll(); setWindowModified(false); - db->cacheCardPixmaps(_deck->getCardList()); + db->cacheCardPixmaps(deckModel->getDeckList()->getCardList()); deckView->expandAll(); setWindowModified(false); } diff --git a/cockatrice/src/tab_deck_editor.h b/cockatrice/src/tab_deck_editor.h index b8c32be4..f4fba405 100644 --- a/cockatrice/src/tab_deck_editor.h +++ b/cockatrice/src/tab_deck_editor.h @@ -4,7 +4,6 @@ #include "tab.h" #include #include -#include "decklist.h" class CardDatabaseModel; class CardDatabaseDisplayModel; @@ -15,6 +14,8 @@ class CardInfoWidget; class QTextEdit; class DlgCardSearch; class QLabel; +class DeckLoader; +class Response; class SearchLineEdit : public QLineEdit { private: @@ -57,14 +58,12 @@ private slots: void actUpdatePrices(); void finishedUpdatingPrices(); + void saveDeckRemoteFinished(const Response &r); private: void addCardHelper(QString zoneName); void recursiveExpand(const QModelIndex &index); bool confirmClose(); - QString lastFileName; - DeckList::FileFormat lastFileFormat; - CardDatabaseModel *databaseModel; CardDatabaseDisplayModel *databaseDisplayModel; DeckListModel *deckModel; @@ -90,7 +89,7 @@ public: ~TabDeckEditor(); void retranslateUi(); QString getTabText() const; - void setDeck(DeckList *_deck, const QString &_lastFileName = QString(), DeckList::FileFormat _lastFileFormat = DeckList::CockatriceFormat); + void setDeck(DeckLoader *_deckLoader); void setWindowModified(bool _windowModified); public slots: void closeRequest(); diff --git a/cockatrice/src/tab_deck_storage.cpp b/cockatrice/src/tab_deck_storage.cpp index b98d17d9..b19e370b 100644 --- a/cockatrice/src/tab_deck_storage.cpp +++ b/cockatrice/src/tab_deck_storage.cpp @@ -14,6 +14,7 @@ #include "abstractclient.h" #include "decklist.h" #include "settingscache.h" +#include "deck_loader.h" #include "pending_command.h" #include "pb/response.pb.h" @@ -128,13 +129,12 @@ void TabDeckStorage::actOpenLocalDeck() if (localDirModel->isDir(curLeft)) return; QString filePath = localDirModel->filePath(curLeft); - DeckList *deck = new DeckList; - if (!deck->loadFromFile(filePath, DeckList::CockatriceFormat)) + + DeckLoader deckLoader; + if (!deckLoader.loadFromFile(filePath, DeckLoader::CockatriceFormat)) return; -// WndDeckEditor *deckEditor = new WndDeckEditor; -// deckEditor->setDeck(deck, filePath, DeckList::CockatriceFormat); -// deckEditor->show(); + emit openDeckEditor(&deckLoader); } void TabDeckStorage::actUpload() @@ -145,8 +145,8 @@ void TabDeckStorage::actUpload() QString filePath = localDirModel->filePath(curLeft); QFile deckFile(filePath); QFileInfo deckFileInfo(deckFile); - DeckList deck; - if (!deck.loadFromFile(filePath, DeckList::CockatriceFormat)) + DeckLoader deck; + if (!deck.loadFromFile(filePath, DeckLoader::CockatriceFormat)) return; if (deck.getName().isEmpty()) { bool ok; @@ -157,7 +157,7 @@ void TabDeckStorage::actUpload() deckName = tr("Unnamed deck"); deck.setName(deckName); } - + QString targetPath; RemoteDeckList_TreeModel::Node *curRight = serverDirView->getCurrentItem(); if (!curRight) @@ -205,20 +205,23 @@ void TabDeckStorage::actOpenRemoteDeck() cmd.set_deck_id(curRight->getId()); PendingCommand *pend = client->prepareSessionCommand(cmd); - connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(openRemoteDeckFinished(const Response &))); + connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(openRemoteDeckFinished(Response, CommandContainer))); client->sendCommand(pend); } -void TabDeckStorage::openRemoteDeckFinished(const Response &r) +void TabDeckStorage::openRemoteDeckFinished(const Response &r, const CommandContainer &commandContainer) { if (r.response_code() != Response::RespOk) return; const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext); + const Command_DeckDownload &cmd = commandContainer.session_command(0).GetExtension(Command_DeckDownload::ext); -// WndDeckEditor *deckEditor = new WndDeckEditor; -// deckEditor->setDeck(new DeckList(QString::fromStdString(resp.deck()))); -// deckEditor->show(); + DeckLoader loader; + if (!loader.loadFromRemote(QString::fromStdString(resp.deck()), cmd.deck_id())) + return; + + emit openDeckEditor(&loader); } void TabDeckStorage::actDownload() @@ -255,8 +258,8 @@ void TabDeckStorage::downloadFinished(const Response &r, const CommandContainer const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext); QString filePath = extraData.toString(); - DeckList deck(QString::fromStdString(resp.deck())); - deck.saveToFile(filePath, DeckList::CockatriceFormat); + DeckLoader deck(QString::fromStdString(resp.deck())); + deck.saveToFile(filePath, DeckLoader::CockatriceFormat); } void TabDeckStorage::actNewFolder() diff --git a/cockatrice/src/tab_deck_storage.h b/cockatrice/src/tab_deck_storage.h index 6996c435..a3ef7fc4 100644 --- a/cockatrice/src/tab_deck_storage.h +++ b/cockatrice/src/tab_deck_storage.h @@ -13,6 +13,7 @@ class QGroupBox; class RemoteDeckList_TreeWidget; class CommandContainer; class Response; +class DeckLoader; class TabDeckStorage : public Tab { Q_OBJECT @@ -34,7 +35,7 @@ private slots: void actDeleteLocalDeck(); void actOpenRemoteDeck(); - void openRemoteDeckFinished(const Response &r); + void openRemoteDeckFinished(const Response &r, const CommandContainer &commandContainer); void actDownload(); void downloadFinished(const Response &r, const CommandContainer &commandContainer, const QVariant &extraData); @@ -49,6 +50,8 @@ public: TabDeckStorage(TabSupervisor *_tabSupervisor, AbstractClient *_client); void retranslateUi(); QString getTabText() const { return tr("Deck storage"); } +signals: + void openDeckEditor(const DeckLoader *deckLoader); }; #endif diff --git a/cockatrice/src/tab_game.cpp b/cockatrice/src/tab_game.cpp index 5524fc78..32144445 100644 --- a/cockatrice/src/tab_game.cpp +++ b/cockatrice/src/tab_game.cpp @@ -22,7 +22,7 @@ #include "zoneviewzone.h" #include "zoneviewwidget.h" #include "deckview.h" -#include "decklist.h" +#include "deck_loader.h" #include "dlg_load_remote_deck.h" #include "abstractclient.h" #include "carditem.h" @@ -149,21 +149,20 @@ void DeckViewContainer::loadLocalDeck() { QFileDialog dialog(this, tr("Load deck")); dialog.setDirectory(settingsCache->getDeckPath()); - dialog.setNameFilters(DeckList::fileNameFilters); + dialog.setNameFilters(DeckLoader::fileNameFilters); if (!dialog.exec()) return; QString fileName = dialog.selectedFiles().at(0); - DeckList::FileFormat fmt = DeckList::getFormatFromNameFilter(dialog.selectedNameFilter()); - DeckList *deck = new DeckList; - if (!deck->loadFromFile(fileName, fmt)) { - delete deck; - // Error message + DeckLoader::FileFormat fmt = DeckLoader::getFormatFromNameFilter(dialog.selectedNameFilter()); + DeckLoader deck; + if (!deck.loadFromFile(fileName, fmt)) { + QMessageBox::critical(this, tr("Error"), tr("The selected file could not be loaded.")); return; } Command_DeckSelect cmd; - cmd.set_deck(deck->writeToString_Native().toStdString()); + cmd.set_deck(deck.writeToString_Native().toStdString()); PendingCommand *pend = static_cast(parent())->prepareGameCommand(cmd); connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(deckSelectFinished(const Response &))); static_cast(parent())->sendGameCommand(pend, playerId); @@ -184,8 +183,8 @@ void DeckViewContainer::loadRemoteDeck() void DeckViewContainer::deckSelectFinished(const Response &r) { const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext); - DeckList *newDeck = new DeckList(QString::fromStdString(resp.deck())); - db->cacheCardPixmaps(newDeck->getCardList()); + DeckLoader newDeck(QString::fromStdString(resp.deck())); + db->cacheCardPixmaps(newDeck.getCardList()); setDeck(newDeck); } @@ -227,7 +226,7 @@ void DeckViewContainer::setSideboardLocked(bool locked) deckView->resetSideboardPlan(); } -void DeckViewContainer::setDeck(DeckList *deck) +void DeckViewContainer::setDeck(const DeckLoader &deck) { deckView->setDeck(deck); readyStartButton->setEnabled(true); @@ -949,8 +948,8 @@ void TabGame::eventGameStateChanged(const Event_GameStateChanged &event, int /*e if (player->getLocal()) { DeckViewContainer *deckViewContainer = deckViewContainers.value(playerId); if (playerInfo.has_deck_list()) { - DeckList *newDeck = new DeckList(QString::fromStdString(playerInfo.deck_list())); - db->cacheCardPixmaps(newDeck->getCardList()); + DeckLoader newDeck(QString::fromStdString(playerInfo.deck_list())); + db->cacheCardPixmaps(newDeck.getCardList()); deckViewContainer->setDeck(newDeck); player->setDeck(newDeck); } diff --git a/cockatrice/src/tab_game.h b/cockatrice/src/tab_game.h index 96b4dadd..ebb4d25f 100644 --- a/cockatrice/src/tab_game.h +++ b/cockatrice/src/tab_game.h @@ -48,7 +48,7 @@ class CardZone; class AbstractCardItem; class CardItem; class TabGame; -class DeckList; +class DeckLoader; class QVBoxLayout; class QHBoxLayout; class GameReplay; @@ -92,7 +92,7 @@ public: void setButtonsVisible(bool _visible); void setReadyStart(bool ready); void setSideboardLocked(bool locked); - void setDeck(DeckList *deck); + void setDeck(const DeckLoader &deck); }; class TabGame : public Tab { @@ -171,7 +171,7 @@ signals: void containerProcessingStarted(const GameEventContext &context); void containerProcessingDone(); void openMessageDialog(const QString &userName, bool focus); - void openDeckEditor(DeckList *deck); + void openDeckEditor(const DeckLoader &deck); private slots: void replayNextEvent(); void replayFinished(); diff --git a/cockatrice/src/tab_supervisor.cpp b/cockatrice/src/tab_supervisor.cpp index ac6eae48..56ce55b8 100644 --- a/cockatrice/src/tab_supervisor.cpp +++ b/cockatrice/src/tab_supervisor.cpp @@ -155,6 +155,7 @@ void TabSupervisor::start(const ServerInfo_User &_userInfo) if (userInfo->user_level() & ServerInfo_User::IsRegistered) { tabDeckStorage = new TabDeckStorage(this, client); + connect(tabDeckStorage, SIGNAL(openDeckEditor(const DeckLoader *)), this, SLOT(addDeckEditorTab(const DeckLoader *))); myAddTab(tabDeckStorage); tabReplays = new TabReplays(this, client); @@ -279,7 +280,8 @@ void TabSupervisor::gameJoined(const Event_GameJoined &event) TabGame *tab = new TabGame(this, QList() << client, event, roomGameTypes); connect(tab, SIGNAL(gameClosing(TabGame *)), this, SLOT(gameLeft(TabGame *))); connect(tab, SIGNAL(openMessageDialog(const QString &, bool)), this, SLOT(addMessageTab(const QString &, bool))); - connect(tab, SIGNAL(openDeckEditor(DeckList *)), this, SLOT(addDeckEditorTab(DeckList *))); + connect(tab, SIGNAL(openDeckEditor(DeckList *, QString, DeckList::FileFormat)), this, SLOT(addDeckEditorTab(DeckList *, QString, DeckList::FileFormat))); + connect(tab, SIGNAL(openDeckEditor(DeckList *, int)), this, SLOT(addDeckEditorTab(DeckList *, int))); int tabIndex = myAddTab(tab); addCloseButtonToTab(tab, tabIndex); gameTabs.insert(event.game_info().game_id(), tab); @@ -384,11 +386,11 @@ void TabSupervisor::talkLeft(TabMessage *tab) removeTab(indexOf(tab)); } -TabDeckEditor *TabSupervisor::addDeckEditorTab(DeckList *deckToOpen) +TabDeckEditor *TabSupervisor::addDeckEditorTab(const DeckLoader *deckToOpen) { TabDeckEditor *tab = new TabDeckEditor(this); if (deckToOpen) - tab->setDeck(deckToOpen); + tab->setDeck(new DeckLoader(*deckToOpen)); connect(tab, SIGNAL(deckEditorClosing(TabDeckEditor *)), this, SLOT(deckEditorClosed(TabDeckEditor *))); int tabIndex = myAddTab(tab); addCloseButtonToTab(tab, tabIndex); diff --git a/cockatrice/src/tab_supervisor.h b/cockatrice/src/tab_supervisor.h index b9785e3a..70941e12 100644 --- a/cockatrice/src/tab_supervisor.h +++ b/cockatrice/src/tab_supervisor.h @@ -4,6 +4,7 @@ #include #include #include +#include "deck_loader.h" class QMenu; class AbstractClient; @@ -75,7 +76,7 @@ signals: void localGameEnded(); void adminLockChanged(bool lock); public slots: - TabDeckEditor *addDeckEditorTab(DeckList *deckToOpen); + TabDeckEditor *addDeckEditorTab(const DeckLoader *deckToOpen); void openReplay(GameReplay *replay); private slots: void closeButtonPressed(); diff --git a/common/decklist.cpp b/common/decklist.cpp index 72ddc8c1..4612ec69 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -264,21 +264,19 @@ QVector > InnerDecklistNode::sort(Qt::SortOrder order) return result; } -const QStringList DeckList::fileNameFilters = QStringList() - << QObject::tr("Cockatrice decks (*.cod)") - << QObject::tr("Plain text decks (*.dec *.mwDeck)") - << QObject::tr("All files (*.*)"); - DeckList::DeckList() { root = new InnerDecklistNode; } -DeckList::DeckList(DeckList *other) +DeckList::DeckList(const DeckList &other) + : name(other.name), + comments(other.comments), + deckHash(other.deckHash) { - root = new InnerDecklistNode(other->getRoot()); + root = new InnerDecklistNode(other.getRoot()); - QMapIterator spIterator(other->getSideboardPlans()); + QMapIterator spIterator(other.getSideboardPlans()); while (spIterator.hasNext()) { spIterator.next(); sideboardPlans.insert(spIterator.key(), new SideboardPlan(spIterator.key(), spIterator.value()->getMoveList())); @@ -289,9 +287,7 @@ DeckList::DeckList(DeckList *other) DeckList::DeckList(const QString &nativeString) { root = new InnerDecklistNode; - - QXmlStreamReader xml(nativeString); - loadFromXml(&xml); + loadFromString_Native(nativeString); } DeckList::~DeckList() @@ -362,13 +358,14 @@ void DeckList::write(QXmlStreamWriter *xml) xml->writeEndElement(); } -void DeckList::loadFromXml(QXmlStreamReader *xml) +bool DeckList::loadFromXml(QXmlStreamReader *xml) { + cleanList(); while (!xml->atEnd()) { xml->readNext(); if (xml->isStartElement()) { if (xml->name() != "cockatrice_deck") - return; + return false; while (!xml->atEnd()) { xml->readNext(); if (!readElement(xml)) @@ -377,6 +374,13 @@ void DeckList::loadFromXml(QXmlStreamReader *xml) } } updateDeckHash(); + return true; +} + +bool DeckList::loadFromString_Native(const QString &nativeString) +{ + QXmlStreamReader xml(nativeString); + return loadFromXml(&xml); } QString DeckList::writeToString_Native() @@ -410,6 +414,8 @@ bool DeckList::saveToFile_Native(QIODevice *device) bool DeckList::loadFromStream_Plain(QTextStream &in) { + cleanList(); + InnerDecklistNode *main = 0, *side = 0; int okRows = 0; @@ -474,52 +480,13 @@ bool DeckList::saveToFile_Plain(QIODevice *device) return saveToStream_Plain(out); } -bool DeckList::loadFromFile(const QString &fileName, FileFormat fmt) -{ - QFile file(fileName); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) - return false; - cleanList(); - - bool result = false; - switch (fmt) { - case PlainTextFormat: result = loadFromFile_Plain(&file); break; - case CockatriceFormat: result = loadFromFile_Native(&file); break; - } - if (result) - emit deckLoaded(); - return result; -} - -bool DeckList::saveToFile(const QString &fileName, FileFormat fmt) -{ - QFile file(fileName); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) - return false; - - bool result = false; - switch (fmt) { - case PlainTextFormat: result = saveToFile_Plain(&file); break; - case CockatriceFormat: result = saveToFile_Native(&file); break; - } - return result; -} - -DeckList::FileFormat DeckList::getFormatFromNameFilter(const QString &selectedNameFilter) -{ - switch (fileNameFilters.indexOf(selectedNameFilter)) { - case 0: return CockatriceFormat; - case 1: return PlainTextFormat; - } - return PlainTextFormat; -} - void DeckList::cleanList() { root->clearTree(); setName(); setComments(); - updateDeckHash(); + deckHash = QString(); + emit deckHashChanged(); } void DeckList::getCardListHelper(InnerDecklistNode *item, QSet &result) const diff --git a/common/decklist.h b/common/decklist.h index d3c852c3..e4532b27 100644 --- a/common/decklist.h +++ b/common/decklist.h @@ -107,44 +107,36 @@ public: void setName(const QString &_name) { name = _name; } float getPrice() const { return price; } void setPrice(const float _price) { price = _price; } - }; +}; class DeckList : public QObject { Q_OBJECT -public: - enum FileFormat { PlainTextFormat, CockatriceFormat }; private: QString name, comments; - QString lastFileName; QString deckHash; - FileFormat lastFileFormat; QMap sideboardPlans; InnerDecklistNode *root; - QString currentElementText; void getCardListHelper(InnerDecklistNode *node, QSet &result) const; signals: - void deckLoaded(); void deckHashChanged(); public slots: void setName(const QString &_name = QString()) { name = _name; } void setComments(const QString &_comments = QString()) { comments = _comments; } public: - static const QStringList fileNameFilters; DeckList(); - DeckList(DeckList *other); + DeckList(const DeckList &other); DeckList(const QString &nativeString); ~DeckList(); QString getName() const { return name; } QString getComments() const { return comments; } - QString getLastFileName() const { return lastFileName; } - FileFormat getLastFileFormat() const { return lastFileFormat; } QList getCurrentSideboardPlan(); void setCurrentSideboardPlan(const QList &plan); const QMap &getSideboardPlans() const { return sideboardPlans; } bool readElement(QXmlStreamReader *xml); void write(QXmlStreamWriter *xml); - void loadFromXml(QXmlStreamReader *xml); + bool loadFromXml(QXmlStreamReader *xml); + bool loadFromString_Native(const QString &nativeString); QString writeToString_Native(); bool loadFromFile_Native(QIODevice *device); bool saveToFile_Native(QIODevice *device); @@ -152,9 +144,6 @@ public: bool loadFromFile_Plain(QIODevice *device); bool saveToStream_Plain(QTextStream &stream); bool saveToFile_Plain(QIODevice *device); - bool loadFromFile(const QString &fileName, FileFormat fmt); - bool saveToFile(const QString &fileName, FileFormat fmt); - static FileFormat getFormatFromNameFilter(const QString &selectedNameFilter); void cleanList(); bool isEmpty() const { return root->isEmpty() && name.isEmpty() && comments.isEmpty() && sideboardPlans.isEmpty(); } diff --git a/common/pb/command_deck_upload.proto b/common/pb/command_deck_upload.proto index b81a6351..41f43ab4 100644 --- a/common/pb/command_deck_upload.proto +++ b/common/pb/command_deck_upload.proto @@ -4,7 +4,7 @@ message Command_DeckUpload { extend SessionCommand { optional Command_DeckUpload ext = 1013; } - optional string path = 1; - optional string deck_list = 2; + optional string path = 1; // to upload a new deck + optional uint32 deck_id = 2; // to replace an existing deck + optional string deck_list = 3; } - diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index 5e94db02..7cc05630 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -455,13 +455,10 @@ Response::ResponseCode ServerSocketInterface::cmdDeckUpload(const Command_DeckUp if (authState != PasswordRight) return Response::RespFunctionNotAllowed; - servatrice->checkSql(); - if (!cmd.has_deck_list()) return Response::RespInvalidData; - int folderId = getDeckPathId(QString::fromStdString(cmd.path())); - if (folderId == -1) - return Response::RespNameNotFound; + + servatrice->checkSql(); QString deckStr = QString::fromStdString(cmd.deck_list()); DeckList deck(deckStr); @@ -469,22 +466,48 @@ Response::ResponseCode ServerSocketInterface::cmdDeckUpload(const Command_DeckUp QString deckName = deck.getName(); if (deckName.isEmpty()) deckName = "Unnamed deck"; - - QMutexLocker locker(&servatrice->dbMutex); - QSqlQuery query; - query.prepare("insert into " + servatrice->getDbPrefix() + "_decklist_files (id_folder, user, name, upload_time, content) values(:id_folder, :user, :name, NOW(), :content)"); - query.bindValue(":id_folder", folderId); - query.bindValue(":user", QString::fromStdString(userInfo->name())); - query.bindValue(":name", deckName); - query.bindValue(":content", deckStr); - servatrice->execSqlQuery(query); - Response_DeckUpload *re = new Response_DeckUpload; - ServerInfo_DeckStorage_TreeItem *fileInfo = re->mutable_new_file(); - fileInfo->set_id(query.lastInsertId().toInt()); - fileInfo->set_name(deckName.toStdString()); - fileInfo->mutable_file()->set_creation_time(QDateTime::currentDateTime().toTime_t()); - rc.setResponseExtension(re); + if (cmd.has_path()) { + int folderId = getDeckPathId(QString::fromStdString(cmd.path())); + if (folderId == -1) + return Response::RespNameNotFound; + + QMutexLocker locker(&servatrice->dbMutex); + QSqlQuery query; + query.prepare("insert into " + servatrice->getDbPrefix() + "_decklist_files (id_folder, user, name, upload_time, content) values(:id_folder, :user, :name, NOW(), :content)"); + query.bindValue(":id_folder", folderId); + query.bindValue(":user", QString::fromStdString(userInfo->name())); + query.bindValue(":name", deckName); + query.bindValue(":content", deckStr); + servatrice->execSqlQuery(query); + + Response_DeckUpload *re = new Response_DeckUpload; + ServerInfo_DeckStorage_TreeItem *fileInfo = re->mutable_new_file(); + fileInfo->set_id(query.lastInsertId().toInt()); + fileInfo->set_name(deckName.toStdString()); + fileInfo->mutable_file()->set_creation_time(QDateTime::currentDateTime().toTime_t()); + rc.setResponseExtension(re); + } else if (cmd.has_deck_id()) { + QMutexLocker locker(&servatrice->dbMutex); + QSqlQuery query; + query.prepare("update " + servatrice->getDbPrefix() + "_decklist_files set name=:name, upload_time=NOW(), content=:content where id = :id_deck and user = :user"); + query.bindValue(":id_deck", cmd.deck_id()); + query.bindValue(":user", QString::fromStdString(userInfo->name())); + query.bindValue(":name", deckName); + query.bindValue(":content", deckStr); + servatrice->execSqlQuery(query); + + if (query.numRowsAffected() == 0) + return Response::RespNameNotFound; + + Response_DeckUpload *re = new Response_DeckUpload; + ServerInfo_DeckStorage_TreeItem *fileInfo = re->mutable_new_file(); + fileInfo->set_id(cmd.deck_id()); + fileInfo->set_name(deckName.toStdString()); + fileInfo->mutable_file()->set_creation_time(QDateTime::currentDateTime().toTime_t()); + rc.setResponseExtension(re); + } else + return Response::RespInvalidData; return Response::RespOk; }