Skip to content

Commit

Permalink
Improve the patterns arg in al_create_native_file_dialog
Browse files Browse the repository at this point in the history
This adds:

- Support for multiple pattern sets (Windows/Linux)
- Supports for custom pattern set descriptions (Windows/Linux)
- Support for MIME types on MacOS

Also:

- Remove the implicit catch-all pattern on Windows (the "All files
  *.*"), it was inconsistent with other platforms.
- Make MacOS do a better job at extracting the file extension
- Improve documentation overall
  • Loading branch information
SiegeLordEx authored and SiegeLord committed Jun 23, 2024
1 parent a3f2ec0 commit ce05752
Show file tree
Hide file tree
Showing 8 changed files with 354 additions and 165 deletions.
22 changes: 19 additions & 3 deletions addons/native_dialog/allegro5/internal/aintern_native_dialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@
#include "allegro5/internal/aintern_vector.h"
#include "allegro5/internal/aintern_native_dialog_cfg.h"

typedef struct
{
ALLEGRO_USTR_INFO info;
bool is_mime;
bool is_catchall;
} _AL_PATTERN;

typedef struct
{
ALLEGRO_USTR_INFO desc;
_AL_VECTOR patterns_vec;
} _AL_PATTERNS_AND_DESC;

typedef struct ALLEGRO_NATIVE_DIALOG ALLEGRO_NATIVE_DIALOG;

/* We could use different structs for the different dialogs. But why
Expand All @@ -19,7 +32,10 @@ struct ALLEGRO_NATIVE_DIALOG
ALLEGRO_PATH *fc_initial_path;
size_t fc_path_count;
ALLEGRO_PATH **fc_paths;
ALLEGRO_USTR *fc_patterns;
/* Vector of _AL_PATTERNS_AND_DESC, substrings index into fc_patterns_ustr.
*/
_AL_VECTOR fc_patterns;
ALLEGRO_USTR *fc_patterns_ustr;

/* Only used by message box. */
ALLEGRO_USTR *mb_heading;
Expand All @@ -42,7 +58,7 @@ struct ALLEGRO_NATIVE_DIALOG
bool is_active;
void *window;
void *async_queue;

_AL_LIST_ITEM *dtor_item;
};

Expand Down Expand Up @@ -113,7 +129,7 @@ extern bool _al_walk_over_menu(ALLEGRO_MENU *menu, bool (*proc)
_AL_MENU_ID *_al_find_parent_menu_by_id(ALLEGRO_DISPLAY *display, uint16_t unique_id);
bool _al_find_menu_item_unique(ALLEGRO_MENU *haystack, uint16_t unique_id, ALLEGRO_MENU **menu,
int *index);

/* Platform Specific Functions
* ---------------------------
* Each of these should return true if successful. If at all possible, they
Expand Down
25 changes: 24 additions & 1 deletion addons/native_dialog/android_dialog.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,30 @@ bool _al_show_native_file_dialog(ALLEGRO_DISPLAY *display, ALLEGRO_NATIVE_DIALOG

/* open the file chooser */
ALLEGRO_DEBUG("waiting for the file chooser");
bool ret = open_file_chooser(fd->flags, al_cstr(fd->fc_patterns), initial_path, &fd->fc_paths, &fd->fc_path_count);
ALLEGRO_USTR *mime_patterns = al_ustr_new("");
bool first = true;
bool any_catchalls = false;
for (size_t i = 0; i < _al_vector_size(&fd->fc_patterns); i++) {
_AL_PATTERNS_AND_DESC *patterns_and_desc = _al_vector_ref(&fd->fc_patterns, (int)i);
for (size_t j = 0; j < _al_vector_size(&patterns_and_desc->patterns_vec); j++) {
_AL_PATTERN *pattern = _al_vector_ref(&patterns_and_desc->patterns_vec, (int)j);
if (pattern->is_catchall) {
any_catchalls = true;
break;
}
if (pattern->is_mime)
{
if (!first)
al_ustr_append_chr(mime_patterns, ';');
first = false;
al_ustr_append(mime_patterns, al_ref_info(&pattern->info));
}
}
}
if (any_catchalls)
al_ustr_truncate(mime_patterns, 0);
bool ret = open_file_chooser(fd->flags, al_cstr(mime_patterns), initial_path, &fd->fc_paths, &fd->fc_path_count);
al_ustr_free(mime_patterns);
ALLEGRO_DEBUG("done waiting for the file chooser");

/* ensure predictable behavior */
Expand Down
117 changes: 115 additions & 2 deletions addons/native_dialog/dialog.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ ALLEGRO_DEBUG_CHANNEL("native_dialog")

static bool inited_addon = false;

static _AL_VECTOR make_patterns_and_desc_vec(const ALLEGRO_USTR* patterns_ustr);
static void free_patterns_and_desc_vec(_AL_VECTOR *patterns_and_desc_vec);

/* Function: al_init_native_dialog_addon
*/
bool al_init_native_dialog_addon(void)
Expand Down Expand Up @@ -60,7 +63,8 @@ ALLEGRO_FILECHOOSER *al_create_native_file_dialog(
fc->fc_initial_path = al_create_path(initial_path);
}
fc->title = al_ustr_new(title);
fc->fc_patterns = al_ustr_new(patterns);
fc->fc_patterns_ustr = al_ustr_new(patterns);
fc->fc_patterns = make_patterns_and_desc_vec(fc->fc_patterns_ustr);
fc->flags = mode;

fc->dtor_item = _al_register_destructor(_al_dtor_list, "native_dialog", fc,
Expand Down Expand Up @@ -115,7 +119,8 @@ void al_destroy_native_file_dialog(ALLEGRO_FILECHOOSER *dialog)
al_destroy_path(fd->fc_paths[i]);
}
al_free(fd->fc_paths);
al_ustr_free(fd->fc_patterns);
free_patterns_and_desc_vec(&fd->fc_patterns);
al_ustr_free(fd->fc_patterns_ustr);
al_free(fd);
}

