diff options
Diffstat (limited to '')
| -rw-r--r-- | filters.c | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/filters.c b/filters.c new file mode 100644 index 0000000..08bb647 --- /dev/null +++ b/filters.c @@ -0,0 +1,173 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + +struct filter { + char *name; + size_t suffixes_start; + size_t suffixes_end; +}; + +static char *suffixes = NULL; +static struct filter *filters = NULL; +static size_t nfilters = 0u; +static struct filter anyfilter = {NULL, 0u, 0u}; + +static int +file_filter(const GtkFileFilterInfo *info, void *user) +{ + struct filter *filter = user; + size_t pos, name_len, suffix_len; + const char *name, *suffix; + size_t backup; + + if (!info->filename) + return 0; + if (!filter || g_file_test(info->filename, G_FILE_TEST_IS_DIR)) + return 1; + + name = info->filename; + name_len = strlen(name); + if (!name_len) + return 0; + + for (pos = filter->suffixes_start; pos != filter->suffixes_end; pos += suffix_len + 1u) { + suffix = &suffixes[pos]; + suffix_len = strlen(suffix); + + backup = (size_t)(name[name_len - 1u] == '~'); + + if (name_len <= 1u + suffix_len + backup) + continue; + if (name[name_len - (1u + suffix_len + backup)] != '.') + continue; + if (memcmp(&name[name_len - (suffix_len + backup)], suffix, suffix_len)) + continue; + + return 1; + } + + return 0; +} + +static int +filtercmp(const void *a_void, const void *b_void) +{ + const struct filter *a = a_void, *b = b_void; + return strcmp(a->name, b->name); +} + +static void +populate_filters(void) +{ + GSList *formats, *item; + GdkPixbufFormat *format; + char **extensions, **extension, *p; + const char *name; + size_t i, len = 0u; + size_t ext_len, need, count; + + formats = gdk_pixbuf_get_formats(); + + for (item = formats; item; item = item->next) { + format = item->data; + if (gdk_pixbuf_format_is_disabled(format)) + continue; + + name = gdk_pixbuf_format_get_description(format); + extensions = gdk_pixbuf_format_get_extensions(format); + + need = 0u; + count = 0u; + for (extension = extensions; *extension; extension++) { + ext_len = strlen(*extension); + if (need > SIZE_MAX - 1u - ext_len) + eprintf("Out of address space"); + need += ext_len + 1u; + count += 1u; + } + if (need > SIZE_MAX - len) + eprintf("Out of address space"); + if (!count) + goto next; + + filters = ereallocarray(filters, nfilters + 1u, sizeof(*filters)); + filters[nfilters].suffixes_start = len; + filters[nfilters].name = emalloc(strlen(name) + sizeof(" ()") + need + (count - 1u)); + + p = stpcpy(stpcpy(filters[nfilters].name, name), " ("); + suffixes = erealloc(suffixes, need + len); + for (extension = extensions;;) { + ext_len = strlen(*extension); + memcpy(&suffixes[len], *extension, ext_len + 1u); + len += ext_len + 1u; + + memcpy(p, *extension, ext_len); + p = &p[ext_len]; + + if (!*++extension) + break; + p = stpcpy(p, ", "); + } + p = stpcpy(p, ")"); + + for (i = 0u; i < nfilters; i++) { + if (!strcmp(filters[nfilters].name, filters[i].name)) { + free(filters[nfilters].name); + len = filters[nfilters].suffixes_start; + goto next; + } + } + + filters[nfilters].suffixes_end = len; + nfilters += 1u; + + next: + g_strfreev(extensions); + } + + qsort(filters, nfilters, sizeof(*filters), &filtercmp); + + anyfilter.suffixes_end = len; + + g_slist_free(formats); +} + +void +add_image_filters(GtkFileChooser *chooser) +{ + static int populated = 0; + + GtkFileFilter *filter; + size_t i; + + if (!populated) { + populate_filters(); + populated = 1; + } + + filter = gtk_file_filter_new(); + gtk_file_filter_set_name(filter, "All images"); + gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME, file_filter, &anyfilter, NULL); + gtk_file_chooser_add_filter(chooser, filter); + + for (i = 0u; i < nfilters; i++) { + filter = gtk_file_filter_new(); + gtk_file_filter_set_name(filter, filters[i].name); + gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME, file_filter, &filters[i], NULL); + gtk_file_chooser_add_filter(chooser, filter); + } + + filter = gtk_file_filter_new(); + gtk_file_filter_set_name(filter, "All files"); + gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME, file_filter, NULL, NULL); + gtk_file_chooser_add_filter(chooser, filter); +} + +void +free_image_filters(void) +{ + free(suffixes); + while (nfilters) + free(filters[--nfilters].name); + free(filters); +} |
