#include "batchexportdialog.h" #include "settings.h" #include "exportdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include BatchExportDialog::BatchExportDialog(QWidget *parent) : QDialog(parent) , mExporting(false) , mCancelled(false) , mItemTree(nullptr) , mSelectionLabel(nullptr) , mOutputPath(nullptr) , mBrowseButton(nullptr) , mPreserveStructure(nullptr) , mConflictCombo(nullptr) , mImageFormatCombo(nullptr) , mAudioFormatCombo(nullptr) , mProgressBar(nullptr) , mProgressLabel(nullptr) , mButtonBox(nullptr) , mExportButton(nullptr) , mCancelButton(nullptr) { setWindowTitle("Batch Export"); setMinimumSize(600, 500); setModal(true); setupUI(); } void BatchExportDialog::setupUI() { QVBoxLayout* mainLayout = new QVBoxLayout(this); // Item tree with checkboxes mItemTree = new QTreeWidget(this); mItemTree->setHeaderLabels({"Name", "Type", "Size"}); mItemTree->setRootIsDecorated(true); mItemTree->setAlternatingRowColors(true); mItemTree->header()->setStretchLastSection(false); mItemTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); mItemTree->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); mItemTree->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); mainLayout->addWidget(mItemTree, 1); connect(mItemTree, &QTreeWidget::itemChanged, this, &BatchExportDialog::onItemChanged); // Selection buttons QHBoxLayout* selectionLayout = new QHBoxLayout(); QPushButton* selectAllBtn = new QPushButton("Select All", this); QPushButton* selectNoneBtn = new QPushButton("Select None", this); QPushButton* selectImagesBtn = new QPushButton("Images Only", this); QPushButton* selectAudioBtn = new QPushButton("Audio Only", this); mSelectionLabel = new QLabel("Selected: 0 of 0", this); selectionLayout->addWidget(selectAllBtn); selectionLayout->addWidget(selectNoneBtn); selectionLayout->addWidget(selectImagesBtn); selectionLayout->addWidget(selectAudioBtn); selectionLayout->addStretch(); selectionLayout->addWidget(mSelectionLabel); mainLayout->addLayout(selectionLayout); connect(selectAllBtn, &QPushButton::clicked, this, &BatchExportDialog::onSelectAll); connect(selectNoneBtn, &QPushButton::clicked, this, &BatchExportDialog::onSelectNone); connect(selectImagesBtn, &QPushButton::clicked, this, &BatchExportDialog::onSelectImages); connect(selectAudioBtn, &QPushButton::clicked, this, &BatchExportDialog::onSelectAudio); // Output options group QGroupBox* optionsGroup = new QGroupBox("Export Options", this); QGridLayout* optionsLayout = new QGridLayout(optionsGroup); // Output directory optionsLayout->addWidget(new QLabel("Output:", this), 0, 0); mOutputPath = new QLineEdit(this); mOutputPath->setText(Settings::instance().batchExportDirectory()); if (mOutputPath->text().isEmpty()) { mOutputPath->setText(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); } optionsLayout->addWidget(mOutputPath, 0, 1); mBrowseButton = new QPushButton("Browse...", this); optionsLayout->addWidget(mBrowseButton, 0, 2); connect(mBrowseButton, &QPushButton::clicked, this, &BatchExportDialog::onBrowseClicked); // Preserve structure mPreserveStructure = new QCheckBox("Preserve folder structure", this); mPreserveStructure->setChecked(Settings::instance().batchExportPreserveStructure()); optionsLayout->addWidget(mPreserveStructure, 1, 0, 1, 3); // Conflict resolution optionsLayout->addWidget(new QLabel("Conflicts:", this), 2, 0); mConflictCombo = new QComboBox(this); mConflictCombo->addItem("Append number", "number"); mConflictCombo->addItem("Overwrite", "overwrite"); mConflictCombo->addItem("Skip", "skip"); QString savedResolution = Settings::instance().batchExportConflictResolution(); for (int i = 0; i < mConflictCombo->count(); ++i) { if (mConflictCombo->itemData(i).toString() == savedResolution) { mConflictCombo->setCurrentIndex(i); break; } } optionsLayout->addWidget(mConflictCombo, 2, 1, 1, 2); // Format options optionsLayout->addWidget(new QLabel("Image format:", this), 3, 0); mImageFormatCombo = new QComboBox(this); mImageFormatCombo->addItem("PNG", "png"); mImageFormatCombo->addItem("JPG", "jpg"); mImageFormatCombo->addItem("BMP", "bmp"); mImageFormatCombo->addItem("TGA", "tga"); mImageFormatCombo->addItem("TIFF", "tiff"); QString savedImageFormat = Settings::instance().defaultImageExportFormat(); for (int i = 0; i < mImageFormatCombo->count(); ++i) { if (mImageFormatCombo->itemData(i).toString() == savedImageFormat) { mImageFormatCombo->setCurrentIndex(i); break; } } optionsLayout->addWidget(mImageFormatCombo, 3, 1, 1, 2); optionsLayout->addWidget(new QLabel("Audio format:", this), 4, 0); mAudioFormatCombo = new QComboBox(this); mAudioFormatCombo->addItem("WAV", "wav"); mAudioFormatCombo->addItem("MP3", "mp3"); mAudioFormatCombo->addItem("OGG", "ogg"); mAudioFormatCombo->addItem("FLAC", "flac"); QString savedAudioFormat = Settings::instance().defaultAudioExportFormat(); for (int i = 0; i < mAudioFormatCombo->count(); ++i) { if (mAudioFormatCombo->itemData(i).toString() == savedAudioFormat) { mAudioFormatCombo->setCurrentIndex(i); break; } } optionsLayout->addWidget(mAudioFormatCombo, 4, 1, 1, 2); mainLayout->addWidget(optionsGroup); // Progress section mProgressBar = new QProgressBar(this); mProgressBar->setVisible(false); mainLayout->addWidget(mProgressBar); mProgressLabel = new QLabel(this); mProgressLabel->setVisible(false); mainLayout->addWidget(mProgressLabel); // Dialog buttons mButtonBox = new QDialogButtonBox(this); mExportButton = mButtonBox->addButton("Export All", QDialogButtonBox::AcceptRole); mCancelButton = mButtonBox->addButton(QDialogButtonBox::Cancel); mainLayout->addWidget(mButtonBox); connect(mExportButton, &QPushButton::clicked, this, &BatchExportDialog::onExportClicked); connect(mCancelButton, &QPushButton::clicked, this, &QDialog::reject); } void BatchExportDialog::setItems(const QList& items) { mItems = items; populateTree(); updateSelectionCount(); // Update title with item count setWindowTitle(QString("Batch Export (%1 items)").arg(items.size())); } void BatchExportDialog::populateTree() { mItemTree->clear(); mItemTree->blockSignals(true); // Group items by path QMap folderItems; for (int i = 0; i < mItems.size(); ++i) { const BatchExportItem& item = mItems[i]; // Determine parent folder QString folder; QString name = item.name; int lastSlash = item.path.lastIndexOf('/'); if (lastSlash > 0) { folder = item.path.left(lastSlash); } QTreeWidgetItem* parent = nullptr; if (!folder.isEmpty()) { if (!folderItems.contains(folder)) { // Create folder item QTreeWidgetItem* folderItem = new QTreeWidgetItem(mItemTree); folderItem->setText(0, folder); folderItem->setCheckState(0, Qt::Checked); folderItem->setFlags(folderItem->flags() | Qt::ItemIsAutoTristate); folderItem->setData(0, Qt::UserRole, -1); // -1 indicates folder folderItems[folder] = folderItem; } parent = folderItems[folder]; } // Create item QTreeWidgetItem* treeItem = parent ? new QTreeWidgetItem(parent) : new QTreeWidgetItem(mItemTree); treeItem->setText(0, name); treeItem->setCheckState(0, item.selected ? Qt::Checked : Qt::Unchecked); treeItem->setData(0, Qt::UserRole, i); // Store index // Type column QString typeStr; switch (item.contentType) { case ExportDialog::Image: typeStr = "Image"; break; case ExportDialog::Audio: typeStr = "Audio"; break; case ExportDialog::Video: typeStr = "Video"; break; case ExportDialog::Text: typeStr = "Text"; break; default: typeStr = "Binary"; break; } treeItem->setText(1, typeStr); // Size column qint64 size = item.data.size(); QString sizeStr; if (size >= 1024 * 1024) { sizeStr = QString("%1 MB").arg(size / (1024.0 * 1024.0), 0, 'f', 1); } else if (size >= 1024) { sizeStr = QString("%1 KB").arg(size / 1024.0, 0, 'f', 0); } else { sizeStr = QString("%1 B").arg(size); } treeItem->setText(2, sizeStr); } mItemTree->expandAll(); mItemTree->blockSignals(false); } QString BatchExportDialog::outputDirectory() const { return mOutputPath->text(); } bool BatchExportDialog::preserveStructure() const { return mPreserveStructure->isChecked(); } QString BatchExportDialog::conflictResolution() const { return mConflictCombo->currentData().toString(); } QString BatchExportDialog::imageFormat() const { return mImageFormatCombo->currentData().toString(); } QString BatchExportDialog::audioFormat() const { return mAudioFormatCombo->currentData().toString(); } QList BatchExportDialog::selectedItems() const { QList selected; std::function collectSelected = [&](QTreeWidgetItem* item) { int idx = item->data(0, Qt::UserRole).toInt(); if (idx >= 0 && idx < mItems.size()) { if (item->checkState(0) == Qt::Checked) { selected.append(mItems[idx]); } } for (int i = 0; i < item->childCount(); ++i) { collectSelected(item->child(i)); } }; for (int i = 0; i < mItemTree->topLevelItemCount(); ++i) { collectSelected(mItemTree->topLevelItem(i)); } return selected; } void BatchExportDialog::updateProgress(int current, int total, const QString& currentItem) { mProgressBar->setMaximum(total); mProgressBar->setValue(current); mProgressLabel->setText(QString("Exporting: %1").arg(currentItem)); } void BatchExportDialog::onExportCompleted(int succeeded, int failed, int skipped) { mExporting = false; mProgressBar->setVisible(false); mProgressLabel->setVisible(false); mExportButton->setEnabled(true); mExportButton->setText("Export All"); // Show results mProgressLabel->setText(QString("Completed: %1 succeeded, %2 failed, %3 skipped") .arg(succeeded).arg(failed).arg(skipped)); mProgressLabel->setVisible(true); if (failed == 0) { accept(); } } void BatchExportDialog::onSelectAll() { mItemTree->blockSignals(true); for (int i = 0; i < mItemTree->topLevelItemCount(); ++i) { QTreeWidgetItem* item = mItemTree->topLevelItem(i); item->setCheckState(0, Qt::Checked); } mItemTree->blockSignals(false); updateSelectionCount(); } void BatchExportDialog::onSelectNone() { mItemTree->blockSignals(true); for (int i = 0; i < mItemTree->topLevelItemCount(); ++i) { QTreeWidgetItem* item = mItemTree->topLevelItem(i); item->setCheckState(0, Qt::Unchecked); } mItemTree->blockSignals(false); updateSelectionCount(); } void BatchExportDialog::onSelectImages() { mItemTree->blockSignals(true); std::function setByType = [&](QTreeWidgetItem* item) { int idx = item->data(0, Qt::UserRole).toInt(); if (idx >= 0 && idx < mItems.size()) { bool isImage = mItems[idx].contentType == ExportDialog::Image; item->setCheckState(0, isImage ? Qt::Checked : Qt::Unchecked); } for (int i = 0; i < item->childCount(); ++i) { setByType(item->child(i)); } }; for (int i = 0; i < mItemTree->topLevelItemCount(); ++i) { setByType(mItemTree->topLevelItem(i)); } mItemTree->blockSignals(false); updateSelectionCount(); } void BatchExportDialog::onSelectAudio() { mItemTree->blockSignals(true); std::function setByType = [&](QTreeWidgetItem* item) { int idx = item->data(0, Qt::UserRole).toInt(); if (idx >= 0 && idx < mItems.size()) { bool isAudio = mItems[idx].contentType == ExportDialog::Audio; item->setCheckState(0, isAudio ? Qt::Checked : Qt::Unchecked); } for (int i = 0; i < item->childCount(); ++i) { setByType(item->child(i)); } }; for (int i = 0; i < mItemTree->topLevelItemCount(); ++i) { setByType(mItemTree->topLevelItem(i)); } mItemTree->blockSignals(false); updateSelectionCount(); } void BatchExportDialog::onBrowseClicked() { QString dir = QFileDialog::getExistingDirectory(this, "Select Output Directory", mOutputPath->text()); if (!dir.isEmpty()) { mOutputPath->setText(dir); } } void BatchExportDialog::onExportClicked() { if (mExporting) { mCancelled = true; return; } // Save settings Settings::instance().setBatchExportDirectory(mOutputPath->text()); Settings::instance().setBatchExportPreserveStructure(mPreserveStructure->isChecked()); Settings::instance().setBatchExportConflictResolution(mConflictCombo->currentData().toString()); // Start export mExporting = true; mCancelled = false; mExportButton->setText("Cancel"); mProgressBar->setVisible(true); mProgressBar->setValue(0); mProgressLabel->setVisible(true); // Signal acceptance - MainWindow will handle actual export accept(); } void BatchExportDialog::onItemChanged(QTreeWidgetItem* item, int column) { Q_UNUSED(item); Q_UNUSED(column); updateSelectionCount(); } void BatchExportDialog::updateSelectionCount() { int selected = 0; int total = 0; std::function count = [&](QTreeWidgetItem* item) { int idx = item->data(0, Qt::UserRole).toInt(); if (idx >= 0) { total++; if (item->checkState(0) == Qt::Checked) { selected++; } } for (int i = 0; i < item->childCount(); ++i) { count(item->child(i)); } }; for (int i = 0; i < mItemTree->topLevelItemCount(); ++i) { count(mItemTree->topLevelItem(i)); } mSelectionLabel->setText(QString("Selected: %1 of %2").arg(selected).arg(total)); mExportButton->setEnabled(selected > 0); } int BatchExportDialog::countByType(int contentType) const { int count = 0; for (const BatchExportItem& item : mItems) { if (item.contentType == contentType) { count++; } } return count; }