| @@ -834,6 +834,9 @@ public: | |||||
| /** The most likely place where a user might store their movie files. */ | /** The most likely place where a user might store their movie files. */ | ||||
| userMoviesDirectory, | userMoviesDirectory, | ||||
| /** The most likely place where a user might store their picture files. */ | |||||
| userPicturesDirectory | |||||
| }; | }; | ||||
| /** Finds the location of a special type of file or directory, such as a home folder or | /** Finds the location of a special type of file or directory, such as a home folder or | ||||
| @@ -99,33 +99,34 @@ File File::getSpecialLocation (const SpecialLocationType type) | |||||
| { | { | ||||
| switch (type) | switch (type) | ||||
| { | { | ||||
| case userHomeDirectory: | |||||
| case userDocumentsDirectory: | |||||
| case userMusicDirectory: | |||||
| case userMoviesDirectory: | |||||
| case userApplicationDataDirectory: | |||||
| case userDesktopDirectory: | |||||
| return File (android.appDataDir); | |||||
| case commonApplicationDataDirectory: | |||||
| return File (android.appDataDir); | |||||
| case globalApplicationsDirectory: | |||||
| return File ("/system/app"); | |||||
| case tempDirectory: | |||||
| //return File (AndroidStatsHelpers::getSystemProperty ("java.io.tmpdir")); | |||||
| return File (android.appDataDir).getChildFile (".temp"); | |||||
| case invokedExecutableFile: | |||||
| case currentExecutableFile: | |||||
| case currentApplicationFile: | |||||
| case hostApplicationPath: | |||||
| return juce_getExecutableFile(); | |||||
| default: | |||||
| jassertfalse; // unknown type? | |||||
| break; | |||||
| case userHomeDirectory: | |||||
| case userDocumentsDirectory: | |||||
| case userMusicDirectory: | |||||
| case userMoviesDirectory: | |||||
| case userPicturesDirectory: | |||||
| case userApplicationDataDirectory: | |||||
| case userDesktopDirectory: | |||||
| return File (android.appDataDir); | |||||
| case commonApplicationDataDirectory: | |||||
| return File (android.appDataDir); | |||||
| case globalApplicationsDirectory: | |||||
| return File ("/system/app"); | |||||
| case tempDirectory: | |||||
| //return File (AndroidStatsHelpers::getSystemProperty ("java.io.tmpdir")); | |||||
| return File (android.appDataDir).getChildFile (".temp"); | |||||
| case invokedExecutableFile: | |||||
| case currentExecutableFile: | |||||
| case currentApplicationFile: | |||||
| case hostApplicationPath: | |||||
| return juce_getExecutableFile(); | |||||
| default: | |||||
| jassertfalse; // unknown type? | |||||
| break; | |||||
| } | } | ||||
| return File::nonexistent; | return File::nonexistent; | ||||
| @@ -129,72 +129,90 @@ File File::getLinkedTarget() const | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| const char* const* juce_argv = nullptr; | |||||
| int juce_argc = 0; | |||||
| File File::getSpecialLocation (const SpecialLocationType type) | |||||
| static File resolveXDGFolder (const char* const type, const char* const fallbackFolder) | |||||
| { | { | ||||
| switch (type) | |||||
| { | |||||
| case userHomeDirectory: | |||||
| StringArray confLines; | |||||
| File ("~/.config/user-dirs.dirs").readLines (confLines); | |||||
| for (int i = 0; i < confLines.size(); ++i) | |||||
| { | { | ||||
| const char* homeDir = getenv ("HOME"); | |||||
| const String line (confLines[i].trimStart()); | |||||
| if (homeDir == nullptr) | |||||
| if (line.startsWith (type)) | |||||
| { | { | ||||
| struct passwd* const pw = getpwuid (getuid()); | |||||
| if (pw != nullptr) | |||||
| homeDir = pw->pw_dir; | |||||
| } | |||||
| // eg. resolve XDG_MUSIC_DIR="$HOME/Music" to /home/user/Music | |||||
| const File f (line.replace ("$HOME", File ("~").getFullPathName()) | |||||
| .fromFirstOccurrenceOf ("=", false, false) | |||||
| .trim().unquoted()); | |||||
| return File (CharPointer_UTF8 (homeDir)); | |||||
| if (f.isDirectory()) | |||||
| return f; | |||||
| } | |||||
| } | } | ||||
| case userDocumentsDirectory: | |||||
| case userMusicDirectory: | |||||
| case userMoviesDirectory: | |||||
| case userApplicationDataDirectory: | |||||
| return File ("~"); | |||||
| return File (fallbackFolder); | |||||
| } | |||||
| case userDesktopDirectory: | |||||
| return File ("~/Desktop"); | |||||
| const char* const* juce_argv = nullptr; | |||||
| int juce_argc = 0; | |||||
| File File::getSpecialLocation (const SpecialLocationType type) | |||||
| { | |||||
| switch (type) | |||||
| { | |||||
| case userHomeDirectory: | |||||
| { | |||||
| const char* homeDir = getenv ("HOME"); | |||||
| case commonApplicationDataDirectory: | |||||
| return File ("/var"); | |||||
| if (homeDir == nullptr) | |||||
| { | |||||
| struct passwd* const pw = getpwuid (getuid()); | |||||
| if (pw != nullptr) | |||||
| homeDir = pw->pw_dir; | |||||
| } | |||||
| case globalApplicationsDirectory: | |||||
| return File ("/usr"); | |||||
| return File (CharPointer_UTF8 (homeDir)); | |||||
| } | |||||
| case tempDirectory: | |||||
| { | |||||
| File tmp ("/var/tmp"); | |||||
| case userDocumentsDirectory: return resolveXDGFolder ("XDG_DOCUMENTS_DIR", "~"); | |||||
| case userMusicDirectory: return resolveXDGFolder ("XDG_MUSIC_DIR", "~"); | |||||
| case userMoviesDirectory: return resolveXDGFolder ("XDG_VIDEOS_DIR", "~"); | |||||
| case userPicturesDirectory: return resolveXDGFolder ("XDG_PICTURES_DIR", "~"); | |||||
| case userDesktopDirectory: return resolveXDGFolder ("XDG_DESKTOP_DIR", "~/Desktop"); | |||||
| case userApplicationDataDirectory: return File ("~"); | |||||
| case commonApplicationDataDirectory: return File ("/var"); | |||||
| case globalApplicationsDirectory: return File ("/usr"); | |||||
| if (! tmp.isDirectory()) | |||||
| case tempDirectory: | |||||
| { | { | ||||
| tmp = "/tmp"; | |||||
| File tmp ("/var/tmp"); | |||||
| if (! tmp.isDirectory()) | if (! tmp.isDirectory()) | ||||
| tmp = File::getCurrentWorkingDirectory(); | |||||
| } | |||||
| { | |||||
| tmp = "/tmp"; | |||||
| return tmp; | |||||
| } | |||||
| if (! tmp.isDirectory()) | |||||
| tmp = File::getCurrentWorkingDirectory(); | |||||
| } | |||||
| return tmp; | |||||
| } | |||||
| case invokedExecutableFile: | |||||
| if (juce_argv != nullptr && juce_argc > 0) | |||||
| return File (CharPointer_UTF8 (juce_argv[0])); | |||||
| // deliberate fall-through... | |||||
| case invokedExecutableFile: | |||||
| if (juce_argv != nullptr && juce_argc > 0) | |||||
| return File (CharPointer_UTF8 (juce_argv[0])); | |||||
| // deliberate fall-through... | |||||
| case currentExecutableFile: | |||||
| case currentApplicationFile: | |||||
| return juce_getExecutableFile(); | |||||
| case currentExecutableFile: | |||||
| case currentApplicationFile: | |||||
| return juce_getExecutableFile(); | |||||
| case hostApplicationPath: | |||||
| return juce_readlink ("/proc/self/exe", juce_getExecutableFile()); | |||||
| case hostApplicationPath: | |||||
| return juce_readlink ("/proc/self/exe", juce_getExecutableFile()); | |||||
| default: | |||||
| jassertfalse; // unknown type? | |||||
| break; | |||||
| default: | |||||
| jassertfalse; // unknown type? | |||||
| break; | |||||
| } | } | ||||
| return File::nonexistent; | return File::nonexistent; | ||||
| @@ -200,6 +200,7 @@ File File::getSpecialLocation (const SpecialLocationType type) | |||||
| #endif | #endif | ||||
| case userMusicDirectory: resultPath = "~/Music"; break; | case userMusicDirectory: resultPath = "~/Music"; break; | ||||
| case userMoviesDirectory: resultPath = "~/Movies"; break; | case userMoviesDirectory: resultPath = "~/Movies"; break; | ||||
| case userPicturesDirectory: resultPath = "~/Pictures"; break; | |||||
| case userApplicationDataDirectory: resultPath = "~/Library"; break; | case userApplicationDataDirectory: resultPath = "~/Library"; break; | ||||
| case commonApplicationDataDirectory: resultPath = "/Library"; break; | case commonApplicationDataDirectory: resultPath = "/Library"; break; | ||||
| case globalApplicationsDirectory: resultPath = "/Applications"; break; | case globalApplicationsDirectory: resultPath = "/Applications"; break; | ||||
| @@ -510,8 +510,9 @@ File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type) | |||||
| case userApplicationDataDirectory: csidlType = CSIDL_APPDATA; break; | case userApplicationDataDirectory: csidlType = CSIDL_APPDATA; break; | ||||
| case commonApplicationDataDirectory: csidlType = CSIDL_COMMON_APPDATA; break; | case commonApplicationDataDirectory: csidlType = CSIDL_COMMON_APPDATA; break; | ||||
| case globalApplicationsDirectory: csidlType = CSIDL_PROGRAM_FILES; break; | case globalApplicationsDirectory: csidlType = CSIDL_PROGRAM_FILES; break; | ||||
| case userMusicDirectory: csidlType = 0x0d /*CSIDL_MYMUSIC*/; break; | |||||
| case userMoviesDirectory: csidlType = 0x0e /*CSIDL_MYVIDEO*/; break; | |||||
| case userMusicDirectory: csidlType = 0x0d; /*CSIDL_MYMUSIC*/ break; | |||||
| case userMoviesDirectory: csidlType = 0x0e; /*CSIDL_MYVIDEO*/ break; | |||||
| case userPicturesDirectory: csidlType = 0x27; /*CSIDL_MYPICTURES*/ break; | |||||
| case tempDirectory: | case tempDirectory: | ||||
| { | { | ||||
| @@ -23,14 +23,19 @@ | |||||
| ============================================================================== | ============================================================================== | ||||
| */ | */ | ||||
| bool FileChooser::isPlatformDialogAvailable() | |||||
| static bool exeIsAvailable (const char* const executable) | |||||
| { | { | ||||
| ChildProcess child; | |||||
| const bool ok = child.start ("which zenity") | |||||
| && child.readAllProcessOutput().trim().isNotEmpty(); | |||||
| ChildProcess child; | |||||
| const bool ok = child.start ("which " + String (executable)) | |||||
| && child.readAllProcessOutput().trim().isNotEmpty(); | |||||
| child.waitForProcessToFinish (60 * 1000); | |||||
| return ok; | |||||
| } | |||||
| child.waitForProcessToFinish (60 * 1000); | |||||
| return ok; | |||||
| bool FileChooser::isPlatformDialogAvailable() | |||||
| { | |||||
| return exeIsAvailable ("zenity") || exeIsAvailable ("kdialog"); | |||||
| } | } | ||||
| void FileChooser::showPlatformDialog (Array<File>& results, | void FileChooser::showPlatformDialog (Array<File>& results, | ||||
| @@ -44,35 +49,82 @@ void FileChooser::showPlatformDialog (Array<File>& results, | |||||
| bool selectMultipleFiles, | bool selectMultipleFiles, | ||||
| FilePreviewComponent* previewComponent) | FilePreviewComponent* previewComponent) | ||||
| { | { | ||||
| const String separator (":"); | |||||
| String separator; | |||||
| StringArray args; | StringArray args; | ||||
| args.add ("zenity"); | |||||
| args.add ("--file-selection"); | |||||
| if (title.isNotEmpty()) args.add ("--title=" + title); | |||||
| if (isDirectory) args.add ("--directory"); | |||||
| if (isSave) args.add ("--save"); | |||||
| const File previousWorkingDirectory (File::getCurrentWorkingDirectory()); | |||||
| const bool isKdeFullSession = SystemStats::getEnvironmentVariable ("KDE_FULL_SESSION", String::empty) | |||||
| .equalsIgnoreCase ("true"); | |||||
| if (selectMultipleFiles) | |||||
| if (exeIsAvailable ("kdialog") && (isKdeFullSession || ! exeIsAvailable ("zenity"))) | |||||
| { | { | ||||
| args.add ("--multiple"); | |||||
| args.add ("--separator=" + separator); | |||||
| } | |||||
| // use kdialog for KDE sessions or if zenity is missing | |||||
| args.add ("kdialog"); | |||||
| const File previousWorkingDirectory (File::getCurrentWorkingDirectory()); | |||||
| if (title.isNotEmpty()) | |||||
| args.add ("--title=" + title); | |||||
| if (file.isDirectory()) | |||||
| { | |||||
| file.setAsCurrentWorkingDirectory(); | |||||
| if (selectMultipleFiles) | |||||
| { | |||||
| separator = "\n"; | |||||
| args.add ("--multiple"); | |||||
| args.add ("--separate-output"); | |||||
| args.add ("--getopenfilename"); | |||||
| } | |||||
| else | |||||
| { | |||||
| if (isSave) args.add ("--getsavefilename"); | |||||
| else if (isDirectory) args.add ("--getexistingdirectory"); | |||||
| else args.add ("--getopenfilename"); | |||||
| } | |||||
| String startPath; | |||||
| if (file.exists() || file.getParentDirectory().exists()) | |||||
| { | |||||
| startPath = file.getFullPathName(); | |||||
| } | |||||
| else | |||||
| { | |||||
| startPath = File::getSpecialLocation (File::userHomeDirectory).getFullPathName(); | |||||
| if (isSave) | |||||
| startPath += "/" + file.getFileName(); | |||||
| } | |||||
| args.add (startPath); | |||||
| } | } | ||||
| else if (file.exists()) | |||||
| else | |||||
| { | { | ||||
| file.getParentDirectory().setAsCurrentWorkingDirectory(); | |||||
| args.add ("--filename=" + file.getFileName()); | |||||
| } | |||||
| // zenity | |||||
| args.add ("zenity"); | |||||
| args.add ("--file-selection"); | |||||
| args.add ("2>&1"); | |||||
| if (title.isNotEmpty()) | |||||
| args.add ("--title=" + title); | |||||
| if (selectMultipleFiles) | |||||
| { | |||||
| separator = ":"; | |||||
| args.add ("--multiple"); | |||||
| args.add ("--separator=" + separator); | |||||
| } | |||||
| else | |||||
| { | |||||
| if (isDirectory) args.add ("--directory"); | |||||
| if (isSave) args.add ("--save"); | |||||
| } | |||||
| if (file.isDirectory()) | |||||
| file.setAsCurrentWorkingDirectory(); | |||||
| else if (file.getParentDirectory().exists()) | |||||
| file.getParentDirectory().setAsCurrentWorkingDirectory(); | |||||
| else | |||||
| File::getSpecialLocation (File::userHomeDirectory).setAsCurrentWorkingDirectory(); | |||||
| if (! file.getFileName().isEmpty()) | |||||
| args.add ("--filename=" + file.getFileName()); | |||||
| } | |||||
| ChildProcess child; | ChildProcess child; | ||||
| if (child.start (args)) | if (child.start (args)) | ||||