diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index b047b0ca6..c2dd3b5e0 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -12,6 +12,7 @@ set(cockatrice_SOURCES src/game/board/abstract_graphics_item.cpp src/game/board/arrow_item.cpp src/game/board/arrow_target.cpp + src/client/ui/widgets/general/display/banner_widget.cpp src/game/cards/card_database.cpp src/game/cards/card_database_manager.cpp src/game/cards/card_database_model.cpp @@ -141,6 +142,7 @@ set(cockatrice_SOURCES src/client/tabs/tab.cpp src/client/tabs/tab_account.cpp src/client/tabs/tab_admin.cpp + src/client/tabs/abstract_tab_deck_editor.cpp src/client/tabs/tab_deck_editor.cpp src/client/tabs/tab_deck_storage.cpp src/client/tabs/tab_game.cpp @@ -177,6 +179,7 @@ set(cockatrice_SOURCES src/client/ui/window_main.cpp src/game/zones/view_zone_widget.cpp src/game/zones/view_zone.cpp + src/client/menus/deck_editor/deck_editor_menu.cpp src/client/tabs/visual_deck_storage/tab_deck_storage_visual.cpp src/client/ui/widgets/cards/deck_preview_card_picture_widget.cpp src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_color_identity_widget.cpp @@ -192,6 +195,11 @@ set(cockatrice_SOURCES src/client/ui/widgets/visual_deck_storage/visual_deck_storage_search_widget.cpp src/client/ui/widgets/visual_deck_storage/visual_deck_storage_sort_widget.cpp src/client/ui/widgets/visual_deck_storage/visual_deck_storage_tag_filter_widget.cpp + src/client/ui/widgets/deck_editor/deck_editor_card_info_dock_widget.cpp + src/client/ui/widgets/deck_editor/deck_editor_database_display_widget.cpp + src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.cpp + src/client/ui/widgets/deck_editor/deck_editor_filter_dock_widget.cpp + src/client/ui/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp ${VERSION_STRING_CPP} ) diff --git a/cockatrice/src/client/menus/deck_editor/deck_editor_menu.cpp b/cockatrice/src/client/menus/deck_editor/deck_editor_menu.cpp new file mode 100644 index 000000000..935cd7580 --- /dev/null +++ b/cockatrice/src/client/menus/deck_editor/deck_editor_menu.cpp @@ -0,0 +1,171 @@ +#include "deck_editor_menu.h" + +#include "../../../settings/cache_settings.h" +#include "../../../settings/shortcuts_settings.h" + +DeckEditorMenu::DeckEditorMenu(QWidget *parent, AbstractTabDeckEditor *_deckEditor) + : QMenu(parent), deckEditor(_deckEditor) +{ + aNewDeck = new QAction(QString(), this); + connect(aNewDeck, SIGNAL(triggered()), deckEditor, SLOT(actNewDeck())); + + aLoadDeck = new QAction(QString(), this); + connect(aLoadDeck, SIGNAL(triggered()), deckEditor, SLOT(actLoadDeck())); + + loadRecentDeckMenu = new QMenu(this); + connect(&SettingsCache::instance().recents(), &RecentsSettings::recentlyOpenedDeckPathsChanged, this, + &DeckEditorMenu::updateRecentlyOpened); + + aClearRecents = new QAction(QString(), this); + connect(aClearRecents, &QAction::triggered, this, &DeckEditorMenu::actClearRecents); + + updateRecentlyOpened(); + + aSaveDeck = new QAction(QString(), this); + connect(aSaveDeck, SIGNAL(triggered()), deckEditor, SLOT(actSaveDeck())); + + aSaveDeckAs = new QAction(QString(), this); + connect(aSaveDeckAs, SIGNAL(triggered()), deckEditor, SLOT(actSaveDeckAs())); + + aLoadDeckFromClipboard = new QAction(QString(), this); + connect(aLoadDeckFromClipboard, SIGNAL(triggered()), deckEditor, SLOT(actLoadDeckFromClipboard())); + + aSaveDeckToClipboard = new QAction(QString(), this); + connect(aSaveDeckToClipboard, SIGNAL(triggered()), deckEditor, SLOT(actSaveDeckToClipboard())); + + aSaveDeckToClipboardNoSetNameAndNumber = new QAction(QString(), this); + connect(aSaveDeckToClipboardNoSetNameAndNumber, SIGNAL(triggered()), deckEditor, + SLOT(actSaveDeckToClipboardNoSetNameAndNumber())); + + aSaveDeckToClipboardRaw = new QAction(QString(), this); + connect(aSaveDeckToClipboardRaw, SIGNAL(triggered()), deckEditor, SLOT(actSaveDeckToClipboardRaw())); + + aSaveDeckToClipboardRawNoSetNameAndNumber = new QAction(QString(), this); + connect(aSaveDeckToClipboardRawNoSetNameAndNumber, SIGNAL(triggered()), deckEditor, + SLOT(actSaveDeckToClipboardRawNoSetNameAndNumber())); + + aPrintDeck = new QAction(QString(), this); + connect(aPrintDeck, SIGNAL(triggered()), deckEditor, SLOT(actPrintDeck())); + + aExportDeckDecklist = new QAction(QString(), this); + connect(aExportDeckDecklist, SIGNAL(triggered()), deckEditor, SLOT(actExportDeckDecklist())); + + aAnalyzeDeckDeckstats = new QAction(QString(), this); + connect(aAnalyzeDeckDeckstats, SIGNAL(triggered()), deckEditor, SLOT(actAnalyzeDeckDeckstats())); + + aAnalyzeDeckTappedout = new QAction(QString(), this); + connect(aAnalyzeDeckTappedout, SIGNAL(triggered()), deckEditor, SLOT(actAnalyzeDeckTappedout())); + + analyzeDeckMenu = new QMenu(this); + analyzeDeckMenu->addAction(aExportDeckDecklist); + analyzeDeckMenu->addAction(aAnalyzeDeckDeckstats); + analyzeDeckMenu->addAction(aAnalyzeDeckTappedout); + + aClose = new QAction(QString(), this); + connect(aClose, &QAction::triggered, deckEditor, &AbstractTabDeckEditor::closeRequest); + + saveDeckToClipboardMenu = new QMenu(this); + saveDeckToClipboardMenu->addAction(aSaveDeckToClipboard); + saveDeckToClipboardMenu->addAction(aSaveDeckToClipboardNoSetNameAndNumber); + saveDeckToClipboardMenu->addAction(aSaveDeckToClipboardRaw); + saveDeckToClipboardMenu->addAction(aSaveDeckToClipboardRawNoSetNameAndNumber); + + addAction(aNewDeck); + addAction(aLoadDeck); + addMenu(loadRecentDeckMenu); + addAction(aSaveDeck); + addAction(aSaveDeckAs); + addSeparator(); + addAction(aLoadDeckFromClipboard); + addMenu(saveDeckToClipboardMenu); + addSeparator(); + addAction(aPrintDeck); + addMenu(analyzeDeckMenu); + addSeparator(); + addAction(deckEditor->filterDockWidget->aClearFilterOne); + addAction(deckEditor->filterDockWidget->aClearFilterAll); + addSeparator(); + addAction(aClose); + + retranslateUi(); + connect(&SettingsCache::instance().shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts())); + refreshShortcuts(); +} + +void DeckEditorMenu::setSaveStatus(bool newStatus) +{ + aSaveDeck->setEnabled(newStatus); + aSaveDeckAs->setEnabled(newStatus); + aSaveDeckToClipboard->setEnabled(newStatus); + aSaveDeckToClipboardNoSetNameAndNumber->setEnabled(newStatus); + aSaveDeckToClipboardRaw->setEnabled(newStatus); + aSaveDeckToClipboardRawNoSetNameAndNumber->setEnabled(newStatus); + saveDeckToClipboardMenu->setEnabled(newStatus); + aPrintDeck->setEnabled(newStatus); + analyzeDeckMenu->setEnabled(newStatus); +} + +void DeckEditorMenu::updateRecentlyOpened() +{ + loadRecentDeckMenu->clear(); + for (const auto &deckPath : SettingsCache::instance().recents().getRecentlyOpenedDeckPaths()) { + QAction *aRecentlyOpenedDeck = new QAction(deckPath, this); + loadRecentDeckMenu->addAction(aRecentlyOpenedDeck); + connect(aRecentlyOpenedDeck, &QAction::triggered, deckEditor, + [=, this] { deckEditor->actOpenRecent(aRecentlyOpenedDeck->text()); }); + } + loadRecentDeckMenu->addSeparator(); + loadRecentDeckMenu->addAction(aClearRecents); + aClearRecents->setEnabled(SettingsCache::instance().recents().getRecentlyOpenedDeckPaths().length() > 0); +} + +void DeckEditorMenu::actClearRecents() +{ + SettingsCache::instance().recents().clearRecentlyOpenedDeckPaths(); +} + +void DeckEditorMenu::retranslateUi() +{ + setTitle(tr("&Deck Editor")); + aNewDeck->setText(tr("&New deck")); + aLoadDeck->setText(tr("&Load deck...")); + loadRecentDeckMenu->setTitle(tr("Load recent deck...")); + aClearRecents->setText(tr("Clear")); + aSaveDeck->setText(tr("&Save deck")); + aSaveDeckAs->setText(tr("Save deck &as...")); + aLoadDeckFromClipboard->setText(tr("Load deck from cl&ipboard...")); + + saveDeckToClipboardMenu->setTitle(tr("Save deck to clipboard")); + aSaveDeckToClipboard->setText(tr("Annotated")); + aSaveDeckToClipboardNoSetNameAndNumber->setText(tr("Annotated (No set name or number)")); + aSaveDeckToClipboardRaw->setText(tr("Not Annotated")); + aSaveDeckToClipboardRawNoSetNameAndNumber->setText(tr("Not Annotated (No set name or number)")); + + aPrintDeck->setText(tr("&Print deck...")); + + 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")); +} + +void DeckEditorMenu::refreshShortcuts() +{ + ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts(); + aNewDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aNewDeck")); + aLoadDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aLoadDeck")); + aSaveDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aSaveDeck")); + aExportDeckDecklist->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aExportDeckDecklist")); + aSaveDeckAs->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aSaveDeckAs")); + aLoadDeckFromClipboard->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aLoadDeckFromClipboard")); + aPrintDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aPrintDeck")); + aAnalyzeDeckDeckstats->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aAnalyzeDeck")); + aClose->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aClose")); + + aSaveDeckToClipboard->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aSaveDeckToClipboard")); + aSaveDeckToClipboardRaw->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aSaveDeckToClipboardRaw")); + + aClose->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aClose")); +} diff --git a/cockatrice/src/client/menus/deck_editor/deck_editor_menu.h b/cockatrice/src/client/menus/deck_editor/deck_editor_menu.h new file mode 100644 index 000000000..c9411a013 --- /dev/null +++ b/cockatrice/src/client/menus/deck_editor/deck_editor_menu.h @@ -0,0 +1,32 @@ +#ifndef DECK_EDITOR_MENU_H +#define DECK_EDITOR_MENU_H + +#include "../../tabs/abstract_tab_deck_editor.h" + +#include + +class AbstractTabDeckEditor; +class DeckEditorMenu : public QMenu +{ + Q_OBJECT +public: + explicit DeckEditorMenu(QWidget *parent, AbstractTabDeckEditor *deckEditor); + + AbstractTabDeckEditor *deckEditor; + + QAction *aNewDeck, *aLoadDeck, *aClearRecents, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, + *aSaveDeckToClipboard, *aSaveDeckToClipboardNoSetNameAndNumber, *aSaveDeckToClipboardRaw, + *aSaveDeckToClipboardRawNoSetNameAndNumber, *aPrintDeck, *aExportDeckDecklist, *aAnalyzeDeckDeckstats, + *aAnalyzeDeckTappedout, *aClose; + QMenu *loadRecentDeckMenu, *analyzeDeckMenu, *saveDeckToClipboardMenu; + + void setSaveStatus(bool newStatus); + +public slots: + void updateRecentlyOpened(); + void actClearRecents(); + void retranslateUi(); + void refreshShortcuts(); +}; + +#endif diff --git a/cockatrice/src/client/tabs/abstract_tab_deck_editor.cpp b/cockatrice/src/client/tabs/abstract_tab_deck_editor.cpp new file mode 100644 index 000000000..94a5139fb --- /dev/null +++ b/cockatrice/src/client/tabs/abstract_tab_deck_editor.cpp @@ -0,0 +1,534 @@ +#include "abstract_tab_deck_editor.h" + +#include "../../client/game_logic/abstract_client.h" +#include "../../client/tapped_out_interface.h" +#include "../../client/ui/widgets/cards/card_info_frame_widget.h" +#include "../../deck/deck_stats_interface.h" +#include "../../dialogs/dlg_load_deck.h" +#include "../../dialogs/dlg_load_deck_from_clipboard.h" +#include "../../game/cards/card_database_manager.h" +#include "../../game/cards/card_database_model.h" +#include "../../server/pending_command.h" +#include "../../settings/cache_settings.h" +#include "../ui/picture_loader/picture_loader.h" +#include "../ui/pixel_map_generator.h" +#include "pb/command_deck_upload.pb.h" +#include "pb/response.pb.h" +#include "tab_supervisor.h" +#include "trice_limits.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor) +{ + setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks); + + databaseDisplayDockWidget = new DeckEditorDatabaseDisplayWidget(this); + deckDockWidget = new DeckEditorDeckDockWidget(this); + cardInfoDockWidget = new DeckEditorCardInfoDockWidget(this); + filterDockWidget = new DeckEditorFilterDockWidget(this); + printingSelectorDockWidget = new DeckEditorPrintingSelectorDockWidget(this); + + connect(deckDockWidget, &DeckEditorDeckDockWidget::deckChanged, this, &AbstractTabDeckEditor::onDeckChanged); + connect(deckDockWidget, &DeckEditorDeckDockWidget::cardChanged, this, &AbstractTabDeckEditor::updateCard); + connect(this, &AbstractTabDeckEditor::decrementCard, deckDockWidget, &DeckEditorDeckDockWidget::actDecrementCard); + connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::cardChanged, this, + &AbstractTabDeckEditor::updateCard); + connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::addCardToMainDeck, this, + &AbstractTabDeckEditor::actAddCard); + connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::addCardToSideboard, this, + &AbstractTabDeckEditor::actAddCardToSideboard); + connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::decrementCardFromMainDeck, this, + &AbstractTabDeckEditor::actDecrementCard); + connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::decrementCardFromSideboard, this, + &AbstractTabDeckEditor::actDecrementCardFromSideboard); + + connect(filterDockWidget, &DeckEditorFilterDockWidget::clearAllDatabaseFilters, databaseDisplayDockWidget, + &DeckEditorDatabaseDisplayWidget::clearAllDatabaseFilters); + + connect(&SettingsCache::instance().shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts())); +} + +void AbstractTabDeckEditor::updateCard(CardInfoPtr _card) +{ + cardInfoDockWidget->updateCard(_card); + printingSelectorDockWidget->printingSelector->setCard(_card, DECK_ZONE_MAIN); +} + +void AbstractTabDeckEditor::onDeckChanged() +{ + setModified(true); + deckMenu->setSaveStatus(!getDeckList()->isEmpty()); +} + +void AbstractTabDeckEditor::addCardHelper(const CardInfoPtr info, QString zoneName) +{ + if (!info) + return; + if (info->getIsToken()) + zoneName = DECK_ZONE_TOKENS; + + QModelIndex newCardIndex = deckDockWidget->deckModel->addPreferredPrintingCard(info->getName(), zoneName, false); + // recursiveExpand(newCardIndex); + deckDockWidget->deckView->clearSelection(); + deckDockWidget->deckView->setCurrentIndex(newCardIndex); + setModified(true); + databaseDisplayDockWidget->searchEdit->setSelection(0, databaseDisplayDockWidget->searchEdit->text().length()); +} + +void AbstractTabDeckEditor::actAddCard(CardInfoPtr info) +{ + if (QApplication::keyboardModifiers() & Qt::ControlModifier) + actAddCardToSideboard(info); + else + addCardHelper(info, DECK_ZONE_MAIN); + deckMenu->setSaveStatus(true); +} + +void AbstractTabDeckEditor::actAddCardToSideboard(CardInfoPtr info) +{ + addCardHelper(info, DECK_ZONE_SIDE); + deckMenu->setSaveStatus(true); +} + +void AbstractTabDeckEditor::actDecrementCard(CardInfoPtr info) +{ + emit decrementCard(info, DECK_ZONE_MAIN); +} + +void AbstractTabDeckEditor::actDecrementCardFromSideboard(CardInfoPtr info) +{ + emit decrementCard(info, DECK_ZONE_SIDE); +} + +void AbstractTabDeckEditor::actSwapCard(CardInfoPtr info, QString zoneName) +{ + QString providerId = CardDatabaseManager::getInstance()->getSetInfoForCard(info).getProperty("uuid"); + QString collectorNumber = CardDatabaseManager::getInstance()->getSetInfoForCard(info).getProperty("num"); + deckDockWidget->swapCard( + deckDockWidget->deckModel->findCard(info->getName(), zoneName, providerId, collectorNumber)); +} + +void AbstractTabDeckEditor::setDeck(DeckLoader *_deck) +{ + deckDockWidget->setDeck(_deck); + PictureLoader::cacheCardPixmaps(CardDatabaseManager::getInstance()->getCards(getDeckList()->getCardList())); + setModified(false); + + // If they load a deck, make the deck list appear + aDeckDockVisible->setChecked(true); + deckDockWidget->setVisible(aDeckDockVisible->isChecked()); +} + +DeckLoader *AbstractTabDeckEditor::getDeckList() const +{ + return deckDockWidget->getDeckList(); +} + +void AbstractTabDeckEditor::setModified(bool _modified) +{ + modified = _modified; + emit tabTextChanged(this, getTabText()); +} + +/** + * @brief Returns true if this tab is a blank newly opened tab, as if it was just created with the `New Deck` action. + */ +bool AbstractTabDeckEditor::isBlankNewDeck() const +{ + DeckLoader *deck = getDeckList(); + return !modified && deck->hasNotBeenLoaded(); +} + +void AbstractTabDeckEditor::actNewDeck() +{ + auto deckOpenLocation = confirmOpen(false); + + if (deckOpenLocation == CANCELLED) { + return; + } + + if (deckOpenLocation == NEW_TAB) { + emit openDeckEditor(nullptr); + return; + } + + cleanDeckAndResetModified(); +} + +void AbstractTabDeckEditor::cleanDeckAndResetModified() +{ + deckMenu->setSaveStatus(false); + deckDockWidget->cleanDeck(); + setModified(false); +} + +/** + * @brief Displays the save confirmation dialogue that is shown before loading a deck, if required. Takes into + * account the `openDeckInNewTab` settting. + * + * @param openInSameTabIfBlank Open the deck in the same tab instead of a new tab if the current tab is completely + * blank. Only relevant when the `openDeckInNewTab` setting is enabled. + * + * @returns An enum that indicates if and where to load the deck + */ +AbstractTabDeckEditor::DeckOpenLocation AbstractTabDeckEditor::confirmOpen(const bool openInSameTabIfBlank) +{ + // handle `openDeckInNewTab` setting + if (SettingsCache::instance().getOpenDeckInNewTab()) { + if (openInSameTabIfBlank && isBlankNewDeck()) { + return SAME_TAB; + } else { + return NEW_TAB; + } + } + + // early return if deck is unmodified + if (!modified) { + return SAME_TAB; + } + + // do the save confirmation dialogue + tabSupervisor->setCurrentWidget(this); + + QMessageBox *msgBox = createSaveConfirmationWindow(); + QPushButton *newTabButton = msgBox->addButton(tr("Open in new tab"), QMessageBox::ApplyRole); + + int ret = msgBox->exec(); + + // `exec()` returns an opaque value if a non-standard button was clicked. + // Directly check if newTabButton was clicked before switching over the standard buttons. + if (msgBox->clickedButton() == newTabButton) { + return NEW_TAB; + } + + switch (ret) { + case QMessageBox::Save: + return actSaveDeck() ? SAME_TAB : CANCELLED; + case QMessageBox::Discard: + return SAME_TAB; + default: + return CANCELLED; + } +} + +/** + * @brief Creates the base save confirmation dialogue box. + * + * @returns A QMessageBox that can be further modified + */ +QMessageBox *AbstractTabDeckEditor::createSaveConfirmationWindow() +{ + QMessageBox *msgBox = new QMessageBox(this); + msgBox->setIcon(QMessageBox::Warning); + msgBox->setWindowTitle(tr("Are you sure?")); + msgBox->setText(tr("The decklist has been modified.\nDo you want to save the changes?")); + msgBox->setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + return msgBox; +} + +void AbstractTabDeckEditor::actLoadDeck() +{ + auto deckOpenLocation = confirmOpen(); + + if (deckOpenLocation == CANCELLED) { + return; + } + + DlgLoadDeck dialog(this); + if (!dialog.exec()) + return; + + QString fileName = dialog.selectedFiles().at(0); + openDeckFromFile(fileName, deckOpenLocation); + deckDockWidget->updateBannerCardComboBox(); +} + +void AbstractTabDeckEditor::actOpenRecent(const QString &fileName) +{ + auto deckOpenLocation = confirmOpen(); + + if (deckOpenLocation == CANCELLED) { + return; + } + + openDeckFromFile(fileName, deckOpenLocation); +} + +/** + * Actually opens the deck from file + * @param fileName The path of the deck to open + * @param deckOpenLocation Which tab to open the deck + */ +void AbstractTabDeckEditor::openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation) +{ + DeckLoader::FileFormat fmt = DeckLoader::getFormatFromName(fileName); + + auto *l = new DeckLoader; + if (l->loadFromFile(fileName, fmt, true)) { + SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(fileName); + if (deckOpenLocation == NEW_TAB) { + emit openDeckEditor(l); + } else { + deckMenu->setSaveStatus(false); + setDeck(l); + } + } else { + delete l; + QMessageBox::critical(this, tr("Error"), tr("Could not open deck at %1").arg(fileName)); + } + deckMenu->setSaveStatus(true); +} + +bool AbstractTabDeckEditor::actSaveDeck() +{ + DeckLoader *const deck = getDeckList(); + if (deck->getLastRemoteDeckId() != -1) { + QString deckString = deck->writeToString_Native(); + if (deckString.length() > MAX_FILE_LENGTH) { + QMessageBox::critical(this, tr("Error"), tr("Could not save remote deck")); + return false; + } + + Command_DeckUpload cmd; + cmd.set_deck_id(static_cast(deck->getLastRemoteDeckId())); + cmd.set_deck_list(deckString.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 (deck->saveToFile(deck->getLastFileName(), deck->getLastFileFormat())) { + setModified(false); + return true; + } + QMessageBox::critical( + this, tr("Error"), + tr("The deck could not be saved.\nPlease check that the directory is writable and try again.")); + return false; +} + +bool AbstractTabDeckEditor::actSaveDeckAs() +{ + QFileDialog dialog(this, tr("Save deck")); + dialog.setDirectory(SettingsCache::instance().getDeckPath()); + dialog.setAcceptMode(QFileDialog::AcceptSave); + dialog.setDefaultSuffix("cod"); + dialog.setNameFilters(DeckLoader::fileNameFilters); + dialog.selectFile(getDeckList()->getName().trimmed() + ".cod"); + if (!dialog.exec()) + return false; + + QString fileName = dialog.selectedFiles().at(0); + DeckLoader::FileFormat fmt = DeckLoader::getFormatFromName(fileName); + + if (!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; + } + setModified(false); + + SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(fileName); + + return true; +} + +void AbstractTabDeckEditor::saveDeckRemoteFinished(const Response &response) +{ + if (response.response_code() != Response::RespOk) + QMessageBox::critical(this, tr("Error"), tr("The deck could not be saved.")); + else + setModified(false); +} + +void AbstractTabDeckEditor::actLoadDeckFromClipboard() +{ + auto deckOpenLocation = confirmOpen(); + + if (deckOpenLocation == CANCELLED) { + return; + } + + DlgLoadDeckFromClipboard dlg(this); + if (!dlg.exec()) + return; + + if (deckOpenLocation == NEW_TAB) { + emit openDeckEditor(dlg.getDeckList()); + } else { + setDeck(dlg.getDeckList()); + setModified(true); + } + + deckMenu->setSaveStatus(true); +} + +void AbstractTabDeckEditor::actSaveDeckToClipboard() +{ + QString buffer; + QTextStream stream(&buffer); + getDeckList()->saveToStream_Plain(stream); + QApplication::clipboard()->setText(buffer, QClipboard::Clipboard); + QApplication::clipboard()->setText(buffer, QClipboard::Selection); +} + +void AbstractTabDeckEditor::actSaveDeckToClipboardNoSetNameAndNumber() +{ + QString buffer; + QTextStream stream(&buffer); + getDeckList()->saveToStream_Plain(stream, true, false); + QApplication::clipboard()->setText(buffer, QClipboard::Clipboard); + QApplication::clipboard()->setText(buffer, QClipboard::Selection); +} + +void AbstractTabDeckEditor::actSaveDeckToClipboardRaw() +{ + QString buffer; + QTextStream stream(&buffer); + getDeckList()->saveToStream_Plain(stream, false); + QApplication::clipboard()->setText(buffer, QClipboard::Clipboard); + QApplication::clipboard()->setText(buffer, QClipboard::Selection); +} + +void AbstractTabDeckEditor::actSaveDeckToClipboardRawNoSetNameAndNumber() +{ + QString buffer; + QTextStream stream(&buffer); + getDeckList()->saveToStream_Plain(stream, false, false); + QApplication::clipboard()->setText(buffer, QClipboard::Clipboard); + QApplication::clipboard()->setText(buffer, QClipboard::Selection); +} + +void AbstractTabDeckEditor::actPrintDeck() +{ + auto *dlg = new QPrintPreviewDialog(this); + connect(dlg, SIGNAL(paintRequested(QPrinter *)), deckDockWidget->deckModel, SLOT(printDeckList(QPrinter *))); + dlg->exec(); +} + +// Action called when export deck to decklist menu item is pressed. +void AbstractTabDeckEditor::actExportDeckDecklist() +{ + // Get the decklist class for the deck. + DeckLoader *const deck = 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 AbstractTabDeckEditor::actAnalyzeDeckDeckstats() +{ + auto *interface = new DeckStatsInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(), + this); // it deletes itself when done + interface->analyzeDeck(getDeckList()); +} + +void AbstractTabDeckEditor::actAnalyzeDeckTappedout() +{ + auto *interface = new TappedOutInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(), + this); // it deletes itself when done + interface->analyzeDeck(getDeckList()); +} + +void AbstractTabDeckEditor::filterTreeChanged(FilterTree *filterTree) +{ + databaseDisplayDockWidget->setFilterTree(filterTree); +} + +// Method uses to sync docks state with menu items state +bool AbstractTabDeckEditor::eventFilter(QObject *o, QEvent *e) +{ + if (e->type() == QEvent::Close) { + if (o == cardInfoDockWidget) { + aCardInfoDockVisible->setChecked(false); + aCardInfoDockFloating->setEnabled(false); + } else if (o == deckDockWidget) { + aDeckDockVisible->setChecked(false); + aDeckDockFloating->setEnabled(false); + } else if (o == filterDockWidget) { + aFilterDockVisible->setChecked(false); + aFilterDockFloating->setEnabled(false); + } else if (o == printingSelectorDockWidget) { + aPrintingSelectorDockVisible->setChecked(false); + aPrintingSelectorDockFloating->setEnabled(false); + } + } + if (o == this && e->type() == QEvent::Hide) { + LayoutsSettings &layouts = SettingsCache::instance().layouts(); + layouts.setDeckEditorLayoutState(saveState()); + layouts.setDeckEditorGeometry(saveGeometry()); + layouts.setDeckEditorCardSize(cardInfoDockWidget->size()); + layouts.setDeckEditorFilterSize(filterDockWidget->size()); + layouts.setDeckEditorDeckSize(deckDockWidget->size()); + layouts.setDeckEditorPrintingSelectorSize(printingSelectorDockWidget->size()); + } + return false; +} + +bool AbstractTabDeckEditor::confirmClose() +{ + if (modified) { + tabSupervisor->setCurrentWidget(this); + int ret = createSaveConfirmationWindow()->exec(); + if (ret == QMessageBox::Save) + return actSaveDeck(); + else if (ret == QMessageBox::Cancel) + return false; + } + return true; +} + +void AbstractTabDeckEditor::closeRequest(bool forced) +{ + if (!forced && !confirmClose()) { + return; + } + + emit deckEditorClosing(this); + close(); +} \ No newline at end of file diff --git a/cockatrice/src/client/tabs/abstract_tab_deck_editor.h b/cockatrice/src/client/tabs/abstract_tab_deck_editor.h new file mode 100644 index 000000000..ee8af472c --- /dev/null +++ b/cockatrice/src/client/tabs/abstract_tab_deck_editor.h @@ -0,0 +1,147 @@ +#ifndef TAB_GENERIC_DECK_EDITOR_H +#define TAB_GENERIC_DECK_EDITOR_H + +#include "../../game/cards/card_database.h" +#include "../menus/deck_editor/deck_editor_menu.h" +#include "../ui/widgets/deck_editor/deck_editor_card_info_dock_widget.h" +#include "../ui/widgets/deck_editor/deck_editor_database_display_widget.h" +#include "../ui/widgets/deck_editor/deck_editor_deck_dock_widget.h" +#include "../ui/widgets/deck_editor/deck_editor_filter_dock_widget.h" +#include "../ui/widgets/deck_editor/deck_editor_printing_selector_dock_widget.h" +#include "../ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h" +#include "tab.h" + +class CardDatabaseModel; +class CardDatabaseDisplayModel; + +class CardInfoFrameWidget; +class DeckLoader; +class DeckEditorMenu; +class DeckEditorCardInfoDockWidget; +class DeckEditorDatabaseDisplayWidget; +class DeckEditorDeckDockWidget; +class DeckEditorFilterDockWidget; +class DeckEditorPrintingSelectorDockWidget; +class DeckPreviewDeckTagsDisplayWidget; +class Response; +class FilterTreeModel; +class FilterBuilder; + +class QTreeView; +class QTextEdit; +class QLabel; +class QComboBox; +class QGroupBox; +class QMessageBox; +class QHBoxLayout; +class QVBoxLayout; +class QPushButton; +class QDockWidget; +class QMenu; +class QAction; + +class AbstractTabDeckEditor : public Tab +{ + Q_OBJECT + +public: + explicit AbstractTabDeckEditor(TabSupervisor *_tabSupervisor); + + // UI and Navigation + virtual void createMenus() = 0; + [[nodiscard]] virtual QString getTabText() const override = 0; + bool confirmClose(); + virtual void retranslateUi() override = 0; + + // Deck Management + virtual void setDeck(DeckLoader *_deckLoader); + DeckLoader *getDeckList() const; + void setModified(bool _windowModified); + + // UI Elements + DeckEditorMenu *deckMenu; + DeckEditorDatabaseDisplayWidget *databaseDisplayDockWidget; + DeckEditorCardInfoDockWidget *cardInfoDockWidget; + DeckEditorDeckDockWidget *deckDockWidget; + DeckEditorFilterDockWidget *filterDockWidget; + DeckEditorPrintingSelectorDockWidget *printingSelectorDockWidget; + +public slots: + void onDeckChanged(); + void updateCard(CardInfoPtr _card); + void actAddCard(CardInfoPtr info); + void actAddCardToSideboard(CardInfoPtr info); + void actDecrementCard(CardInfoPtr info); + void actDecrementCardFromSideboard(CardInfoPtr info); + void actOpenRecent(const QString &fileName); + void filterTreeChanged(FilterTree *filterTree); + void closeRequest(bool forced = false) override; + virtual void showPrintingSelector() = 0; + virtual void dockTopLevelChanged(bool topLevel) = 0; + +signals: + void openDeckEditor(const DeckLoader *deckLoader); + void deckEditorClosing(AbstractTabDeckEditor *tab); + void decrementCard(CardInfoPtr card, QString zoneName); + +protected slots: + // Deck Operations + virtual void actNewDeck(); + void cleanDeckAndResetModified(); + virtual void actLoadDeck(); + bool actSaveDeck(); + bool actSaveDeckAs(); + virtual void actLoadDeckFromClipboard(); + void actSaveDeckToClipboard(); + void actSaveDeckToClipboardNoSetNameAndNumber(); + void actSaveDeckToClipboardRaw(); + void actSaveDeckToClipboardRawNoSetNameAndNumber(); + void actPrintDeck(); + void actExportDeckDecklist(); + void actAnalyzeDeckDeckstats(); + void actAnalyzeDeckTappedout(); + + // Remote Save + void saveDeckRemoteFinished(const Response &r); + + // UI Layout Management + virtual void loadLayout() = 0; + virtual void restartLayout() = 0; + virtual void freeDocksSize() = 0; + virtual void refreshShortcuts() = 0; + + bool eventFilter(QObject *o, QEvent *e) override; + virtual void dockVisibleTriggered() = 0; + virtual void dockFloatingTriggered() = 0; + +protected: + /** + * @brief Enum for selecting deck open location + */ + enum DeckOpenLocation + { + CANCELLED, + SAME_TAB, + NEW_TAB + }; + + DeckOpenLocation confirmOpen(bool openInSameTabIfBlank = true); + QMessageBox *createSaveConfirmationWindow(); + bool isBlankNewDeck() const; + + // Helper functions for card actions + void addCardHelper(CardInfoPtr info, QString zoneName); + void actSwapCard(CardInfoPtr info, QString zoneName); + virtual void openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation); + + // UI Menu Elements + QMenu *viewMenu, *cardInfoDockMenu, *deckDockMenu, *filterDockMenu, *printingSelectorDockMenu; + + QAction *aResetLayout; + QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aDeckDockVisible, *aDeckDockFloating; + QAction *aFilterDockVisible, *aFilterDockFloating, *aPrintingSelectorDockVisible, *aPrintingSelectorDockFloating; + + bool modified; +}; + +#endif // TAB_GENERIC_DECK_EDITOR_H diff --git a/cockatrice/src/client/tabs/tab_deck_editor.cpp b/cockatrice/src/client/tabs/tab_deck_editor.cpp index ccf42be9d..dbe38a215 100644 --- a/cockatrice/src/client/tabs/tab_deck_editor.cpp +++ b/cockatrice/src/client/tabs/tab_deck_editor.cpp @@ -3,412 +3,59 @@ #include "../../client/game_logic/abstract_client.h" #include "../../client/tapped_out_interface.h" #include "../../client/ui/widgets/cards/card_info_frame_widget.h" -#include "../../deck/deck_stats_interface.h" #include "../../dialogs/dlg_load_deck.h" #include "../../dialogs/dlg_load_deck_from_clipboard.h" #include "../../game/cards/card_database_manager.h" #include "../../game/cards/card_database_model.h" #include "../../game/filters/filter_builder.h" #include "../../game/filters/filter_tree_model.h" -#include "../../main.h" #include "../../server/pending_command.h" #include "../../settings/cache_settings.h" -#include "../ui/picture_loader/picture_loader.h" +#include "../menus/deck_editor/deck_editor_menu.h" #include "../ui/pixel_map_generator.h" -#include "pb/command_deck_upload.pb.h" -#include "pb/response.pb.h" +#include "../ui/widgets/deck_editor/deck_editor_filter_dock_widget.h" #include "tab_supervisor.h" #include "trice_limits.h" #include #include -#include #include #include #include -#include #include #include #include #include #include #include -#include #include #include #include #include -#include -#include #include -#include #include -#include #include -#include #include #include -#include #include -static bool canBeCommander(const CardInfoPtr &cardInfo) +TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor) : AbstractTabDeckEditor(_tabSupervisor) { - return ((cardInfo->getCardType().contains("Legendary", Qt::CaseInsensitive) && - cardInfo->getCardType().contains("Creature", Qt::CaseInsensitive))) || - cardInfo->getText().contains("can be your commander", Qt::CaseInsensitive); -} + setObjectName("TabDeckEditor"); -void TabDeckEditor::createDeckDock() -{ - deckModel = new DeckListModel(this); - deckModel->setObjectName("deckModel"); - connect(deckModel, SIGNAL(deckHashChanged()), this, SLOT(updateHash())); - deckView = new QTreeView(); - deckView->setObjectName("deckView"); - deckView->setModel(deckModel); - deckView->setUniformRowHeights(true); - deckView->setSortingEnabled(true); - deckView->sortByColumn(1, Qt::AscendingOrder); - deckView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - deckView->installEventFilter(&deckViewKeySignals); - deckView->setContextMenuPolicy(Qt::CustomContextMenu); - deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); - connect(deckView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, - SLOT(updateCardInfoRight(const QModelIndex &, const QModelIndex &))); - connect(deckView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, - SLOT(updatePrintingSelectorDeckView(const QModelIndex &, const QModelIndex &))); - connect(deckView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(actSwapCard())); - connect(deckView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(decklistCustomMenu(QPoint))); - connect(&deckViewKeySignals, SIGNAL(onShiftS()), this, SLOT(actSwapCard())); - connect(&deckViewKeySignals, SIGNAL(onEnter()), this, SLOT(actIncrement())); - connect(&deckViewKeySignals, SIGNAL(onCtrlAltEqual()), this, SLOT(actIncrement())); - connect(&deckViewKeySignals, SIGNAL(onCtrlAltMinus()), this, SLOT(actDecrement())); - connect(&deckViewKeySignals, SIGNAL(onShiftRight()), this, SLOT(actIncrement())); - connect(&deckViewKeySignals, SIGNAL(onShiftLeft()), this, SLOT(actDecrement())); - connect(&deckViewKeySignals, SIGNAL(onDelete()), this, SLOT(actRemoveCard())); + TabDeckEditor::createMenus(); - nameLabel = new QLabel(); - nameLabel->setObjectName("nameLabel"); - nameEdit = new LineEditUnfocusable; - nameEdit->setMaxLength(MAX_NAME_LENGTH); - nameEdit->setObjectName("nameEdit"); - nameLabel->setBuddy(nameEdit); - connect(nameEdit, SIGNAL(textChanged(const QString &)), this, SLOT(updateName(const QString &))); - commentsLabel = new QLabel(); - commentsLabel->setObjectName("commentsLabel"); - commentsEdit = new QTextEdit; - commentsEdit->setAcceptRichText(false); - commentsEdit->setMinimumHeight(nameEdit->minimumSizeHint().height()); - commentsEdit->setObjectName("commentsEdit"); - commentsLabel->setBuddy(commentsEdit); - connect(commentsEdit, SIGNAL(textChanged()), this, SLOT(updateComments())); + installEventFilter(this); - bannerCardLabel = new QLabel(); - bannerCardLabel->setObjectName("bannerCardLabel"); - bannerCardLabel->setText(tr("Banner Card")); - bannerCardComboBox = new QComboBox(this); - connect(deckModel, &DeckListModel::dataChanged, this, [this]() { - // Delay the update to avoid race conditions - QTimer::singleShot(100, this, &TabDeckEditor::updateBannerCardComboBox); - }); - connect(bannerCardComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, - &TabDeckEditor::setBannerCard); + TabDeckEditor::retranslateUi(); + TabDeckEditor::refreshShortcuts(); - deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckModel->getDeckList()); - - aIncrement = new QAction(QString(), this); - aIncrement->setIcon(QPixmap("theme:icons/increment")); - connect(aIncrement, SIGNAL(triggered()), this, SLOT(actIncrement())); - auto *tbIncrement = new QToolButton(this); - tbIncrement->setDefaultAction(aIncrement); - - aDecrement = new QAction(QString(), this); - aDecrement->setIcon(QPixmap("theme:icons/decrement")); - connect(aDecrement, SIGNAL(triggered()), this, SLOT(actDecrement())); - auto *tbDecrement = new QToolButton(this); - tbDecrement->setDefaultAction(aDecrement); - - aRemoveCard = new QAction(QString(), this); - aRemoveCard->setIcon(QPixmap("theme:icons/remove_row")); - connect(aRemoveCard, SIGNAL(triggered()), this, SLOT(actRemoveCard())); - auto *tbRemoveCard = new QToolButton(this); - tbRemoveCard->setDefaultAction(aRemoveCard); - - aSwapCard = new QAction(QString(), this); - aSwapCard->setIcon(QPixmap("theme:icons/swap")); - connect(aSwapCard, SIGNAL(triggered()), this, SLOT(actSwapCard())); - auto *tbSwapCard = new QToolButton(this); - tbSwapCard->setDefaultAction(aSwapCard); - - auto *upperLayout = new QGridLayout; - upperLayout->setObjectName("upperLayout"); - upperLayout->addWidget(nameLabel, 0, 0); - upperLayout->addWidget(nameEdit, 0, 1); - - upperLayout->addWidget(commentsLabel, 1, 0); - upperLayout->addWidget(commentsEdit, 1, 1); - - upperLayout->addWidget(bannerCardLabel, 2, 0); - upperLayout->addWidget(bannerCardComboBox, 2, 1); - - upperLayout->addWidget(deckTagsDisplayWidget, 3, 1); - - hashLabel1 = new QLabel(); - hashLabel1->setObjectName("hashLabel1"); - auto *hashSizePolicy = new QSizePolicy(); - hashSizePolicy->setHorizontalPolicy(QSizePolicy::Fixed); - hashLabel1->setSizePolicy(*hashSizePolicy); - hashLabel = new LineEditUnfocusable; - hashLabel->setObjectName("hashLabel"); - hashLabel->setReadOnly(true); - hashLabel->setFrame(false); - - auto *lowerLayout = new QGridLayout; - lowerLayout->setObjectName("lowerLayout"); - lowerLayout->addWidget(hashLabel1, 0, 0); - lowerLayout->addWidget(hashLabel, 0, 1); - lowerLayout->addWidget(tbIncrement, 0, 2); - lowerLayout->addWidget(tbDecrement, 0, 3); - lowerLayout->addWidget(tbRemoveCard, 0, 4); - lowerLayout->addWidget(tbSwapCard, 0, 5); - lowerLayout->addWidget(deckView, 1, 0, 1, 6); - - // Create widgets for both layouts to make splitter work correctly - auto *topWidget = new QWidget; - topWidget->setLayout(upperLayout); - auto *bottomWidget = new QWidget; - bottomWidget->setLayout(lowerLayout); - - auto *split = new QSplitter; - split->setObjectName("deckSplitter"); - split->setOrientation(Qt::Vertical); - split->setChildrenCollapsible(true); - split->addWidget(topWidget); - split->addWidget(bottomWidget); - split->setStretchFactor(0, 1); - split->setStretchFactor(1, 4); - - auto *rightFrame = new QVBoxLayout; - rightFrame->setObjectName("rightFrame"); - rightFrame->addWidget(split); - - deckDock = new QDockWidget(this); - deckDock->setObjectName("deckDock"); - - deckDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); - deckDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | - QDockWidget::DockWidgetMovable); - auto *deckDockContents = new QWidget(); - deckDockContents->setObjectName("deckDockContents"); - deckDockContents->setLayout(rightFrame); - deckDock->setWidget(deckDockContents); - - deckDock->installEventFilter(this); - connect(deckDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool))); -} - -void TabDeckEditor::createCardInfoDock() -{ - cardInfo = new CardInfoFrameWidget(); - cardInfo->setObjectName("cardInfo"); - auto *cardInfoFrame = new QVBoxLayout; - cardInfoFrame->setObjectName("cardInfoFrame"); - cardInfoFrame->addWidget(cardInfo); - - cardInfoDock = new QDockWidget(this); - cardInfoDock->setObjectName("cardInfoDock"); - - cardInfoDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); - cardInfoDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | - QDockWidget::DockWidgetMovable); - auto *cardInfoDockContents = new QWidget(); - cardInfoDockContents->setObjectName("cardInfoDockContents"); - cardInfoDockContents->setLayout(cardInfoFrame); - cardInfoDock->setWidget(cardInfoDockContents); - - cardInfoDock->installEventFilter(this); - connect(cardInfoDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool))); -} - -void TabDeckEditor::createFiltersDock() -{ - filterModel = new FilterTreeModel(); - filterModel->setObjectName("filterModel"); - databaseDisplayModel->setFilterTree(filterModel->filterTree()); - databaseDisplayModel->setObjectName("databaseDisplayModel"); - filterView = new QTreeView; - filterView->setObjectName("filterView"); - filterView->setModel(filterModel); - filterView->setUniformRowHeights(true); - filterView->setHeaderHidden(true); - filterView->setContextMenuPolicy(Qt::CustomContextMenu); - filterView->installEventFilter(&filterViewKeySignals); - connect(filterModel, SIGNAL(layoutChanged()), filterView, SLOT(expandAll())); - connect(filterView, SIGNAL(customContextMenuRequested(const QPoint &)), this, - SLOT(filterViewCustomContextMenu(const QPoint &))); - connect(&filterViewKeySignals, SIGNAL(onDelete()), this, SLOT(actClearFilterOne())); - - auto *filterBuilder = new FilterBuilder; - filterBuilder->setObjectName("filterBuilder"); - connect(filterBuilder, SIGNAL(add(const CardFilter *)), filterModel, SLOT(addFilter(const CardFilter *))); - - auto *filterDelOne = new QToolButton(); - filterDelOne->setObjectName("filterDelOne"); - filterDelOne->setDefaultAction(aClearFilterOne); - filterDelOne->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - - auto *filterDelAll = new QToolButton(); - filterDelAll->setObjectName("filterDelAll"); - filterDelAll->setDefaultAction(aClearFilterAll); - filterDelAll->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - - auto *filterLayout = new QGridLayout; - filterLayout->setObjectName("filterLayout"); - filterLayout->setContentsMargins(0, 0, 0, 0); - filterLayout->addWidget(filterBuilder, 0, 0, 1, 3); - filterLayout->addWidget(filterView, 1, 0, 1, 3); - filterLayout->addWidget(filterDelOne, 2, 0, 1, 1); - filterLayout->addWidget(filterDelAll, 2, 2, 1, 1); - - filterBox = new QWidget(); - filterBox->setObjectName("filterBox"); - filterBox->setLayout(filterLayout); - - auto *filterFrame = new QVBoxLayout; - filterFrame->setObjectName("filterFrame"); - filterFrame->addWidget(filterBox); - - filterDock = new QDockWidget(this); - filterDock->setObjectName("filterDock"); - - filterDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | - QDockWidget::DockWidgetMovable); - auto *filterDockContents = new QWidget(this); - filterDockContents->setObjectName("filterDockContents"); - filterDockContents->setLayout(filterFrame); - filterDock->setWidget(filterDockContents); - - filterDock->installEventFilter(this); - connect(filterDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool))); -} - -void TabDeckEditor::createPrintingSelectorDock() -{ - printingSelector = new PrintingSelector(this, this, deckModel, deckView); - printingSelector->setObjectName("printingSelector"); - auto *printingSelectorFrame = new QVBoxLayout; - printingSelectorFrame->setObjectName("printingSelectorFrame"); - printingSelectorFrame->addWidget(printingSelector); - - printingSelectorDock = new QDockWidget(this); - printingSelectorDock->setObjectName("printingSelectorDock"); - - printingSelectorDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); - printingSelectorDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | - QDockWidget::DockWidgetMovable); - auto *printingSelectorDockContents = new QWidget(); - printingSelectorDockContents->setObjectName("printingSelectorDockContents"); - printingSelectorDockContents->setLayout(printingSelectorFrame); - printingSelectorDock->setWidget(printingSelectorDockContents); - - printingSelectorDock->installEventFilter(this); - connect(printingSelectorDock, SIGNAL(topLevelChanged(bool)), this, SLOT(dockTopLevelChanged(bool))); - - addDockWidget(Qt::RightDockWidgetArea, printingSelectorDock); - printingSelectorDock->setFloating(false); + TabDeckEditor::loadLayout(); } void TabDeckEditor::createMenus() { - aNewDeck = new QAction(QString(), this); - connect(aNewDeck, SIGNAL(triggered()), this, SLOT(actNewDeck())); - - aLoadDeck = new QAction(QString(), this); - connect(aLoadDeck, SIGNAL(triggered()), this, SLOT(actLoadDeck())); - - loadRecentDeckMenu = new QMenu(this); - connect(&SettingsCache::instance().recents(), &RecentsSettings::recentlyOpenedDeckPathsChanged, this, - &TabDeckEditor::updateRecentlyOpened); - - aClearRecents = new QAction(QString(), this); - connect(aClearRecents, &QAction::triggered, this, &TabDeckEditor::actClearRecents); - - updateRecentlyOpened(); - - aSaveDeck = new QAction(QString(), this); - connect(aSaveDeck, SIGNAL(triggered()), this, SLOT(actSaveDeck())); - - aSaveDeckAs = new QAction(QString(), this); - connect(aSaveDeckAs, SIGNAL(triggered()), this, SLOT(actSaveDeckAs())); - - aLoadDeckFromClipboard = new QAction(QString(), this); - connect(aLoadDeckFromClipboard, SIGNAL(triggered()), this, SLOT(actLoadDeckFromClipboard())); - - aSaveDeckToClipboard = new QAction(QString(), this); - connect(aSaveDeckToClipboard, SIGNAL(triggered()), this, SLOT(actSaveDeckToClipboard())); - - aSaveDeckToClipboardNoSetNameAndNumber = new QAction(QString(), this); - connect(aSaveDeckToClipboardNoSetNameAndNumber, SIGNAL(triggered()), this, - SLOT(actSaveDeckToClipboardNoSetNameAndNumber())); - - aSaveDeckToClipboardRaw = new QAction(QString(), this); - connect(aSaveDeckToClipboardRaw, SIGNAL(triggered()), this, SLOT(actSaveDeckToClipboardRaw())); - - aSaveDeckToClipboardRawNoSetNameAndNumber = new QAction(QString(), this); - connect(aSaveDeckToClipboardRawNoSetNameAndNumber, SIGNAL(triggered()), this, - SLOT(actSaveDeckToClipboardRawNoSetNameAndNumber())); - - 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())); - - aAnalyzeDeckTappedout = new QAction(QString(), this); - connect(aAnalyzeDeckTappedout, SIGNAL(triggered()), this, SLOT(actAnalyzeDeckTappedout())); - - analyzeDeckMenu = new QMenu(this); - analyzeDeckMenu->addAction(aExportDeckDecklist); - analyzeDeckMenu->addAction(aAnalyzeDeckDeckstats); - analyzeDeckMenu->addAction(aAnalyzeDeckTappedout); - - aClose = new QAction(QString(), this); - connect(aClose, &QAction::triggered, this, [this] { closeRequest(); }); - - aClearFilterAll = new QAction(QString(), this); - aClearFilterAll->setIcon(QPixmap("theme:icons/clearsearch")); - connect(aClearFilterAll, SIGNAL(triggered()), this, SLOT(actClearFilterAll())); - - aClearFilterOne = new QAction(QString(), this); - aClearFilterOne->setIcon(QPixmap("theme:icons/decrement")); - connect(aClearFilterOne, SIGNAL(triggered()), this, SLOT(actClearFilterOne())); - - saveDeckToClipboardMenu = new QMenu(this); - saveDeckToClipboardMenu->addAction(aSaveDeckToClipboard); - saveDeckToClipboardMenu->addAction(aSaveDeckToClipboardNoSetNameAndNumber); - saveDeckToClipboardMenu->addAction(aSaveDeckToClipboardRaw); - saveDeckToClipboardMenu->addAction(aSaveDeckToClipboardRawNoSetNameAndNumber); - - deckMenu = new QMenu(this); - deckMenu->addAction(aNewDeck); - deckMenu->addAction(aLoadDeck); - deckMenu->addMenu(loadRecentDeckMenu); - deckMenu->addAction(aSaveDeck); - deckMenu->addAction(aSaveDeckAs); - deckMenu->addSeparator(); - deckMenu->addAction(aLoadDeckFromClipboard); - deckMenu->addMenu(saveDeckToClipboardMenu); - deckMenu->addSeparator(); - deckMenu->addAction(aPrintDeck); - deckMenu->addMenu(analyzeDeckMenu); - deckMenu->addSeparator(); - deckMenu->addAction(aClearFilterOne); - deckMenu->addAction(aClearFilterAll); - deckMenu->addSeparator(); - deckMenu->addAction(aClose); + deckMenu = new DeckEditorMenu(this, this); addTabMenu(deckMenu); viewMenu = new QMenu(this); @@ -452,351 +99,27 @@ void TabDeckEditor::createMenus() connect(aResetLayout, SIGNAL(triggered()), this, SLOT(restartLayout())); viewMenu->addAction(aResetLayout); - setSaveStatus(false); + deckMenu->setSaveStatus(false); addTabMenu(viewMenu); } -void TabDeckEditor::createCentralFrame() +QString TabDeckEditor::getTabText() const { - searchEdit = new SearchLineEdit; - searchEdit->setObjectName("searchEdit"); - searchEdit->setPlaceholderText(tr("Search by card name (or search expressions)")); - searchEdit->setClearButtonEnabled(true); - searchEdit->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition); - auto help = searchEdit->addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition); - searchEdit->installEventFilter(&searchKeySignals); - - setFocusProxy(searchEdit); - setFocusPolicy(Qt::ClickFocus); - - searchKeySignals.setObjectName("searchKeySignals"); - connect(searchEdit, SIGNAL(textChanged(const QString &)), this, SLOT(updateSearch(const QString &))); - connect(&searchKeySignals, SIGNAL(onEnter()), this, SLOT(actAddCard())); - connect(&searchKeySignals, SIGNAL(onCtrlAltEqual()), this, SLOT(actAddCard())); - connect(&searchKeySignals, SIGNAL(onCtrlAltRBracket()), this, SLOT(actAddCardToSideboard())); - connect(&searchKeySignals, SIGNAL(onCtrlAltMinus()), this, SLOT(actDecrementCard())); - connect(&searchKeySignals, SIGNAL(onCtrlAltLBracket()), this, SLOT(actDecrementCardFromSideboard())); - connect(&searchKeySignals, SIGNAL(onCtrlAltEnter()), this, SLOT(actAddCardToSideboard())); - connect(&searchKeySignals, SIGNAL(onCtrlEnter()), this, SLOT(actAddCardToSideboard())); - connect(&searchKeySignals, SIGNAL(onCtrlC()), this, SLOT(copyDatabaseCellContents())); - connect(help, &QAction::triggered, this, &TabDeckEditor::showSearchSyntaxHelp); - - databaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), true, this); - databaseModel->setObjectName("databaseModel"); - databaseDisplayModel = new CardDatabaseDisplayModel(this); - databaseDisplayModel->setSourceModel(databaseModel); - databaseDisplayModel->setFilterKeyColumn(0); - - databaseView = new QTreeView(); - databaseView->setObjectName("databaseView"); - databaseView->setFocusProxy(searchEdit); - databaseView->setUniformRowHeights(true); - databaseView->setRootIsDecorated(false); - databaseView->setAlternatingRowColors(true); - databaseView->setSortingEnabled(true); - databaseView->sortByColumn(0, Qt::AscendingOrder); - databaseView->setModel(databaseDisplayModel); - databaseView->setContextMenuPolicy(Qt::CustomContextMenu); - connect(databaseView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(databaseCustomMenu(QPoint))); - connect(databaseView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, - SLOT(updateCardInfoLeft(const QModelIndex &, const QModelIndex &))); - connect(databaseView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, - SLOT(updatePrintingSelectorDatabase(const QModelIndex &, const QModelIndex &))); - connect(databaseView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(actAddCard())); - - QByteArray dbHeaderState = SettingsCache::instance().layouts().getDeckEditorDbHeaderState(); - if (dbHeaderState.isNull()) { - // first run - databaseView->setColumnWidth(0, 200); - } else { - databaseView->header()->restoreState(dbHeaderState); - } - connect(databaseView->header(), SIGNAL(geometriesChanged()), this, SLOT(saveDbHeaderState())); - - searchEdit->setTreeView(databaseView); - - aAddCard = new QAction(QString(), this); - aAddCard->setIcon(QPixmap("theme:icons/arrow_right_green")); - connect(aAddCard, SIGNAL(triggered()), this, SLOT(actAddCard())); - auto *tbAddCard = new QToolButton(this); - tbAddCard->setDefaultAction(aAddCard); - - aAddCardToSideboard = new QAction(QString(), this); - aAddCardToSideboard->setIcon(QPixmap("theme:icons/arrow_right_blue")); - connect(aAddCardToSideboard, SIGNAL(triggered()), this, SLOT(actAddCardToSideboard())); - auto *tbAddCardToSideboard = new QToolButton(this); - tbAddCardToSideboard->setDefaultAction(aAddCardToSideboard); - - searchLayout = new QHBoxLayout; - searchLayout->setObjectName("searchLayout"); - searchLayout->addWidget(searchEdit); - searchLayout->addWidget(tbAddCard); - searchLayout->addWidget(tbAddCardToSideboard); - - centralFrame = new QVBoxLayout; - centralFrame->setObjectName("centralFrame"); - centralFrame->addLayout(searchLayout); - centralFrame->addWidget(databaseView); - - centralWidget = new QWidget(this); - centralWidget->setObjectName("centralWidget"); - centralWidget->setLayout(centralFrame); - centralWidget->setMaximumSize(900, 5000); - setCentralWidget(centralWidget); - setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks); -} - -void TabDeckEditor::databaseCustomMenu(QPoint point) -{ - QMenu menu; - const CardInfoPtr info = currentCardInfo(); - - if (info) { - // add to deck and sideboard options - QAction *addToDeck, *addToSideboard, *selectPrinting, *edhRecCommander, *edhRecCard; - addToDeck = menu.addAction(tr("Add to Deck")); - addToSideboard = menu.addAction(tr("Add to Sideboard")); - selectPrinting = menu.addAction(tr("Select Printing")); - if (canBeCommander(info)) { - edhRecCommander = menu.addAction(tr("Show on EDHREC (Commander)")); - connect(edhRecCommander, &QAction::triggered, this, - [this, info] { this->tabSupervisor->addEdhrecTab(info, true); }); - } - edhRecCard = menu.addAction(tr("Show on EDHREC (Card)")); - - connect(addToDeck, SIGNAL(triggered()), this, SLOT(actAddCard())); - connect(addToSideboard, SIGNAL(triggered()), this, SLOT(actAddCardToSideboard())); - connect(selectPrinting, &QAction::triggered, this, [this, info] { this->showPrintingSelector(); }); - connect(edhRecCard, &QAction::triggered, this, [this, info] { this->tabSupervisor->addEdhrecTab(info); }); - - // filling out the related cards submenu - auto *relatedMenu = new QMenu(tr("Show Related cards")); - menu.addMenu(relatedMenu); - auto relatedCards = info->getAllRelatedCards(); - if (relatedCards.isEmpty()) { - relatedMenu->setDisabled(true); - } else { - for (const CardRelation *rel : relatedCards) { - const QString &relatedCardName = rel->getName(); - QAction *relatedCard = relatedMenu->addAction(relatedCardName); - connect(relatedCard, &QAction::triggered, cardInfo, - [this, relatedCardName] { cardInfo->setCard(relatedCardName); }); - } - } - menu.exec(databaseView->mapToGlobal(point)); - } -} - -void TabDeckEditor::decklistCustomMenu(QPoint point) -{ - QMenu menu; - const CardInfoPtr info = cardInfo->getInfo(); - - QAction *selectPrinting = menu.addAction(tr("Select Printing")); - - connect(selectPrinting, &QAction::triggered, this, &TabDeckEditor::showPrintingSelector); - - menu.exec(deckView->mapToGlobal(point)); -} - -void TabDeckEditor::showPrintingSelector() -{ - printingSelector->setCard(cardInfo->getInfo(), DECK_ZONE_MAIN); - printingSelector->updateDisplay(); - aPrintingSelectorDockVisible->setChecked(true); - printingSelectorDock->setVisible(true); -} - -void TabDeckEditor::restartLayout() -{ - deckDock->setVisible(true); - cardInfoDock->setVisible(true); - filterDock->setVisible(true); - printingSelectorDock->setVisible(false); - - deckDock->setFloating(false); - cardInfoDock->setFloating(false); - filterDock->setFloating(false); - printingSelectorDock->setFloating(false); - - aCardInfoDockVisible->setChecked(true); - aDeckDockVisible->setChecked(true); - aFilterDockVisible->setChecked(true); - aPrintingSelectorDockVisible->setChecked(false); - - aCardInfoDockFloating->setChecked(false); - aDeckDockFloating->setChecked(false); - aFilterDockFloating->setChecked(false); - aPrintingSelectorDockFloating->setChecked(false); - - addDockWidget(static_cast(2), deckDock); - addDockWidget(static_cast(2), cardInfoDock); - addDockWidget(static_cast(2), filterDock); - addDockWidget(static_cast(2), printingSelectorDock); - - splitDockWidget(cardInfoDock, printingSelectorDock, Qt::Horizontal); - splitDockWidget(printingSelectorDock, deckDock, Qt::Horizontal); - splitDockWidget(cardInfoDock, printingSelectorDock, Qt::Horizontal); - splitDockWidget(cardInfoDock, filterDock, Qt::Vertical); - - QTimer::singleShot(100, this, SLOT(freeDocksSize())); -} - -void TabDeckEditor::freeDocksSize() -{ - deckDock->setMinimumSize(100, 100); - deckDock->setMaximumSize(5000, 5000); - - cardInfoDock->setMinimumSize(100, 100); - cardInfoDock->setMaximumSize(5000, 5000); - - filterDock->setMinimumSize(100, 100); - filterDock->setMaximumSize(5000, 5000); - - printingSelectorDock->setMinimumSize(525, 100); - printingSelectorDock->setMaximumSize(5000, 5000); - - centralWidget->setMaximumSize(900, 5000); -} - -void TabDeckEditor::refreshShortcuts() -{ - ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts(); - aNewDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aNewDeck")); - aLoadDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aLoadDeck")); - aSaveDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aSaveDeck")); - aExportDeckDecklist->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aExportDeckDecklist")); - aSaveDeckAs->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aSaveDeckAs")); - aLoadDeckFromClipboard->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aLoadDeckFromClipboard")); - aPrintDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aPrintDeck")); - aAnalyzeDeckDeckstats->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aAnalyzeDeck")); - aClose->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aClose")); - aResetLayout->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aResetLayout")); - aClearFilterAll->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aClearFilterAll")); - aClearFilterOne->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aClearFilterOne")); - - aSaveDeckToClipboard->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aSaveDeckToClipboard")); - aSaveDeckToClipboardRaw->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aSaveDeckToClipboardRaw")); - - aClearFilterOne->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aClearFilterOne")); - aClose->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aClose")); - aRemoveCard->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aRemoveCard")); - aIncrement->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aIncrement")); - aDecrement->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aDecrement")); -} - -void TabDeckEditor::loadLayout() -{ - LayoutsSettings &layouts = SettingsCache::instance().layouts(); - auto &layoutState = layouts.getDeckEditorLayoutState(); - if (layoutState.isNull()) { - restartLayout(); - } else { - restoreState(layoutState); - restoreGeometry(layouts.getDeckEditorGeometry()); - } - - aCardInfoDockVisible->setChecked(!cardInfoDock->isHidden()); - aFilterDockVisible->setChecked(!filterDock->isHidden()); - aDeckDockVisible->setChecked(!deckDock->isHidden()); - aPrintingSelectorDockVisible->setChecked(!printingSelectorDock->isHidden()); - - aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked()); - aDeckDockFloating->setEnabled(aDeckDockVisible->isChecked()); - aFilterDockFloating->setEnabled(aFilterDockVisible->isChecked()); - aPrintingSelectorDockFloating->setEnabled(aPrintingSelectorDockVisible->isChecked()); - - aCardInfoDockFloating->setChecked(cardInfoDock->isFloating()); - aFilterDockFloating->setChecked(filterDock->isFloating()); - aDeckDockFloating->setChecked(deckDock->isFloating()); - aPrintingSelectorDockFloating->setChecked(printingSelectorDock->isFloating()); - - cardInfoDock->setMinimumSize(layouts.getDeckEditorCardSize()); - cardInfoDock->setMaximumSize(layouts.getDeckEditorCardSize()); - - filterDock->setMinimumSize(layouts.getDeckEditorFilterSize()); - filterDock->setMaximumSize(layouts.getDeckEditorFilterSize()); - - deckDock->setMinimumSize(layouts.getDeckEditorDeckSize()); - deckDock->setMaximumSize(layouts.getDeckEditorDeckSize()); - - printingSelectorDock->setMinimumSize(layouts.getDeckEditorPrintingSelectorSize()); - printingSelectorDock->setMaximumSize(layouts.getDeckEditorPrintingSelectorSize()); - - QTimer::singleShot(100, this, SLOT(freeDocksSize())); -} - -TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor), modified(false) -{ - setObjectName("TabDeckEditor"); - - createMenus(); - - createCentralFrame(); - - createDeckDock(); - createCardInfoDock(); - createFiltersDock(); - createPrintingSelectorDock(); - - this->installEventFilter(this); - - retranslateUi(); - connect(&SettingsCache::instance().shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts())); - refreshShortcuts(); - - loadLayout(); + QString result = tr("Deck: %1").arg(deckDockWidget->getSimpleDeckName()); + if (modified) + result.prepend("* "); + return result; } void TabDeckEditor::retranslateUi() { - cardInfo->retranslateUi(); + deckMenu->retranslateUi(); - aClearFilterAll->setText(tr("&Clear all filters")); - aClearFilterOne->setText(tr("Delete selected")); - - nameLabel->setText(tr("Deck &name:")); - commentsLabel->setText(tr("&Comments:")); - hashLabel1->setText(tr("Hash:")); - - aNewDeck->setText(tr("&New deck")); - aLoadDeck->setText(tr("&Load deck...")); - loadRecentDeckMenu->setTitle(tr("Load recent deck...")); - aClearRecents->setText(tr("Clear")); - aSaveDeck->setText(tr("&Save deck")); - aSaveDeckAs->setText(tr("Save deck &as...")); - aLoadDeckFromClipboard->setText(tr("Load deck from cl&ipboard...")); - - saveDeckToClipboardMenu->setTitle(tr("Save deck to clipboard")); - aSaveDeckToClipboard->setText(tr("Annotated")); - aSaveDeckToClipboardNoSetNameAndNumber->setText(tr("Annotated (No set name or number)")); - aSaveDeckToClipboardRaw->setText(tr("Not Annotated")); - aSaveDeckToClipboardRawNoSetNameAndNumber->setText(tr("Not Annotated (No set name or number)")); - - aPrintDeck->setText(tr("&Print deck...")); - - 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")); - - aAddCard->setText(tr("Add card to &maindeck")); - aAddCardToSideboard->setText(tr("Add card to &sideboard")); - - aIncrement->setText(tr("&Increment number")); - aDecrement->setText(tr("&Decrement number")); - aRemoveCard->setText(tr("&Remove row")); - aSwapCard->setText(tr("Swap card to/from sideboard")); - - deckMenu->setTitle(tr("&Deck Editor")); - - cardInfoDock->setWindowTitle(tr("Card Info")); - deckDock->setWindowTitle(tr("Deck")); - filterDock->setWindowTitle(tr("Filters")); - printingSelectorDock->setWindowTitle(tr("Printing Selector")); + cardInfoDockWidget->retranslateUi(); + deckDockWidget->retranslateUi(); + filterDockWidget->retranslateUi(); + printingSelectorDockWidget->retranslateUi(); viewMenu->setTitle(tr("&View")); cardInfoDockMenu->setTitle(tr("Card Info")); @@ -819,885 +142,144 @@ void TabDeckEditor::retranslateUi() aResetLayout->setText(tr("Reset layout")); } -QString TabDeckEditor::getTabText() const +void TabDeckEditor::refreshShortcuts() { - QString result = tr("Deck: %1").arg(nameEdit->text().simplified()); - if (modified) - result.prepend("* "); - return result; + ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts(); + aResetLayout->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aResetLayout")); } -void TabDeckEditor::updateName(const QString &name) +void TabDeckEditor::showPrintingSelector() { - deckModel->getDeckList()->setName(name); - setModified(true); - setSaveStatus(true); + printingSelectorDockWidget->printingSelector->setCard(cardInfoDockWidget->cardInfo->getInfo(), DECK_ZONE_MAIN); + printingSelectorDockWidget->printingSelector->updateDisplay(); + aPrintingSelectorDockVisible->setChecked(true); + printingSelectorDockWidget->setVisible(true); } -void TabDeckEditor::updateComments() +void TabDeckEditor::loadLayout() { - deckModel->getDeckList()->setComments(commentsEdit->toPlainText()); - setModified(true); - setSaveStatus(true); -} + LayoutsSettings &layouts = SettingsCache::instance().layouts(); -void TabDeckEditor::updateBannerCardComboBox() -{ - // Store the current text of the combo box - QString currentText = bannerCardComboBox->currentText(); + setCentralWidget(databaseDisplayDockWidget); - // Block signals temporarily - bool wasBlocked = bannerCardComboBox->blockSignals(true); - - // Clear the existing items in the combo box - bannerCardComboBox->clear(); - - // Prepare the new items with deduplication - QSet> bannerCardSet; - InnerDecklistNode *listRoot = deckModel->getDeckList()->getRoot(); - for (int i = 0; i < listRoot->size(); i++) { - InnerDecklistNode *currentZone = dynamic_cast(listRoot->at(i)); - for (int j = 0; j < currentZone->size(); j++) { - DecklistCardNode *currentCard = dynamic_cast(currentZone->at(j)); - if (!currentCard) - continue; - - for (int k = 0; k < currentCard->getNumber(); ++k) { - CardInfoPtr info = CardDatabaseManager::getInstance()->getCardByNameAndProviderId( - currentCard->getName(), currentCard->getCardProviderId()); - if (info) { - bannerCardSet.insert( - QPair(currentCard->getName(), currentCard->getCardProviderId())); - } - } - } - } - - QList> pairList = bannerCardSet.values(); - - // Sort QList by the first() element of the QPair - std::sort(pairList.begin(), pairList.end(), [](const QPair &a, const QPair &b) { - return a.first.toLower() < b.first.toLower(); - }); - - for (const auto &pair : pairList) { - QVariantMap dataMap; - dataMap["name"] = pair.first; - dataMap["uuid"] = pair.second; - - bannerCardComboBox->addItem(pair.first, dataMap); - } - - // Try to restore the previous selection by finding the currentText - int restoredIndex = bannerCardComboBox->findText(currentText); - if (restoredIndex != -1) { - bannerCardComboBox->setCurrentIndex(restoredIndex); + auto &layoutState = layouts.getDeckEditorLayoutState(); + if (layoutState.isNull()) { + restartLayout(); } else { - // Add a placeholder "-" and set it as the current selection - int bannerIndex = bannerCardComboBox->findText(deckModel->getDeckList()->getBannerCard().first); - if (bannerIndex != -1) { - bannerCardComboBox->setCurrentIndex(bannerIndex); - } else { - bannerCardComboBox->insertItem(0, "-"); - bannerCardComboBox->setCurrentIndex(0); - } + restoreState(layoutState); + restoreGeometry(layouts.getDeckEditorGeometry()); } - // Restore the previous signal blocking state - bannerCardComboBox->blockSignals(wasBlocked); + aCardInfoDockVisible->setChecked(!cardInfoDockWidget->isHidden()); + aFilterDockVisible->setChecked(!filterDockWidget->isHidden()); + aDeckDockVisible->setChecked(!deckDockWidget->isHidden()); + aPrintingSelectorDockVisible->setChecked(!printingSelectorDockWidget->isHidden()); + + aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked()); + aDeckDockFloating->setEnabled(aDeckDockVisible->isChecked()); + aFilterDockFloating->setEnabled(aFilterDockVisible->isChecked()); + aPrintingSelectorDockFloating->setEnabled(aPrintingSelectorDockVisible->isChecked()); + + aCardInfoDockFloating->setChecked(cardInfoDockWidget->isFloating()); + aFilterDockFloating->setChecked(filterDockWidget->isFloating()); + aDeckDockFloating->setChecked(deckDockWidget->isFloating()); + aPrintingSelectorDockFloating->setChecked(printingSelectorDockWidget->isFloating()); + + cardInfoDockWidget->setMinimumSize(layouts.getDeckEditorCardSize()); + cardInfoDockWidget->setMaximumSize(layouts.getDeckEditorCardSize()); + + filterDockWidget->setMinimumSize(layouts.getDeckEditorFilterSize()); + filterDockWidget->setMaximumSize(layouts.getDeckEditorFilterSize()); + + deckDockWidget->setMinimumSize(layouts.getDeckEditorDeckSize()); + deckDockWidget->setMaximumSize(layouts.getDeckEditorDeckSize()); + + printingSelectorDockWidget->setMinimumSize(layouts.getDeckEditorPrintingSelectorSize()); + printingSelectorDockWidget->setMaximumSize(layouts.getDeckEditorPrintingSelectorSize()); + + databaseDisplayDockWidget->setMinimumSize(100, 100); + databaseDisplayDockWidget->setMaximumSize(1400, 5000); + + QTimer::singleShot(100, this, SLOT(freeDocksSize())); } -void TabDeckEditor::setBannerCard(int /* changedIndex */) +void TabDeckEditor::restartLayout() { - QVariantMap itemData = bannerCardComboBox->itemData(bannerCardComboBox->currentIndex()).toMap(); - deckModel->getDeckList()->setBannerCard( - QPair(itemData["name"].toString(), itemData["uuid"].toString())); - setModified(true); -} + deckDockWidget->setVisible(true); + cardInfoDockWidget->setVisible(true); + filterDockWidget->setVisible(true); + printingSelectorDockWidget->setVisible(false); -void TabDeckEditor::updateCardInfo(CardInfoPtr _card) -{ - cardInfo->setCard(_card); -} + deckDockWidget->setFloating(false); + cardInfoDockWidget->setFloating(false); + filterDockWidget->setFloating(false); + printingSelectorDockWidget->setFloating(false); -void TabDeckEditor::updateCardInfoLeft(const QModelIndex ¤t, const QModelIndex & /*previous*/) -{ - cardInfo->setCard(current.sibling(current.row(), 0).data().toString()); -} - -void TabDeckEditor::updateCardInfoRight(const QModelIndex ¤t, const QModelIndex & /*previous*/) -{ - if (!current.isValid()) - return; - if (!current.model()->hasChildren(current.sibling(current.row(), 0))) { - cardInfo->setCard(current.sibling(current.row(), 1).data().toString(), - current.sibling(current.row(), 4).data().toString()); - } -} - -void TabDeckEditor::updatePrintingSelectorDatabase(const QModelIndex ¤t, const QModelIndex & /*previous*/) -{ - const QString cardName = current.sibling(current.row(), 0).data().toString(); - const QString cardProviderID = CardDatabaseManager::getInstance()->getPreferredPrintingProviderIdForCard(cardName); - - if (!current.isValid()) { - return; - } - - if (!current.model()->hasChildren(current.sibling(current.row(), 0))) { - printingSelector->setCard( - CardDatabaseManager::getInstance()->getCardByNameAndProviderId(cardName, cardProviderID), DECK_ZONE_MAIN); - } -} - -void TabDeckEditor::updatePrintingSelectorDeckView(const QModelIndex ¤t, const QModelIndex & /*previous*/) -{ - const QString cardName = current.sibling(current.row(), 1).data().toString(); - const QString cardProviderID = current.sibling(current.row(), 4).data().toString(); - const QModelIndex gparent = current.parent().parent(); - - if (!gparent.isValid()) { - return; - } - - const QString zoneName = gparent.sibling(gparent.row(), 1).data(Qt::EditRole).toString(); - - if (!current.isValid()) { - return; - } - - if (!current.model()->hasChildren(current.sibling(current.row(), 0))) { - printingSelector->setCard( - CardDatabaseManager::getInstance()->getCardByNameAndProviderId(cardName, cardProviderID), zoneName); - } -} - -void TabDeckEditor::updateSearch(const QString &search) -{ - databaseDisplayModel->setStringFilter(search); - QModelIndexList sel = databaseView->selectionModel()->selectedRows(); - if (sel.isEmpty() && databaseDisplayModel->rowCount()) - databaseView->selectionModel()->setCurrentIndex(databaseDisplayModel->index(0, 0), - QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); -} - -void TabDeckEditor::updateHash() -{ - hashLabel->setText(deckModel->getDeckList()->getDeckHash()); -} - -void TabDeckEditor::updateRecentlyOpened() -{ - loadRecentDeckMenu->clear(); - for (const auto &deckPath : SettingsCache::instance().recents().getRecentlyOpenedDeckPaths()) { - QAction *aRecentlyOpenedDeck = new QAction(deckPath, this); - loadRecentDeckMenu->addAction(aRecentlyOpenedDeck); - connect(aRecentlyOpenedDeck, &QAction::triggered, this, - [=, this] { actOpenRecent(aRecentlyOpenedDeck->text()); }); - } - loadRecentDeckMenu->addSeparator(); - loadRecentDeckMenu->addAction(aClearRecents); - aClearRecents->setEnabled(SettingsCache::instance().recents().getRecentlyOpenedDeckPaths().length() > 0); -} - -bool TabDeckEditor::confirmClose() -{ - if (modified) { - tabSupervisor->setCurrentWidget(this); - int ret = createSaveConfirmationWindow()->exec(); - if (ret == QMessageBox::Save) - return actSaveDeck(); - else if (ret == QMessageBox::Cancel) - return false; - } - return true; -} - -void TabDeckEditor::closeRequest(bool forced) -{ - if (!forced && !confirmClose()) { - return; - } - - emit deckEditorClosing(this); - close(); -} - -void TabDeckEditor::actNewDeck() -{ - auto deckOpenLocation = confirmOpen(false); - - if (deckOpenLocation == CANCELLED) { - return; - } - - if (deckOpenLocation == NEW_TAB) { - emit openDeckEditor(nullptr); - return; - } - - deckModel->cleanList(); - nameEdit->setText(QString()); - commentsEdit->setText(QString()); - hashLabel->setText(QString()); - setModified(false); - setSaveStatus(false); -} - -void TabDeckEditor::actLoadDeck() -{ - auto deckOpenLocation = confirmOpen(); - - if (deckOpenLocation == CANCELLED) { - return; - } - - DlgLoadDeck dialog(this); - if (!dialog.exec()) - return; - - QString fileName = dialog.selectedFiles().at(0); - openDeckFromFile(fileName, deckOpenLocation); - updateBannerCardComboBox(); -} - -void TabDeckEditor::actOpenRecent(const QString &fileName) -{ - auto deckOpenLocation = confirmOpen(); - - if (deckOpenLocation == CANCELLED) { - return; - } - - openDeckFromFile(fileName, deckOpenLocation); -} - -/** - * Actually opens the deck from file - * @param fileName The path of the deck to open - * @param deckOpenLocation Which tab to open the deck - */ -void TabDeckEditor::openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation) -{ - DeckLoader::FileFormat fmt = DeckLoader::getFormatFromName(fileName); - - auto *l = new DeckLoader; - if (l->loadFromFile(fileName, fmt, true)) { - SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(fileName); - if (deckOpenLocation == NEW_TAB) { - emit openDeckEditor(l); - } else { - setSaveStatus(false); - setDeck(l); - } - } else { - delete l; - QMessageBox::critical(this, tr("Error"), tr("Could not open deck at %1").arg(fileName)); - } - setSaveStatus(true); -} - -void TabDeckEditor::actClearRecents() -{ - SettingsCache::instance().recents().clearRecentlyOpenedDeckPaths(); -} - -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 - setModified(false); -} - -bool TabDeckEditor::actSaveDeck() -{ - DeckLoader *const deck = deckModel->getDeckList(); - if (deck->getLastRemoteDeckId() != -1) { - QString deckString = deck->writeToString_Native(); - if (deckString.length() > MAX_FILE_LENGTH) { - QMessageBox::critical(this, tr("Error"), tr("Could not save remote deck")); - return false; - } - - Command_DeckUpload cmd; - cmd.set_deck_id(static_cast(deck->getLastRemoteDeckId())); - cmd.set_deck_list(deckString.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 (deck->saveToFile(deck->getLastFileName(), deck->getLastFileFormat())) { - setModified(false); - return true; - } - QMessageBox::critical( - this, tr("Error"), - tr("The deck could not be saved.\nPlease check that the directory is writable and try again.")); - return false; -} - -bool TabDeckEditor::actSaveDeckAs() -{ - QFileDialog dialog(this, tr("Save deck")); - dialog.setDirectory(SettingsCache::instance().getDeckPath()); - dialog.setAcceptMode(QFileDialog::AcceptSave); - dialog.setDefaultSuffix("cod"); - dialog.setNameFilters(DeckLoader::fileNameFilters); - dialog.selectFile(deckModel->getDeckList()->getName().trimmed() + ".cod"); - if (!dialog.exec()) - return false; - - QString fileName = dialog.selectedFiles().at(0); - DeckLoader::FileFormat fmt = DeckLoader::getFormatFromName(fileName); - - 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; - } - setModified(false); - - SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(fileName); - - return true; -} - -void TabDeckEditor::actLoadDeckFromClipboard() -{ - auto deckOpenLocation = confirmOpen(); - - if (deckOpenLocation == CANCELLED) { - return; - } - - DlgLoadDeckFromClipboard dlg(this); - if (!dlg.exec()) - return; - - if (deckOpenLocation == NEW_TAB) { - emit openDeckEditor(dlg.getDeckList()); - } else { - setDeck(dlg.getDeckList()); - setModified(true); - } - - setSaveStatus(true); -} - -static void saveDeckToClipboard(DeckLoader *deckLoader, bool addComments, bool addSetNameAndNumber) -{ - QString buffer; - QTextStream stream(&buffer); - deckLoader->saveToStream_Plain(stream, addComments, addSetNameAndNumber); - QApplication::clipboard()->setText(buffer, QClipboard::Clipboard); - QApplication::clipboard()->setText(buffer, QClipboard::Selection); -} - -void TabDeckEditor::actSaveDeckToClipboard() -{ - saveDeckToClipboard(deckModel->getDeckList(), true, true); -} - -void TabDeckEditor::actSaveDeckToClipboardNoSetNameAndNumber() -{ - saveDeckToClipboard(deckModel->getDeckList(), true, false); -} - -void TabDeckEditor::actSaveDeckToClipboardRaw() -{ - saveDeckToClipboard(deckModel->getDeckList(), false, true); -} - -void TabDeckEditor::actSaveDeckToClipboardRawNoSetNameAndNumber() -{ - saveDeckToClipboard(deckModel->getDeckList(), false, false); -} - -void TabDeckEditor::actPrintDeck() -{ - auto *dlg = new QPrintPreviewDialog(this); - connect(dlg, SIGNAL(paintRequested(QPrinter *)), deckModel, SLOT(printDeckList(QPrinter *))); - 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() -{ - auto *interface = new DeckStatsInterface(*databaseModel->getDatabase(), - this); // it deletes itself when done - interface->analyzeDeck(deckModel->getDeckList()); -} - -void TabDeckEditor::actAnalyzeDeckTappedout() -{ - auto *interface = new TappedOutInterface(*databaseModel->getDatabase(), - this); // it deletes itself when done - interface->analyzeDeck(deckModel->getDeckList()); -} - -void TabDeckEditor::actClearFilterAll() -{ - databaseDisplayModel->clearFilterAll(); - searchEdit->setText(""); -} - -void TabDeckEditor::actClearFilterOne() -{ - QModelIndexList selIndexes = filterView->selectionModel()->selectedIndexes(); - for (QModelIndex idx : selIndexes) { - filterModel->removeRow(idx.row(), idx.parent()); - } -} - -void TabDeckEditor::recursiveExpand(const QModelIndex &index) -{ - if (index.parent().isValid()) - recursiveExpand(index.parent()); - deckView->expand(index); -} - -/** - * @brief Displays the save confirmation dialogue that is shown before loading a deck, if required. Takes into - * account the `openDeckInNewTab` settting. - * - * @param openInSameTabIfBlank Open the deck in the same tab instead of a new tab if the current tab is completely - * blank. Only relevant when the `openDeckInNewTab` setting is enabled. - * - * @returns An enum that indicates if and where to load the deck - */ -TabDeckEditor::DeckOpenLocation TabDeckEditor::confirmOpen(const bool openInSameTabIfBlank) -{ - // handle `openDeckInNewTab` setting - if (SettingsCache::instance().getOpenDeckInNewTab()) { - if (openInSameTabIfBlank && isBlankNewDeck()) { - return SAME_TAB; - } else { - return NEW_TAB; - } - } - - // early return if deck is unmodified - if (!modified) { - return SAME_TAB; - } - - // do the save confirmation dialogue - tabSupervisor->setCurrentWidget(this); - - QMessageBox *msgBox = createSaveConfirmationWindow(); - QPushButton *newTabButton = msgBox->addButton(tr("Open in new tab"), QMessageBox::ApplyRole); - - int ret = msgBox->exec(); - - // `exec()` returns an opaque value if a non-standard button was clicked. - // Directly check if newTabButton was clicked before switching over the standard buttons. - if (msgBox->clickedButton() == newTabButton) { - return NEW_TAB; - } - - switch (ret) { - case QMessageBox::Save: - return actSaveDeck() ? SAME_TAB : CANCELLED; - case QMessageBox::Discard: - return SAME_TAB; - default: - return CANCELLED; - } -} - -/** - * @brief Creates the base save confirmation dialogue box. - * - * @returns A QMessageBox that can be further modified - */ -QMessageBox *TabDeckEditor::createSaveConfirmationWindow() -{ - QMessageBox *msgBox = new QMessageBox(this); - msgBox->setIcon(QMessageBox::Warning); - msgBox->setWindowTitle(tr("Are you sure?")); - msgBox->setText(tr("The decklist has been modified.\nDo you want to save the changes?")); - msgBox->setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); - return msgBox; -} - -/** - * @brief Returns true if this tab is a blank newly opened tab, as if it was just created with the `New Deck` action. - */ -bool TabDeckEditor::isBlankNewDeck() const -{ - DeckLoader *const deck = deckModel->getDeckList(); - return !modified && deck->getLastFileName().isEmpty() && deck->getLastRemoteDeckId() == -1; -} - -CardInfoPtr TabDeckEditor::currentCardInfo() const -{ - const QModelIndex currentIndex = databaseView->selectionModel()->currentIndex(); - if (!currentIndex.isValid()) { - return {}; - } - - const QString cardName = currentIndex.sibling(currentIndex.row(), 0).data().toString(); - - return CardDatabaseManager::getInstance()->getCard(cardName); -} - -/** - * Gets the index of all the currently selected card nodes in the decklist table. - * The list is in reverse order of the visual selection, so that rows can be deleted while iterating over them. - * - * @return A model index list containing all selected card nodes - */ -QModelIndexList TabDeckEditor::getSelectedCardNodes() const -{ - auto selectedRows = deckView->selectionModel()->selectedRows(); - - const auto notLeafNode = [this](const auto &index) { return deckModel->hasChildren(index); }; - selectedRows.erase(std::remove_if(selectedRows.begin(), selectedRows.end(), notLeafNode), selectedRows.end()); - - std::reverse(selectedRows.begin(), selectedRows.end()); - return selectedRows; -} - -void TabDeckEditor::addCardHelper(const CardInfoPtr info, QString zoneName) -{ - if (!info) - return; - if (info->getIsToken()) - zoneName = DECK_ZONE_TOKENS; - - QModelIndex newCardIndex = deckModel->addPreferredPrintingCard(info->getName(), zoneName, false); - recursiveExpand(newCardIndex); - deckView->clearSelection(); - deckView->setCurrentIndex(newCardIndex); - setModified(true); - searchEdit->setSelection(0, searchEdit->text().length()); -} - -void TabDeckEditor::actSwapCard() -{ - auto selectedRows = getSelectedCardNodes(); - - // hack to maintain the old reselection behavior when currently selected row of a single-selection gets deleted - // TODO: remove the hack and also handle reselection when all rows of a multi-selection gets deleted - if (selectedRows.length() == 1) { - deckView->setSelectionMode(QAbstractItemView::SingleSelection); - } - - bool isModified = false; - for (const auto ¤tIndex : selectedRows) { - if (swapCard(currentIndex)) { - isModified = true; - } - } - - deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); - - if (isModified) { - setModified(true); - setSaveStatus(true); - } - - update(); -} - -/** - * Swaps the card at the index between the maindeck and sideboard - * - * @param currentIndex The index to swap. - * @return True if the swap was successful - */ -bool TabDeckEditor::swapCard(const QModelIndex ¤tIndex) -{ - if (!currentIndex.isValid()) - return false; - const QString cardName = currentIndex.sibling(currentIndex.row(), 1).data().toString(); - const QString cardProviderID = currentIndex.sibling(currentIndex.row(), 4).data().toString(); - const QModelIndex gparent = currentIndex.parent().parent(); - - if (!gparent.isValid()) - return false; - - const QString zoneName = gparent.sibling(gparent.row(), 1).data(Qt::EditRole).toString(); - offsetCountAtIndex(currentIndex, -1); - const QString otherZoneName = zoneName == DECK_ZONE_MAIN ? DECK_ZONE_SIDE : DECK_ZONE_MAIN; - - // Third argument (true) says create the card no matter what, even if not in DB - QModelIndex newCardIndex = deckModel->addCard( - cardName, CardDatabaseManager::getInstance()->getSpecificSetForCard(cardName, cardProviderID), otherZoneName, - true); - recursiveExpand(newCardIndex); - - return true; -} - -void TabDeckEditor::actAddCard() -{ - if (QApplication::keyboardModifiers() & Qt::ControlModifier) - actAddCardToSideboard(); - else - addCardHelper(currentCardInfo(), DECK_ZONE_MAIN); - setSaveStatus(true); -} - -void TabDeckEditor::actAddCardToSideboard() -{ - addCardHelper(currentCardInfo(), DECK_ZONE_SIDE); - setSaveStatus(true); -} - -void TabDeckEditor::actRemoveCard() -{ - auto selectedRows = getSelectedCardNodes(); - - // hack to maintain the old reselection behavior when currently selected row of a single-selection gets deleted - // TODO: remove the hack and also handle reselection when all rows of a multi-selection gets deleted - if (selectedRows.length() == 1) { - deckView->setSelectionMode(QAbstractItemView::SingleSelection); - } - - bool isModified = false; - for (const auto &index : selectedRows) { - if (!index.isValid() || deckModel->hasChildren(index)) { - continue; - } - deckModel->removeRow(index.row(), index.parent()); - isModified = true; - } - - deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); - - if (isModified) { - DeckLoader *const deck = deckModel->getDeckList(); - setSaveStatus(!deck->isEmpty()); - setModified(true); - } -} - -void TabDeckEditor::offsetCountAtIndex(const QModelIndex &idx, int offset) -{ - if (!idx.isValid() || deckModel->hasChildren(idx)) { - return; - } - - const QModelIndex numberIndex = idx.sibling(idx.row(), 0); - const int count = deckModel->data(numberIndex, Qt::EditRole).toInt(); - const int new_count = count + offset; - if (new_count <= 0) - deckModel->removeRow(idx.row(), idx.parent()); - else - deckModel->setData(numberIndex, new_count, Qt::EditRole); - setModified(true); -} - -void TabDeckEditor::decrementCardHelper(QString zoneName) -{ - const CardInfoPtr info = currentCardInfo(); - - if (!info) - return; - if (info->getIsToken()) - zoneName = DECK_ZONE_TOKENS; - - QModelIndex idx = deckModel->findCard(info->getName(), zoneName); - if (!idx.isValid()) { - return; - } - deckView->clearSelection(); - deckView->setCurrentIndex(idx); - offsetCountAtIndex(idx, -1); -} - -void TabDeckEditor::actDecrementCard() -{ - decrementCardHelper(DECK_ZONE_MAIN); -} - -void TabDeckEditor::actDecrementCardFromSideboard() -{ - decrementCardHelper(DECK_ZONE_SIDE); -} - -void TabDeckEditor::copyDatabaseCellContents() -{ - auto _data = databaseView->selectionModel()->currentIndex().data(); - QApplication::clipboard()->setText(_data.toString()); -} - -void TabDeckEditor::actIncrement() -{ - auto selectedRows = getSelectedCardNodes(); - - for (const auto &index : selectedRows) { - offsetCountAtIndex(index, 1); - } -} - -void TabDeckEditor::actDecrement() -{ - auto selectedRows = getSelectedCardNodes(); - - // hack to maintain the old reselection behavior when currently selected row of a single-selection gets deleted - // TODO: remove the hack and also handle reselection when all rows of a multi-selection gets deleted - if (selectedRows.length() == 1) { - deckView->setSelectionMode(QAbstractItemView::SingleSelection); - } - - for (const auto &index : selectedRows) { - offsetCountAtIndex(index, -1); - } - - deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); -} - -void TabDeckEditor::setDeck(DeckLoader *_deck) -{ - deckModel->setDeckList(_deck); - - nameEdit->setText(deckModel->getDeckList()->getName()); - commentsEdit->setText(deckModel->getDeckList()->getComments()); - bannerCardComboBox->setCurrentText(deckModel->getDeckList()->getBannerCard().first); - updateBannerCardComboBox(); - updateHash(); - deckModel->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder()); - deckView->expandAll(); - setModified(false); - - PictureLoader::cacheCardPixmaps( - CardDatabaseManager::getInstance()->getCards(deckModel->getDeckList()->getCardList())); - deckView->expandAll(); - setModified(false); - - deckTagsDisplayWidget->connectDeckList(deckModel->getDeckList()); - - // If they load a deck, make the deck list appear + aCardInfoDockVisible->setChecked(true); aDeckDockVisible->setChecked(true); - deckDock->setVisible(aDeckDockVisible->isChecked()); + aFilterDockVisible->setChecked(true); + aPrintingSelectorDockVisible->setChecked(false); + + aCardInfoDockFloating->setChecked(false); + aDeckDockFloating->setChecked(false); + aFilterDockFloating->setChecked(false); + aPrintingSelectorDockFloating->setChecked(false); + + setCentralWidget(databaseDisplayDockWidget); + addDockWidget(static_cast(2), deckDockWidget); + addDockWidget(static_cast(2), cardInfoDockWidget); + addDockWidget(static_cast(2), filterDockWidget); + addDockWidget(static_cast(2), printingSelectorDockWidget); + + splitDockWidget(cardInfoDockWidget, printingSelectorDockWidget, Qt::Horizontal); + splitDockWidget(printingSelectorDockWidget, deckDockWidget, Qt::Horizontal); + splitDockWidget(cardInfoDockWidget, printingSelectorDockWidget, Qt::Horizontal); + splitDockWidget(cardInfoDockWidget, filterDockWidget, Qt::Vertical); + + QTimer::singleShot(100, this, SLOT(freeDocksSize())); } -void TabDeckEditor::setModified(bool _modified) +void TabDeckEditor::freeDocksSize() { - modified = _modified; - emit tabTextChanged(this, getTabText()); -} + deckDockWidget->setMinimumSize(100, 100); + deckDockWidget->setMaximumSize(5000, 5000); -void TabDeckEditor::filterViewCustomContextMenu(const QPoint &point) -{ - QMenu menu; - QAction *action; - QModelIndex idx; + cardInfoDockWidget->setMinimumSize(100, 100); + cardInfoDockWidget->setMaximumSize(5000, 5000); - idx = filterView->indexAt(point); - if (!idx.isValid()) - return; + filterDockWidget->setMinimumSize(100, 100); + filterDockWidget->setMaximumSize(5000, 5000); - action = menu.addAction(QString("delete")); - action->setData(point); - connect(&menu, SIGNAL(triggered(QAction *)), this, SLOT(filterRemove(QAction *))); - menu.exec(filterView->mapToGlobal(point)); -} + printingSelectorDockWidget->setMinimumSize(525, 100); + printingSelectorDockWidget->setMaximumSize(5000, 5000); -void TabDeckEditor::filterRemove(QAction *action) -{ - QPoint point; - QModelIndex idx; - - point = action->data().toPoint(); - idx = filterView->indexAt(point); - if (!idx.isValid()) - return; - - filterModel->removeRow(idx.row(), idx.parent()); -} - -// Method uses to sync docks state with menu items state -bool TabDeckEditor::eventFilter(QObject *o, QEvent *e) -{ - if (e->type() == QEvent::Close) { - if (o == cardInfoDock) { - aCardInfoDockVisible->setChecked(false); - aCardInfoDockFloating->setEnabled(false); - } else if (o == deckDock) { - aDeckDockVisible->setChecked(false); - aDeckDockFloating->setEnabled(false); - } else if (o == filterDock) { - aFilterDockVisible->setChecked(false); - aFilterDockFloating->setEnabled(false); - } else if (o == printingSelectorDock) { - aPrintingSelectorDockVisible->setChecked(false); - aPrintingSelectorDockFloating->setEnabled(false); - } - } - if (o == this && e->type() == QEvent::Hide) { - LayoutsSettings &layouts = SettingsCache::instance().layouts(); - layouts.setDeckEditorLayoutState(saveState()); - layouts.setDeckEditorGeometry(saveGeometry()); - layouts.setDeckEditorCardSize(cardInfoDock->size()); - layouts.setDeckEditorFilterSize(filterDock->size()); - layouts.setDeckEditorDeckSize(deckDock->size()); - layouts.setDeckEditorPrintingSelectorSize(printingSelectorDock->size()); - } - return false; + databaseDisplayDockWidget->setMinimumSize(100, 100); + databaseDisplayDockWidget->setMaximumSize(1400, 5000); } void TabDeckEditor::dockVisibleTriggered() { QObject *o = sender(); if (o == aCardInfoDockVisible) { - cardInfoDock->setHidden(!aCardInfoDockVisible->isChecked()); + cardInfoDockWidget->setHidden(!aCardInfoDockVisible->isChecked()); aCardInfoDockFloating->setEnabled(aCardInfoDockVisible->isChecked()); return; } if (o == aDeckDockVisible) { - deckDock->setHidden(!aDeckDockVisible->isChecked()); + deckDockWidget->setHidden(!aDeckDockVisible->isChecked()); aDeckDockFloating->setEnabled(aDeckDockVisible->isChecked()); return; } if (o == aFilterDockVisible) { - filterDock->setHidden(!aFilterDockVisible->isChecked()); + filterDockWidget->setHidden(!aFilterDockVisible->isChecked()); aFilterDockFloating->setEnabled(aFilterDockVisible->isChecked()); return; } if (o == aPrintingSelectorDockVisible) { - printingSelectorDock->setHidden(!aPrintingSelectorDockVisible->isChecked()); + printingSelectorDockWidget->setHidden(!aPrintingSelectorDockVisible->isChecked()); aPrintingSelectorDockFloating->setEnabled(aPrintingSelectorDockVisible->isChecked()); return; } @@ -1707,22 +289,22 @@ void TabDeckEditor::dockFloatingTriggered() { QObject *o = sender(); if (o == aCardInfoDockFloating) { - cardInfoDock->setFloating(aCardInfoDockFloating->isChecked()); + cardInfoDockWidget->setFloating(aCardInfoDockFloating->isChecked()); return; } if (o == aDeckDockFloating) { - deckDock->setFloating(aDeckDockFloating->isChecked()); + deckDockWidget->setFloating(aDeckDockFloating->isChecked()); return; } if (o == aFilterDockFloating) { - filterDock->setFloating(aFilterDockFloating->isChecked()); + filterDockWidget->setFloating(aFilterDockFloating->isChecked()); return; } if (o == aPrintingSelectorDockFloating) { - printingSelectorDock->setFloating(aPrintingSelectorDockFloating->isChecked()); + printingSelectorDockWidget->setFloating(aPrintingSelectorDockFloating->isChecked()); return; } } @@ -1730,77 +312,53 @@ void TabDeckEditor::dockFloatingTriggered() void TabDeckEditor::dockTopLevelChanged(bool topLevel) { QObject *o = sender(); - if (o == cardInfoDock) { + if (o == cardInfoDockWidget) { aCardInfoDockFloating->setChecked(topLevel); return; } - if (o == deckDock) { + if (o == deckDockWidget) { aDeckDockFloating->setChecked(topLevel); return; } - if (o == filterDock) { + if (o == filterDockWidget) { aFilterDockFloating->setChecked(topLevel); return; } - if (o == printingSelectorDock) { + if (o == printingSelectorDockWidget) { aPrintingSelectorDockFloating->setChecked(topLevel); return; } } -void TabDeckEditor::saveDbHeaderState() +// Method uses to sync docks state with menu items state +bool TabDeckEditor::eventFilter(QObject *o, QEvent *e) { - SettingsCache::instance().layouts().setDeckEditorDbHeaderState(databaseView->header()->saveState()); -} - -void TabDeckEditor::setSaveStatus(bool newStatus) -{ - aSaveDeck->setEnabled(newStatus); - aSaveDeckAs->setEnabled(newStatus); - aSaveDeckToClipboard->setEnabled(newStatus); - aSaveDeckToClipboardNoSetNameAndNumber->setEnabled(newStatus); - aSaveDeckToClipboardRaw->setEnabled(newStatus); - aSaveDeckToClipboardRawNoSetNameAndNumber->setEnabled(newStatus); - saveDeckToClipboardMenu->setEnabled(newStatus); - aPrintDeck->setEnabled(newStatus); - analyzeDeckMenu->setEnabled(newStatus); -} - -void TabDeckEditor::showSearchSyntaxHelp() -{ - - QFile file("theme:help/search.md"); - - if (!file.open(QFile::ReadOnly | QFile::Text)) { - return; + if (e->type() == QEvent::Close) { + if (o == cardInfoDockWidget) { + aCardInfoDockVisible->setChecked(false); + aCardInfoDockFloating->setEnabled(false); + } else if (o == deckDockWidget) { + aDeckDockVisible->setChecked(false); + aDeckDockFloating->setEnabled(false); + } else if (o == filterDockWidget) { + aFilterDockVisible->setChecked(false); + aFilterDockFloating->setEnabled(false); + } else if (o == printingSelectorDockWidget) { + aPrintingSelectorDockVisible->setChecked(false); + aPrintingSelectorDockFloating->setEnabled(false); + } } - - QTextStream in(&file); - QString text = in.readAll(); - file.close(); - - // Poor Markdown Converter - auto opts = QRegularExpression::MultilineOption; - text = text.replace(QRegularExpression("^(###)(.*)", opts), "

\\2

") - .replace(QRegularExpression("^(##)(.*)", opts), "

\\2

") - .replace(QRegularExpression("^(#)(.*)", opts), "

\\2

") - .replace(QRegularExpression("^------*", opts), "
") - .replace(QRegularExpression(R"(\[([^[]+)\]\(([^\)]+)\))", opts), R"(\1)"); - - auto browser = new QTextBrowser; - browser->setParent(this, Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | - Qt::WindowCloseButtonHint | Qt::WindowFullscreenButtonHint); - browser->setWindowTitle("Search Help"); - browser->setReadOnly(true); - browser->setMinimumSize({500, 600}); - - QString sheet = QString("a { text-decoration: underline; color: rgb(71,158,252) };"); - browser->document()->setDefaultStyleSheet(sheet); - - browser->setHtml(text); - connect(browser, &QTextBrowser::anchorClicked, [this](const QUrl &link) { searchEdit->setText(link.fragment()); }); - browser->show(); -} + if (o == this && e->type() == QEvent::Hide) { + LayoutsSettings &layouts = SettingsCache::instance().layouts(); + layouts.setDeckEditorLayoutState(saveState()); + layouts.setDeckEditorGeometry(saveGeometry()); + layouts.setDeckEditorCardSize(cardInfoDockWidget->size()); + layouts.setDeckEditorFilterSize(filterDockWidget->size()); + layouts.setDeckEditorDeckSize(deckDockWidget->size()); + layouts.setDeckEditorPrintingSelectorSize(printingSelectorDockWidget->size()); + } + return false; +} \ No newline at end of file diff --git a/cockatrice/src/client/tabs/tab_deck_editor.h b/cockatrice/src/client/tabs/tab_deck_editor.h index 9bb696e34..d9b10de7d 100644 --- a/cockatrice/src/client/tabs/tab_deck_editor.h +++ b/cockatrice/src/client/tabs/tab_deck_editor.h @@ -1,194 +1,41 @@ #ifndef WINDOW_DECKEDITOR_H #define WINDOW_DECKEDITOR_H -#include "../../deck/custom_line_edit.h" #include "../../game/cards/card_database.h" #include "../game_logic/key_signals.h" -#include "../ui/widgets/printing_selector/printing_selector.h" #include "../ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h" -#include "tab.h" - -#include -#include +#include "abstract_tab_deck_editor.h" class CardDatabaseModel; class CardDatabaseDisplayModel; class DeckListModel; -class QTreeView; -class CardInfoFrameWidget; -class QTextEdit; class QLabel; class DeckLoader; -class DeckPreviewDeckTagsDisplayWidget; -class Response; -class FilterTreeModel; -class FilterBuilder; -class QComboBox; -class QGroupBox; -class QMessageBox; -class QHBoxLayout; -class QVBoxLayout; -class QPushButton; -class QDockWidget; -class TabDeckEditor : public Tab +class TabDeckEditor : public AbstractTabDeckEditor { Q_OBJECT -private slots: - void updateName(const QString &name); - void updateComments(); - void updateBannerCardComboBox(); - void setBannerCard(int); - void updateHash(); - void updateCardInfoLeft(const QModelIndex ¤t, const QModelIndex &previous); - void updateCardInfoRight(const QModelIndex ¤t, const QModelIndex &previous); - void updatePrintingSelectorDatabase(const QModelIndex ¤t, const QModelIndex &previous); - void updatePrintingSelectorDeckView(const QModelIndex ¤t, const QModelIndex &previous); - void updateSearch(const QString &search); - void databaseCustomMenu(QPoint point); - void decklistCustomMenu(QPoint point); - void updateRecentlyOpened(); - void actNewDeck(); - void actLoadDeck(); - void actOpenRecent(const QString &fileName); - void actClearRecents(); - bool actSaveDeck(); - bool actSaveDeckAs(); - void actLoadDeckFromClipboard(); - void actSaveDeckToClipboard(); - void actSaveDeckToClipboardNoSetNameAndNumber(); - void actSaveDeckToClipboardRaw(); - void actSaveDeckToClipboardRawNoSetNameAndNumber(); - void actPrintDeck(); - void actExportDeckDecklist(); - void actAnalyzeDeckDeckstats(); - void actAnalyzeDeckTappedout(); - - void actClearFilterAll(); - void actClearFilterOne(); - - void actSwapCard(); - void actAddCard(); - void actAddCardToSideboard(); - void actRemoveCard(); - void actIncrement(); - void actDecrement(); - void actDecrementCard(); - void actDecrementCardFromSideboard(); - void copyDatabaseCellContents(); - - void saveDeckRemoteFinished(const Response &r); - void filterViewCustomContextMenu(const QPoint &point); - void filterRemove(QAction *action); - - void loadLayout(); - void restartLayout(); - void freeDocksSize(); - void refreshShortcuts(); +protected slots: + void loadLayout() override; + void restartLayout() override; + void freeDocksSize() override; + void refreshShortcuts() override; bool eventFilter(QObject *o, QEvent *e) override; - void dockVisibleTriggered(); - void dockFloatingTriggered(); - void dockTopLevelChanged(bool topLevel); - void saveDbHeaderState(); - void setSaveStatus(bool newStatus); - void showSearchSyntaxHelp(); - -private: - /** - * @brief Which tab to open the new deck in - */ - enum DeckOpenLocation - { - CANCELLED, - SAME_TAB, - NEW_TAB - }; - - DeckOpenLocation confirmOpen(const bool openInSameTabIfBlank = true); - QMessageBox *createSaveConfirmationWindow(); - - bool isBlankNewDeck() const; - CardInfoPtr currentCardInfo() const; - void offsetCountAtIndex(const QModelIndex &idx, int offset); - void decrementCardHelper(QString zoneName); - bool swapCard(const QModelIndex &idx); - void recursiveExpand(const QModelIndex &index); - void openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation); - - QModelIndexList getSelectedCardNodes() const; - - CardDatabaseModel *databaseModel; - CardDatabaseDisplayModel *databaseDisplayModel; - DeckListModel *deckModel; - QTreeView *databaseView; - - QTreeView *deckView; - KeySignals deckViewKeySignals; - CardInfoFrameWidget *cardInfo; - PrintingSelector *printingSelector; - SearchLineEdit *searchEdit; - KeySignals searchKeySignals; - - QLabel *nameLabel; - LineEditUnfocusable *nameEdit; - QLabel *commentsLabel; - QTextEdit *commentsEdit; - QLabel *bannerCardLabel; - QComboBox *bannerCardComboBox; - DeckPreviewDeckTagsDisplayWidget *deckTagsDisplayWidget; - QLabel *hashLabel1; - LineEditUnfocusable *hashLabel; - FilterTreeModel *filterModel; - QTreeView *filterView; - KeySignals filterViewKeySignals; - QWidget *filterBox; - - QMenu *deckMenu, *viewMenu, *cardInfoDockMenu, *deckDockMenu, *filterDockMenu, *printingSelectorDockMenu, - *analyzeDeckMenu, *saveDeckToClipboardMenu, *loadRecentDeckMenu; - QAction *aNewDeck, *aLoadDeck, *aClearRecents, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, - *aSaveDeckToClipboard, *aSaveDeckToClipboardNoSetNameAndNumber, *aSaveDeckToClipboardRaw, - *aSaveDeckToClipboardRawNoSetNameAndNumber, *aPrintDeck, *aExportDeckDecklist, *aAnalyzeDeckDeckstats, - *aAnalyzeDeckTappedout, *aClose; - QAction *aClearFilterAll, *aClearFilterOne; - QAction *aAddCard, *aAddCardToSideboard, *aRemoveCard, *aIncrement, *aDecrement, *aSwapCard; - QAction *aResetLayout; - QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aDeckDockVisible, *aDeckDockFloating, *aFilterDockVisible, - *aFilterDockFloating, *aPrintingSelectorDockVisible, *aPrintingSelectorDockFloating; - - bool modified; - QVBoxLayout *centralFrame; - QHBoxLayout *searchLayout; - QDockWidget *cardInfoDock; - QDockWidget *deckDock; - QDockWidget *filterDock; - QDockWidget *printingSelectorDock; - QWidget *centralWidget; + void dockVisibleTriggered() override; + void dockFloatingTriggered() override; + void dockTopLevelChanged(bool topLevel) override; public: explicit TabDeckEditor(TabSupervisor *_tabSupervisor); void retranslateUi() override; QString getTabText() const override; - void setDeck(DeckLoader *_deckLoader); - void setModified(bool _windowModified); - bool confirmClose(); - void createDeckDock(); - void createCardInfoDock(); - void createFiltersDock(); - void createPrintingSelectorDock(); - void createMenus(); - void createCentralFrame(); - void updateCardInfo(CardInfoPtr _card); - void addCardHelper(CardInfoPtr info, QString zoneName); + void createMenus() override; public slots: - void closeRequest(bool forced = false) override; - void showPrintingSelector(); -signals: - void openDeckEditor(const DeckLoader *deckLoader); - void deckEditorClosing(TabDeckEditor *tab); + void showPrintingSelector() override; }; #endif diff --git a/cockatrice/src/client/tabs/tab_supervisor.cpp b/cockatrice/src/client/tabs/tab_supervisor.cpp index af9a4a101..cd8a0d2f2 100644 --- a/cockatrice/src/client/tabs/tab_supervisor.cpp +++ b/cockatrice/src/client/tabs/tab_supervisor.cpp @@ -201,7 +201,7 @@ void TabSupervisor::retranslateUi() QListIterator replayIterator(replayTabs); while (replayIterator.hasNext()) tabs.append(replayIterator.next()); - QListIterator deckEditorIterator(deckEditorTabs); + QListIterator deckEditorIterator(deckEditorTabs); while (deckEditorIterator.hasNext()) tabs.append(deckEditorIterator.next()); QMapIterator messageIterator(messageTabs); @@ -242,7 +242,7 @@ bool TabSupervisor::closeRequest() } } - for (TabDeckEditor *tab : deckEditorTabs) { + for (AbstractTabDeckEditor *tab : deckEditorTabs) { if (!tab->confirmClose()) return false; } @@ -706,8 +706,8 @@ TabDeckEditor *TabSupervisor::addDeckEditorTab(const DeckLoader *deckToOpen) auto *tab = new TabDeckEditor(this); if (deckToOpen) tab->setDeck(new DeckLoader(*deckToOpen)); - connect(tab, &TabDeckEditor::deckEditorClosing, this, &TabSupervisor::deckEditorClosed); - connect(tab, &TabDeckEditor::openDeckEditor, this, &TabSupervisor::addDeckEditorTab); + connect(tab, &AbstractTabDeckEditor::deckEditorClosing, this, &TabSupervisor::deckEditorClosed); + connect(tab, &AbstractTabDeckEditor::openDeckEditor, this, &TabSupervisor::addDeckEditorTab); myAddTab(tab); deckEditorTabs.append(tab); setCurrentWidget(tab); @@ -726,7 +726,7 @@ TabEdhRec *TabSupervisor::addEdhrecTab(const CardInfoPtr &cardToQuery, bool isCo return tab; } -void TabSupervisor::deckEditorClosed(TabDeckEditor *tab) +void TabSupervisor::deckEditorClosed(AbstractTabDeckEditor *tab) { if (tab == currentWidget()) emit setMenu(); diff --git a/cockatrice/src/client/tabs/tab_supervisor.h b/cockatrice/src/client/tabs/tab_supervisor.h index db941aacc..b26fae558 100644 --- a/cockatrice/src/client/tabs/tab_supervisor.h +++ b/cockatrice/src/client/tabs/tab_supervisor.h @@ -3,6 +3,7 @@ #include "../../deck/deck_loader.h" #include "../../server/user/user_list_proxy.h" +#include "abstract_tab_deck_editor.h" #include "api/edhrec/tab_edhrec.h" #include "visual_deck_storage/tab_deck_storage_visual.h" @@ -87,7 +88,7 @@ private: QMap gameTabs; QList replayTabs; QMap messageTabs; - QList deckEditorTabs; + QList deckEditorTabs; bool isLocalGame; QAction *aTabDeckEditor, *aTabVisualDeckStorage, *aTabServer, *aTabAccount, *aTabDeckStorage, *aTabReplays, @@ -132,7 +133,7 @@ public: { return roomTabs; } - QList getDeckEditorTabs() const + QList getDeckEditorTabs() const { return deckEditorTabs; } @@ -174,7 +175,7 @@ private slots: void processUserLeft(const QString &userName); void processUserJoined(const ServerInfo_User &userInfo); void talkLeft(TabMessage *tab); - void deckEditorClosed(TabDeckEditor *tab); + void deckEditorClosed(AbstractTabDeckEditor *tab); void tabUserEvent(bool globalEvent); void updateTabText(Tab *tab, const QString &newTabText); void processRoomEvent(const RoomEvent &event); diff --git a/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.cpp b/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.cpp index f5a54e5c2..5d746a442 100644 --- a/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.cpp +++ b/cockatrice/src/client/ui/widgets/cards/card_info_picture_widget.cpp @@ -296,7 +296,7 @@ QMenu *CardInfoPictureWidget::createAddToOpenDeckMenu() auto addToOpenDeckMenu = new QMenu(tr("Add card to deck")); auto *mainWindow = qobject_cast(window()); - QList deckEditorTabs = mainWindow->getTabSupervisor()->getDeckEditorTabs(); + QList deckEditorTabs = mainWindow->getTabSupervisor()->getDeckEditorTabs(); if (deckEditorTabs.isEmpty()) { addToOpenDeckMenu->setEnabled(false); @@ -308,14 +308,14 @@ QMenu *CardInfoPictureWidget::createAddToOpenDeckMenu() QAction *addCard = addCardMenu->addAction(tr("Mainboard")); connect(addCard, &QAction::triggered, this, [this, deckEditorTab] { - deckEditorTab->updateCardInfo(info); - deckEditorTab->addCardHelper(info, DECK_ZONE_MAIN); + deckEditorTab->updateCard(info); + deckEditorTab->actAddCard(info); }); QAction *addCardSideboard = addCardMenu->addAction(tr("Sideboard")); connect(addCardSideboard, &QAction::triggered, this, [this, deckEditorTab] { - deckEditorTab->updateCardInfo(info); - deckEditorTab->addCardHelper(info, DECK_ZONE_SIDE); + deckEditorTab->updateCard(info); + deckEditorTab->actAddCardToSideboard(info); }); } diff --git a/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_card_info_dock_widget.cpp b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_card_info_dock_widget.cpp new file mode 100644 index 000000000..0fe4ae76d --- /dev/null +++ b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_card_info_dock_widget.cpp @@ -0,0 +1,46 @@ +#include "deck_editor_card_info_dock_widget.h" + +#include "../cards/card_info_frame_widget.h" + +#include + +DeckEditorCardInfoDockWidget::DeckEditorCardInfoDockWidget(AbstractTabDeckEditor *parent) + : QDockWidget(parent), deckEditor(parent) +{ + setObjectName("cardInfoDock"); + + setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); + setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); + + createCardInfoDock(); + + retranslateUi(); +} + +void DeckEditorCardInfoDockWidget::createCardInfoDock() +{ + cardInfo = new CardInfoFrameWidget(); + cardInfo->setObjectName("cardInfo"); + auto *cardInfoFrame = new QVBoxLayout; + cardInfoFrame->setObjectName("cardInfoFrame"); + cardInfoFrame->addWidget(cardInfo); + + auto *cardInfoDockContents = new QWidget(); + cardInfoDockContents->setObjectName("cardInfoDockContents"); + cardInfoDockContents->setLayout(cardInfoFrame); + setWidget(cardInfoDockContents); + + installEventFilter(deckEditor); + connect(this, &QDockWidget::topLevelChanged, deckEditor, &AbstractTabDeckEditor::dockTopLevelChanged); +} + +void DeckEditorCardInfoDockWidget::updateCard(CardInfoPtr _card) +{ + cardInfo->setCard(_card); +} + +void DeckEditorCardInfoDockWidget::retranslateUi() +{ + setWindowTitle(tr("Card Info")); + cardInfo->retranslateUi(); +} \ No newline at end of file diff --git a/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_card_info_dock_widget.h b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_card_info_dock_widget.h new file mode 100644 index 000000000..455df1e88 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_card_info_dock_widget.h @@ -0,0 +1,25 @@ +#ifndef DECK_EDITOR_CARD_INFO_DOCK_WIDGET_H +#define DECK_EDITOR_CARD_INFO_DOCK_WIDGET_H + +#include "../../../tabs/abstract_tab_deck_editor.h" +#include "../cards/card_info_frame_widget.h" + +#include + +class AbstractTabDeckEditor; +class DeckEditorCardInfoDockWidget : public QDockWidget +{ + Q_OBJECT +public: + explicit DeckEditorCardInfoDockWidget(AbstractTabDeckEditor *parent); + void createCardInfoDock(); + void retranslateUi(); + + AbstractTabDeckEditor *deckEditor; + CardInfoFrameWidget *cardInfo; + +public slots: + void updateCard(CardInfoPtr _card); +}; + +#endif // DECK_EDITOR_CARD_INFO_DOCK_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_database_display_widget.cpp b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_database_display_widget.cpp new file mode 100644 index 000000000..b21062245 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_database_display_widget.cpp @@ -0,0 +1,279 @@ +#include "deck_editor_database_display_widget.h" + +#include "../../../../game/cards/card_database_manager.h" +#include "../../../../settings/cache_settings.h" +#include "../../../tabs/abstract_tab_deck_editor.h" +#include "../../../tabs/tab_supervisor.h" +#include "../../../ui/pixel_map_generator.h" + +#include +#include +#include +#include +#include +#include +#include + +static bool canBeCommander(const CardInfoPtr &cardInfo) +{ + return ((cardInfo->getCardType().contains("Legendary", Qt::CaseInsensitive) && + cardInfo->getCardType().contains("Creature", Qt::CaseInsensitive))) || + cardInfo->getText().contains("can be your commander", Qt::CaseInsensitive); +} + +DeckEditorDatabaseDisplayWidget::DeckEditorDatabaseDisplayWidget(AbstractTabDeckEditor *parent) + : QWidget(parent), deckEditor(parent) +{ + setObjectName("centralWidget"); + + centralFrame = new QVBoxLayout(this); + centralFrame->setObjectName("centralFrame"); + setLayout(centralFrame); + + searchEdit = new SearchLineEdit(); + searchEdit->setObjectName("searchEdit"); + searchEdit->setPlaceholderText(tr("Search by card name (or search expressions)")); + searchEdit->setClearButtonEnabled(true); + searchEdit->addAction(loadColorAdjustedPixmap("theme:icons/search"), QLineEdit::LeadingPosition); + auto help = searchEdit->addAction(QPixmap("theme:icons/info"), QLineEdit::TrailingPosition); + searchEdit->installEventFilter(&searchKeySignals); + + setFocusProxy(searchEdit); + setFocusPolicy(Qt::ClickFocus); + + searchKeySignals.setObjectName("searchKeySignals"); + connect(searchEdit, SIGNAL(textChanged(const QString &)), this, SLOT(updateSearch(const QString &))); + connect(&searchKeySignals, &KeySignals::onEnter, this, &DeckEditorDatabaseDisplayWidget::actAddCardToMainDeck); + connect(&searchKeySignals, &KeySignals::onCtrlAltEqual, this, + &DeckEditorDatabaseDisplayWidget::actAddCardToMainDeck); + connect(&searchKeySignals, &KeySignals::onCtrlAltRBracket, this, + &DeckEditorDatabaseDisplayWidget::actAddCardToSideboard); + connect(&searchKeySignals, &KeySignals::onCtrlAltMinus, this, + &DeckEditorDatabaseDisplayWidget::actDecrementCardFromMainDeck); + connect(&searchKeySignals, &KeySignals::onCtrlAltLBracket, this, + &DeckEditorDatabaseDisplayWidget::actDecrementCardFromSideboard); + connect(&searchKeySignals, &KeySignals::onCtrlAltEnter, this, + &DeckEditorDatabaseDisplayWidget::actAddCardToSideboard); + connect(&searchKeySignals, &KeySignals::onCtrlEnter, this, &DeckEditorDatabaseDisplayWidget::actAddCardToSideboard); + connect(&searchKeySignals, &KeySignals::onCtrlC, this, &DeckEditorDatabaseDisplayWidget::copyDatabaseCellContents); + connect(help, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::showSearchSyntaxHelp); + + databaseModel = new CardDatabaseModel(CardDatabaseManager::getInstance(), true, this); + databaseModel->setObjectName("databaseModel"); + databaseDisplayModel = new CardDatabaseDisplayModel(this); + databaseDisplayModel->setObjectName("databaseDisplayModel"); + databaseDisplayModel->setSourceModel(databaseModel); + databaseDisplayModel->setFilterKeyColumn(0); + + databaseView = new QTreeView(this); + databaseView->setObjectName("databaseView"); + databaseView->setFocusProxy(searchEdit); + databaseView->setUniformRowHeights(true); + databaseView->setRootIsDecorated(false); + databaseView->setAlternatingRowColors(true); + databaseView->setSortingEnabled(true); + databaseView->sortByColumn(0, Qt::AscendingOrder); + databaseView->setModel(databaseDisplayModel); + databaseView->setContextMenuPolicy(Qt::CustomContextMenu); + connect(databaseView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(databaseCustomMenu(QPoint))); + connect(databaseView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, + &DeckEditorDatabaseDisplayWidget::updateCard); + connect(databaseView, &QTreeView::doubleClicked, this, &DeckEditorDatabaseDisplayWidget::actAddCardToMainDeck); + + QByteArray dbHeaderState = SettingsCache::instance().layouts().getDeckEditorDbHeaderState(); + if (dbHeaderState.isNull()) { + // first run + databaseView->setColumnWidth(0, 200); + } else { + databaseView->header()->restoreState(dbHeaderState); + } + connect(databaseView->header(), SIGNAL(geometriesChanged()), this, SLOT(saveDbHeaderState())); + + searchEdit->setTreeView(databaseView); + + aAddCard = new QAction(QString(), this); + aAddCard->setIcon(QPixmap("theme:icons/arrow_right_green")); + connect(aAddCard, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::actAddCardToMainDeck); + auto *tbAddCard = new QToolButton(this); + tbAddCard->setDefaultAction(aAddCard); + + aAddCardToSideboard = new QAction(QString(), this); + aAddCardToSideboard->setIcon(QPixmap("theme:icons/arrow_right_blue")); + connect(aAddCardToSideboard, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::actAddCardToSideboard); + auto *tbAddCardToSideboard = new QToolButton(this); + tbAddCardToSideboard->setDefaultAction(aAddCardToSideboard); + + searchLayout = new QHBoxLayout; + searchLayout->setObjectName("searchLayout"); + searchLayout->addWidget(searchEdit); + searchLayout->addWidget(tbAddCard); + searchLayout->addWidget(tbAddCardToSideboard); + + centralFrame->addLayout(searchLayout); + centralFrame->addWidget(databaseView); + + retranslateUi(); +} + +void DeckEditorDatabaseDisplayWidget::updateSearch(const QString &search) +{ + databaseDisplayModel->setStringFilter(search); + QModelIndexList sel = databaseView->selectionModel()->selectedRows(); + if (sel.isEmpty() && databaseDisplayModel->rowCount()) + databaseView->selectionModel()->setCurrentIndex(databaseDisplayModel->index(0, 0), + QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); +} + +void DeckEditorDatabaseDisplayWidget::clearAllDatabaseFilters() +{ + databaseDisplayModel->clearFilterAll(); + searchEdit->setText(""); +} + +void DeckEditorDatabaseDisplayWidget::updateCard(const QModelIndex ¤t, const QModelIndex & /*previous*/) +{ + const QString cardName = current.sibling(current.row(), 0).data().toString(); + const QString cardProviderID = CardDatabaseManager::getInstance()->getPreferredPrintingProviderIdForCard(cardName); + + if (!current.isValid()) { + return; + } + + if (!current.model()->hasChildren(current.sibling(current.row(), 0))) { + CardInfoPtr card = CardDatabaseManager::getInstance()->getCardByNameAndProviderId(cardName, cardProviderID); + emit cardChanged(card); + } +} + +void DeckEditorDatabaseDisplayWidget::actAddCardToMainDeck() +{ + emit addCardToMainDeck(currentCardInfo()); +} + +void DeckEditorDatabaseDisplayWidget::actAddCardToSideboard() +{ + emit addCardToSideboard(currentCardInfo()); +} + +void DeckEditorDatabaseDisplayWidget::actDecrementCardFromMainDeck() +{ + emit decrementCardFromMainDeck(currentCardInfo()); +} + +void DeckEditorDatabaseDisplayWidget::actDecrementCardFromSideboard() +{ + emit decrementCardFromSideboard(currentCardInfo()); +} + +CardInfoPtr DeckEditorDatabaseDisplayWidget::currentCardInfo() const +{ + const QModelIndex currentIndex = databaseView->selectionModel()->currentIndex(); + if (!currentIndex.isValid()) { + return {}; + } + + const QString cardName = currentIndex.sibling(currentIndex.row(), 0).data().toString(); + + return CardDatabaseManager::getInstance()->getCard(cardName); +} + +void DeckEditorDatabaseDisplayWidget::databaseCustomMenu(QPoint point) +{ + QMenu menu; + const CardInfoPtr info = currentCardInfo(); + + if (info) { + // add to deck and sideboard options + QAction *addToDeck, *addToSideboard, *selectPrinting, *edhRecCommander, *edhRecCard; + addToDeck = menu.addAction(tr("Add to Deck")); + addToSideboard = menu.addAction(tr("Add to Sideboard")); + selectPrinting = menu.addAction(tr("Select Printing")); + if (canBeCommander(info)) { + edhRecCommander = menu.addAction(tr("Show on EDHREC (Commander)")); + connect(edhRecCommander, &QAction::triggered, this, + [this, info] { deckEditor->getTabSupervisor()->addEdhrecTab(info, true); }); + } + edhRecCard = menu.addAction(tr("Show on EDHREC (Card)")); + + connect(addToDeck, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::actAddCardToMainDeck); + connect(addToSideboard, &QAction::triggered, this, &DeckEditorDatabaseDisplayWidget::actAddCardToSideboard); + connect(selectPrinting, &QAction::triggered, this, [this, info] { deckEditor->showPrintingSelector(); }); + connect(edhRecCard, &QAction::triggered, this, + [this, info] { deckEditor->getTabSupervisor()->addEdhrecTab(info); }); + + // filling out the related cards submenu + auto *relatedMenu = new QMenu(tr("Show Related cards")); + menu.addMenu(relatedMenu); + auto relatedCards = info->getAllRelatedCards(); + if (relatedCards.isEmpty()) { + relatedMenu->setDisabled(true); + } else { + for (const CardRelation *rel : relatedCards) { + const QString &relatedCardName = rel->getName(); + QAction *relatedCard = relatedMenu->addAction(relatedCardName); + connect( + relatedCard, &QAction::triggered, deckEditor->cardInfoDockWidget->cardInfo, + [this, relatedCardName] { deckEditor->cardInfoDockWidget->cardInfo->setCard(relatedCardName); }); + } + } + menu.exec(databaseView->mapToGlobal(point)); + } +} + +void DeckEditorDatabaseDisplayWidget::copyDatabaseCellContents() +{ + auto _data = databaseView->selectionModel()->currentIndex().data(); + QApplication::clipboard()->setText(_data.toString()); +} + +void DeckEditorDatabaseDisplayWidget::saveDbHeaderState() +{ + SettingsCache::instance().layouts().setDeckEditorDbHeaderState(databaseView->header()->saveState()); +} + +void DeckEditorDatabaseDisplayWidget::showSearchSyntaxHelp() +{ + + QFile file("theme:help/search.md"); + + if (!file.open(QFile::ReadOnly | QFile::Text)) { + return; + } + + QTextStream in(&file); + QString text = in.readAll(); + file.close(); + + // Poor Markdown Converter + auto opts = QRegularExpression::MultilineOption; + text = text.replace(QRegularExpression("^(###)(.*)", opts), "

\\2

") + .replace(QRegularExpression("^(##)(.*)", opts), "

\\2

") + .replace(QRegularExpression("^(#)(.*)", opts), "

\\2

") + .replace(QRegularExpression("^------*", opts), "
") + .replace(QRegularExpression(R"(\[([^[]+)\]\(([^\)]+)\))", opts), R"(\1)"); + + auto browser = new QTextBrowser; + browser->setParent(this, Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | + Qt::WindowCloseButtonHint | Qt::WindowFullscreenButtonHint); + browser->setWindowTitle("Search Help"); + browser->setReadOnly(true); + browser->setMinimumSize({500, 600}); + + QString sheet = QString("a { text-decoration: underline; color: rgb(71,158,252) };"); + browser->document()->setDefaultStyleSheet(sheet); + + browser->setHtml(text); + connect(browser, &QTextBrowser::anchorClicked, [this](const QUrl &link) { searchEdit->setText(link.fragment()); }); + browser->show(); +} + +void DeckEditorDatabaseDisplayWidget::setFilterTree(FilterTree *filterTree) +{ + databaseDisplayModel->setFilterTree(filterTree); +} + +void DeckEditorDatabaseDisplayWidget::retranslateUi() +{ + aAddCard->setText(tr("Add card to &maindeck")); + aAddCardToSideboard->setText(tr("Add card to &sideboard")); +} \ No newline at end of file diff --git a/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_database_display_widget.h b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_database_display_widget.h new file mode 100644 index 000000000..9022a2cb7 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_database_display_widget.h @@ -0,0 +1,58 @@ +#ifndef DECK_EDITOR_DATABASE_DISPLAY_WIDGET_H +#define DECK_EDITOR_DATABASE_DISPLAY_WIDGET_H + +#include "../../../../deck/custom_line_edit.h" +#include "../../../../game/cards/card_database_model.h" +#include "../../../game_logic/key_signals.h" +#include "../../../tabs/abstract_tab_deck_editor.h" + +#include +#include + +class AbstractTabDeckEditor; +class DeckEditorDatabaseDisplayWidget : public QWidget +{ + + Q_OBJECT +public: + explicit DeckEditorDatabaseDisplayWidget(AbstractTabDeckEditor *parent); + AbstractTabDeckEditor *deckEditor; + SearchLineEdit *searchEdit; + CardDatabaseModel *databaseModel; + CardDatabaseDisplayModel *databaseDisplayModel; + +public slots: + CardInfoPtr currentCardInfo() const; + void setFilterTree(FilterTree *filterTree); + void clearAllDatabaseFilters(); + +signals: + void addCardToMainDeck(CardInfoPtr card); + void addCardToSideboard(CardInfoPtr card); + void decrementCardFromMainDeck(CardInfoPtr card); + void decrementCardFromSideboard(CardInfoPtr card); + void cardChanged(CardInfoPtr _card); + +private: + KeySignals searchKeySignals; + QTreeView *databaseView; + QHBoxLayout *searchLayout; + QAction *aAddCard, *aAddCardToSideboard; + QVBoxLayout *centralFrame; + QWidget *centralWidget; + +private slots: + void showSearchSyntaxHelp(); + void retranslateUi(); + void updateSearch(const QString &search); + void updateCard(const QModelIndex ¤t, const QModelIndex &); + void actAddCardToMainDeck(); + void actAddCardToSideboard(); + void actDecrementCardFromMainDeck(); + void actDecrementCardFromSideboard(); + void databaseCustomMenu(QPoint point); + void copyDatabaseCellContents(); + void saveDbHeaderState(); +}; + +#endif // DECK_EDITOR_DATABASE_DISPLAY_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.cpp b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.cpp new file mode 100644 index 000000000..6f3c9e513 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.cpp @@ -0,0 +1,522 @@ +#include "deck_editor_deck_dock_widget.h" + +#include "../../../../game/cards/card_database_manager.h" +#include "../../../../settings/cache_settings.h" + +#include +#include +#include +#include +#include +#include +#include + +DeckEditorDeckDockWidget::DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent) + : QDockWidget(parent), deckEditor(parent) +{ + setObjectName("deckDock"); + + setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); + setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); + + installEventFilter(deckEditor); + connect(this, SIGNAL(topLevelChanged(bool)), deckEditor, SLOT(dockTopLevelChanged(bool))); + createDeckDock(); +} + +void DeckEditorDeckDockWidget::createDeckDock() +{ + deckModel = new DeckListModel(this); + deckModel->setObjectName("deckModel"); + connect(deckModel, SIGNAL(deckHashChanged()), this, SLOT(updateHash())); + deckView = new QTreeView(); + deckView->setObjectName("deckView"); + deckView->setModel(deckModel); + deckView->setUniformRowHeights(true); + deckView->setSortingEnabled(true); + deckView->sortByColumn(1, Qt::AscendingOrder); + deckView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + deckView->installEventFilter(&deckViewKeySignals); + deckView->setContextMenuPolicy(Qt::CustomContextMenu); + deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); + connect(deckView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, + &DeckEditorDeckDockWidget::updateCard); + connect(deckView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(actSwapCard())); + connect(deckView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(decklistCustomMenu(QPoint))); + connect(&deckViewKeySignals, SIGNAL(onShiftS()), this, SLOT(actSwapCard())); + connect(&deckViewKeySignals, SIGNAL(onEnter()), this, SLOT(actIncrement())); + connect(&deckViewKeySignals, SIGNAL(onCtrlAltEqual()), this, SLOT(actIncrement())); + connect(&deckViewKeySignals, SIGNAL(onCtrlAltMinus()), this, SLOT(actDecrementSelection())); + connect(&deckViewKeySignals, SIGNAL(onShiftRight()), this, SLOT(actIncrement())); + connect(&deckViewKeySignals, SIGNAL(onShiftLeft()), this, SLOT(actDecrementSelection())); + connect(&deckViewKeySignals, SIGNAL(onDelete()), this, SLOT(actRemoveCard())); + + nameLabel = new QLabel(); + nameLabel->setObjectName("nameLabel"); + nameEdit = new LineEditUnfocusable; + nameEdit->setMaxLength(MAX_NAME_LENGTH); + nameEdit->setObjectName("nameEdit"); + nameLabel->setBuddy(nameEdit); + connect(nameEdit, SIGNAL(textChanged(const QString &)), this, SLOT(updateName(const QString &))); + commentsLabel = new QLabel(); + commentsLabel->setObjectName("commentsLabel"); + commentsEdit = new QTextEdit; + commentsEdit->setAcceptRichText(false); + commentsEdit->setMinimumHeight(nameEdit->minimumSizeHint().height()); + commentsEdit->setObjectName("commentsEdit"); + commentsLabel->setBuddy(commentsEdit); + connect(commentsEdit, SIGNAL(textChanged()), this, SLOT(updateComments())); + bannerCardLabel = new QLabel(); + bannerCardLabel->setObjectName("bannerCardLabel"); + bannerCardLabel->setText(tr("Banner Card")); + bannerCardComboBox = new QComboBox(this); + connect(deckModel, &DeckListModel::dataChanged, this, [this]() { + // Delay the update to avoid race conditions + QTimer::singleShot(100, this, &DeckEditorDeckDockWidget::updateBannerCardComboBox); + }); + connect(bannerCardComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, + &DeckEditorDeckDockWidget::setBannerCard); + + deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckModel->getDeckList()); + + aIncrement = new QAction(QString(), this); + aIncrement->setIcon(QPixmap("theme:icons/increment")); + connect(aIncrement, SIGNAL(triggered()), this, SLOT(actIncrement())); + auto *tbIncrement = new QToolButton(this); + tbIncrement->setDefaultAction(aIncrement); + + aDecrement = new QAction(QString(), this); + aDecrement->setIcon(QPixmap("theme:icons/decrement")); + connect(aDecrement, SIGNAL(triggered()), this, SLOT(actDecrementSelection())); + auto *tbDecrement = new QToolButton(this); + tbDecrement->setDefaultAction(aDecrement); + + aRemoveCard = new QAction(QString(), this); + aRemoveCard->setIcon(QPixmap("theme:icons/remove_row")); + connect(aRemoveCard, SIGNAL(triggered()), this, SLOT(actRemoveCard())); + auto *tbRemoveCard = new QToolButton(this); + tbRemoveCard->setDefaultAction(aRemoveCard); + + aSwapCard = new QAction(QString(), this); + aSwapCard->setIcon(QPixmap("theme:icons/swap")); + connect(aSwapCard, SIGNAL(triggered()), this, SLOT(actSwapCard())); + auto *tbSwapCard = new QToolButton(this); + tbSwapCard->setDefaultAction(aSwapCard); + + auto *upperLayout = new QGridLayout; + upperLayout->setObjectName("upperLayout"); + upperLayout->addWidget(nameLabel, 0, 0); + upperLayout->addWidget(nameEdit, 0, 1); + + upperLayout->addWidget(commentsLabel, 1, 0); + upperLayout->addWidget(commentsEdit, 1, 1); + + upperLayout->addWidget(bannerCardLabel, 2, 0); + upperLayout->addWidget(bannerCardComboBox, 2, 1); + + upperLayout->addWidget(deckTagsDisplayWidget, 3, 1); + + hashLabel1 = new QLabel(); + hashLabel1->setObjectName("hashLabel1"); + auto *hashSizePolicy = new QSizePolicy(); + hashSizePolicy->setHorizontalPolicy(QSizePolicy::Fixed); + hashLabel1->setSizePolicy(*hashSizePolicy); + hashLabel = new LineEditUnfocusable; + hashLabel->setObjectName("hashLabel"); + hashLabel->setReadOnly(true); + hashLabel->setFrame(false); + + auto *lowerLayout = new QGridLayout; + lowerLayout->setObjectName("lowerLayout"); + lowerLayout->addWidget(hashLabel1, 0, 0); + lowerLayout->addWidget(hashLabel, 0, 1); + lowerLayout->addWidget(tbIncrement, 0, 2); + lowerLayout->addWidget(tbDecrement, 0, 3); + lowerLayout->addWidget(tbRemoveCard, 0, 4); + lowerLayout->addWidget(tbSwapCard, 0, 5); + lowerLayout->addWidget(deckView, 1, 0, 1, 6); + + // Create widgets for both layouts to make splitter work correctly + auto *topWidget = new QWidget; + topWidget->setLayout(upperLayout); + auto *bottomWidget = new QWidget; + bottomWidget->setLayout(lowerLayout); + + auto *split = new QSplitter; + split->setObjectName("deckSplitter"); + split->setOrientation(Qt::Vertical); + split->setChildrenCollapsible(true); + split->addWidget(topWidget); + split->addWidget(bottomWidget); + split->setStretchFactor(0, 1); + split->setStretchFactor(1, 4); + + auto *rightFrame = new QVBoxLayout; + rightFrame->setObjectName("rightFrame"); + rightFrame->addWidget(split); + + auto *deckDockContents = new QWidget(); + deckDockContents->setObjectName("deckDockContents"); + deckDockContents->setLayout(rightFrame); + setWidget(deckDockContents); + + refreshShortcuts(); + retranslateUi(); +} + +CardInfoPtr DeckEditorDeckDockWidget::getCurrentCard() +{ + QModelIndex current = deckView->selectionModel()->currentIndex(); + if (!current.isValid()) + return {}; + const QString cardName = current.sibling(current.row(), 1).data().toString(); + const QString cardProviderID = current.sibling(current.row(), 4).data().toString(); + const QModelIndex gparent = current.parent().parent(); + + if (!gparent.isValid()) { + return {}; + } + + const QString zoneName = gparent.sibling(gparent.row(), 1).data(Qt::EditRole).toString(); + + if (!current.model()->hasChildren(current.sibling(current.row(), 0))) { + QString cardName = current.sibling(current.row(), 1).data().toString(); + QString providerId = current.sibling(current.row(), 4).data().toString(); + if (CardInfoPtr selectedCard = + CardDatabaseManager::getInstance()->getCardByNameAndProviderId(cardName, providerId)) { + return selectedCard; + } + } + return {}; +} + +void DeckEditorDeckDockWidget::updateCard(const QModelIndex /*¤t*/, const QModelIndex & /*previous*/) +{ + if (CardInfoPtr card = getCurrentCard()) { + emit cardChanged(card); + } +} + +void DeckEditorDeckDockWidget::updateName(const QString &name) +{ + deckModel->getDeckList()->setName(name); + deckEditor->setModified(true); + deckEditor->deckMenu->setSaveStatus(true); +} + +void DeckEditorDeckDockWidget::updateComments() +{ + deckModel->getDeckList()->setComments(commentsEdit->toPlainText()); + deckEditor->setModified(true); + deckEditor->deckMenu->setSaveStatus(true); +} + +void DeckEditorDeckDockWidget::updateHash() +{ + hashLabel->setText(deckModel->getDeckList()->getDeckHash()); +} + +void DeckEditorDeckDockWidget::updateBannerCardComboBox() +{ + // Store the current text of the combo box + QString currentText = bannerCardComboBox->currentText(); + + // Block signals temporarily + bool wasBlocked = bannerCardComboBox->blockSignals(true); + + // Clear the existing items in the combo box + bannerCardComboBox->clear(); + + // Prepare the new items with deduplication + QSet> bannerCardSet; + InnerDecklistNode *listRoot = deckModel->getDeckList()->getRoot(); + for (int i = 0; i < listRoot->size(); i++) { + InnerDecklistNode *currentZone = dynamic_cast(listRoot->at(i)); + for (int j = 0; j < currentZone->size(); j++) { + DecklistCardNode *currentCard = dynamic_cast(currentZone->at(j)); + if (!currentCard) + continue; + + for (int k = 0; k < currentCard->getNumber(); ++k) { + CardInfoPtr info = CardDatabaseManager::getInstance()->getCardByNameAndProviderId( + currentCard->getName(), currentCard->getCardProviderId()); + if (info) { + bannerCardSet.insert( + QPair(currentCard->getName(), currentCard->getCardProviderId())); + } + } + } + } + + QList> pairList = bannerCardSet.values(); + + // Sort QList by the first() element of the QPair + std::sort(pairList.begin(), pairList.end(), [](const QPair &a, const QPair &b) { + return a.first.toLower() < b.first.toLower(); + }); + + for (const auto &pair : pairList) { + QVariantMap dataMap; + dataMap["name"] = pair.first; + dataMap["uuid"] = pair.second; + + bannerCardComboBox->addItem(pair.first, dataMap); + } + + // Try to restore the previous selection by finding the currentText + int restoredIndex = bannerCardComboBox->findText(currentText); + if (restoredIndex != -1) { + bannerCardComboBox->setCurrentIndex(restoredIndex); + } else { + // Add a placeholder "-" and set it as the current selection + int bannerIndex = bannerCardComboBox->findText(deckModel->getDeckList()->getBannerCard().first); + if (bannerIndex != -1) { + bannerCardComboBox->setCurrentIndex(bannerIndex); + } else { + bannerCardComboBox->insertItem(0, "-"); + bannerCardComboBox->setCurrentIndex(0); + } + } + + // Restore the previous signal blocking state + bannerCardComboBox->blockSignals(wasBlocked); +} + +void DeckEditorDeckDockWidget::setBannerCard(int /* changedIndex */) +{ + QVariantMap itemData = bannerCardComboBox->itemData(bannerCardComboBox->currentIndex()).toMap(); + deckModel->getDeckList()->setBannerCard( + QPair(itemData["name"].toString(), itemData["uuid"].toString())); +} + +void DeckEditorDeckDockWidget::setDeck(DeckLoader *_deck) +{ + deckModel->setDeckList(_deck); + + nameEdit->setText(deckModel->getDeckList()->getName()); + commentsEdit->setText(deckModel->getDeckList()->getComments()); + bannerCardComboBox->setCurrentText(deckModel->getDeckList()->getBannerCard().first); + updateBannerCardComboBox(); + updateHash(); + deckModel->sort(deckView->header()->sortIndicatorSection(), deckView->header()->sortIndicatorOrder()); + deckView->expandAll(); + deckView->expandAll(); + + deckTagsDisplayWidget->connectDeckList(deckModel->getDeckList()); +} + +DeckLoader *DeckEditorDeckDockWidget::getDeckList() +{ + return deckModel->getDeckList(); +} + +void DeckEditorDeckDockWidget::cleanDeck() +{ + deckModel->cleanList(); + nameEdit->setText(QString()); + commentsEdit->setText(QString()); + hashLabel->setText(QString()); +} + +void DeckEditorDeckDockWidget::recursiveExpand(const QModelIndex &index) +{ + if (index.parent().isValid()) + recursiveExpand(index.parent()); + deckView->expand(index); +} + +/** + * Gets the index of all the currently selected card nodes in the decklist table. + * The list is in reverse order of the visual selection, so that rows can be deleted while iterating over them. + * + * @return A model index list containing all selected card nodes + */ +QModelIndexList DeckEditorDeckDockWidget::getSelectedCardNodes() const +{ + auto selectedRows = deckView->selectionModel()->selectedRows(); + + const auto notLeafNode = [this](const auto &index) { return deckModel->hasChildren(index); }; + selectedRows.erase(std::remove_if(selectedRows.begin(), selectedRows.end(), notLeafNode), selectedRows.end()); + + std::reverse(selectedRows.begin(), selectedRows.end()); + return selectedRows; +} + +void DeckEditorDeckDockWidget::actIncrement() +{ + auto selectedRows = getSelectedCardNodes(); + + for (const auto &index : selectedRows) { + offsetCountAtIndex(index, 1); + } +} + +void DeckEditorDeckDockWidget::actSwapCard() +{ + auto selectedRows = getSelectedCardNodes(); + + // hack to maintain the old reselection behavior when currently selected row of a single-selection gets deleted + // TODO: remove the hack and also handle reselection when all rows of a multi-selection gets deleted + if (selectedRows.length() == 1) { + deckView->setSelectionMode(QAbstractItemView::SingleSelection); + } + + bool isModified = false; + for (const auto ¤tIndex : selectedRows) { + if (swapCard(currentIndex)) { + isModified = true; + } + } + + deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); + + if (isModified) { + emit deckChanged(); + } + + update(); +} + +/** + * Swaps the card at the index between the maindeck and sideboard + * + * @param currentIndex The index to swap. + * @return True if the swap was successful + */ +bool DeckEditorDeckDockWidget::swapCard(const QModelIndex ¤tIndex) +{ + if (!currentIndex.isValid()) + return false; + const QString cardName = currentIndex.sibling(currentIndex.row(), 1).data().toString(); + const QString cardProviderID = currentIndex.sibling(currentIndex.row(), 4).data().toString(); + const QModelIndex gparent = currentIndex.parent().parent(); + + if (!gparent.isValid()) + return false; + + const QString zoneName = gparent.sibling(gparent.row(), 1).data(Qt::EditRole).toString(); + offsetCountAtIndex(currentIndex, -1); + const QString otherZoneName = zoneName == DECK_ZONE_MAIN ? DECK_ZONE_SIDE : DECK_ZONE_MAIN; + + // Third argument (true) says create the card no matter what, even if not in DB + QModelIndex newCardIndex = deckModel->addCard( + cardName, CardDatabaseManager::getInstance()->getSpecificSetForCard(cardName, cardProviderID), otherZoneName, + true); + recursiveExpand(newCardIndex); + + return true; +} + +void DeckEditorDeckDockWidget::actDecrementCard(CardInfoPtr info, QString zoneName) +{ + if (!info) + return; + if (info->getIsToken()) + zoneName = DECK_ZONE_TOKENS; + + QString providerId = CardDatabaseManager::getInstance()->getSetInfoForCard(info).getProperty("uuid"); + QString collectorNumber = CardDatabaseManager::getInstance()->getSetInfoForCard(info).getProperty("num"); + + QModelIndex idx = deckModel->findCard(info->getName(), zoneName, providerId, collectorNumber); + if (!idx.isValid()) { + return; + } + + deckView->clearSelection(); + deckView->setCurrentIndex(idx); + offsetCountAtIndex(idx, -1); +} + +void DeckEditorDeckDockWidget::actDecrementSelection() +{ + auto selectedRows = getSelectedCardNodes(); + + // hack to maintain the old reselection behavior when currently selected row of a single-selection gets deleted + // TODO: remove the hack and also handle reselection when all rows of a multi-selection gets deleted + if (selectedRows.length() == 1) { + deckView->setSelectionMode(QAbstractItemView::SingleSelection); + } + + for (const auto &index : selectedRows) { + offsetCountAtIndex(index, -1); + } + + deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); +} + +void DeckEditorDeckDockWidget::actRemoveCard() +{ + auto selectedRows = getSelectedCardNodes(); + + // hack to maintain the old reselection behavior when currently selected row of a single-selection gets deleted + // TODO: remove the hack and also handle reselection when all rows of a multi-selection gets deleted + if (selectedRows.length() == 1) { + deckView->setSelectionMode(QAbstractItemView::SingleSelection); + } + + bool isModified = false; + for (const auto &index : selectedRows) { + if (!index.isValid() || deckModel->hasChildren(index)) { + continue; + } + deckModel->removeRow(index.row(), index.parent()); + isModified = true; + } + + deckView->setSelectionMode(QAbstractItemView::ExtendedSelection); + + if (isModified) { + emit deckChanged(); + } +} + +void DeckEditorDeckDockWidget::offsetCountAtIndex(const QModelIndex &idx, int offset) +{ + if (!idx.isValid() || deckModel->hasChildren(idx)) { + return; + } + + const QModelIndex numberIndex = idx.sibling(idx.row(), 0); + const int count = deckModel->data(numberIndex, Qt::EditRole).toInt(); + const int new_count = count + offset; + if (new_count <= 0) + deckModel->removeRow(idx.row(), idx.parent()); + else + deckModel->setData(numberIndex, new_count, Qt::EditRole); + + emit deckChanged(); +} + +void DeckEditorDeckDockWidget::decklistCustomMenu(QPoint point) +{ + QMenu menu; + const CardInfoPtr info = getCurrentCard(); + + QAction *selectPrinting = menu.addAction(tr("Select Printing")); + + connect(selectPrinting, &QAction::triggered, deckEditor, &AbstractTabDeckEditor::showPrintingSelector); + + menu.exec(deckView->mapToGlobal(point)); +} + +void DeckEditorDeckDockWidget::refreshShortcuts() +{ + ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts(); + aRemoveCard->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aRemoveCard")); + aIncrement->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aIncrement")); + aDecrement->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aDecrement")); +} + +void DeckEditorDeckDockWidget::retranslateUi() +{ + setWindowTitle(tr("Deck")); + + nameLabel->setText(tr("Deck &name:")); + commentsLabel->setText(tr("&Comments:")); + hashLabel1->setText(tr("Hash:")); + + aIncrement->setText(tr("&Increment number")); + aDecrement->setText(tr("&Decrement number")); + aRemoveCard->setText(tr("&Remove row")); + aSwapCard->setText(tr("Swap card to/from sideboard")); +} \ No newline at end of file diff --git a/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.h b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.h new file mode 100644 index 000000000..0500b5fca --- /dev/null +++ b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_deck_dock_widget.h @@ -0,0 +1,82 @@ +#ifndef DECK_EDITOR_DECK_DOCK_WIDGET_H +#define DECK_EDITOR_DECK_DOCK_WIDGET_H + +#include "../../../../deck/custom_line_edit.h" +#include "../../../../game/cards/card_database.h" +#include "../../../game_logic/key_signals.h" +#include "../../../tabs/abstract_tab_deck_editor.h" +#include "../visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h" + +#include +#include +#include +#include +#include + +class DeckListModel; +class AbstractTabDeckEditor; +class DeckEditorDeckDockWidget : public QDockWidget +{ + Q_OBJECT +public: + explicit DeckEditorDeckDockWidget(AbstractTabDeckEditor *parent); + DeckListModel *deckModel; + QTreeView *deckView; + QComboBox *bannerCardComboBox; + void createDeckDock(); + CardInfoPtr getCurrentCard(); + void retranslateUi(); + QString getDeckName() + { + return nameEdit->text(); + } + QString getSimpleDeckName() + { + return nameEdit->text().simplified(); + } + +public slots: + void cleanDeck(); + void updateBannerCardComboBox(); + void setDeck(DeckLoader *_deck); + DeckLoader *getDeckList(); + void actIncrement(); + bool swapCard(const QModelIndex &idx); + void actDecrementCard(CardInfoPtr info, QString zoneName); + void actDecrementSelection(); + void actSwapCard(); + void actRemoveCard(); + void offsetCountAtIndex(const QModelIndex &idx, int offset); + +signals: + void deckChanged(); + void cardChanged(CardInfoPtr _card); + +private: + AbstractTabDeckEditor *deckEditor; + KeySignals deckViewKeySignals; + QLabel *nameLabel; + LineEditUnfocusable *nameEdit; + QLabel *commentsLabel; + QTextEdit *commentsEdit; + QLabel *bannerCardLabel; + DeckPreviewDeckTagsDisplayWidget *deckTagsDisplayWidget; + QLabel *hashLabel1; + LineEditUnfocusable *hashLabel; + + QAction *aRemoveCard, *aIncrement, *aDecrement, *aSwapCard; + + void recursiveExpand(const QModelIndex &index); + QModelIndexList getSelectedCardNodes() const; + +private slots: + void decklistCustomMenu(QPoint point); + void updateCard(QModelIndex, const QModelIndex ¤t); + void updateName(const QString &name); + void updateComments(); + void setBannerCard(int); + void updateHash(); + void refreshShortcuts(); +}; + +#endif // DECK_EDITOR_DECK_DOCK_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_filter_dock_widget.cpp b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_filter_dock_widget.cpp new file mode 100644 index 000000000..2f03ad656 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_filter_dock_widget.cpp @@ -0,0 +1,143 @@ +#include "deck_editor_filter_dock_widget.h" + +#include "../../../../game/cards/card_database_model.h" +#include "../../../../game/filters/filter_builder.h" +#include "../../../../game/filters/filter_tree_model.h" +#include "../../../../settings/cache_settings.h" + +#include +#include +#include + +DeckEditorFilterDockWidget::DeckEditorFilterDockWidget(AbstractTabDeckEditor *parent) + : QDockWidget(parent), deckEditor(parent) +{ + setObjectName("filterDock"); + + setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); + + createFiltersDock(); + retranslateUi(); +} + +void DeckEditorFilterDockWidget::createFiltersDock() +{ + filterModel = new FilterTreeModel(); + filterModel->setObjectName("filterModel"); + deckEditor->filterTreeChanged(filterModel->filterTree()); + filterView = new QTreeView; + filterView->setObjectName("filterView"); + filterView->setModel(filterModel); + filterView->setUniformRowHeights(true); + filterView->setHeaderHidden(true); + filterView->setContextMenuPolicy(Qt::CustomContextMenu); + filterView->installEventFilter(&filterViewKeySignals); + connect(filterModel, SIGNAL(layoutChanged()), filterView, SLOT(expandAll())); + connect(filterView, SIGNAL(customContextMenuRequested(const QPoint &)), this, + SLOT(filterViewCustomContextMenu(const QPoint &))); + connect(&filterViewKeySignals, SIGNAL(onDelete()), this, SLOT(actClearFilterOne())); + + auto *filterBuilder = new FilterBuilder; + filterBuilder->setObjectName("filterBuilder"); + connect(filterBuilder, SIGNAL(add(const CardFilter *)), filterModel, SLOT(addFilter(const CardFilter *))); + + aClearFilterOne = new QAction(QString(), this); + aClearFilterOne->setIcon(QPixmap("theme:icons/decrement")); + connect(aClearFilterOne, SIGNAL(triggered()), this, SLOT(actClearFilterOne())); + + aClearFilterAll = new QAction(QString(), this); + aClearFilterAll->setIcon(QPixmap("theme:icons/clearsearch")); + connect(aClearFilterAll, SIGNAL(triggered()), this, SLOT(actClearFilterAll())); + + auto *filterDelOne = new QToolButton(); + filterDelOne->setObjectName("filterDelOne"); + filterDelOne->setDefaultAction(aClearFilterOne); + filterDelOne->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + + auto *filterDelAll = new QToolButton(); + filterDelAll->setObjectName("filterDelAll"); + filterDelAll->setDefaultAction(aClearFilterAll); + filterDelAll->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + + auto *filterLayout = new QGridLayout; + filterLayout->setObjectName("filterLayout"); + filterLayout->setContentsMargins(0, 0, 0, 0); + filterLayout->addWidget(filterBuilder, 0, 0, 1, 3); + filterLayout->addWidget(filterView, 1, 0, 1, 3); + filterLayout->addWidget(filterDelOne, 2, 0, 1, 1); + filterLayout->addWidget(filterDelAll, 2, 2, 1, 1); + + filterBox = new QWidget(); + filterBox->setObjectName("filterBox"); + filterBox->setLayout(filterLayout); + + auto *filterFrame = new QVBoxLayout; + filterFrame->setObjectName("filterFrame"); + filterFrame->addWidget(filterBox); + + auto *filterDockContents = new QWidget(this); + filterDockContents->setObjectName("filterDockContents"); + filterDockContents->setLayout(filterFrame); + setWidget(filterDockContents); + + installEventFilter(deckEditor); + connect(this, &QDockWidget::topLevelChanged, deckEditor, &AbstractTabDeckEditor::dockTopLevelChanged); +} + +void DeckEditorFilterDockWidget::filterViewCustomContextMenu(const QPoint &point) +{ + QMenu menu; + QAction *action; + QModelIndex idx; + + idx = filterView->indexAt(point); + if (!idx.isValid()) + return; + + action = menu.addAction(QString("delete")); + action->setData(point); + connect(&menu, SIGNAL(triggered(QAction *)), this, SLOT(filterRemove(QAction *))); + menu.exec(filterView->mapToGlobal(point)); +} + +void DeckEditorFilterDockWidget::filterRemove(QAction *action) +{ + QPoint point; + QModelIndex idx; + + point = action->data().toPoint(); + idx = filterView->indexAt(point); + if (!idx.isValid()) + return; + + filterModel->removeRow(idx.row(), idx.parent()); +} + +void DeckEditorFilterDockWidget::actClearFilterAll() +{ + emit clearAllDatabaseFilters(); +} + +void DeckEditorFilterDockWidget::actClearFilterOne() +{ + QModelIndexList selIndexes = filterView->selectionModel()->selectedIndexes(); + for (QModelIndex idx : selIndexes) { + filterModel->removeRow(idx.row(), idx.parent()); + } +} + +void DeckEditorFilterDockWidget::refreshShortcuts() +{ + ShortcutsSettings &shortcuts = SettingsCache::instance().shortcuts(); + + aClearFilterAll->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aClearFilterAll")); + aClearFilterOne->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aClearFilterOne")); +} + +void DeckEditorFilterDockWidget::retranslateUi() +{ + setWindowTitle(tr("Filters")); + + aClearFilterAll->setText(tr("&Clear all filters")); + aClearFilterOne->setText(tr("Delete selected")); +} \ No newline at end of file diff --git a/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_filter_dock_widget.h b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_filter_dock_widget.h new file mode 100644 index 000000000..3113b2e24 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_filter_dock_widget.h @@ -0,0 +1,40 @@ +#ifndef DECK_EDITOR_FILTER_DOCK_WIDGET_H +#define DECK_EDITOR_FILTER_DOCK_WIDGET_H + +#include "../../../game_logic/key_signals.h" +#include "../../../tabs/abstract_tab_deck_editor.h" + +#include +#include + +class FilterTreeModel; +class AbstractTabDeckEditor; +class DeckEditorFilterDockWidget : public QDockWidget +{ + Q_OBJECT +public: + explicit DeckEditorFilterDockWidget(AbstractTabDeckEditor *parent); + void createFiltersDock(); + void retranslateUi(); + QAction *aClearFilterAll, *aClearFilterOne; + +signals: + void clearAllDatabaseFilters(); + +private: + AbstractTabDeckEditor *deckEditor; + FilterTreeModel *filterModel; + QTreeView *filterView; + KeySignals filterViewKeySignals; + QWidget *filterBox; + + void filterRemove(QAction *action); + +private slots: + void filterViewCustomContextMenu(const QPoint &point); + void actClearFilterAll(); + void actClearFilterOne(); + void refreshShortcuts(); +}; + +#endif // DECK_EDITOR_FILTER_DOCK_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp new file mode 100644 index 000000000..732c74cf2 --- /dev/null +++ b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_printing_selector_dock_widget.cpp @@ -0,0 +1,41 @@ +#include "deck_editor_printing_selector_dock_widget.h" + +#include "../../../tabs/abstract_tab_deck_editor.h" + +#include + +DeckEditorPrintingSelectorDockWidget::DeckEditorPrintingSelectorDockWidget(AbstractTabDeckEditor *parent) + : QDockWidget(parent), deckEditor(parent) +{ + setObjectName("printingSelectorDock"); + + setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); + setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); + setFloating(false); + + createPrintingSelectorDock(); + + retranslateUi(); +} + +void DeckEditorPrintingSelectorDockWidget::createPrintingSelectorDock() +{ + printingSelector = new PrintingSelector(this, deckEditor); + printingSelector->setObjectName("printingSelector"); + auto *printingSelectorFrame = new QVBoxLayout; + printingSelectorFrame->setObjectName("printingSelectorFrame"); + printingSelectorFrame->addWidget(printingSelector); + + auto *printingSelectorDockContents = new QWidget(); + printingSelectorDockContents->setObjectName("printingSelectorDockContents"); + printingSelectorDockContents->setLayout(printingSelectorFrame); + setWidget(printingSelectorDockContents); + + installEventFilter(deckEditor); + connect(this, &QDockWidget::topLevelChanged, deckEditor, &AbstractTabDeckEditor::dockTopLevelChanged); +} + +void DeckEditorPrintingSelectorDockWidget::retranslateUi() +{ + setWindowTitle(tr("Printing Selector")); +} diff --git a/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_printing_selector_dock_widget.h b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_printing_selector_dock_widget.h new file mode 100644 index 000000000..ffc4297cb --- /dev/null +++ b/cockatrice/src/client/ui/widgets/deck_editor/deck_editor_printing_selector_dock_widget.h @@ -0,0 +1,23 @@ +#ifndef DECK_EDITOR_PRINTING_SELECTOR_DOCK_WIDGET_H +#define DECK_EDITOR_PRINTING_SELECTOR_DOCK_WIDGET_H + +#include "../../../tabs/abstract_tab_deck_editor.h" +#include "../printing_selector/printing_selector.h" + +#include + +class TabDeckEditor; +class DeckEditorPrintingSelectorDockWidget : public QDockWidget +{ + Q_OBJECT +public: + explicit DeckEditorPrintingSelectorDockWidget(AbstractTabDeckEditor *parent); + void createPrintingSelectorDock(); + void retranslateUi(); + PrintingSelector *printingSelector; + +private: + AbstractTabDeckEditor *deckEditor; +}; + +#endif // DECK_EDITOR_PRINTING_SELECTOR_DOCK_WIDGET_H diff --git a/cockatrice/src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.cpp b/cockatrice/src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.cpp index 309c21c17..77c7dc6cd 100644 --- a/cockatrice/src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.cpp +++ b/cockatrice/src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.cpp @@ -19,7 +19,7 @@ * @param setInfoForCard The set information for the card. */ AllZonesCardAmountWidget::AllZonesCardAmountWidget(QWidget *parent, - TabDeckEditor *deckEditor, + AbstractTabDeckEditor *deckEditor, DeckListModel *deckModel, QTreeView *deckView, QSlider *cardSizeSlider, diff --git a/cockatrice/src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.h b/cockatrice/src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.h index c08d6760e..94399b1d6 100644 --- a/cockatrice/src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.h +++ b/cockatrice/src/client/ui/widgets/printing_selector/all_zones_card_amount_widget.h @@ -12,7 +12,7 @@ class AllZonesCardAmountWidget : public QWidget Q_OBJECT public: explicit AllZonesCardAmountWidget(QWidget *parent, - TabDeckEditor *deckEditor, + AbstractTabDeckEditor *deckEditor, DeckListModel *deckModel, QTreeView *deckView, QSlider *cardSizeSlider, @@ -31,7 +31,7 @@ public slots: private: QVBoxLayout *layout; - TabDeckEditor *deckEditor; + AbstractTabDeckEditor *deckEditor; DeckListModel *deckModel; QTreeView *deckView; QSlider *cardSizeSlider; diff --git a/cockatrice/src/client/ui/widgets/printing_selector/card_amount_widget.cpp b/cockatrice/src/client/ui/widgets/printing_selector/card_amount_widget.cpp index 6b4b54e95..9a295c207 100644 --- a/cockatrice/src/client/ui/widgets/printing_selector/card_amount_widget.cpp +++ b/cockatrice/src/client/ui/widgets/printing_selector/card_amount_widget.cpp @@ -16,7 +16,7 @@ * @param zoneName The zone name (e.g., DECK_ZONE_MAIN or DECK_ZONE_SIDE). */ CardAmountWidget::CardAmountWidget(QWidget *parent, - TabDeckEditor *deckEditor, + AbstractTabDeckEditor *deckEditor, DeckListModel *deckModel, QTreeView *deckView, QSlider *cardSizeSlider, diff --git a/cockatrice/src/client/ui/widgets/printing_selector/card_amount_widget.h b/cockatrice/src/client/ui/widgets/printing_selector/card_amount_widget.h index 848ed0d5d..a051d2ea2 100644 --- a/cockatrice/src/client/ui/widgets/printing_selector/card_amount_widget.h +++ b/cockatrice/src/client/ui/widgets/printing_selector/card_amount_widget.h @@ -4,7 +4,7 @@ #include "../../../../deck/deck_list_model.h" #include "../../../../deck/deck_loader.h" #include "../../../../game/cards/card_database.h" -#include "../../../tabs/tab_deck_editor.h" +#include "../../../tabs/abstract_tab_deck_editor.h" #include "../general/display/dynamic_font_size_push_button.h" #include @@ -18,7 +18,7 @@ class CardAmountWidget : public QWidget Q_OBJECT public: explicit CardAmountWidget(QWidget *parent, - TabDeckEditor *deckEditor, + AbstractTabDeckEditor *deckEditor, DeckListModel *deckModel, QTreeView *deckView, QSlider *cardSizeSlider, @@ -36,7 +36,7 @@ protected: void showEvent(QShowEvent *event) override; private: - TabDeckEditor *deckEditor; + AbstractTabDeckEditor *deckEditor; DeckListModel *deckModel; QTreeView *deckView; QSlider *cardSizeSlider; diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.cpp b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.cpp index a5407a15d..c0605fc39 100644 --- a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.cpp +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.cpp @@ -21,11 +21,9 @@ * @param deckModel The DeckListModel instance that provides data for the deck's contents. * @param deckView The QTreeView instance used to display the deck and its contents. */ -PrintingSelector::PrintingSelector(QWidget *parent, - TabDeckEditor *deckEditor, - DeckListModel *deckModel, - QTreeView *deckView) - : QWidget(parent), deckEditor(deckEditor), deckModel(deckModel), deckView(deckView) +PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deckEditor) + : QWidget(parent), deckEditor(_deckEditor), deckModel(deckEditor->deckDockWidget->deckModel), + deckView(deckEditor->deckDockWidget->deckView) { setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); layout = new QVBoxLayout(this); diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.h b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.h index ee66f3872..fd360f37a 100644 --- a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.h +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.h @@ -19,13 +19,13 @@ class PrintingSelectorCardSearchWidget; class PrintingSelectorCardSelectionWidget; class PrintingSelectorCardSortingWidget; class PrintingSelectorViewOptionsWidget; -class TabDeckEditor; +class AbstractTabDeckEditor; class PrintingSelector : public QWidget { Q_OBJECT public: - PrintingSelector(QWidget *parent, TabDeckEditor *deckEditor, DeckListModel *deckModel, QTreeView *deckView); + PrintingSelector(QWidget *parent, AbstractTabDeckEditor *deckEditor); void setCard(const CardInfoPtr &newCard, const QString &_currentZone); void getAllSetsForCurrentCard(); @@ -51,7 +51,7 @@ private: FlowWidget *flowWidget; CardSizeWidget *cardSizeWidget; PrintingSelectorCardSelectionWidget *cardSelectionBar; - TabDeckEditor *deckEditor; + AbstractTabDeckEditor *deckEditor; DeckListModel *deckModel; QTreeView *deckView; CardInfoPtr selectedCard; diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.cpp b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.cpp index 6772fe224..cebf645cb 100644 --- a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.cpp +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.cpp @@ -19,16 +19,16 @@ * display. * * @param parent The parent widget for this display. - * @param deckEditor The TabDeckEditor instance for deck management. - * @param deckModel The DeckListModel instance providing deck data. - * @param deckView The QTreeView instance displaying the deck. - * @param cardSizeSlider The slider controlling the size of the displayed card. - * @param rootCard The root card object, representing the card to be displayed. - * @param setInfoForCard The set-specific information for the card being displayed. - * @param currentZone The current zone in which the card is located. + * @param _deckEditor The TabDeckEditor instance for deck management. + * @param _deckModel The DeckListModel instance providing deck data. + * @param _deckView The QTreeView instance displaying the deck. + * @param _cardSizeSlider The slider controlling the size of the displayed card. + * @param _rootCard The root card object, representing the card to be displayed. + * @param _setInfoForCard The set-specific information for the card being displayed. + * @param _currentZone The current zone in which the card is located. */ PrintingSelectorCardDisplayWidget::PrintingSelectorCardDisplayWidget(QWidget *parent, - TabDeckEditor *_deckEditor, + AbstractTabDeckEditor *_deckEditor, DeckListModel *_deckModel, QTreeView *_deckView, QSlider *_cardSizeSlider, diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.h b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.h index 6e8d99d67..32e1e925d 100644 --- a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.h +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_display_widget.h @@ -1,16 +1,13 @@ #ifndef PRINTING_SELECTOR_CARD_DISPLAY_WIDGET_H #define PRINTING_SELECTOR_CARD_DISPLAY_WIDGET_H -#include "../../../../client/ui/widgets/cards/card_info_picture_widget.h" #include "../../../../deck/deck_list_model.h" #include "../../../../game/cards/card_database.h" -#include "../../../tabs/tab_deck_editor.h" +#include "../../../tabs/abstract_tab_deck_editor.h" #include "printing_selector_card_overlay_widget.h" #include "set_name_and_collectors_number_display_widget.h" #include -#include -#include #include class PrintingSelectorCardDisplayWidget : public QWidget @@ -19,7 +16,7 @@ class PrintingSelectorCardDisplayWidget : public QWidget public: PrintingSelectorCardDisplayWidget(QWidget *parent, - TabDeckEditor *_deckEditor, + AbstractTabDeckEditor *_deckEditor, DeckListModel *_deckModel, QTreeView *_deckView, QSlider *_cardSizeSlider, @@ -36,7 +33,7 @@ signals: private: QVBoxLayout *layout; SetNameAndCollectorsNumberDisplayWidget *setNameAndCollectorsNumberDisplayWidget; - TabDeckEditor *deckEditor; + AbstractTabDeckEditor *deckEditor; DeckListModel *deckModel; QTreeView *deckView; QSlider *cardSizeSlider; diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.cpp b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.cpp index f94a76252..5604046cd 100644 --- a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.cpp +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.cpp @@ -17,15 +17,15 @@ * as a widget that displays the card amounts in different zones (mainboard, sideboard, etc.). * * @param parent The parent widget for this overlay. - * @param deckEditor The TabDeckEditor instance for deck management. - * @param deckModel The DeckListModel instance providing deck data. - * @param deckView The QTreeView instance displaying the deck. - * @param cardSizeSlider The slider controlling the size of the card. - * @param rootCard The root card object that contains information about the card. - * @param setInfoForCard The set-specific information for the card being displayed. + * @param _deckEditor The TabDeckEditor instance for deck management. + * @param _deckModel The DeckListModel instance providing deck data. + * @param _deckView The QTreeView instance displaying the deck. + * @param _cardSizeSlider The slider controlling the size of the card. + * @param _rootCard The root card object that contains information about the card. + * @param _setInfoForCard The set-specific information for the card being displayed. */ PrintingSelectorCardOverlayWidget::PrintingSelectorCardOverlayWidget(QWidget *parent, - TabDeckEditor *_deckEditor, + AbstractTabDeckEditor *_deckEditor, DeckListModel *_deckModel, QTreeView *_deckView, QSlider *_cardSizeSlider, @@ -120,7 +120,7 @@ void PrintingSelectorCardOverlayWidget::enterEvent(QEvent *event) #endif { QWidget::enterEvent(event); - deckEditor->updateCardInfo(setCard); + deckEditor->updateCard(setCard); // Check if either mainboard or sideboard amount is greater than 0 if (allZonesCardAmountWidget->getMainboardAmount() > 0 || allZonesCardAmountWidget->getSideboardAmount() > 0) { @@ -199,7 +199,7 @@ void PrintingSelectorCardOverlayWidget::customMenu(QPoint point) const QString &relatedCardName = rel->getName(); QAction *relatedCard = relatedMenu->addAction(relatedCardName); connect(relatedCard, &QAction::triggered, deckEditor, [this, relatedCardName] { - deckEditor->updateCardInfo(CardDatabaseManager::getInstance()->getCard(relatedCardName)); + deckEditor->updateCard(CardDatabaseManager::getInstance()->getCard(relatedCardName)); deckEditor->showPrintingSelector(); }); } diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.h b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.h index 72d861bda..5ab27a274 100644 --- a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.h +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector_card_overlay_widget.h @@ -4,7 +4,7 @@ #include "../../../../client/ui/widgets/cards/card_info_picture_widget.h" #include "../../../../deck/deck_list_model.h" #include "../../../../game/cards/card_database.h" -#include "../../../tabs/tab_deck_editor.h" +#include "../../../tabs/abstract_tab_deck_editor.h" #include "all_zones_card_amount_widget.h" #include "card_amount_widget.h" #include "set_name_and_collectors_number_display_widget.h" @@ -15,7 +15,7 @@ class PrintingSelectorCardOverlayWidget : public QWidget public: explicit PrintingSelectorCardOverlayWidget(QWidget *parent, - TabDeckEditor *_deckEditor, + AbstractTabDeckEditor *_deckEditor, DeckListModel *_deckModel, QTreeView *_deckView, QSlider *_cardSizeSlider, @@ -39,7 +39,7 @@ signals: private: CardInfoPictureWidget *cardInfoPicture; AllZonesCardAmountWidget *allZonesCardAmountWidget; - TabDeckEditor *deckEditor; + AbstractTabDeckEditor *deckEditor; DeckListModel *deckModel; QTreeView *deckView; QSlider *cardSizeSlider; diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h index 344f6119e..cf8917d14 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_deck_tags_display_widget.h @@ -1,7 +1,7 @@ #ifndef DECK_PREVIEW_DECK_TAGS_DISPLAY_WIDGET_H #define DECK_PREVIEW_DECK_TAGS_DISPLAY_WIDGET_H -#include "../../../../tabs/tab_deck_editor.h" +#include "../../../../../deck/deck_loader.h" #include "deck_preview_widget.h" #include diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_addition_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_addition_widget.cpp index fb1f99879..2e575a842 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_addition_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_tag_addition_widget.cpp @@ -1,6 +1,7 @@ #include "deck_preview_tag_addition_widget.h" #include "../../../../../settings/cache_settings.h" +#include "../../../../tabs/abstract_tab_deck_editor.h" #include "deck_preview_tag_dialog.h" #include diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h index b12cfce10..6fd82e3d5 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.h @@ -7,6 +7,8 @@ #include "deck_preview_color_identity_widget.h" #include "deck_preview_deck_tags_display_widget.h" +#include +#include #include #include diff --git a/cockatrice/src/deck/deck_loader.h b/cockatrice/src/deck/deck_loader.h index ceb103009..8a219b0c6 100644 --- a/cockatrice/src/deck/deck_loader.h +++ b/cockatrice/src/deck/deck_loader.h @@ -45,6 +45,11 @@ public: return lastRemoteDeckId; } + bool hasNotBeenLoaded() const + { + return getLastFileName().isEmpty() && getLastRemoteDeckId() == -1; + } + void clearSetNamesAndNumbers(); static FileFormat getFormatFromName(const QString &fileName);