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 Unix
This commit is contained in:
parent
5fa87e29e7
commit
6ad390fc50
18 changed files with 501 additions and 125 deletions
237
src/dialog/SDL_dialog_utils.c
Normal file
237
src/dialog/SDL_dialog_utils.c
Normal file
|
@ -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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue