Merge pull request #644 from jpcima/sfz-directory
File manipulation features in editor
This commit is contained in:
commit
8e84da84ad
6 changed files with 226 additions and 21 deletions
|
|
@ -20,6 +20,7 @@ enum class EditId : int {
|
|||
StretchTuning,
|
||||
CanEditUserFilesDir,
|
||||
UserFilesDir,
|
||||
FallbackFilesDir,
|
||||
//
|
||||
Controller0,
|
||||
ControllerLast = Controller0 + sfz::config::numCCs - 1,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -7,3 +7,5 @@
|
|||
#pragma once
|
||||
|
||||
bool openFileInExternalEditor(const char *filename);
|
||||
bool openDirectoryInExplorer(const char *filename);
|
||||
bool askQuestion(const char *text);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue