preset: Automatic code style fixes
[platform/upstream/gstreamer.git] / gst / gstpreset.c
1 /* GStreamer
2  * Copyright (C) 2006 Stefan Kost <ensonic@users.sf.net>
3  *
4  * gstpreset.c: helper interface for element presets
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 /**
22  * SECTION:gstpreset
23  * @short_description: helper interface for element presets
24  *
25  * This interface offers methods to query and manipulate parameter preset sets.
26  * A preset is a bunch of property settings, together with meta data and a name.
27  * The name of a preset serves as key for subsequent method calls to manipulate
28  * single presets.
29  * All instances of one type will share the list of presets. The list is created
30  * on demand, if presets are not used, the list is not created.
31  *
32  * The interface comes with a default implementation that serves most plugins.
33  * Wrapper plugins will override most methods to implement support for the
34  * native preset format of those wrapped plugins.
35  * One method that is useful to be overridden is gst_preset_get_property_names().
36  * With that one can control which properties are saved and in which order.
37  *
38  * The default implementation supports presets located in a system directory, 
39  * application specific directory and in the users home directory. When getting
40  * a list of presets individual presets are read and overlaid in 1) system, 
41  * 2) application and 3) user order. Whenever an earlier entry is newer, the
42  * later entries will be updated. 
43  * 
44  */
45 /* FIXME:
46  * - non racyness
47  *   - we need to avoid two instances writing the preset file
48  *     -> flock(fileno()), http://www.ecst.csuchico.edu/~beej/guide/ipc/flock.html
49  *     -> open exclusive
50  *     -> better save the new file to a tempfile and then rename?
51  *   - we like to know when any other instance makes changes to the keyfile
52  *     - then ui can be updated
53  *     - and we make sure that we don't lose edits
54  *   -> its the same problem actually, once for inside a process, once system-
55  *      wide
56  *     - can we use a lock inside a names shared memory segment?
57  *
58  * - should there be a 'preset-list' property to get the preset list
59  *   (and to connect a notify:: to to listen for changes)
60  *   we could use gnome_vfs_monitor_add() to monitor the user preset_file.
61  *
62  * - should there be a 'preset-name' property so that we can set a preset via
63  *   gst-launch, or should we handle this with special syntax in gst-launch:
64  *   gst-launch element preset:<preset-name> property=value ...
65  *   - this would allow to have preset-bundles too (a preset on bins that
66  *     specifies presets for children
67  */
68
69 #include "gst_private.h"
70
71 #include "gstpreset.h"
72 #include "gstchildproxy.h"
73 #include "gstinfo.h"
74 #include "gstvalue.h"
75
76 #ifdef HAVE_UNISTD_H
77 #include <unistd.h>
78 #endif
79 #include <glib/gstdio.h>
80
81 #ifdef G_OS_WIN32
82 #define WIN32_LEAN_AND_MEAN
83 #include <windows.h>
84
85 extern HMODULE _priv_gst_dll_handle;
86 #endif
87
88 #define GST_CAT_DEFAULT preset_debug
89 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
90
91 /* defines for keyfile usage, this group contains the element type name and
92  * version these presets belong to. */
93 #define PRESET_HEADER "_presets_"
94
95 /* keys of the preset header section */
96 #define PRESET_HEADER_ELEMENT_NAME "element-name"
97 #define PRESET_HEADER_VERSION "version"
98
99 static GQuark preset_user_path_quark = 0;
100 static GQuark preset_app_path_quark = 0;
101 static GQuark preset_system_path_quark = 0;
102 static GQuark preset_quark = 0;
103
104 /*static GQuark property_list_quark = 0;*/
105
106 /* the application can set a custom path that is checked in addition to standard
107  * system and user dirs. This helps to develop new presets first local to the
108  * application.
109  */
110 static gchar *preset_app_dir = NULL;
111
112 /* default iface implementation */
113
114 static gboolean gst_preset_default_save_presets_file (GstPreset * preset);
115
116 /*
117  * preset_get_paths:
118  * @preset: a #GObject that implements #GstPreset
119  * @preset_user_path: location for path or %NULL
120  * @preset_app_path: location for path or %NULL
121  * @preset_system_path: location for path or %NULL
122  *
123  * Fetch the preset_path for user local, application specific and system wide
124  * settings. Don't free after use.
125  *
126  * Returns: %FALSE if no paths could be found.
127  */
128 static gboolean
129 preset_get_paths (GstPreset * preset, const gchar ** preset_user_path,
130     const gchar ** preset_app_path, const gchar ** preset_system_path)
131 {
132   GType type = G_TYPE_FROM_INSTANCE (preset);
133   gchar *preset_path;
134   const gchar *element_name;
135
136   /* we use the element name when we must construct the paths */
137   element_name = G_OBJECT_TYPE_NAME (preset);
138   GST_INFO_OBJECT (preset, "element_name: '%s'", element_name);
139
140   if (preset_user_path) {
141     /* preset user path requested, see if we have it cached in the qdata */
142     if (!(preset_path = g_type_get_qdata (type, preset_user_path_quark))) {
143       gchar *preset_dir;
144
145       /* user presets go in  user's XDG data directory. */
146       preset_dir = g_build_filename (g_get_user_data_dir (),
147           "gstreamer-" GST_API_VERSION, "presets", NULL);
148       GST_INFO_OBJECT (preset, "user_preset_dir: '%s'", preset_dir);
149       preset_path =
150           g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.prs", preset_dir,
151           element_name);
152       GST_INFO_OBJECT (preset, "user_preset_path: '%s'", preset_path);
153       /* create dirs */
154       g_mkdir_with_parents (preset_dir, 0755);
155       g_free (preset_dir);
156
157       /* cache the preset path to the type */
158       g_type_set_qdata (type, preset_user_path_quark, preset_path);
159     }
160     *preset_user_path = preset_path;
161   }
162
163   if (preset_app_path) {
164     if (preset_app_dir) {
165       if (!(preset_path = g_type_get_qdata (type, preset_system_path_quark))) {
166         preset_path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.prs",
167             preset_app_dir, element_name);
168         GST_INFO_OBJECT (preset, "application_preset_path: '%s'", preset_path);
169
170         /* cache the preset path to the type */
171         g_type_set_qdata (type, preset_app_path_quark, preset_path);
172       }
173       *preset_app_path = preset_path;
174     } else {
175       *preset_app_path = NULL;
176     }
177   }
178
179   if (preset_system_path) {
180     /* preset system path requested, see if we have it cached in the qdata */
181     if (!(preset_path = g_type_get_qdata (type, preset_system_path_quark))) {
182       gchar *preset_dir;
183
184       /* system presets in '$GST_DATADIR/gstreamer-1.0/presets/GstAudioPanorama.prs' */
185 #ifdef G_OS_WIN32
186       gchar *basedir =
187           g_win32_get_package_installation_directory_of_module
188           (_priv_gst_dll_handle);
189       preset_dir =
190           g_build_filename (basedir, "share", "gstreamer-" GST_API_VERSION,
191           "presets", NULL);
192       g_free (basedir);
193 #else
194       preset_dir = g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION,
195           "presets", NULL);
196 #endif
197       GST_INFO_OBJECT (preset, "system_preset_dir: '%s'", preset_dir);
198       preset_path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.prs",
199           preset_dir, element_name);
200       GST_INFO_OBJECT (preset, "system_preset_path: '%s'", preset_path);
201       /* create dirs */
202       g_mkdir_with_parents (preset_dir, 0755);
203       g_free (preset_dir);
204
205       /* cache the preset path to the type */
206       g_type_set_qdata (type, preset_system_path_quark, preset_path);
207     }
208     *preset_system_path = preset_path;
209   }
210   return TRUE;
211 }
212
213 static gboolean
214 preset_skip_property (GParamSpec * property)
215 {
216   if (((property->flags & G_PARAM_READWRITE) != G_PARAM_READWRITE) ||
217       (property->flags & G_PARAM_CONSTRUCT_ONLY))
218     return TRUE;
219   /* FIXME: skip GST_PARAM_NOT_PRESETABLE, see #522205 */
220   return FALSE;
221 }
222
223 static guint64
224 preset_parse_version (const gchar * str_version)
225 {
226   guint major, minor, micro, nano;
227   gint num;
228
229   major = minor = micro = nano = 0;
230
231   /* parse version (e.g. 0.10.15.1) to guint64 */
232   num = sscanf (str_version, "%u.%u.%u.%u", &major, &minor, &micro, &nano);
233   /* make sure we have at least "major.minor" */
234   if (num > 1) {
235     guint64 version;
236
237     version = ((((major << 8 | minor) << 8) | micro) << 8) | nano;
238     GST_DEBUG ("version %s -> %" G_GUINT64_FORMAT, str_version, version);
239     return version;
240   }
241   return G_GUINT64_CONSTANT (0);
242 }
243
244 static GKeyFile *
245 preset_open_and_parse_header (GstPreset * preset, const gchar * preset_path,
246     guint64 * preset_version)
247 {
248   GKeyFile *in;
249   GError *error = NULL;
250   gboolean res;
251   const gchar *element_name;
252   gchar *name;
253
254   in = g_key_file_new ();
255
256   res = g_key_file_load_from_file (in, preset_path,
257       G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &error);
258   if (!res || error != NULL)
259     goto load_error;
260
261   /* element type name and preset name must match or we are dealing with a wrong
262    * preset file */
263   element_name = G_OBJECT_TYPE_NAME (preset);
264   name =
265       g_key_file_get_value (in, PRESET_HEADER, PRESET_HEADER_ELEMENT_NAME,
266       NULL);
267
268   if (!name || strcmp (name, element_name))
269     goto wrong_name;
270
271   g_free (name);
272
273   /* get the version now so that the caller can check it */
274   if (preset_version) {
275     gchar *str =
276         g_key_file_get_value (in, PRESET_HEADER, PRESET_HEADER_VERSION, NULL);
277     *preset_version = preset_parse_version (str);
278     g_free (str);
279   }
280
281   return in;
282
283   /* ERRORS */
284 load_error:
285   {
286     GST_WARNING_OBJECT (preset, "Unable to read preset file %s: %s",
287         preset_path, error->message);
288     g_error_free (error);
289     g_key_file_free (in);
290     return NULL;
291   }
292 wrong_name:
293   {
294     GST_WARNING_OBJECT (preset,
295         "Wrong element name in preset file %s. Expected %s, got %s",
296         preset_path, element_name, GST_STR_NULL (name));
297     g_free (name);
298     g_key_file_free (in);
299     return NULL;
300   }
301 }
302
303 static void
304 preset_merge (GKeyFile * system, GKeyFile * user)
305 {
306   gchar *str;
307   gchar **groups, **keys;
308   gsize i, j, num_groups, num_keys;
309
310   /* copy file comment if there is any */
311   if ((str = g_key_file_get_comment (user, NULL, NULL, NULL))) {
312     g_key_file_set_comment (system, NULL, NULL, str, NULL);
313     g_free (str);
314   }
315
316   /* get groups in user and copy into system */
317   groups = g_key_file_get_groups (user, &num_groups);
318   for (i = 0; i < num_groups; i++) {
319     /* copy group comment if there is any */
320     if ((str = g_key_file_get_comment (user, groups[i], NULL, NULL))) {
321       g_key_file_set_comment (system, groups[i], NULL, str, NULL);
322       g_free (str);
323     }
324
325     /* ignore private groups */
326     if (groups[i][0] == '_')
327       continue;
328
329     /* if group already exists in system, remove and re-add keys from user */
330     if (g_key_file_has_group (system, groups[i])) {
331       g_key_file_remove_group (system, groups[i], NULL);
332     }
333
334     keys = g_key_file_get_keys (user, groups[i], &num_keys, NULL);
335     for (j = 0; j < num_keys; j++) {
336       /* copy key comment if there is any */
337       if ((str = g_key_file_get_comment (user, groups[i], keys[j], NULL))) {
338         g_key_file_set_comment (system, groups[i], keys[j], str, NULL);
339         g_free (str);
340       }
341       str = g_key_file_get_value (user, groups[i], keys[j], NULL);
342       g_key_file_set_value (system, groups[i], keys[j], str);
343       g_free (str);
344     }
345     g_strfreev (keys);
346   }
347   g_strfreev (groups);
348 }
349
350 /* reads the user and system presets files and merges them together. This
351  * function caches the GKeyFile on the element type. If there is no existing
352  * preset file, a new in-memory GKeyFile will be created. */
353 static GKeyFile *
354 preset_get_keyfile (GstPreset * preset)
355 {
356   GKeyFile *presets;
357   GType type = G_TYPE_FROM_INSTANCE (preset);
358
359   /* first see if the have a cached version for the type */
360   if (!(presets = g_type_get_qdata (type, preset_quark))) {
361     const gchar *preset_user_path, *preset_app_path, *preset_system_path;
362     guint64 version_system = G_GUINT64_CONSTANT (0);
363     guint64 version_app = G_GUINT64_CONSTANT (0);
364     guint64 version_user = G_GUINT64_CONSTANT (0);
365     guint64 version = G_GUINT64_CONSTANT (0);
366     gboolean merged = FALSE;
367     GKeyFile *in_user, *in_app = NULL, *in_system;
368
369     preset_get_paths (preset, &preset_user_path, &preset_app_path,
370         &preset_system_path);
371
372     /* try to load the user, app and system presets, we do this to get the
373      * versions of all files. */
374     in_user = preset_open_and_parse_header (preset, preset_user_path,
375         &version_user);
376     if (preset_app_path) {
377       in_app = preset_open_and_parse_header (preset, preset_app_path,
378           &version_app);
379     }
380     in_system = preset_open_and_parse_header (preset, preset_system_path,
381         &version_system);
382
383     /* compare version to check for merge */
384     if (in_system) {
385       presets = in_system;
386       version = version_system;
387     }
388     if (in_app) {
389       /* if system version is higher, merge */
390       if (version > version_app) {
391         preset_merge (presets, in_app);
392         g_key_file_free (in_app);
393       } else {
394         if (presets)
395           g_key_file_free (presets);
396         presets = in_app;
397         version = version_system;
398       }
399     }
400     if (in_user) {
401       /* if system or app version is higher, merge */
402       if (version > version_user) {
403         preset_merge (presets, in_user);
404         g_key_file_free (in_user);
405         merged = TRUE;
406       } else {
407         if (presets)
408           g_key_file_free (presets);
409         presets = in_user;
410       }
411     }
412
413     if (!presets) {
414       /* we did not load a user, app or system presets file, create a new one */
415       presets = g_key_file_new ();
416       g_key_file_set_string (presets, PRESET_HEADER, PRESET_HEADER_ELEMENT_NAME,
417           G_OBJECT_TYPE_NAME (preset));
418     }
419
420     /* attach the preset to the type */
421     g_type_set_qdata (type, preset_quark, (gpointer) presets);
422
423     if (merged) {
424       gst_preset_default_save_presets_file (preset);
425     }
426   }
427   return presets;
428 }
429
430 /* get a list of all supported preset names for an element */
431 static gchar **
432 gst_preset_default_get_preset_names (GstPreset * preset)
433 {
434   GKeyFile *presets;
435   gsize i, num_groups;
436   gchar **groups;
437
438   /* get the presets from the type */
439   if (!(presets = preset_get_keyfile (preset)))
440     goto no_presets;
441
442   /* get the groups, which are also the preset names */
443   if (!(groups = g_key_file_get_groups (presets, &num_groups)))
444     goto no_groups;
445
446   /* remove all private group names starting with '_' from the array */
447   for (i = 0; i < num_groups; i++) {
448     if (groups[i][0] == '_') {
449       /* free private group */
450       g_free (groups[i]);
451       /* move last element of list down */
452       num_groups--;
453       /* move last element into removed element */
454       groups[i] = groups[num_groups];
455       groups[num_groups] = NULL;
456     }
457   }
458   /* sort the array now */
459   g_qsort_with_data (groups, num_groups, sizeof (gchar *),
460       (GCompareDataFunc) strcmp, NULL);
461
462   return groups;
463
464   /* ERRORS */
465 no_presets:
466   {
467     GST_WARNING_OBJECT (preset, "Could not load presets");
468     return NULL;
469   }
470 no_groups:
471   {
472     GST_WARNING_OBJECT (preset, "Could not find preset groups");
473     return NULL;
474   }
475 }
476
477 /* get a list of all property names that are used for presets */
478 static gchar **
479 gst_preset_default_get_property_names (GstPreset * preset)
480 {
481   GParamSpec **props;
482   guint i, j = 0, n_props;
483   GObjectClass *gclass;
484   gboolean is_child_proxy;
485   gchar **result = NULL;
486
487   gclass = G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS (preset));
488   is_child_proxy = GST_IS_CHILD_PROXY (preset);
489
490   /* get a list of object properties */
491   props = g_object_class_list_properties (gclass, &n_props);
492   if (props) {
493     /* allocate array big enough to hold the worst case, including a terminating
494      * NULL pointer. */
495     result = g_new (gchar *, n_props + 1);
496
497     /* now filter out the properties that we can use for presets */
498     GST_DEBUG_OBJECT (preset, "  filtering properties: %u", n_props);
499     for (i = 0; i < n_props; i++) {
500       if (preset_skip_property (props[i]))
501         continue;
502       GST_DEBUG_OBJECT (preset, "    using: %s", props[i]->name);
503       result[j++] = g_strdup (props[i]->name);
504     }
505     g_free (props);
506   }
507
508   if (is_child_proxy) {
509     guint c, n_children;
510     GObject *child;
511
512     n_children = gst_child_proxy_get_children_count ((GstChildProxy *) preset);
513     for (c = 0; c < n_children; c++) {
514       child = gst_child_proxy_get_child_by_index ((GstChildProxy *) preset, c);
515       gclass = G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS (child));
516
517       props = g_object_class_list_properties (gclass, &n_props);
518       if (props) {
519         /* resize property name array */
520         result = g_renew (gchar *, result, j + n_props + 1);
521
522         /* now filter out the properties that we can use for presets */
523         GST_DEBUG_OBJECT (preset, "  filtering properties: %u", n_props);
524         for (i = 0; i < n_props; i++) {
525           if (preset_skip_property (props[i]))
526             continue;
527           GST_DEBUG_OBJECT (preset, "    using: %s::%s",
528               GST_OBJECT_NAME (child), props[i]->name);
529           result[j++] = g_strdup_printf ("%s::%s", GST_OBJECT_NAME (child),
530               props[i]->name);
531         }
532         g_free (props);
533       }
534     }
535   }
536   if (!result) {
537     GST_INFO_OBJECT (preset, "object has no properties");
538   } else {
539     result[j] = NULL;
540   }
541   return result;
542 }
543
544 /* load the presets of @name for the instance @preset. Returns %FALSE if something
545  * failed. */
546 static gboolean
547 gst_preset_default_load_preset (GstPreset * preset, const gchar * name)
548 {
549   GKeyFile *presets;
550   gchar **props;
551   guint i;
552   GObjectClass *gclass;
553   gboolean is_child_proxy;
554
555   /* get the presets from the type */
556   if (!(presets = preset_get_keyfile (preset)))
557     goto no_presets;
558
559   /* get the preset name */
560   if (!g_key_file_has_group (presets, name))
561     goto no_group;
562
563   GST_DEBUG_OBJECT (preset, "loading preset : '%s'", name);
564
565   /* get the properties that we can configure in this element */
566   if (!(props = gst_preset_get_property_names (preset)))
567     goto no_properties;
568
569   gclass = G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS (preset));
570   is_child_proxy = GST_IS_CHILD_PROXY (preset);
571
572   /* for each of the property names, find the preset parameter and try to
573    * configure the property with its value */
574   for (i = 0; props[i]; i++) {
575     gchar *str;
576     GValue gvalue = { 0, };
577     GParamSpec *property = NULL;
578
579     /* check if we have a settings for this element property */
580     if (!(str = g_key_file_get_value (presets, name, props[i], NULL))) {
581       /* the element has a property but the parameter is not in the keyfile */
582       GST_WARNING_OBJECT (preset, "parameter '%s' not in preset", props[i]);
583       continue;
584     }
585
586     GST_DEBUG_OBJECT (preset, "setting value '%s' for property '%s'", str,
587         props[i]);
588
589     if (is_child_proxy) {
590       gst_child_proxy_lookup ((GstChildProxy *) preset, props[i], NULL,
591           &property);
592     } else {
593       property = g_object_class_find_property (gclass, props[i]);
594     }
595     if (!property) {
596       /* the parameter was in the keyfile, the element said it supported it but
597        * then the property was not found in the element. This should not happen. */
598       GST_WARNING_OBJECT (preset, "property '%s' not in object", props[i]);
599       g_free (str);
600       continue;
601     }
602
603     /* try to deserialize the property value from the keyfile and set it as
604      * the object property */
605     g_value_init (&gvalue, property->value_type);
606     if (gst_value_deserialize (&gvalue, str)) {
607       if (is_child_proxy) {
608         gst_child_proxy_set_property ((GstChildProxy *) preset, props[i],
609             &gvalue);
610       } else {
611         g_object_set_property ((GObject *) preset, props[i], &gvalue);
612       }
613     } else {
614       GST_WARNING_OBJECT (preset,
615           "deserialization of value '%s' for property '%s' failed", str,
616           props[i]);
617     }
618     g_value_unset (&gvalue);
619     g_free (str);
620   }
621   g_strfreev (props);
622
623   return TRUE;
624
625   /* ERRORS */
626 no_presets:
627   {
628     GST_WARNING_OBJECT (preset, "no presets");
629     return FALSE;
630   }
631 no_group:
632   {
633     GST_WARNING_OBJECT (preset, "no preset named '%s'", name);
634     return FALSE;
635   }
636 no_properties:
637   {
638     GST_INFO_OBJECT (preset, "no properties");
639     return FALSE;
640   }
641 }
642
643 /* save the presets file. A copy of the existing presets file is stored in a
644  * .bak file */
645 static gboolean
646 gst_preset_default_save_presets_file (GstPreset * preset)
647 {
648   GKeyFile *presets;
649   const gchar *preset_path;
650   GError *error = NULL;
651   gchar *bak_file_name;
652   gboolean backup = TRUE;
653   gchar *data;
654   gsize data_size;
655
656   preset_get_paths (preset, &preset_path, NULL, NULL);
657
658   /* get the presets from the type */
659   if (!(presets = preset_get_keyfile (preset)))
660     goto no_presets;
661
662   GST_DEBUG_OBJECT (preset, "saving preset file: '%s'", preset_path);
663
664   /* create backup if possible */
665   bak_file_name = g_strdup_printf ("%s.bak", preset_path);
666   if (g_file_test (bak_file_name, G_FILE_TEST_EXISTS)) {
667     if (g_unlink (bak_file_name)) {
668       backup = FALSE;
669       GST_INFO_OBJECT (preset, "cannot remove old backup file : %s",
670           bak_file_name);
671     }
672   }
673   if (backup) {
674     if (g_rename (preset_path, bak_file_name)) {
675       GST_INFO_OBJECT (preset, "cannot backup file : %s -> %s", preset_path,
676           bak_file_name);
677     }
678   }
679   g_free (bak_file_name);
680
681   /* update gstreamer version */
682   g_key_file_set_string (presets, PRESET_HEADER, PRESET_HEADER_VERSION,
683       PACKAGE_VERSION);
684
685   /* get new contents, wee need this to save it */
686   if (!(data = g_key_file_to_data (presets, &data_size, &error)))
687     goto convert_failed;
688
689   /* write presets */
690   if (!g_file_set_contents (preset_path, data, data_size, &error))
691     goto write_failed;
692
693   g_free (data);
694
695   return TRUE;
696
697   /* ERRORS */
698 no_presets:
699   {
700     GST_WARNING_OBJECT (preset,
701         "no presets, trying to unlink possibly existing preset file: '%s'",
702         preset_path);
703     g_unlink (preset_path);
704     return FALSE;
705   }
706 convert_failed:
707   {
708     GST_WARNING_OBJECT (preset, "can not get the keyfile contents: %s",
709         error->message);
710     g_error_free (error);
711     g_free (data);
712     return FALSE;
713   }
714 write_failed:
715   {
716     GST_WARNING_OBJECT (preset, "Unable to store preset file %s: %s",
717         preset_path, error->message);
718     g_error_free (error);
719     g_free (data);
720     return FALSE;
721   }
722 }
723
724 /* save the preset with the given name */
725 static gboolean
726 gst_preset_default_save_preset (GstPreset * preset, const gchar * name)
727 {
728   GKeyFile *presets;
729   gchar **props;
730   guint i;
731   GObjectClass *gclass;
732   gboolean is_child_proxy;
733
734   GST_INFO_OBJECT (preset, "saving new preset: %s", name);
735
736   /* get the presets from the type */
737   if (!(presets = preset_get_keyfile (preset)))
738     goto no_presets;
739
740   /* take copies of current gobject properties from preset */
741   if (!(props = gst_preset_get_property_names (preset)))
742     goto no_properties;
743
744   gclass = G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS (preset));
745   is_child_proxy = GST_IS_CHILD_PROXY (preset);
746
747   /* loop over the object properties and store the property value in the
748    * keyfile */
749   for (i = 0; props[i]; i++) {
750     GValue gvalue = { 0, };
751     gchar *str;
752     GParamSpec *property = NULL;
753
754     if (is_child_proxy) {
755       gst_child_proxy_lookup ((GstChildProxy *) preset, props[i], NULL,
756           &property);
757     } else {
758       property = g_object_class_find_property (gclass, props[i]);
759     }
760     if (!property) {
761       /* the element said it supported the property but then it does not have
762        * that property. This should not happen. */
763       GST_WARNING_OBJECT (preset, "property '%s' not in object", props[i]);
764       continue;
765     }
766
767     g_value_init (&gvalue, property->value_type);
768     if (is_child_proxy) {
769       gst_child_proxy_get_property ((GstChildProxy *) preset, props[i],
770           &gvalue);
771     } else {
772       g_object_get_property ((GObject *) preset, props[i], &gvalue);
773     }
774
775     if ((str = gst_value_serialize (&gvalue))) {
776       g_key_file_set_string (presets, name, props[i], (gpointer) str);
777       g_free (str);
778     } else {
779       GST_WARNING_OBJECT (preset, "serialization for property '%s' failed",
780           props[i]);
781     }
782     g_value_unset (&gvalue);
783   }
784   GST_INFO_OBJECT (preset, "  saved");
785   g_strfreev (props);
786
787   /* save updated version */
788   return gst_preset_default_save_presets_file (preset);
789
790   /* ERRORS */
791 no_presets:
792   {
793     GST_WARNING_OBJECT (preset, "no presets");
794     return FALSE;
795   }
796 no_properties:
797   {
798     GST_INFO_OBJECT (preset, "no properties");
799     return FALSE;
800   }
801 }
802
803 /* copies all keys and comments from one group to another, deleting the old
804  * group. */
805 static gboolean
806 gst_preset_default_rename_preset (GstPreset * preset, const gchar * old_name,
807     const gchar * new_name)
808 {
809   GKeyFile *presets;
810   gchar *str;
811   gchar **keys;
812   gsize i, num_keys;
813
814   /* get the presets from the type */
815   if (!(presets = preset_get_keyfile (preset)))
816     goto no_presets;
817
818   if (!g_key_file_has_group (presets, old_name))
819     goto no_group;
820
821   /* copy group comment if there is any */
822   if ((str = g_key_file_get_comment (presets, old_name, NULL, NULL))) {
823     g_key_file_set_comment (presets, new_name, NULL, str, NULL);
824     g_free (str);
825   }
826
827   /* get all keys from the old group and copy them in the new group */
828   keys = g_key_file_get_keys (presets, old_name, &num_keys, NULL);
829   for (i = 0; i < num_keys; i++) {
830     /* copy key comment if there is any */
831     if ((str = g_key_file_get_comment (presets, old_name, keys[i], NULL))) {
832       g_key_file_set_comment (presets, new_name, keys[i], str, NULL);
833       g_free (str);
834     }
835     /* copy key value */
836     str = g_key_file_get_value (presets, old_name, keys[i], NULL);
837     g_key_file_set_value (presets, new_name, keys[i], str);
838     g_free (str);
839   }
840   g_strfreev (keys);
841
842   /* remove old group */
843   g_key_file_remove_group (presets, old_name, NULL);
844
845   /* save updated version */
846   return gst_preset_default_save_presets_file (preset);
847
848   /* ERRORS */
849 no_presets:
850   {
851     GST_WARNING_OBJECT (preset, "no presets");
852     return FALSE;
853   }
854 no_group:
855   {
856     GST_WARNING_OBJECT (preset, "no preset named %s", old_name);
857     return FALSE;
858   }
859 }
860
861 /* delete a group from the keyfile */
862 static gboolean
863 gst_preset_default_delete_preset (GstPreset * preset, const gchar * name)
864 {
865   GKeyFile *presets;
866
867   /* get the presets from the type */
868   if (!(presets = preset_get_keyfile (preset)))
869     goto no_presets;
870
871   /* get the group */
872   if (!g_key_file_has_group (presets, name))
873     goto no_group;
874
875   /* remove the group */
876   g_key_file_remove_group (presets, name, NULL);
877
878   /* save updated version */
879   return gst_preset_default_save_presets_file (preset);
880
881   /* ERRORS */
882 no_presets:
883   {
884     GST_WARNING_OBJECT (preset, "no presets");
885     return FALSE;
886   }
887 no_group:
888   {
889     GST_WARNING_OBJECT (preset, "no preset named %s", name);
890     return FALSE;
891   }
892 }
893
894 static gboolean
895 gst_preset_default_set_meta (GstPreset * preset, const gchar * name,
896     const gchar * tag, const gchar * value)
897 {
898   GKeyFile *presets;
899   gchar *key;
900
901   /* get the presets from the type */
902   if (!(presets = preset_get_keyfile (preset)))
903     goto no_presets;
904
905   key = g_strdup_printf ("_meta/%s", tag);
906   if (value && *value) {
907     g_key_file_set_value (presets, name, key, value);
908   } else {
909     g_key_file_remove_key (presets, name, key, NULL);
910   }
911   g_free (key);
912
913   /* save updated keyfile */
914   return gst_preset_default_save_presets_file (preset);
915
916   /* ERRORS */
917 no_presets:
918   {
919     GST_WARNING_OBJECT (preset, "no presets");
920     return FALSE;
921   }
922 }
923
924 /* the caller must free @value after usage */
925 static gboolean
926 gst_preset_default_get_meta (GstPreset * preset, const gchar * name,
927     const gchar * tag, gchar ** value)
928 {
929   GKeyFile *presets;
930   gchar *key;
931
932   /* get the presets from the type */
933   if (!(presets = preset_get_keyfile (preset)))
934     goto no_presets;
935
936   key = g_strdup_printf ("_meta/%s", tag);
937   *value = g_key_file_get_value (presets, name, key, NULL);
938   g_free (key);
939
940   return TRUE;
941
942   /* ERRORS */
943 no_presets:
944   {
945     GST_WARNING_OBJECT (preset, "no presets");
946     *value = NULL;
947     return FALSE;
948   }
949 }
950
951 /* wrapper */
952
953 /**
954  * gst_preset_get_preset_names:
955  * @preset: a #GObject that implements #GstPreset
956  *
957  * Get a copy of preset names as a NULL terminated string array.
958  *
959  * Returns: (transfer full) (array zero-terminated=1) (element-type gchar*):
960  *     list with names, use g_strfreev() after usage.
961  */
962 gchar **
963 gst_preset_get_preset_names (GstPreset * preset)
964 {
965   g_return_val_if_fail (GST_IS_PRESET (preset), NULL);
966
967   return (GST_PRESET_GET_INTERFACE (preset)->get_preset_names (preset));
968 }
969
970 /**
971  * gst_preset_get_property_names:
972  * @preset: a #GObject that implements #GstPreset
973  *
974  * Get a the names of the GObject properties that can be used for presets.
975  *
976  * Returns: (transfer full) (array zero-terminated=1) (element-type gchar*): an
977  *   array of property names which should be freed with g_strfreev() after use.
978  */
979 gchar **
980 gst_preset_get_property_names (GstPreset * preset)
981 {
982   g_return_val_if_fail (GST_IS_PRESET (preset), NULL);
983
984   return (GST_PRESET_GET_INTERFACE (preset)->get_property_names (preset));
985 }
986
987 /**
988  * gst_preset_load_preset:
989  * @preset: a #GObject that implements #GstPreset
990  * @name: preset name to load
991  *
992  * Load the given preset.
993  *
994  * Returns: %TRUE for success, %FALSE if e.g. there is no preset with that @name
995  */
996 gboolean
997 gst_preset_load_preset (GstPreset * preset, const gchar * name)
998 {
999   g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
1000   g_return_val_if_fail (name, FALSE);
1001
1002   return (GST_PRESET_GET_INTERFACE (preset)->load_preset (preset, name));
1003 }
1004
1005 /**
1006  * gst_preset_save_preset:
1007  * @preset: a #GObject that implements #GstPreset
1008  * @name: preset name to save
1009  *
1010  * Save the current object settings as a preset under the given name. If there
1011  * is already a preset by this @name it will be overwritten.
1012  *
1013  * Returns: %TRUE for success, %FALSE
1014  */
1015 gboolean
1016 gst_preset_save_preset (GstPreset * preset, const gchar * name)
1017 {
1018   g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
1019   g_return_val_if_fail (name, FALSE);
1020
1021   return (GST_PRESET_GET_INTERFACE (preset)->save_preset (preset, name));
1022 }
1023
1024 /**
1025  * gst_preset_rename_preset:
1026  * @preset: a #GObject that implements #GstPreset
1027  * @old_name: current preset name
1028  * @new_name: new preset name
1029  *
1030  * Renames a preset. If there is already a preset by the @new_name it will be
1031  * overwritten.
1032  *
1033  * Returns: %TRUE for success, %FALSE if e.g. there is no preset with @old_name
1034  */
1035 gboolean
1036 gst_preset_rename_preset (GstPreset * preset, const gchar * old_name,
1037     const gchar * new_name)
1038 {
1039   g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
1040   g_return_val_if_fail (old_name, FALSE);
1041   g_return_val_if_fail (new_name, FALSE);
1042
1043   return (GST_PRESET_GET_INTERFACE (preset)->rename_preset (preset, old_name,
1044           new_name));
1045 }
1046
1047 /**
1048  * gst_preset_delete_preset:
1049  * @preset: a #GObject that implements #GstPreset
1050  * @name: preset name to remove
1051  *
1052  * Delete the given preset.
1053  *
1054  * Returns: %TRUE for success, %FALSE if e.g. there is no preset with that @name
1055  */
1056 gboolean
1057 gst_preset_delete_preset (GstPreset * preset, const gchar * name)
1058 {
1059   g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
1060   g_return_val_if_fail (name, FALSE);
1061
1062   return (GST_PRESET_GET_INTERFACE (preset)->delete_preset (preset, name));
1063 }
1064
1065 /**
1066  * gst_preset_set_meta:
1067  * @preset: a #GObject that implements #GstPreset
1068  * @name: preset name
1069  * @tag: meta data item name
1070  * @value: new value
1071  *
1072  * Sets a new @value for an existing meta data item or adds a new item. Meta
1073  * data @tag names can be something like e.g. "comment". Supplying %NULL for the
1074  * @value will unset an existing value.
1075  *
1076  * Returns: %TRUE for success, %FALSE if e.g. there is no preset with that @name
1077  */
1078 gboolean
1079 gst_preset_set_meta (GstPreset * preset, const gchar * name, const gchar * tag,
1080     const gchar * value)
1081 {
1082   g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
1083   g_return_val_if_fail (name, FALSE);
1084   g_return_val_if_fail (tag, FALSE);
1085
1086   return GST_PRESET_GET_INTERFACE (preset)->set_meta (preset, name, tag, value);
1087 }
1088
1089 /**
1090  * gst_preset_get_meta:
1091  * @preset: a #GObject that implements #GstPreset
1092  * @name: preset name
1093  * @tag: meta data item name
1094  * @value: (out callee-allocates): value
1095  *
1096  * Gets the @value for an existing meta data @tag. Meta data @tag names can be
1097  * something like e.g. "comment". Returned values need to be released when done.
1098  *
1099  * Returns: %TRUE for success, %FALSE if e.g. there is no preset with that @name
1100  * or no value for the given @tag
1101  */
1102 gboolean
1103 gst_preset_get_meta (GstPreset * preset, const gchar * name, const gchar * tag,
1104     gchar ** value)
1105 {
1106   g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
1107   g_return_val_if_fail (name, FALSE);
1108   g_return_val_if_fail (tag, FALSE);
1109   g_return_val_if_fail (value, FALSE);
1110
1111   return GST_PRESET_GET_INTERFACE (preset)->get_meta (preset, name, tag, value);
1112 }
1113
1114 /**
1115  * gst_preset_set_app_dir:
1116  * @app_dir: the application specific preset dir
1117  *
1118  * Sets an extra directory as an absolute path that should be considered when
1119  * looking for presets. Any presets in the application dir will shadow the 
1120  * system presets.
1121  *
1122  * Returns: %TRUE for success, %FALSE if the dir already has been set
1123  */
1124 gboolean
1125 gst_preset_set_app_dir (const gchar * app_dir)
1126 {
1127   g_return_val_if_fail (app_dir, FALSE);
1128
1129   if (!preset_app_dir) {
1130     preset_app_dir = g_strdup (app_dir);
1131     return TRUE;
1132   }
1133   return FALSE;
1134 }
1135
1136 /**
1137  * gst_preset_get_app_dir:
1138  *
1139  * Gets the directory for application specific presets if set by the
1140  * application.
1141  *
1142  * Returns: the directory or %NULL, don't free or modify the string
1143  */
1144 const gchar *
1145 gst_preset_get_app_dir (void)
1146 {
1147   return preset_app_dir;
1148 }
1149
1150 /* class internals */
1151
1152 static void
1153 gst_preset_class_init (GstPresetInterface * iface)
1154 {
1155   iface->get_preset_names = gst_preset_default_get_preset_names;
1156   iface->get_property_names = gst_preset_default_get_property_names;
1157
1158   iface->load_preset = gst_preset_default_load_preset;
1159   iface->save_preset = gst_preset_default_save_preset;
1160   iface->rename_preset = gst_preset_default_rename_preset;
1161   iface->delete_preset = gst_preset_default_delete_preset;
1162
1163   iface->set_meta = gst_preset_default_set_meta;
1164   iface->get_meta = gst_preset_default_get_meta;
1165 }
1166
1167 static void
1168 gst_preset_base_init (gpointer g_class)
1169 {
1170   static gboolean initialized = FALSE;
1171
1172   if (!initialized) {
1173     /* init default implementation */
1174     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "preset",
1175         GST_DEBUG_FG_WHITE | GST_DEBUG_BG_BLACK, "preset interface");
1176
1177     /* create quarks for use with g_type_{g,s}et_qdata() */
1178     preset_quark = g_quark_from_static_string ("GstPreset::presets");
1179     preset_user_path_quark =
1180         g_quark_from_static_string ("GstPreset::user_path");
1181     preset_app_path_quark = g_quark_from_static_string ("GstPreset::app_path");
1182     preset_system_path_quark =
1183         g_quark_from_static_string ("GstPreset::system_path");
1184
1185 #if 0
1186     property_list_quark = g_quark_from_static_string ("GstPreset::properties");
1187
1188     /* create interface properties, each element would need to override this
1189      *   g_object_class_override_property(gobject_class, PROP_PRESET_NAME, "preset-name");
1190      * and in _set_property() do
1191      *   case PROP_PRESET_NAME: {
1192      *     gchar *name = g_value_get_string (value);
1193      *     if (name)
1194      *       gst_preset_load_preset(preset, name);
1195      *   } break;
1196      */
1197     g_object_interface_install_property (g_class,
1198         g_param_spec_string ("preset-name",
1199             "preset-name property",
1200             "load given preset", NULL, G_PARAM_WRITABLE));
1201 #endif
1202
1203     initialized = TRUE;
1204   }
1205 }
1206
1207 GType
1208 gst_preset_get_type (void)
1209 {
1210   static volatile gsize type = 0;
1211
1212   if (g_once_init_enter (&type)) {
1213     GType _type;
1214     const GTypeInfo info = {
1215       sizeof (GstPresetInterface),
1216       (GBaseInitFunc) gst_preset_base_init,     /* base_init */
1217       NULL,                     /* base_finalize */
1218       (GClassInitFunc) gst_preset_class_init,   /* class_init */
1219       NULL,                     /* class_finalize */
1220       NULL,                     /* class_data */
1221       0,
1222       0,                        /* n_preallocs */
1223       NULL                      /* instance_init */
1224     };
1225     _type = g_type_register_static (G_TYPE_INTERFACE, "GstPreset", &info, 0);
1226     g_once_init_leave (&type, _type);
1227   }
1228   return type;
1229 }