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