/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Pix
*
* Copyright (C) 2014 The Free Software Foundation, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include "file-tools-enum-types.h"
#include "gth-curve-preset.h"
/* Signals */
enum {
CHANGED,
PRESET_CHANGED,
LAST_SIGNAL
};
typedef struct {
GthPoints points[GTH_HISTOGRAM_N_CHANNELS];
int id;
char *name;
} Preset;
struct _GthCurvePresetPrivate {
GFile *file;
GList *set;
int next_id;
};
static guint gth_curve_preset_signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE_WITH_CODE (GthCurvePreset,
gth_curve_preset,
G_TYPE_OBJECT,
G_ADD_PRIVATE (GthCurvePreset))
static char * Channel_Names[GTH_HISTOGRAM_N_CHANNELS] = { "value", "red", "green", "blue", "alpha" };
static int
get_channel_number (const char *name)
{
int i;
for (i = 0; i < GTH_HISTOGRAM_N_CHANNELS; i++)
if (g_strcmp0 (Channel_Names[i], name) == 0)
return i;
return -1;
}
static Preset *
preset_new (int id)
{
Preset *preset;
int c;
preset = g_new (Preset, 1);
for (c = 0; c < GTH_HISTOGRAM_N_CHANNELS; c++)
gth_points_init (&preset->points[c], 0);
preset->id = id;
preset->name = NULL;
return preset;
}
static DomElement *
preset_create_element (Preset *preset,
DomDocument *doc)
{
DomElement *element;
int c;
element = dom_document_create_element (doc, "preset", "name", preset->name, NULL);
for (c = 0; c < GTH_HISTOGRAM_N_CHANNELS; c++) {
DomElement *channel;
int i;
channel = dom_document_create_element (doc, "channel", "type", Channel_Names[c], NULL);
for (i = 0; i < preset->points[c].n; i++) {
GthPoint *p = preset->points[c].p + i;
char *x;
char *y;
x = g_strdup_printf ("%d", (int) p->x);
y = g_strdup_printf ("%d", (int) p->y);
dom_element_append_child (channel, dom_document_create_element (doc, "point", "x", x, "y", y, NULL));
g_free (x);
g_free (y);
}
dom_element_append_child (element, channel);
}
return element;
}
static void
preset_load_from_element (Preset *preset,
DomElement *element)
{
int c;
DomElement *node;
g_return_if_fail (element != NULL);
g_return_if_fail (g_strcmp0 (element->tag_name, "preset") == 0);
g_free (preset->name);
preset->name = g_strdup (dom_element_get_attribute (element, "name"));
for (c = 0; c < GTH_HISTOGRAM_N_CHANNELS; c++)
gth_points_dispose (&preset->points[c]);
for (node = element->first_child; node; node = node->next_sibling) {
if (g_strcmp0 (node->tag_name, "channel") == 0) {
int n_channel;
n_channel = get_channel_number (dom_element_get_attribute (node, "type"));
if (n_channel > -1) {
DomElement *point;
for (point = node->first_child; point; point = point->next_sibling) {
if (g_strcmp0 (point->tag_name, "point") == 0) {
const char *sx, *sy;
int x, y;
sx = dom_element_get_attribute (point, "x");
sy = dom_element_get_attribute (point, "y");
if (sscanf (sx, "%d", &x) != 1)
continue;
if (sscanf (sy, "%d", &y) != 1)
continue;
gth_points_add_point (&preset->points[n_channel], x, y);
}
}
}
}
}
}
static void
preset_free (Preset *preset)
{
int c;
g_return_if_fail (preset != NULL);
for (c = 0; c < GTH_HISTOGRAM_N_CHANNELS; c++)
gth_points_dispose (&preset->points[c]);
g_free (preset->name);
g_free (preset);
}
static void
gth_curve_preset_finalize (GObject *obj)
{
GthCurvePreset *self;
self = GTH_CURVE_PRESET (obj);
g_list_free_full (self->priv->set, (GDestroyNotify) preset_free);
_g_object_unref (self->priv->file);
G_OBJECT_CLASS (gth_curve_preset_parent_class)->finalize (obj);
}
static void
gth_curve_preset_class_init (GthCurvePresetClass *klass)
{
GObjectClass *object_class;
object_class = (GObjectClass*) klass;
object_class->finalize = gth_curve_preset_finalize;
/* signals */
gth_curve_preset_signals[CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GthCurvePresetClass, changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
gth_curve_preset_signals[PRESET_CHANGED] =
g_signal_new ("preset-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GthCurvePresetClass, preset_changed),
NULL, NULL,
gth_marshal_VOID__ENUM_INT,
G_TYPE_NONE,
2,
GTH_TYPE_PRESET_ACTION,
G_TYPE_INT);
}
static void
gth_curve_preset_changed (GthCurvePreset *self)
{
g_signal_emit (self, gth_curve_preset_signals[CHANGED], 0);
}
static void
gth_curve_preset_init (GthCurvePreset *self)
{
self->priv = gth_curve_preset_get_instance_private (self);
self->priv->set = NULL;
self->priv->file = NULL;
self->priv->next_id = 0;
}
GthCurvePreset *
gth_curve_preset_new_from_file (GFile *file)
{
GthCurvePreset *self;
DomDocument *doc;
void *buffer;
gsize size;
self = (GthCurvePreset *) g_object_new (GTH_TYPE_CURVE_PRESET, NULL);
self->priv->file = g_file_dup (file);
doc = dom_document_new ();
if (_g_file_load_in_buffer (self->priv->file, &buffer, &size, NULL, NULL)) {
if (dom_document_load (doc, buffer, size, NULL)) {
DomElement *presets = DOM_ELEMENT (doc)->first_child;
if ((presets != NULL) && (g_strcmp0 (presets->tag_name, "presets") == 0)) {
DomElement *node;
for (node = presets->first_child; node; node = node->next_sibling) {
if (g_strcmp0 (node->tag_name, "preset") == 0) {
Preset *preset;
preset = preset_new (self->priv->next_id++);
preset_load_from_element (preset, node);
self->priv->set = g_list_append (self->priv->set, preset);
}
}
}
}
g_free (buffer);
}
g_object_unref (doc);
return self;
}
int
gth_curve_preset_get_size (GthCurvePreset *self)
{
return g_list_length (self->priv->set);
}
gboolean
gth_curve_preset_get_nth (GthCurvePreset *self,
int n,
int *id,
const char **name,
GthPoints **points)
{
Preset *preset;
preset = g_list_nth_data (self->priv->set, n);
if (preset == NULL)
return FALSE;
if (id) *id = preset->id;
if (name) *name = preset->name;
if (points) *points = preset->points;
return TRUE;
}
gboolean
gth_curve_preset_get_by_id (GthCurvePreset *self,
int id,
const char **name,
GthPoints **points)
{
GList *scan;
for (scan = self->priv->set; scan; scan = scan->next) {
Preset *preset = scan->data;
if (preset->id == id) {
if (name) *name = preset->name;
if (points) *points = preset->points;
return TRUE;
}
}
return FALSE;
}
int
gth_curve_preset_get_pos (GthCurvePreset *self,
int id)
{
GList *scan;
int pos;
for (pos = 0, scan = self->priv->set; scan; scan = scan->next, pos++) {
Preset *preset = scan->data;
if (preset->id == id)
return pos;
}
return -1;
}
int
gth_curve_preset_add (GthCurvePreset *self,
const char *name,
GthPoints *points)
{
Preset *preset;
int c;
preset = preset_new (self->priv->next_id++);
preset->name = g_strdup (name);
for (c = 0; c < GTH_HISTOGRAM_N_CHANNELS; c++)
gth_points_copy (points + c, &preset->points[c]);
self->priv->set = g_list_append (self->priv->set, preset);
gth_curve_preset_changed (self);
g_signal_emit (self, gth_curve_preset_signals[PRESET_CHANGED], 0, GTH_PRESET_ACTION_ADDED, preset->id);
return preset->id;
}
static int
compare_preset_by_id (Preset *a,
gpointer id_p)
{
int id = GPOINTER_TO_INT (id_p);
return (a->id == id) ? 0 : (a->id > id) ? 1 : -1;
}
void
gth_curve_preset_remove (GthCurvePreset *self,
int id)
{
GList *link;
link = g_list_find_custom (self->priv->set, GINT_TO_POINTER (id), (GCompareFunc) compare_preset_by_id);
if (link == NULL)
return;
self->priv->set = g_list_remove_link (self->priv->set, link);
gth_curve_preset_changed (self);
g_signal_emit (self, gth_curve_preset_signals[PRESET_CHANGED], 0, GTH_PRESET_ACTION_REMOVED, id);
g_list_free_full (link, (GDestroyNotify) preset_free);
}
void
gth_curve_preset_rename (GthCurvePreset *self,
int id,
const char *new_name)
{
GList *link;
Preset *preset;
link = g_list_find_custom (self->priv->set, GINT_TO_POINTER (id), (GCompareFunc) compare_preset_by_id);
g_return_if_fail (link != NULL);
preset = link->data;
g_free (preset->name);
preset->name = g_strdup (new_name);
gth_curve_preset_changed (self);
g_signal_emit (self, gth_curve_preset_signals[PRESET_CHANGED], 0, GTH_PRESET_ACTION_RENAMED, id);
}
void
gth_curve_preset_change_order (GthCurvePreset *self,
GList *id_list)
{
GList *set, *scan;
set = NULL;
for (scan = id_list; scan; scan = scan->next) {
int id = GPOINTER_TO_INT (scan->data);
GList *link;
link = g_list_find_custom (self->priv->set, GINT_TO_POINTER (id), (GCompareFunc) compare_preset_by_id);
g_return_if_fail (link != NULL);
set = g_list_prepend (set, link->data);
}
set = g_list_reverse (set);
g_list_free (self->priv->set);
self->priv->set = set;
gth_curve_preset_changed (self);
g_signal_emit (self, gth_curve_preset_signals[PRESET_CHANGED], 0, GTH_PRESET_ACTION_CHANGED_ORDER, -1);
}
GList *
gth_curve_preset_get_order (GthCurvePreset *self)
{
GList *id_list, *scan;
id_list = NULL;
for (scan = self->priv->set; scan; scan = scan->next) {
Preset *preset = scan->data;
id_list = g_list_prepend (id_list, GINT_TO_POINTER (preset->id));
}
return g_list_reverse (id_list);
}
gboolean
gth_curve_preset_save (GthCurvePreset *self,
GError **error)
{
DomDocument *doc;
DomElement *curves;
GList *scan;
char *buffer;
gsize size;
gboolean result;
g_return_val_if_fail (self->priv->file != NULL, FALSE);
doc = dom_document_new ();
curves = dom_document_create_element (doc, "presets", NULL);
for (scan = self->priv->set; scan; scan = scan->next) {
Preset *preset = scan->data;
dom_element_append_child (curves, preset_create_element (preset, doc));
}
dom_element_append_child (DOM_ELEMENT (doc), curves);
buffer = dom_document_dump (doc, &size);
result = _g_file_write (self->priv->file, FALSE, G_FILE_CREATE_NONE, buffer, size, NULL, error);
g_free (buffer);
g_object_unref (doc);
return result;
}