[Doxygen] abstract_tab_deck_editor (#6286)

* Doxygen abstract_tab_deck_editor

Took 15 minutes

Took 15 minutes


Took 4 seconds

* Lint.

Took 28 minutes

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2025-11-08 23:25:33 +01:00 committed by GitHub
parent 757e9f3415
commit 7e6cad974f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 319 additions and 88 deletions

View file

@ -1,3 +1,12 @@
/**
* @file abstract_tab_deck_editor.cpp
* @brief Implementation of the AbstractTabDeckEditor class.
*
* Handles deck editor tab UI, deck management, card operations, clipboard
* operations, printing, deck export, remote uploads, and interactions with
* external services (DeckStats, TappedOut, etc.).
*/
#include "abstract_tab_deck_editor.h" #include "abstract_tab_deck_editor.h"
#include "../../../client/settings/cache_settings.h" #include "../../../client/settings/cache_settings.h"
@ -40,6 +49,11 @@
#include <libcockatrice/protocol/pending_command.h> #include <libcockatrice/protocol/pending_command.h>
#include <libcockatrice/utility/trice_limits.h> #include <libcockatrice/utility/trice_limits.h>
/**
* @brief Constructs the AbstractTabDeckEditor.
* Initializes all dock widgets and connects signals/slots.
* @param _tabSupervisor The tab supervisor managing this tab.
*/
AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor) AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Tab(_tabSupervisor)
{ {
setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks); setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
@ -53,10 +67,13 @@ AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Ta
printingSelectorDockWidget->setHidden(SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference()); printingSelectorDockWidget->setHidden(SettingsCache::instance().getOverrideAllCardArtWithPersonalPreference());
}); });
// Connect deck signals to this tab
connect(deckDockWidget, &DeckEditorDeckDockWidget::deckChanged, this, &AbstractTabDeckEditor::onDeckChanged); connect(deckDockWidget, &DeckEditorDeckDockWidget::deckChanged, this, &AbstractTabDeckEditor::onDeckChanged);
connect(deckDockWidget, &DeckEditorDeckDockWidget::deckModified, this, &AbstractTabDeckEditor::onDeckModified); connect(deckDockWidget, &DeckEditorDeckDockWidget::deckModified, this, &AbstractTabDeckEditor::onDeckModified);
connect(deckDockWidget, &DeckEditorDeckDockWidget::cardChanged, this, &AbstractTabDeckEditor::updateCard); connect(deckDockWidget, &DeckEditorDeckDockWidget::cardChanged, this, &AbstractTabDeckEditor::updateCard);
connect(this, &AbstractTabDeckEditor::decrementCard, deckDockWidget, &DeckEditorDeckDockWidget::actDecrementCard); connect(this, &AbstractTabDeckEditor::decrementCard, deckDockWidget, &DeckEditorDeckDockWidget::actDecrementCard);
// Connect database display signals to this tab
connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::cardChanged, this, connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::cardChanged, this,
&AbstractTabDeckEditor::updateCard); &AbstractTabDeckEditor::updateCard);
connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::addCardToMainDeck, this, connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::addCardToMainDeck, this,
@ -68,69 +85,97 @@ AbstractTabDeckEditor::AbstractTabDeckEditor(TabSupervisor *_tabSupervisor) : Ta
connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::decrementCardFromSideboard, this, connect(databaseDisplayDockWidget, &DeckEditorDatabaseDisplayWidget::decrementCardFromSideboard, this,
&AbstractTabDeckEditor::actDecrementCardFromSideboard); &AbstractTabDeckEditor::actDecrementCardFromSideboard);
// Connect filter signals
connect(filterDockWidget, &DeckEditorFilterDockWidget::clearAllDatabaseFilters, databaseDisplayDockWidget, connect(filterDockWidget, &DeckEditorFilterDockWidget::clearAllDatabaseFilters, databaseDisplayDockWidget,
&DeckEditorDatabaseDisplayWidget::clearAllDatabaseFilters); &DeckEditorDatabaseDisplayWidget::clearAllDatabaseFilters);
// Connect shortcut changes
connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this, connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
&AbstractTabDeckEditor::refreshShortcuts); &AbstractTabDeckEditor::refreshShortcuts);
} }
/**
* @brief Updates the card info dock and printing selector.
* @param card The card to display.
*/
void AbstractTabDeckEditor::updateCard(const ExactCard &card) void AbstractTabDeckEditor::updateCard(const ExactCard &card)
{ {
cardInfoDockWidget->updateCard(card); cardInfoDockWidget->updateCard(card);
printingSelectorDockWidget->printingSelector->setCard(card.getCardPtr(), DECK_ZONE_MAIN); printingSelectorDockWidget->printingSelector->setCard(card.getCardPtr(), DECK_ZONE_MAIN);
} }
/** @brief Placeholder: called when the deck changes. */
void AbstractTabDeckEditor::onDeckChanged() void AbstractTabDeckEditor::onDeckChanged()
{ {
} }
/**
* @brief Marks the tab as modified and updates the save menu status.
*/
void AbstractTabDeckEditor::onDeckModified() void AbstractTabDeckEditor::onDeckModified()
{ {
setModified(!isBlankNewDeck()); setModified(!isBlankNewDeck());
deckMenu->setSaveStatus(!isBlankNewDeck()); deckMenu->setSaveStatus(!isBlankNewDeck());
} }
/**
* @brief Helper for adding a card to a deck zone.
* @param card Card to add.
* @param zoneName Zone to add the card to.
*/
void AbstractTabDeckEditor::addCardHelper(const ExactCard &card, QString zoneName) void AbstractTabDeckEditor::addCardHelper(const ExactCard &card, QString zoneName)
{ {
if (!card) if (!card)
return; return;
if (card.getInfo().getIsToken()) if (card.getInfo().getIsToken())
zoneName = DECK_ZONE_TOKENS; zoneName = DECK_ZONE_TOKENS;
QModelIndex newCardIndex = deckDockWidget->deckModel->addCard(card, zoneName); QModelIndex newCardIndex = deckDockWidget->deckModel->addCard(card, zoneName);
// recursiveExpand(newCardIndex);
deckDockWidget->deckView->clearSelection(); deckDockWidget->deckView->clearSelection();
deckDockWidget->deckView->setCurrentIndex(newCardIndex); deckDockWidget->deckView->setCurrentIndex(newCardIndex);
setModified(true); setModified(true);
databaseDisplayDockWidget->searchEdit->setSelection(0, databaseDisplayDockWidget->searchEdit->text().length()); databaseDisplayDockWidget->searchEdit->setSelection(0, databaseDisplayDockWidget->searchEdit->text().length());
} }
/**
* @brief Adds a card to the main deck or sideboard depending on Ctrl key.
*/
void AbstractTabDeckEditor::actAddCard(const ExactCard &card) void AbstractTabDeckEditor::actAddCard(const ExactCard &card)
{ {
if (QApplication::keyboardModifiers() & Qt::ControlModifier) if (QApplication::keyboardModifiers() & Qt::ControlModifier)
actAddCardToSideboard(card); actAddCardToSideboard(card);
else else
addCardHelper(card, DECK_ZONE_MAIN); addCardHelper(card, DECK_ZONE_MAIN);
deckMenu->setSaveStatus(true); deckMenu->setSaveStatus(true);
} }
/** @brief Adds a card to the sideboard explicitly. */
void AbstractTabDeckEditor::actAddCardToSideboard(const ExactCard &card) void AbstractTabDeckEditor::actAddCardToSideboard(const ExactCard &card)
{ {
addCardHelper(card, DECK_ZONE_SIDE); addCardHelper(card, DECK_ZONE_SIDE);
deckMenu->setSaveStatus(true); deckMenu->setSaveStatus(true);
} }
/** @brief Decrements a card from the main deck. */
void AbstractTabDeckEditor::actDecrementCard(const ExactCard &card) void AbstractTabDeckEditor::actDecrementCard(const ExactCard &card)
{ {
emit decrementCard(card, DECK_ZONE_MAIN); emit decrementCard(card, DECK_ZONE_MAIN);
} }
/** @brief Decrements a card from the sideboard. */
void AbstractTabDeckEditor::actDecrementCardFromSideboard(const ExactCard &card) void AbstractTabDeckEditor::actDecrementCardFromSideboard(const ExactCard &card)
{ {
emit decrementCard(card, DECK_ZONE_SIDE); emit decrementCard(card, DECK_ZONE_SIDE);
} }
/**
* @brief Swaps a card in a deck zone.
* @param card Card to swap.
* @param zoneName Zone to swap in.
*/
void AbstractTabDeckEditor::actSwapCard(const ExactCard &card, const QString &zoneName) void AbstractTabDeckEditor::actSwapCard(const ExactCard &card, const QString &zoneName)
{ {
QString providerId = card.getPrinting().getUuid(); QString providerId = card.getPrinting().getUuid();
@ -145,8 +190,8 @@ void AbstractTabDeckEditor::actSwapCard(const ExactCard &card, const QString &zo
} }
/** /**
* Opens the deck in this tab. * @brief Opens a deck in this tab.
* @param deck The deck. Takes ownership of the object * @param deck DeckLoader object (takes ownership).
*/ */
void AbstractTabDeckEditor::openDeck(DeckLoader *deck) void AbstractTabDeckEditor::openDeck(DeckLoader *deck)
{ {
@ -158,8 +203,8 @@ void AbstractTabDeckEditor::openDeck(DeckLoader *deck)
} }
/** /**
* Sets the currently active deck for this tab * @brief Sets the currently active deck.
* @param _deck The deck. Takes ownership of the object * @param _deck DeckLoader object.
*/ */
void AbstractTabDeckEditor::setDeck(DeckLoader *_deck) void AbstractTabDeckEditor::setDeck(DeckLoader *_deck)
{ {
@ -167,16 +212,20 @@ void AbstractTabDeckEditor::setDeck(DeckLoader *_deck)
CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(getDeckList()->getCardRefList())); CardPictureLoader::cacheCardPixmaps(CardDatabaseManager::query()->getCards(getDeckList()->getCardRefList()));
setModified(false); setModified(false);
// If they load a deck, make the deck list appear
aDeckDockVisible->setChecked(true); aDeckDockVisible->setChecked(true);
deckDockWidget->setVisible(aDeckDockVisible->isChecked()); deckDockWidget->setVisible(aDeckDockVisible->isChecked());
} }
/** @brief Returns the currently loaded deck. */
DeckLoader *AbstractTabDeckEditor::getDeckList() const DeckLoader *AbstractTabDeckEditor::getDeckList() const
{ {
return deckDockWidget->getDeckList(); return deckDockWidget->getDeckList();
} }
/**
* @brief Sets the modified state of the tab.
* @param _modified True if tab is modified, false otherwise.
*/
void AbstractTabDeckEditor::setModified(bool _modified) void AbstractTabDeckEditor::setModified(bool _modified)
{ {
modified = _modified; modified = _modified;
@ -184,7 +233,7 @@ void AbstractTabDeckEditor::setModified(bool _modified)
} }
/** /**
* @brief Returns true if this tab is a blank newly opened tab, as if it was just created with the `New Deck` action. * @brief Returns true if the tab is a blank newly created deck.
*/ */
bool AbstractTabDeckEditor::isBlankNewDeck() const bool AbstractTabDeckEditor::isBlankNewDeck() const
{ {
@ -192,13 +241,12 @@ bool AbstractTabDeckEditor::isBlankNewDeck() const
return !modified && deck->isBlankDeck() && deck->hasNotBeenLoaded(); return !modified && deck->isBlankDeck() && deck->hasNotBeenLoaded();
} }
/** @brief Creates a new deck. Handles opening in new tab if needed. */
void AbstractTabDeckEditor::actNewDeck() void AbstractTabDeckEditor::actNewDeck()
{ {
auto deckOpenLocation = confirmOpen(false); auto deckOpenLocation = confirmOpen(false);
if (deckOpenLocation == CANCELLED)
if (deckOpenLocation == CANCELLED) {
return; return;
}
if (deckOpenLocation == NEW_TAB) { if (deckOpenLocation == NEW_TAB) {
emit openDeckEditor(nullptr); emit openDeckEditor(nullptr);
@ -208,6 +256,7 @@ void AbstractTabDeckEditor::actNewDeck()
cleanDeckAndResetModified(); cleanDeckAndResetModified();
} }
/** @brief Clears the current deck and resets modified flag. */
void AbstractTabDeckEditor::cleanDeckAndResetModified() void AbstractTabDeckEditor::cleanDeckAndResetModified()
{ {
deckMenu->setSaveStatus(false); deckMenu->setSaveStatus(false);
@ -216,43 +265,29 @@ void AbstractTabDeckEditor::cleanDeckAndResetModified()
} }
/** /**
* @brief Displays the save confirmation dialogue that is shown before loading a deck, if required. Takes into * @brief Displays the save confirmation dialog before loading a deck.
* account the `openDeckInNewTab` settting. * @param openInSameTabIfBlank Open in same tab if current tab is blank.
* * @return DeckOpenLocation enum indicating where to open the deck.
* @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) AbstractTabDeckEditor::DeckOpenLocation AbstractTabDeckEditor::confirmOpen(const bool openInSameTabIfBlank)
{ {
// handle `openDeckInNewTab` setting
if (SettingsCache::instance().getOpenDeckInNewTab()) { if (SettingsCache::instance().getOpenDeckInNewTab()) {
if (openInSameTabIfBlank && isBlankNewDeck()) { if (openInSameTabIfBlank && isBlankNewDeck())
return SAME_TAB; return SAME_TAB;
} else { else
return NEW_TAB; return NEW_TAB;
}
} }
// early return if deck is unmodified if (!modified)
if (!modified) {
return SAME_TAB; return SAME_TAB;
}
// do the save confirmation dialogue
tabSupervisor->setCurrentWidget(this); tabSupervisor->setCurrentWidget(this);
QMessageBox *msgBox = createSaveConfirmationWindow(); QMessageBox *msgBox = createSaveConfirmationWindow();
QPushButton *newTabButton = msgBox->addButton(tr("Open in new tab"), QMessageBox::ApplyRole); QPushButton *newTabButton = msgBox->addButton(tr("Open in new tab"), QMessageBox::ApplyRole);
int ret = msgBox->exec(); int ret = msgBox->exec();
// `exec()` returns an opaque value if a non-standard button was clicked. if (msgBox->clickedButton() == newTabButton)
// Directly check if newTabButton was clicked before switching over the standard buttons.
if (msgBox->clickedButton() == newTabButton) {
return NEW_TAB; return NEW_TAB;
}
switch (ret) { switch (ret) {
case QMessageBox::Save: case QMessageBox::Save:
@ -265,9 +300,8 @@ AbstractTabDeckEditor::DeckOpenLocation AbstractTabDeckEditor::confirmOpen(const
} }
/** /**
* @brief Creates the base save confirmation dialogue box. * @brief Creates the base save confirmation dialog.
* * @return Pointer to a QMessageBox.
* @returns A QMessageBox that can be further modified
*/ */
QMessageBox *AbstractTabDeckEditor::createSaveConfirmationWindow() QMessageBox *AbstractTabDeckEditor::createSaveConfirmationWindow()
{ {
@ -279,13 +313,15 @@ QMessageBox *AbstractTabDeckEditor::createSaveConfirmationWindow()
return msgBox; return msgBox;
} }
/**
* @brief Loads a deck from file using a dialog.
* Displays a save confirmation if needed.
*/
void AbstractTabDeckEditor::actLoadDeck() void AbstractTabDeckEditor::actLoadDeck()
{ {
auto deckOpenLocation = confirmOpen(); auto deckOpenLocation = confirmOpen();
if (deckOpenLocation == CANCELLED)
if (deckOpenLocation == CANCELLED) {
return; return;
}
DlgLoadDeck dialog(this); DlgLoadDeck dialog(this);
if (!dialog.exec()) if (!dialog.exec())
@ -296,21 +332,23 @@ void AbstractTabDeckEditor::actLoadDeck()
deckDockWidget->updateBannerCardComboBox(); deckDockWidget->updateBannerCardComboBox();
} }
/**
* @brief Opens a recently used deck file.
* @param fileName Path to the deck file.
*/
void AbstractTabDeckEditor::actOpenRecent(const QString &fileName) void AbstractTabDeckEditor::actOpenRecent(const QString &fileName)
{ {
auto deckOpenLocation = confirmOpen(); auto deckOpenLocation = confirmOpen();
if (deckOpenLocation == CANCELLED)
if (deckOpenLocation == CANCELLED) {
return; return;
}
openDeckFromFile(fileName, deckOpenLocation); openDeckFromFile(fileName, deckOpenLocation);
} }
/** /**
* Actually opens the deck from file * @brief Actually opens a deck from file.
* @param fileName The path of the deck to open * @param fileName Path to the deck file.
* @param deckOpenLocation Which tab to open the deck * @param deckOpenLocation Where to open the deck (same or new tab).
*/ */
void AbstractTabDeckEditor::openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation) void AbstractTabDeckEditor::openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation)
{ {
@ -332,6 +370,11 @@ void AbstractTabDeckEditor::openDeckFromFile(const QString &fileName, DeckOpenLo
deckMenu->setSaveStatus(true); deckMenu->setSaveStatus(true);
} }
/**
* @brief Saves the current deck.
* If remote deck, sends upload command.
* @return True if save succeeded, false otherwise.
*/
bool AbstractTabDeckEditor::actSaveDeck() bool AbstractTabDeckEditor::actSaveDeck()
{ {
DeckLoader *const deck = getDeckList(); DeckLoader *const deck = getDeckList();
@ -357,12 +400,17 @@ bool AbstractTabDeckEditor::actSaveDeck()
setModified(false); setModified(false);
return true; return true;
} }
QMessageBox::critical( QMessageBox::critical(
this, tr("Error"), this, tr("Error"),
tr("The deck could not be saved.\nPlease check that the directory is writable and try again.")); tr("The deck could not be saved.\nPlease check that the directory is writable and try again."));
return false; return false;
} }
/**
* @brief Saves the deck to a user-selected file.
* @return True if save succeeded.
*/
bool AbstractTabDeckEditor::actSaveDeckAs() bool AbstractTabDeckEditor::actSaveDeckAs()
{ {
QFileDialog dialog(this, tr("Save deck")); QFileDialog dialog(this, tr("Save deck"));
@ -384,13 +432,16 @@ bool AbstractTabDeckEditor::actSaveDeckAs()
tr("The deck could not be saved.\nPlease check that the directory is writable and try again.")); tr("The deck could not be saved.\nPlease check that the directory is writable and try again."));
return false; return false;
} }
setModified(false); setModified(false);
SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(fileName); SettingsCache::instance().recents().updateRecentlyOpenedDeckPaths(fileName);
return true; return true;
} }
/**
* @brief Callback for remote deck save completion.
* @param response Server response.
*/
void AbstractTabDeckEditor::saveDeckRemoteFinished(const Response &response) void AbstractTabDeckEditor::saveDeckRemoteFinished(const Response &response)
{ {
if (response.response_code() != Response::RespOk) if (response.response_code() != Response::RespOk)
@ -399,13 +450,15 @@ void AbstractTabDeckEditor::saveDeckRemoteFinished(const Response &response)
setModified(false); setModified(false);
} }
/**
* @brief Loads a deck from clipboard.
* Displays confirmation if the tab is modified.
*/
void AbstractTabDeckEditor::actLoadDeckFromClipboard() void AbstractTabDeckEditor::actLoadDeckFromClipboard()
{ {
auto deckOpenLocation = confirmOpen(); auto deckOpenLocation = confirmOpen();
if (deckOpenLocation == CANCELLED)
if (deckOpenLocation == CANCELLED) {
return; return;
}
DlgLoadDeckFromClipboard dlg(this); DlgLoadDeckFromClipboard dlg(this);
if (!dlg.exec()) if (!dlg.exec())
@ -421,6 +474,10 @@ void AbstractTabDeckEditor::actLoadDeckFromClipboard()
deckMenu->setSaveStatus(true); deckMenu->setSaveStatus(true);
} }
/**
* @brief Opens the deck editor to edit clipboard contents.
* @param annotated If true, edits with annotations.
*/
void AbstractTabDeckEditor::editDeckInClipboard(bool annotated) void AbstractTabDeckEditor::editDeckInClipboard(bool annotated)
{ {
DlgEditDeckInClipboard dlg(*getDeckList(), annotated, this); DlgEditDeckInClipboard dlg(*getDeckList(), annotated, this);
@ -429,40 +486,46 @@ void AbstractTabDeckEditor::editDeckInClipboard(bool annotated)
setDeck(dlg.getDeckList()); setDeck(dlg.getDeckList());
setModified(true); setModified(true);
deckMenu->setSaveStatus(true); deckMenu->setSaveStatus(true);
} }
/** @brief Slot for editing deck in clipboard with annotations. */
void AbstractTabDeckEditor::actEditDeckInClipboard() void AbstractTabDeckEditor::actEditDeckInClipboard()
{ {
editDeckInClipboard(true); editDeckInClipboard(true);
} }
/** @brief Slot for editing deck in clipboard without annotations. */
void AbstractTabDeckEditor::actEditDeckInClipboardRaw() void AbstractTabDeckEditor::actEditDeckInClipboardRaw()
{ {
editDeckInClipboard(false); editDeckInClipboard(false);
} }
/** @brief Saves deck to clipboard with set info and annotation. */
void AbstractTabDeckEditor::actSaveDeckToClipboard() void AbstractTabDeckEditor::actSaveDeckToClipboard()
{ {
getDeckList()->saveToClipboard(true, true); getDeckList()->saveToClipboard(true, true);
} }
/** @brief Saves deck to clipboard with annotation, without set info. */
void AbstractTabDeckEditor::actSaveDeckToClipboardNoSetInfo() void AbstractTabDeckEditor::actSaveDeckToClipboardNoSetInfo()
{ {
getDeckList()->saveToClipboard(true, false); getDeckList()->saveToClipboard(true, false);
} }
/** @brief Saves deck to clipboard without annotations, with set info. */
void AbstractTabDeckEditor::actSaveDeckToClipboardRaw() void AbstractTabDeckEditor::actSaveDeckToClipboardRaw()
{ {
getDeckList()->saveToClipboard(false, true); getDeckList()->saveToClipboard(false, true);
} }
/** @brief Saves deck to clipboard without annotations or set info. */
void AbstractTabDeckEditor::actSaveDeckToClipboardRawNoSetInfo() void AbstractTabDeckEditor::actSaveDeckToClipboardRawNoSetInfo()
{ {
getDeckList()->saveToClipboard(false, false); getDeckList()->saveToClipboard(false, false);
} }
/** @brief Prints the deck using a QPrintPreviewDialog. */
void AbstractTabDeckEditor::actPrintDeck() void AbstractTabDeckEditor::actPrintDeck()
{ {
auto *dlg = new QPrintPreviewDialog(this); auto *dlg = new QPrintPreviewDialog(this);
@ -470,13 +533,14 @@ void AbstractTabDeckEditor::actPrintDeck()
dlg->exec(); dlg->exec();
} }
/**
* @brief Loads a deck from a website.
*/
void AbstractTabDeckEditor::actLoadDeckFromWebsite() void AbstractTabDeckEditor::actLoadDeckFromWebsite()
{ {
auto deckOpenLocation = confirmOpen(); auto deckOpenLocation = confirmOpen();
if (deckOpenLocation == CANCELLED)
if (deckOpenLocation == CANCELLED) {
return; return;
}
DlgLoadDeckFromWebsite dlg(this); DlgLoadDeckFromWebsite dlg(this);
if (!dlg.exec()) if (!dlg.exec())
@ -492,18 +556,21 @@ void AbstractTabDeckEditor::actLoadDeckFromWebsite()
deckMenu->setSaveStatus(true); deckMenu->setSaveStatus(true);
} }
/**
* @brief Exports the deck to a decklist website.
* @param website Website to export to.
*/
void AbstractTabDeckEditor::exportToDecklistWebsite(DeckLoader::DecklistWebsite website) void AbstractTabDeckEditor::exportToDecklistWebsite(DeckLoader::DecklistWebsite website)
{ {
// check if deck is not null
if (DeckLoader *const deck = getDeckList()) { if (DeckLoader *const deck = getDeckList()) {
// Get the decklist url string from the deck loader class.
QString decklistUrlString = deck->exportDeckToDecklist(website); QString decklistUrlString = deck->exportDeckToDecklist(website);
// Check to make sure the string isn't empty. // Check to make sure the string isn't empty.
if (QString::compare(decklistUrlString, "", Qt::CaseInsensitive) == 0) { if (decklistUrlString.isEmpty()) {
// Show an error if the deck is empty, and return. // 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")); QMessageBox::critical(this, tr("Error"), tr("There are no cards in your deck to be exported"));
return; return;
} }
// Encode the string recieved from the model to make sure all characters are encoded. // Encode the string recieved from the model to make sure all characters are encoded.
// first we put it into a qurl object // first we put it into a qurl object
QUrl decklistUrl = QUrl(decklistUrlString); QUrl decklistUrl = QUrl(decklistUrlString);
@ -517,48 +584,54 @@ void AbstractTabDeckEditor::exportToDecklistWebsite(DeckLoader::DecklistWebsite
} }
} }
/** /** @brief Exports deck to www.decklist.org. */
* Exports the deck to www.decklist.org (the old website)
*/
void AbstractTabDeckEditor::actExportDeckDecklist() void AbstractTabDeckEditor::actExportDeckDecklist()
{ {
exportToDecklistWebsite(DeckLoader::DecklistOrg); exportToDecklistWebsite(DeckLoader::DecklistOrg);
} }
/** /** @brief Exports deck to www.decklist.xyz. */
* Exports the deck to www.decklist.xyz (the new website)
*/
void AbstractTabDeckEditor::actExportDeckDecklistXyz() void AbstractTabDeckEditor::actExportDeckDecklistXyz()
{ {
exportToDecklistWebsite(DeckLoader::DecklistXyz); exportToDecklistWebsite(DeckLoader::DecklistXyz);
} }
/** @brief Analyzes the deck using DeckStats. */
void AbstractTabDeckEditor::actAnalyzeDeckDeckstats() void AbstractTabDeckEditor::actAnalyzeDeckDeckstats()
{ {
auto *interface = new DeckStatsInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(), auto *interface = new DeckStatsInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(), this);
this); // it deletes itself when done
interface->analyzeDeck(getDeckList()); interface->analyzeDeck(getDeckList());
} }
/** @brief Analyzes the deck using TappedOut. */
void AbstractTabDeckEditor::actAnalyzeDeckTappedout() void AbstractTabDeckEditor::actAnalyzeDeckTappedout()
{ {
auto *interface = new TappedOutInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(), auto *interface = new TappedOutInterface(*databaseDisplayDockWidget->databaseModel->getDatabase(), this);
this); // it deletes itself when done
interface->analyzeDeck(getDeckList()); interface->analyzeDeck(getDeckList());
} }
/** @brief Applies a new filter tree to the database display. */
void AbstractTabDeckEditor::filterTreeChanged(FilterTree *filterTree) void AbstractTabDeckEditor::filterTreeChanged(FilterTree *filterTree)
{ {
databaseDisplayDockWidget->setFilterTree(filterTree); databaseDisplayDockWidget->setFilterTree(filterTree);
} }
/**
* @brief Handles the close event of the tab.
* @param event Close event.
*/
void AbstractTabDeckEditor::closeEvent(QCloseEvent *event) void AbstractTabDeckEditor::closeEvent(QCloseEvent *event)
{ {
emit deckEditorClosing(this); emit deckEditorClosing(this);
event->accept(); event->accept();
} }
// Method uses to sync docks state with menu items state /**
* @brief Event filter for dock visibility and geometry changes.
* @param o Object sending the event.
* @param e Event.
* @return False always.
*/
bool AbstractTabDeckEditor::eventFilter(QObject *o, QEvent *e) bool AbstractTabDeckEditor::eventFilter(QObject *o, QEvent *e)
{ {
if (e->type() == QEvent::Close) { if (e->type() == QEvent::Close) {
@ -576,6 +649,7 @@ bool AbstractTabDeckEditor::eventFilter(QObject *o, QEvent *e)
aPrintingSelectorDockFloating->setEnabled(false); aPrintingSelectorDockFloating->setEnabled(false);
} }
} }
if (o == this && e->type() == QEvent::Hide) { if (o == this && e->type() == QEvent::Hide) {
LayoutsSettings &layouts = SettingsCache::instance().layouts(); LayoutsSettings &layouts = SettingsCache::instance().layouts();
layouts.setDeckEditorLayoutState(saveState()); layouts.setDeckEditorLayoutState(saveState());
@ -585,9 +659,11 @@ bool AbstractTabDeckEditor::eventFilter(QObject *o, QEvent *e)
layouts.setDeckEditorDeckSize(deckDockWidget->size()); layouts.setDeckEditorDeckSize(deckDockWidget->size());
layouts.setDeckEditorPrintingSelectorSize(printingSelectorDockWidget->size()); layouts.setDeckEditorPrintingSelectorSize(printingSelectorDockWidget->size());
} }
return false; return false;
} }
/** @brief Shows a confirmation dialog before closing. */
bool AbstractTabDeckEditor::confirmClose() bool AbstractTabDeckEditor::confirmClose()
{ {
if (modified) { if (modified) {
@ -601,11 +677,10 @@ bool AbstractTabDeckEditor::confirmClose()
return true; return true;
} }
/** @brief Handles close requests from outside (tab manager). */
bool AbstractTabDeckEditor::closeRequest() bool AbstractTabDeckEditor::closeRequest()
{ {
if (!confirmClose()) { if (!confirmClose())
return false; return false;
}
return close(); return close();
} }

View file

@ -1,3 +1,10 @@
/**
* @file abstract_tab_deck_editor.h
* @brief Defines the AbstractTabDeckEditor class, which provides a base for
* deck editor tabs in the application.
*
*/
#ifndef TAB_GENERIC_DECK_EDITOR_H #ifndef TAB_GENERIC_DECK_EDITOR_H
#define TAB_GENERIC_DECK_EDITOR_H #define TAB_GENERIC_DECK_EDITOR_H
@ -40,6 +47,45 @@ class QDockWidget;
class QMenu; class QMenu;
class QAction; class QAction;
/**
* @class AbstractTabDeckEditor
* @ingroup DeckEditorTabs
* @brief AbstractTabDeckEditor is the base class for all deck editor tabs.
*
* **Description:**
* AbstractTabDeckEditor is the base class for all deck editor tabs. It provides core functionality such as deck model
* management, card addition/removal, and integration with dock widgets and tab supervisors.
*
* **Purpose:**
*
* - Acts as the foundation for deck editor tabs (TabDeckEditor and TabDeckEditorVisual).
* - Provides basic deck operations like adding, removing, swapping, and decrementing cards.
* - Integrates with DeckListModel and CardDatabaseModel to access deck and card data.
* - Handles saving, loading, and layout persistence at the tab level.
*
* **Dock Widgets (typically managed in derived classes):**
*
* - DeckEditorCardInfoDockWidget Displays detailed card info.
* - DeckEditorDeckDockWidget Displays mainboard/sideboard cards and zones.
* - DeckEditorFilterDockWidget Provides filtering options for card searches.
* - DeckEditorPrintingSelectorDockWidget Selector for different card printings.
* - DeckEditorDatabaseDisplayWidget Shows card database for adding cards to deck.
*
* **Key Methods:**
*
* - actAddCard(const ExactCard &card) Adds a card to the deck.
* - actDecrementCard(const ExactCard &card) Removes a single instance of a card from the deck.
* - actSwapCard(const ExactCard &card, const QString &zone) Swaps a card between zones.
* - actRemoveCard() Removes the currently selected card from the deck.
* - actSaveDeckAs() Performs a "Save As" action for the deck.
* - updateCard(const ExactCard &card) Updates the currently displayed card info in the dock.
* - onDeckModified() Called when the deck model is changed.
*
* Provides UI docks for the deck, database, card info, printing selector,
* and filters. Supports loading, saving, editing, exporting decks, and
* interactions with external services such as DeckStats, TappedOut, and
* remote deck uploads.
*/
class AbstractTabDeckEditor : public Tab class AbstractTabDeckEditor : public Tab
{ {
Q_OBJECT Q_OBJECT
@ -47,68 +93,150 @@ class AbstractTabDeckEditor : public Tab
friend class DeckEditorMenu; friend class DeckEditorMenu;
public: public:
/**
* @brief Constructs an AbstractTabDeckEditor.
* @param _tabSupervisor Pointer to the TabSupervisor managing this tab.
*/
explicit AbstractTabDeckEditor(TabSupervisor *_tabSupervisor); explicit AbstractTabDeckEditor(TabSupervisor *_tabSupervisor);
// UI and Navigation /** @brief Creates the menus for this tab. Pure virtual. */
virtual void createMenus() = 0; virtual void createMenus() = 0;
/** @brief Returns the display text for the tab. */
[[nodiscard]] virtual QString getTabText() const override = 0; [[nodiscard]] virtual QString getTabText() const override = 0;
/** @brief Confirms whether the tab can be safely closed. */
bool confirmClose(); bool confirmClose();
/** @brief Retranslates the UI text. Pure virtual. */
virtual void retranslateUi() override = 0; virtual void retranslateUi() override = 0;
// Deck Management /** @brief Opens a deck in this tab.
* @param deck Pointer to a DeckLoader object.
*/
void openDeck(DeckLoader *deck); void openDeck(DeckLoader *deck);
/** @brief Returns the currently active deck. */
DeckLoader *getDeckList() const; DeckLoader *getDeckList() const;
/** @brief Sets the modified state of the tab.
* @param _windowModified Whether the tab is modified.
*/
void setModified(bool _windowModified); void setModified(bool _windowModified);
// UI Elements // UI Elements
DeckEditorMenu *deckMenu; DeckEditorMenu *deckMenu; ///< Menu for deck operations
DeckEditorDatabaseDisplayWidget *databaseDisplayDockWidget; DeckEditorDatabaseDisplayWidget *databaseDisplayDockWidget; ///< Database dock
DeckEditorCardInfoDockWidget *cardInfoDockWidget; DeckEditorCardInfoDockWidget *cardInfoDockWidget; ///< Card info dock
DeckEditorDeckDockWidget *deckDockWidget; DeckEditorDeckDockWidget *deckDockWidget; ///< Deck dock
DeckEditorFilterDockWidget *filterDockWidget; DeckEditorFilterDockWidget *filterDockWidget; ///< Filter dock
DeckEditorPrintingSelectorDockWidget *printingSelectorDockWidget; DeckEditorPrintingSelectorDockWidget *printingSelectorDockWidget; ///< Printing selector dock
public slots: public slots:
/** @brief Called when the deck changes. */
virtual void onDeckChanged(); virtual void onDeckChanged();
/** @brief Called when the deck is modified. */
virtual void onDeckModified(); virtual void onDeckModified();
/** @brief Updates the card info panel.
* @param card The card to display.
*/
void updateCard(const ExactCard &card); void updateCard(const ExactCard &card);
/** @brief Adds a card to the main deck or sideboard based on Ctrl key. */
void actAddCard(const ExactCard &card); void actAddCard(const ExactCard &card);
/** @brief Adds a card to the sideboard explicitly. */
void actAddCardToSideboard(const ExactCard &card); void actAddCardToSideboard(const ExactCard &card);
/** @brief Decrements a card from the main deck. */
void actDecrementCard(const ExactCard &card); void actDecrementCard(const ExactCard &card);
/** @brief Decrements a card from the sideboard. */
void actDecrementCardFromSideboard(const ExactCard &card); void actDecrementCardFromSideboard(const ExactCard &card);
/** @brief Opens a recently opened deck file. */
void actOpenRecent(const QString &fileName); void actOpenRecent(const QString &fileName);
/** @brief Called when the filter tree changes. */
void filterTreeChanged(FilterTree *filterTree); void filterTreeChanged(FilterTree *filterTree);
/** @brief Requests closing the tab. */
bool closeRequest() override; bool closeRequest() override;
/** @brief Shows the printing selector dock. Pure virtual. */
virtual void showPrintingSelector() = 0; virtual void showPrintingSelector() = 0;
/** @brief Slot for when a dock's top-level state changes. Pure virtual. */
virtual void dockTopLevelChanged(bool topLevel) = 0; virtual void dockTopLevelChanged(bool topLevel) = 0;
signals: signals:
/** @brief Emitted when a deck should be opened in a new editor tab. */
void openDeckEditor(const DeckLoader *deckLoader); void openDeckEditor(const DeckLoader *deckLoader);
/** @brief Emitted before the tab is closed. */
void deckEditorClosing(AbstractTabDeckEditor *tab); void deckEditorClosing(AbstractTabDeckEditor *tab);
/** @brief Emitted when a card should be decremented. */
void decrementCard(const ExactCard &card, QString zoneName); void decrementCard(const ExactCard &card, QString zoneName);
protected slots: protected slots:
// Deck Operations /** @brief Starts a new deck in this tab. */
virtual void actNewDeck(); virtual void actNewDeck();
/** @brief Cleans the current deck and resets the modified state. */
void cleanDeckAndResetModified(); void cleanDeckAndResetModified();
/** @brief Loads a deck from file. */
virtual void actLoadDeck(); virtual void actLoadDeck();
/** @brief Saves the current deck. */
bool actSaveDeck(); bool actSaveDeck();
/** @brief Saves the current deck under a new name. */
virtual bool actSaveDeckAs(); virtual bool actSaveDeckAs();
/** @brief Loads a deck from the clipboard. */
virtual void actLoadDeckFromClipboard(); virtual void actLoadDeckFromClipboard();
/** @brief Opens a deck editor for clipboard contents. */
void actEditDeckInClipboard(); void actEditDeckInClipboard();
/** @brief Opens a raw clipboard deck editor. */
void actEditDeckInClipboardRaw(); void actEditDeckInClipboardRaw();
/** @brief Saves deck to clipboard with full info. */
void actSaveDeckToClipboard(); void actSaveDeckToClipboard();
/** @brief Saves deck to clipboard without set info. */
void actSaveDeckToClipboardNoSetInfo(); void actSaveDeckToClipboardNoSetInfo();
/** @brief Saves deck to clipboard in raw format. */
void actSaveDeckToClipboardRaw(); void actSaveDeckToClipboardRaw();
/** @brief Saves deck to clipboard in raw format without set info. */
void actSaveDeckToClipboardRawNoSetInfo(); void actSaveDeckToClipboardRawNoSetInfo();
/** @brief Prints the deck using a preview dialog. */
void actPrintDeck(); void actPrintDeck();
/** @brief Loads a deck from an online website. */
void actLoadDeckFromWebsite(); void actLoadDeckFromWebsite();
/** @brief Exports the deck to decklist.org. */
void actExportDeckDecklist(); void actExportDeckDecklist();
/** @brief Exports the deck to decklist.xyz. */
void actExportDeckDecklistXyz(); void actExportDeckDecklistXyz();
/** @brief Analyzes the deck using deckstats.net. */
void actAnalyzeDeckDeckstats(); void actAnalyzeDeckDeckstats();
/** @brief Analyzes the deck using tappedout.net. */
void actAnalyzeDeckTappedout(); void actAnalyzeDeckTappedout();
// Remote Save /** @brief Callback when a remote deck save finishes. */
void saveDeckRemoteFinished(const Response &r); void saveDeckRemoteFinished(const Response &r);
// UI Layout Management // UI Layout Management
@ -117,34 +245,62 @@ protected slots:
virtual void freeDocksSize() = 0; virtual void freeDocksSize() = 0;
virtual void refreshShortcuts() = 0; virtual void refreshShortcuts() = 0;
/** @brief Handles dock close events. */
void closeEvent(QCloseEvent *event) override; void closeEvent(QCloseEvent *event) override;
/** @brief Event filter for dock state changes. */
bool eventFilter(QObject *o, QEvent *e) override; bool eventFilter(QObject *o, QEvent *e) override;
/** @brief Slot triggered when a dock visibility changes. Pure virtual. */
virtual void dockVisibleTriggered() = 0; virtual void dockVisibleTriggered() = 0;
/** @brief Slot triggered when a dock floating state changes. Pure virtual. */
virtual void dockFloatingTriggered() = 0; virtual void dockFloatingTriggered() = 0;
private: private:
/** @brief Sets the deck for this tab.
* @param _deck The deck object.
*/
virtual void setDeck(DeckLoader *_deck); virtual void setDeck(DeckLoader *_deck);
/** @brief Helper for editing decks from the clipboard. */
void editDeckInClipboard(bool annotated); void editDeckInClipboard(bool annotated);
/** @brief Helper for exporting decks to websites.
* @param website Target website.
*/
void exportToDecklistWebsite(DeckLoader::DecklistWebsite website); void exportToDecklistWebsite(DeckLoader::DecklistWebsite website);
protected: protected:
/** /** @brief Enum describing deck open locations */
* @brief Enum for selecting deck open location
*/
enum DeckOpenLocation enum DeckOpenLocation
{ {
CANCELLED, CANCELLED, ///< Operation cancelled
SAME_TAB, SAME_TAB, ///< Open deck in the same tab
NEW_TAB NEW_TAB ///< Open deck in a new tab
}; };
/** @brief Confirms deck open action based on settings and modified state.
* @param openInSameTabIfBlank Whether to reuse same tab if blank.
* @return Selected DeckOpenLocation.
*/
DeckOpenLocation confirmOpen(bool openInSameTabIfBlank = true); DeckOpenLocation confirmOpen(bool openInSameTabIfBlank = true);
/** @brief Creates a save confirmation message box.
* @return Pointer to a QMessageBox.
*/
QMessageBox *createSaveConfirmationWindow(); QMessageBox *createSaveConfirmationWindow();
/** @brief Returns true if the tab is a blank newly created deck. */
bool isBlankNewDeck() const; bool isBlankNewDeck() const;
// Helper functions for card actions /** @brief Helper function to add a card to a specific deck zone. */
void addCardHelper(const ExactCard &card, QString zoneName); void addCardHelper(const ExactCard &card, QString zoneName);
/** @brief Swaps a card in the deck view. */
void actSwapCard(const ExactCard &card, const QString &zoneName); void actSwapCard(const ExactCard &card, const QString &zoneName);
/** @brief Opens a deck from a file. */
virtual void openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation); virtual void openDeckFromFile(const QString &fileName, DeckOpenLocation deckOpenLocation);
// UI Menu Elements // UI Menu Elements
@ -154,7 +310,7 @@ protected:
QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aDeckDockVisible, *aDeckDockFloating; QAction *aCardInfoDockVisible, *aCardInfoDockFloating, *aDeckDockVisible, *aDeckDockFloating;
QAction *aFilterDockVisible, *aFilterDockFloating, *aPrintingSelectorDockVisible, *aPrintingSelectorDockFloating; QAction *aFilterDockVisible, *aFilterDockFloating, *aPrintingSelectorDockVisible, *aPrintingSelectorDockFloating;
bool modified = false; bool modified = false; ///< Whether the deck/tab has unsaved changes
}; };
#endif // TAB_GENERIC_DECK_EDITOR_H #endif // TAB_GENERIC_DECK_EDITOR_H