/* 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 * * 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 #include "guppi-date-indexed.h" #include #include #include #include #include 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); }