Try to load a system-wide rc file if any, before per-user rc file.
[geeqie.git] / src / filefilter.c
1 /*
2  * Geeqie
3  * (C) 2006 John Ellis
4  * Copyright (C) 2008 The Geeqie Team
5  *
6  * Author: John Ellis
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13
14 #include "main.h"
15 #include "filefilter.h"
16
17 #include "cache.h"
18 #include "rcfile.h"
19 #include "secure_save.h"
20 #include "thumb_standard.h"
21 #include "ui_fileops.h"
22
23
24 /*
25  *-----------------------------------------------------------------------------
26  * file filtering
27  *-----------------------------------------------------------------------------
28  */
29
30 static GList *filter_list = NULL;
31 static GList *extension_list = NULL;
32 static GList *sidecar_ext_list = NULL;
33
34 static GList *file_class_extension_list[FILE_FORMAT_CLASSES];
35
36
37 gint ishidden(const gchar *name)
38 {
39         if (name[0] != '.') return FALSE;
40         if (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')) return FALSE;
41         return TRUE;
42 }
43
44 static FilterEntry *filter_entry_new(const gchar *key, const gchar *description,
45                                      const gchar *extensions, FileFormatClass file_class, gboolean enabled)
46 {
47         FilterEntry *fe;
48
49         fe = g_new0(FilterEntry, 1);
50         fe->key = g_strdup(key);
51         fe->description = g_strdup(description);
52         fe->extensions = g_strdup(extensions);
53         fe->enabled = enabled;
54         fe->file_class = file_class;
55
56         return fe;
57 }
58
59 static void filter_entry_free(FilterEntry *fe)
60 {
61         if (!fe) return;
62
63         g_free(fe->key);
64         g_free(fe->description);
65         g_free(fe->extensions);
66         g_free(fe);
67 }
68
69 GList *filter_get_list(void)
70 {
71         return filter_list;
72 }
73
74 void filter_remove_entry(FilterEntry *fe)
75 {
76         if (!g_list_find(filter_list, fe)) return;
77
78         filter_list = g_list_remove(filter_list, fe);
79         filter_entry_free(fe);
80 }
81
82 static FilterEntry *filter_get_by_key(const gchar *key)
83 {
84         GList *work;
85
86         if (!key) return NULL;
87
88         work = filter_list;
89         while (work)
90                 {
91                 FilterEntry *fe = work->data;
92                 work = work->next;
93
94                 if (strcmp(fe->key, key) == 0) return fe;
95                 }
96
97         return NULL;
98 }
99
100 static gint filter_key_exists(const gchar *key)
101 {
102         return (filter_get_by_key(key) == NULL ? FALSE : TRUE);
103 }
104
105 void filter_add(const gchar *key, const gchar *description, const gchar *extensions, FileFormatClass file_class, gint enabled)
106 {
107         filter_list = g_list_append(filter_list, filter_entry_new(key, description, extensions, file_class, enabled));
108 }
109
110 void filter_add_unique(const gchar *description, const gchar *extensions, FileFormatClass file_class, gint enabled)
111 {
112         gchar *key;
113         guint n;
114
115         key = g_strdup("user0");
116         n = 1;
117         while (filter_key_exists(key))
118                 {
119                 g_free(key);
120                 if (n > 999) return;
121                 key = g_strdup_printf("user%d", n);
122                 n++;
123                 }
124
125         filter_add(key, description, extensions, file_class, enabled);
126         g_free(key);
127 }
128
129 static void filter_add_if_missing(const gchar *key, const gchar *description, const gchar *extensions, FileFormatClass file_class, gint enabled)
130 {
131         GList *work;
132
133         if (!key) return;
134
135         work = filter_list;
136         while (work)
137                 {
138                 FilterEntry *fe = work->data;
139                 work = work->next;
140                 if (fe->key && strcmp(fe->key, key) == 0)
141                         {
142                         if (fe->file_class == FORMAT_CLASS_UNKNOWN)
143                                 fe->file_class = file_class;    /* for compatibility */
144                         return;
145                         }
146                 }
147
148         filter_add(key, description, extensions, file_class, enabled);
149 }
150
151 void filter_reset(void)
152 {
153         GList *work;
154
155         work = filter_list;
156         while (work)
157                 {
158                 FilterEntry *fe = work->data;
159                 work = work->next;
160                 filter_entry_free(fe);
161                 }
162
163         g_list_free(filter_list);
164         filter_list = NULL;
165 }
166
167 void filter_add_defaults(void)
168 {
169         GSList *list, *work;
170
171         list = gdk_pixbuf_get_formats();
172         work = list;
173         while (work)
174                 {
175                 GdkPixbufFormat *format;
176                 gchar *name;
177                 gchar *desc;
178                 gchar **extensions;
179                 GString *filter = NULL;
180                 guint i;
181
182                 format = work->data;
183                 work = work->next;
184
185                 name = gdk_pixbuf_format_get_name(format);
186                 desc = gdk_pixbuf_format_get_description(format);
187                 extensions = gdk_pixbuf_format_get_extensions(format);
188
189                 i = 0;
190                 while (extensions[i])
191                         {
192                         if (!filter)
193                                 {
194                                 filter = g_string_new(".");
195                                 filter = g_string_append(filter, extensions[i]);
196                                 }
197                         else
198                                 {
199                                 filter = g_string_append(filter, ";.");
200                                 filter = g_string_append(filter, extensions[i]);
201                                 }
202                         i++;
203                         }
204
205                 DEBUG_1("loader reported [%s] [%s] [%s]", name, desc, filter->str);
206
207                 filter_add_if_missing(name, desc, filter->str, FORMAT_CLASS_IMAGE, TRUE);
208
209                 g_free(name);
210                 g_free(desc);
211                 g_strfreev(extensions);
212                 g_string_free(filter, TRUE);
213                 }
214         g_slist_free(list);
215
216         /* add defaults even if gdk-pixbuf does not have them, but disabled */
217         filter_add_if_missing("jpeg", "JPEG group", ".jpg;.jpeg;.jpe", FORMAT_CLASS_IMAGE, FALSE);
218         filter_add_if_missing("png", "Portable Network Graphic", ".png", FORMAT_CLASS_IMAGE, FALSE);
219         filter_add_if_missing("tiff", "Tiff", ".tif;.tiff", FORMAT_CLASS_IMAGE, FALSE);
220         filter_add_if_missing("pnm", "Packed Pixel formats", ".pbm;.pgm;.pnm;.ppm", FORMAT_CLASS_IMAGE, FALSE);
221         filter_add_if_missing("gif", "Graphics Interchange Format", ".gif", FORMAT_CLASS_IMAGE, FALSE);
222         filter_add_if_missing("xbm", "X bitmap", ".xbm", FORMAT_CLASS_IMAGE, FALSE);
223         filter_add_if_missing("xpm", "X pixmap", ".xpm", FORMAT_CLASS_IMAGE, FALSE);
224         filter_add_if_missing("bmp", "Bitmap", ".bmp", FORMAT_CLASS_IMAGE, FALSE);
225         filter_add_if_missing("ico", "Icon file", ".ico;.cur", FORMAT_CLASS_IMAGE, FALSE);
226         filter_add_if_missing("ras", "Raster", ".ras", FORMAT_CLASS_IMAGE, FALSE);
227         filter_add_if_missing("svg", "Scalable Vector Graphics", ".svg", FORMAT_CLASS_IMAGE, FALSE);
228
229         /* non-image files that might be desirable to show */
230         filter_add_if_missing("xmp", "XMP sidecar", ".xmp", FORMAT_CLASS_META, TRUE);
231         filter_add_if_missing("gqv", GQ_APPNAME " image collection", ".gqv", FORMAT_CLASS_META, TRUE);
232
233         /* These are the raw camera formats with embedded jpeg/exif.
234          * (see format_raw.c and/or exiv2.cc)
235          */
236         filter_add_if_missing("arw", "Sony raw format", ".arw;.srf;.sr2", FORMAT_CLASS_RAWIMAGE, TRUE);
237         filter_add_if_missing("crw", "Canon raw format", ".crw;.cr2", FORMAT_CLASS_RAWIMAGE, TRUE);
238         filter_add_if_missing("kdc", "Kodak raw format", ".kdc;.dcr", FORMAT_CLASS_RAWIMAGE, TRUE);
239         filter_add_if_missing("raf", "Fujifilm raw format", ".raf", FORMAT_CLASS_RAWIMAGE, TRUE);
240         filter_add_if_missing("mef", "Mamiya raw format", ".mef;.mos", FORMAT_CLASS_RAWIMAGE, TRUE);
241         filter_add_if_missing("mrw", "Minolta raw format", ".mrw", FORMAT_CLASS_RAWIMAGE, TRUE);
242         filter_add_if_missing("nef", "Nikon raw format", ".nef", FORMAT_CLASS_RAWIMAGE, TRUE);
243         filter_add_if_missing("orf", "Olympus raw format", ".orf", FORMAT_CLASS_RAWIMAGE, TRUE);
244         filter_add_if_missing("pef", "Pentax or Samsung raw format", ".pef;.ptx", FORMAT_CLASS_RAWIMAGE, TRUE);
245         filter_add_if_missing("dng", "Adobe Digital Negative raw format", ".dng", FORMAT_CLASS_RAWIMAGE, TRUE);
246         filter_add_if_missing("x3f", "Sigma raw format", ".x3f", FORMAT_CLASS_RAWIMAGE, TRUE);
247         filter_add_if_missing("raw", "Panasonic raw format", ".raw", FORMAT_CLASS_RAWIMAGE, TRUE);
248         filter_add_if_missing("r3d", "Red raw format", ".r3d", FORMAT_CLASS_RAWIMAGE, TRUE);
249         filter_add_if_missing("3fr", "Hasselblad raw format", ".3fr", FORMAT_CLASS_RAWIMAGE, TRUE);
250         filter_add_if_missing("erf", "Epson raw format", ".erf", FORMAT_CLASS_RAWIMAGE, TRUE);
251 }
252
253 GList *filter_to_list(const gchar *extensions)
254 {
255         GList *list = NULL;
256         const gchar *p;
257
258         if (!extensions) return NULL;
259
260         p = extensions;
261         while (*p != '\0')
262                 {
263                 const gchar *b;
264                 guint l = 0;
265
266                 b = p;
267                 while (*p != '\0' && *p != ';')
268                         {
269                         p++;
270                         l++;
271                         }
272                 list = g_list_append(list, g_strndup(b, l));
273                 if (*p == ';') p++;
274                 }
275
276         return list;
277 }
278
279 void filter_rebuild(void)
280 {
281         GList *work;
282         guint i;
283
284         string_list_free(extension_list);
285         extension_list = NULL;
286
287         for (i = 0; i < FILE_FORMAT_CLASSES; i++)
288                 {
289                 string_list_free(file_class_extension_list[i]);
290                 file_class_extension_list[i] = NULL;
291                 }
292
293         work = filter_list;
294         while (work)
295                 {
296                 FilterEntry *fe;
297
298                 fe = work->data;
299                 work = work->next;
300
301                 if (fe->enabled)
302                         {
303                         GList *ext;
304
305                         ext = filter_to_list(fe->extensions);
306                         if (ext) extension_list = g_list_concat(extension_list, ext);
307
308                         if (fe->file_class >= 0 && fe->file_class < FILE_FORMAT_CLASSES)
309                                 {
310                                 ext = filter_to_list(fe->extensions);
311                                 if (ext) file_class_extension_list[fe->file_class] = g_list_concat(file_class_extension_list[fe->file_class], ext);
312                                 }
313                         else
314                                 {
315                                 log_printf("WARNING: invalid file class %d\n", fe->file_class);
316                                 }
317                         }
318                 }
319 }
320
321 gint filter_name_exists(const gchar *name)
322 {
323         GList *work;
324         guint ln;
325
326         if (!extension_list || options->file_filter.disable) return TRUE;
327
328         ln = strlen(name);
329         work = extension_list;
330         while (work)
331                 {
332                 gchar *filter = work->data;
333                 guint lf = strlen(filter);
334
335                 if (ln >= lf)
336                         {
337                         /* FIXME: utf8 */
338                         if (strncasecmp(name + ln - lf, filter, lf) == 0) return TRUE;
339                         }
340                 work = work->next;
341                 }
342
343         return FALSE;
344 }
345
346 gint filter_file_class(const gchar *name, FileFormatClass file_class)
347 {
348         GList *work;
349         guint ln;
350
351         if (file_class < 0 || file_class >= FILE_FORMAT_CLASSES)
352                 {
353                 log_printf("WARNING: invalid file class %d\n", file_class);
354                 return FALSE;
355                 }
356
357         ln = strlen(name);
358         work = file_class_extension_list[file_class];
359         while (work)
360                 {
361                 gchar *filter = work->data;
362                 guint lf = strlen(filter);
363
364                 if (ln >= lf)
365                         {
366                         /* FIXME: utf8 */
367                         if (strncasecmp(name + ln - lf, filter, lf) == 0) return TRUE;
368                         }
369                 work = work->next;
370                 }
371
372         return FALSE;
373 }
374
375 void filter_write_list(SecureSaveInfo *ssi)
376 {
377         GList *work;
378
379         work = filter_list;
380         while (work)
381                 {
382                 FilterEntry *fe = work->data;
383                 work = work->next;
384
385                 gchar *extensions = escquote_value(fe->extensions);
386                 gchar *description = escquote_value(fe->description);
387
388                 secure_fprintf(ssi, "file_filter.ext: \"%s%s\" %s %s %d\n",
389                                (fe->enabled) ? "" : "#",
390                                fe->key, extensions, description, fe->file_class);
391                 g_free(extensions);
392                 g_free(description);
393                 }
394 }
395
396 void filter_parse(const gchar *text)
397 {
398         const gchar *p;
399         gchar *key;
400         gchar *ext;
401         gchar *desc;
402         gint enabled = TRUE;
403         guint file_class;
404
405         if (!text || text[0] != '"') return;
406
407         key = quoted_value(text, &p);
408         if (!key) return;
409
410         ext = quoted_value(p, &p);
411         desc = quoted_value(p, &p);
412
413         file_class = strtoul(p, NULL, 10);
414
415         if (file_class >= FILE_FORMAT_CLASSES) file_class = FORMAT_CLASS_UNKNOWN;
416
417         if (key && key[0] == '#')
418                 {
419                 gchar *tmp;
420                 tmp = g_strdup(key + 1);
421                 g_free(key);
422                 key = tmp;
423
424                 enabled = FALSE;
425                 }
426
427         if (key && strlen(key) > 0 && ext)
428                 {
429                 FilterEntry *fe = filter_get_by_key(key);
430
431                 if (fe != NULL) filter_remove_entry(fe);
432                 filter_add(key, desc, ext, file_class, enabled);
433                 }
434
435         g_free(key);
436         g_free(ext);
437         g_free(desc);
438 }
439
440 /*
441  *-----------------------------------------------------------------------------
442  * sidecar extension list
443  *-----------------------------------------------------------------------------
444  */
445
446 GList *sidecar_ext_get_list(void)
447 {
448         return sidecar_ext_list;
449 }
450
451 void sidecar_ext_parse(const gchar *text, gint quoted)
452 {
453         GList *work;
454         gchar *value;
455
456         work = sidecar_ext_list;
457         while (work)
458                 {
459                 gchar *ext = work->data;
460                 work = work->next;
461                 g_free(ext);
462                 }
463         g_list_free(sidecar_ext_list);
464         sidecar_ext_list = NULL;
465
466         if (quoted)
467                 value = quoted_value(text, NULL);
468         else
469                 value = g_strdup(text);
470
471         if (value == NULL) return;
472
473         sidecar_ext_list = filter_to_list(value);
474
475         g_free(value);
476 }
477
478 void sidecar_ext_write(SecureSaveInfo *ssi)
479 {
480         secure_fprintf(ssi, "sidecar.ext: \"%s\"\n", sidecar_ext_to_string());
481 }
482
483 gchar *sidecar_ext_to_string(void)
484 {
485         GList *work;
486         GString *str = g_string_new("");
487
488         work = sidecar_ext_list;
489         while (work)
490                 {
491                 gchar *ext = work->data;
492                 work = work->next;
493                 g_string_append(str, ext);
494                 if (work) g_string_append(str, ";");
495                 }
496         return g_string_free(str, FALSE);
497 }
498
499 void sidecar_ext_add_defaults(void)
500 {
501         sidecar_ext_parse(".jpg;.cr2;.nef;.crw;.xmp", FALSE);
502 }