From e6faf25559a519f01f2d9064c5eba9372a444fa3 Mon Sep 17 00:00:00 2001 From: jules Date: Mon, 15 Oct 2012 15:09:55 +0100 Subject: [PATCH] Added File::userPicturesDirectory, and improved detection of special file locations on linux. --- modules/juce_core/files/juce_File.h | 3 + .../juce_core/native/juce_android_Files.cpp | 55 ++++----- modules/juce_core/native/juce_linux_Files.cpp | 110 ++++++++++-------- modules/juce_core/native/juce_mac_Files.mm | 1 + modules/juce_core/native/juce_win32_Files.cpp | 5 +- .../native/juce_linux_FileChooser.cpp | 104 ++++++++++++----- 6 files changed, 177 insertions(+), 101 deletions(-) diff --git a/modules/juce_core/files/juce_File.h b/modules/juce_core/files/juce_File.h index 1aa13c6e74..555ea3d4b2 100644 --- a/modules/juce_core/files/juce_File.h +++ b/modules/juce_core/files/juce_File.h @@ -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 diff --git a/modules/juce_core/native/juce_android_Files.cpp b/modules/juce_core/native/juce_android_Files.cpp index d75d20c6ab..17470e5414 100644 --- a/modules/juce_core/native/juce_android_Files.cpp +++ b/modules/juce_core/native/juce_android_Files.cpp @@ -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; diff --git a/modules/juce_core/native/juce_linux_Files.cpp b/modules/juce_core/native/juce_linux_Files.cpp index 55b043445e..4e63e2794d 100644 --- a/modules/juce_core/native/juce_linux_Files.cpp +++ b/modules/juce_core/native/juce_linux_Files.cpp @@ -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; diff --git a/modules/juce_core/native/juce_mac_Files.mm b/modules/juce_core/native/juce_mac_Files.mm index dd2b3e855a..e1d6bfa181 100644 --- a/modules/juce_core/native/juce_mac_Files.mm +++ b/modules/juce_core/native/juce_mac_Files.mm @@ -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; diff --git a/modules/juce_core/native/juce_win32_Files.cpp b/modules/juce_core/native/juce_win32_Files.cpp index b14dfd8490..b465eb95af 100644 --- a/modules/juce_core/native/juce_win32_Files.cpp +++ b/modules/juce_core/native/juce_win32_Files.cpp @@ -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: { diff --git a/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp b/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp index 4721749b07..8506405045 100644 --- a/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp +++ b/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp @@ -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& results, @@ -44,35 +49,82 @@ void FileChooser::showPlatformDialog (Array& 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))