diff --git a/CMakeLists.txt b/CMakeLists.txt index 1541672a3..c3c2667e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -214,8 +214,8 @@ find_package(OpenSSL REQUIRED) target_link_libraries(${EXE} PRIVATE OpenSSL::SSL OpenSSL::Crypto) # Find Qt -find_package(Qt5 COMPONENTS Core Widgets Gui PrintSupport REQUIRED) -target_link_libraries(${EXE} PRIVATE Qt5::Core Qt5::Widgets Qt5::Gui Qt5::PrintSupport) +find_package(Qt5 COMPONENTS Core Widgets Gui PrintSupport Multimedia REQUIRED) +target_link_libraries(${EXE} PRIVATE Qt5::Core Qt5::Widgets Qt5::Gui Qt5::PrintSupport Qt5::Multimedia) if(MSVC) diff --git a/Components/Utility.cpp b/Components/Utility.cpp index c3d0aba15..e3724537f 100644 --- a/Components/Utility.cpp +++ b/Components/Utility.cpp @@ -4,40 +4,94 @@ #include #include #include +#include -ImageDialog::ImageDialog(QWidget* parent, QString xmlExtension, bool writer) : QFileDialog(parent, "Select image") { - QString fileExt("*." + xmlExtension + ".gmx"); - QString xmlName = xmlExtension; - xmlName[0] = xmlName[0].toUpper(); - - QStringList mimeTypeFilters; - const QByteArrayList supportedMimeTypes = - (writer) ? QImageWriter::supportedMimeTypes() : QImageReader::supportedMimeTypes(); - foreach (const QByteArray& mimeTypeName, supportedMimeTypes) { mimeTypeFilters.append(mimeTypeName); } - mimeTypeFilters.sort(Qt::CaseInsensitive); - - QMimeDatabase mimeDB; - QStringList allSupportedFormats; - for (const QString& mimeTypeFilter : mimeTypeFilters) { - QMimeType mimeType = mimeDB.mimeTypeForName(mimeTypeFilter); - if (mimeType.isValid()) { - allSupportedFormats.append(mimeType.globPatterns()); +struct MimeType { + MimeType() {} + MimeType(QString filter, QString desc) : fileFilter(filter), description(desc) {} + QString fileFilter; + QString description; +}; + +class MimeTypeList { +public: + MimeTypeList() {} + MimeTypeList(const QList& mimeTypes, const QList additionalMimeTypes) { + QStringList temp; + foreach(const QByteArray& mime, mimeTypes) temp.append(mime); + + QMimeDatabase mimeDB; + for (const QString& mimeTypeFilter : temp) { + QMimeType mimeType = mimeDB.mimeTypeForName(mimeTypeFilter); + if (mimeType.isValid()) { + nameFilters.append(mimeType.name() + " " + QString("(%1)").arg(mimeType.globPatterns().join(' '))); + filters.append(QString("%1").arg(mimeType.globPatterns().join(' '))); + } + } + + for (const MimeType& mime : additionalMimeTypes) { + filters.append(mime.fileFilter); } + + for (const MimeType& mime : additionalMimeTypes) { + nameFilters.append(mime.description); + } + + filters.prepend(QString("(%1)").arg(filters.join(' '))); + nameFilters.prepend("All Supported " + filters[0]); + nameFilters.append("All Files (*)"); } - allSupportedFormats.append(fileExt); - QString allSupportedFormatsFilter = QString("All supported formats (%1)").arg(allSupportedFormats.join(' ')); - setFileMode(QFileDialog::ExistingFile); - setMimeTypeFilters(mimeTypeFilters); - QStringList nameFilters = this->nameFilters(); + QStringList nameFilters; + QStringList filters; + QString allSupported; +}; + +static const QMap derp = { + {FileDialog_t::BackgroundLoad, MimeTypeList(QImageReader::supportedMimeTypes(), + { + MimeType("*.Background.gmx", "GMX Background (*.Background.gmx)"), + MimeType("*.bkg", "EGM Background (*.bkg)") + }) + }, + + {FileDialog_t::BackgroundSave, MimeTypeList(QImageWriter::supportedMimeTypes(), + { + MimeType("*.bkg", "EGM Background (*.bkg)") + }) + }, + + {FileDialog_t::SoundLoad, MimeTypeList({}, + { + MimeType("*.Sound.gmx", "GMX Sound (*.Sound.gmx)"), + MimeType("*.snd", "EGM Sound (*.snd)"), + MimeType("*.ogg", "OGG (*.ogg)"), + MimeType("*.flac", "FLAC (*.flac)"), + MimeType("*.mp3", "MP3 (*.mp3)"), + MimeType("*.mod", "MOD (*.mod)"), + MimeType("*.wav", "Wav (*.wav)"), + }) + }, + + {FileDialog_t::SoundSave, MimeTypeList({}, + { + MimeType("*.snd", "EGM Sound (*.snd)") + }) + } +}; + +FileDialog::FileDialog(QWidget* parent, FileDialog_t type, bool writer) : + QFileDialog(parent, "Select ") { if (writer) setAcceptMode(QFileDialog::AcceptSave); - else - nameFilters.append("GMX " + xmlName + " (" + fileExt + ")"); + else { + setAcceptMode(QFileDialog::AcceptOpen); + setFileMode(QFileDialog::ExistingFile); + } - nameFilters.prepend(allSupportedFormatsFilter); - nameFilters.prepend("All Files (*)"); - setNameFilters(nameFilters); - selectNameFilter(allSupportedFormatsFilter); + const MimeTypeList& mimes = derp[type]; + setMimeTypeFilters(mimes.filters); + setNameFilters(mimes.nameFilters); + selectNameFilter(mimes.allSupported); } diff --git a/Components/Utility.h b/Components/Utility.h index af79bf89d..e053fa393 100644 --- a/Components/Utility.h +++ b/Components/Utility.h @@ -5,7 +5,14 @@ #include -class ImageDialog : public QFileDialog { +enum FileDialog_t { + BackgroundLoad, + BackgroundSave, + SoundSave, + SoundLoad +}; + +class FileDialog : public QFileDialog { public: - ImageDialog(QWidget* parent, QString xmlExtension, bool writer = false); + FileDialog(QWidget* parent, FileDialog_t type, bool writer); }; diff --git a/Editors/BackgroundEditor.cpp b/Editors/BackgroundEditor.cpp index 657835918..75ed7107c 100644 --- a/Editors/BackgroundEditor.cpp +++ b/Editors/BackgroundEditor.cpp @@ -11,6 +11,7 @@ #include #include +#include using buffers::resources::Background; @@ -67,28 +68,30 @@ void BackgroundEditor::on_actionNewImage_triggered() { } void BackgroundEditor::on_actionLoadImage_triggered() { - ImageDialog* dialog = new ImageDialog(this, "background"); - dialog->exec(); + FileDialog* dialog = new FileDialog(this, FileDialog_t::BackgroundLoad, false); - if (dialog->selectedFiles().size() > 0) { + if (dialog->exec() && dialog->selectedFiles().size() > 0) { QString fName = dialog->selectedFiles()[0]; - Background* bkg = gmx::LoadBackground(fName.toStdString()); - if (bkg != nullptr) { - QString lastImage = GetModelData(Background::kImageFieldNumber).toString(); - ReplaceBuffer(bkg); - QString newImage = GetModelData(Background::kImageFieldNumber).toString(); - if (!ui->backgroundView->SetImage(newImage)) SetModelData(Background::kImageFieldNumber, lastImage); + if (fName.endsWith("Background.gmx")) { + Background* bkg = gmx::LoadBackground(fName.toStdString()); + if (bkg != nullptr) { + // QString lastData = GetModelData(Background::kImageFieldNumber).toString(); + ReplaceBuffer(bkg); + // QString newData = GetModelData(Background::kImageFieldNumber).toString(); + // TODO: Copy data into our egm and reset the path + // SetModelData(Background::kImageFieldNumber, lastData); + } else qDebug() << "Failed to load gmx Background"; } else { - if (ui->backgroundView->SetImage(fName)) SetModelData(Background::kImageFieldNumber, fName); + // TODO: Copy data into our egm + SetModelData(Background::kImageFieldNumber, fName); } } } void BackgroundEditor::on_actionSaveImage_triggered() { - ImageDialog* dialog = new ImageDialog(this, "background", true); - dialog->exec(); + FileDialog* dialog = new FileDialog(this, FileDialog_t::BackgroundSave, true); - if (dialog->selectedFiles().size() > 0) { + if (dialog->exec() && dialog->selectedFiles().size() > 0) { QString fName = dialog->selectedFiles()[0]; ui->backgroundView->WriteImage(fName, dialog->selectedMimeTypeFilter()); } @@ -96,5 +99,7 @@ void BackgroundEditor::on_actionSaveImage_triggered() { void BackgroundEditor::on_actionEditImage_triggered() { QString fName = GetModelData(Background::kImageFieldNumber).toString(); - QDesktopServices::openUrl(fName); //TODO: file watcher reload + QDesktopServices::openUrl(QUrl::fromLocalFile(fName)); + // TODO: file watcher reload + // TODO: editor settings } diff --git a/Editors/BaseEditor.h b/Editors/BaseEditor.h index 46cca7d4c..617684ac8 100644 --- a/Editors/BaseEditor.h +++ b/Editors/BaseEditor.h @@ -34,8 +34,8 @@ class BaseEditor : public QWidget { void OnSave(); protected: - ModelMapper *nodeMapper; - ModelMapper *resMapper; + ModelMapper* nodeMapper; + ModelMapper* resMapper; }; #endif // BASEEDTIOR_H diff --git a/Editors/SoundEditor.cpp b/Editors/SoundEditor.cpp index 589e2d293..fb4fc4bf2 100644 --- a/Editors/SoundEditor.cpp +++ b/Editors/SoundEditor.cpp @@ -1,9 +1,135 @@ #include "SoundEditor.h" #include "ui_SoundEditor.h" -SoundEditor::SoundEditor(ProtoModelPtr model, QWidget* parent) : BaseEditor(model, parent), ui(new Ui::SoundEditor) { +#include "gmx.h" + +#include "Components/ArtManager.h" +#include "Components/Utility.h" + +#include +#include +#include +#include + +SoundEditor::SoundEditor(ProtoModelPtr model, QWidget* parent) : + BaseEditor(model, parent), ui(new Ui::SoundEditor), mediaPlayer(new QMediaPlayer(this)), + playlist(new QMediaPlaylist(mediaPlayer)), userPaused(false) { ui->setupUi(this); + + nodeMapper->addMapping(ui->nameEdit, TreeNode::kNameFieldNumber); + nodeMapper->toFirst(); + + resMapper->addMapping(ui->volumeSpinBox, Sound::kVolumeFieldNumber); + resMapper->toFirst(); + + ui->volumeSlider->setValue(static_cast(ui->volumeSpinBox->value() * 100)); + connect(ui->saveButton, &QAbstractButton::pressed, this, &BaseEditor::OnSave); + + ProtoModelPtr soundModel = model->GetSubModel(TreeNode::kSoundFieldNumber); + playlist->setPlaybackMode(QMediaPlaylist::CurrentItemOnce); + playlist->addMedia(QUrl::fromLocalFile(soundModel->data(Sound::kDataFieldNumber).toString())); + mediaPlayer->setPlaylist(playlist); + // Update the signals every 50ms instead of Qt's default 1000ms to keep slider up to date + mediaPlayer->setNotifyInterval(50); + + connect(mediaPlayer, &QMediaPlayer::positionChanged, [=]() { + if (mediaPlayer->duration() > 0) { + int percent = static_cast(100*(static_cast(mediaPlayer->position()) / mediaPlayer->duration())); + ui->playbackSlider->setSliderPosition(percent); + } else ui->playbackSlider->setSliderPosition(0); + + QTime timestamp(0,0); + ui->playbackPositionLabel->setText(timestamp.addMSecs(static_cast(mediaPlayer->position())).toString()); + }); + + connect(mediaPlayer, &QMediaPlayer::mediaChanged, [=]() { + playlist->clear(); + playlist->addMedia(QUrl::fromLocalFile(soundModel->data(Sound::kDataFieldNumber).toString())); + }); + + connect(mediaPlayer, &QMediaPlayer::stateChanged, [=]() { + if (mediaPlayer->state() == QMediaPlayer::PausedState || mediaPlayer->state() == QMediaPlayer::StoppedState) + ui->playButton->setIcon(ArtManager::GetIcon(":/actions/play.png")); + else + ui->playButton->setIcon(ArtManager::GetIcon(":/actions/pause.png")); + }); + + playlist->addMedia(QUrl::fromLocalFile(soundModel->data(Sound::kDataFieldNumber).toString())); + mediaPlayer->setPlaylist(playlist); } SoundEditor::~SoundEditor() { delete ui; } + +void SoundEditor::on_playButton_clicked() { + if (mediaPlayer->state() == QMediaPlayer::PausedState || mediaPlayer->state() == QMediaPlayer::StoppedState) { + mediaPlayer->play(); + userPaused = false; + } else { + mediaPlayer->pause(); + userPaused = true; + } +} + +void SoundEditor::on_loopButton_clicked() { + if (playlist->playbackMode() == QMediaPlaylist::CurrentItemOnce) + playlist->setPlaybackMode(QMediaPlaylist::CurrentItemInLoop); + else + playlist->setPlaybackMode(QMediaPlaylist::CurrentItemOnce); +} + +void SoundEditor::on_playbackSlider_sliderPressed() { + if (mediaPlayer->state() == QMediaPlayer::PlayingState) mediaPlayer->pause(); +} + +void SoundEditor::on_playbackSlider_sliderReleased() { + mediaPlayer->setPosition(static_cast((ui->playbackSlider->value()/100.f)*mediaPlayer->duration())); + if (mediaPlayer->state() == QMediaPlayer::PausedState && !userPaused) mediaPlayer->play(); +} + +void SoundEditor::on_volumeSlider_sliderMoved(int position) { + ui->volumeSpinBox->setValue(position / 100.0); + mediaPlayer->setVolume(position); +} + +void SoundEditor::on_volumeSpinBox_valueChanged(double arg1) { + ui->volumeSlider->setValue(static_cast(arg1 * 100)); + mediaPlayer->setVolume(static_cast(arg1 * 100)); +} + +void SoundEditor::on_saveAsButton_clicked() { + // TODO: implement this when egm is done +} + +void SoundEditor::on_loadButton_clicked() { + FileDialog* dialog = new FileDialog(this, FileDialog_t::SoundLoad, false); + + if (dialog->exec() && dialog->selectedFiles().size() > 0) { + QString fName = dialog->selectedFiles()[0]; + if (fName.endsWith("Sound.gmx")) { + Sound* snd = gmx::LoadSound(fName.toStdString()); + if (snd != nullptr) { + // QString lastData = GetModelData(Sound::kDataFieldNumber).toString(); + ReplaceBuffer(snd); + // QString newData = GetModelData(Sound::kDataFieldNumber).toString(); + // TODO: Copy data into our egm and reset the path + // SetModelData(Sound::kDataFieldNumber, lastData); + } else qDebug() << "Failed to load gmx sound"; + } else { + // TODO: Copy data into our egm + SetModelData(Sound::kDataFieldNumber, fName); + emit mediaPlayer->mediaChanged(mediaPlayer->media()); + } + } +} + +void SoundEditor::on_editButton_clicked() { + QString fName = GetModelData(Sound::kDataFieldNumber).toString(); + QDesktopServices::openUrl(QUrl::fromLocalFile(fName)); + // TODO: file watcher reload + // TODO: editor settings +} + +void SoundEditor::on_stopButton_clicked() { + mediaPlayer->stop(); +} diff --git a/Editors/SoundEditor.h b/Editors/SoundEditor.h index 4e30b4b82..e077f3228 100644 --- a/Editors/SoundEditor.h +++ b/Editors/SoundEditor.h @@ -3,6 +3,9 @@ #include "BaseEditor.h" +#include +#include + namespace Ui { class SoundEditor; } @@ -14,8 +17,23 @@ class SoundEditor : public BaseEditor { explicit SoundEditor(ProtoModelPtr model, QWidget* parent); ~SoundEditor(); - private: +private slots: + void on_playButton_clicked(); + void on_loopButton_clicked(); + void on_playbackSlider_sliderPressed(); + void on_playbackSlider_sliderReleased(); + void on_volumeSlider_sliderMoved(int position); + void on_volumeSpinBox_valueChanged(double arg1); + void on_saveAsButton_clicked(); + void on_loadButton_clicked(); + void on_editButton_clicked(); + void on_stopButton_clicked(); + +private: Ui::SoundEditor* ui; + QMediaPlayer* mediaPlayer; + QMediaPlaylist *playlist; //it's only one song but Qt puts looping stuff here + bool userPaused; }; #endif // SOUNDEDITOR_H diff --git a/Editors/SoundEditor.ui b/Editors/SoundEditor.ui index 88790400c..105ff3b8b 100644 --- a/Editors/SoundEditor.ui +++ b/Editors/SoundEditor.ui @@ -143,7 +143,21 @@ - + + + Stop playing sound + + + &Stop + + + + :/actions/sound-stop.png:/actions/sound-stop.png + + + + + Loop Playback @@ -157,6 +171,9 @@ true + + false + @@ -200,6 +217,12 @@ 100 + + 0 + + + 0 + 100 @@ -231,6 +254,9 @@ 1.000000000000000 + + 0.010000000000000 + 1.000000000000000 @@ -267,12 +293,21 @@ 0 + + 0 + + + 0 + 20 Qt::Horizontal + + false + diff --git a/Images/actions/sound-stop.png b/Images/actions/sound-stop.png index caa140ccf..57012f056 100644 Binary files a/Images/actions/sound-stop.png and b/Images/actions/sound-stop.png differ diff --git a/RadialGM.pro b/RadialGM.pro index 97cca5a5d..a4a8a0780 100644 --- a/RadialGM.pro +++ b/RadialGM.pro @@ -4,7 +4,7 @@ # #------------------------------------------------- -QT += core gui printsupport +QT += core gui printsupport multimedia CONFIG += c++11 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets diff --git a/images.qrc b/images.qrc index be349ca2d..b7fffbcb1 100644 --- a/images.qrc +++ b/images.qrc @@ -37,6 +37,7 @@ Images/actions/loop.png Images/actions/pause.png Images/actions/play.png + Images/actions/sound-stop.png Images/actions/snap-to-grid.png Images/actions/print.png Images/actions/line-goto.png