Expand Down Expand Up @@ -165,4 +170,112 @@ uint32_t al_get_allegro_native_dialog_version(void)
}


static _AL_VECTOR split_patterns(const ALLEGRO_USTR* ustr)
{
int pattern_start = 0;
int cur_pos = 0;
bool is_mime = false;
bool is_catchall = true;
_AL_VECTOR patterns_vec;
_al_vector_init(&patterns_vec, sizeof(_AL_PATTERN));
/* This does a straightforward split on semicolons. We check for MIME types
* and catchalls, as some backends care about this.
*/
while (true) {
int32_t c = al_ustr_get(ustr, cur_pos);
if (c == -1 || c == ';') {
ALLEGRO_USTR_INFO info;
const ALLEGRO_USTR *pattern_ustr = al_ref_buffer(&info,
al_cstr(ustr) + pattern_start, cur_pos - pattern_start);
if (al_ustr_length(pattern_ustr) > 0) {
_AL_PATTERN pattern = {
.info = info, .is_mime = is_mime, .is_catchall = is_catchall
};
*((_AL_PATTERN*)_al_vector_alloc_back(&patterns_vec)) = pattern;
}

is_mime = false;
is_catchall = true;
pattern_start = cur_pos + 1;
}
else if (c == '/') {
is_mime = true;
is_catchall = false;
}
else if (c != '*' && c != '.') {
is_catchall = false;
}
if (c == -1) {
break;
}
al_ustr_next(ustr, &cur_pos);
}
return patterns_vec;
}


