Add action to Edit Deck via Clipboard (#5681)

* implement functionality in dlg

* add action to deck editor

* refactor and comments

* is this refactor even a good idea?

* remove the friend class stuff

* reorder

* add option for not annotated
This commit is contained in:
RickyRister 2025-03-04 16:55:05 -08:00 committed by GitHub
parent 8fc1b22889
commit 2f415dcc6e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 237 additions and 54 deletions

View file

@ -30,6 +30,12 @@ DeckEditorMenu::DeckEditorMenu(QWidget *parent, AbstractTabDeckEditor *_deckEdit
aLoadDeckFromClipboard = new QAction(QString(), this); aLoadDeckFromClipboard = new QAction(QString(), this);
connect(aLoadDeckFromClipboard, SIGNAL(triggered()), deckEditor, SLOT(actLoadDeckFromClipboard())); connect(aLoadDeckFromClipboard, SIGNAL(triggered()), deckEditor, SLOT(actLoadDeckFromClipboard()));
aEditDeckInClipboard = new QAction(QString(), this);
connect(aEditDeckInClipboard, SIGNAL(triggered()), deckEditor, SLOT(actEditDeckInClipboard()));
aEditDeckInClipboardRaw = new QAction(QString(), this);
connect(aEditDeckInClipboardRaw, SIGNAL(triggered()), deckEditor, SLOT(actEditDeckInClipboardRaw()));
aSaveDeckToClipboard = new QAction(QString(), this); aSaveDeckToClipboard = new QAction(QString(), this);
connect(aSaveDeckToClipboard, SIGNAL(triggered()), deckEditor, SLOT(actSaveDeckToClipboard())); connect(aSaveDeckToClipboard, SIGNAL(triggered()), deckEditor, SLOT(actSaveDeckToClipboard()));
@ -64,6 +70,10 @@ DeckEditorMenu::DeckEditorMenu(QWidget *parent, AbstractTabDeckEditor *_deckEdit
aClose = new QAction(QString(), this); aClose = new QAction(QString(), this);
connect(aClose, &QAction::triggered, deckEditor, &AbstractTabDeckEditor::closeRequest); connect(aClose, &QAction::triggered, deckEditor, &AbstractTabDeckEditor::closeRequest);
editDeckInClipboardMenu = new QMenu(this);
editDeckInClipboardMenu->addAction(aEditDeckInClipboard);
editDeckInClipboardMenu->addAction(aEditDeckInClipboardRaw);
saveDeckToClipboardMenu = new QMenu(this); saveDeckToClipboardMenu = new QMenu(this);
saveDeckToClipboardMenu->addAction(aSaveDeckToClipboard); saveDeckToClipboardMenu->addAction(aSaveDeckToClipboard);
saveDeckToClipboardMenu->addAction(aSaveDeckToClipboardNoSetNameAndNumber); saveDeckToClipboardMenu->addAction(aSaveDeckToClipboardNoSetNameAndNumber);
@ -77,6 +87,7 @@ DeckEditorMenu::DeckEditorMenu(QWidget *parent, AbstractTabDeckEditor *_deckEdit
addAction(aSaveDeckAs); addAction(aSaveDeckAs);
addSeparator(); addSeparator();
addAction(aLoadDeckFromClipboard); addAction(aLoadDeckFromClipboard);
addMenu(editDeckInClipboardMenu);
addMenu(saveDeckToClipboardMenu); addMenu(saveDeckToClipboardMenu);
addSeparator(); addSeparator();
addAction(aPrintDeck); addAction(aPrintDeck);
@ -133,8 +144,13 @@ void DeckEditorMenu::retranslateUi()
aClearRecents->setText(tr("Clear")); aClearRecents->setText(tr("Clear"));
aSaveDeck->setText(tr("&Save deck")); aSaveDeck->setText(tr("&Save deck"));
aSaveDeckAs->setText(tr("Save deck &as...")); aSaveDeckAs->setText(tr("Save deck &as..."));
aLoadDeckFromClipboard->setText(tr("Load deck from cl&ipboard...")); aLoadDeckFromClipboard->setText(tr("Load deck from cl&ipboard..."));
editDeckInClipboardMenu->setTitle(tr("Edit deck in clipboard"));
aEditDeckInClipboard->setText(tr("Annotated"));
aEditDeckInClipboardRaw->setText(tr("Not Annotated"));
saveDeckToClipboardMenu->setTitle(tr("Save deck to clipboard")); saveDeckToClipboardMenu->setTitle(tr("Save deck to clipboard"));
aSaveDeckToClipboard->setText(tr("Annotated")); aSaveDeckToClipboard->setText(tr("Annotated"));
aSaveDeckToClipboardNoSetNameAndNumber->setText(tr("Annotated (No set name or number)")); aSaveDeckToClipboardNoSetNameAndNumber->setText(tr("Annotated (No set name or number)"));
@ -160,6 +176,8 @@ void DeckEditorMenu::refreshShortcuts()
aExportDeckDecklist->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aExportDeckDecklist")); aExportDeckDecklist->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aExportDeckDecklist"));
aSaveDeckAs->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aSaveDeckAs")); aSaveDeckAs->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aSaveDeckAs"));
aLoadDeckFromClipboard->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aLoadDeckFromClipboard")); aLoadDeckFromClipboard->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aLoadDeckFromClipboard"));
aEditDeckInClipboard->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aEditDeckInClipboard"));
aEditDeckInClipboardRaw->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aEditDeckInClipboardRaw"));
aPrintDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aPrintDeck")); aPrintDeck->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aPrintDeck"));
aAnalyzeDeckDeckstats->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aAnalyzeDeck")); aAnalyzeDeckDeckstats->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aAnalyzeDeck"));
aClose->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aClose")); aClose->setShortcuts(shortcuts.getShortcut("TabDeckEditor/aClose"));

