1 /*
2  * Swami
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: IpatchRange
22  * @short_description: A boxed type which defines a number range
23  * @see_also:
24  * @stability: Stable
25  *
26  * Boxed type used for #GValue and #GParamSpec properties.  Consists of a low
27  * and a high integer value defining a range.
28  */
29 #include "config.h"
30 
31 #include <stdio.h>
32 #include <glib.h>
33 #include <glib-object.h>
34 
35 #include "IpatchRange.h"
36 #include "IpatchXml.h"
37 #include "IpatchXmlObject.h"
38 #include "misc.h"
39 #include "ipatch_priv.h"
40 
41 static gboolean
42 ipatch_range_xml_encode_func(GNode *node, GObject *object, GParamSpec *pspec,
43                              GValue *value, GError **err);
44 static gboolean
45 ipatch_range_xml_decode_func(GNode *node, GObject *object, GParamSpec *pspec,
46                              GValue *value, GError **err);
47 static void ipatch_param_spec_range_set_default(GParamSpec *pspec,
48         GValue *value);
49 static gboolean ipatch_param_spec_range_validate(GParamSpec *pspec,
50         GValue *value);
51 static gint ipatch_param_spec_range_values_cmp(GParamSpec *pspec,
52         const GValue *value1,
53         const GValue *value2);
54 
55 /**
56  * _ipatch_range_init: (skip)
57  *
58  * Init function to register pickle XML encode/decode functions for ranges
59  */
60 void
_ipatch_range_init(void)61 _ipatch_range_init(void)
62 {
63     ipatch_xml_register_handler(IPATCH_TYPE_RANGE, NULL,
64                                 ipatch_range_xml_encode_func,
65                                 ipatch_range_xml_decode_func);
66 }
67 
68 /* XML range value encoding function */
69 static gboolean
ipatch_range_xml_encode_func(GNode * node,GObject * object,GParamSpec * pspec,GValue * value,GError ** err)70 ipatch_range_xml_encode_func(GNode *node, GObject *object, GParamSpec *pspec,
71                              GValue *value, GError **err)
72 {
73     IpatchRange *range;
74 
75     g_return_val_if_fail(IPATCH_VALUE_HOLDS_RANGE(value), FALSE);
76 
77     range = g_value_get_boxed(value);
78 
79     if(range)
80     {
81         ipatch_xml_set_value_printf(node, "%d-%d", range->low, range->high);
82     }
83 
84     return (TRUE);
85 }
86 
87 static gboolean
ipatch_range_xml_decode_func(GNode * node,GObject * object,GParamSpec * pspec,GValue * value,GError ** err)88 ipatch_range_xml_decode_func(GNode *node, GObject *object, GParamSpec *pspec,
89                              GValue *value, GError **err)
90 {
91     IpatchRange *range;
92     int low, high;
93     const char *strval;
94 
95     strval = ipatch_xml_get_value(node);
96 
97     if(!strval)
98     {
99         g_value_set_boxed(value, NULL);
100     }
101 
102     if(sscanf(strval, "%d-%d", &low, &high) != 2)
103     {
104         g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_INVALID,
105                     _("Invalid XML '%s' for range value"), strval);
106         return (FALSE);
107     }
108 
109     range = ipatch_range_new(low, high);
110     g_value_take_boxed(value, range);
111 
112     return (TRUE);
113 }
114 
115 GType
ipatch_range_get_type(void)116 ipatch_range_get_type(void)
117 {
118     static GType item_type = 0;
119 
120     if(!item_type)
121     {
122         item_type = g_boxed_type_register_static
123                     ("IpatchRange", (GBoxedCopyFunc) ipatch_range_copy,
124                      (GBoxedFreeFunc) ipatch_range_free);
125     }
126 
127     return (item_type);
128 }
129 
130 /**
131  * ipatch_range_new:
132  * @low: Low value to initialize range to
133  * @high: High value to initialize range to
134  *
135  * Create a new value range structure (to store an integer range).
136  *
137  * Returns: Newly allocated integer range structure.
138  */
139 IpatchRange *
ipatch_range_new(int low,int high)140 ipatch_range_new(int low, int high)
141 {
142     IpatchRange *range;
143 
144     range = g_slice_new(IpatchRange);
145     range->low = low;
146     range->high = high;
147 
148     return (range);
149 }
150 
151 /**
152  * ipatch_range_copy:
153  * @range: Range structure to duplicate
154  *
155  * Duplicates an integer range structure.
156  *
157  * Returns: New duplicate range structure.
158  */
159 IpatchRange *
ipatch_range_copy(IpatchRange * range)160 ipatch_range_copy(IpatchRange *range)
161 {
162     g_return_val_if_fail(range != NULL, NULL);
163     return (ipatch_range_new(range->low, range->high));
164 }
165 
166 /**
167  * ipatch_range_free:
168  * @range: Integer range structure to free
169  *
170  * Free a range structure previously allocated with ipatch_range_new().
171  */
172 void
ipatch_range_free(IpatchRange * range)173 ipatch_range_free(IpatchRange *range)
174 {
175     g_slice_free(IpatchRange, range);
176 }
177 
178 /**
179  * ipatch_value_set_range:
180  * @value: a valid GValue of IPATCH_TYPE_RANGE boxed type
181  * @range: Range structure to assign to @value
182  *
183  * Set the range values of a IPATCH_TYPE_RANGE GValue. The @range
184  * structure is copied.
185  */
186 void
ipatch_value_set_range(GValue * value,const IpatchRange * range)187 ipatch_value_set_range(GValue *value, const IpatchRange *range)
188 {
189     g_return_if_fail(IPATCH_VALUE_HOLDS_RANGE(value));
190     g_value_set_boxed(value, range);
191 }
192 
193 /**
194  * ipatch_value_set_static_range: (skip)
195  * @value: A valid GValue of IPATCH_TYPE_RANGE boxed type
196  * @range: (transfer full): Range structure to assign to @value
197  *
198  * Set the range values of a IPATCH_TYPE_RANGE GValue. This function uses
199  * @range directly and so it should be static, use ipatch_value_set_range()
200  * if the @range value should be duplicated.
201  */
202 void
ipatch_value_set_static_range(GValue * value,IpatchRange * range)203 ipatch_value_set_static_range(GValue *value, IpatchRange *range)
204 {
205     g_return_if_fail(IPATCH_VALUE_HOLDS_RANGE(value));
206     g_value_set_static_boxed(value, range);
207 }
208 
209 /**
210  * ipatch_value_get_range:
211  * @value: A valid GValue of IPATCH_TYPE_RANGE boxed type
212  *
213  * Get the range structure from a IPATCH_TYPE_RANGE GValue.
214  *
215  * Returns: (transfer none): #IpatchRange structure containing the range values of @value or
216  * %NULL if not set. The returned structure is NOT duplicated and is the
217  * same pointer used in @value.
218  */
219 IpatchRange *
ipatch_value_get_range(const GValue * value)220 ipatch_value_get_range(const GValue *value)
221 {
222     g_return_val_if_fail(IPATCH_VALUE_HOLDS_RANGE(value), NULL);
223     return ((IpatchRange *)g_value_get_boxed(value));
224 }
225 
226 
227 GType
ipatch_param_spec_range_get_type(void)228 ipatch_param_spec_range_get_type(void)
229 {
230     static GType spec_type = 0;
231 
232     if(!spec_type)
233     {
234         static const GParamSpecTypeInfo spec_info =
235         {
236             sizeof(IpatchParamSpecRange),
237             0,
238             NULL,			/* instance_init */
239             G_TYPE_BOXED,		/* value type */
240             NULL,			/* finalize */
241             ipatch_param_spec_range_set_default,
242             ipatch_param_spec_range_validate,
243             ipatch_param_spec_range_values_cmp,
244         };
245 
246         spec_type = g_param_type_register_static("IpatchParamSpecRange",
247                     &spec_info);
248     }
249 
250     return (spec_type);
251 }
252 
253 static void
ipatch_param_spec_range_set_default(GParamSpec * pspec,GValue * value)254 ipatch_param_spec_range_set_default(GParamSpec *pspec, GValue *value)
255 {
256     IpatchParamSpecRange *range_pspec = IPATCH_PARAM_SPEC_RANGE(pspec);
257     IpatchRange *range;
258 
259     range = ipatch_value_get_range(value);
260 
261     if(!range)
262     {
263         range = ipatch_range_new(0, 0);
264         ipatch_value_set_range(value, range);
265     }
266 
267     range->low = range_pspec->default_low;
268     range->high = range_pspec->default_high;
269 }
270 
271 static gboolean
ipatch_param_spec_range_validate(GParamSpec * pspec,GValue * value)272 ipatch_param_spec_range_validate(GParamSpec *pspec, GValue *value)
273 {
274     IpatchParamSpecRange *range_pspec = IPATCH_PARAM_SPEC_RANGE(pspec);
275     IpatchRange *range, old_range;
276 
277     range = ipatch_value_get_range(value);
278 
279     if(!range)
280     {
281         range = ipatch_range_new(0, 0);
282         range->low = range_pspec->default_low;
283         range->high = range_pspec->default_high;
284         return (TRUE);
285     }
286 
287     old_range = *range;
288     range->low = CLAMP(range->low, range_pspec->min, range_pspec->max);
289     range->high = CLAMP(range->high, range_pspec->min, range_pspec->max);
290 
291     return (old_range.low != range->low || old_range.high != range->high);
292 }
293 
294 static gint
ipatch_param_spec_range_values_cmp(GParamSpec * pspec,const GValue * value1,const GValue * value2)295 ipatch_param_spec_range_values_cmp(GParamSpec *pspec, const GValue *value1,
296                                    const GValue *value2)
297 {
298     IpatchRange *range1, *range2;
299 
300     range1 = ipatch_value_get_range(value1);
301     range2 = ipatch_value_get_range(value2);
302 
303     /* either one NULL? */
304     if(!range1 || !range2)
305     {
306         if(!range1 && !range2)
307         {
308             return 0;
309         }
310 
311         if(!range1)
312         {
313             return -1;
314         }
315 
316         return 1;
317     }
318 
319     /* check if low value is not equal */
320     if(range1->low < range2->low)
321     {
322         return -1;
323     }
324 
325     if(range1->low > range2->low)
326     {
327         return 1;
328     }
329 
330     /* low values are equal */
331 
332     /* check if high values are not equal */
333     if(range1->high < range2->high)
334     {
335         return -1;
336     }
337 
338     if(range1->high > range2->high)
339     {
340         return 1;
341     }
342 
343     /* range is equal */
344     return 0;
345 }
346 
347 /**
348  * ipatch_param_spec_range:
349  * @name: Property name
350  * @nick: Property nick name
351  * @blurb: Property description blurb
352  * @min: Minimum value for range end points (can be -1 to allow undefined
353  *   ranges)
354  * @max: Maximum value for range end points
355  * @default_low: Default value for low endpoint of range
356  * @default_high: Default value for high endpoint of range
357  * @flags: Property flags
358  *
359  * Create a parameter specification for IPATCH_TYPE_RANGE GValues.
360  *
361  * Returns: (transfer full): New range parameter specification.
362  */
363 GParamSpec *
ipatch_param_spec_range(const char * name,const char * nick,const char * blurb,int min,int max,int default_low,int default_high,GParamFlags flags)364 ipatch_param_spec_range(const char *name, const char *nick, const char *blurb,
365                         int min, int max, int default_low, int default_high,
366                         GParamFlags flags)
367 {
368     IpatchParamSpecRange *range_spec;
369 
370     g_return_val_if_fail(min >= -1 && min <= max, NULL);
371     g_return_val_if_fail(default_low >= min && default_low <= max, NULL);
372     g_return_val_if_fail(default_high >= min && default_high <= max, NULL);
373 
374     /* FIXME - Whats the proper way to create a boxed GParamSpec? */
375     range_spec =  g_param_spec_internal(IPATCH_TYPE_PARAM_RANGE,
376                                         name, nick, blurb, flags);
377     G_PARAM_SPEC(range_spec)->value_type = IPATCH_TYPE_RANGE;
378 
379     range_spec->min = min;
380     range_spec->max = max;
381     range_spec->default_low = default_low;
382     range_spec->default_high = default_high;
383 
384     return ((GParamSpec *)range_spec);
385 }
386