static _AL_VECTOR make_patterns_and_desc_vec(const ALLEGRO_USTR* patterns_ustr)
{
_AL_VECTOR patterns_and_desc_vec;
_al_vector_init(&patterns_and_desc_vec, sizeof(_AL_PATTERNS_AND_DESC));

if (al_ustr_length(patterns_ustr) == 0)
return patterns_and_desc_vec;

int line_start = 0;
int chunk_start = 0;
int cur_pos = 0;
/* Split by newlines + spaces simultaneously. Chunks are separated by
* spaces, and the final chunk is interpreted as the actual patterns, while
* the rest of the line is the "description".
*/
while (true) {
int32_t c = al_ustr_get(patterns_ustr, cur_pos);
if (c == ' ') {
chunk_start = cur_pos + 1;
}
else if (c == '\n' || c == -1) {
ALLEGRO_USTR_INFO desc_info, real_patterns_info;
const ALLEGRO_USTR *ustr;
/* Strip trailing whitespace. */
int desc_end = chunk_start - 1;
for (; desc_end >= line_start; desc_end--) {
if (al_ustr_get(patterns_ustr, desc_end) != ' ')
break;
}
al_ref_ustr(&desc_info, patterns_ustr, line_start, desc_end + 1);
ustr = al_ref_ustr(&real_patterns_info, patterns_ustr, chunk_start, cur_pos);

_AL_VECTOR patterns_vec = split_patterns(ustr);
if (_al_vector_size(&patterns_vec) > 0) {
_AL_PATTERNS_AND_DESC patterns_and_desc = {
.desc = desc_info,
.patterns_vec = patterns_vec
};
*((_AL_PATTERNS_AND_DESC*)_al_vector_alloc_back(&patterns_and_desc_vec)) = patterns_and_desc;
}

chunk_start = cur_pos + 1;
line_start = cur_pos + 1;
}
if (c == -1) {
break;
}
al_ustr_next(patterns_ustr, &cur_pos);
}

return patterns_and_desc_vec;
}


static void free_patterns_and_desc_vec(_AL_VECTOR *patterns_and_desc_vec)
{
for (size_t i = 0; i < _al_vector_size(patterns_and_desc_vec); i++) {
_AL_PATTERNS_AND_DESC *patterns_and_desc =
(_AL_PATTERNS_AND_DESC*)_al_vector_ref(patterns_and_desc_vec, i);
_al_vector_free(&patterns_and_desc->patterns_vec);
}
_al_vector_free(patterns_and_desc_vec);
}

/* vim: set sts=3 sw=3 et: */
48 changes: 21 additions & 27 deletions addons/native_dialog/gtk_filesel.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,36 +98,30 @@ static gboolean create_gtk_file_dialog(gpointer data)
if (fd->flags & ALLEGRO_FILECHOOSER_MULTIPLE)
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(window), true);

/* FIXME: Move all this filter parsing stuff into a common file. */
if (al_ustr_size(fd->fc_patterns) > 0) {
for (size_t i = 0; i < _al_vector_size(&fd->fc_patterns); i++)
{
_AL_PATTERNS_AND_DESC *patterns_and_desc = _al_vector_ref(&fd->fc_patterns, i);
const ALLEGRO_USTR *desc = al_ref_info(&patterns_and_desc->desc);
GtkFileFilter* filter = gtk_file_filter_new();
int start = 0;
int end = 0;
bool is_mime_type = false;
while (true) {
int32_t c = al_ustr_get(fd->fc_patterns, end);
if (c < 0 || c == ';') {
if (end - start > 0) {
ALLEGRO_USTR* pattern = al_ustr_dup_substr(fd->fc_patterns, start, end);
if (is_mime_type) {
gtk_file_filter_add_mime_type(filter, al_cstr(pattern));
}
else {
gtk_file_filter_add_pattern(filter, al_cstr(pattern));
}
al_ustr_free(pattern);
}
start = end + 1;
is_mime_type = false;
if (al_ustr_size(desc) > 0) {
char *cstr = al_cstr_dup(desc);
gtk_file_filter_set_name(filter, cstr);
al_free(cstr);
}
else {
gtk_file_filter_set_name(filter, "All supported files");
}
for (size_t j = 0; j < _al_vector_size(&patterns_and_desc->patterns_vec); j++) {
_AL_PATTERN *pattern = _al_vector_ref(&patterns_and_desc->patterns_vec, j);
char *cstr = al_cstr_dup(al_ref_info(&pattern->info));
if (pattern->is_mime) {
gtk_file_filter_add_mime_type(filter, cstr);
}
else {
gtk_file_filter_add_pattern(filter, cstr);
}
if (c == '/')
is_mime_type = true;
if (c < 0)
break;
end += al_utf8_width(c);
al_free(cstr);
}

gtk_file_filter_set_name(filter, "All supported files");
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(window), filter);
}

Expand Down
Loading

0 comments on commit ce05752

Please sign in to comment.