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 b75ca213e..3e4a651d7 100644 --- a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.cpp +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.cpp @@ -1,5 +1,6 @@ #include "printing_selector.h" +#include "../../../../dialogs/dlg_select_set_for_cards.h" #include "../../../../settings/cache_settings.h" #include "../../picture_loader/picture_loader.h" #include "printing_selector_card_display_widget.h" @@ -87,6 +88,13 @@ PrintingSelector::PrintingSelector(QWidget *parent, AbstractTabDeckEditor *_deck sortAndOptionsLayout->addWidget(searchBar); sortAndOptionsLayout->addWidget(displayOptionsWidget); + searchAndSetLayout->addWidget(searchBar); + + selectSetForCardsButton = new QPushButton(this); + connect(selectSetForCardsButton, &QPushButton::clicked, this, &PrintingSelector::selectSetForCards); + searchAndSetLayout->addWidget(selectSetForCardsButton); + + layout->addLayout(searchAndSetLayout); layout->addWidget(sortAndOptionsContainer); @@ -268,3 +276,9 @@ void PrintingSelector::toggleVisibilityNavigationButtons(bool _state) { cardSelectionBar->setVisible(_state); } + +void PrintingSelector::selectSetForCards() +{ + DlgSelectSetForCards *setSelectionDialog = new DlgSelectSetForCards(nullptr, deckModel); + setSelectionDialog->show(); +} 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 515807831..70b5c17e4 100644 --- a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.h +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -36,6 +37,7 @@ public slots: void selectPreviousCard(); void selectNextCard(); void toggleVisibilityNavigationButtons(bool _state); + void selectSetForCards(); private slots: void printingsInDeckChanged(); @@ -48,7 +50,9 @@ private: QCheckBox *navigationCheckBox; QLabel *warningLabel; PrintingSelectorCardSortingWidget *sortToolBar; + QHBoxLayout *searchAndSetLayout; PrintingSelectorCardSearchWidget *searchBar; + QPushButton *selectSetForCardsButton; FlowWidget *flowWidget; CardSizeWidget *cardSizeWidget; PrintingSelectorCardSelectionWidget *cardSelectionBar; diff --git a/cockatrice/src/dialogs/dlg_select_set_for_cards.cpp b/cockatrice/src/dialogs/dlg_select_set_for_cards.cpp new file mode 100644 index 000000000..da2841124 --- /dev/null +++ b/cockatrice/src/dialogs/dlg_select_set_for_cards.cpp @@ -0,0 +1,438 @@ +#include "dlg_select_set_for_cards.h" + +#include "../client/ui/widgets/cards/card_info_picture_widget.h" +#include "../client/ui/widgets/general/layout_containers/flow_widget.h" +#include "../deck/deck_loader.h" +#include "../game/cards/card_database_manager.h" +#include "dlg_select_set_for_cards.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DlgSelectSetForCards::DlgSelectSetForCards(QWidget *parent, DeckListModel *_model) + : QDialog(parent), model(_model) +{ + setMinimumSize(500, 500); + setAcceptDrops(true); + + QVBoxLayout *mainLayout = new QVBoxLayout(this); + setLayout(mainLayout); + + QSplitter *splitter = new QSplitter(Qt::Vertical, this); + mainLayout->addWidget(splitter); + + // Top scroll area + scrollArea = new QScrollArea(this); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scrollArea->setWidgetResizable(true); + + listContainer = new QWidget(scrollArea); + listLayout = new QVBoxLayout(listContainer); + listContainer->setLayout(listLayout); + scrollArea->setWidget(listContainer); + + // Bottom section container + QWidget *bottomContainer = new QWidget(this); + QVBoxLayout *bottomLayout = new QVBoxLayout(bottomContainer); + bottomLayout->setContentsMargins(0, 0, 0, 0); + + uneditedCardsLabel = new QLabel("Unmodified Cards:", this); + uneditedCardsArea = new QScrollArea(this); + uneditedCardsArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + uneditedCardsArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + uneditedCardsArea->setWidgetResizable(true); + + uneditedCardsFlowWidget = new FlowWidget(uneditedCardsArea, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded); + uneditedCardsArea->setWidget(uneditedCardsFlowWidget); + + bottomLayout->addWidget(uneditedCardsLabel); + bottomLayout->addWidget(uneditedCardsArea); + bottomContainer->setLayout(bottomLayout); + + // Add widgets to the splitter + splitter->addWidget(scrollArea); + splitter->addWidget(bottomContainer); + + sortSetsByCount(); + updateCardLists(); + + // Set stretch factors: top (2:3), bottom (1:3) + splitter->setStretchFactor(0, 3); + splitter->setStretchFactor(1, 1); +} + +void DlgSelectSetForCards::sortSetsByCount() +{ + QMap setsForCards = getSetsForCards(); + + // Convert map to a sortable list + QVector> setList; + for (auto it = setsForCards.begin(); it != setsForCards.end(); ++it) { + setList.append(qMakePair(it.key(), it.value())); + } + + // Sort in descending order of count + std::sort(setList.begin(), setList.end(), + [](const QPair &a, const QPair &b) { return a.second > b.second; }); + + // Clear existing entries + qDeleteAll(setEntries); + setEntries.clear(); + + // Populate with sorted entries + for (const auto &entry : setList) { + SetEntryWidget *widget = new SetEntryWidget(this, entry.first, entry.second); + listLayout->addWidget(widget); + setEntries.insert(entry.first, widget); + } +} + +QMap DlgSelectSetForCards::getSetsForCards() +{ + QMap setCounts; + if (!model) + return setCounts; + + DeckList *decklist = model->getDeckList(); + if (!decklist) + return setCounts; + + InnerDecklistNode *listRoot = decklist->getRoot(); + if (!listRoot) + return setCounts; + + for (auto *i : *listRoot) { + auto *countCurrentZone = dynamic_cast(i); + if (!countCurrentZone) + continue; + + for (auto *cardNode : *countCurrentZone) { + auto *currentCard = dynamic_cast(cardNode); + if (!currentCard) + continue; + + CardInfoPtr infoPtr = CardDatabaseManager::getInstance()->getCard(currentCard->getName()); + if (!infoPtr) + continue; + + CardInfoPerSetMap infoPerSetMap = infoPtr->getSets(); + for (auto it = infoPerSetMap.begin(); it != infoPerSetMap.end(); ++it) { + setCounts[it.key()]++; + } + } + } + return setCounts; +} + +void DlgSelectSetForCards::updateCardLists() +{ + QList entry_widgets; + for (int i = 0; i < listLayout->count(); ++i) { + QWidget *widget = listLayout->itemAt(i)->widget(); + if (auto entry = qobject_cast(widget)) { + entry_widgets.append(entry); + } + } + for (SetEntryWidget *entryWidget : entry_widgets) { + if (entryWidget->expanded) { + entryWidget->populateCardList(); + } + } + + uneditedCardsFlowWidget->clearLayout(); + + QStringList selectedCards; + for (SetEntryWidget *entryWidget : entry_widgets) { + if (entryWidget->isChecked()) { + QStringList cardsInSet = entryWidget->getAllCardsForSet(); + for (QString cardInSet : cardsInSet) { + selectedCards.append(cardInSet); + } + } + } + selectedCards.removeDuplicates(); + + DeckList *decklist = model->getDeckList(); + if (!decklist) + return; + + InnerDecklistNode *listRoot = decklist->getRoot(); + if (!listRoot) + return; + + for (auto *i : *listRoot) { + auto *countCurrentZone = dynamic_cast(i); + if (!countCurrentZone) + continue; + + for (auto *cardNode : *countCurrentZone) { + auto *currentCard = dynamic_cast(cardNode); + if (!currentCard) + continue; + + if (!selectedCards.contains(currentCard->getName())) { + CardInfoPtr infoPtr = CardDatabaseManager::getInstance()->getCard(currentCard->getName()); + CardInfoPictureWidget *picture_widget = new CardInfoPictureWidget(uneditedCardsFlowWidget); + picture_widget->setCard(infoPtr); + uneditedCardsFlowWidget->addWidget(picture_widget); + } + } + } +} + + + +void DlgSelectSetForCards::dragEnterEvent(QDragEnterEvent *event) +{ + if (event->mimeData()->hasFormat("application/x-setentrywidget")) { + event->acceptProposedAction(); + } +} + +void DlgSelectSetForCards::dropEvent(QDropEvent *event) +{ + QByteArray itemData = event->mimeData()->data("application/x-setentrywidget"); + QString draggedSetName = QString::fromUtf8(itemData); + + int dropIndex = -1; + for (int i = 0; i < listLayout->count(); ++i) { + QWidget *widget = listLayout->itemAt(i)->widget(); + if (widget && widget->geometry().contains(event->position().toPoint())) { + dropIndex = i; + break; + } + } + + if (dropIndex != -1) { + // Find the dragged widget + SetEntryWidget *draggedWidget = setEntries.value(draggedSetName, nullptr); + if (draggedWidget) { + listLayout->removeWidget(draggedWidget); + listLayout->insertWidget(dropIndex, draggedWidget); + } + } + + event->acceptProposedAction(); +} + + +SetEntryWidget::SetEntryWidget(DlgSelectSetForCards *_parent, const QString &_setName, int count) + : QWidget(_parent), parent(_parent), setName(_setName), expanded(false) +{ + layout = new QVBoxLayout(this); + setLayout(layout); + + QHBoxLayout *headerLayout = new QHBoxLayout(); + checkBox = new QCheckBox(setName, this); + connect(checkBox, &QCheckBox::checkStateChanged, parent, &DlgSelectSetForCards::updateCardLists); + expandButton = new QPushButton("+", this); + countLabel = new QLabel(QString::number(count), this); + + connect(expandButton, &QPushButton::clicked, this, &SetEntryWidget::toggleExpansion); + + headerLayout->addWidget(checkBox); + headerLayout->addWidget(countLabel); + headerLayout->addWidget(expandButton); + layout->addLayout(headerLayout); + + possibleCardsLabel = new QLabel(this); + possibleCardsLabel->setText("Unselected cards in set:"); + possibleCardsLabel->hide(); + + cardListContainer = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAlwaysOff); + cardListContainer->hide(); + + alreadySelectedCardsLabel = new QLabel(this); + alreadySelectedCardsLabel->setText("Cards in set already selected in higher priority set:"); + alreadySelectedCardsLabel->hide(); + + alreadySelectedCardListContainer = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAlwaysOff); + alreadySelectedCardListContainer->hide(); + + layout->addWidget(possibleCardsLabel); + layout->addWidget(cardListContainer); + layout->addWidget(alreadySelectedCardsLabel); + layout->addWidget(alreadySelectedCardListContainer); + setAttribute(Qt::WA_DeleteOnClose, false); +} + +void SetEntryWidget::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + QDrag *drag = new QDrag(this); + QMimeData *mimeData = new QMimeData; + + mimeData->setData("application/x-setentrywidget", setName.toUtf8()); + drag->setMimeData(mimeData); + + // Create a drag preview (snapshot of the widget) + QPixmap pixmap(size()); + pixmap.fill(Qt::transparent); // Ensure transparency + + QPainter painter(&pixmap); + this->render(&painter); + painter.end(); + + drag->setPixmap(pixmap); + drag->setHotSpot(event->position().toPoint()); // Keeps the cursor aligned + + drag->exec(Qt::MoveAction); + } +} + + +void SetEntryWidget::mouseMoveEvent(QMouseEvent *event) { + if (!(event->buttons() & Qt::LeftButton)) + return; + + // Start a drag operation + QDrag *drag = new QDrag(this); + QMimeData *mimeData = new QMimeData(); + mimeData->setText("SetEntryWidget Data"); // Customize with relevant data + drag->setMimeData(mimeData); + + // Create a drag preview of the widget + QPixmap pixmap(size()); + pixmap.fill(Qt::transparent); + + // Render the widget onto the pixmap + render(&pixmap); + + // Optionally, apply transparency to make it look like a drag effect + QPixmap transparentPixmap = pixmap; + QPainter painter(&transparentPixmap); + painter.setCompositionMode(QPainter::CompositionMode_DestinationIn); + painter.fillRect(transparentPixmap.rect(), QColor(0, 0, 0, 128)); // Semi-transparent effect + painter.end(); + + // Set the drag pixmap + drag->setPixmap(transparentPixmap); + drag->setHotSpot(event->pos()); // Ensure the drag starts from where the user clicked + + // Start the drag operation + drag->exec(Qt::MoveAction); +} + + +bool SetEntryWidget::isChecked() const +{ + return checkBox->isChecked(); +} + +void SetEntryWidget::toggleExpansion() +{ + expanded = !expanded; + possibleCardsLabel->setVisible(expanded); + cardListContainer->setVisible(expanded); + alreadySelectedCardsLabel->setVisible(expanded); + alreadySelectedCardListContainer->setVisible(expanded); + expandButton->setText(expanded ? "-" : "+"); + + parent->updateCardLists(); +} + +QStringList SetEntryWidget::getAllCardsForSet() +{ + QStringList list; + QMap setCards = parent->getCardsForSets(); + if (setCards.contains(setName)) { + for (const QString &cardName : setCards[setName]) { + list << cardName; + } + } + return list; +} + +void SetEntryWidget::populateCardList() +{ + cardListContainer->clearLayout(); + alreadySelectedCardListContainer->clearLayout(); + + QStringList possibleCards = getAllCardsForSet(); + QList entry_widgets; + for (int i = 0; i < parent->listLayout->count(); ++i) { + QWidget *widget = parent->listLayout->itemAt(i)->widget(); + if (auto entry = qobject_cast(widget)) { + entry_widgets.append(entry); + } + } + + for (SetEntryWidget *entryWidget : entry_widgets) { + if (entryWidget == this) { + break; + } + if (entryWidget->isChecked()) { + QStringList alreadyDoneCards = entryWidget->getAllCardsForSet(); + for (const QString &cardName : alreadyDoneCards) { + possibleCards.removeAll(cardName); + } + } + } + + for (const QString &cardName : possibleCards) { + CardInfoPictureWidget *picture_widget = new CardInfoPictureWidget(cardListContainer); + picture_widget->setCard(CardDatabaseManager::getInstance()->getCardByNameAndProviderId( + cardName, + CardDatabaseManager::getInstance()->getSpecificSetForCard(cardName, setName, nullptr).getProperty("uuid"))); + cardListContainer->addWidget(picture_widget); + } + + QStringList unusedCards = getAllCardsForSet(); + for (const QString &cardName : possibleCards) { + unusedCards.removeAll(cardName); + } + + for (const QString &cardName : unusedCards) { + CardInfoPictureWidget *picture_widget = new CardInfoPictureWidget(alreadySelectedCardListContainer); + picture_widget->setCard(CardDatabaseManager::getInstance()->getCardByNameAndProviderId( + cardName, + CardDatabaseManager::getInstance()->getSpecificSetForCard(cardName, setName, nullptr).getProperty("uuid"))); + alreadySelectedCardListContainer->addWidget(picture_widget); + } +} + +QMap DlgSelectSetForCards::getCardsForSets() +{ + QMap setCards; + if (!model) + return setCards; + + DeckList *decklist = model->getDeckList(); + if (!decklist) + return setCards; + + InnerDecklistNode *listRoot = decklist->getRoot(); + if (!listRoot) + return setCards; + + for (auto *i : *listRoot) { + auto *countCurrentZone = dynamic_cast(i); + if (!countCurrentZone) + continue; + + for (auto *cardNode : *countCurrentZone) { + auto *currentCard = dynamic_cast(cardNode); + if (!currentCard) + continue; + + CardInfoPtr infoPtr = CardDatabaseManager::getInstance()->getCard(currentCard->getName()); + if (!infoPtr) + continue; + + CardInfoPerSetMap infoPerSetMap = infoPtr->getSets(); + for (auto it = infoPerSetMap.begin(); it != infoPerSetMap.end(); ++it) { + setCards[it.key()].append(currentCard->getName()); + } + } + } + return setCards; +} diff --git a/cockatrice/src/dialogs/dlg_select_set_for_cards.h b/cockatrice/src/dialogs/dlg_select_set_for_cards.h new file mode 100644 index 000000000..57e02b0ff --- /dev/null +++ b/cockatrice/src/dialogs/dlg_select_set_for_cards.h @@ -0,0 +1,76 @@ +#ifndef DLG_SELECT_SET_FOR_CARDS_H +#define DLG_SELECT_SET_FOR_CARDS_H + +#include "../client/ui/widgets/general/layout_containers/flow_widget.h" +#include "../deck/deck_list_model.h" + +#include +#include +#include +#include +#include +#include + +class QCheckBox; +class SetEntryWidget; // Forward declaration + +class DlgSelectSetForCards : public QDialog +{ + Q_OBJECT + +public: + explicit DlgSelectSetForCards(QWidget *parent, DeckListModel *_model); + void sortSetsByCount(); + QMap getCardsForSets(); + QVBoxLayout *listLayout; + +public slots: + void updateCardLists(); + void dragEnterEvent(QDragEnterEvent *event) override; + void dropEvent(QDropEvent *event) override; + +private: + QVBoxLayout *layout; + QScrollArea *scrollArea; + QScrollArea *uneditedCardsArea; + FlowWidget *uneditedCardsFlowWidget; + QLabel *uneditedCardsLabel; + QWidget *listContainer; + QListWidget *listWidget; + DeckListModel *model; + QMap setEntries; + + QMap getSetsForCards(); + void updateCardAvailability(); +}; + +class SetEntryWidget : public QWidget +{ + Q_OBJECT + +public: + explicit SetEntryWidget(DlgSelectSetForCards *parent, const QString &setName, int count); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void toggleExpansion(); + QStringList getAllCardsForSet(); + void populateCardList(); + void updateCardState(bool checked); + bool isChecked() const; + DlgSelectSetForCards *parent; + QString setName; + bool expanded; + +private: + QVBoxLayout *layout; + QCheckBox *checkBox; + QPushButton *expandButton; + QLabel *countLabel; + QLabel *possibleCardsLabel; + FlowWidget *cardListContainer; + QLabel *alreadySelectedCardsLabel; + FlowWidget *alreadySelectedCardListContainer; + QVBoxLayout *cardListLayout; +}; + +#endif // DLG_SELECT_SET_FOR_CARDS_H \ No newline at end of file diff --git a/cockatrice/src/game/cards/card_database.cpp b/cockatrice/src/game/cards/card_database.cpp index 396a32a10..504054bbe 100644 --- a/cockatrice/src/game/cards/card_database.cpp +++ b/cockatrice/src/game/cards/card_database.cpp @@ -384,8 +384,13 @@ CardInfoPerSet CardDatabase::getSpecificSetForCard(const QString &cardName, for (const auto &cardInfoPerSetList : setMap) { for (auto &cardInfoForSet : cardInfoPerSetList) { - if (cardInfoForSet.getPtr()->getShortName() == setShortName) { - if (cardInfoForSet.getProperty("num") == collectorNumber || collectorNumber.isEmpty()) { + if (collectorNumber != nullptr) { + if (cardInfoForSet.getPtr()->getShortName() == setShortName && + cardInfoForSet.getProperty("num") == collectorNumber) { + return cardInfoForSet; + } + } else { + if (cardInfoForSet.getPtr()->getShortName() == setShortName) { return cardInfoForSet; } }