/* This is -*- C -*- */
/* vim: set sw=2: */
/*
* guppi-date-indexed.c
*
* Copyright (C) 2000 EMC Capital Management, Inc.
* Copyright (C) 2001 The Free Software Foundation
*
* Developed by Jon Trowbridge <trow@gnu.org>
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include <config.h>
#include "guppi-date-indexed.h"
#include <stdlib.h>
#include <string.h>
#include <guppi-convenient.h>
#include <guppi-memory.h>
#include <guppi-i18n.h>
typedef struct _GuppiDateIndexedPrivate GuppiDateIndexedPrivate;
struct _GuppiDateIndexedPrivate {
gboolean have_bounds;
GDate start_date, end_date;
gboolean have_size;
gint size;
};
#define priv(x) ((GuppiDateIndexedPrivate *)((x)->opaque_internals))
static GtkObjectClass * parent_class = NULL;
static void
guppi_date_indexed_finalize (GtkObject *obj)
{
GuppiDateIndexed *ind = GUPPI_DATE_INDEXED (obj);
GuppiDateIndexedPrivate *p = priv (ind);
p->have_bounds = FALSE;
p->have_size = FALSE;
guppi_free (ind->opaque_internals);
ind->opaque_internals = NULL;
if (parent_class->finalize)
parent_class->finalize (obj);
}
static gchar *
get_size_info (GuppiData *d)
{
GuppiDateIndexed *ind = GUPPI_DATE_INDEXED (d);
const GDate *dt1, *dt2;
gchar dt1buf[32];
gchar dt2buf[32];
if (guppi_date_indexed_empty (ind))
return guppi_strdup (_("empty"));
dt1 = guppi_date_indexed_start (ind);
dt2 = guppi_date_indexed_end (ind);
if (dt1 && dt2 && g_date_valid ((GDate *)dt1) && g_date_valid ((GDate *)dt2)) {
g_date_strftime (dt1buf, 32, "%x", (GDate *) dt1);
g_date_strftime (dt2buf, 32, "%x", (GDate *) dt2);
return guppi_strdup_printf (_("%s to %s"), dt1buf, dt2buf);
}
return guppi_strdup (_("invalid"));
}
static void
changed (GuppiData *data)
{
GuppiDateIndexedPrivate *p = priv (GUPPI_DATE_INDEXED (data));
p->have_bounds = p->have_size = FALSE;
if (GUPPI_DATA_CLASS (parent_class)->changed)
GUPPI_DATA_CLASS (parent_class)->changed (data);
}
static void
export_xml (GuppiData *data, GuppiXMLDocument *doc, xmlNodePtr content_node)
{
GuppiDateIndexed *ind = GUPPI_DATE_INDEXED (data);
GuppiDateIndexedClass *klass;
GDate dt;
gchar buf[64];
xmlNodePtr days_node;
klass = GUPPI_DATE_INDEXED_CLASS (GTK_OBJECT (data)->klass);
if (! klass->export_xml_element) {
xmlAddChild (content_node, xmlNewComment ("XML element format undefined."));
return;
}
days_node = guppi_xml_new_node (doc, "Days");
xmlAddChild (content_node, days_node);
dt = *guppi_date_indexed_start (ind);
while (guppi_date_indexed_in_bounds (ind, &dt)) {
xmlNodePtr node = klass->export_xml_element (ind, &dt, doc);
g_snprintf (buf, 64, "%d-%d-%d",
g_date_year ((GDate *)&dt), g_date_month ((GDate *)&dt), g_date_day ((GDate *)&dt));
xmlNewProp (node, "date", buf);
if (node)
xmlAddChild (days_node, node);
guppi_date_indexed_incr (ind, &dt);
}
}
static gboolean
import_xml (GuppiData *data, GuppiXMLDocument *doc, xmlNodePtr node)
{
GuppiDateIndexed *ind = GUPPI_DATE_INDEXED (data);
GuppiDateIndexedClass *klass;
GDate dt;
gchar *buf;
gint y, m, d;
klass = GUPPI_DATE_INDEXED_CLASS (GTK_OBJECT (data)->klass);
if (! klass->import_xml_element) {
g_warning ("XML element reader undefined.");
return FALSE;
}
if (! strcmp (node->name, "Days")) {
node = node->xmlChildrenNode;
while (node) {
buf = xmlGetProp (node, "date");
if (buf && sscanf (buf, "%d-%d-%d", &y, &m, &d) == 3) {
g_date_set_dmy (&dt, d, m, y);
klass->import_xml_element (ind, &dt, doc, node);
xmlFree (buf);
}
node = node->next;
}
return TRUE;
}
if (GUPPI_DATA_CLASS (parent_class)->import_xml)
return GUPPI_DATA_CLASS (parent_class)->import_xml (data, doc, node);
else
return FALSE;
}
static void
guppi_date_indexed_class_init (GuppiDateIndexedClass *klass)
{
GtkObjectClass *object_class = (GtkObjectClass *)klass;
GuppiDataClass *data_class = GUPPI_DATA_CLASS (klass);
parent_class = gtk_type_class(GUPPI_TYPE_DATA);
object_class->finalize = guppi_date_indexed_finalize;
data_class->get_size_info = get_size_info;
data_class->changed = changed;
data_class->export_xml = export_xml;
data_class->import_xml = import_xml;
}
static void
guppi_date_indexed_init (GuppiDateIndexed *obj)
{
GuppiDateIndexedPrivate *p;
p = guppi_new0 (GuppiDateIndexedPrivate, 1);
obj->opaque_internals = p;
}
GtkType
guppi_date_indexed_get_type (void)
{
static GtkType guppi_date_indexed_type = 0;
if (!guppi_date_indexed_type) {
static const GtkTypeInfo guppi_date_indexed_info = {
"GuppiDateIndexed",
sizeof(GuppiDateIndexed),
sizeof(GuppiDateIndexedClass),
(GtkClassInitFunc)guppi_date_indexed_class_init,
(GtkObjectInitFunc)guppi_date_indexed_init,
NULL, NULL, (GtkClassInitFunc)NULL
};
guppi_date_indexed_type = gtk_type_unique (GUPPI_TYPE_DATA,
&guppi_date_indexed_info);
}
return guppi_date_indexed_type;
}
static void
get_bounds (GuppiDateIndexed *ind)
{
GuppiDateIndexedPrivate *p;
GuppiDateIndexedClass *klass;
p = priv (ind);
klass = GUPPI_DATE_INDEXED_CLASS (GTK_OBJECT (ind)->klass);
g_assert (klass->bounds);
klass->bounds (ind, &p->start_date, &p->end_date);
p->have_bounds = TRUE;
}
const GDate *
guppi_date_indexed_start (GuppiDateIndexed *ind)
{
GuppiDateIndexedPrivate *p;
g_return_val_if_fail (GUPPI_IS_DATE_INDEXED (ind), NULL);
p = priv (ind);
if (! p->have_bounds)
get_bounds (ind);
return &p->start_date;
}
const GDate *
guppi_date_indexed_end (GuppiDateIndexed *ind)
{
GuppiDateIndexedPrivate *p;
g_return_val_if_fail (GUPPI_IS_DATE_INDEXED (ind), NULL);
p = priv (ind);
if (! p->have_bounds)
get_bounds (ind);
return &p->end_date;
}
#define IN_LOWER_BOUND(p, dt) (g_date_compare (&(p)->start_date, (GDate *)(dt)) <= 0)
#define IN_UPPER_BOUND(p, dt) (g_date_compare (&(p)->end_date, (GDate *)(dt)) >= 0)
#define IN_BOUNDS(p, dt) (IN_LOWER_BOUND (p, dt) && IN_UPPER_BOUND (p, dt))
#define OUT_OF_BOUNDS(p, dt) (!(IN_BOUNDS (p, dt)))
gboolean
guppi_date_indexed_in_bounds (GuppiDateIndexed *ind, const GDate *dt)
{
GuppiDateIndexedPrivate *p;
g_return_val_if_fail (GUPPI_IS_DATE_INDEXED (ind), FALSE);
g_return_val_if_fail (dt && g_date_valid ((GDate *) dt), FALSE);
p = priv (ind);
if (! p->have_bounds)
get_bounds (ind);
return IN_BOUNDS (p, dt);
}
void
guppi_date_indexed_clamp (GuppiDateIndexed *ind, GDate *dt)
{
GuppiDateIndexedPrivate *p;
g_return_if_fail (GUPPI_IS_DATE_INDEXED (ind));
g_return_if_fail (dt && g_date_valid (dt));
p = priv (ind);
if (! p->have_bounds)
get_bounds (ind);
if (g_date_lt (dt, &p->start_date))
*dt = p->start_date;
else if (g_date_lt (&p->end_date, dt))
*dt = p->end_date;
}
gboolean
guppi_date_indexed_valid (GuppiDateIndexed *ind, const GDate *dt)
{
GuppiDateIndexedPrivate *p;
GuppiDateIndexedClass *klass;
g_return_val_if_fail (GUPPI_IS_DATE_INDEXED (ind), FALSE);
g_return_val_if_fail (dt && g_date_valid ((GDate *) dt), FALSE);
p = priv (ind);
if (! p->have_bounds)
get_bounds (ind);
if (OUT_OF_BOUNDS (p, dt))
return FALSE;
klass = GUPPI_DATE_INDEXED_CLASS (GTK_OBJECT (ind)->klass);
g_assert (klass->valid);
return klass->valid (ind, dt);
}
gboolean
guppi_date_indexed_step (GuppiDateIndexed *ind, GDate *dt, gint delta)
{
GuppiDateIndexedPrivate *p;
GDate min_stepped_date;
GuppiDateIndexedClass *klass;
g_return_val_if_fail (GUPPI_IS_DATE_INDEXED (ind), FALSE);
g_return_val_if_fail (dt != NULL && g_date_valid (dt), FALSE);
if (delta == 0)
return TRUE;
p = priv (ind);
if (! p->have_bounds)
get_bounds (ind);
/* Make sure we aren't starting out of bounds. */
if (OUT_OF_BOUNDS (p, dt))
return FALSE;
/* Make sure that we aren't guaranteed to end up out of bounds,
no matter when happens when we step. */
min_stepped_date = *dt;
g_date_add_days (&min_stepped_date, delta);
if (OUT_OF_BOUNDS (p, &min_stepped_date)) {
*dt = min_stepped_date;
return FALSE;
}
klass = GUPPI_DATE_INDEXED_CLASS (GTK_OBJECT(ind)->klass);
if (klass->step) {
GDate base_dt = *dt;
return klass->step (ind, &base_dt, delta, dt);
} else {
/* Walk through the dates, decrementing delta every time we pass over
a valid date. */
g_assert (klass->valid);
while (delta) {
if (delta > 0) {
g_date_add_days (dt, 1);
if (! IN_UPPER_BOUND (p, dt))
return FALSE;
} else {
g_date_subtract_days (dt, 1);
if (! IN_LOWER_BOUND (p, dt))
return FALSE;
}
if (klass->valid (ind, dt))
delta += (delta > 0) ? -1 : +1;
}
}
return TRUE;
}
gboolean
guppi_date_indexed_incr (GuppiDateIndexed *ind, GDate *dt)
{
return guppi_date_indexed_step (ind, dt, 1);
}
gboolean
guppi_date_indexed_decr (GuppiDateIndexed *ind, GDate *dt)
{
return guppi_date_indexed_step (ind, dt, -1);
}
gint
guppi_date_indexed_size (GuppiDateIndexed *ind)
{
GuppiDateIndexedPrivate *p;
g_return_val_if_fail (GUPPI_IS_DATE_INDEXED (ind), -1);
if (guppi_date_indexed_empty (ind))
return 0;
p = priv (ind);
if (! p->have_size) {
GuppiDateIndexedClass *klass;
klass = GUPPI_DATE_INDEXED_CLASS (GTK_OBJECT (ind)->klass);
if (klass->size) {
p->size = klass->size (ind);
} else {
/* We do this the slow way: we walk across our dataset,
counting up valid days. */
GDate dt;
g_assert (klass->valid);
if (! p->have_bounds)
get_bounds (ind);
dt = p->start_date;
p->size = 0;
if (g_date_valid (&dt)) {
while (IN_UPPER_BOUND (p, &dt)) {
if (klass->valid (ind, &dt))
++p->size;
g_date_add_days (&dt, 1);
}
}
}
p->have_size = TRUE;
}
return p->size;
}
gboolean
guppi_date_indexed_empty (GuppiDateIndexed *ind)
{
gboolean sv, ev;
g_return_val_if_fail (GUPPI_IS_DATE_INDEXED (ind), TRUE);
sv = g_date_valid ((GDate *) guppi_date_indexed_start (ind));
ev = g_date_valid ((GDate *) guppi_date_indexed_end (ind));
if (sv && ev)
return FALSE;
if ((!sv) && (!ev))
return TRUE;
g_assert_not_reached ();
return FALSE;
}
gboolean
guppi_date_indexed_nonempty (GuppiDateIndexed *ind)
{
g_return_val_if_fail (GUPPI_IS_DATE_INDEXED (ind), FALSE);
return !guppi_date_indexed_empty (ind);
}
void
guppi_date_indexed_bounds_hint (GuppiDateIndexed *ind, const GDate *start, const GDate *end)
{
GuppiDateIndexedClass *klass;
g_return_if_fail (GUPPI_IS_DATE_INDEXED (ind));
g_return_if_fail (start && g_date_valid ((GDate *)start));
g_return_if_fail (end && g_date_valid ((GDate *)end));
if (g_date_compare ((GDate *)start, (GDate *)end) > 0)
return;
klass = GUPPI_DATE_INDEXED_CLASS (GTK_OBJECT (ind)->klass);
if (klass->bounds_hint)
klass->bounds_hint (ind, start, end);
}
syntax highlighted by Code2HTML, v. 0.9.1