From ed658fa97f4d7f449beeacc6e4ef19275df0a3fe Mon Sep 17 00:00:00 2001 From: khoidauminh Date: Mon, 4 Nov 2024 13:28:35 +0700 Subject: [PATCH] Make Dialog remember last opened directories for different operations --- include/FileDialog.h | 35 ++++- plugins/GigPlayer/GigPlayer.cpp | 2 +- plugins/Patman/Patman.cpp | 2 +- plugins/Sf2Player/Sf2Player.cpp | 4 +- plugins/Vestige/Vestige.cpp | 2 +- plugins/VstBase/VstPlugin.cpp | 2 +- plugins/ZynAddSubFx/zynaddsubfx | 2 +- src/gui/MainWindow.cpp | 22 ++- src/gui/SampleLoader.cpp | 7 +- src/gui/clips/SampleClipView.cpp | 6 +- src/gui/instrument/InstrumentTrackWindow.cpp | 2 +- src/gui/modals/FileDialog.cpp | 137 ++++++++++--------- src/gui/modals/VersionedSaveDialog.cpp | 3 +- 13 files changed, 124 insertions(+), 102 deletions(-) diff --git a/include/FileDialog.h b/include/FileDialog.h index 67ef1d8eb76..b34bdc811e6 100644 --- a/include/FileDialog.h +++ b/include/FileDialog.h @@ -26,7 +26,6 @@ #define LMMS_GUI_FILE_DIALOG_H #include - #include "lmms_export.h" namespace lmms::gui @@ -37,20 +36,48 @@ class LMMS_EXPORT FileDialog : public QFileDialog { Q_OBJECT public: + + enum Operation { + OpBegin = 0, // This variant is purely for looping conveviences. + OpGeneric = OpBegin, + OpProject, + OpMidi, + OpPreset, + OpPlugin, + OpSample, + OpSong, + OpEnd, // This variant is purely for looping conveviences + }; + explicit FileDialog( QWidget *parent = 0, const QString &caption = QString(), const QString &directory = QString(), - const QString &filter = QString() ); + const QString &filter = QString(), + const Operation operation = OpGeneric); + + ~FileDialog(); static QString getExistingDirectory(QWidget *parent, const QString &caption, const QString &directory, - QFileDialog::Options options = QFileDialog::ShowDirsOnly); + QFileDialog::Options options = QFileDialog::ShowDirsOnly, + const Operation operation = OpGeneric); static QString getOpenFileName(QWidget *parent = 0, const QString &caption = QString(), const QString &directory = QString(), const QString &filter = QString(), - QString *selectedFilter = 0); + QString *selectedFilter = 0, + const Operation operation = OpGeneric); void clearSelection(); + +private: + Operation operation; + + static std::vector OperationPaths; + static bool OperationPathsReady; + static void prepareOperationPaths(); + // If existing path is not empty, getOperationPath returns it instead. + static QString getOperationPath(const enum Operation op, const QString& existing); + static void setOperationPath(const enum Operation op, const QString& path); }; diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index b72e30b3335..de524983887 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -1033,7 +1033,7 @@ void GigInstrumentView::showFileDialog() { auto k = castModel(); - FileDialog ofd( nullptr, tr( "Open GIG file" ) ); + FileDialog ofd( nullptr, tr( "Open GIG file" ), "", "", FileDialog::Operation::OpSample ); ofd.setFileMode( FileDialog::ExistingFiles ); QStringList types; diff --git a/plugins/Patman/Patman.cpp b/plugins/Patman/Patman.cpp index 4ecbe1bb83b..20f881b1d73 100644 --- a/plugins/Patman/Patman.cpp +++ b/plugins/Patman/Patman.cpp @@ -490,7 +490,7 @@ PatmanView::PatmanView( Instrument * _instrument, QWidget * _parent ) : void PatmanView::openFile() { - FileDialog ofd( nullptr, tr( "Open patch file" ) ); + FileDialog ofd( nullptr, tr( "Open patch file" ), "", "", FileDialog::Operation::OpSample); ofd.setFileMode( FileDialog::ExistingFiles ); QStringList types; diff --git a/plugins/Sf2Player/Sf2Player.cpp b/plugins/Sf2Player/Sf2Player.cpp index 13dab7b8abd..38fbe9bd1f6 100644 --- a/plugins/Sf2Player/Sf2Player.cpp +++ b/plugins/Sf2Player/Sf2Player.cpp @@ -211,7 +211,7 @@ Sf2Instrument::Sf2Instrument( InstrumentTrack * _instrument_track ) : connect( &m_chorusLevel, SIGNAL( dataChanged() ), this, SLOT( updateChorus() ) ); connect( &m_chorusSpeed, SIGNAL( dataChanged() ), this, SLOT( updateChorus() ) ); connect( &m_chorusDepth, SIGNAL( dataChanged() ), this, SLOT( updateChorus() ) ); - + // Microtuning connect(Engine::getSong(), &Song::scaleListChanged, this, &Sf2Instrument::updateTuning); connect(Engine::getSong(), &Song::keymapListChanged, this, &Sf2Instrument::updateTuning); @@ -1200,7 +1200,7 @@ void Sf2InstrumentView::showFileDialog() { auto k = castModel(); - FileDialog ofd( nullptr, tr( "Open SoundFont file" ) ); + FileDialog ofd( nullptr, tr( "Open SoundFont file" ), "", "", FileDialog::Operation::OpPreset ); ofd.setFileMode( FileDialog::ExistingFiles ); QStringList types; diff --git a/plugins/Vestige/Vestige.cpp b/plugins/Vestige/Vestige.cpp index 5a7d4afa059..3b3d4c098d8 100644 --- a/plugins/Vestige/Vestige.cpp +++ b/plugins/Vestige/Vestige.cpp @@ -665,7 +665,7 @@ void VestigeInstrumentView::modelChanged() void VestigeInstrumentView::openPlugin() { - FileDialog ofd( nullptr, tr( "Open VST plugin" ) ); + FileDialog ofd( nullptr, tr( "Open VST plugin" ) , "", "", FileDialog::Operation::OpPlugin); // set filters QStringList types; diff --git a/plugins/VstBase/VstPlugin.cpp b/plugins/VstBase/VstPlugin.cpp index 5dbe7a698ae..329d95d7aab 100644 --- a/plugins/VstBase/VstPlugin.cpp +++ b/plugins/VstBase/VstPlugin.cpp @@ -504,7 +504,7 @@ QWidget *VstPlugin::editor() void VstPlugin::openPreset() { - gui::FileDialog ofd(nullptr, tr("Open Preset"), "", tr("VST Plugin Preset (*.fxp *.fxb)")); + gui::FileDialog ofd(nullptr, tr("Open Preset"), "", tr("VST Plugin Preset (*.fxp *.fxb)"), gui::FileDialog::Operation::OpPlugin); ofd.setFileMode(gui::FileDialog::ExistingFiles); if (ofd.exec() == QDialog::Accepted && !ofd.selectedFiles().isEmpty()) { diff --git a/plugins/ZynAddSubFx/zynaddsubfx b/plugins/ZynAddSubFx/zynaddsubfx index d958c3668cc..aac04bc55c4 160000 --- a/plugins/ZynAddSubFx/zynaddsubfx +++ b/plugins/ZynAddSubFx/zynaddsubfx @@ -1 +1 @@ -Subproject commit d958c3668cc163805d581e97eb4d742168b6aad9 +Subproject commit aac04bc55c4114460009897686b597f98adfaa65 diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index c45ea14aca1..2422ba44e29 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -757,9 +757,8 @@ void MainWindow::openProject() { if( mayChangeProject(false) ) { - FileDialog ofd( this, tr( "Open Project" ), "", tr( "LMMS (*.mmp *.mmpz)" ) ); + FileDialog ofd( this, tr( "Open Project" ), "", tr( "LMMS (*.mmp *.mmpz)" ), FileDialog::Operation::OpProject ); - ofd.setDirectory( ConfigManager::inst()->userProjectsDir() ); ofd.setFileMode( FileDialog::ExistingFiles ); if( ofd.exec () == QDialog::Accepted && !ofd.selectedFiles().isEmpty() ) @@ -1087,7 +1086,7 @@ void MainWindow::updateViewMenu() ); m_viewMenu->addSeparator(); - + m_viewMenu->addAction(embed::getIconPixmap( "fullscreen" ), tr( "Fullscreen" ) + "\tF11", this, SLOT(toggleFullscreen()) @@ -1403,7 +1402,7 @@ void MainWindow::autoSave() void MainWindow::onExportProjectMidi() { - FileDialog efd( this ); + FileDialog efd( this, "Export Project", "", "", FileDialog::Operation::OpMidi); efd.setFileMode( FileDialog::AnyFile ); @@ -1419,7 +1418,6 @@ void MainWindow::onExportProjectMidi() } else { - efd.setDirectory( ConfigManager::inst()->userProjectsDir() ); base_filename = tr( "untitled" ); } efd.selectFile( base_filename + ".mid" ); @@ -1444,16 +1442,12 @@ void MainWindow::exportProject(bool multiExport) { QString const & projectFileName = Engine::getSong()->projectFileName(); - FileDialog efd( getGUI()->mainWindow() ); + FileDialog efd( getGUI()->mainWindow(), "", "", "", FileDialog::Operation::OpSong); if ( multiExport ) { efd.setFileMode( FileDialog::Directory); efd.setWindowTitle( tr( "Select directory for writing exported tracks..." ) ); - if( !projectFileName.isEmpty() ) - { - efd.setDirectory( QFileInfo( projectFileName ).absolutePath() ); - } } else { @@ -1471,12 +1465,10 @@ void MainWindow::exportProject(bool multiExport) QString baseFilename; if( !projectFileName.isEmpty() ) { - efd.setDirectory( QFileInfo( projectFileName ).absolutePath() ); baseFilename = QFileInfo( projectFileName ).completeBaseName(); } else { - efd.setDirectory( ConfigManager::inst()->userProjectsDir() ); baseFilename = tr( "untitled" ); } efd.selectFile( baseFilename + ProjectRenderer::fileEncodeDevices[0].m_extension ); @@ -1575,13 +1567,15 @@ void MainWindow::onImportProject() if (song) { FileDialog ofd( nullptr, tr( "Import file" ), - ConfigManager::inst()->userProjectsDir(), + "", tr("MIDI sequences") + " (*.mid *.midi *.rmi);;" + tr("Hydrogen projects") + " (*.h2song);;" + tr("All file types") + - " (*.*)"); + " (*.*)", + FileDialog::Operation::OpProject + ); ofd.setFileMode( FileDialog::ExistingFiles ); if( ofd.exec () == QDialog::Accepted && !ofd.selectedFiles().isEmpty() ) diff --git a/src/gui/SampleLoader.cpp b/src/gui/SampleLoader.cpp index f2340852d77..e5044691c48 100644 --- a/src/gui/SampleLoader.cpp +++ b/src/gui/SampleLoader.cpp @@ -38,11 +38,8 @@ namespace lmms::gui { QString SampleLoader::openAudioFile(const QString& previousFile) { - auto openFileDialog = FileDialog(nullptr, QObject::tr("Open audio file")); - auto dir = !previousFile.isEmpty() ? PathUtil::toAbsolute(previousFile) : ConfigManager::inst()->userSamplesDir(); - - // change dir to position of previously opened file - openFileDialog.setDirectory(dir); + auto dir = !previousFile.isEmpty() ? PathUtil::toAbsolute(previousFile) : ""; + auto openFileDialog = FileDialog(nullptr, QObject::tr("Open audio file"), dir, "", FileDialog::Operation::OpSample); openFileDialog.setFileMode(FileDialog::ExistingFiles); // set filters diff --git a/src/gui/clips/SampleClipView.cpp b/src/gui/clips/SampleClipView.cpp index a7251be8de6..4347f4bd5b8 100644 --- a/src/gui/clips/SampleClipView.cpp +++ b/src/gui/clips/SampleClipView.cpp @@ -21,7 +21,7 @@ * Boston, MA 02110-1301 USA. * */ - + #include "SampleClipView.h" #include @@ -180,10 +180,10 @@ void SampleClipView::mouseReleaseEvent(QMouseEvent *_me) void SampleClipView::mouseDoubleClickEvent( QMouseEvent * ) { - const QString selectedAudioFile = SampleLoader::openAudioFile(); + const QString selectedAudioFile = SampleLoader::openAudioFile(m_clip ? m_clip->sample().sampleFile() : QString()); if (selectedAudioFile.isEmpty()) { return; } - + if (m_clip->hasSampleFileLoaded(selectedAudioFile)) { m_clip->changeLengthToSampleLength(); diff --git a/src/gui/instrument/InstrumentTrackWindow.cpp b/src/gui/instrument/InstrumentTrackWindow.cpp index 22133813849..1d8592eb7d9 100644 --- a/src/gui/instrument/InstrumentTrackWindow.cpp +++ b/src/gui/instrument/InstrumentTrackWindow.cpp @@ -413,7 +413,7 @@ void InstrumentTrackWindow::modelChanged() void InstrumentTrackWindow::saveSettingsBtnClicked() { - FileDialog sfd(this, tr("Save preset"), "", tr("XML preset file (*.xpf)")); + FileDialog sfd(this, tr("Save preset"), "", tr("XML preset file (*.xpf)"), FileDialog::Operation::OpPreset); QString presetRoot = ConfigManager::inst()->userPresetsDir(); if(!QDir(presetRoot).exists()) diff --git a/src/gui/modals/FileDialog.cpp b/src/gui/modals/FileDialog.cpp index a6cf4827a3e..b4870a6f8f8 100644 --- a/src/gui/modals/FileDialog.cpp +++ b/src/gui/modals/FileDialog.cpp @@ -28,90 +28,43 @@ #include #include #include +#include #include "ConfigManager.h" +#include "FileBrowser.h" +#include "volume.h" #include "FileDialog.h" namespace lmms::gui { +std::vector FileDialog::OperationPaths = std::vector(Operation::OpEnd, ""); +bool FileDialog::OperationPathsReady = false; -FileDialog::FileDialog( QWidget *parent, const QString &caption, - const QString &directory, const QString &filter ) : - QFileDialog( parent, caption, directory, filter ) +FileDialog::FileDialog(QWidget *parent, const QString &caption, + const QString &directory, const QString &filter, + const Operation operation) : + QFileDialog( parent, caption, getOperationPath(operation, directory), filter), + operation(operation) { #if QT_VERSION > 0x050200 setOption( QFileDialog::DontUseCustomDirectoryIcons ); #endif - setOption( QFileDialog::DontUseNativeDialog ); - -#ifdef LMMS_BUILD_LINUX - QList urls; -#else - QList urls = sidebarUrls(); -#endif - - QDir desktopDir; - desktopDir.setPath(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); - if (desktopDir.exists()) - { - urls << QUrl::fromLocalFile(desktopDir.absolutePath()); - } - - QDir downloadDir(QDir::homePath() + "/Downloads"); - if (!downloadDir.exists()) - { - downloadDir.setPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); - } - if (downloadDir.exists()) - { - urls << QUrl::fromLocalFile(downloadDir.absolutePath()); - } - - QDir musicDir; - musicDir.setPath(QStandardPaths::writableLocation(QStandardPaths::MusicLocation)); - if (musicDir.exists()) - { - urls << QUrl::fromLocalFile(musicDir.absolutePath()); - } - - urls << QUrl::fromLocalFile(ConfigManager::inst()->workingDir()); - - // Add `/Volumes` directory on OS X systems, this allows the user to browse - // external disk drives. -#ifdef LMMS_BUILD_APPLE - QDir volumesDir( QDir("/Volumes") ); - if ( volumesDir.exists() ) - urls << QUrl::fromLocalFile( volumesDir.absolutePath() ); -#endif - -#ifdef LMMS_BUILD_LINUX - - // FileSystem types : https://www.javatpoint.com/linux-file-system - QStringList usableFileSystems = {"ext", "ext2", "ext3", "ext4", "jfs", "reiserfs", "ntfs3", "fuse.sshfs", "fuseblk"}; - - for(QStorageInfo storage : QStorageInfo::mountedVolumes()) - { - storage.refresh(); - - if (usableFileSystems.contains(QString(storage.fileSystemType()), Qt::CaseInsensitive) && storage.isValid() && storage.isReady()) - { - urls << QUrl::fromLocalFile(storage.rootPath()); - } - } -#endif - - setSidebarUrls(urls); } +FileDialog::~FileDialog() +{ + setOperationPath(operation, directory().absolutePath()); +} QString FileDialog::getExistingDirectory(QWidget *parent, const QString &caption, const QString &directory, - QFileDialog::Options options) + QFileDialog::Options options, + const Operation operation) { - FileDialog dialog(parent, caption, directory, QString()); + FileDialog dialog(parent, caption, directory, QString(), operation); dialog.setFileMode(QFileDialog::Directory); dialog.setOptions(dialog.options() | options); if (dialog.exec() == QDialog::Accepted) { @@ -124,9 +77,10 @@ QString FileDialog::getOpenFileName(QWidget *parent, const QString &caption, const QString &directory, const QString &filter, - QString *selectedFilter) + QString *selectedFilter, + const Operation operation) { - FileDialog dialog(parent, caption, directory, filter); + FileDialog dialog(parent, caption, directory, QString(), operation); if (selectedFilter && !selectedFilter->isEmpty()) dialog.selectNameFilter(*selectedFilter); if (dialog.exec() == QDialog::Accepted) { @@ -145,5 +99,54 @@ void FileDialog::clearSelection() view->clearSelection(); } +void FileDialog::prepareOperationPaths() +{ + if (OperationPathsReady) + { + return; + } + + auto* config = ConfigManager::inst(); + + OperationPaths[OpGeneric] = config->workingDir(); + OperationPaths[OpProject] = config->userProjectsDir(); + OperationPaths[OpMidi] = config->workingDir(); + OperationPaths[OpPreset] = config->userPresetsDir(); + OperationPaths[OpPlugin] = config->userVstDir(); + OperationPaths[OpSample] = config->userSamplesDir(); + OperationPaths[OpSong] = config->workingDir(); + + OperationPathsReady = true; +} + +QString FileDialog::getOperationPath(const FileDialog::Operation op, const QString& existing) +{ + if (!existing.isEmpty()) + { + return existing; + } + + prepareOperationPaths(); + + for (int i = OpBegin; i != OpEnd; i++) + { + if (i == op) + { + return OperationPaths[i]; + } + } + return OperationPaths[OpGeneric]; +} + +void FileDialog::setOperationPath(const FileDialog::Operation op, const QString& path) +{ + for (int i = OpBegin; i != OpEnd; i++) + { + if (i == op) + { + OperationPaths[i] = path; + } + } +} -} // namespace lmms::gui \ No newline at end of file +} // namespace lmms::gui diff --git a/src/gui/modals/VersionedSaveDialog.cpp b/src/gui/modals/VersionedSaveDialog.cpp index c8e1c682120..02c3a11d300 100644 --- a/src/gui/modals/VersionedSaveDialog.cpp +++ b/src/gui/modals/VersionedSaveDialog.cpp @@ -31,6 +31,7 @@ #include "DeprecationHelper.h" #include "VersionedSaveDialog.h" +#include "FileDialog.h" #include "LedCheckBox.h" @@ -43,7 +44,7 @@ VersionedSaveDialog::VersionedSaveDialog( QWidget *parent, const QString &caption, const QString &directory, const QString &filter ) : - FileDialog(parent, caption, directory, filter) + FileDialog(parent, caption, directory, filter, FileDialog::Operation::OpProject) { setAcceptMode( QFileDialog::AcceptSave ); setFileMode( QFileDialog::AnyFile );