diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 0fc6e78b3..7881d330b 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -35,6 +35,7 @@ set(cockatrice_SOURCES src/deck/deck_list_model.cpp src/deck/deck_stats_interface.cpp src/dialogs/dlg_connect.cpp + src/dialogs/dlg_convert_deck_to_cod_format.cpp src/dialogs/dlg_create_token.cpp src/dialogs/dlg_create_game.cpp src/dialogs/dlg_edit_avatar.cpp 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 3572daaee..a188f23b6 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,5 +1,7 @@ #include "deck_preview_tag_addition_widget.h" +#include "../../../../../dialogs/dlg_convert_deck_to_cod_format.h" +#include "../../../../../settings/cache_settings.h" #include "deck_preview_tag_dialog.h" #include @@ -40,11 +42,50 @@ void DeckPreviewTagAdditionWidget::mousePressEvent(QMouseEvent *event) QStringList knownTags = parent->parent->parent->gatherAllTagsFromFlowWidget(); QStringList activeTags = parent->deckLoader->getTags(); - DeckPreviewTagDialog dialog(knownTags, activeTags); - if (dialog.exec() == QDialog::Accepted) { - QStringList updatedTags = dialog.getActiveTags(); - parent->deckLoader->setTags(updatedTags); - parent->deckLoader->saveToFile(parent->parent->filePath, DeckLoader::CockatriceFormat); + bool canAddTags = true; + + if (parent->deckLoader->getLastFileFormat() != DeckLoader::CockatriceFormat) { + canAddTags = false; + // Retrieve saved preference if the prompt is disabled + if (!SettingsCache::instance().getVisualDeckStoragePromptForConversion()) { + if (SettingsCache::instance().getVisualDeckStorageAlwaysConvert()) { + parent->deckLoader->convertToCockatriceFormat(parent->parent->filePath); + parent->parent->filePath = parent->deckLoader->getLastFileName(); + parent->parent->refreshBannerCardText(); + canAddTags = true; + } + } else { + // Show the dialog to the user + DialogConvertDeckToCodFormat conversionDialog(parent); + if (conversionDialog.exec() == QDialog::Accepted) { + parent->deckLoader->convertToCockatriceFormat(parent->parent->filePath); + parent->parent->filePath = parent->deckLoader->getLastFileName(); + parent->parent->refreshBannerCardText(); + canAddTags = true; + + if (conversionDialog.dontAskAgain()) { + SettingsCache::instance().setVisualDeckStoragePromptForConversion(Qt::CheckState::Unchecked); + SettingsCache::instance().setVisualDeckStorageAlwaysConvert(Qt::CheckState::Checked); + } + } else { + SettingsCache::instance().setVisualDeckStorageAlwaysConvert(Qt::CheckState::Unchecked); + + if (conversionDialog.dontAskAgain()) { + SettingsCache::instance().setVisualDeckStoragePromptForConversion(Qt::CheckState::Unchecked); + } else { + SettingsCache::instance().setVisualDeckStoragePromptForConversion(Qt::CheckState::Checked); + } + } + } + } + + if (canAddTags) { + DeckPreviewTagDialog dialog(knownTags, activeTags); + if (dialog.exec() == QDialog::Accepted) { + QStringList updatedTags = dialog.getActiveTags(); + parent->deckLoader->setTags(updatedTags); + parent->deckLoader->saveToFile(parent->parent->filePath, DeckLoader::CockatriceFormat); + } } } diff --git a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp index 18bc771d1..59e02ccbc 100644 --- a/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp +++ b/cockatrice/src/client/ui/widgets/visual_deck_storage/deck_preview/deck_preview_widget.cpp @@ -17,7 +17,7 @@ DeckPreviewWidget::DeckPreviewWidget(VisualDeckStorageWidget *_parent, const QSt deckLoader = new DeckLoader(); connect(deckLoader, &DeckLoader::loadFinished, this, &DeckPreviewWidget::initializeUi); - deckLoader->loadFromFileAsync(filePath, DeckLoader::CockatriceFormat, false); + deckLoader->loadFromFileAsync(filePath, DeckLoader::getFormatFromName(filePath), false); bannerCardDisplayWidget = new DeckPreviewCardPictureWidget(this); @@ -88,6 +88,12 @@ void DeckPreviewWidget::setFilePath(const QString &_filePath) filePath = _filePath; } +void DeckPreviewWidget::refreshBannerCardText() +{ + bannerCardDisplayWidget->setOverlayText( + deckLoader->getName().isEmpty() ? QFileInfo(deckLoader->getLastFileName()).fileName() : deckLoader->getName()); +} + void DeckPreviewWidget::imageClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance) { Q_UNUSED(instance); 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 689c94b18..91a2060a3 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 @@ -33,6 +33,7 @@ signals: public slots: void setFilePath(const QString &filePath); + void refreshBannerCardText(); void imageClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance); void imageDoubleClickedEvent(QMouseEvent *event, DeckPreviewCardPictureWidget *instance); void initializeUi(bool deckLoadSuccess); diff --git a/cockatrice/src/deck/deck_loader.cpp b/cockatrice/src/deck/deck_loader.cpp index bfb8711ea..ed1d8fa71 100644 --- a/cockatrice/src/deck/deck_loader.cpp +++ b/cockatrice/src/deck/deck_loader.cpp @@ -6,6 +6,7 @@ #include "decklist.h" #include +#include #include #include #include @@ -481,6 +482,53 @@ void DeckLoader::saveToStream_DeckZoneCards(QTextStream &out, } } +bool DeckLoader::convertToCockatriceFormat(QString fileName) +{ + // Change the file extension to .cod + QFileInfo fileInfo(fileName); + QString newFileName = QDir::toNativeSeparators(fileInfo.path() + "/" + fileInfo.completeBaseName() + ".cod"); + + // Open the new file for writing + QFile file(newFileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qCWarning(DeckLoaderLog) << "Failed to open file for writing:" << newFileName; + return false; + } + + bool result = false; + + // Perform file modifications based on the detected format + switch (getFormatFromName(fileName)) { + case PlainTextFormat: + // Save in Cockatrice's native format + result = saveToFile_Native(&file); + break; + case CockatriceFormat: + qCInfo(DeckLoaderLog) << "File is already in Cockatrice format. No conversion needed."; + result = true; + break; + default: + qCWarning(DeckLoaderLog) << "Unsupported file format for conversion:" << fileName; + result = false; + break; + } + + file.close(); + + // Delete the old file if conversion was successful + if (result) { + if (!QFile::remove(fileName)) { + qCWarning(DeckLoaderLog) << "Failed to delete original file:" << fileName; + } else { + qCInfo(DeckLoaderLog) << "Original file deleted successfully:" << fileName; + } + lastFileName = newFileName; + lastFileFormat = CockatriceFormat; + } + + return result; +} + QString DeckLoader::getCardZoneFromName(QString cardName, QString currentZoneName) { CardInfoPtr card = CardDatabaseManager::getInstance()->getCard(cardName); diff --git a/cockatrice/src/deck/deck_loader.h b/cockatrice/src/deck/deck_loader.h index 0e9dfa374..d9863ac3e 100644 --- a/cockatrice/src/deck/deck_loader.h +++ b/cockatrice/src/deck/deck_loader.h @@ -59,6 +59,7 @@ public: // overload bool saveToStream_Plain(QTextStream &out, bool addComments = true, bool addSetNameAndNumber = true); + bool convertToCockatriceFormat(QString fileName); protected: void saveToStream_DeckHeader(QTextStream &out); diff --git a/cockatrice/src/dialogs/dlg_convert_deck_to_cod_format.cpp b/cockatrice/src/dialogs/dlg_convert_deck_to_cod_format.cpp new file mode 100644 index 000000000..198fa259b --- /dev/null +++ b/cockatrice/src/dialogs/dlg_convert_deck_to_cod_format.cpp @@ -0,0 +1,40 @@ +#include "dlg_convert_deck_to_cod_format.h" + +#include +#include +#include +#include + +DialogConvertDeckToCodFormat::DialogConvertDeckToCodFormat(QWidget *parent) : QDialog(parent) +{ + layout = new QVBoxLayout(this); + label = new QLabel(); + layout->addWidget(label); + + dontAskAgainCheckbox = new QCheckBox(this); + layout->addWidget(dontAskAgainCheckbox); + + buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + layout->addWidget(buttonBox); + + connect(buttonBox, &QDialogButtonBox::accepted, this, [this]() { accept(); }); + + connect(buttonBox, &QDialogButtonBox::rejected, this, [this]() { reject(); }); + + setLayout(layout); + retranslateUi(); +} + +void DialogConvertDeckToCodFormat::retranslateUi() +{ + setWindowTitle(tr("Deck Format Conversion")); + label->setText( + tr("You tried to add a tag to a .txt format deck.\n Tags can only be added to .cod format decks.\n Do " + "you want to convert the deck to the .cod format?")); + dontAskAgainCheckbox->setText(tr("Remember and automatically apply choice in the future")); +} + +bool DialogConvertDeckToCodFormat::dontAskAgain() const +{ + return dontAskAgainCheckbox->isChecked(); +} diff --git a/cockatrice/src/dialogs/dlg_convert_deck_to_cod_format.h b/cockatrice/src/dialogs/dlg_convert_deck_to_cod_format.h new file mode 100644 index 000000000..88a19591e --- /dev/null +++ b/cockatrice/src/dialogs/dlg_convert_deck_to_cod_format.h @@ -0,0 +1,29 @@ +#ifndef DIALOG_CONVERT_DECK_TO_COD_FORMAT_H +#define DIALOG_CONVERT_DECK_TO_COD_FORMAT_H + +#include +#include +#include +#include +#include + +class DialogConvertDeckToCodFormat : public QDialog +{ + Q_OBJECT + +public: + explicit DialogConvertDeckToCodFormat(QWidget *parent); + void retranslateUi(); + + bool dontAskAgain() const; + +private: + QVBoxLayout *layout; + QLabel *label; + QCheckBox *dontAskAgainCheckbox; + QDialogButtonBox *buttonBox; + + Q_DISABLE_COPY(DialogConvertDeckToCodFormat) +}; + +#endif // DIALOG_CONVERT_DECK_TO_COD_FORMAT_H diff --git a/cockatrice/src/dialogs/dlg_settings.cpp b/cockatrice/src/dialogs/dlg_settings.cpp index 496aa7ee6..8010eaf39 100644 --- a/cockatrice/src/dialogs/dlg_settings.cpp +++ b/cockatrice/src/dialogs/dlg_settings.cpp @@ -573,6 +573,15 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() connect(&useTearOffMenusCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), [](const QT_STATE_CHANGED_T state) { SettingsCache::instance().setUseTearOffMenus(state == Qt::Checked); }); + visualDeckStoragePromptForConversionCheckBox.setChecked( + SettingsCache::instance().getVisualDeckStoragePromptForConversion()); + connect(&visualDeckStoragePromptForConversionCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setVisualDeckStoragePromptForConversion); + + visualDeckStorageAlwaysConvertCheckBox.setChecked(SettingsCache::instance().getVisualDeckStorageAlwaysConvert()); + connect(&visualDeckStorageAlwaysConvertCheckBox, &QCheckBox::QT_STATE_CHANGED, &SettingsCache::instance(), + &SettingsCache::setVisualDeckStorageAlwaysConvert); + auto *generalGrid = new QGridLayout; generalGrid->addWidget(&doubleClickToPlayCheckBox, 0, 0); generalGrid->addWidget(&clickPlaysAllSelectedCheckBox, 1, 0); @@ -580,6 +589,8 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() generalGrid->addWidget(&closeEmptyCardViewCheckBox, 3, 0); generalGrid->addWidget(&annotateTokensCheckBox, 4, 0); generalGrid->addWidget(&useTearOffMenusCheckBox, 5, 0); + generalGrid->addWidget(&visualDeckStoragePromptForConversionCheckBox, 6, 0); + generalGrid->addWidget(&visualDeckStorageAlwaysConvertCheckBox, 7, 0); generalGroupBox = new QGroupBox; generalGroupBox->setLayout(generalGrid); @@ -658,6 +669,8 @@ void UserInterfaceSettingsPage::retranslateUi() closeEmptyCardViewCheckBox.setText(tr("Close card view window when last card is removed")); annotateTokensCheckBox.setText(tr("Annotate card text on tokens")); useTearOffMenusCheckBox.setText(tr("Use tear-off menus, allowing right click menus to persist on screen")); + visualDeckStoragePromptForConversionCheckBox.setText(tr("Prompt before converting .txt decks to .cod format")); + visualDeckStorageAlwaysConvertCheckBox.setText(tr("Always convert if not prompted")); notificationsGroupBox->setTitle(tr("Notifications settings")); notificationsEnabledCheckBox.setText(tr("Enable notifications in taskbar")); specNotificationsEnabledCheckBox.setText(tr("Notify in the taskbar for game events while you are spectating")); diff --git a/cockatrice/src/dialogs/dlg_settings.h b/cockatrice/src/dialogs/dlg_settings.h index 489d798e9..ad9429cd2 100644 --- a/cockatrice/src/dialogs/dlg_settings.h +++ b/cockatrice/src/dialogs/dlg_settings.h @@ -142,6 +142,8 @@ private: QCheckBox closeEmptyCardViewCheckBox; QCheckBox annotateTokensCheckBox; QCheckBox useTearOffMenusCheckBox; + QCheckBox visualDeckStoragePromptForConversionCheckBox; + QCheckBox visualDeckStorageAlwaysConvertCheckBox; QCheckBox tapAnimationCheckBox; QCheckBox openDeckInNewTabCheckBox; QLabel rewindBufferingMsLabel; diff --git a/cockatrice/src/settings/cache_settings.cpp b/cockatrice/src/settings/cache_settings.cpp index 329c08a20..5bf114706 100644 --- a/cockatrice/src/settings/cache_settings.cpp +++ b/cockatrice/src/settings/cache_settings.cpp @@ -274,6 +274,9 @@ SettingsCache::SettingsCache() settings->value("interface/visualdeckstoragedrawunusedcoloridentities", true).toBool(); visualDeckStorageUnusedColorIdentitiesOpacity = settings->value("interface/visualdeckstorageunusedcoloridentitiesopacity", 15).toInt(); + visualDeckStoragePromptForConversion = + settings->value("interface/visualdeckstoragepromptforconversion", true).toBool(); + visualDeckStorageAlwaysConvert = settings->value("interface/visualdeckstoragealwaysconvert", false).toBool(); horizontalHand = settings->value("hand/horizontal", true).toBool(); invertVerticalCoordinate = settings->value("table/invert_vertical", false).toBool(); minPlayersForMultiColumnLayout = settings->value("interface/min_players_multicolumn", 4).toInt(); @@ -699,6 +702,18 @@ void SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity(int _visual visualDeckStorageUnusedColorIdentitiesOpacity); } +void SettingsCache::setVisualDeckStoragePromptForConversion(QT_STATE_CHANGED_T _visualDeckStoragePromptForConversion) +{ + visualDeckStoragePromptForConversion = _visualDeckStoragePromptForConversion; + settings->setValue("interface/visualdeckstoragepromptforconversion", visualDeckStoragePromptForConversion); +} + +void SettingsCache::setVisualDeckStorageAlwaysConvert(QT_STATE_CHANGED_T _visualDeckStorageAlwaysConvert) +{ + visualDeckStorageAlwaysConvert = _visualDeckStorageAlwaysConvert; + settings->setValue("interface/visualdeckstoragealwaysconvert", visualDeckStorageAlwaysConvert); +} + void SettingsCache::setHorizontalHand(QT_STATE_CHANGED_T _horizontalHand) { horizontalHand = static_cast(_horizontalHand); diff --git a/cockatrice/src/settings/cache_settings.h b/cockatrice/src/settings/cache_settings.h index 7b5649904..50c970a6b 100644 --- a/cockatrice/src/settings/cache_settings.h +++ b/cockatrice/src/settings/cache_settings.h @@ -133,6 +133,8 @@ private: int visualDeckStorageCardSize; bool visualDeckStorageDrawUnusedColorIdentities; int visualDeckStorageUnusedColorIdentitiesOpacity; + bool visualDeckStoragePromptForConversion; + bool visualDeckStorageAlwaysConvert; bool horizontalHand; bool invertVerticalCoordinate; int minPlayersForMultiColumnLayout; @@ -424,6 +426,14 @@ public: { return visualDeckStorageUnusedColorIdentitiesOpacity; } + bool getVisualDeckStoragePromptForConversion() const + { + return visualDeckStoragePromptForConversion; + } + bool getVisualDeckStorageAlwaysConvert() const + { + return visualDeckStorageAlwaysConvert; + } bool getHorizontalHand() const { return horizontalHand; @@ -748,6 +758,8 @@ public slots: void setVisualDeckStorageCardSize(int _visualDeckStorageCardSize); void setVisualDeckStorageDrawUnusedColorIdentities(QT_STATE_CHANGED_T _visualDeckStorageDrawUnusedColorIdentities); void setVisualDeckStorageUnusedColorIdentitiesOpacity(int _visualDeckStorageUnusedColorIdentitiesOpacity); + void setVisualDeckStoragePromptForConversion(QT_STATE_CHANGED_T _visualDeckStoragePromptForConversion); + void setVisualDeckStorageAlwaysConvert(QT_STATE_CHANGED_T _visualDeckStorageAlwaysConvert); void setHorizontalHand(QT_STATE_CHANGED_T _horizontalHand); void setInvertVerticalCoordinate(QT_STATE_CHANGED_T _invertVerticalCoordinate); void setMinPlayersForMultiColumnLayout(int _minPlayersForMultiColumnLayout); diff --git a/dbconverter/src/mocks.cpp b/dbconverter/src/mocks.cpp index 00336305a..a34dcc191 100644 --- a/dbconverter/src/mocks.cpp +++ b/dbconverter/src/mocks.cpp @@ -222,6 +222,13 @@ void SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity( int /* _visualDeckStorageUnusedColorIdentitiesOpacity */) { } +void SettingsCache::setVisualDeckStoragePromptForConversion( + QT_STATE_CHANGED_T /* _visualDeckStoragePromptForConversion */) +{ +} +void SettingsCache::setVisualDeckStorageAlwaysConvert(QT_STATE_CHANGED_T /* _visualDeckStorageAlwaysConvert */) +{ +} void SettingsCache::setHorizontalHand(QT_STATE_CHANGED_T /* _horizontalHand */) { } diff --git a/tests/carddatabase/mocks.cpp b/tests/carddatabase/mocks.cpp index 358b8361d..40e8fc5a9 100644 --- a/tests/carddatabase/mocks.cpp +++ b/tests/carddatabase/mocks.cpp @@ -226,6 +226,13 @@ void SettingsCache::setVisualDeckStorageUnusedColorIdentitiesOpacity( int /* _visualDeckStorageUnusedColorIdentitiesOpacity */) { } +void SettingsCache::setVisualDeckStoragePromptForConversion( + QT_STATE_CHANGED_T /* _visualDeckStoragePromptForConversion */) +{ +} +void SettingsCache::setVisualDeckStorageAlwaysConvert(QT_STATE_CHANGED_T /* _visualDeckStorageAlwaysConvert */) +{ +} void SettingsCache::setHorizontalHand(QT_STATE_CHANGED_T /* _horizontalHand */) { }