aboutsummaryrefslogtreecommitdiffstats
path: root/filters.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--filters.c173
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);
+}