diff --git a/Source/Engine/Platform/Base/FileSystemBase.h b/Source/Engine/Platform/Base/FileSystemBase.h index d0284042b..fc3d75bb1 100644 --- a/Source/Engine/Platform/Base/FileSystemBase.h +++ b/Source/Engine/Platform/Base/FileSystemBase.h @@ -50,11 +50,16 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(FileSystemBase); /// /// The parent window or null. /// The initial directory. - /// The custom filter. + /// The file filter string as null-terminated pairs of name and list of extensions. Multiple file extensions must be separated with semicolon. /// True if allow multiple files to be selected, otherwise use single-file mode. /// The dialog title. /// The output names of the files picked by the user. /// True if failed, otherwise false. + /// + /// Example file filters: + /// "All Files\0*.*" + /// "All Files\0*.*\0Image Files\0*.png;*.jpg" + /// API_FUNCTION() static bool ShowOpenFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, API_PARAM(Out) Array& filenames); /// @@ -62,11 +67,16 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(FileSystemBase); /// /// The parent window. /// The initial directory. - /// The filter. + /// The file filter string as null-terminated pairs of name and list of extensions. Multiple file extensions must be separated with semicolon. /// True if allow multiple files to be selected, otherwise use single-file mode. /// The title. /// The output names of the files picked by the user. /// True if failed, otherwise false. + /// + /// Example file filters: + /// "All Files\0*.*" + /// "All Files\0*.*\0Image Files\0*.png;*.jpg" + /// API_FUNCTION() static bool ShowSaveFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, API_PARAM(Out) Array& filenames); /// diff --git a/Source/Engine/Platform/Base/StringUtilsBase.cpp b/Source/Engine/Platform/Base/StringUtilsBase.cpp index 6f1d741d5..d005f8a04 100644 --- a/Source/Engine/Platform/Base/StringUtilsBase.cpp +++ b/Source/Engine/Platform/Base/StringUtilsBase.cpp @@ -528,4 +528,17 @@ String StringUtils::ToString(double value) return String::Format(TEXT("{}"), value); } +String StringUtils::GetZZString(const Char* str) +{ + const Char* end = str; + while (*end != '\0') + { + end++; + if (*end == '\0') + end++; + } + const int len = end - str; + return String(str, len); +} + #undef STRING_UTILS_ITOSTR_BUFFER_SIZE diff --git a/Source/Engine/Platform/Linux/LinuxFileSystem.cpp b/Source/Engine/Platform/Linux/LinuxFileSystem.cpp index 7291dc8ac..f8ad0d476 100644 --- a/Source/Engine/Platform/Linux/LinuxFileSystem.cpp +++ b/Source/Engine/Platform/Linux/LinuxFileSystem.cpp @@ -4,7 +4,9 @@ #include "LinuxFileSystem.h" #include "Engine/Platform/File.h" +#include "Engine/Platform/StringUtils.h" #include "Engine/Core/Types/String.h" +#include "Engine/Core/Types/StringBuilder.h" #include "Engine/Core/Types/StringView.h" #include "Engine/Core/Types/TimeSpan.h" #include "Engine/Core/Math/Math.h" @@ -27,20 +29,38 @@ bool LinuxFileSystem::ShowOpenFileDialog(Window* parentWindow, const StringView& { const StringAsANSI<> initialDirectoryAnsi(*initialDirectory, initialDirectory.Length()); const StringAsANSI<> titleAnsi(*title, title.Length()); + const char* initDir = initialDirectory.HasChars() ? initialDirectoryAnsi.Get() : "."; char cmd[2048]; - if (FileSystem::FileExists(TEXT("/usr/bin/zenity"))) + String xdgCurrentDesktop; + StringBuilder fileFilter; + Array fileFilterEntries; + Platform::GetEnvironmentVariable(TEXT("XDG_CURRENT_DESKTOP"), xdgCurrentDesktop); + StringUtils::GetZZString(filter.Get()).Split('\0', fileFilterEntries); + + const bool zenitySupported = FileSystem::FileExists(TEXT("/usr/bin/zenity")); + const bool kdialogSupported = FileSystem::FileExists(TEXT("/usr/bin/kdialog")); + if (zenitySupported && (xdgCurrentDesktop != TEXT("KDE") || !kdialogSupported)) // Prefer kdialog when running on KDE { - // TODO: initialDirectory support - // TODO: multiSelect support - // TODO: filter support - sprintf(cmd, "/usr/bin/zenity --modal --file-selection --title=\"%s\" ", titleAnsi.Get()); + for (int32 i = 1; i < fileFilterEntries.Count(); i += 2) + { + String extensions(fileFilterEntries[i]); + fileFilterEntries[i].Replace(TEXT(";"), TEXT(" ")); + fileFilter.Append(String::Format(TEXT("{0}--file-filter=\"{1}|{2}\""), i > 1 ? TEXT(" ") : TEXT(""), fileFilterEntries[i-1].Get(), extensions.Get())); + } + + sprintf(cmd, "/usr/bin/zenity --modal --file-selection %s--filename=\"%s\" --title=\"%s\" %s ", multiSelect ? "--multiple --separator=$'\n' " : " ", initDir, titleAnsi.Get(), fileFilter.ToStringView().ToStringAnsi().GetText()); } - else if (FileSystem::FileExists(TEXT("/usr/bin/kdialog"))) + else if (kdialogSupported) { - // TODO: multiSelect support - // TODO: filter support - const char* initDir = initialDirectory.HasChars() ? initialDirectoryAnsi.Get() : "."; - sprintf(cmd, "/usr/bin/kdialog --getopenfilename \"%s\" --title \"%s\" ", initDir, titleAnsi.Get()); + for (int32 i = 1; i < fileFilterEntries.Count(); i += 2) + { + String extensions(fileFilterEntries[i]); + fileFilterEntries[i].Replace(TEXT(";"), TEXT(" ")); + fileFilter.Append(String::Format(TEXT("{0}\"{1}({2})\""), i > 1 ? TEXT(" ") : TEXT(""), fileFilterEntries[i-1].Get(), extensions.Get())); + } + fileFilter.Append(String::Format(TEXT("{0}\"{1}({2})\""), TEXT(" "), TEXT("many things"), TEXT("*.png *.jpg"))); + + sprintf(cmd, "/usr/bin/kdialog --getopenfilename %s--title \"%s\" \"%s\" %s ", multiSelect ? "--multiple --separate-output " : " ", titleAnsi.Get(), initDir, fileFilter.ToStringView().ToStringAnsi().GetText()); } else { diff --git a/Source/Engine/Platform/StringUtils.h b/Source/Engine/Platform/StringUtils.h index 6f90bbe92..ea982c2ee 100644 --- a/Source/Engine/Platform/StringUtils.h +++ b/Source/Engine/Platform/StringUtils.h @@ -438,6 +438,13 @@ public: static String ToString(uint64 value); static String ToString(float value); static String ToString(double value); + +public: + + // Returns the String to double null-terminated string + // @param str Double null-terminated string + // @return Double null-terminated String + static String GetZZString(const Char* str); }; inline uint32 GetHash(const char* key)