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