From 6c038a91e4cf567fe436693cdef63f7e47fb3e17 Mon Sep 17 00:00:00 2001 From: John Robe Date: Tue, 5 Dec 2017 20:34:19 -0700 Subject: [PATCH] Deck export (#2938) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added “export deck” option to export deck to decklist.org for viewing/printing. Implemented using a new menu item, added shortcut ability, and functionality to parse the deck correctly and add main/sideboard cards to decklist. Per issue #2931 --- cockatrice/src/deck_loader.cpp | 69 ++++++++++++++++++- cockatrice/src/deck_loader.h | 1 + cockatrice/src/sequenceEdit/ui_shortcutstab.h | 13 ++++ cockatrice/src/shortcutssettings.cpp | 1 + cockatrice/src/tab_deck_editor.cpp | 44 +++++++++++- cockatrice/src/tab_deck_editor.h | 3 +- 6 files changed, 126 insertions(+), 5 deletions(-) diff --git a/cockatrice/src/deck_loader.cpp b/cockatrice/src/deck_loader.cpp index d5a06af2..1bdc7e3f 100644 --- a/cockatrice/src/deck_loader.cpp +++ b/cockatrice/src/deck_loader.cpp @@ -103,6 +103,73 @@ bool DeckLoader::saveToFile(const QString &fileName, FileFormat fmt) return result; } +//This struct is here to support the forEachCard function call, defined in decklist. It +//requires a function to be called for each card, and passes an inner node and a card for +//each card in the decklist. +struct FormatDeckListForExport +{ + //Create refrences for the strings that will be passed in. + QString &mainBoardCards; + QString &sideBoardCards; + //create main operator for struct, allowing the foreachcard to work. + FormatDeckListForExport( + QString &_mainBoardCards, QString &_sideBoardCards + ) : mainBoardCards(_mainBoardCards), + sideBoardCards(_sideBoardCards){}; + + void operator()(const InnerDecklistNode *node, const DecklistCardNode *card) const{ + //Get the card name + CardInfo * dbCard = db->getCard(card->getName()); + if (!dbCard || dbCard->getIsToken()){ + //If it's a token, we don't care about the card. + return; + } + //Check if it's a sideboard card. + if(node->getName() == DECK_ZONE_SIDE){ + //Get the number of cards and add the card name + sideBoardCards+=QString::number(card->getNumber()); + //Add a space between card num and name + sideBoardCards+="%20"; + //Add card name + sideBoardCards+=card->getName(); + //Add a return at the end of the card + sideBoardCards+="%0A"; + } + //If it's a mainboard card, do the same thing, but for the mainboard card string + else{ + mainBoardCards+=QString::number(card->getNumber()); + mainBoardCards+="%20"; + mainBoardCards+=card->getName(); + mainBoardCards+="%0A"; + } + } +}; + +//Export deck to decklist function, called to format the deck in a way to be sent to a server +QString DeckLoader::exportDeckToDecklist() +{ + //Add the base url + QString deckString = "https://www.decklist.org/?"; + //Create two strings to pass to function + QString mainBoardCards, sideBoardCards; + //Set up the struct to call. + FormatDeckListForExport formatDeckListForExport(mainBoardCards, sideBoardCards); + //call our struct function for each card in the deck + forEachCard(formatDeckListForExport); + //Remove the extra return at the end of the last cards + mainBoardCards.chop(3); + sideBoardCards.chop(3); + //if after we've called it for each card, and the strings are empty, we know that + //there were no non-token cards in the deck, so show an error message. + if((QString::compare(mainBoardCards, "", Qt::CaseInsensitive) == 0) && + (QString::compare(sideBoardCards, "", Qt::CaseInsensitive) == 0)) { + return ""; + } + //return a string with the url for decklist export + deckString+="deckmain="+mainBoardCards+"&deckside="+sideBoardCards; + return deckString; +} + DeckLoader::FileFormat DeckLoader::getFormatFromName(const QString &fileName) { if (fileName.endsWith(".cod", Qt::CaseInsensitive)) { @@ -215,4 +282,4 @@ QString DeckLoader::getCompleteCardName(const QString cardName) const } return cardName; -} \ No newline at end of file +} diff --git a/cockatrice/src/deck_loader.h b/cockatrice/src/deck_loader.h index 9f388042..233136ee 100644 --- a/cockatrice/src/deck_loader.h +++ b/cockatrice/src/deck_loader.h @@ -28,6 +28,7 @@ public: bool loadFromFile(const QString &fileName, FileFormat fmt); bool loadFromRemote(const QString &nativeString, int remoteDeckId); bool saveToFile(const QString &fileName, FileFormat fmt); + QString exportDeckToDecklist(); // overload bool saveToStream_Plain(QTextStream &out); diff --git a/cockatrice/src/sequenceEdit/ui_shortcutstab.h b/cockatrice/src/sequenceEdit/ui_shortcutstab.h index ef5a66ee..a58eeec9 100644 --- a/cockatrice/src/sequenceEdit/ui_shortcutstab.h +++ b/cockatrice/src/sequenceEdit/ui_shortcutstab.h @@ -80,6 +80,8 @@ public: SequenceEdit *TabDeckEditor_aIncrement; QLabel *lbl_TabDeckEditor_aSaveDeck; SequenceEdit *TabDeckEditor_aSaveDeck; + QLabel *lbl_TabDeckEditor_aExportDeckDecklist; + SequenceEdit *TabDeckEditor_aExportDeckDecklist; QLabel *lbl_TabDeckEditor_aDecrement; SequenceEdit *TabDeckEditor_aDecrement; QLabel *lbl_TabDeckEditor_aSaveDeckAs; @@ -642,6 +644,16 @@ public: TabDeckEditor_aSaveDeckToClipboard->setObjectName("TabDeckEditor_aSaveDeckToClipboard"); gridLayout->addWidget(TabDeckEditor_aSaveDeckToClipboard, 8, 3, 1, 1); + + lbl_TabDeckEditor_aExportDeckDecklist = new QLabel(groupBox_2); + lbl_TabDeckEditor_aExportDeckDecklist->setObjectName("lbl_TabDeckEditor_aExportDeckDecklist"); + + gridLayout->addWidget(lbl_TabDeckEditor_aExportDeckDecklist, 9, 2, 1, 1); + + TabDeckEditor_aExportDeckDecklist = new SequenceEdit("TabDeckEditor/aExportDeckDecklist",groupBox_2); + TabDeckEditor_aExportDeckDecklist->setObjectName("TabDeckEditor_aExportDeckDecklist"); + + gridLayout->addWidget(TabDeckEditor_aExportDeckDecklist, 9, 3, 1, 1); gridLayout_3->addWidget(groupBox_2, 0, 1, 1, 1); @@ -1749,6 +1761,7 @@ public: lbl_TabDeckEditor_aResetLayout->setText(QApplication::translate("shortcutsTab", "Reset layout", 0)); lbl_TabDeckEditor_aIncrement->setText(QApplication::translate("shortcutsTab", "Add card", 0)); lbl_TabDeckEditor_aSaveDeck->setText(QApplication::translate("shortcutsTab", "Save deck", 0)); + lbl_TabDeckEditor_aExportDeckDecklist->setText(QApplication::translate("shortcutsTab", "Export deck", 0)); lbl_TabDeckEditor_aDecrement->setText(QApplication::translate("shortcutsTab", "Remove card", 0)); lbl_TabDeckEditor_aSaveDeckAs->setText(QApplication::translate("shortcutsTab", "Save deck as", 0)); lbl_TabDeckEditor_aLoadDeck->setText(QApplication::translate("shortcutsTab", "Load deck", 0)); diff --git a/cockatrice/src/shortcutssettings.cpp b/cockatrice/src/shortcutssettings.cpp index 911bab29..c6adeb3e 100644 --- a/cockatrice/src/shortcutssettings.cpp +++ b/cockatrice/src/shortcutssettings.cpp @@ -157,6 +157,7 @@ void ShortcutsSettings::fillDefaultShorcuts() defaultShortCuts["TabDeckEditor/aDecrement"] = parseSequenceString("-"); defaultShortCuts["TabDeckEditor/aEditSets"] = parseSequenceString(""); defaultShortCuts["TabDeckEditor/aEditTokens"] = parseSequenceString(""); + defaultShortCuts["TabDeckEditor/aExportDeckDecklist"] = parseSequenceString(""); defaultShortCuts["TabDeckEditor/aIncrement"] = parseSequenceString("+"); defaultShortCuts["TabDeckEditor/aLoadDeck"] = parseSequenceString("Ctrl+O"); defaultShortCuts["TabDeckEditor/aLoadDeckFromClipboard"] = parseSequenceString("Ctrl+Shift+V"); diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index d1b49991..828173bd 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "tab_deck_editor.h" #include "carddatabase.h" #include "pictureloader.h" @@ -226,6 +227,9 @@ void TabDeckEditor::createMenus() aPrintDeck = new QAction(QString(), this); connect(aPrintDeck, SIGNAL(triggered()), this, SLOT(actPrintDeck())); + aExportDeckDecklist = new QAction(QString(), this); + connect(aExportDeckDecklist, SIGNAL(triggered()), this, SLOT(actExportDeckDecklist())); + aAnalyzeDeckDeckstats = new QAction(QString(), this); connect(aAnalyzeDeckDeckstats, SIGNAL(triggered()), this, SLOT(actAnalyzeDeckDeckstats())); @@ -233,6 +237,7 @@ void TabDeckEditor::createMenus() connect(aAnalyzeDeckTappedout, SIGNAL(triggered()), this, SLOT(actAnalyzeDeckTappedout())); analyzeDeckMenu = new QMenu(this); + analyzeDeckMenu->addAction(aExportDeckDecklist); analyzeDeckMenu->addAction(aAnalyzeDeckDeckstats); analyzeDeckMenu->addAction(aAnalyzeDeckTappedout); @@ -453,6 +458,7 @@ void TabDeckEditor::refreshShortcuts() aNewDeck->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aNewDeck")); aLoadDeck->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aLoadDeck")); aSaveDeck->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aSaveDeck")); + aExportDeckDecklist->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aExportDeckDecklist")); aSaveDeckAs->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aSaveDeckAs")); aLoadDeckFromClipboard->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aLoadDeckFromClipboard")); aPrintDeck->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aPrintDeck")); @@ -546,9 +552,10 @@ void TabDeckEditor::retranslateUi() aSaveDeckToClipboard->setText(tr("Save deck to clip&board")); aPrintDeck->setText(tr("&Print deck...")); - analyzeDeckMenu->setTitle(tr("&Analyze deck online")); - aAnalyzeDeckDeckstats->setText("deckstats.net"); - aAnalyzeDeckTappedout->setText("tappedout.net"); + analyzeDeckMenu->setTitle(tr("&Send deck to online service")); + aExportDeckDecklist->setText(tr("Create decklist (decklist.org)")); + aAnalyzeDeckDeckstats->setText(tr("Analyze deck (deckstats.net)")); + aAnalyzeDeckTappedout->setText(tr("Analyze deck (tappedout.net)")); aClose->setText(tr("&Close")); @@ -764,6 +771,37 @@ void TabDeckEditor::actPrintDeck() dlg->exec(); } +//Action called when export deck to decklist menu item is pressed. +void TabDeckEditor::actExportDeckDecklist() +{ + //Get the decklist class for the deck. + DeckLoader *const deck = deckModel->getDeckList(); + //create a string to load the decklist url into. + QString decklistUrlString; + //check if deck is not null + if(deck){ + //Get the decklist url string from the deck loader class. + decklistUrlString = deck->exportDeckToDecklist(); + //Check to make sure the string isn't empty. + if(QString::compare(decklistUrlString, "", Qt::CaseInsensitive) == 0){ + //Show an error if the deck is empty, and return. + QMessageBox::critical(this, tr("Error"), tr("There are no cards in your deck to be exported")); + return; + } + //Encode the string recieved from the model to make sure all characters are encoded. + //first we put it into a qurl object + QUrl decklistUrl = QUrl(decklistUrlString); + //we get the correctly encoded url. + decklistUrlString = decklistUrl.toEncoded(); + //We open the url in the user's default browser + QDesktopServices::openUrl(decklistUrlString); + } + else{ + //if there's no deck loader object, return an error + QMessageBox::critical(this, tr("Error"), tr("No deck was selected to be saved.")); + } +} + void TabDeckEditor::actAnalyzeDeckDeckstats() { DeckStatsInterface *interface = new DeckStatsInterface( diff --git a/cockatrice/src/tab_deck_editor.h b/cockatrice/src/tab_deck_editor.h index 22211b41..22d9ba87 100644 --- a/cockatrice/src/tab_deck_editor.h +++ b/cockatrice/src/tab_deck_editor.h @@ -52,6 +52,7 @@ class TabDeckEditor : public Tab { void actLoadDeckFromClipboard(); void actSaveDeckToClipboard(); void actPrintDeck(); + void actExportDeckDecklist(); void actAnalyzeDeckDeckstats(); void actAnalyzeDeckTappedout(); @@ -110,7 +111,7 @@ private: QWidget *filterBox; QMenu *deckMenu, *viewMenu, *cardInfoDockMenu, *deckDockMenu, *filterDockMenu, *analyzeDeckMenu; - QAction *aNewDeck, *aLoadDeck, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, *aSaveDeckToClipboard, *aPrintDeck, *aAnalyzeDeckDeckstats, *aAnalyzeDeckTappedout, *aClose; + QAction *aNewDeck, *aLoadDeck, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, *aSaveDeckToClipboard, *aPrintDeck, *aExportDeckDecklist, *aAnalyzeDeckDeckstats, *aAnalyzeDeckTappedout, *aClose; QAction *aClearFilterAll, *aClearFilterOne; QAction *aAddCard, *aAddCardToSideboard, *aRemoveCard, *aIncrement, *aDecrement; QAction *aResetLayout;