Merge pull request #644 from jpcima/sfz-directory

File manipulation features in editor
This commit is contained in:
JP Cimalando 2021-02-22 10:53:30 +01:00 committed by GitHub
commit 8e84da84ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 226 additions and 21 deletions

View file

@ -20,6 +20,7 @@ enum class EditId : int {
StretchTuning, StretchTuning,
CanEditUserFilesDir, CanEditUserFilesDir,
UserFilesDir, UserFilesDir,
FallbackFilesDir,
// //
Controller0, Controller0,
ControllerLast = Controller0 + sfz::config::numCCs - 1, ControllerLast = Controller0 + sfz::config::numCCs - 1,

View file

@ -22,6 +22,7 @@
#include <functional> #include <functional>
#include <type_traits> #include <type_traits>
#include <system_error> #include <system_error>
#include <fstream>
#include <cstdarg> #include <cstdarg>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
@ -42,6 +43,8 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
std::string currentSfzFile_; std::string currentSfzFile_;
std::string currentScalaFile_; std::string currentScalaFile_;
std::string userFilesDir_;
std::string fallbackFilesDir_;
enum { enum {
kPanelGeneral, kPanelGeneral,
@ -56,6 +59,8 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
enum { enum {
kTagLoadSfzFile, kTagLoadSfzFile,
kTagEditSfzFile, kTagEditSfzFile,
kTagCreateNewSfzFile,
kTagOpenSfzFolder,
kTagPreviousSfzFile, kTagPreviousSfzFile,
kTagNextSfzFile, kTagNextSfzFile,
kTagFileOperations, kTagFileOperations,
@ -132,11 +137,13 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
} }
void chooseSfzFile(); void chooseSfzFile();
void createNewSfzFile();
void changeSfzFile(const std::string& filePath); void changeSfzFile(const std::string& filePath);
void changeToNextSfzFile(long offset); void changeToNextSfzFile(long offset);
void chooseScalaFile(); void chooseScalaFile();
void changeScalaFile(const std::string& filePath); void changeScalaFile(const std::string& filePath);
void chooseUserFilesDir(); void chooseUserFilesDir();
std::string getFileChooserInitialDir(const std::string& previousFilePath) const;
static bool scanDirectoryFiles(const fs::path& dirPath, std::function<bool(const fs::path&)> filter, std::vector<fs::path>& fileNames); static bool scanDirectoryFiles(const fs::path& dirPath, std::function<bool(const fs::path&)> filter, std::vector<fs::path>& fileNames);
@ -319,7 +326,13 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
} }
case EditId::UserFilesDir: case EditId::UserFilesDir:
{ {
updateUserFilesDirLabel(v.to_string()); userFilesDir_ = v.to_string();
updateUserFilesDirLabel(userFilesDir_);
break;
}
case EditId::FallbackFilesDir:
{
fallbackFilesDir_ = v.to_string();
break; break;
} }
case EditId::UINumCurves: case EditId::UINumCurves:
@ -839,6 +852,8 @@ void Editor::Impl::createFrameContents()
if (SActionMenu* menu = fileOperationsMenu_) { if (SActionMenu* menu = fileOperationsMenu_) {
menu->addEntry("Load file", kTagLoadSfzFile); menu->addEntry("Load file", kTagLoadSfzFile);
menu->addEntry("Edit file", kTagEditSfzFile); menu->addEntry("Edit file", kTagEditSfzFile);
menu->addEntry("Create new file", kTagCreateNewSfzFile);
menu->addEntry("Open SFZ folder", kTagOpenSfzFolder);
} }
if (SPiano* piano = piano_) { if (SPiano* piano = piano_) {
@ -891,10 +906,10 @@ void Editor::Impl::chooseSfzFile()
fs->setTitle("Load SFZ file"); fs->setTitle("Load SFZ file");
fs->setDefaultExtension(CFileExtension("SFZ", "sfz")); fs->setDefaultExtension(CFileExtension("SFZ", "sfz"));
if (!currentSfzFile_.empty()) {
std::string initialDir = fs::path(currentSfzFile_).parent_path().u8string() + '/'; std::string initialDir = getFileChooserInitialDir(currentSfzFile_);
if (!initialDir.empty())
fs->setInitialDirectory(initialDir.c_str()); fs->setInitialDirectory(initialDir.c_str());
}
if (fs->runModal()) { if (fs->runModal()) {
UTF8StringPtr file = fs->getSelectedFile(0); UTF8StringPtr file = fs->getSelectedFile(0);
@ -903,6 +918,44 @@ void Editor::Impl::chooseSfzFile()
} }
} }
///
static const char defaultSfzText[] =
"<region>sample=*sine" "\n"
"ampeg_attack=0.02 ampeg_release=0.1" "\n";
static void createDefaultSfzFileIfNotExisting(const fs::path& path)
{
if (!fs::exists(path))
fs::ofstream { path } << defaultSfzText;
}
///
void Editor::Impl::createNewSfzFile()
{
SharedPointer<CNewFileSelector> fs = owned(CNewFileSelector::create(frame_, CNewFileSelector::kSelectSaveFile));
fs->setTitle("Create SFZ file");
fs->setDefaultExtension(CFileExtension("SFZ", "sfz"));
std::string initialDir = getFileChooserInitialDir(currentSfzFile_);
if (!initialDir.empty())
fs->setInitialDirectory(initialDir.c_str());
if (fs->runModal()) {
UTF8StringPtr file = fs->getSelectedFile(0);
std::string fileStr;
if (file && !absl::EndsWithIgnoreCase(file, ".sfz")) {
fileStr = std::string(file) + ".sfz";
file = fileStr.c_str();
}
if (file) {
createDefaultSfzFileIfNotExisting(fs::u8path(file));
changeSfzFile(file);
openFileInExternalEditor(file);
}
}
}
void Editor::Impl::changeSfzFile(const std::string& filePath) void Editor::Impl::changeSfzFile(const std::string& filePath)
{ {
ctrl_->uiSendValue(EditId::SfzFile, filePath); ctrl_->uiSendValue(EditId::SfzFile, filePath);
@ -969,10 +1022,10 @@ void Editor::Impl::chooseScalaFile()
fs->setTitle("Load Scala file"); fs->setTitle("Load Scala file");
fs->setDefaultExtension(CFileExtension("SCL", "scl")); fs->setDefaultExtension(CFileExtension("SCL", "scl"));
if (!currentScalaFile_.empty()) {
std::string initialDir = fs::path(currentScalaFile_).parent_path().u8string() + '/'; std::string initialDir = getFileChooserInitialDir(currentScalaFile_);
if (!initialDir.empty())
fs->setInitialDirectory(initialDir.c_str()); fs->setInitialDirectory(initialDir.c_str());
}
if (fs->runModal()) { if (fs->runModal()) {
UTF8StringPtr file = fs->getSelectedFile(0); UTF8StringPtr file = fs->getSelectedFile(0);
@ -998,12 +1051,31 @@ void Editor::Impl::chooseUserFilesDir()
if (fs->runModal()) { if (fs->runModal()) {
UTF8StringPtr dir = fs->getSelectedFile(0); UTF8StringPtr dir = fs->getSelectedFile(0);
if (dir) { if (dir) {
updateUserFilesDirLabel(dir); userFilesDir_ = std::string(dir);
ctrl_->uiSendValue(EditId::UserFilesDir, std::string(dir)); updateUserFilesDirLabel(userFilesDir_);
ctrl_->uiSendValue(EditId::UserFilesDir, userFilesDir_);
} }
} }
} }
std::string Editor::Impl::getFileChooserInitialDir(const std::string& previousFilePath) const
{
fs::path initialPath;
if (!previousFilePath.empty())
initialPath = fs::u8path(previousFilePath).parent_path();
else if (!userFilesDir_.empty())
initialPath = fs::u8path(userFilesDir_);
else if (!fallbackFilesDir_.empty())
initialPath = fs::u8path(fallbackFilesDir_);
std::string initialDir = initialPath.u8string();
if (!initialDir.empty())
initialDir.push_back('/');
return initialDir;
}
bool Editor::Impl::scanDirectoryFiles(const fs::path& dirPath, std::function<bool(const fs::path&)> filter, std::vector<fs::path>& fileNames) bool Editor::Impl::scanDirectoryFiles(const fs::path& dirPath, std::function<bool(const fs::path&)> filter, std::vector<fs::path>& fileNames)
{ {
std::error_code ec; std::error_code ec;
@ -1292,6 +1364,23 @@ void Editor::Impl::valueChanged(CControl* ctl)
openFileInExternalEditor(currentSfzFile_.c_str()); openFileInExternalEditor(currentSfzFile_.c_str());
break; break;
case kTagCreateNewSfzFile:
if (value != 1)
break;
createNewSfzFile();
break;
case kTagOpenSfzFolder:
if (value != 1)
break;
if (!userFilesDir_.empty())
openDirectoryInExplorer(userFilesDir_.c_str());
else if (!fallbackFilesDir_.empty())
openDirectoryInExplorer(fallbackFilesDir_.c_str());
break;
case kTagPreviousSfzFile: case kTagPreviousSfzFile:
if (value != 1) if (value != 1)
break; break;

View file

@ -11,9 +11,20 @@
#include <windows.h> #include <windows.h>
#include <cstring> #include <cstring>
static WCHAR *stringToWideChar(const char *str, int strCch = -1)
{
unsigned strSize = MultiByteToWideChar(CP_UTF8, 0, str, strCch, nullptr, 0);
if (strSize == 0)
return {};
std::unique_ptr<WCHAR[]> strW(new WCHAR[strSize]);
if (MultiByteToWideChar(CP_UTF8, 0, str, strCch, strW.get(), strSize) == 0)
return {};
return strW.release();
}
bool openFileInExternalEditor(const char *filename) bool openFileInExternalEditor(const char *filename)
{ {
std::wstring path = fs::u8path(filename).wstring(); std::wstring path = stringToWideChar(filename);
SHELLEXECUTEINFOW info; SHELLEXECUTEINFOW info;
memset(&info, 0, sizeof(info)); memset(&info, 0, sizeof(info));
@ -27,14 +38,42 @@ bool openFileInExternalEditor(const char *filename)
return ShellExecuteExW(&info); return ShellExecuteExW(&info);
} }
bool openDirectoryInExplorer(const char *filename)
{
std::wstring path = stringToWideChar(filename);
SHELLEXECUTEINFOW info;
memset(&info, 0, sizeof(info));
info.cbSize = sizeof(info);
info.lpVerb = L"explore";
info.lpFile = path.c_str();
info.nShow = SW_SHOW;
return ShellExecuteExW(&info);
}
bool askQuestion(const char *text)
{
int ret = MessageBoxW(nullptr, stringToWideChar(text), L"Question", MB_YESNO);
return ret == IDYES;
}
#elif defined(__APPLE__) #elif defined(__APPLE__)
// implemented in NativeHelpers.mm // implemented in NativeHelpers.mm
#else #else
#include <gio/gio.h> #include <gio/gio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>
#include <cstring>
#include <cerrno>
extern "C" { extern char **environ; }
bool openFileInExternalEditor(const char *filename) static bool openFileByMimeType(const char *filename, const char *mimetype)
{ {
GAppInfo* appinfo = g_app_info_get_default_for_type("text/plain", FALSE); GAppInfo* appinfo = g_app_info_get_default_for_type(mimetype, FALSE);
if (!appinfo) if (!appinfo)
return 1; return 1;
@ -47,4 +86,63 @@ bool openFileInExternalEditor(const char *filename)
g_object_unref(appinfo); g_object_unref(appinfo);
return success == TRUE; return success == TRUE;
} }
bool openFileInExternalEditor(const char *filename)
{
return openFileByMimeType(filename, "text/plain");
}
bool openDirectoryInExplorer(const char *filename)
{
return openFileByMimeType(filename, "inode/directory");
}
static std::vector<char *> createForkEnviron()
{
std::vector<char *> newEnv;
newEnv.reserve(256);
for (char **envp = environ; *envp; ++envp) {
// ensure the process will link with system libraries,
// and not these from the Ardour bundle.
if (strncmp(*envp, "LD_LIBRARY_PATH=", 16) == 0)
continue;
newEnv.push_back(*envp);
}
newEnv.push_back(nullptr);
return newEnv;
}
bool askQuestion(const char *text)
{
char *argv[] = {
const_cast<char *>("/usr/bin/zenity"),
const_cast<char *>("--question"),
const_cast<char *>("--text"),
const_cast<char *>(text),
nullptr,
};
std::vector<char *> newEnv = createForkEnviron();
char **envp = newEnv.data();
pid_t forkPid = vfork();
if (forkPid == -1)
return false;
if (forkPid == 0) {
execve(argv[0], argv, envp);
_exit(1);
}
int wret;
int wstatus;
do {
wret = waitpid(forkPid, &wstatus, 0);
} while (wret == -1 && errno == EINTR);
if (wret == -1 || !WIFEXITED(wstatus))
return false;
return WEXITSTATUS(wstatus) == 0;
}
#endif #endif

View file

@ -7,3 +7,5 @@
#pragma once #pragma once
bool openFileInExternalEditor(const char *filename); bool openFileInExternalEditor(const char *filename);
bool openDirectoryInExplorer(const char *filename);
bool askQuestion(const char *text);

View file

@ -11,20 +11,34 @@
#import <CoreServices/CoreServices.h> #import <CoreServices/CoreServices.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
bool openFileInExternalEditor(const char *fileNameUTF8) static bool openFileWithApplication(const char *fileName, NSString *application)
{ {
BOOL wasOpened = NO; NSWorkspace* workspace = [NSWorkspace sharedWorkspace];
NSString* fileNameNs = [NSString stringWithUTF8String:fileName];
return [workspace openFile:fileNameNs withApplication:application] == YES;
}
bool openFileInExternalEditor(const char *fileName)
{
NSURL* applicationURL = (__bridge_transfer NSURL*)LSCopyDefaultApplicationURLForContentType( NSURL* applicationURL = (__bridge_transfer NSURL*)LSCopyDefaultApplicationURLForContentType(
kUTTypePlainText, kLSRolesEditor, nil); kUTTypePlainText, kLSRolesEditor, nil);
if (!applicationURL) if (!applicationURL || ![applicationURL isFileURL])
return false; return false;
if ([applicationURL isFileURL]) { return openFileWithApplication(fileName, [applicationURL path]);
NSWorkspace* workspace = [NSWorkspace sharedWorkspace]; }
NSString* fileName = [NSString stringWithUTF8String:fileNameUTF8];
wasOpened = [workspace openFile:fileName withApplication:[applicationURL path]];
}
return wasOpened == YES; bool openDirectoryInExplorer(const char *fileName)
{
return openFileWithApplication(fileName, @"Finder");
}
bool askQuestion(const char *text)
{
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:[NSString stringWithUTF8String:text]];
[alert addButtonWithTitle:@"OK"];
[alert addButtonWithTitle:@"Cancel"];
NSInteger button = [alert runModal];
return button == NSAlertFirstButtonReturn;
} }
#endif #endif

View file

@ -91,6 +91,7 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p
absl::optional<fs::path> userFilesDir = SfizzPaths::getSfzConfigDefaultPath(); absl::optional<fs::path> userFilesDir = SfizzPaths::getSfzConfigDefaultPath();
uiReceiveValue(EditId::CanEditUserFilesDir, 1); uiReceiveValue(EditId::CanEditUserFilesDir, 1);
uiReceiveValue(EditId::UserFilesDir, userFilesDir.value_or(fs::path()).u8string()); uiReceiveValue(EditId::UserFilesDir, userFilesDir.value_or(fs::path()).u8string());
uiReceiveValue(EditId::FallbackFilesDir, SfizzPaths::getSfzFallbackDefaultPath().u8string());
return true; return true;
} }