View file

@ -15,10 +15,10 @@ public:
AbstractTabDeckEditor *deckEditor; AbstractTabDeckEditor *deckEditor;
QAction *aNewDeck, *aLoadDeck, *aClearRecents, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, QAction *aNewDeck, *aLoadDeck, *aClearRecents, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard,
*aSaveDeckToClipboard, *aSaveDeckToClipboardNoSetNameAndNumber, *aSaveDeckToClipboardRaw, *aEditDeckInClipboard, *aEditDeckInClipboardRaw, *aSaveDeckToClipboard, *aSaveDeckToClipboardNoSetNameAndNumber,
*aSaveDeckToClipboardRawNoSetNameAndNumber, *aPrintDeck, *aExportDeckDecklist, *aAnalyzeDeckDeckstats, *aSaveDeckToClipboardRaw, *aSaveDeckToClipboardRawNoSetNameAndNumber, *aPrintDeck, *aExportDeckDecklist,
*aAnalyzeDeckTappedout, *aClose; *aAnalyzeDeckDeckstats, *aAnalyzeDeckTappedout, *aClose;
QMenu *loadRecentDeckMenu, *analyzeDeckMenu, *saveDeckToClipboardMenu; QMenu *loadRecentDeckMenu, *analyzeDeckMenu, *editDeckInClipboardMenu, *saveDeckToClipboardMenu;
void setSaveStatus(bool newStatus); void setSaveStatus(bool newStatus);

View file

