mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2025-12-07 21:30:28 -08:00
Compare commits
5 Commits
proper-pro
...
oracle-mem
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c0c8b416a | ||
|
|
65a3423009 | ||
|
|
4ddbc8d018 | ||
|
|
d4bf40694a | ||
|
|
ec98bcf95d |
@@ -154,10 +154,8 @@ void DeckEditorDeckDockWidget::createDeckDock()
|
||||
&DeckEditorDeckDockWidget::setBannerCard);
|
||||
bannerCardComboBox->setHidden(!SettingsCache::instance().getDeckEditorBannerCardComboBoxVisible());
|
||||
|
||||
deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckModel->getDeckList()->getTags());
|
||||
deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckModel->getDeckList());
|
||||
deckTagsDisplayWidget->setHidden(!SettingsCache::instance().getDeckEditorTagsWidgetVisible());
|
||||
connect(deckTagsDisplayWidget, &DeckPreviewDeckTagsDisplayWidget::tagsChanged, this,
|
||||
&DeckEditorDeckDockWidget::setTags);
|
||||
|
||||
activeGroupCriteriaLabel = new QLabel(this);
|
||||
|
||||
@@ -385,13 +383,6 @@ void DeckEditorDeckDockWidget::setBannerCard(int /* changedIndex */)
|
||||
emit deckModified();
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::setTags(const QStringList &tags)
|
||||
{
|
||||
deckModel->getDeckList()->setTags(tags);
|
||||
deckEditor->setModified(true);
|
||||
emit deckModified();
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::syncDeckListBannerCardWithComboBox()
|
||||
{
|
||||
auto [name, id] = bannerCardComboBox->currentData().value<QPair<QString, QString>>();
|
||||
@@ -460,7 +451,7 @@ void DeckEditorDeckDockWidget::syncDisplayWidgetsToModel()
|
||||
sortDeckModelToDeckView();
|
||||
expandAll();
|
||||
|
||||
deckTagsDisplayWidget->setTags(deckModel->getDeckList()->getTags());
|
||||
deckTagsDisplayWidget->setDeckList(deckModel->getDeckList());
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::sortDeckModelToDeckView()
|
||||
@@ -493,7 +484,7 @@ void DeckEditorDeckDockWidget::cleanDeck()
|
||||
emit deckModified();
|
||||
emit deckChanged();
|
||||
updateBannerCardComboBox();
|
||||
deckTagsDisplayWidget->setTags(deckModel->getDeckList()->getTags());
|
||||
deckTagsDisplayWidget->setDeckList(deckModel->getDeckList());
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::recursiveExpand(const QModelIndex &index)
|
||||
@@ -588,7 +579,7 @@ bool DeckEditorDeckDockWidget::swapCard(const QModelIndex ¤tIndex)
|
||||
QModelIndex newCardIndex = card ? deckModel->addCard(card, otherZoneName)
|
||||
// Third argument (true) says create the card no matter what, even if not in DB
|
||||
: deckModel->addPreferredPrintingCard(cardName, otherZoneName, true);
|
||||
recursiveExpand(proxy->mapFromSource(newCardIndex));
|
||||
recursiveExpand(proxy->mapToSource(newCardIndex));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -609,18 +600,8 @@ void DeckEditorDeckDockWidget::actDecrementCard(const ExactCard &card, QString z
|
||||
}
|
||||
|
||||
deckView->clearSelection();
|
||||
setCurrentProxyIndex(idx);
|
||||
offsetCountAtIndex(proxy->mapFromSource(idx), -1);
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::setCurrentProxyIndex(const QModelIndex &index)
|
||||
{
|
||||
deckView->setCurrentIndex(proxy->mapFromSource(index));
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::scrollToProxyIndex(const QModelIndex &index)
|
||||
{
|
||||
deckView->setCurrentIndex(proxy->mapFromSource(index));
|
||||
deckView->setCurrentIndex(proxy->mapToSource(idx));
|
||||
offsetCountAtIndex(idx, -1);
|
||||
}
|
||||
|
||||
void DeckEditorDeckDockWidget::actDecrementSelection()
|
||||
|
||||
@@ -70,8 +70,6 @@ public slots:
|
||||
void actRemoveCard();
|
||||
void offsetCountAtIndex(const QModelIndex &idx, int offset);
|
||||
void expandAll();
|
||||
void setCurrentProxyIndex(const QModelIndex &index);
|
||||
void scrollToProxyIndex(const QModelIndex &index);
|
||||
|
||||
signals:
|
||||
void nameChanged();
|
||||
@@ -114,7 +112,6 @@ private slots:
|
||||
void updateName(const QString &name);
|
||||
void updateComments();
|
||||
void setBannerCard(int);
|
||||
void setTags(const QStringList &tags);
|
||||
void syncDeckListBannerCardWithComboBox();
|
||||
void updateHash();
|
||||
void refreshShortcuts();
|
||||
|
||||
@@ -190,7 +190,7 @@ void CardAmountWidget::addPrinting(const QString &zone)
|
||||
newCardIndex = deckModel->findCard(rootCard.getName(), zone, rootCard.getPrinting().getUuid(),
|
||||
rootCard.getPrinting().getProperty("num"));
|
||||
|
||||
deckEditor->deckDockWidget->setCurrentProxyIndex(newCardIndex);
|
||||
deckView->setCurrentIndex(newCardIndex);
|
||||
deckView->setFocus(Qt::FocusReason::MouseFocusReason);
|
||||
deckEditor->setModified(true);
|
||||
}
|
||||
@@ -256,7 +256,7 @@ void CardAmountWidget::offsetCountAtIndex(const QModelIndex &idx, int offset)
|
||||
const int count = deckModel->data(numberIndex, Qt::EditRole).toInt();
|
||||
const int new_count = count + offset;
|
||||
|
||||
deckEditor->deckDockWidget->setCurrentProxyIndex(numberIndex);
|
||||
deckView->setCurrentIndex(numberIndex);
|
||||
|
||||
if (new_count <= 0) {
|
||||
deckModel->removeRow(idx.row(), idx.parent());
|
||||
|
||||
@@ -185,7 +185,7 @@ void PrintingSelector::selectCard(const int changeBy)
|
||||
}
|
||||
|
||||
if (nextIndex.isValid()) {
|
||||
deckEditor->deckDockWidget->setCurrentProxyIndex(nextIndex);
|
||||
deckView->setCurrentIndex(nextIndex);
|
||||
deckView->setFocus(Qt::FocusReason::MouseFocusReason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ void AbstractTabDeckEditor::addCardHelper(const ExactCard &card, QString zoneNam
|
||||
QModelIndex newCardIndex = deckDockWidget->deckModel->addCard(card, zoneName);
|
||||
deckDockWidget->expandAll();
|
||||
deckDockWidget->deckView->clearSelection();
|
||||
deckDockWidget->setCurrentProxyIndex(newCardIndex);
|
||||
deckDockWidget->deckView->setCurrentIndex(newCardIndex);
|
||||
setModified(true);
|
||||
|
||||
databaseDisplayDockWidget->searchEdit->setSelection(0, databaseDisplayDockWidget->searchEdit->text().length());
|
||||
|
||||
@@ -232,8 +232,8 @@ void TabDeckEditorVisual::processMainboardCardClick(QMouseEvent *event,
|
||||
return;
|
||||
} else {
|
||||
// Normal click = clear selection, select this, set current
|
||||
deckDockWidget->setCurrentProxyIndex(idx);
|
||||
deckDockWidget->scrollToProxyIndex(idx);
|
||||
deckDockWidget->deckView->setCurrentIndex(idx);
|
||||
deckDockWidget->deckView->scrollTo(idx);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,16 +18,6 @@ VisualDatabaseDisplayFilterSaveLoadWidget::VisualDatabaseDisplayFilterSaveLoadWi
|
||||
layout = new QVBoxLayout(this);
|
||||
setLayout(layout);
|
||||
|
||||
// Filter search input
|
||||
searchInput = new QLineEdit(this);
|
||||
layout->addWidget(searchInput);
|
||||
|
||||
connect(searchInput, &QLineEdit::textChanged, this, &VisualDatabaseDisplayFilterSaveLoadWidget::applySearchFilter);
|
||||
|
||||
// File list container
|
||||
fileListWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||
layout->addWidget(fileListWidget);
|
||||
|
||||
// Input for filter filename
|
||||
filenameInput = new QLineEdit(this);
|
||||
layout->addWidget(filenameInput);
|
||||
@@ -35,20 +25,18 @@ VisualDatabaseDisplayFilterSaveLoadWidget::VisualDatabaseDisplayFilterSaveLoadWi
|
||||
// Save button
|
||||
saveButton = new QPushButton(this);
|
||||
layout->addWidget(saveButton);
|
||||
// Disable save if empty
|
||||
saveButton->setEnabled(false);
|
||||
connect(filenameInput, &QLineEdit::textChanged, this,
|
||||
[this](const QString &text) { saveButton->setEnabled(!text.trimmed().isEmpty()); });
|
||||
|
||||
connect(saveButton, &QPushButton::clicked, this, &VisualDatabaseDisplayFilterSaveLoadWidget::saveFilter);
|
||||
|
||||
// File list container
|
||||
fileListWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||
layout->addWidget(fileListWidget);
|
||||
|
||||
refreshFilterList(); // Populate the file list on startup
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayFilterSaveLoadWidget::retranslateUi()
|
||||
{
|
||||
searchInput->setPlaceholderText(tr("Search filter..."));
|
||||
saveButton->setText(tr("Save Filter"));
|
||||
saveButton->setToolTip(tr("Save all currently applied filters to a file"));
|
||||
filenameInput->setPlaceholderText(tr("Enter filename..."));
|
||||
@@ -124,36 +112,42 @@ void VisualDatabaseDisplayFilterSaveLoadWidget::loadFilter(const QString &filena
|
||||
emit filterModel->layoutChanged();
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayFilterSaveLoadWidget::applySearchFilter(const QString &text)
|
||||
{
|
||||
fileListWidget->clearLayout();
|
||||
|
||||
QString filter = text.trimmed();
|
||||
QStringList filtered = allFilterFiles;
|
||||
|
||||
if (!filter.isEmpty()) {
|
||||
filtered = filtered.filter(QRegularExpression(filter, QRegularExpression::CaseInsensitiveOption));
|
||||
}
|
||||
|
||||
for (const QString &filename : filtered) {
|
||||
FilterDisplayWidget *filterWidget = new FilterDisplayWidget(this, filename, filterModel);
|
||||
fileListWidget->addWidget(filterWidget);
|
||||
|
||||
connect(filterWidget, &FilterDisplayWidget::filterLoadRequested, this,
|
||||
&VisualDatabaseDisplayFilterSaveLoadWidget::loadFilter);
|
||||
|
||||
connect(filterWidget, &FilterDisplayWidget::filterDeleted, this,
|
||||
&VisualDatabaseDisplayFilterSaveLoadWidget::refreshFilterList);
|
||||
}
|
||||
}
|
||||
|
||||
void VisualDatabaseDisplayFilterSaveLoadWidget::refreshFilterList()
|
||||
{
|
||||
fileListWidget->clearLayout();
|
||||
fileButtons.clear();
|
||||
// Clear existing widgets
|
||||
for (auto buttonPair : fileButtons) {
|
||||
buttonPair.first->deleteLater();
|
||||
buttonPair.second->deleteLater();
|
||||
}
|
||||
fileButtons.clear(); // Clear the list of buttons
|
||||
|
||||
// Refresh the filter file list
|
||||
QDir dir(SettingsCache::instance().getFiltersPath());
|
||||
allFilterFiles = dir.entryList({"*.json"}, QDir::Files, QDir::Name);
|
||||
QStringList filterFiles = dir.entryList(QStringList() << "*.json", QDir::Files, QDir::Name);
|
||||
|
||||
applySearchFilter(searchInput->text());
|
||||
// Loop through the filter files and create widgets for them
|
||||
for (const QString &filename : filterFiles) {
|
||||
bool alreadyAdded = false;
|
||||
|
||||
// Check if the widget for this filter file already exists to avoid duplicates
|
||||
for (const auto &pair : fileButtons) {
|
||||
if (pair.first->text() == filename) {
|
||||
alreadyAdded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!alreadyAdded) {
|
||||
// Create a new custom widget for the filter
|
||||
FilterDisplayWidget *filterWidget = new FilterDisplayWidget(this, filename, filterModel);
|
||||
fileListWidget->addWidget(filterWidget);
|
||||
|
||||
// Connect signals to handle loading and deletion
|
||||
connect(filterWidget, &FilterDisplayWidget::filterLoadRequested, this,
|
||||
&VisualDatabaseDisplayFilterSaveLoadWidget::loadFilter);
|
||||
connect(filterWidget, &FilterDisplayWidget::filterDeleted, this,
|
||||
&VisualDatabaseDisplayFilterSaveLoadWidget::refreshFilterList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ public:
|
||||
|
||||
void saveFilter();
|
||||
void loadFilter(const QString &filename);
|
||||
void applySearchFilter(const QString &text);
|
||||
void refreshFilterList();
|
||||
void deleteFilter(const QString &filename, QPushButton *deleteButton);
|
||||
|
||||
@@ -38,11 +37,9 @@ private:
|
||||
FilterTreeModel *filterModel;
|
||||
|
||||
QVBoxLayout *layout;
|
||||
QLineEdit *searchInput;
|
||||
FlowWidget *fileListWidget;
|
||||
QLineEdit *filenameInput;
|
||||
QPushButton *saveButton;
|
||||
QStringList allFilterFiles;
|
||||
FlowWidget *fileListWidget;
|
||||
|
||||
QMap<QString, QPair<QPushButton *, QPushButton *>> fileButtons;
|
||||
};
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
#include <QHBoxLayout>
|
||||
#include <QMessageBox>
|
||||
|
||||
DeckPreviewDeckTagsDisplayWidget::DeckPreviewDeckTagsDisplayWidget(QWidget *_parent, const QStringList &_tags)
|
||||
: QWidget(_parent), currentTags(_tags)
|
||||
DeckPreviewDeckTagsDisplayWidget::DeckPreviewDeckTagsDisplayWidget(QWidget *_parent, DeckList *_deckList)
|
||||
: QWidget(_parent), deckList(nullptr)
|
||||
{
|
||||
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
@@ -27,14 +27,16 @@ DeckPreviewDeckTagsDisplayWidget::DeckPreviewDeckTagsDisplayWidget(QWidget *_par
|
||||
|
||||
flowWidget = new FlowWidget(this, Qt::Horizontal, Qt::ScrollBarAlwaysOff, Qt::ScrollBarAsNeeded);
|
||||
|
||||
layout->addWidget(flowWidget);
|
||||
if (_deckList) {
|
||||
setDeckList(_deckList);
|
||||
}
|
||||
|
||||
refreshTags();
|
||||
layout->addWidget(flowWidget);
|
||||
}
|
||||
|
||||
void DeckPreviewDeckTagsDisplayWidget::setTags(const QStringList &_tags)
|
||||
void DeckPreviewDeckTagsDisplayWidget::setDeckList(DeckList *_deckList)
|
||||
{
|
||||
currentTags = _tags;
|
||||
deckList = _deckList;
|
||||
refreshTags();
|
||||
}
|
||||
|
||||
@@ -42,7 +44,7 @@ void DeckPreviewDeckTagsDisplayWidget::refreshTags()
|
||||
{
|
||||
flowWidget->clearLayout();
|
||||
|
||||
for (const QString &tag : currentTags) {
|
||||
for (const QString &tag : deckList->getTags()) {
|
||||
flowWidget->addWidget(new DeckPreviewTagDisplayWidget(this, tag));
|
||||
}
|
||||
|
||||
@@ -69,45 +71,7 @@ static QStringList getAllFiles(const QString &filePath)
|
||||
return allFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all tags that appear in the deck folder
|
||||
*/
|
||||
static QStringList findAllKnownTags()
|
||||
{
|
||||
QStringList allFiles = getAllFiles(SettingsCache::instance().getDeckPath());
|
||||
|
||||
QStringList knownTags;
|
||||
auto loader = DeckLoader(nullptr);
|
||||
for (const QString &file : allFiles) {
|
||||
loader.loadFromFile(file, DeckLoader::getFormatFromName(file), false);
|
||||
QStringList tags = loader.getDeckList()->getTags();
|
||||
knownTags.append(tags);
|
||||
knownTags.removeDuplicates();
|
||||
}
|
||||
|
||||
return knownTags;
|
||||
}
|
||||
|
||||
void DeckPreviewDeckTagsDisplayWidget::openTagEditDlg()
|
||||
{
|
||||
if (qobject_cast<DeckPreviewWidget *>(parentWidget())) {
|
||||
// If we're the child of a DeckPreviewWidget, then we need to handle conversion
|
||||
auto *deckPreviewWidget = qobject_cast<DeckPreviewWidget *>(parentWidget());
|
||||
|
||||
bool canAddTags = promptFileConversionIfRequired(deckPreviewWidget);
|
||||
|
||||
if (canAddTags) {
|
||||
QStringList knownTags = deckPreviewWidget->visualDeckStorageWidget->tagFilterWidget->getAllKnownTags();
|
||||
execTagDialog(knownTags);
|
||||
}
|
||||
} else {
|
||||
// If we're the child of an AbstractTabDeckEditor, then we don't bother with conversion
|
||||
QStringList knownTags = findAllKnownTags();
|
||||
execTagDialog(knownTags);
|
||||
}
|
||||
}
|
||||
|
||||
static bool confirmOverwriteIfExists(QWidget *parent, const QString &filePath)
|
||||
bool confirmOverwriteIfExists(QWidget *parent, const QString &filePath)
|
||||
{
|
||||
QFileInfo fileInfo(filePath);
|
||||
QString newFileName = QDir::toNativeSeparators(fileInfo.path() + "/" + fileInfo.completeBaseName() + ".cod");
|
||||
@@ -122,70 +86,98 @@ static bool confirmOverwriteIfExists(QWidget *parent, const QString &filePath)
|
||||
return true; // Safe to proceed
|
||||
}
|
||||
|
||||
static void convertFileToCockatriceFormat(DeckPreviewWidget *deckPreviewWidget)
|
||||
void DeckPreviewDeckTagsDisplayWidget::openTagEditDlg()
|
||||
{
|
||||
deckPreviewWidget->deckLoader->convertToCockatriceFormat(deckPreviewWidget->filePath);
|
||||
deckPreviewWidget->filePath = deckPreviewWidget->deckLoader->getLastLoadInfo().fileName;
|
||||
deckPreviewWidget->refreshBannerCardText();
|
||||
if (qobject_cast<DeckPreviewWidget *>(parentWidget())) {
|
||||
auto *deckPreviewWidget = qobject_cast<DeckPreviewWidget *>(parentWidget());
|
||||
QStringList knownTags = deckPreviewWidget->visualDeckStorageWidget->tagFilterWidget->getAllKnownTags();
|
||||
QStringList activeTags = deckList->getTags();
|
||||
|
||||
bool canAddTags = true;
|
||||
|
||||
if (DeckLoader::getFormatFromName(deckPreviewWidget->filePath) != DeckLoader::CockatriceFormat) {
|
||||
canAddTags = false;
|
||||
// Retrieve saved preference if the prompt is disabled
|
||||
if (!SettingsCache::instance().getVisualDeckStoragePromptForConversion()) {
|
||||
if (SettingsCache::instance().getVisualDeckStorageAlwaysConvert()) {
|
||||
|
||||
if (!confirmOverwriteIfExists(this, deckPreviewWidget->filePath))
|
||||
return;
|
||||
|
||||
deckPreviewWidget->deckLoader->convertToCockatriceFormat(deckPreviewWidget->filePath);
|
||||
deckPreviewWidget->filePath = deckPreviewWidget->deckLoader->getLastLoadInfo().fileName;
|
||||
deckPreviewWidget->refreshBannerCardText();
|
||||
canAddTags = true;
|
||||
}
|
||||
} else {
|
||||
// Show the dialog to the user
|
||||
DialogConvertDeckToCodFormat conversionDialog(parentWidget());
|
||||
if (conversionDialog.exec() == QDialog::Accepted) {
|
||||
|
||||
if (!confirmOverwriteIfExists(this, deckPreviewWidget->filePath))
|
||||
return;
|
||||
|
||||
deckPreviewWidget->deckLoader->convertToCockatriceFormat(deckPreviewWidget->filePath);
|
||||
deckPreviewWidget->filePath = deckPreviewWidget->deckLoader->getLastLoadInfo().fileName;
|
||||
deckPreviewWidget->refreshBannerCardText();
|
||||
canAddTags = true;
|
||||
|
||||
if (conversionDialog.dontAskAgain()) {
|
||||
SettingsCache::instance().setVisualDeckStoragePromptForConversion(false);
|
||||
SettingsCache::instance().setVisualDeckStorageAlwaysConvert(true);
|
||||
}
|
||||
} else {
|
||||
SettingsCache::instance().setVisualDeckStorageAlwaysConvert(false);
|
||||
|
||||
if (conversionDialog.dontAskAgain()) {
|
||||
SettingsCache::instance().setVisualDeckStoragePromptForConversion(false);
|
||||
} else {
|
||||
SettingsCache::instance().setVisualDeckStoragePromptForConversion(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (canAddTags) {
|
||||
DeckPreviewTagDialog dialog(knownTags, activeTags);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
QStringList updatedTags = dialog.getActiveTags();
|
||||
deckList->setTags(updatedTags);
|
||||
deckPreviewWidget->deckLoader->saveToFile(deckPreviewWidget->filePath, DeckLoader::CockatriceFormat);
|
||||
refreshTags();
|
||||
}
|
||||
}
|
||||
} else if (parentWidget()) {
|
||||
// If we're the child of an AbstractTabDeckEditor, we are buried under a ton of childWidgets in the
|
||||
// DeckInfoDock.
|
||||
QWidget *currentParent = parentWidget();
|
||||
while (currentParent) {
|
||||
if (qobject_cast<AbstractTabDeckEditor *>(currentParent)) {
|
||||
break;
|
||||
}
|
||||
currentParent = currentParent->parentWidget();
|
||||
}
|
||||
if (qobject_cast<AbstractTabDeckEditor *>(currentParent)) {
|
||||
auto *deckEditor = qobject_cast<AbstractTabDeckEditor *>(currentParent);
|
||||
QStringList knownTags;
|
||||
QStringList allFiles = getAllFiles(SettingsCache::instance().getDeckPath());
|
||||
DeckLoader loader(this);
|
||||
for (const QString &file : allFiles) {
|
||||
loader.loadFromFile(file, DeckLoader::getFormatFromName(file), false);
|
||||
QStringList tags = loader.getDeckList()->getTags();
|
||||
knownTags.append(tags);
|
||||
knownTags.removeDuplicates();
|
||||
}
|
||||
|
||||
QStringList activeTags = deckList->getTags();
|
||||
|
||||
DeckPreviewTagDialog dialog(knownTags, activeTags);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
QStringList updatedTags = dialog.getActiveTags();
|
||||
deckList->setTags(updatedTags);
|
||||
deckEditor->setModified(true);
|
||||
refreshTags();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the deck's file format supports tags.
|
||||
* If not, then prompt the user for file conversion.
|
||||
* @return whether the resulting file can support adding tags
|
||||
*/
|
||||
bool DeckPreviewDeckTagsDisplayWidget::promptFileConversionIfRequired(DeckPreviewWidget *deckPreviewWidget)
|
||||
{
|
||||
if (DeckLoader::getFormatFromName(deckPreviewWidget->filePath) == DeckLoader::CockatriceFormat) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Retrieve saved preference if the prompt is disabled
|
||||
if (!SettingsCache::instance().getVisualDeckStoragePromptForConversion()) {
|
||||
if (!SettingsCache::instance().getVisualDeckStorageAlwaysConvert()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!confirmOverwriteIfExists(this, deckPreviewWidget->filePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
convertFileToCockatriceFormat(deckPreviewWidget);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Show the dialog to the user
|
||||
DialogConvertDeckToCodFormat conversionDialog(parentWidget());
|
||||
if (conversionDialog.exec() != QDialog::Accepted) {
|
||||
SettingsCache::instance().setVisualDeckStoragePromptForConversion(!conversionDialog.dontAskAgain());
|
||||
SettingsCache::instance().setVisualDeckStorageAlwaysConvert(false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to convert file
|
||||
if (!confirmOverwriteIfExists(this, deckPreviewWidget->filePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
convertFileToCockatriceFormat(deckPreviewWidget);
|
||||
|
||||
if (conversionDialog.dontAskAgain()) {
|
||||
SettingsCache::instance().setVisualDeckStoragePromptForConversion(false);
|
||||
SettingsCache::instance().setVisualDeckStorageAlwaysConvert(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeckPreviewDeckTagsDisplayWidget::execTagDialog(const QStringList &knownTags)
|
||||
{
|
||||
DeckPreviewTagDialog dialog(knownTags, currentTags);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
QStringList updatedTags = dialog.getActiveTags();
|
||||
if (updatedTags != currentTags) {
|
||||
setTags(updatedTags);
|
||||
emit tagsChanged(updatedTags);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,31 +12,21 @@
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
inline bool confirmOverwriteIfExists(QWidget *parent, const QString &filePath);
|
||||
|
||||
class DeckPreviewWidget;
|
||||
class DeckPreviewDeckTagsDisplayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QStringList currentTags;
|
||||
FlowWidget *flowWidget;
|
||||
|
||||
public:
|
||||
explicit DeckPreviewDeckTagsDisplayWidget(QWidget *_parent, const QStringList &_tags);
|
||||
void setTags(const QStringList &_tags);
|
||||
explicit DeckPreviewDeckTagsDisplayWidget(QWidget *_parent, DeckList *_deckList);
|
||||
void setDeckList(DeckList *_deckList);
|
||||
void refreshTags();
|
||||
DeckList *deckList;
|
||||
FlowWidget *flowWidget;
|
||||
|
||||
public slots:
|
||||
void openTagEditDlg();
|
||||
|
||||
private:
|
||||
bool promptFileConversionIfRequired(DeckPreviewWidget *deckPreviewWidget);
|
||||
void execTagDialog(const QStringList &knownTags);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* Emitted when the tags have changed due to user interaction.
|
||||
* @param tags The new list of tags.
|
||||
*/
|
||||
void tagsChanged(const QStringList &tags);
|
||||
};
|
||||
#endif // DECK_PREVIEW_DECK_TAGS_DISPLAY_WIDGET_H
|
||||
|
||||
@@ -83,8 +83,7 @@ void DeckPreviewWidget::initializeUi(const bool deckLoadSuccess)
|
||||
setFilePath(deckLoader->getLastLoadInfo().fileName);
|
||||
|
||||
colorIdentityWidget = new ColorIdentityWidget(this, getColorIdentity());
|
||||
deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckLoader->getDeckList()->getTags());
|
||||
connect(deckTagsDisplayWidget, &DeckPreviewDeckTagsDisplayWidget::tagsChanged, this, &DeckPreviewWidget::setTags);
|
||||
deckTagsDisplayWidget = new DeckPreviewDeckTagsDisplayWidget(this, deckLoader->getDeckList());
|
||||
|
||||
bannerCardLabel = new QLabel(this);
|
||||
bannerCardLabel->setObjectName("bannerCardLabel");
|
||||
@@ -308,12 +307,6 @@ void DeckPreviewWidget::imageDoubleClickedEvent(QMouseEvent *event, DeckPreviewC
|
||||
emit deckLoadRequested(filePath);
|
||||
}
|
||||
|
||||
void DeckPreviewWidget::setTags(const QStringList &tags)
|
||||
{
|
||||
deckLoader->getDeckList()->setTags(tags);
|
||||
deckLoader->saveToFile(filePath, DeckLoader::CockatriceFormat);
|
||||
}
|
||||
|
||||
QMenu *DeckPreviewWidget::createRightClickMenu()
|
||||
{
|
||||
auto *menu = new QMenu(this);
|
||||
|
||||
@@ -72,8 +72,6 @@ private:
|
||||
void addSetBannerCardMenu(QMenu *menu);
|
||||
|
||||
private slots:
|
||||
void setTags(const QStringList &tags);
|
||||
|
||||
void actRenameDeck();
|
||||
void actRenameFile();
|
||||
void actDeleteFile();
|
||||
|
||||
@@ -6,12 +6,17 @@ set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
set(UTILITY_SOURCES libcockatrice/utility/expression.cpp libcockatrice/utility/levenshtein.cpp
|
||||
libcockatrice/utility/passwordhasher.cpp
|
||||
libcockatrice/utility/passwordhasher.cpp libcockatrice/utility/system_memory_querier.cpp
|
||||
)
|
||||
|
||||
set(UTILITY_HEADERS
|
||||
libcockatrice/utility/color.h libcockatrice/utility/expression.h libcockatrice/utility/levenshtein.h
|
||||
libcockatrice/utility/macros.h libcockatrice/utility/passwordhasher.h libcockatrice/utility/trice_limits.h
|
||||
libcockatrice/utility/color.h
|
||||
libcockatrice/utility/expression.h
|
||||
libcockatrice/utility/levenshtein.h
|
||||
libcockatrice/utility/macros.h
|
||||
libcockatrice/utility/passwordhasher.h
|
||||
libcockatrice/utility/system_memory_querier.h
|
||||
libcockatrice/utility/trice_limits.h
|
||||
)
|
||||
|
||||
add_library(libcockatrice_utility STATIC ${UTILITY_SOURCES} ${UTILITY_HEADERS})
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
#include "system_memory_querier.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#include <QFile>
|
||||
#include <QRegularExpression>
|
||||
#include <QTextStream>
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
#include <mach/mach.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
qulonglong SystemMemoryQuerier::totalMemoryBytes()
|
||||
{
|
||||
#if defined(Q_OS_WIN)
|
||||
|
||||
MEMORYSTATUSEX statex;
|
||||
statex.dwLength = sizeof(statex);
|
||||
if (GlobalMemoryStatusEx(&statex))
|
||||
return statex.ullTotalPhys;
|
||||
return 0;
|
||||
|
||||
#elif defined(Q_OS_LINUX)
|
||||
|
||||
QFile file("/proc/meminfo");
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return 0;
|
||||
|
||||
QTextStream in(&file);
|
||||
while (!in.atEnd()) {
|
||||
QString line = in.readLine();
|
||||
if (line.startsWith("MemTotal:")) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
QStringList parts = line.split(QRegExp("\\s+"), QString::SkipEmptyParts);
|
||||
#else
|
||||
QStringList parts = line.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
|
||||
#endif
|
||||
if (parts.size() >= 2)
|
||||
return parts[1].toULongLong() * 1024; // kB → bytes
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
#elif defined(Q_OS_MACOS)
|
||||
|
||||
int mib[2] = {CTL_HW, HW_MEMSIZE};
|
||||
qulonglong memsize = 0;
|
||||
size_t len = sizeof(memsize);
|
||||
|
||||
if (sysctl(mib, 2, &memsize, &len, nullptr, 0) == 0)
|
||||
return memsize;
|
||||
|
||||
return 0;
|
||||
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
qulonglong SystemMemoryQuerier::availableMemoryBytes()
|
||||
{
|
||||
#if defined(Q_OS_WIN)
|
||||
|
||||
MEMORYSTATUSEX statex;
|
||||
statex.dwLength = sizeof(statex);
|
||||
if (GlobalMemoryStatusEx(&statex))
|
||||
return statex.ullAvailPhys;
|
||||
return 0;
|
||||
|
||||
#elif defined(Q_OS_LINUX)
|
||||
|
||||
QFile file("/proc/meminfo");
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return 0;
|
||||
|
||||
QTextStream in(&file);
|
||||
while (!in.atEnd()) {
|
||||
QString line = in.readLine();
|
||||
if (line.startsWith("MemAvailable:")) {
|
||||
QStringList parts = line.split(QRegExp("\\s+"), Qt::SkipEmptyParts);
|
||||
if (parts.size() >= 2)
|
||||
return parts[1].toULongLong() * 1024;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
#elif defined(Q_OS_MACOS)
|
||||
|
||||
vm_size_t pageSize;
|
||||
host_page_size(mach_host_self(), &pageSize);
|
||||
|
||||
mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
|
||||
vm_statistics64_data_t vmstat;
|
||||
|
||||
if (host_statistics64(mach_host_self(), HOST_VM_INFO, (host_info64_t)&vmstat, &count) != KERN_SUCCESS)
|
||||
return 0;
|
||||
|
||||
qulonglong freeBytes = (qulonglong)vmstat.free_count * (qulonglong)pageSize;
|
||||
|
||||
return freeBytes;
|
||||
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef COCKATRICE_SYSTEM_MEMORY_QUERIER_H
|
||||
#define COCKATRICE_SYSTEM_MEMORY_QUERIER_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
class SystemMemoryQuerier
|
||||
{
|
||||
public:
|
||||
static qulonglong totalMemoryBytes();
|
||||
static qulonglong availableMemoryBytes();
|
||||
|
||||
static bool hasAtLeastGiB(int gib)
|
||||
{
|
||||
const qulonglong GiB = 1024ull * 1024ull * 1024ull;
|
||||
return totalMemoryBytes() >= (qulonglong)gib * GiB;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // COCKATRICE_SYSTEM_MEMORY_QUERIER_H
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "pages.h"
|
||||
|
||||
#include "client/settings/cache_settings.h"
|
||||
#include "libcockatrice/utility/system_memory_querier.h"
|
||||
#include "main.h"
|
||||
#include "oracleimporter.h"
|
||||
#include "oraclewizard.h"
|
||||
@@ -43,6 +44,7 @@
|
||||
#define MTGJSON_V4_URL_COMPONENT "mtgjson.com/files/"
|
||||
#define ALLSETS_URL_FALLBACK "https://www.mtgjson.com/api/v5/AllPrintings.json"
|
||||
#define MTGJSON_VERSION_URL "https://www.mtgjson.com/api/v5/Meta.json"
|
||||
#define MTGXML_URL "https://github.com/ebbit1q/mtgxml/releases/latest/download/mtg.xml.xz"
|
||||
|
||||
#ifdef HAS_LZMA
|
||||
#define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json.xz"
|
||||
@@ -185,6 +187,23 @@ void LoadSetsPage::initializePage()
|
||||
{
|
||||
urlLineEdit->setText(wizard()->settings->value("allsetsurl", ALLSETS_URL).toString());
|
||||
|
||||
// Memory check because Oracle parsing fails on systems with less than 4GiB for MTGJsons allPrintings.json
|
||||
if (!SystemMemoryQuerier::hasAtLeastGiB(4) && urlLineEdit->text() == ALLSETS_URL) {
|
||||
// Ask user whether to switch URL
|
||||
QMessageBox msgBox(
|
||||
QMessageBox::Question, tr("Low Memory Detected"),
|
||||
tr("Your system has less than 4 GiB of memory.\n"
|
||||
"Using the default AllPrintings URL may cause high memory usage and is known to fail as a result.\n\n"
|
||||
"Would you like to switch to the direct-download pre-parsed URL instead? (Updated daily)"),
|
||||
QMessageBox::Yes | QMessageBox::No, this);
|
||||
|
||||
msgBox.setDefaultButton(QMessageBox::Yes);
|
||||
|
||||
if (msgBox.exec() == QMessageBox::Yes) {
|
||||
urlLineEdit->setText(MTGXML_URL);
|
||||
}
|
||||
}
|
||||
|
||||
progressLabel->hide();
|
||||
progressBar->hide();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user