| @@ -834,6 +834,9 @@ public: | |||
| /** The most likely place where a user might store their movie files. */ | |||
| 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 | |||
| @@ -99,33 +99,34 @@ File File::getSpecialLocation (const SpecialLocationType 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; | |||
| @@ -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()) | |||
| 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; | |||
| @@ -200,6 +200,7 @@ File File::getSpecialLocation (const SpecialLocationType type) | |||
| #endif | |||
| case userMusicDirectory: resultPath = "~/Music"; break; | |||
| case userMoviesDirectory: resultPath = "~/Movies"; break; | |||
| case userPicturesDirectory: resultPath = "~/Pictures"; break; | |||
| case userApplicationDataDirectory: resultPath = "~/Library"; break; | |||
| case commonApplicationDataDirectory: resultPath = "/Library"; 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 commonApplicationDataDirectory: csidlType = CSIDL_COMMON_APPDATA; 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: | |||
| { | |||
| @@ -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, | |||
| @@ -44,35 +49,82 @@ void FileChooser::showPlatformDialog (Array<File>& results, | |||
| bool selectMultipleFiles, | |||
| FilePreviewComponent* previewComponent) | |||
| { | |||
| const String separator (":"); | |||
| String separator; | |||
| 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; | |||
| if (child.start (args)) | |||