@ -394,6 +394,28 @@ void AbstractTabDeckEditor::actLoadDeckFromClipboard()
deckMenu->setSaveStatus(true); deckMenu->setSaveStatus(true);
} }
void AbstractTabDeckEditor::editDeckInClipboard(bool annotated)
{
DlgEditDeckInClipboard dlg(*getDeckList(), annotated, this);
if (!dlg.exec())
return;
setDeck(dlg.getDeckList());
setModified(true);
deckMenu->setSaveStatus(true);
}
void AbstractTabDeckEditor::actEditDeckInClipboard()
{
editDeckInClipboard(true);
}
void AbstractTabDeckEditor::actEditDeckInClipboardRaw()
{
editDeckInClipboard(false);
}
void AbstractTabDeckEditor::actSaveDeckToClipboard() void AbstractTabDeckEditor::actSaveDeckToClipboard()
{ {
getDeckList()->saveToClipboard(true, true); getDeckList()->saveToClipboard(true, true);

View file

@ -92,6 +92,8 @@ protected slots:
bool actSaveDeck(); bool actSaveDeck();
bool actSaveDeckAs(); bool actSaveDeckAs();
virtual void actLoadDeckFromClipboard(); virtual void actLoadDeckFromClipboard();
void actEditDeckInClipboard();
void actEditDeckInClipboardRaw();
void actSaveDeckToClipboard(); void actSaveDeckToClipboard();
void actSaveDeckToClipboardNoSetNameAndNumber(); void actSaveDeckToClipboardNoSetNameAndNumber();
void actSaveDeckToClipboardRaw(); void actSaveDeckToClipboardRaw();
@ -114,6 +116,9 @@ protected slots:
virtual void dockVisibleTriggered() = 0; virtual void dockVisibleTriggered() = 0;
virtual void dockFloatingTriggered() = 0; virtual void dockFloatingTriggered() = 0;
private:
void editDeckInClipboard(bool annotated);
protected: protected:
/** /**
* @brief Enum for selecting deck open location * @brief Enum for selecting deck open location

View file

@ -2,6 +2,7 @@
#include "../deck/deck_loader.h" #include "../deck/deck_loader.h"
#include "../settings/cache_settings.h" #include "../settings/cache_settings.h"
#include "dlg_settings.h"
#include <QApplication> #include <QApplication>
#include <QCheckBox> #include <QCheckBox>
@ -13,17 +14,20 @@
#include <QTextStream> #include <QTextStream>
#include <QVBoxLayout> #include <QVBoxLayout>
DlgLoadDeckFromClipboard::DlgLoadDeckFromClipboard(QWidget *parent) : QDialog(parent), deckList(nullptr) /**
* Creates the main layout and connects the signals that are common to all versions of this window
*/
AbstractDlgDeckTextEdit::AbstractDlgDeckTextEdit(QWidget *parent) : QDialog(parent)
{ {
contentsEdit = new QPlainTextEdit; contentsEdit = new QPlainTextEdit;
refreshButton = new QPushButton(tr("&Refresh")); refreshButton = new QPushButton(tr("&Refresh"));
connect(refreshButton, SIGNAL(clicked()), this, SLOT(actRefresh())); connect(refreshButton, &QPushButton::clicked, this, &AbstractDlgDeckTextEdit::actRefresh);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
buttonBox->addButton(refreshButton, QDialogButtonBox::ActionRole); buttonBox->addButton(refreshButton, QDialogButtonBox::ActionRole);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOK())); connect(buttonBox, &QDialogButtonBox::accepted, this, &AbstractDlgDeckTextEdit::actOK);
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(buttonBox, &QDialogButtonBox::rejected, this, &AbstractDlgDeckTextEdit::reject);
loadSetNameAndNumberCheckBox = new QCheckBox(tr("Parse Set Name and Number (if available)")); loadSetNameAndNumberCheckBox = new QCheckBox(tr("Parse Set Name and Number (if available)"));
loadSetNameAndNumberCheckBox->setChecked(true); loadSetNameAndNumberCheckBox->setChecked(true);
@ -38,54 +42,57 @@ DlgLoadDeckFromClipboard::DlgLoadDeckFromClipboard(QWidget *parent) : QDialog(pa
setLayout(mainLayout); setLayout(mainLayout);
setWindowTitle(tr("Load deck from clipboard"));
resize(500, 500); resize(500, 500);
actRefresh(); connect(&SettingsCache::instance().shortcuts(), &ShortcutsSettings::shortCutChanged, this,
connect(&SettingsCache::instance().shortcuts(), SIGNAL(shortCutChanged()), this, SLOT(refreshShortcuts())); &AbstractDlgDeckTextEdit::refreshShortcuts);
refreshShortcuts(); refreshShortcuts();
} }
void DlgLoadDeckFromClipboard::actRefresh() void AbstractDlgDeckTextEdit::refreshShortcuts()
{
contentsEdit->setPlainText(QApplication::clipboard()->text());
}
void DlgLoadDeckFromClipboard::refreshShortcuts()
{ {
refreshButton->setShortcut( refreshButton->setShortcut(
SettingsCache::instance().shortcuts().getSingleShortcut("DlgLoadDeckFromClipboard/refreshButton")); SettingsCache::instance().shortcuts().getSingleShortcut("DlgLoadDeckFromClipboard/refreshButton"));
} }
void DlgLoadDeckFromClipboard::actOK() /**
* Replaces the contents of the contentsEdit with the given text.
* @param text The text
*/
void AbstractDlgDeckTextEdit::setText(const QString &text)
{ {
QString buffer = contentsEdit->toPlainText(); contentsEdit->setPlainText(text);
QTextStream stream(&buffer);
auto *deckLoader = new DeckLoader;
deckLoader->setParent(this);
if (buffer.contains("<cockatrice_deck version=\"1\">")) {
if (deckLoader->loadFromString_Native(buffer)) {
deckList = deckLoader;
accept();
} else {
QMessageBox::critical(this, tr("Error"), tr("Invalid deck list."));
}
} else if (deckLoader->loadFromStream_Plain(stream)) {
deckList = deckLoader;
if (loadSetNameAndNumberCheckBox->isChecked()) {
deckList->resolveSetNameAndNumberToProviderID();
} else {
deckList->clearSetNamesAndNumbers();
}
accept();
} else {
QMessageBox::critical(this, tr("Error"), tr("Invalid deck list."));
}
} }
void DlgLoadDeckFromClipboard::keyPressEvent(QKeyEvent *event) /**
* Tries to load the current contents of the contentsEdit into the DeckLoader
*
* @param deckLoader The DeckLoader to load the deck into
* @return Whether the loading was successful
*/
bool AbstractDlgDeckTextEdit::loadIntoDeck(DeckLoader *deckLoader) const
{
QString buffer = contentsEdit->toPlainText();
if (buffer.contains("<cockatrice_deck version=\"1\">")) {
return deckLoader->loadFromString_Native(buffer);
}
QTextStream stream(&buffer);
if (deckLoader->loadFromStream_Plain(stream)) {
if (loadSetNameAndNumberCheckBox->isChecked()) {
deckLoader->resolveSetNameAndNumberToProviderID();
} else {
deckLoader->clearSetNamesAndNumbers();
}
return true;
}
return false;
}
void AbstractDlgDeckTextEdit::keyPressEvent(QKeyEvent *event)
{ {
if (event->key() == Qt::Key_Return && event->modifiers() & Qt::ControlModifier) { if (event->key() == Qt::Key_Return && event->modifiers() & Qt::ControlModifier) {
event->accept(); event->accept();
@ -93,4 +100,79 @@ void DlgLoadDeckFromClipboard::keyPressEvent(QKeyEvent *event)
return; return;
} }
QDialog::keyPressEvent(event); QDialog::keyPressEvent(event);
}
/**
* Creates the dialog window for the "Load deck from clipboard" action
*
* @param parent The parent widget
*/
DlgLoadDeckFromClipboard::DlgLoadDeckFromClipboard(QWidget *parent) : AbstractDlgDeckTextEdit(parent), deckList(nullptr)
{
setWindowTitle(tr("Load deck from clipboard"));
DlgLoadDeckFromClipboard::actRefresh();
}
void DlgLoadDeckFromClipboard::actRefresh()
{
setText(QApplication::clipboard()->text());
}
void DlgLoadDeckFromClipboard::actOK()
{
deckList = new DeckLoader;
deckList->setParent(this);
if (loadIntoDeck(deckList)) {
accept();
} else {
QMessageBox::critical(this, tr("Error"), tr("Invalid deck list."));
}
}
/**
* Creates the dialog window for the "Edit deck in clipboard" action
*
* @param deckList The existing deck in the deck editor. Copies the instance
* @param _annotated Whether to add annotations to the text that is loaded from the deck
* @param parent The parent widget
*/
DlgEditDeckInClipboard::DlgEditDeckInClipboard(const DeckLoader &deckList, bool _annotated, QWidget *parent)
: AbstractDlgDeckTextEdit(parent), annotated(_annotated)
{
setWindowTitle(tr("Edit deck in clipboard"));
deckLoader = new DeckLoader(deckList);
deckLoader->setParent(this);
DlgEditDeckInClipboard::actRefresh();
}
/**
* Loads the contents of the DeckList into a String. Always loads it with addSetNameAndNumber=true
* @param deckList The deck to load
* @param addComments Whether to add annotations
* @return A QString
*/
static QString deckListToString(const DeckLoader *deckList, bool addComments)
{
QString buffer;
QTextStream stream(&buffer);
deckList->saveToStream_Plain(stream, addComments);
return buffer;
}
void DlgEditDeckInClipboard::actRefresh()
{
setText(deckListToString(deckLoader, annotated));
}
void DlgEditDeckInClipboard::actOK()
{
if (loadIntoDeck(deckLoader)) {
accept();
} else {
QMessageBox::critical(this, tr("Error"), tr("Invalid deck list."));
}
} }

View file

@ -8,22 +8,22 @@ class DeckLoader;
class QPlainTextEdit; class QPlainTextEdit;
class QPushButton; class QPushButton;
class DlgLoadDeckFromClipboard : public QDialog /**
* Base class for dialog windows for actions that involve loading decks from text input.
*/
class AbstractDlgDeckTextEdit : public QDialog
{ {
Q_OBJECT Q_OBJECT
private slots:
void actOK();
void actRefresh();
void refreshShortcuts();
private: private:
DeckLoader *deckList;
QPlainTextEdit *contentsEdit; QPlainTextEdit *contentsEdit;
QPushButton *refreshButton; QPushButton *refreshButton;
QCheckBox *loadSetNameAndNumberCheckBox; QCheckBox *loadSetNameAndNumberCheckBox;
private slots:
void refreshShortcuts();
public: public:
explicit DlgLoadDeckFromClipboard(QWidget *parent = nullptr); explicit AbstractDlgDeckTextEdit(QWidget *parent = nullptr);
/** /**
* Gets the loaded deck. Only call this method after this dialog window has been successfully exec'd. * Gets the loaded deck. Only call this method after this dialog window has been successfully exec'd.
@ -32,13 +32,61 @@ public:
* to use it, since otherwise it will get destroyed once this dlg is destroyed * to use it, since otherwise it will get destroyed once this dlg is destroyed
* @return The DeckLoader * @return The DeckLoader
*/ */
DeckLoader *getDeckList() const virtual DeckLoader *getDeckList() const = 0;
protected:
void setText(const QString &text);
bool loadIntoDeck(DeckLoader *deckLoader) const;
void keyPressEvent(QKeyEvent *event) override;
protected slots:
virtual void actOK() = 0;
virtual void actRefresh() = 0;
};
/**
* Dialog window for the "Load deck from clipboard" action
*/
class DlgLoadDeckFromClipboard : public AbstractDlgDeckTextEdit
{
Q_OBJECT
protected slots:
void actOK() override;
void actRefresh() override;
private:
DeckLoader *deckList;
public:
explicit DlgLoadDeckFromClipboard(QWidget *parent = nullptr);
DeckLoader *getDeckList() const override
{ {
return deckList; return deckList;
} }
};
protected: /**
void keyPressEvent(QKeyEvent *event) override; * Dialog window for the "Edit deck in clipboard" action
*/
class DlgEditDeckInClipboard : public AbstractDlgDeckTextEdit
{
Q_OBJECT
protected slots:
void actOK() override;
void actRefresh() override;
private:
DeckLoader *deckLoader;
bool annotated;
public:
explicit DlgEditDeckInClipboard(const DeckLoader &deckList, bool _annotated, QWidget *parent = nullptr);
DeckLoader *getDeckList() const override
{
return deckLoader;
}
}; };
#endif #endif

View file

@ -206,6 +206,14 @@ private:
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Deck from Clipboard..."), ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Load Deck from Clipboard..."),
parseSequenceString("Ctrl+Shift+V"), parseSequenceString("Ctrl+Shift+V"),
ShortcutGroup::Deck_Editor)}, ShortcutGroup::Deck_Editor)},
{"TabDeckEditor/aEditDeckInClipboard",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Edit Deck in Clipboard, Annotated"),
parseSequenceString(""),
ShortcutGroup::Deck_Editor)},
{"TabDeckEditor/aEditDeckInClipboardRaw",
ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "Edit Deck in Clipboard"),
parseSequenceString(""),
ShortcutGroup::Deck_Editor)},
{"TabDeckEditor/aNewDeck", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "New Deck"), {"TabDeckEditor/aNewDeck", ShortcutKey(QT_TRANSLATE_NOOP("shortcutsTab", "New Deck"),
parseSequenceString("Ctrl+N"), parseSequenceString("Ctrl+N"),
ShortcutGroup::Deck_Editor)}, ShortcutGroup::Deck_Editor)},