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