File dialog improvements
- Add a globally-accessible function to handle the parsing of filter extensions - Remove the ability of putting the wildcard ('*') among other patterns; it's either a list of patterns or a single '*' now - Add a hint to select between portals and Zenity on Unixmain
parent
5fa87e29e7
commit
6ad390fc50
|
@ -2873,6 +2873,7 @@ elseif(N3DS)
|
|||
endif()
|
||||
|
||||
if (SDL_DIALOG)
|
||||
sdl_sources(${SDL3_SOURCE_DIR}/src/dialog/SDL_dialog_utils.c)
|
||||
if(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
|
||||
sdl_sources(${SDL3_SOURCE_DIR}/src/dialog/unix/SDL_unixdialog.c)
|
||||
sdl_sources(${SDL3_SOURCE_DIR}/src/dialog/unix/SDL_portaldialog.c)
|
||||
|
|
|
@ -507,6 +507,7 @@
|
|||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\camera\dummy\SDL_camera_dummy.c" />
|
||||
<ClCompile Include="..\..\src\camera\SDL_camera.c" />
|
||||
<ClCompile Include="..\..\src\dialog\SDL_dialog_utils.c" />
|
||||
<ClCompile Include="..\..\src\filesystem\SDL_filesystem.c" />
|
||||
<ClCompile Include="..\..\src\filesystem\windows\SDL_sysfsops.c" />
|
||||
<ClCompile Include="..\..\src\main\generic\SDL_sysmain_callbacks.c" />
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
<ClCompile Include="..\..\src\core\gdk\SDL_gdk.cpp" />
|
||||
<ClCompile Include="..\..\src\core\windows\pch.c" />
|
||||
<ClCompile Include="..\..\src\core\windows\pch_cpp.cpp" />
|
||||
<ClCompile Include="..\..\src\dialog\SDL_dialog_utils.c">
|
||||
<Filter>dialog</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\filesystem\SDL_filesystem.c">
|
||||
<Filter>filesystem</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
@ -315,6 +315,7 @@
|
|||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\dialog\SDL_dialog_utils.c" />
|
||||
<ClCompile Include="..\src\events\SDL_clipboardevents.c" />
|
||||
<ClCompile Include="..\src\events\SDL_displayevents.c" />
|
||||
<ClCompile Include="..\src\events\SDL_dropevents.c" />
|
||||
|
|
|
@ -31,6 +31,9 @@
|
|||
<Filter Include="time\windows">
|
||||
<UniqueIdentifier>{0000012051ca8361c8e1013aee1d0000}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="dialog">
|
||||
<UniqueIdentifier>{0000c99bfadbbcb05a474a8472910000}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\include\SDL3\SDL_begin_code.h">
|
||||
|
@ -567,6 +570,9 @@
|
|||
<ClCompile Include="..\src\cpuinfo\SDL_cpuinfo.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\dialog\SDL_dialog_utils.c">
|
||||
<Filter>dialog</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\dynapi\SDL_dynapi.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
@ -404,6 +404,7 @@
|
|||
<ClCompile Include="..\..\src\camera\dummy\SDL_camera_dummy.c" />
|
||||
<ClCompile Include="..\..\src\camera\mediafoundation\SDL_camera_mediafoundation.c" />
|
||||
<ClCompile Include="..\..\src\camera\SDL_camera.c" />
|
||||
<ClCompile Include="..\..\src\dialog\SDL_dialog_utils.c" />
|
||||
<ClCompile Include="..\..\src\filesystem\SDL_filesystem.c" />
|
||||
<ClCompile Include="..\..\src\filesystem\windows\SDL_sysfsops.c" />
|
||||
<ClCompile Include="..\..\src\main\generic\SDL_sysmain_callbacks.c" />
|
||||
|
|
|
@ -196,6 +196,9 @@
|
|||
<Filter Include="time\windows">
|
||||
<UniqueIdentifier>{0000d7fda065b13b0ca4ab262c380000}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="dialog">
|
||||
<UniqueIdentifier>{00008dfdfa0190856fbf3c7db52d0000}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\include\SDL3\SDL_begin_code.h">
|
||||
|
@ -883,6 +886,9 @@
|
|||
<ClCompile Include="..\..\src\camera\SDL_camera.c">
|
||||
<Filter>camera</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\dialog\SDL_dialog_utils.c">
|
||||
<Filter>dialog</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\filesystem\SDL_filesystem.c">
|
||||
<Filter>filesystem</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
@ -513,6 +513,7 @@
|
|||
F3FA5A242B59ACE000FEAD97 /* yuv_rgb_lsx.h in Headers */ = {isa = PBXBuildFile; fileRef = F3FA5A1B2B59ACE000FEAD97 /* yuv_rgb_lsx.h */; };
|
||||
F3FA5A252B59ACE000FEAD97 /* yuv_rgb_common.h in Headers */ = {isa = PBXBuildFile; fileRef = F3FA5A1C2B59ACE000FEAD97 /* yuv_rgb_common.h */; };
|
||||
FA73671D19A540EF004122E4 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA73671C19A540EF004122E4 /* CoreVideo.framework */; platformFilters = (ios, maccatalyst, macos, tvos, watchos, ); };
|
||||
0000140640E77F73F1DF0000 /* SDL_dialog_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 0000F6C6A072ED4E3D660000 /* SDL_dialog_utils.c */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
@ -1054,6 +1055,7 @@
|
|||
F59C710600D5CB5801000001 /* SDL.info */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = SDL.info; sourceTree = "<group>"; };
|
||||
F5A2EF3900C6A39A01000001 /* BUGS.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; name = BUGS.txt; path = ../../BUGS.txt; sourceTree = SOURCE_ROOT; };
|
||||
FA73671C19A540EF004122E4 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; };
|
||||
0000F6C6A072ED4E3D660000 /* SDL_dialog_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_dialog_utils.c; path = SDL_dialog_utils.c; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -2233,6 +2235,7 @@
|
|||
children = (
|
||||
F37E18552BA50ED50098C111 /* cocoa */,
|
||||
F37E18562BA50F2A0098C111 /* dummy */,
|
||||
0000F6C6A072ED4E3D660000 /* SDL_dialog_utils.c */,
|
||||
);
|
||||
path = dialog;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2872,6 +2875,7 @@
|
|||
0000481D255AF155B42C0000 /* SDL_sysfsops.c in Sources */,
|
||||
0000494CC93F3E624D3C0000 /* SDL_systime.c in Sources */,
|
||||
000095FA1BDE436CF3AF0000 /* SDL_time.c in Sources */,
|
||||
0000140640E77F73F1DF0000 /* SDL_dialog_utils.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -37,7 +37,9 @@ extern "C" {
|
|||
* `name` is a user-readable label for the filter (for example, "Office document").
|
||||
*
|
||||
* `pattern` is a semicolon-separated list of file extensions (for example,
|
||||
* "doc;docx").
|
||||
* "doc;docx"). File extensions may only contain alphanumeric characters,
|
||||
* hyphens, underscores and periods. Alternatively, the whole string can be a
|
||||
* single asterisk ("*"), which serves as an "All files" filter.
|
||||
*
|
||||
* \sa SDL_DialogFileCallback
|
||||
* \sa SDL_ShowOpenFileDialog
|
||||
|
|
|
@ -414,6 +414,26 @@ extern "C" {
|
|||
*/
|
||||
#define SDL_HINT_JOYSTICK_DIRECTINPUT "SDL_JOYSTICK_DIRECTINPUT"
|
||||
|
||||
/**
|
||||
* A variable that specifies a dialog backend to use.
|
||||
*
|
||||
* By default, SDL will try all available dialog backends in a reasonable order until it finds one that can work, but this hint allows the app or user to force a specific target.
|
||||
*
|
||||
* If the specified target does not exist or is not available, the dialog-related function calls will fail.
|
||||
*
|
||||
* This hint currently only applies to platforms using the generic "Unix" dialog implementation, but may be extended to more platforms in the future. Note that some Unix and Unix-like platforms have their own implementation, such as macOS and Haiku.
|
||||
*
|
||||
* The variable can be set to the following values:
|
||||
* NULL - Select automatically (default, all platforms)
|
||||
* "portal" - Use XDG Portals through DBus (Unix only)
|
||||
* "zenity" - Use the Zenity program (Unix only)
|
||||
*
|
||||
* More options may be added in the future.
|
||||
*
|
||||
* This hint can be set anytime.
|
||||
*/
|
||||
#define SDL_HINT_FILE_DIALOG_DRIVER "SDL_FILE_DIALOG_DRIVER"
|
||||
|
||||
/**
|
||||
* Override for SDL_GetDisplayUsableBounds()
|
||||
*
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_dialog_utils.h"
|
||||
|
||||
char *convert_filters(const SDL_DialogFileFilter *filters, NameTransform ntf,
|
||||
const char *prefix, const char *separator,
|
||||
const char *suffix, const char *filt_prefix,
|
||||
const char *filt_separator, const char *filt_suffix,
|
||||
const char *ext_prefix, const char *ext_separator,
|
||||
const char *ext_suffix)
|
||||
{
|
||||
char *combined;
|
||||
char *new_combined;
|
||||
char *converted;
|
||||
const char *terminator;
|
||||
int new_length;
|
||||
|
||||
combined = SDL_strdup(prefix);
|
||||
|
||||
if (!combined) {
|
||||
SDL_OutOfMemory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (const SDL_DialogFileFilter *f = filters; f->name; f++) {
|
||||
converted = convert_filter(*f, ntf, filt_prefix, filt_separator,
|
||||
filt_suffix, ext_prefix, ext_separator,
|
||||
ext_suffix);
|
||||
|
||||
if (!converted) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
terminator = f[1].name ? separator : suffix;
|
||||
new_length = SDL_strlen(combined) + SDL_strlen(converted)
|
||||
+ SDL_strlen(terminator);
|
||||
|
||||
new_combined = SDL_realloc(combined, new_length);
|
||||
|
||||
if (!new_combined) {
|
||||
SDL_free(converted);
|
||||
SDL_free(combined);
|
||||
SDL_OutOfMemory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
combined = new_combined;
|
||||
|
||||
SDL_strlcat(combined, converted, new_length);
|
||||
SDL_strlcat(combined, terminator, new_length);
|
||||
}
|
||||
|
||||
return combined;
|
||||
}
|
||||
|
||||
char *convert_filter(const SDL_DialogFileFilter filter, NameTransform ntf,
|
||||
const char *prefix, const char *separator,
|
||||
const char *suffix, const char *ext_prefix,
|
||||
const char *ext_separator, const char *ext_suffix)
|
||||
{
|
||||
char *converted;
|
||||
char *name_filtered;
|
||||
int total_length;
|
||||
char *list;
|
||||
|
||||
list = convert_ext_list(filter.pattern, ext_prefix, ext_separator,
|
||||
ext_suffix);
|
||||
|
||||
if (!list) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ntf) {
|
||||
name_filtered = ntf(filter.name);
|
||||
} else {
|
||||
/* Useless strdup, but easier to read and maintain code this way */
|
||||
name_filtered = SDL_strdup(filter.name);
|
||||
}
|
||||
|
||||
if (!name_filtered) {
|
||||
SDL_free(list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
total_length = SDL_strlen(prefix) + SDL_strlen(name_filtered)
|
||||
+ SDL_strlen(separator) + SDL_strlen(list)
|
||||
+ SDL_strlen(suffix) + 1;
|
||||
|
||||
converted = (char *) SDL_malloc(total_length);
|
||||
|
||||
if (!converted) {
|
||||
SDL_free(list);
|
||||
SDL_free(name_filtered);
|
||||
SDL_OutOfMemory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_snprintf(converted, total_length, "%s%s%s%s%s", prefix, name_filtered,
|
||||
separator, list, suffix);
|
||||
|
||||
SDL_free(list);
|
||||
SDL_free(name_filtered);
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
char *convert_ext_list(const char *list, const char *prefix,
|
||||
const char *separator, const char *suffix)
|
||||
{
|
||||
char *converted;
|
||||
int semicolons;
|
||||
int total_length;
|
||||
|
||||
semicolons = 0;
|
||||
|
||||
for (const char *c = list; *c; c++) {
|
||||
semicolons += (*c == ';');
|
||||
}
|
||||
|
||||
total_length =
|
||||
SDL_strlen(list) - semicolons /* length of list contents */
|
||||
+ semicolons * SDL_strlen(separator) /* length of separators */
|
||||
+ SDL_strlen(prefix) + SDL_strlen(suffix) /* length of prefix/suffix */
|
||||
+ 1; /* terminating null byte */
|
||||
|
||||
converted = (char *) SDL_malloc(total_length);
|
||||
|
||||
if (!converted) {
|
||||
SDL_OutOfMemory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*converted = '\0';
|
||||
|
||||
SDL_strlcat(converted, prefix, total_length);
|
||||
|
||||
/* Some platforms may prefer to handle the asterisk manually, but this
|
||||
function offers to handle it for ease of use. */
|
||||
if (SDL_strcmp(list, "*") == 0) {
|
||||
SDL_strlcat(converted, "*", total_length);
|
||||
} else {
|
||||
for (const char *c = list; *c; c++) {
|
||||
if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z')
|
||||
|| (*c >= '0' && *c <= '9') || *c == '-' || *c == '_'
|
||||
|| *c == '.') {
|
||||
char str[2];
|
||||
str[0] = *c;
|
||||
str[1] = '\0';
|
||||
SDL_strlcat(converted, str, total_length);
|
||||
} else if (*c == ';') {
|
||||
if (c == list || c[-1] == ';') {
|
||||
SDL_SetError("Empty pattern not allowed");
|
||||
SDL_free(converted);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_strlcat(converted, separator, total_length);
|
||||
} else {
|
||||
SDL_SetError("Invalid character '%c' in pattern (Only [a-zA-Z0-9_.-] allowed, or a single *)", *c);
|
||||
SDL_free(converted);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (list[SDL_strlen(list) - 1] == ';') {
|
||||
SDL_SetError("Empty pattern not allowed");
|
||||
SDL_free(converted);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_strlcat(converted, suffix, total_length);
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
const char *validate_filters(const SDL_DialogFileFilter *filters)
|
||||
{
|
||||
if (filters) {
|
||||
for (const SDL_DialogFileFilter *f = filters; f->name; f++) {
|
||||
const char *msg = validate_list(f->pattern);
|
||||
|
||||
if (msg) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *validate_list(const char *list)
|
||||
{
|
||||
if (SDL_strcmp(list, "*") == 0) {
|
||||
return NULL;
|
||||
} else {
|
||||
for (const char *c = list; *c; c++) {
|
||||
if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z')
|
||||
|| (*c >= '0' && *c <= '9') || *c == '-' || *c == '_'
|
||||
|| *c == '.') {
|
||||
continue;
|
||||
} else if (*c == ';') {
|
||||
if (c == list || c[-1] == ';') {
|
||||
return "Empty pattern not allowed";
|
||||
}
|
||||
} else {
|
||||
return "Invalid character in pattern (Only [a-zA-Z0-9_.-] allowed, or a single *)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (list[SDL_strlen(list) - 1] == ';') {
|
||||
return "Empty pattern not allowed";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
/* The following are utility functions to help implementations.
|
||||
They are ordered by scope largeness, decreasing. All implementations
|
||||
should use them, as they check for invalid filters. Where they are unused,
|
||||
the validate_* function further down below should be used. */
|
||||
|
||||
/* Transform the name given in argument into something viable for the engine.
|
||||
Useful if there are special characters to avoid on certain platforms (such
|
||||
as "|" with Zenity). */
|
||||
typedef char *(NameTransform)(const char * name);
|
||||
|
||||
/* Converts all the filters into a single string. */
|
||||
/* <prefix>[filter]{<separator>[filter]...}<suffix> */
|
||||
char *convert_filters(const SDL_DialogFileFilter *filters, NameTransform ntf,
|
||||
const char *prefix, const char *separator,
|
||||
const char *suffix, const char *filt_prefix,
|
||||
const char *filt_separator, const char *filt_suffix,
|
||||
const char *ext_prefix, const char *ext_separator,
|
||||
const char *ext_suffix);
|
||||
|
||||
/* Converts one filter into a single string. */
|
||||
/* <prefix>[filter name]<separator>[filter extension list]<suffix> */
|
||||
char *convert_filter(const SDL_DialogFileFilter filter, NameTransform ntf,
|
||||
const char *prefix, const char *separator,
|
||||
const char *suffix, const char *ext_prefix,
|
||||
const char *ext_separator, const char *ext_suffix);
|
||||
|
||||
/* Converts the extenstion list of a filter into a single string. */
|
||||
/* <prefix>[extension]{<separator>[extension]...}<suffix> */
|
||||
char *convert_ext_list(const char *list, const char *prefix,
|
||||
const char *suffix, const char *separator);
|
||||
|
||||
/* Must be used if convert_* functions aren't used */
|
||||
/* Returns an error message if there's a problem, NULL otherwise */
|
||||
const char *validate_filters(const SDL_DialogFileFilter *filters);
|
||||
const char *validate_list(const char *list);
|
|
@ -19,6 +19,7 @@
|
|||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
#include "../SDL_dialog_utils.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <UniformTypeIdentifiers/UTType.h>
|
||||
|
@ -36,6 +37,20 @@ void show_file_dialog(cocoa_FileDialogType type, SDL_DialogFileCallback callback
|
|||
SDL_SetError("tvOS and iOS don't support path-based file dialogs");
|
||||
callback(userdata, NULL, -1);
|
||||
#else
|
||||
const char *msg = validate_filters(filters);
|
||||
|
||||
if (msg) {
|
||||
SDL_SetError("%s", msg);
|
||||
callback(userdata, NULL, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (SDL_GetHint(SDL_HINT_FILE_DIALOG_DRIVER) != NULL) {
|
||||
SDL_SetError("File dialog driver unsupported");
|
||||
callback(userdata, NULL, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* NSOpenPanel inherits from NSSavePanel */
|
||||
NSSavePanel *dialog;
|
||||
NSOpenPanel *dialog_as_open;
|
||||
|
@ -83,10 +98,6 @@ void show_file_dialog(cocoa_FileDialogType type, SDL_DialogFileCallback callback
|
|||
[types addObject: [NSString stringWithFormat: @"%s", pattern_ptr]];
|
||||
}
|
||||
pattern_ptr = c + 1;
|
||||
} else if (!((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || (*c >= '0' && *c <= '9') || *c == '.' || *c == '_' || *c == '-' || (*c == '*' && (c[1] == '\0' || c[1] == ';')))) {
|
||||
SDL_SetError("Illegal character in pattern name: %c (Only alphanumeric characters, periods, underscores and hyphens allowed)", *c);
|
||||
callback(userdata, NULL, -1);
|
||||
SDL_free(pattern);
|
||||
} else if (*c == '*') {
|
||||
has_all_files = 1;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
extern "C" {
|
||||
#include "../SDL_dialog_utils.h"
|
||||
}
|
||||
#include "../../core/haiku/SDL_BeApp.h"
|
||||
|
||||
#include <string>
|
||||
|
@ -197,10 +200,33 @@ void ShowDialog(bool save, SDL_DialogFileCallback callback, void *userdata, bool
|
|||
return;
|
||||
}
|
||||
|
||||
const char *msg = validate_filters(filters);
|
||||
|
||||
if (msg) {
|
||||
SDL_SetError("%s", msg);
|
||||
callback(userdata, NULL, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (SDL_GetHint(SDL_HINT_FILE_DIALOG_DRIVER) != NULL) {
|
||||
SDL_SetError("File dialog driver unsupported");
|
||||
callback(userdata, NULL, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
// No unique_ptr's because they need to survive the end of the function
|
||||
CallbackLooper *looper = new CallbackLooper(callback, userdata);
|
||||
BMessenger *messenger = new BMessenger(NULL, looper);
|
||||
SDLBRefFilter *filter = new SDLBRefFilter(filters);
|
||||
CallbackLooper *looper = new(std::nothrow) CallbackLooper(callback, userdata);
|
||||
BMessenger *messenger = new(std::nothrow) BMessenger(NULL, looper);
|
||||
SDLBRefFilter *filter = new(std::nothrow) SDLBRefFilter(filters);
|
||||
|
||||
if (looper == NULL || messenger == NULL || filter == NULL) {
|
||||
SDL_free(looper);
|
||||
SDL_free(messenger);
|
||||
SDL_free(filter);
|
||||
SDL_OutOfMemory();
|
||||
callback(userdata, NULL, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
BEntry entry;
|
||||
entry_ref entryref;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
#include "./SDL_dialog.h"
|
||||
#include "../SDL_dialog_utils.h"
|
||||
|
||||
#include "../../core/linux/SDL_dbus.h"
|
||||
|
||||
|
@ -270,6 +270,14 @@ static void DBus_OpenDialog(const char *method, const char *method_title, SDL_Di
|
|||
static char *default_parent_window = "";
|
||||
SDL_PropertiesID props = SDL_GetWindowProperties(window);
|
||||
|
||||
const char *err_msg = validate_filters(filters);
|
||||
|
||||
if (err_msg) {
|
||||
SDL_SetError("%s", err_msg);
|
||||
callback(userdata, NULL, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dbus == NULL) {
|
||||
SDL_SetError("Failed to connect to DBus");
|
||||
return;
|
||||
|
|
|
@ -27,31 +27,56 @@ static void (*detected_open)(SDL_DialogFileCallback callback, void* userdata, SD
|
|||
static void (*detected_save)(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, const char* default_location) = NULL;
|
||||
static void (*detected_folder)(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const char* default_location, SDL_bool allow_many) = NULL;
|
||||
|
||||
/* Returns non-zero on success, 0 on failure */
|
||||
static int detect_available_methods(void)
|
||||
static int detect_available_methods(const char *value);
|
||||
|
||||
void SDLCALL hint_callback(void *userdata, const char *name, const char *oldValue, const char *newValue)
|
||||
{
|
||||
if (SDL_Portal_detect()) {
|
||||
detected_open = SDL_Portal_ShowOpenFileDialog;
|
||||
detected_save = SDL_Portal_ShowSaveFileDialog;
|
||||
detected_folder = SDL_Portal_ShowOpenFolderDialog;
|
||||
return 1;
|
||||
detect_available_methods(newValue);
|
||||
}
|
||||
|
||||
static void set_callback(void)
|
||||
{
|
||||
static SDL_bool is_set = SDL_FALSE;
|
||||
|
||||
if (is_set == SDL_FALSE) {
|
||||
is_set = SDL_TRUE;
|
||||
SDL_AddHintCallback(SDL_HINT_FILE_DIALOG_DRIVER, hint_callback, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns non-zero on success, 0 on failure */
|
||||
static int detect_available_methods(const char *value)
|
||||
{
|
||||
const char *driver = value ? value : SDL_GetHint(SDL_HINT_FILE_DIALOG_DRIVER);
|
||||
|
||||
set_callback();
|
||||
|
||||
if (driver == NULL || SDL_strcmp(driver, "portal") == 0) {
|
||||
if (SDL_Portal_detect()) {
|
||||
detected_open = SDL_Portal_ShowOpenFileDialog;
|
||||
detected_save = SDL_Portal_ShowSaveFileDialog;
|
||||
detected_folder = SDL_Portal_ShowOpenFolderDialog;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (SDL_Zenity_detect()) {
|
||||
detected_open = SDL_Zenity_ShowOpenFileDialog;
|
||||
detected_save = SDL_Zenity_ShowSaveFileDialog;
|
||||
detected_folder = SDL_Zenity_ShowOpenFolderDialog;
|
||||
return 2;
|
||||
if (driver == NULL || SDL_strcmp(driver, "zenity") == 0) {
|
||||
if (SDL_Zenity_detect()) {
|
||||
detected_open = SDL_Zenity_ShowOpenFileDialog;
|
||||
detected_save = SDL_Zenity_ShowSaveFileDialog;
|
||||
detected_folder = SDL_Zenity_ShowOpenFolderDialog;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_SetError("No supported method for file dialogs");
|
||||
SDL_SetError("File dialog driver unsupported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SDL_ShowOpenFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, const char* default_location, SDL_bool allow_many)
|
||||
{
|
||||
/* Call detect_available_methods() again each time in case the situation changed */
|
||||
if (!detected_open && !detect_available_methods()) {
|
||||
if (!detected_open && !detect_available_methods(NULL)) {
|
||||
/* SetError() done by detect_available_methods() */
|
||||
callback(userdata, NULL, -1);
|
||||
return;
|
||||
|
@ -63,7 +88,7 @@ void SDL_ShowOpenFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL
|
|||
void SDL_ShowSaveFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const SDL_DialogFileFilter *filters, const char* default_location)
|
||||
{
|
||||
/* Call detect_available_methods() again each time in case the situation changed */
|
||||
if (!detected_save && !detect_available_methods()) {
|
||||
if (!detected_save && !detect_available_methods(NULL)) {
|
||||
/* SetError() done by detect_available_methods() */
|
||||
callback(userdata, NULL, -1);
|
||||
return;
|
||||
|
@ -75,7 +100,7 @@ void SDL_ShowSaveFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL
|
|||
void SDL_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* userdata, SDL_Window* window, const char* default_location, SDL_bool allow_many)
|
||||
{
|
||||
/* Call detect_available_methods() again each time in case the situation changed */
|
||||
if (!detected_folder && !detect_available_methods()) {
|
||||
if (!detected_folder && !detect_available_methods(NULL)) {
|
||||
/* SetError() done by detect_available_methods() */
|
||||
callback(userdata, NULL, -1);
|
||||
return;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
#include "./SDL_dialog.h"
|
||||
#include "../SDL_dialog_utils.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -65,6 +65,22 @@ typedef struct
|
|||
} \
|
||||
}
|
||||
|
||||
char *zenity_clean_name(const char *name)
|
||||
{
|
||||
char *newname = SDL_strdup(name);
|
||||
|
||||
/* Filter out "|", which Zenity considers a special character. Let's hope
|
||||
there aren't others. TODO: find something better. */
|
||||
for (char *c = newname; *c; c++) {
|
||||
if (*c == '|') {
|
||||
/* Zenity doesn't support escaping with \ */
|
||||
*c = '/';
|
||||
}
|
||||
}
|
||||
|
||||
return newname;
|
||||
}
|
||||
|
||||
/* Exec call format:
|
||||
*
|
||||
* /usr/bin/env zenity --file-selection --separator=\n [--multiple]
|
||||
|
@ -147,68 +163,15 @@ static char** generate_args(const zenityArgs* info)
|
|||
const SDL_DialogFileFilter *filter_ptr = info->filters;
|
||||
|
||||
while (filter_ptr->name && filter_ptr->pattern) {
|
||||
/* *Normally*, no filter arg should exceed 4096 bytes. */
|
||||
char buffer[4096];
|
||||
char *filter_str = convert_filter(*filter_ptr, zenity_clean_name,
|
||||
"--file-filter=", " | ", "",
|
||||
"*.", " *.", "");
|
||||
|
||||
SDL_snprintf(buffer, 4096, "--file-filter=%s | *.", filter_ptr->name);
|
||||
size_t i_buf = SDL_strlen(buffer);
|
||||
|
||||
/* "|" is a special character for Zenity */
|
||||
for (char *c = buffer; *c; c++) {
|
||||
if (*c == '|') {
|
||||
*c = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i_pat = 0; i_buf < 4095 && filter_ptr->pattern[i_pat]; i_pat++) {
|
||||
const char *c = filter_ptr->pattern + i_pat;
|
||||
|
||||
if (*c == ';') {
|
||||
/* Disallow empty patterns (might bug Zenity) */
|
||||
int at_end = (c[1] == '\0');
|
||||
int at_mid = (c[1] == ';');
|
||||
int at_beg = (i_pat == 0);
|
||||
if (at_end || at_mid || at_beg) {
|
||||
const char *pos_str = "";
|
||||
|
||||
if (at_end) {
|
||||
pos_str = "end";
|
||||
} else if (at_mid) {
|
||||
pos_str = "middle";
|
||||
} else if (at_beg) {
|
||||
pos_str = "beginning";
|
||||
}
|
||||
|
||||
SDL_SetError("Empty pattern file extension (at %s of list)", pos_str);
|
||||
CLEAR_AND_RETURN()
|
||||
}
|
||||
|
||||
if (i_buf + 3 >= 4095) {
|
||||
i_buf += 3;
|
||||
break;
|
||||
}
|
||||
|
||||
buffer[i_buf++] = ' ';
|
||||
buffer[i_buf++] = '*';
|
||||
buffer[i_buf++] = '.';
|
||||
} else if (*c == '*' && (c[1] == '\0' || c[1] == ';') && (i_pat == 0 || *(c - 1) == ';')) {
|
||||
buffer[i_buf++] = '*';
|
||||
} else if (!((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || (*c >= '0' && *c <= '9') || *c == '.' || *c == '_' || *c == '-')) {
|
||||
SDL_SetError("Illegal character in pattern name: %c (Only alphanumeric characters, periods, underscores and hyphens allowed)", *c);
|
||||
CLEAR_AND_RETURN()
|
||||
} else {
|
||||
buffer[i_buf++] = *c;
|
||||
}
|
||||
}
|
||||
|
||||
if (i_buf >= 4095) {
|
||||
SDL_SetError("Filter '%s' wouldn't fit in a 4096 byte buffer; please report your use case if you need filters that long", filter_ptr->name);
|
||||
if (!filter_str) {
|
||||
CLEAR_AND_RETURN()
|
||||
}
|
||||
|
||||
buffer[i_buf] = '\0';
|
||||
|
||||
argv[nextarg++] = SDL_strdup(buffer);
|
||||
argv[nextarg++] = filter_str;
|
||||
CHECK_OOM()
|
||||
|
||||
filter_ptr++;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
#include "../SDL_dialog_utils.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
|
@ -122,61 +123,42 @@ void windows_ShowFileDialog(void *ptr)
|
|||
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, default_folder, -1, filebuffer, MAX_PATH);
|
||||
}
|
||||
|
||||
size_t len = 0;
|
||||
for (const SDL_DialogFileFilter *filter = filters; filter && filter->name && filter->pattern; filter++) {
|
||||
const char *pattern_ptr = filter->pattern;
|
||||
len += SDL_strlen(filter->name) + SDL_strlen(filter->pattern) + 4;
|
||||
while (*pattern_ptr) {
|
||||
if (*pattern_ptr == ';') {
|
||||
len += 2;
|
||||
}
|
||||
pattern_ptr++;
|
||||
}
|
||||
}
|
||||
wchar_t *filterlist = SDL_malloc((len + 1) * sizeof(wchar_t));
|
||||
/* '\x01' is used in place of a null byte */
|
||||
char *filterlist = convert_filters(filters, NULL, "", "", "\x01", "",
|
||||
"\x01", "\x01", "*.", ";*.", "");
|
||||
|
||||
if (!filterlist) {
|
||||
SDL_OutOfMemory();
|
||||
callback(userdata, NULL, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
wchar_t *filter_ptr = filterlist;
|
||||
for (const SDL_DialogFileFilter *filter = filters; filter && filter->name && filter->pattern; filter++) {
|
||||
size_t l = SDL_strlen(filter->name);
|
||||
const char *pattern_ptr = filter->pattern;
|
||||
int filter_len = SDL_strlen(filterlist);
|
||||
|
||||
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filter->name, -1, filter_ptr, MAX_PATH);
|
||||
filter_ptr += l + 1;
|
||||
|
||||
*filter_ptr++ = L'*';
|
||||
*filter_ptr++ = L'.';
|
||||
while (*pattern_ptr) {
|
||||
if (*pattern_ptr == ';') {
|
||||
*filter_ptr++ = L';';
|
||||
*filter_ptr++ = L'*';
|
||||
*filter_ptr++ = L'.';
|
||||
} else if (*pattern_ptr == '*' && (pattern_ptr[1] == '\0' || pattern_ptr[1] == ';')) {
|
||||
*filter_ptr++ = L'*';
|
||||
} else if (!((*pattern_ptr >= 'a' && *pattern_ptr <= 'z') || (*pattern_ptr >= 'A' && *pattern_ptr <= 'Z') || (*pattern_ptr >= '0' && *pattern_ptr <= '9') || *pattern_ptr == '.' || *pattern_ptr == '_' || *pattern_ptr == '-')) {
|
||||
SDL_SetError("Illegal character in pattern name: %c (Only alphanumeric characters, periods, underscores and hyphens allowed)", *pattern_ptr);
|
||||
callback(userdata, NULL, -1);
|
||||
} else {
|
||||
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, pattern_ptr, 1, filter_ptr, 1);
|
||||
filter_ptr++;
|
||||
}
|
||||
pattern_ptr++;
|
||||
for (char *c = filterlist; *c; c++) {
|
||||
if (*c == '\x01') {
|
||||
*c = '\0';
|
||||
}
|
||||
*filter_ptr++ = '\0';
|
||||
}
|
||||
*filter_ptr = '\0';
|
||||
|
||||
int filter_wlen = MultiByteToWideChar(CP_UTF8, 0, filterlist, filter_len, NULL, 0);
|
||||
wchar_t *filter_wchar = SDL_malloc(filter_wlen * sizeof(wchar_t));
|
||||
|
||||
if (!filter_wchar) {
|
||||
SDL_OutOfMemory();
|
||||
SDL_free(filterlist);
|
||||
callback(userdata, NULL, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
MultiByteToWideChar(CP_UTF8, 0, filterlist, filter_len, filter_wchar, filter_wlen);
|
||||
|
||||
SDL_free(filterlist);
|
||||
|
||||
OPENFILENAMEW dialog;
|
||||
dialog.lStructSize = sizeof(OPENFILENAME);
|
||||
dialog.hwndOwner = window;
|
||||
dialog.hInstance = 0;
|
||||
dialog.lpstrFilter = filterlist;
|
||||
dialog.lpstrFilter = filter_wchar;
|
||||
dialog.lpstrCustomFilter = NULL;
|
||||
dialog.nMaxCustFilter = 0;
|
||||
dialog.nFilterIndex = 0;
|
||||
|
@ -198,7 +180,7 @@ void windows_ShowFileDialog(void *ptr)
|
|||
|
||||
BOOL result = pGetAnyFileName(&dialog);
|
||||
|
||||
SDL_free(filterlist);
|
||||
SDL_free(filter_wchar);
|
||||
|
||||
if (result) {
|
||||
if (!(flags & OFN_ALLOWMULTISELECT)) {
|
||||
|
@ -401,6 +383,13 @@ void SDL_ShowOpenFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL
|
|||
winArgs *args;
|
||||
SDL_Thread *thread;
|
||||
|
||||
if (SDL_GetHint(SDL_HINT_FILE_DIALOG_DRIVER) != NULL) {
|
||||
SDL_Log("%s", SDL_GetHint(SDL_HINT_FILE_DIALOG_DRIVER));
|
||||
SDL_SetError("File dialog driver unsupported");
|
||||
callback(userdata, NULL, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
args = SDL_malloc(sizeof(winArgs));
|
||||
if (args == NULL) {
|
||||
SDL_OutOfMemory();
|
||||
|
@ -421,6 +410,7 @@ void SDL_ShowOpenFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL
|
|||
|
||||
if (thread == NULL) {
|
||||
callback(userdata, NULL, -1);
|
||||
SDL_free(args);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -432,6 +422,12 @@ void SDL_ShowSaveFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL
|
|||
winArgs *args;
|
||||
SDL_Thread *thread;
|
||||
|
||||
if (SDL_GetHint(SDL_HINT_FILE_DIALOG_DRIVER) != NULL) {
|
||||
SDL_SetError("File dialog driver unsupported");
|
||||
callback(userdata, NULL, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
args = SDL_malloc(sizeof(winArgs));
|
||||
if (args == NULL) {
|
||||
SDL_OutOfMemory();
|
||||
|
@ -452,6 +448,7 @@ void SDL_ShowSaveFileDialog(SDL_DialogFileCallback callback, void* userdata, SDL
|
|||
|
||||
if (thread == NULL) {
|
||||
callback(userdata, NULL, -1);
|
||||
SDL_free(args);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -463,6 +460,12 @@ void SDL_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* userdata, S
|
|||
winFArgs *args;
|
||||
SDL_Thread *thread;
|
||||
|
||||
if (SDL_GetHint(SDL_HINT_FILE_DIALOG_DRIVER) != NULL) {
|
||||
SDL_SetError("File dialog driver unsupported");
|
||||
callback(userdata, NULL, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
args = SDL_malloc(sizeof(winFArgs));
|
||||
if (args == NULL) {
|
||||
SDL_OutOfMemory();
|
||||
|
@ -479,6 +482,7 @@ void SDL_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void* userdata, S
|
|||
|
||||
if (thread == NULL) {
|
||||
callback(userdata, NULL, -1);
|
||||
SDL_free(args);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue