1 /*
2  * libInstPatch
3  * Copyright (C) 1999-2014 Element Green <element@elementsofsound.org>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public License
7  * as published by the Free Software Foundation; version 2.1
8  * of the License only.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA or on the web at http://www.gnu.org.
19  */
20 /**
21  * SECTION: IpatchConverter
22  * @short_description: Base class for object conversion handlers
23  * @see_also:
24  * @stability: Stable
25  *
26  * A base abstract type for object conversion handlers.
27  */
28 #include <stdio.h>
29 #include <glib.h>
30 #include <glib-object.h>
31 #include "misc.h"
32 #include "IpatchConverter.h"
33 #include "i18n.h"
34 
35 enum
36 {
37     PROP_0,
38     PROP_PROGRESS
39 };
40 
41 /* structure used for log entries (the log is a prepend log, newest items
42    at the head of the list) */
43 typedef struct _LogEntry
44 {
45     GObject *item;	   /* item this message applies to or %NULL */
46     guint8 type; /* type of message and flags (IpatchConverterLogType) */
47     union
48     {
49         char *msg;			/* LOG_INFO/WARN/CRITICAL/FATAL */
50         float rating;		/* LOG_RATING */
51     } data;
52 } LogEntry;
53 
54 
55 static void _ipatch_converter_free_converter_info(IpatchConverterInfo *data,
56                                                   gpointer user_data);
57 static gint priority_GCompareFunc(gconstpointer a, gconstpointer b);
58 static const IpatchConverterInfo *convert_lookup_map_U(GType **array, GType conv_type,
59         GType src_type, GType dest_type, guint flags);
60 static void ipatch_converter_class_init(IpatchConverterClass *klass);
61 static void ipatch_converter_finalize(GObject *gobject);
62 static void ipatch_converter_set_property(GObject *object, guint property_id,
63         const GValue *value,
64         GParamSpec *pspec);
65 static void ipatch_converter_get_property(GObject *object, guint property_id,
66         GValue *value, GParamSpec *pspec);
67 
68 /* lock used for list and hash tables */
69 G_LOCK_DEFINE_STATIC(conv_maps);
70 static GList *conv_maps = NULL;	/* list of all registered IpatchConverterInfo */
71 static gpointer parent_class = NULL;
72 
73 /*------ Initialization/deinitialization of converter system ----------------*/
74 /* Initialize converter system (conv_maps static list) */
_ipatch_converter_init(void)75 void _ipatch_converter_init(void)
76 {
77     /* list of all registered IpatchConverterInfo */
78     conv_maps = NULL;
79 }
80 
81 /* Free converter system */
_ipatch_converter_deinit(void)82 void _ipatch_converter_deinit(void)
83 {
84     /* free list of all registered IpatchConverterInfo */
85     g_list_foreach(conv_maps,
86                    (GFunc)_ipatch_converter_free_converter_info, NULL);
87     g_list_free(conv_maps);
88 }
89 
90 /* free one conv_maps data */
_ipatch_converter_free_converter_info(IpatchConverterInfo * data,gpointer user_data)91 static void _ipatch_converter_free_converter_info(IpatchConverterInfo *data,
92                                                   gpointer user_data)
93 {
94     g_slice_free(IpatchConverterInfo,data);
95 }
96 
97 /*------ Converter system API ----------------------------------------------*/
98 /**
99  * ipatch_convert_objects:
100  * @input: Input object
101  * @output: Output object
102  * @err: Location to store error info or %NULL
103  *
104  * A convenience function for converting from one object to another.  This
105  * function will only work for converters which take exactly one input and
106  * output object.
107  *
108  * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set)
109  */
110 gboolean
ipatch_convert_objects(GObject * input,GObject * output,GError ** err)111 ipatch_convert_objects(GObject *input, GObject *output, GError **err)
112 {
113     IpatchConverter *conv;
114 
115     conv = ipatch_create_converter_for_objects(input, output, err);       // ++ ref converter
116 
117     if(!conv)
118     {
119         return FALSE;
120     }
121 
122     if(!ipatch_converter_convert(conv, err))                              // -- unref converter
123     {
124         g_object_unref(conv);
125         return (FALSE);
126     }
127 
128     g_object_unref(conv);                                                 // -- unref converter
129 
130     return (TRUE);
131 }
132 
133 /**
134  * ipatch_convert_object_to_type:
135  * @object: Object to convert from
136  * @type: Type of object to convert to
137  * @err: Location to store error info or %NULL to ignore
138  *
139  * A convenience function to convert an object to another object of a given
140  * type.  This function will only work for converters which require 1
141  * input and one or zero outputs. The output object is created as needed
142  * and returned.
143  *
144  * Returns: (transfer full): The output object or %NULL on error (in which
145  * case @err may be set). The returned object has a refcount of 1 which the caller owns.
146  */
147 GObject *
ipatch_convert_object_to_type(GObject * object,GType type,GError ** err)148 ipatch_convert_object_to_type(GObject *object, GType type, GError **err)
149 {
150     const IpatchConverterInfo *info;
151     IpatchConverter *conv;
152     GObject *output = NULL;
153     GType convtype;
154 
155     convtype = ipatch_find_converter(G_OBJECT_TYPE(object), type);
156 
157     if(!convtype)
158     {
159         g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNHANDLED_CONVERSION,
160                     _("Unsupported conversion of type %s to %s"),
161                     G_OBJECT_TYPE_NAME(object), g_type_name(type));
162         return (NULL);
163     }
164 
165     info = ipatch_lookup_converter_info(convtype, G_OBJECT_TYPE(object), type);
166     g_return_val_if_fail(info != NULL, NULL);	/* shouldn't happen */
167 
168     if(info->dest_count < 0 || info->dest_count > 1)
169     {
170         g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNSUPPORTED,
171                     _("Conversion from %s to %s requires %d outputs"),
172                     G_OBJECT_TYPE_NAME(object), g_type_name(type),
173                     info->dest_count);
174         return (NULL);
175     }
176 
177     conv = IPATCH_CONVERTER(g_object_new(convtype, NULL));	/* ++ ref */
178 
179     ipatch_converter_add_input(conv, object);
180 
181     if(info->dest_count == 1)	/* if 1 output object expected, create it */
182     {
183         output = g_object_new(type, NULL);	/* ++ ref */
184         ipatch_converter_add_output(conv, output);
185     }
186 
187     if(!ipatch_converter_convert(conv, err))	/* do the conversion */
188     {
189         if(output)
190         {
191             g_object_unref(output);
192         }
193 
194         g_object_unref(conv);
195         return (NULL);
196     }
197 
198     if(!output)
199     {
200         output = ipatch_converter_get_output(conv);    /* ++ ref */
201     }
202 
203     g_object_unref(conv);
204 
205     return (output);	/* !! caller takes over reference */
206 }
207 
208 /**
209  * ipatch_convert_object_to_type_multi: (skip)
210  * @object: Object to convert from
211  * @type: Type of object to convert to
212  * @err: Location to store error info or %NULL to ignore
213  *
214  * A convenience function to convert an object to one or more objects of a given
215  * @type.  This function will work for converters which require 1
216  * input and any number of outputs.
217  *
218  * Returns: (transfer full): List of output objects or %NULL on error (in which
219  * case @err may be set). The returned object list has a refcount of 1 which the caller owns.
220  */
221 IpatchList *
ipatch_convert_object_to_type_multi(GObject * object,GType type,GError ** err)222 ipatch_convert_object_to_type_multi(GObject *object, GType type, GError **err)
223 {
224     return (ipatch_convert_object_to_type_multi_set(object, type, err, NULL));
225 }
226 
227 /**
228  * ipatch_convert_object_to_type_multi_list: (rename-to ipatch_convert_object_to_type_multi)
229  * @object: Object to convert from
230  * @type: Type of object to convert to
231  * @err: Location to store error info or %NULL to ignore
232  *
233  * A convenience function to convert an object to one or more objects of a given
234  * @type.  This function will work for converters which require 1
235  * input and any number of outputs.
236  *
237  * Returns: (element-type GObject.Object) (transfer full): List of output objects or %NULL on error (in which
238  * case @err may be set). Free the list with ipatch_glist_unref_free() when finished using it.
239  *
240  * Since: 1.1.0
241  */
242 GList *
ipatch_convert_object_to_type_multi_list(GObject * object,GType type,GError ** err)243 ipatch_convert_object_to_type_multi_list(GObject *object, GType type, GError **err)
244 {
245     /* Note: empty is intentionally not initialized. It allows to fix some build on ARM and PPC.
246        It is save to use, because empty is not accessed in ipatch_convert_object_to_type_multi_set_vlist
247        when the second last argument is NULL.
248     */
249     va_list empty;
250     return (ipatch_convert_object_to_type_multi_set_vlist(object, type, err, NULL, empty));
251 }
252 
253 /**
254  * ipatch_convert_object_to_type_multi_set: (skip)
255  * @object: Object to convert from
256  * @type: Type of object to convert to
257  * @err: Location to store error info or %NULL to ignore
258  * @first_property_name: (nullable): Name of first property to assign or %NULL
259  * @...: First property value followed by property name/value pairs (as per
260  *   g_object_set()) to assign to the resulting converter, terminated with a
261  *   %NULL property name.
262  *
263  * A convenience function to convert an object to one or more objects of a given
264  * @type.  This function will work for converters which require 1
265  * input and any number of outputs.  Like ipatch_convert_object_to_type_multi()
266  * but allows for properties of the converter to be assigned.
267  *
268  * Returns: (transfer full): List of output objects or %NULL on error (in which
269  * case @err may be set). The returned object list has a refcount of 1 which the caller owns.
270  */
271 IpatchList *
ipatch_convert_object_to_type_multi_set(GObject * object,GType type,GError ** err,const char * first_property_name,...)272 ipatch_convert_object_to_type_multi_set(GObject *object, GType type, GError **err,
273                                         const char *first_property_name, ...)
274 {
275     IpatchList *list;
276     GList *items;
277     va_list args;
278 
279     va_start(args, first_property_name);
280     items = ipatch_convert_object_to_type_multi_set_vlist(object, type,           // ++ alloc items list
281             err, first_property_name, args);
282     va_end(args);
283 
284     if(!items)
285     {
286         return NULL;
287     }
288 
289     list = ipatch_list_new();             // ++ ref new list
290     list->items = items;                  // !! Assign items to list
291     return list;                          // !! Caller takes over list
292 }
293 
294 /**
295  * ipatch_convert_object_to_type_multi_set_vlist: (skip)
296  * @object: Object to convert from
297  * @type: Type of object to convert to
298  * @err: Location to store error info or %NULL to ignore
299  * @first_property_name: (nullable): Name of first property to assign or %NULL
300  * @args: First property value followed by property name/value pairs (as per
301  *   g_object_set()) to assign to the resulting converter, terminated with a
302  *   %NULL property name.
303  *
304  * A convenience function to convert an object to one or more objects of a given
305  * @type.  This function will work for converters which require 1
306  * input and any number of outputs.  Like ipatch_convert_object_to_type_multi()
307  * but allows for properties of the converter to be assigned.
308  *
309  * Returns: (element-type GObject.Object) (transfer full): List of output objects or %NULL on error (in which
310  * case @err may be set). Free the list with ipatch_glist_unref_free() when finished using it.
311  *
312  * Since: 1.1.0
313  */
314 GList *
ipatch_convert_object_to_type_multi_set_vlist(GObject * object,GType type,GError ** err,const char * first_property_name,va_list args)315 ipatch_convert_object_to_type_multi_set_vlist(GObject *object, GType type, GError **err,
316         const char *first_property_name, va_list args)
317 {
318     IpatchConverter *conv;
319     GList *items;
320 
321     conv = ipatch_create_converter_for_object_to_type(object, type, err);         // ++ ref converter
322 
323     if(!conv)
324     {
325         return NULL;
326     }
327 
328     /* assign properties (if any) */
329     if(first_property_name)
330     {
331         g_object_set_valist((GObject *)conv, first_property_name, args);
332     }
333 
334     if(!ipatch_converter_convert(conv, err))	/* do the conversion */
335     {
336         g_object_unref(conv);                       // -- unref converter
337         return (NULL);
338     }
339 
340     items = ipatch_converter_get_outputs_list(conv);      /* ++ alloc object list */
341 
342     g_object_unref(conv);         /* -- unref converter */
343 
344     return (items);	/* !! caller takes over ownership */
345 }
346 
347 /**
348  * ipatch_create_converter:
349  * @src_type: #GObject derived source type
350  * @dest_type: #GObject derived destination type
351  *
352  * Create a converter object for converting an object of type @src_type to
353  * @dest_type. A convenience function, since one could use
354  * ipatch_find_converter() and create an instance of the returned type.
355  * See ipatch_find_converter() for more details.
356  *
357  * Returns: (transfer full): The new converter object with a reference count
358  *   of 1 which the caller owns, or %NULL if there is no matching conversion
359  *   handler type.
360  */
361 IpatchConverter *
ipatch_create_converter(GType src_type,GType dest_type)362 ipatch_create_converter(GType src_type, GType dest_type)
363 {
364     GType conv_type;
365 
366     g_return_val_if_fail(g_type_is_a(src_type, G_TYPE_OBJECT), NULL);
367     g_return_val_if_fail(g_type_is_a(dest_type, G_TYPE_OBJECT), NULL);
368 
369     conv_type = ipatch_find_converter(src_type, dest_type);
370 
371     if(!conv_type)
372     {
373         return (NULL);
374     }
375 
376     /* ++ ref new object and let the caller have it */
377     return (IPATCH_CONVERTER(g_object_new(conv_type, NULL)));
378 }
379 
380 /**
381  * ipatch_create_converter_for_objects:
382  * @input: Input object
383  * @output: Output object
384  * @err: Location to store error info or %NULL
385  *
386  * A convenience function for creating a converter for converting from one
387  * object to another. This function will only work for converters which take
388  * exactly one input and output object.
389  *
390  * Returns: (transfer full): The new converter or %NULL on error
391  *
392  * Since: 1.1.0
393  */
394 IpatchConverter *
ipatch_create_converter_for_objects(GObject * input,GObject * output,GError ** err)395 ipatch_create_converter_for_objects(GObject *input, GObject *output, GError **err)
396 {
397     IpatchConverter *conv;
398 
399     g_return_val_if_fail(G_IS_OBJECT(input), FALSE);
400     g_return_val_if_fail(G_IS_OBJECT(output), FALSE);
401     g_return_val_if_fail(!err || !*err, FALSE);
402 
403     /* ++ ref new converter */
404     conv = ipatch_create_converter(G_OBJECT_TYPE(input), G_OBJECT_TYPE(output));
405 
406     if(!conv)
407     {
408         g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNHANDLED_CONVERSION,
409                     _("Unsupported conversion of type %s to %s."),
410                     G_OBJECT_TYPE_NAME(input), G_OBJECT_TYPE_NAME(output));
411         return (FALSE);
412     }
413 
414     ipatch_converter_add_input(conv, input);
415     ipatch_converter_add_output(conv, output);
416 
417     return (conv);        // !! Caller takes over reference
418 }
419 
420 /**
421  * ipatch_create_converter_for_object_to_type:
422  * @object: Object to convert from
423  * @dest_type: Type of object to convert to
424  * @err: Location to store error info or %NULL to ignore
425  *
426  * A convenience function to create a converter for converting an object to
427  * another object of a given type.
428  *
429  * Returns: (transfer full): The new converter object with a reference count
430  *   of 1 which the caller owns, or %NULL if there is no matching conversion
431  *   handler type.
432  *
433  * Since: 1.1.0
434  */
435 IpatchConverter *
ipatch_create_converter_for_object_to_type(GObject * object,GType dest_type,GError ** err)436 ipatch_create_converter_for_object_to_type(GObject *object, GType dest_type, GError **err)
437 {
438     const IpatchConverterInfo *info;
439     IpatchConverter *conv;
440     GObject *output = NULL;
441     GType convtype;
442     int i;
443 
444     convtype = ipatch_find_converter(G_OBJECT_TYPE(object), dest_type);
445 
446     if(!convtype)
447     {
448         g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNHANDLED_CONVERSION,
449                     _("Unsupported conversion of type %s to %s"),
450                     G_OBJECT_TYPE_NAME(object), g_type_name(dest_type));
451         return (NULL);
452     }
453 
454     info = ipatch_lookup_converter_info(convtype, G_OBJECT_TYPE(object), dest_type);
455     g_return_val_if_fail(info != NULL, NULL);	/* shouldn't happen */
456 
457     conv = IPATCH_CONVERTER(g_object_new(convtype, NULL));	/* ++ ref */
458 
459     ipatch_converter_add_input(conv, object);
460 
461 
462     if(info->dest_count > 0)	/* if outputs are expected, create them */
463     {
464         for(i = 0; i < info->dest_count; i++)
465         {
466             output = g_object_new(dest_type, NULL);	/* ++ ref output */
467             ipatch_converter_add_output(conv, output);
468             g_object_unref(output);   /* -- unref output */
469         }
470     }
471 
472     return (conv);                /* !! caller takes over reference */
473 }
474 
475 /**
476  * ipatch_register_converter_map:
477  * @conv_type: #IpatchConverter derived GType of conversion handler
478  * @flags: #IpatchConverterFlags, #IPATCH_CONVERTER_SRC_DERIVED or
479  *   #IPATCH_CONVERTER_DEST_DERIVED will match derived types of @src_type
480  *   or @dest_type respectively.
481  * @priority: Converter map priority (number from 0 to 100, 0 will use the default).
482  *   #IpatchConverterPriority defines some common priorities. Used for overriding other
483  *   converters for specific types.
484  * @src_type: Type for source object (GObject derived type or interface type).
485  * @src_match: The furthest parent type of @src_type to match (all types in
486  *   between are also matched, 0 or G_TYPE_NONE to only use @src_type).
487  * @src_count: Required number of source items (can also be an #IpatchConverterCount value)
488  * @dest_type: Type for destination object (GObject derived type or interface type).
489  * @dest_match: The furthest parent type of @dest_type to match (all types in
490  *   between are also matched, 0, or G_TYPE_NONE to only use @dest_type).
491  * @dest_count: Required number of destination items (can also be
492  *   an #IpatchConverterCount value).  This value can be 0 in the case where
493  *   no objects should be supplied, but will be instead assigned after
494  *   conversion.
495  *
496  * Registers a #IpatchConverter handler to convert objects of @src_type to @dest_type.
497  * NOTE: Previous versions of this function combined flags and priority into a single param.
498  *
499  * Since: 1.1.0
500  */
501 void
ipatch_register_converter_map(GType conv_type,guint8 flags,guint8 priority,GType src_type,GType src_match,gint8 src_count,GType dest_type,GType dest_match,gint8 dest_count)502 ipatch_register_converter_map(GType conv_type, guint8 flags, guint8 priority,
503                               GType src_type, GType src_match, gint8 src_count,
504                               GType dest_type, GType dest_match, gint8 dest_count)
505 {
506     const IpatchConverterInfo *converter_exists;
507     IpatchConverterInfo *map;
508 
509     g_return_if_fail(g_type_is_a(conv_type, IPATCH_TYPE_CONVERTER));
510     g_return_if_fail(g_type_is_a(src_type, G_TYPE_OBJECT) || G_TYPE_IS_INTERFACE(src_type));
511     g_return_if_fail(g_type_is_a(dest_type, G_TYPE_OBJECT) || G_TYPE_IS_INTERFACE(dest_type));
512 
513     g_return_if_fail(!src_match || g_type_is_a(src_type, src_match));
514     g_return_if_fail(!dest_match || g_type_is_a(dest_type, dest_match));
515 
516     converter_exists = ipatch_lookup_converter_info(conv_type, 0, 0);
517     g_return_if_fail(!converter_exists);
518 
519     priority = flags & 0xFF;
520 
521     if(priority == 0)
522     {
523         priority = IPATCH_CONVERTER_PRIORITY_DEFAULT;
524     }
525 
526     // Enable derived flags for interface types
527 
528     if(G_TYPE_IS_INTERFACE(src_type))
529     {
530         flags |= IPATCH_CONVERTER_SRC_DERIVED;
531     }
532 
533     if(G_TYPE_IS_INTERFACE(dest_type))
534     {
535         flags |= IPATCH_CONVERTER_DEST_DERIVED;
536     }
537 
538     map = g_slice_new(IpatchConverterInfo);
539     map->conv_type = conv_type;
540     map->flags = flags;
541     map->priority = priority;
542     map->src_type = src_type;
543     map->src_match = src_match;
544     map->src_count = src_count;
545     map->dest_type = dest_type;
546     map->dest_match = dest_match;
547     map->dest_count = dest_count;
548 
549     G_LOCK(conv_maps);
550     conv_maps = g_list_insert_sorted(conv_maps, map, priority_GCompareFunc);
551     G_UNLOCK(conv_maps);
552 }
553 
554 /* GList GCompareFunc to sort list by mapping priority */
555 static gint
priority_GCompareFunc(gconstpointer a,gconstpointer b)556 priority_GCompareFunc(gconstpointer a, gconstpointer b)
557 {
558     IpatchConverterInfo *mapa = (IpatchConverterInfo *)a, *mapb = (IpatchConverterInfo *)b;
559 
560     /* priority sorts from highest to lowest, so subtract a from b */
561     return (mapb->priority - mapa->priority);
562 }
563 
564 /**
565  * ipatch_find_converter:
566  * @src_type: #GObject derived source type (0 or G_TYPE_NONE for wildcard - Since 1.1.0)
567  * @dest_type: #GObject derived destination type (0 or G_TYPE_NONE for wildcard - Since 1.1.0)
568  *
569  * Lookup a conversion handler type for a given @src_type to @dest_type
570  * conversion.  In some cases there may be multiple conversion handlers for
571  * the given types, this function only returns the highest priority type.
572  * To get a list of all available converters use ipatch_find_converters().
573  *
574  * Returns: An #IpatchConverter derived GType of the matching conversion
575  *   handler or 0 if no matches.
576  */
577 GType
ipatch_find_converter(GType src_type,GType dest_type)578 ipatch_find_converter(GType src_type, GType dest_type)
579 {
580     const IpatchConverterInfo *info;
581 
582     g_return_val_if_fail(g_type_is_a(src_type, G_TYPE_OBJECT) || G_TYPE_IS_INTERFACE(src_type), 0);
583     g_return_val_if_fail(g_type_is_a(dest_type, G_TYPE_OBJECT) || G_TYPE_IS_INTERFACE(dest_type), 0);
584 
585     G_LOCK(conv_maps);
586     info = convert_lookup_map_U(NULL, 0, src_type, dest_type, 0);
587     G_UNLOCK(conv_maps);
588 
589     return (info ? info->conv_type : 0);
590 }
591 
592 /**
593  * ipatch_find_converters:
594  * @src_type: #GObject derived source type (G_TYPE_NONE for wildcard)
595  * @dest_type: #GObject derived destination type (G_TYPE_NONE for wildcard)
596  * @flags: #IpatchConverterFlags to modify converter matching behavior (logically OR'd with registered converter flags)
597  *
598  * Lookup conversion handler types matching search criteria.
599  *
600  * Returns: (array zero-terminated=1) (transfer full) (nullable): 0 terminated array of #IpatchConverter derived GTypes or %NULL
601  * if no matching converters.  Array should be freed with g_free() when finished using it.
602  *
603  * Since: 1.1.0
604  */
605 GType *
ipatch_find_converters(GType src_type,GType dest_type,guint flags)606 ipatch_find_converters(GType src_type, GType dest_type, guint flags)
607 {
608     GType *types_array;
609 
610     G_LOCK(conv_maps);
611     convert_lookup_map_U(&types_array, 0, src_type, dest_type, flags);
612     G_UNLOCK(conv_maps);
613 
614     return types_array;
615 }
616 
617 /* Lookup a IpatchConverterInfo in the conv_maps list (caller responsible for LOCK of conv_maps).
618  * Pass a pointer for "array" if all matching info is desired or NULL to ignore.
619  * Use 0 or G_TYPE_NONE for wildcard types.
620  * IpatchConverterInfo structures are considered static (never unregistered and don't change).
621  */
622 static const IpatchConverterInfo *
convert_lookup_map_U(GType ** array,GType conv_type,GType src_type,GType dest_type,guint flags)623 convert_lookup_map_U(GType **array, GType conv_type, GType src_type, GType dest_type, guint flags)
624 {
625     GArray *types_array = NULL;
626     IpatchConverterInfo *info;
627     GList *p;
628 
629     if(array)
630     {
631         *array = NULL;
632     }
633 
634     if(conv_type == G_TYPE_NONE)
635     {
636         conv_type = 0;
637     }
638 
639     if(src_type == G_TYPE_NONE)
640     {
641         src_type = 0;
642     }
643 
644     if(dest_type == G_TYPE_NONE)
645     {
646         dest_type = 0;
647     }
648 
649     for(p = conv_maps; p; p = p->next)
650     {
651         info = (IpatchConverterInfo *)(p->data);
652 
653         if(conv_type && conv_type != info->conv_type)
654         {
655             continue;
656         }
657 
658         if(src_type)
659         {
660             if((flags | info->flags) & IPATCH_CONVERTER_SRC_DERIVED)          // If derived flag set (from map or caller) and source type is not a descendant of map type, skip
661             {
662                 // Derived will automatically be set for interface types as well
663                 if(!g_type_is_a(info->src_type, src_type))
664                 {
665                     continue;
666                 }
667             }
668             else if(info->src_match)  // If parent source match set and type is outside of map type range of src_match <-> src_type, skip
669             {
670                 if(!g_type_is_a(src_type, info->src_match)
671                         || (src_type != info->src_type && g_type_is_a(src_type, info->src_type)))
672                 {
673                     continue;
674                 }
675             }                 // Neither derived or src_map, just do a direct comparison
676             else if(src_type != info->src_type)
677             {
678                 continue;
679             }
680         }
681 
682         if(dest_type)
683         {
684             if((flags | info->flags) & IPATCH_CONVERTER_DEST_DERIVED)         // If derived flag set and destination type is not a descendant of map type, skip
685             {
686                 // Derived will automatically be set for interface types as well
687                 if(!g_type_is_a(info->dest_type, dest_type))
688                 {
689                     continue;
690                 }
691             }
692             else if(info->dest_match)         // If parent destination match set and type is outside of map type range of dest_match <-> dest_type, skip
693             {
694                 if(!g_type_is_a(dest_type, info->dest_match)
695                         || (dest_type != info->dest_type && g_type_is_a(dest_type, info->dest_type)))
696                 {
697                     continue;
698                 }
699             }                 // Neither derived or dest_map, just do a direct comparison
700             else if(dest_type != info->dest_type)
701             {
702                 continue;
703             }
704         }
705 
706         if(!array)          // Not requesting array? Just return highest priority match
707         {
708             return info;
709         }
710 
711         // Create types array if not already created
712         if(!types_array)
713         {
714             types_array = g_array_new(TRUE, FALSE, sizeof(GType));
715         }
716 
717         g_array_append_val(types_array, info->conv_type);           // Append converter type to array
718     }
719 
720     /* free type array but not the array itself */
721     if(types_array)
722     {
723         *array = (GType *)g_array_free(types_array, FALSE);
724     }
725 
726     return NULL;
727 }
728 
729 /**
730  * ipatch_lookup_converter_info:
731  * @conv_type: #IpatchConverter derived GType to lookup info on (or 0 for wildcard, or G_TYPE_NONE - since 1.1.0)
732  * @src_type: Source type of conversion map to lookup (or 0 for default map, or G_TYPE_NONE - since 1.1.0)
733  * @dest_type: Destination type of conversion map (0 if @src_type is 0, or G_TYPE_NONE - since 1.1.0)
734  *
735  * Lookup converter map info.
736  *
737  * Returns: (transfer none) (nullable): Converter info structure or %NULL if no match.
738  * The returned pointer is internal and should not be modified or freed.
739  */
740 const IpatchConverterInfo *
ipatch_lookup_converter_info(GType conv_type,GType src_type,GType dest_type)741 ipatch_lookup_converter_info(GType conv_type, GType src_type, GType dest_type)
742 {
743     const IpatchConverterInfo *info;
744 
745     G_LOCK(conv_maps);
746     info = convert_lookup_map_U(NULL, conv_type, src_type, dest_type, 0);
747     G_UNLOCK(conv_maps);
748 
749     return (info);
750 }
751 
752 /**
753  * ipatch_get_converter_info:
754  * @conv_type: #IpatchConverter derived GType to lookup info on
755  *
756  * Get converter info structure for a converter type.
757  *
758  * Returns: (transfer none) (nullable): Converter info structure or %NULL if no match.
759  * The returned pointer is internal and should not be modified or freed.
760  *
761  * Since: 1.1.0
762  */
763 const IpatchConverterInfo *
ipatch_get_converter_info(GType conv_type)764 ipatch_get_converter_info(GType conv_type)
765 {
766     return ipatch_lookup_converter_info(conv_type, 0, 0);
767 }
768 
769 
770 GType
ipatch_converter_get_type(void)771 ipatch_converter_get_type(void)
772 {
773     static GType obj_type = 0;
774 
775     if(!obj_type)
776     {
777         static const GTypeInfo obj_info =
778         {
779             sizeof(IpatchConverterClass), NULL, NULL,
780             (GClassInitFunc)ipatch_converter_class_init, NULL, NULL,
781             sizeof(IpatchConverter), 0,
782             (GInstanceInitFunc) NULL,
783         };
784 
785         obj_type = g_type_register_static(G_TYPE_OBJECT, "IpatchConverter",
786                                           &obj_info, G_TYPE_FLAG_ABSTRACT);
787     }
788 
789     return (obj_type);
790 }
791 
792 static void
ipatch_converter_class_init(IpatchConverterClass * klass)793 ipatch_converter_class_init(IpatchConverterClass *klass)
794 {
795     GObjectClass *obj_class = G_OBJECT_CLASS(klass);
796 
797     parent_class = g_type_class_peek_parent(klass);
798 
799     obj_class->set_property = ipatch_converter_set_property;
800     obj_class->get_property = ipatch_converter_get_property;
801     obj_class->finalize = ipatch_converter_finalize;
802 
803     g_object_class_install_property(obj_class, PROP_PROGRESS,
804                                     g_param_spec_float("progress", _("Progress"),
805                                             _("Conversion progress"),
806                                             0.0, 1.0, 0.0,
807                                             G_PARAM_READWRITE));
808 }
809 
810 /* function called when a patch is being destroyed */
811 static void
ipatch_converter_finalize(GObject * gobject)812 ipatch_converter_finalize(GObject *gobject)
813 {
814     IpatchConverter *converter = IPATCH_CONVERTER(gobject);
815     GList *p;
816 
817     // Call destroy notify function for link function assignment (if set)
818     if(converter->notify_func)
819     {
820         converter->notify_func(converter->user_data);
821     }
822 
823     p = converter->inputs;
824 
825     while(p)
826     {
827         g_object_unref(p->data);
828         p = g_list_delete_link(p, p);
829     }
830 
831     p = converter->outputs;
832 
833     while(p)
834     {
835         g_object_unref(p->data);
836         p = g_list_delete_link(p, p);
837     }
838 
839     if(G_OBJECT_CLASS(parent_class)->finalize)
840     {
841         G_OBJECT_CLASS(parent_class)->finalize(gobject);
842     }
843 }
844 
845 static void
ipatch_converter_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)846 ipatch_converter_set_property(GObject *object, guint property_id,
847                               const GValue *value, GParamSpec *pspec)
848 {
849     IpatchConverter *converter;
850 
851     g_return_if_fail(IPATCH_IS_CONVERTER(object));
852     converter = IPATCH_CONVERTER(object);
853 
854     switch(property_id)
855     {
856     case PROP_PROGRESS:
857         converter->progress = g_value_get_float(value);
858         break;
859 
860     default:
861         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
862         break;
863     }
864 }
865 
866 static void
ipatch_converter_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)867 ipatch_converter_get_property(GObject *object, guint property_id,
868                               GValue *value, GParamSpec *pspec)
869 {
870     IpatchConverter *converter;
871 
872     g_return_if_fail(IPATCH_IS_CONVERTER(object));
873     converter = IPATCH_CONVERTER(object);
874 
875     switch(property_id)
876     {
877     case PROP_PROGRESS:
878         g_value_set_float(value, converter->progress);
879         break;
880 
881     default:
882         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
883         break;
884     }
885 }
886 
887 /**
888  * ipatch_converter_add_input:
889  * @converter: Converter instance
890  * @object: Object to add
891  *
892  * Add an input object to a converter object.
893  */
894 void
ipatch_converter_add_input(IpatchConverter * converter,GObject * object)895 ipatch_converter_add_input(IpatchConverter *converter, GObject *object)
896 {
897     g_return_if_fail(IPATCH_IS_CONVERTER(converter));
898     g_return_if_fail(G_IS_OBJECT(object));
899 
900     converter->inputs = g_list_append(converter->inputs, g_object_ref(object));
901 }
902 
903 /**
904  * ipatch_converter_add_output:
905  * @converter: Converter instance
906  * @object: Object to add
907  *
908  * Add an output object to a converter object.
909  */
910 void
ipatch_converter_add_output(IpatchConverter * converter,GObject * object)911 ipatch_converter_add_output(IpatchConverter *converter, GObject *object)
912 {
913     g_return_if_fail(IPATCH_IS_CONVERTER(converter));
914     g_return_if_fail(G_IS_OBJECT(object));
915 
916     converter->outputs = g_list_append(converter->outputs,
917                                        g_object_ref(object));
918 }
919 
920 /**
921  * ipatch_converter_add_inputs:
922  * @converter: Converter instance
923  * @objects: (element-type GObject.Object) (transfer none): List of objects to add
924  *
925  * Add a list of input objects to a converter object.
926  */
927 void
ipatch_converter_add_inputs(IpatchConverter * converter,GList * objects)928 ipatch_converter_add_inputs(IpatchConverter *converter, GList *objects)
929 {
930     GList *p;
931 
932     g_return_if_fail(IPATCH_IS_CONVERTER(converter));
933     g_return_if_fail(objects != NULL);
934 
935     p = objects;
936 
937     while(p)
938     {
939         converter->inputs = g_list_append(converter->inputs,
940                                           g_object_ref(p->data));
941         p = g_list_next(p);
942     }
943 }
944 
945 /**
946  * ipatch_converter_add_outputs:
947  * @converter: Converter instance
948  * @objects: (element-type GObject.Object) (transfer none): List of objects to add
949  *
950  * Add a list of output objects to a converter object.
951  */
952 void
ipatch_converter_add_outputs(IpatchConverter * converter,GList * objects)953 ipatch_converter_add_outputs(IpatchConverter *converter, GList *objects)
954 {
955     GList *p;
956 
957     g_return_if_fail(IPATCH_IS_CONVERTER(converter));
958     g_return_if_fail(objects != NULL);
959 
960     p = objects;
961 
962     while(p)
963     {
964         converter->outputs = g_list_append(converter->outputs,
965                                            g_object_ref(p->data));
966         p = g_list_next(p);
967     }
968 }
969 
970 /**
971  * ipatch_converter_get_input:
972  * @converter: Converter instance
973  *
974  * Get a single input object from a converter.
975  *
976  * Returns: (transfer full): The first input object from a converter or %NULL if
977  *   no input objects. The caller owns a reference to the returned
978  *   object.
979  */
980 GObject *
ipatch_converter_get_input(IpatchConverter * converter)981 ipatch_converter_get_input(IpatchConverter *converter)
982 {
983     GObject *obj = NULL;
984 
985     g_return_val_if_fail(IPATCH_IS_CONVERTER(converter), NULL);
986 
987     if(converter->inputs)
988     {
989         obj = (GObject *)(converter->inputs->data);
990     }
991 
992     if(obj)
993     {
994         g_object_ref(obj);
995     }
996 
997     return (obj);
998 }
999 
1000 /**
1001  * ipatch_converter_get_output:
1002  * @converter: Converter instance
1003  *
1004  * Get a single output object from a converter.
1005  *
1006  * Returns: (transfer full): The first output object from a converter or %NULL if
1007  *   no output objects. The caller owns a reference to the returned
1008  *   object.
1009  */
1010 GObject *
ipatch_converter_get_output(IpatchConverter * converter)1011 ipatch_converter_get_output(IpatchConverter *converter)
1012 {
1013     GObject *obj = NULL;
1014 
1015     g_return_val_if_fail(IPATCH_IS_CONVERTER(converter), NULL);
1016 
1017     if(converter->outputs)
1018     {
1019         obj = (GObject *)(converter->outputs->data);
1020     }
1021 
1022     if(obj)
1023     {
1024         g_object_ref(obj);
1025     }
1026 
1027     return (obj);
1028 }
1029 
1030 /**
1031  * ipatch_converter_get_inputs: (skip)
1032  * @converter: Converter instance
1033  *
1034  * Get a list of input objects from a converter.
1035  *
1036  * Returns: (transfer full): A newly created input object list from a converter or
1037  *   %NULL if no input objects. The caller owns a reference to the
1038  *   returned list.
1039  */
1040 IpatchList *
ipatch_converter_get_inputs(IpatchConverter * converter)1041 ipatch_converter_get_inputs(IpatchConverter *converter)
1042 {
1043     IpatchList *list;
1044     GList *items;
1045 
1046     items = ipatch_converter_get_inputs_list(converter);
1047 
1048     if(!items)
1049     {
1050         return NULL;
1051     }
1052 
1053     list = ipatch_list_new();	/* ++ ref new */
1054     list->items = items;          // !! list takes over items
1055     return (list);		/* !! caller takes over list reference */
1056 }
1057 
1058 /**
1059  * ipatch_converter_get_inputs_list: (rename-to ipatch_converter_get_inputs)
1060  * @converter: Converter instance
1061  *
1062  * Get a list of input objects from a converter.
1063  *
1064  * Returns: (element-type GObject.Object) (transfer full): A newly created input
1065  *   object list from a converter or %NULL if no input objects.
1066  *   Free the list with ipatch_glist_unref_free() when finished using it.
1067  *
1068  * Since: 1.1.0
1069  */
1070 GList *
ipatch_converter_get_inputs_list(IpatchConverter * converter)1071 ipatch_converter_get_inputs_list(IpatchConverter *converter)
1072 {
1073     GList *items = NULL, *p;
1074 
1075     g_return_val_if_fail(IPATCH_IS_CONVERTER(converter), NULL);
1076 
1077     if(!converter->inputs)
1078     {
1079         return (NULL);
1080     }
1081 
1082     for(p = converter->inputs; p; p = g_list_next(p))
1083     {
1084         items = g_list_prepend(items, g_object_ref(p->data));
1085     }
1086 
1087     return g_list_reverse(items);         // !! caller takes over list
1088 }
1089 
1090 /**
1091  * ipatch_converter_get_outputs: (skip)
1092  * @converter: Converter instance
1093  *
1094  * Get a list of output objects from a converter.
1095  *
1096  * Returns: (transfer full): A newly created output object list from a converter or
1097  *   %NULL if no output objects. The caller owns a reference to the
1098  *   returned list.
1099  */
1100 IpatchList *
ipatch_converter_get_outputs(IpatchConverter * converter)1101 ipatch_converter_get_outputs(IpatchConverter *converter)
1102 {
1103     IpatchList *list;
1104     GList *items;
1105 
1106     items = ipatch_converter_get_outputs_list(converter);
1107 
1108     if(!items)
1109     {
1110         return NULL;
1111     }
1112 
1113     list = ipatch_list_new();	/* ++ ref new */
1114     list->items = items;          // !! list takes over items
1115     return (list);		/* !! caller takes over list reference */
1116 }
1117 
1118 /**
1119  * ipatch_converter_get_outputs_list: (rename-to ipatch_converter_get_outputs)
1120  * @converter: Converter instance
1121  *
1122  * Get a list of output objects from a converter.
1123  *
1124  * Returns: (element-type GObject.Object) (transfer full): A newly created output
1125  *   object list from a converter or %NULL if no output objects.
1126  *   Free the list with ipatch_glist_unref_free() when finished using it.
1127  *
1128  * Since: 1.1.0
1129  */
1130 GList *
ipatch_converter_get_outputs_list(IpatchConverter * converter)1131 ipatch_converter_get_outputs_list(IpatchConverter *converter)
1132 {
1133     GList *items = NULL, *p;
1134 
1135     g_return_val_if_fail(IPATCH_IS_CONVERTER(converter), NULL);
1136 
1137     if(!converter->outputs)
1138     {
1139         return (NULL);
1140     }
1141 
1142     for(p = converter->outputs; p; p = g_list_next(p))
1143     {
1144         items = g_list_prepend(items, g_object_ref(p->data));
1145     }
1146 
1147     return g_list_reverse(items);         // !! caller takes over list
1148 }
1149 
1150 /**
1151  * ipatch_converter_verify:
1152  * @converter: Converter object
1153  * @failmsg: (out) (transfer full): Location to store a failure message if @converter fails
1154  *   verification. The stored message should be freed when no longer needed.
1155  *
1156  * Verifies the settings of a converter object. This is automatically called
1157  * before a conversion is done, so it doesn't usually need to be explicitly
1158  * called.
1159  *
1160  * Returns: %TRUE if @converter passed verification, %FALSE otherwise in which
1161  *   case an error message may be stored in @failmsg. Remember to free the
1162  *   message when finished with it.
1163  */
1164 gboolean
ipatch_converter_verify(IpatchConverter * converter,char ** failmsg)1165 ipatch_converter_verify(IpatchConverter *converter, char **failmsg)
1166 {
1167     IpatchConverterClass *klass;
1168     const IpatchConverterInfo *info;
1169     char *msg = NULL;
1170     gboolean retval;
1171     GType type;
1172     int count;
1173     GList *p;
1174 
1175     g_return_val_if_fail(IPATCH_IS_CONVERTER(converter), FALSE);
1176 
1177     klass = IPATCH_CONVERTER_GET_CLASS(converter);
1178 
1179     // Verify method set? - use it
1180     if(klass->verify)
1181     {
1182         retval = (klass->verify)(converter, &msg);
1183 
1184         if(failmsg)
1185         {
1186             *failmsg = msg;
1187         }
1188         else
1189         {
1190             g_free(msg);
1191         }
1192 
1193         return (retval);
1194     }
1195 
1196     // No verify method, check input/output types and counts
1197     info = ipatch_lookup_converter_info(G_OBJECT_TYPE(converter), 0, 0);
1198 
1199     if(info->src_count == 0 && converter->inputs)
1200     {
1201         goto input_failed;
1202     }
1203 
1204     for(p = converter->inputs, count = 0; p; p = p->next, count++)
1205     {
1206         type = G_OBJECT_TYPE(p->data);
1207 
1208         if(info->flags & IPATCH_CONVERTER_SRC_DERIVED)      // If derived flag set and source type is not a descendant of map type, fail
1209         {
1210             if(!g_type_is_a(info->src_type, type))
1211             {
1212                 goto input_failed;
1213             }
1214         }
1215         else if(info->src_match)    // If parent source match set and type is outside of map type range of src_match <-> src_type, fail
1216         {
1217             if(!g_type_is_a(type, info->src_match)
1218                     || (type != info->src_type && g_type_is_a(type, info->src_type)))
1219             {
1220                 goto input_failed;
1221             }
1222         }                           // Neither derived or src_map, just do a direct type comparison, if not equal, fail
1223         else if(type != info->src_type)
1224         {
1225             goto input_failed;
1226         }
1227     }
1228 
1229     if(info->src_count == IPATCH_CONVERTER_COUNT_ONE_OR_MORE)
1230     {
1231         if(count < 1)
1232         {
1233             goto input_failed;
1234         }
1235     }
1236     else if(info->src_count != IPATCH_CONVERTER_COUNT_ZERO_OR_MORE && count != info->src_count)
1237     {
1238         goto input_failed;
1239     }
1240 
1241 
1242     for(p = converter->outputs, count = 0; p; p = p->next, count++)
1243     {
1244         type = G_OBJECT_TYPE(p->data);
1245 
1246         if(info->flags & IPATCH_CONVERTER_DEST_DERIVED)     // If derived flag set and dest type is not a descendant of map type, fail
1247         {
1248             if(!g_type_is_a(info->dest_type, type))
1249             {
1250                 goto input_failed;
1251             }
1252         }
1253         else if(info->dest_match)           // If parent dest match set and type is outside of map type range of dest_match <-> dest_type, fail
1254         {
1255             if(!g_type_is_a(type, info->dest_match)
1256                     || (type != info->dest_type && g_type_is_a(type, info->dest_type)))
1257             {
1258                 goto input_failed;
1259             }
1260         }                                   // Neither derived or dest_map, just do a direct type comparison, if not equal, fail
1261         else if(type != info->dest_type)
1262         {
1263             goto input_failed;
1264         }
1265     }
1266 
1267     if(info->dest_count == IPATCH_CONVERTER_COUNT_ONE_OR_MORE)
1268     {
1269         if(count < 1)
1270         {
1271             goto output_failed;
1272         }
1273     }
1274     else if(info->dest_count != IPATCH_CONVERTER_COUNT_ZERO_OR_MORE && count != info->dest_count)
1275     {
1276         goto output_failed;
1277     }
1278 
1279     return TRUE;
1280 
1281 input_failed:
1282 
1283     if(failmsg)
1284     {
1285         *failmsg = g_strdup("Converter inputs failed to verify");
1286     }
1287 
1288     return (FALSE);
1289 
1290 output_failed:
1291 
1292     if(failmsg)
1293     {
1294         *failmsg = g_strdup("Converter outputs failed to verify");
1295     }
1296 
1297     return (FALSE);
1298 }
1299 
1300 /**
1301  * ipatch_converter_init:
1302  * @converter: Converter object
1303  *
1304  * Allows a converter type to initialize its parameters based on its input
1305  * and/or output objects. This function should be called after setting the
1306  * input and output objects, but before setting object parameters or
1307  * converting. Calling this function is not required, but certain converters
1308  * will work more intuitively if it is (an example is an audio sample saver
1309  * converter which could initialize the output sample file object format
1310  * based on the input sample object format).
1311  *
1312  * NOTE: Verification of converter parameters is not done by this routine
1313  * so converter types implementing an init method are responsible for their
1314  * own verification.
1315  */
1316 void
ipatch_converter_init(IpatchConverter * converter)1317 ipatch_converter_init(IpatchConverter *converter)
1318 {
1319     IpatchConverterClass *klass;
1320 
1321     g_return_if_fail(IPATCH_IS_CONVERTER(converter));
1322 
1323     klass = IPATCH_CONVERTER_GET_CLASS(converter);
1324 
1325     if(!klass->init)
1326     {
1327         return;
1328     }
1329 
1330     (klass->init)(converter);
1331 }
1332 
1333 /**
1334  * ipatch_converter_convert:
1335  * @converter: Converter object
1336  * @err: Location to store error info or %NULL
1337  *
1338  * Runs the conversion method of a converter object. The @converter object's
1339  * conversion paramters are first verified before the conversion is run.
1340  *
1341  * Returns: %TRUE on success, %FALSE otherwise
1342  */
1343 gboolean
ipatch_converter_convert(IpatchConverter * converter,GError ** err)1344 ipatch_converter_convert(IpatchConverter *converter, GError **err)
1345 {
1346     IpatchConverterClass *klass;
1347     char *failmsg = NULL;
1348 
1349     g_return_val_if_fail(IPATCH_IS_CONVERTER(converter), FALSE);
1350     g_return_val_if_fail(!err || !*err, FALSE);
1351 
1352     klass = IPATCH_CONVERTER_GET_CLASS(converter);
1353     g_return_val_if_fail(klass->convert != NULL, FALSE);
1354 
1355     if(!ipatch_converter_verify(converter, &failmsg))
1356     {
1357         g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_INVALID,
1358                     _("Verification of conversion parameters failed: %s"),
1359                     failmsg ? failmsg : _("<No detailed error message>"));
1360         return (FALSE);
1361     }
1362 
1363     return ((klass->convert)(converter, err));
1364 }
1365 
1366 /**
1367  * ipatch_converter_reset:
1368  * @converter: Converter object
1369  *
1370  * Reset a converter object so it can be re-used.
1371  */
1372 void
ipatch_converter_reset(IpatchConverter * converter)1373 ipatch_converter_reset(IpatchConverter *converter)
1374 {
1375     GList *p;
1376 
1377     g_return_if_fail(IPATCH_IS_CONVERTER(converter));
1378 
1379     p = converter->inputs;
1380 
1381     while(p)
1382     {
1383         g_object_unref(p->data);
1384         p = g_list_delete_link(p, p);
1385     }
1386 
1387     converter->inputs = NULL;
1388 
1389     p = converter->outputs;
1390 
1391     while(p)
1392     {
1393         g_object_unref(p->data);
1394         p = g_list_delete_link(p, p);
1395     }
1396 
1397     converter->outputs = NULL;
1398 
1399     p = converter->log;
1400 
1401     while(p)
1402     {
1403         g_free(p->data);
1404         p = g_list_delete_link(p, p);
1405     }
1406 
1407     converter->log = NULL;
1408 
1409     converter->min_rate = 0.0;
1410     converter->max_rate = 0.0;
1411     converter->avg_rate = 0.0;
1412     converter->sum_rate = 0.0;
1413     converter->item_count = 0;
1414 }
1415 
1416 /**
1417  * ipatch_converter_get_notes:
1418  * @converter: Converter object
1419  *
1420  * Get notes about a conversion implementation. These notes could include
1421  * things such as information about any loss of information in the conversion
1422  * that may occur, etc.
1423  *
1424  * Returns: Newly allocated and possibly multi-line notes and comments
1425  *   about a given conversion or %NULL if no notes. Meant for display to
1426  *   the user. This string should be freed when no longer needed.
1427  */
1428 char *
ipatch_converter_get_notes(IpatchConverter * converter)1429 ipatch_converter_get_notes(IpatchConverter *converter)
1430 {
1431     IpatchConverterClass *klass;
1432 
1433     g_return_val_if_fail(IPATCH_IS_CONVERTER(converter), NULL);
1434 
1435     klass = IPATCH_CONVERTER_GET_CLASS(converter);
1436 
1437     if(!klass->notes)
1438     {
1439         return (NULL);
1440     }
1441 
1442     return ((klass->notes)(converter));
1443 }
1444 
1445 /* FIXME-GIR: @type consists of multiple flags types?  @msg can be static or dynamic. */
1446 
1447 /**
1448  * ipatch_converter_log: (skip)
1449  * @converter: Converter object
1450  * @item: (nullable): Item the log entry pertains to or %NULL if not item specific
1451  * @type: #IpatchConverterLogType and other flags
1452  * @msg: Message of the log. If message is dynamically allocated then
1453  *   the #IPATCH_CONVERTER_LOG_MSG_ALLOC flag should be set in @type
1454  *
1455  * Logs an entry to a converter log. Usually only used by converter
1456  * object handlers.
1457  */
1458 void
ipatch_converter_log(IpatchConverter * converter,GObject * item,int type,char * msg)1459 ipatch_converter_log(IpatchConverter *converter, GObject *item,
1460                      int type, char *msg)
1461 {
1462     LogEntry *entry;
1463 
1464     g_return_if_fail(IPATCH_IS_CONVERTER(converter));
1465     g_return_if_fail(!item || G_IS_OBJECT(item));
1466     g_return_if_fail(msg != NULL);
1467 
1468     entry = g_new0(LogEntry, 1);
1469 
1470     if(item)
1471     {
1472         entry->item = g_object_ref(item);
1473     }
1474 
1475     entry->type = type;
1476     entry->data.msg = msg;
1477 
1478     converter->log = g_list_prepend(converter->log, entry);
1479 }
1480 
1481 /* FIXME-GIR: @type consists of multiple flags types? */
1482 
1483 /**
1484  * ipatch_converter_log_printf:
1485  * @converter: Converter object
1486  * @item: (nullable): Item the log entry pertains to or %NULL if not item specific
1487  * @type: #IpatchConverterLogType and other flags
1488  * @fmt: Printf format style string
1489  * @...: Arguments to @fmt message string
1490  *
1491  * Logs a printf style message to a converter log. Usually only used by converter
1492  * object handlers.  The #IPATCH_CONVERTER_LOG_MSG_ALLOC flag is automatically
1493  * set on the log entry, since it is dynamically allocated.
1494  */
1495 void
ipatch_converter_log_printf(IpatchConverter * converter,GObject * item,int type,const char * fmt,...)1496 ipatch_converter_log_printf(IpatchConverter *converter, GObject *item,
1497                             int type, const char *fmt, ...)
1498 {
1499     LogEntry *entry;
1500     va_list args;
1501 
1502     g_return_if_fail(IPATCH_IS_CONVERTER(converter));
1503     g_return_if_fail(!item || G_IS_OBJECT(item));
1504     g_return_if_fail(fmt != NULL);
1505 
1506     entry = g_new0(LogEntry, 1);
1507 
1508     if(item)
1509     {
1510         entry->item = g_object_ref(item);
1511     }
1512 
1513     entry->type = type | IPATCH_CONVERTER_LOG_MSG_ALLOC;
1514 
1515     va_start(args, fmt);
1516     entry->data.msg = g_strdup_vprintf(fmt, args);
1517     va_end(args);
1518 
1519     converter->log = g_list_prepend(converter->log, entry);
1520 }
1521 
1522 /**
1523  * ipatch_converter_log_next:
1524  * @converter: Converter object
1525  * @pos: (out): Opaque current position in log, should be %NULL on first call to
1526  *   this function to return first log item (oldest item)
1527  * @item: (out) (transfer none) (optional): Location to store item of the log entry or %NULL,
1528  *   no reference is added so the item is only guarenteed to exist for as long as the
1529  *   @converter does
1530  * @type: (out) (optional): Location to store the type parameter of the log entry or %NULL
1531  * @msg: (out) (transfer none): Location to store the message of the log entry or %NULL, message
1532  *   is internal and should not be messed with and is only guarenteed for
1533  *   the lifetime of the @converter
1534  *
1535  * Get the first or next log entry from a converter object.
1536  *
1537  * Returns: %TRUE if an entry was returned, %FALSE if no more entries in which
1538  *   case item, type and msg are all undefined.
1539  */
1540 gboolean
ipatch_converter_log_next(IpatchConverter * converter,gpointer * pos,GObject ** item,int * type,char ** msg)1541 ipatch_converter_log_next(IpatchConverter *converter, gpointer *pos,
1542                           GObject **item, int *type, char **msg)
1543 {
1544     LogEntry *entry;
1545     GList *p;
1546 
1547     g_return_val_if_fail(IPATCH_IS_CONVERTER(converter), FALSE);
1548     g_return_val_if_fail(pos != NULL, FALSE);
1549 
1550     if(!*pos)
1551     {
1552         p = g_list_last(converter->log);
1553     }
1554     else
1555     {
1556         p = g_list_previous((GList *)(*pos));
1557     }
1558 
1559     if(!p)
1560     {
1561         return (FALSE);
1562     }
1563 
1564     entry = (LogEntry *)(p->data);
1565 
1566     if(item)
1567     {
1568         *item = entry->item;
1569     }
1570 
1571     if(type)
1572     {
1573         *type = entry->type;
1574     }
1575 
1576     if(msg)
1577     {
1578         *msg = entry->data.msg;
1579     }
1580 
1581     return (TRUE);
1582 }
1583 
1584 /**
1585  * ipatch_converter_set_link_funcs: (skip)
1586  * @converter: Converter object
1587  * @link_lookup: Set the link lookup callback function
1588  * @link_notify: Set the link notify callback function
1589  *
1590  * This function allows for object link interception by the user of
1591  * an #IpatchConverter instance.  The callback functions are used by
1592  * conversion processes which create objects linking other external
1593  * objects which need to be converted.  For each link object needing
1594  * conversion @link_lookup will be called.  If @link_lookup returns a valid
1595  * pointer it is used as the converted link object, if %NULL is returned then
1596  * the link will be converted and @link_notify will be called with the new
1597  * converted item.  An example usage of this feature is
1598  * the #IpatchPaste system, which does object conversions and substitutes
1599  * already converted objects (a conversion pool).
1600  */
1601 void
ipatch_converter_set_link_funcs(IpatchConverter * converter,IpatchConverterLinkLookupFunc * link_lookup,IpatchConverterLinkNotifyFunc * link_notify)1602 ipatch_converter_set_link_funcs(IpatchConverter *converter,
1603                                 IpatchConverterLinkLookupFunc *link_lookup,
1604                                 IpatchConverterLinkNotifyFunc *link_notify)
1605 {
1606     g_return_if_fail(IPATCH_IS_CONVERTER(converter));
1607     ipatch_converter_set_link_funcs_full(converter, link_lookup, link_notify, NULL, NULL);
1608 }
1609 
1610 /**
1611  * ipatch_converter_set_link_funcs_full: (rename-to ipatch_converter_set_link_funcs)
1612  * @converter: Converter object
1613  * @link_lookup: (scope notified) (nullable): Set the link lookup callback function
1614  * @link_notify: (scope notified) (nullable): Set the link notify callback function
1615  * @notify_func: (scope async) (closure user_data) (nullable): Callback which gets
1616  *   called when link functions are removed.
1617  * @user_data: (nullable): User data passed to @notify_func (not @link_lookup or @link_notify)
1618  *
1619  * This function allows for object link interception by the user of
1620  * an #IpatchConverter instance.  The callback functions are used by
1621  * conversion processes which create objects linking other external
1622  * objects which need to be converted.  For each link object needing
1623  * conversion @link_lookup will be called.  If @link_lookup returns a valid
1624  * pointer it is used as the converted link object, if %NULL is returned then
1625  * the link will be converted and @link_notify will be called with the new
1626  * converted item.  An example usage of this feature is
1627  * the #IpatchPaste system, which does object conversions and substitutes
1628  * already converted objects (a conversion pool).
1629  *
1630  * Since: 1.1.0
1631  */
1632 void
ipatch_converter_set_link_funcs_full(IpatchConverter * converter,IpatchConverterLinkLookupFunc * link_lookup,IpatchConverterLinkNotifyFunc * link_notify,GDestroyNotify notify_func,gpointer user_data)1633 ipatch_converter_set_link_funcs_full(IpatchConverter *converter,
1634                                      IpatchConverterLinkLookupFunc *link_lookup,
1635                                      IpatchConverterLinkNotifyFunc *link_notify,
1636                                      GDestroyNotify notify_func, gpointer user_data)
1637 {
1638     g_return_if_fail(IPATCH_IS_CONVERTER(converter));
1639 
1640     if(converter->notify_func)
1641     {
1642         converter->notify_func(converter->user_data);
1643     }
1644 
1645     converter->link_lookup = link_lookup;
1646     converter->link_notify = link_notify;
1647     converter->notify_func = notify_func;
1648     converter->user_data = user_data;
1649 }
1650