Implement a unified export system for extracting data from parsed files: ExportManager (singleton): - Centralized export handling for all content types - Content type detection (image, audio, video, text, binary) - Batch export support with progress tracking Format-specific export dialogs: - ImageExportDialog: PNG, JPEG, BMP, TGA with quality options - AudioExportDialog: WAV, MP3, OGG with FFmpeg integration - BinaryExportDialog: Raw data export with optional decompression - BatchExportDialog: Recursive export with filtering options - Base ExportDialog class for common functionality Settings additions: - FFmpeg path configuration with auto-detection - Search common install locations and PATH Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
222 lines
6.6 KiB
C++
222 lines
6.6 KiB
C++
#include "exportdialog.h"
|
|
#include "exportmanager.h"
|
|
#include "settings.h"
|
|
|
|
#include <QVBoxLayout>
|
|
#include <QHBoxLayout>
|
|
#include <QGridLayout>
|
|
#include <QComboBox>
|
|
#include <QLabel>
|
|
#include <QLineEdit>
|
|
#include <QPushButton>
|
|
#include <QCheckBox>
|
|
#include <QDialogButtonBox>
|
|
#include <QGroupBox>
|
|
#include <QFileDialog>
|
|
#include <QFileInfo>
|
|
#include <QStandardPaths>
|
|
|
|
ExportDialog::ExportDialog(ContentType type, QWidget *parent)
|
|
: QDialog(parent)
|
|
, mContentType(type)
|
|
, mPreviewContainer(nullptr)
|
|
, mOptionsContainer(nullptr)
|
|
, mFormatCombo(nullptr)
|
|
, mOutputPath(nullptr)
|
|
, mBrowseButton(nullptr)
|
|
, mButtonBox(nullptr)
|
|
, mRememberSettings(nullptr)
|
|
, mInfoLabel(nullptr)
|
|
{
|
|
// Set dialog title based on content type
|
|
QString title;
|
|
switch (type) {
|
|
case Image: title = "Export Image"; break;
|
|
case Audio: title = "Export Audio"; break;
|
|
case Video: title = "Export Video"; break;
|
|
case Text: title = "Export Text"; break;
|
|
case Binary: title = "Export Data"; break;
|
|
}
|
|
setWindowTitle(title);
|
|
|
|
setMinimumWidth(500);
|
|
setModal(true);
|
|
|
|
setupCommonUI();
|
|
}
|
|
|
|
void ExportDialog::setupCommonUI()
|
|
{
|
|
mMainLayout = new QVBoxLayout(this);
|
|
|
|
// Content area: preview on left, options on right
|
|
mContentLayout = new QHBoxLayout();
|
|
|
|
// Left side: preview container
|
|
mLeftLayout = new QVBoxLayout();
|
|
mPreviewContainer = new QWidget(this);
|
|
mPreviewContainer->setMinimumSize(256, 256);
|
|
mPreviewContainer->setMaximumSize(300, 300);
|
|
mPreviewContainer->setStyleSheet("background-color: #1e1e1e; border: 1px solid #3e3e3e;");
|
|
mLeftLayout->addWidget(mPreviewContainer);
|
|
|
|
// Info label below preview
|
|
mInfoLabel = new QLabel(this);
|
|
mInfoLabel->setAlignment(Qt::AlignCenter);
|
|
mLeftLayout->addWidget(mInfoLabel);
|
|
mLeftLayout->addStretch();
|
|
|
|
mContentLayout->addLayout(mLeftLayout);
|
|
|
|
// Right side: format and options
|
|
mRightLayout = new QVBoxLayout();
|
|
|
|
// Format selection
|
|
QHBoxLayout* formatLayout = new QHBoxLayout();
|
|
QLabel* formatLabel = new QLabel("Format:", this);
|
|
mFormatCombo = new QComboBox(this);
|
|
formatLayout->addWidget(formatLabel);
|
|
formatLayout->addWidget(mFormatCombo, 1);
|
|
mRightLayout->addLayout(formatLayout);
|
|
|
|
// Options container (subclasses add format-specific options here)
|
|
mOptionsContainer = new QGroupBox("Options", this);
|
|
QVBoxLayout* optionsLayout = new QVBoxLayout(mOptionsContainer);
|
|
optionsLayout->setContentsMargins(8, 8, 8, 8);
|
|
mRightLayout->addWidget(mOptionsContainer);
|
|
|
|
mRightLayout->addStretch();
|
|
mContentLayout->addLayout(mRightLayout, 1);
|
|
|
|
mMainLayout->addLayout(mContentLayout);
|
|
|
|
// Output path
|
|
QHBoxLayout* pathLayout = new QHBoxLayout();
|
|
QLabel* outputLabel = new QLabel("Output:", this);
|
|
mOutputPath = new QLineEdit(this);
|
|
mBrowseButton = new QPushButton("Browse...", this);
|
|
pathLayout->addWidget(outputLabel);
|
|
pathLayout->addWidget(mOutputPath, 1);
|
|
pathLayout->addWidget(mBrowseButton);
|
|
mMainLayout->addLayout(pathLayout);
|
|
|
|
// Remember settings checkbox
|
|
mRememberSettings = new QCheckBox("Remember these settings", this);
|
|
mRememberSettings->setChecked(Settings::instance().exportRememberSettings());
|
|
mMainLayout->addWidget(mRememberSettings);
|
|
|
|
// Dialog buttons
|
|
mButtonBox = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok, this);
|
|
mButtonBox->button(QDialogButtonBox::Ok)->setText("Export");
|
|
mMainLayout->addWidget(mButtonBox);
|
|
|
|
// Connect signals
|
|
connect(mBrowseButton, &QPushButton::clicked, this, &ExportDialog::onBrowseClicked);
|
|
connect(mFormatCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
this, &ExportDialog::onFormatComboChanged);
|
|
connect(mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
|
connect(mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
|
}
|
|
|
|
void ExportDialog::setData(const QByteArray& data, const QString& suggestedName)
|
|
{
|
|
mData = data;
|
|
mSuggestedName = suggestedName;
|
|
|
|
// Set default output path
|
|
QString dir;
|
|
switch (mContentType) {
|
|
case Image:
|
|
dir = ExportManager::instance().lastExportDir("image");
|
|
if (dir.isEmpty()) dir = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
|
|
break;
|
|
case Audio:
|
|
dir = ExportManager::instance().lastExportDir("audio");
|
|
if (dir.isEmpty()) dir = QStandardPaths::writableLocation(QStandardPaths::MusicLocation);
|
|
break;
|
|
default:
|
|
dir = ExportManager::instance().lastExportDir("raw");
|
|
if (dir.isEmpty()) dir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
|
break;
|
|
}
|
|
|
|
// Get base name without extension
|
|
QString baseName = QFileInfo(suggestedName).baseName();
|
|
if (baseName.isEmpty()) baseName = "export";
|
|
|
|
// Get default format
|
|
QString format = mFormatCombo->currentText().toLower();
|
|
if (format.isEmpty() && mFormatCombo->count() > 0) {
|
|
format = mFormatCombo->itemText(0).toLower();
|
|
}
|
|
|
|
mOutputPath->setText(dir + "/" + baseName + "." + format);
|
|
|
|
// Update preview (subclass implementation)
|
|
updatePreview();
|
|
}
|
|
|
|
void ExportDialog::setMetadata(const QVariantMap& metadata)
|
|
{
|
|
mMetadata = metadata;
|
|
}
|
|
|
|
QString ExportDialog::selectedFormat() const
|
|
{
|
|
return mFormatCombo->currentText().toLower();
|
|
}
|
|
|
|
QString ExportDialog::outputPath() const
|
|
{
|
|
return mOutputPath->text();
|
|
}
|
|
|
|
bool ExportDialog::rememberSettings() const
|
|
{
|
|
return mRememberSettings->isChecked();
|
|
}
|
|
|
|
void ExportDialog::onFormatChanged(const QString& format)
|
|
{
|
|
// Update output path extension
|
|
QString path = mOutputPath->text();
|
|
QFileInfo fi(path);
|
|
QString newPath = fi.path() + "/" + fi.baseName() + "." + format.toLower();
|
|
mOutputPath->setText(newPath);
|
|
}
|
|
|
|
void ExportDialog::onBrowseClicked()
|
|
{
|
|
QString filter;
|
|
QString format = selectedFormat();
|
|
|
|
// Build filter based on content type
|
|
switch (mContentType) {
|
|
case Image:
|
|
filter = QString("%1 Files (*.%2)").arg(format.toUpper()).arg(format);
|
|
break;
|
|
case Audio:
|
|
filter = QString("%1 Files (*.%2)").arg(format.toUpper()).arg(format);
|
|
break;
|
|
case Text:
|
|
filter = "Text Files (*.txt);;All Files (*)";
|
|
break;
|
|
case Binary:
|
|
default:
|
|
filter = "Binary Files (*.bin *.dat);;All Files (*)";
|
|
break;
|
|
}
|
|
|
|
QString path = QFileDialog::getSaveFileName(this, "Export", mOutputPath->text(), filter);
|
|
if (!path.isEmpty()) {
|
|
mOutputPath->setText(path);
|
|
}
|
|
}
|
|
|
|
void ExportDialog::onFormatComboChanged(int index)
|
|
{
|
|
Q_UNUSED(index);
|
|
QString format = mFormatCombo->currentText();
|
|
onFormatChanged(format);
|
|
}
|