1 /*
2 * * Copyright (C) 2006-2011 Anders Brander <anders@brander.dk>,
3 * * Anders Kvist <akv@lnxbx.dk> and Klaus Post <klauspost@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program 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 General Public License for more details.
14 *
15 * You should have received a copy of the GNU 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 02110-1301, USA.
18 */
19
20 #include "rs-settings.h"
21 #include "rs-utils.h"
22 #include <config.h>
23 #include "gettext.h"
24 #include <string.h> /* memcmp() */
25
26 G_DEFINE_TYPE (RSSettings, rs_settings, G_TYPE_OBJECT)
27
28 enum {
29 SETTINGS_CHANGED,
30 WB_CHANGED,
31 LAST_SIGNAL
32 };
33
34 static guint signals[LAST_SIGNAL] = { 0 };
35
36 static void get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
37 static void set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
38
39 static void
rs_settings_finalize(GObject * object)40 rs_settings_finalize (GObject *object)
41 {
42 if (G_OBJECT_CLASS (rs_settings_parent_class)->finalize)
43 G_OBJECT_CLASS (rs_settings_parent_class)->finalize (object);
44 }
45
46 enum {
47 PROP_0,
48 PROP_EXPOSURE,
49 PROP_SATURATION,
50 PROP_HUE,
51 PROP_CONTRAST,
52 PROP_WARMTH,
53 PROP_TINT,
54 PROP_DCP_TEMP,
55 PROP_DCP_TINT,
56 PROP_WB_ASCII,
57 PROP_SHARPEN,
58 PROP_DENOISE_LUMA,
59 PROP_DENOISE_CHROMA,
60 PROP_TCA_KR,
61 PROP_TCA_KB,
62 PROP_VIGNETTING,
63 PROP_CHANNELMIXER_RED,
64 PROP_CHANNELMIXER_GREEN,
65 PROP_CHANNELMIXER_BLUE,
66 PROP_RECALC_TEMP
67 };
68
69 static void
rs_settings_class_init(RSSettingsClass * klass)70 rs_settings_class_init (RSSettingsClass *klass)
71 {
72 GObjectClass *object_class = G_OBJECT_CLASS (klass);
73 object_class->finalize = rs_settings_finalize;
74 object_class->get_property = get_property;
75 object_class->set_property = set_property;
76
77 g_object_class_install_property(object_class,
78 PROP_EXPOSURE, g_param_spec_float(
79 /* @TRANSLATORS: "Expos" is short version of "Exposure". You cannot use more than 5 characters for this! */
80 "exposure", _("Expos"), _("Exposure Compensation"),
81 -3.0, 3.0, 0.0, G_PARAM_READWRITE)
82 );
83 g_object_class_install_property(object_class,
84 PROP_SATURATION, g_param_spec_float(
85 /* @TRANSLATORS: "Satur" is short version of "Saturation". You cannot use more than 5 characters for this! */
86 "saturation", _("Satur"), _("Saturation"),
87 0.0, 2.0, 1.0, G_PARAM_READWRITE)
88 );
89 g_object_class_install_property(object_class,
90 PROP_HUE, g_param_spec_float(
91 /* @TRANSLATORS: You cannot use more than 5 characters for "Hue" */
92 "hue", _("Hue"), _("Hue Shift"),
93 -180.0, 180.0, 0.0, G_PARAM_READWRITE)
94 );
95 g_object_class_install_property(object_class,
96 PROP_CONTRAST, g_param_spec_float(
97 /* @TRANSLATORS: "Contr" is short version of "Contrast". You cannot use more than 5 characters for this! */
98 "contrast", _("Contr"), _("Contrast"),
99 0.5, 2.5, 1.0, G_PARAM_READWRITE)
100 );
101 g_object_class_install_property(object_class,
102 PROP_WARMTH, g_param_spec_float(
103 /* @TRANSLATORS: "Temp" is short version of "Temperature". You cannot use more than 5 characters for this! */
104 "warmth", _("Temp"), _("Temperature"),
105 -1.0, 1.0, 0.0, G_PARAM_READWRITE)
106 );
107 g_object_class_install_property(object_class,
108 PROP_TINT, g_param_spec_float(
109 /* @TRANSLATORS: You cannot use more than 5 characters for "Tint" */
110 "tint", _("Tint"), _("Tint Shift"),
111 -2.0, 2.0, 0.0, G_PARAM_READWRITE)
112 );
113 g_object_class_install_property(object_class,
114 PROP_DCP_TEMP, g_param_spec_float(
115 /* @TRANSLATORS: "Temp" is short version of "Temperature". You cannot use more than 5 characters for this! */
116 "dcp-temp", _("Temp"), _("Temperature"),
117 2000.0, 12000.0, 5000.0, G_PARAM_READWRITE)
118 );
119 g_object_class_install_property(object_class,
120 PROP_DCP_TINT, g_param_spec_float(
121 /* @TRANSLATORS: You cannot use more than 5 characters for "Tint" */
122 "dcp-tint", _("Tint"), _("Tint Shift"),
123 -150.0, 150.0, 0.0, G_PARAM_READWRITE)
124 );
125 g_object_class_install_property(object_class,
126 PROP_WB_ASCII, g_param_spec_string(
127 "wb_ascii", _("WBAscii"), _("WBAscii"),
128 NULL, G_PARAM_READWRITE)
129 );
130 g_object_class_install_property(object_class,
131 PROP_SHARPEN, g_param_spec_float(
132 /* @TRANSLATORS: "Sharp" is short version of "Sharpen". You cannot use more than 5 characters for this! */
133 "sharpen", _("Sharp"), _("Sharpen Amount"),
134 0.0, 100.0, 0.0, G_PARAM_READWRITE)
135 );
136 g_object_class_install_property(object_class,
137 PROP_DENOISE_LUMA, g_param_spec_float(
138 /* @TRANSLATORS: "Denoi" is short version of "Denoise". You cannot use more than 5 characters for this! */
139 "denoise_luma", _("Denoi"), _("Light Denoising"),
140 0.0, 100.0, 0.0, G_PARAM_READWRITE)
141 );
142 g_object_class_install_property(object_class,
143 PROP_DENOISE_CHROMA, g_param_spec_float(
144 /* @TRANSLATORS: "ColDn" is short version of "Colour Denoise". You cannot use more than 5 characters for this! */
145 "denoise_chroma", _("ColDn"), _("Colour Denoising"),
146 0.0, 100.0, 0.0, G_PARAM_READWRITE)
147 );
148 g_object_class_install_property(object_class,
149 PROP_TCA_KR, g_param_spec_float(
150 /* @TRANSLATORS: "CA R" is short version of "Chromatic Aberration Red". You cannot use more than 5 characters for this! */
151 "tca_kr", _("CA R"), _("Red Chromatic Aberration Correction"),
152 -0.5, 0.5, 0.0, G_PARAM_READWRITE)
153 );
154 g_object_class_install_property(object_class,
155 PROP_TCA_KB, g_param_spec_float(
156 /* @TRANSLATORS: "CA B" is short version of "Chromatic Aberration Blue". You cannot use more than 5 characters for this! */
157 "tca_kb", _("CA B"), _("Blue Chromatic Aberration Correction"),
158 -0.5, 0.5, 0.0, G_PARAM_READWRITE)
159 );
160 g_object_class_install_property(object_class,
161 PROP_VIGNETTING, g_param_spec_float(
162 /* @TRANSLATORS: "Vign" is short version of "Vignetting". You cannot use more than 5 characters for this! */
163 "vignetting", _("Vign"), _("Vignetting Correction"),
164 -1.0, 1.0, 0.0, G_PARAM_READWRITE)
165 );
166 g_object_class_install_property(object_class,
167 PROP_CHANNELMIXER_RED, g_param_spec_float(
168 /* @TRANSLATORS: You cannot use more than 5 characters for "Red" */
169 "channelmixer_red", _("Red"), _("Red Amount Adjustment"),
170 0.0, 300.0, 100.0, G_PARAM_READWRITE)
171 );
172 g_object_class_install_property(object_class,
173 PROP_CHANNELMIXER_GREEN, g_param_spec_float(
174 /* @TRANSLATORS: You cannot use more than 5 characters for "Green" */
175 "channelmixer_green", _("Green"), _("Green Amount Adjustment"),
176 0.0, 300.0, 100.0, G_PARAM_READWRITE)
177 );
178 g_object_class_install_property(object_class,
179 PROP_CHANNELMIXER_BLUE, g_param_spec_float(
180 /* @TRANSLATORS: You cannot use more than 5 characters for "Blue" */
181 "channelmixer_blue", _("Blue"), _("Blue Amount Adjustment"),
182 0.0, 300.0, 100.0, G_PARAM_READWRITE)
183 );
184 g_object_class_install_property(object_class,
185 PROP_RECALC_TEMP, g_param_spec_boolean(
186 "recalc-temp", "recalc-temp", "Recalculate Temperature",
187 FALSE, G_PARAM_READWRITE)
188 );
189
190 signals[SETTINGS_CHANGED] = g_signal_new ("settings-changed",
191 G_TYPE_FROM_CLASS (klass),
192 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
193 0, /* Is this right? */
194 NULL,
195 NULL,
196 g_cclosure_marshal_VOID__INT,
197 G_TYPE_NONE, 1, G_TYPE_INT);
198 signals[WB_CHANGED] = g_signal_new ("wb-recalculated",
199 G_TYPE_FROM_CLASS (klass),
200 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
201 0, /* Is this right? */
202 NULL,
203 NULL,
204 g_cclosure_marshal_VOID__VOID,
205 G_TYPE_NONE, 0);
206 }
207
208 static void
rs_settings_init(RSSettings * self)209 rs_settings_init (RSSettings *self)
210 {
211 self->commit = 0;
212 self->commit_todo = 0;
213 self->curve_knots = NULL;
214 self->wb_ascii = NULL;
215 rs_settings_reset(self, MASK_ALL);
216 }
217
218 RSSettings *
rs_settings_new(void)219 rs_settings_new (void)
220 {
221 return g_object_new (RS_TYPE_SETTINGS, NULL);
222 }
223
224 static void
get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)225 get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
226 {
227 RSSettings *settings = RS_SETTINGS(object);
228
229 #define CASE(upper, lower) \
230 case PROP_##upper: \
231 g_value_set_float(value, settings->lower); \
232 break
233 switch (property_id)
234 {
235 CASE(EXPOSURE, exposure);
236 CASE(SATURATION, saturation);
237 CASE(HUE, hue);
238 CASE(CONTRAST, contrast);
239 CASE(WARMTH, warmth);
240 CASE(TINT, tint);
241 CASE(DCP_TEMP, dcp_temp);
242 CASE(DCP_TINT, dcp_tint);
243 case PROP_WB_ASCII:
244 g_value_set_string(value, settings->wb_ascii);
245 break;
246 CASE(SHARPEN, sharpen);
247 CASE(DENOISE_LUMA, denoise_luma);
248 CASE(DENOISE_CHROMA, denoise_chroma);
249 CASE(TCA_KR, tca_kr);
250 CASE(TCA_KB, tca_kb);
251 CASE(VIGNETTING, vignetting);
252 CASE(CHANNELMIXER_RED, channelmixer_red);
253 CASE(CHANNELMIXER_GREEN, channelmixer_green);
254 CASE(CHANNELMIXER_BLUE, channelmixer_blue);
255 case PROP_RECALC_TEMP:
256 g_value_set_boolean(value, settings->recalc_temp);
257 break;
258 default:
259 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
260 }
261 #undef CASE
262 }
263
264 static void
set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)265 set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
266 {
267 RSSettings *settings = RS_SETTINGS(object);
268 RSSettingsMask changed_mask = 0;
269
270 #define CASE(upper, lower) \
271 case PROP_##upper: \
272 if (settings->lower != g_value_get_float(value)) \
273 { \
274 settings->lower = g_value_get_float(value); \
275 changed_mask |= MASK_##upper; \
276 } \
277 break
278 switch (property_id)
279 {
280 CASE(EXPOSURE, exposure);
281 CASE(SATURATION, saturation);
282 CASE(HUE, hue);
283 CASE(CONTRAST, contrast);
284 case PROP_WARMTH:
285 if (settings->warmth != g_value_get_float(value))
286 {
287 settings->warmth = g_value_get_float(value);
288 changed_mask |= MASK_WARMTH;
289 g_object_set(settings, "wb_ascii", NULL, NULL);
290 }
291 break;
292 case PROP_TINT:
293 if (settings->tint != g_value_get_float(value))
294 {
295 settings->tint = g_value_get_float(value);
296 changed_mask |= MASK_TINT;
297 g_object_set(settings, "wb_ascii", NULL, NULL);
298 }
299 break;
300 case PROP_DCP_TEMP:
301 if (settings->dcp_temp != g_value_get_float(value))
302 {
303 settings->dcp_temp = g_value_get_float(value);
304 changed_mask |= MASK_WARMTH;
305 g_object_set(settings, "wb_ascii", NULL, NULL);
306 }
307 break;
308 case PROP_DCP_TINT:
309 if (settings->dcp_tint != g_value_get_float(value))
310 {
311 settings->dcp_tint = g_value_get_float(value);
312 changed_mask |= MASK_TINT;
313 g_object_set(settings, "wb_ascii", NULL, NULL);
314 }
315 break;
316 case PROP_WB_ASCII:
317 if (settings->wb_ascii)
318 g_free(settings->wb_ascii);
319 settings->wb_ascii = g_strdup(g_value_get_string(value));
320 changed_mask |= MASK_WB;
321 break;
322 CASE(SHARPEN, sharpen);
323 CASE(DENOISE_LUMA, denoise_luma);
324 CASE(DENOISE_CHROMA, denoise_chroma);
325 CASE(TCA_KR, tca_kr);
326 CASE(TCA_KB, tca_kb);
327 CASE(VIGNETTING, vignetting);
328 CASE(CHANNELMIXER_RED, channelmixer_red);
329 CASE(CHANNELMIXER_GREEN, channelmixer_green);
330 CASE(CHANNELMIXER_BLUE, channelmixer_blue);
331 case PROP_RECALC_TEMP:
332 settings->recalc_temp = g_value_get_boolean(value);
333 if (settings->recalc_temp)
334 changed_mask |= MASK_WB;
335 break;
336 default:
337 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
338 }
339 #undef CASE
340
341 if (changed_mask > 0)
342 {
343 if (settings->commit > 0)
344 settings->commit_todo |= changed_mask;
345 else
346 g_signal_emit(settings, signals[SETTINGS_CHANGED], 0, changed_mask);
347 }
348 }
349
350 /**
351 * Reset a RSSettings
352 * @param settings A RSSettings
353 * @param mask A mask for only resetting some values
354 */
355 void
rs_settings_reset(RSSettings * settings,const RSSettingsMask mask)356 rs_settings_reset(RSSettings *settings, const RSSettingsMask mask)
357 {
358 g_assert(RS_IS_SETTINGS(settings));
359 GObject *object = G_OBJECT(settings);
360
361 rs_settings_commit_start(settings);
362
363 if (mask & MASK_EXPOSURE)
364 rs_object_class_property_reset(object, "exposure");
365
366 if (mask & MASK_SATURATION)
367 rs_object_class_property_reset(object, "saturation");
368
369 if (mask & MASK_HUE)
370 rs_object_class_property_reset(object, "hue");
371
372 if (mask & MASK_CONTRAST)
373 rs_object_class_property_reset(object, "contrast");
374
375 if (mask & MASK_WARMTH)
376 rs_object_class_property_reset(object, "warmth");
377
378 if (mask & MASK_TINT)
379 rs_object_class_property_reset(object, "tint");
380
381 if (mask & MASK_WARMTH)
382 rs_object_class_property_reset(object, "dcp-temp");
383
384 if (mask & MASK_TINT)
385 rs_object_class_property_reset(object, "dcp-tint");
386
387 if (mask & MASK_SHARPEN)
388 rs_object_class_property_reset(object, "sharpen");
389
390 if (mask & MASK_DENOISE_LUMA)
391 rs_object_class_property_reset(object, "denoise_luma");
392
393 if (mask & MASK_DENOISE_CHROMA)
394 rs_object_class_property_reset(object, "denoise_chroma");
395
396 if (mask & MASK_TCA_KR)
397 rs_object_class_property_reset(object, "tca_kr");
398
399 if (mask & MASK_TCA_KB)
400 rs_object_class_property_reset(object, "tca_kb");
401
402 if (mask & MASK_VIGNETTING)
403 rs_object_class_property_reset(object, "vignetting");
404
405 if (mask & MASK_CHANNELMIXER_RED)
406 rs_object_class_property_reset(object, "channelmixer_red");
407
408 if (mask & MASK_CHANNELMIXER_GREEN)
409 rs_object_class_property_reset(object, "channelmixer_green");
410
411 if (mask & MASK_CHANNELMIXER_BLUE)
412 rs_object_class_property_reset(object, "channelmixer_blue");
413
414 if (mask && MASK_CURVE)
415 {
416 if (settings->curve_knots)
417 g_free(settings->curve_knots);
418 settings->curve_knots = g_new(gfloat, 4);
419 settings->curve_knots[0] = 0.0;
420 settings->curve_knots[1] = 0.0;
421 settings->curve_knots[2] = 1.0;
422 settings->curve_knots[3] = 1.0;
423 settings->curve_nknots = 2;
424 settings->commit_todo |= MASK_CURVE;
425 }
426 rs_settings_commit_stop(settings);
427 }
428
429 /**
430 * Stop signal emission from a RSSettings and queue up signals
431 * @param settings A RSSettings
432 */
433 void
rs_settings_commit_start(RSSettings * settings)434 rs_settings_commit_start(RSSettings *settings)
435 {
436 g_assert(RS_IS_SETTINGS(settings));
437 g_assert(settings->commit >= 0);
438
439 /* If we have no current commit running, reset todo */
440 if (settings->commit == 0)
441 settings->commit_todo = 0;
442
443 /* Increment commit */
444 settings->commit++;
445 }
446
447 /**
448 * Restart signal emission and process signal queue if any
449 * @param settings A RSSettings
450 * @return The mask of changes since rs_settings_commit_start()
451 */
452 RSSettingsMask
rs_settings_commit_stop(RSSettings * settings)453 rs_settings_commit_stop(RSSettings *settings)
454 {
455 g_assert(RS_IS_SETTINGS(settings));
456 g_assert(settings->commit >= 0);
457
458 /* If this is the last nested commit, do the todo */
459 if ((settings->commit == 1) && (settings->commit_todo != 0))
460 {
461 g_signal_emit(settings, signals[SETTINGS_CHANGED], 0, settings->commit_todo);
462 }
463
464 /* Make sure we never go below 0 */
465 settings->commit = MAX(settings->commit-1, 0);
466
467 return settings->commit_todo;
468 }
469
470 /**
471 * Copy settings from one RSSettins to another
472 * @param source The source RSSettings
473 * @param mask A RSSettingsMask to do selective copying
474 * @param target The target RSSettings
475 */
476 RSSettingsMask
rs_settings_copy(RSSettings * source,RSSettingsMask mask,RSSettings * target)477 rs_settings_copy(RSSettings *source, RSSettingsMask mask, RSSettings *target)
478 {
479 RSSettingsMask changed_mask = 0;
480
481 g_assert(RS_IS_SETTINGS(source));
482 g_assert(RS_IS_SETTINGS(target));
483
484 /* Convenience macro */
485 #define SETTINGS_COPY(upper, lower) \
486 do { \
487 if ((mask & MASK_##upper) && (target->lower != source->lower)) \
488 { \
489 changed_mask |= MASK_ ##upper; \
490 target->lower = source->lower; \
491 } \
492 } while(0)
493
494 if ((mask & MASK_WB) && (g_strcmp0(target->wb_ascii, source->wb_ascii) != 0))
495 {
496 if (target->wb_ascii)
497 g_free(target->wb_ascii);
498
499 changed_mask |= MASK_WB; \
500 target->wb_ascii = g_strdup(source->wb_ascii);
501 }
502 SETTINGS_COPY(EXPOSURE, exposure);
503 SETTINGS_COPY(SATURATION, saturation);
504 SETTINGS_COPY(HUE, hue);
505 SETTINGS_COPY(CONTRAST, contrast);
506 SETTINGS_COPY(WARMTH, warmth);
507 SETTINGS_COPY(TINT, tint);
508 SETTINGS_COPY(DCP_TEMP, dcp_temp);
509 SETTINGS_COPY(DCP_TINT, dcp_tint);
510 SETTINGS_COPY(SHARPEN, sharpen);
511 SETTINGS_COPY(DENOISE_LUMA, denoise_luma);
512 SETTINGS_COPY(DENOISE_CHROMA, denoise_chroma);
513 SETTINGS_COPY(TCA_KR, tca_kr);
514 SETTINGS_COPY(TCA_KB, tca_kb);
515 SETTINGS_COPY(VIGNETTING, vignetting);
516 SETTINGS_COPY(CHANNELMIXER_RED, channelmixer_red);
517 SETTINGS_COPY(CHANNELMIXER_GREEN, channelmixer_green);
518 SETTINGS_COPY(CHANNELMIXER_BLUE, channelmixer_blue);
519 #undef SETTINGS_COPY
520
521 if (mask & MASK_WB)
522 target->recalc_temp = source->recalc_temp;
523
524 if (mask & MASK_CURVE)
525 {
526 /* Check if we actually have changed */
527 if (target->curve_nknots != source->curve_nknots)
528 changed_mask |= MASK_CURVE;
529 else
530 {
531 if (memcmp(source->curve_knots, target->curve_knots, sizeof(gfloat)*2*source->curve_nknots)!=0)
532 changed_mask |= MASK_CURVE;
533 }
534
535 /* Copy the knots if needed */
536 if (changed_mask & MASK_CURVE)
537 {
538 g_free(target->curve_knots);
539 target->curve_knots = g_memdup(source->curve_knots, sizeof(gfloat)*2*source->curve_nknots);
540 target->curve_nknots = source->curve_nknots;
541 }
542 }
543
544 /* Emit seignal if needed */
545 if (changed_mask > 0)
546 g_signal_emit(target, signals[SETTINGS_CHANGED], 0, changed_mask);
547
548 return changed_mask;
549 }
550
551 /**
552 * Set curve knots
553 * @param settings A RSSettings
554 * @param knots Knots for curve
555 * @param nknots Number of knots
556 */
557 void
rs_settings_set_curve_knots(RSSettings * settings,const gfloat * knots,const gint nknots)558 rs_settings_set_curve_knots(RSSettings *settings, const gfloat *knots, const gint nknots)
559 {
560 g_assert(RS_IS_SETTINGS(settings));
561 g_assert(nknots > 0);
562 g_assert(knots != NULL);
563
564 g_free(settings->curve_knots);
565
566 settings->curve_knots = g_memdup(knots, sizeof(gfloat)*2*nknots);
567 settings->curve_nknots = nknots;
568
569 g_signal_emit(settings, signals[SETTINGS_CHANGED], 0, MASK_CURVE);
570 }
571
572 /**
573 * Set the warmth and tint values of a RSSettings
574 * @param settings A RSSettings
575 * @param exposure New value
576 */
577 void
rs_settings_set_wb(RSSettings * settings,const gfloat warmth,const gfloat tint,const gchar * ascii)578 rs_settings_set_wb(RSSettings *settings, const gfloat warmth, const gfloat tint, const gchar *ascii)
579 {
580 g_assert(RS_IS_SETTINGS(settings));
581
582 rs_settings_commit_start(settings);
583 g_object_set(settings, "warmth", warmth, "tint", tint, "wb_ascii", ascii, "recalc-temp", TRUE, NULL);
584 rs_settings_commit_stop(settings);
585 }
586
587 /**
588 * Get the knots from the curve
589 * @param settings A RSSettings
590 * @return All knots as a newly allocated array
591 */
592 gfloat *
rs_settings_get_curve_knots(RSSettings * settings)593 rs_settings_get_curve_knots(RSSettings *settings)
594 {
595 g_assert(RS_IS_SETTINGS(settings));
596
597 return g_memdup(settings->curve_knots, sizeof(gfloat)*2*settings->curve_nknots);
598 }
599
600 /**
601 * Get number of knots in curve in a RSSettings
602 * @param settings A RSSettings
603 * @return Number of knots
604 */
605 gint
rs_settings_get_curve_nknots(RSSettings * settings)606 rs_settings_get_curve_nknots(RSSettings *settings)
607 {
608 g_assert(RS_IS_SETTINGS(settings));
609
610 return settings->curve_nknots;
611 }
612
613 /**
614 * Link two RSSettings together, if source gets updated, it will propagate to target
615 * @param source A RSSettings
616 * @param target A RSSettings
617 */
618 void
rs_settings_link(RSSettings * source,RSSettings * target)619 rs_settings_link(RSSettings *source, RSSettings *target)
620 {
621 g_assert(RS_IS_SETTINGS(source));
622 g_assert(RS_IS_SETTINGS(target));
623
624 /* Add a weak reference to target, we would really like to know if it disappears */
625 g_object_weak_ref(G_OBJECT(target), (GWeakNotify) rs_settings_unlink, source);
626
627 /* Use glib signals to propagate changes */
628 g_signal_connect(source, "settings-changed", G_CALLBACK(rs_settings_copy), target);
629 }
630
631 /**
632 * Unlink two RSSettings - this will be done automaticly if target from a
633 * previous rs_settings_link() is finalized
634 * @param source A RSSettings
635 * @param target A RSSettings - can be destroyed, doesn't matter, we just need the pointer
636 */
637 void
rs_settings_unlink(RSSettings * source,RSSettings * target)638 rs_settings_unlink(RSSettings *source, RSSettings *target)
639 {
640 gulong signal_id;
641
642 g_assert(RS_IS_SETTINGS(source));
643
644 /* If we can find a signal linking these two pointers, disconnect it */
645 signal_id = g_signal_handler_find(source, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, target);
646 if (signal_id > 0)
647 g_signal_handler_disconnect(source, signal_id);
648 }
649