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,
CanEditUserFilesDir,
UserFilesDir,
FallbackFilesDir,
//
Controller0,
ControllerLast = Controller0 + sfz::config::numCCs - 1,

View file

@ -22,6 +22,7 @@
#include <functional>
#include <type_traits>
#include <system_error>
#include <fstream>
#include <cstdarg>
#include <cstdio>
#include <cstring>
@ -42,6 +43,8 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
std::string currentSfzFile_;
std::string currentScalaFile_;
std::string userFilesDir_;
std::string fallbackFilesDir_;
enum {
kPanelGeneral,
@ -56,6 +59,8 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
enum {
kTagLoadSfzFile,
kTagEditSfzFile,
kTagCreateNewSfzFile,
kTagOpenSfzFolder,
kTagPreviousSfzFile,
kTagNextSfzFile,
kTagFileOperations,
@ -132,11 +137,13 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
}
void chooseSfzFile();
void createNewSfzFile();
void changeSfzFile(const std::string& filePath);
void changeToNextSfzFile(long offset);
void chooseScalaFile();
void changeScalaFile(const std::string& filePath);
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);
@ -319,7 +326,13 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
}
case EditId::UserFilesDir:
{
updateUserFilesDirLabel(v.to_string());
userFilesDir_ = v.to_string();
updateUserFilesDirLabel(userFilesDir_);
break;
}
case EditId::FallbackFilesDir:
{
fallbackFilesDir_ = v.to_string();
break;
}
case EditId::UINumCurves:
@ -839,6 +852,8 @@ void Editor::Impl::createFrameContents()
if (SActionMenu* menu = fileOperationsMenu_) {
menu->addEntry("Load file", kTagLoadSfzFile);
menu->addEntry("Edit file", kTagEditSfzFile);
menu->addEntry("Create new file", kTagCreateNewSfzFile);
menu->addEntry("Open SFZ folder", kTagOpenSfzFolder);
}
if (SPiano* piano = piano_) {
@ -891,10 +906,10 @@ void Editor::Impl::chooseSfzFile()
fs->setTitle("Load SFZ file");
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());
}
if (fs->runModal()) {
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)
{
ctrl_->uiSendValue(EditId::SfzFile, filePath);
@ -969,10 +1022,10 @@ void Editor::Impl::chooseScalaFile()
fs->setTitle("Load Scala file");
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());
}
if (fs->runModal()) {
UTF8StringPtr file = fs->getSelectedFile(0);
@ -998,12 +1051,31 @@ void Editor::Impl::chooseUserFilesDir()
if (fs->runModal()) {
UTF8StringPtr dir = fs->getSelectedFile(0);
if (dir) {
updateUserFilesDirLabel(dir);
ctrl_->uiSendValue(EditId::UserFilesDir, std::string(dir));
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)
{
std::error_code ec;
@ -1292,6 +1364,23 @@ void Editor::Impl::valueChanged(CControl* ctl)
openFileInExternalEditor(currentSfzFile_.c_str());
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:
if (value != 1)
break;

View file

@ -11,9 +11,20 @@
#include <windows.h>
#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)
{
std::wstring path = fs::u8path(filename).wstring();
std::wstring path = stringToWideChar(filename);
SHELLEXECUTEINFOW info;
memset(&info, 0, sizeof(info));
@ -27,14 +38,42 @@ bool openFileInExternalEditor(const char *filename)
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__)
// implemented in NativeHelpers.mm
#else
#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)
return 1;
@ -47,4 +86,63 @@ bool openFileInExternalEditor(const char *filename)
g_object_unref(appinfo);
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

View file

@ -7,3 +7,5 @@
#pragma once
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 <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(
kUTTypePlainText, kLSRolesEditor, nil);
if (!applicationURL)
if (!applicationURL || ![applicationURL isFileURL])
return false;
if ([applicationURL isFileURL]) {
NSWorkspace* workspace = [NSWorkspace sharedWorkspace];
NSString* fileName = [NSString stringWithUTF8String:fileNameUTF8];
wasOpened = [workspace openFile:fileName withApplication:[applicationURL path]];
}
return openFileWithApplication(fileName, [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

View file

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