#include #include #include #include #include #include #include #include #include #include #include #include "ag-chart.h" struct _AgChartPrivate { gchar *name; gchar *country; gchar *city; gchar *save_buffer; GList *planet_list; gchar *note; }; enum { PROP_0, PROP_NAME, PROP_COUNTRY, PROP_CITY }; typedef enum { XML_CONVERT_STRING, XML_CONVERT_DOUBLE, XML_CONVERT_INT } XmlConvertType; G_DEFINE_QUARK(ag_chart_error_quark, ag_chart_error); G_DEFINE_TYPE_WITH_PRIVATE(AgChart, ag_chart, GSWE_TYPE_MOMENT); #define ag_g_variant_unref(v) \ if ((v) != NULL) { \ g_variant_unref((v)); \ } static void ag_chart_set_property(GObject *gobject, guint prop_id, const GValue *value, GParamSpec *param_spec); static void ag_chart_get_property(GObject *gobject, guint prop_id, GValue *value, GParamSpec *param_spec); static void ag_chart_finalize(GObject *gobject); static void ag_chart_class_init(AgChartClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->set_property = ag_chart_set_property; gobject_class->get_property = ag_chart_get_property; gobject_class->finalize = ag_chart_finalize; g_object_class_install_property( gobject_class, PROP_NAME, g_param_spec_string( "name", "Chart name", "Name of the person on this chart", NULL, G_PARAM_READWRITE ) ); g_object_class_install_property( gobject_class, PROP_COUNTRY, g_param_spec_string( "country", "Country name", "Name of the country of birth", NULL, G_PARAM_READWRITE ) ); g_object_class_install_property( gobject_class, PROP_CITY, g_param_spec_string( "city", "City name", "Name of the city of birth", NULL, G_PARAM_READWRITE ) ); } static void ag_chart_init(AgChart *chart) { chart->priv = ag_chart_get_instance_private(chart); chart->priv->name = NULL; chart->priv->country = NULL; chart->priv->city = NULL; chart->priv->save_buffer = NULL; chart->priv->planet_list = NULL; } static void ag_chart_set_property(GObject *gobject, guint prop_id, const GValue *value, GParamSpec *param_spec) { switch (prop_id) { case PROP_NAME: ag_chart_set_name(AG_CHART(gobject), g_value_get_string(value)); break; case PROP_COUNTRY: ag_chart_set_country(AG_CHART(gobject), g_value_get_string(value)); break; case PROP_CITY: ag_chart_set_city(AG_CHART(gobject), g_value_get_string(value)); break; } } static void ag_chart_get_property(GObject *gobject, guint prop_id, GValue *value, GParamSpec *param_spec) { switch (prop_id) { case PROP_NAME: g_value_set_string(value, AG_CHART(gobject)->priv->name); break; case PROP_COUNTRY: g_value_set_string(value, AG_CHART(gobject)->priv->country); break; case PROP_CITY: g_value_set_string(value, AG_CHART(gobject)->priv->city); break; } } static void ag_chart_finalize(GObject *gobject) { AgChart *chart = AG_CHART(gobject); if (chart->priv->name != NULL) { g_free(chart->priv->name); } if (chart->priv->country != NULL) { g_free(chart->priv->country); } if (chart->priv->city != NULL) { g_free(chart->priv->city); } if (chart->priv->save_buffer != NULL) { g_free(chart->priv->save_buffer); } } void ag_chart_add_planets(AgChart *chart) { gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_CHARIKLO, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_CHARIKLO) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_VESTA, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_VESTA) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_JUNO, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_JUNO) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_PALLAS, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_PALLAS) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_CERES, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_CERES) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_NESSUS, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_NESSUS) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_PHOLUS, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_PHOLUS) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_CHIRON, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_CHIRON) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_MOON_APOGEE, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_MOON_APOGEE) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_MOON_NODE, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_MOON_NODE) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_PLUTO, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_PLUTO) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_NEPTUNE, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_NEPTUNE) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_URANUS, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_URANUS) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_SATURN, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_SATURN) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_JUPITER, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_JUPITER) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_MARS, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_MARS) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_VENUS, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_VENUS) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_MERCURY, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_MERCURY) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_MOON, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_MOON) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_SUN, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_SUN) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_VERTEX, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_VERTEX) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_ASCENDANT, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_ASCENDANT) ); gswe_moment_add_planet(GSWE_MOMENT(chart), GSWE_PLANET_MC, NULL); chart->priv->planet_list = g_list_prepend( chart->priv->planet_list, GINT_TO_POINTER(GSWE_PLANET_MC) ); } AgChart * ag_chart_new_full(GsweTimestamp *timestamp, gdouble longitude, gdouble latitude, gdouble altitude, GsweHouseSystem house_system) { AgChart *chart; GsweCoordinates *coords = g_new0(GsweCoordinates, 1); coords->longitude = longitude; coords->latitude = latitude; coords->altitude = altitude; chart = AG_CHART(g_object_new(AG_TYPE_CHART, "timestamp", timestamp, "coordinates", coords, "house-system", house_system, NULL)); g_free(coords); ag_chart_add_planets(chart); return chart; } void ag_chart_set_name(AgChart *chart, const gchar *name) { if (chart->priv->name != NULL) { g_free(chart->priv->name); } chart->priv->name = g_strdup(name); } gchar * ag_chart_get_name(AgChart *chart) { return g_strdup(chart->priv->name); } void ag_chart_set_country(AgChart *chart, const gchar *country) { if (chart->priv->country != NULL) { g_free(chart->priv->country); } chart->priv->country = g_strdup(country); } gchar * ag_chart_get_country(AgChart *chart) { return g_strdup(chart->priv->country); } void ag_chart_set_city(AgChart *chart, const gchar *city) { if (chart->priv->city != NULL) { g_free(chart->priv->city); } chart->priv->city = g_strdup(city); } gchar * ag_chart_get_city(AgChart *chart) { return g_strdup(chart->priv->city); } /** * get_by_xpath: * @xpath_context: an XPath context * @uri: the name of the file currently being processed. Used in error messages * only * @xpath: an XPath expression * @value_required: marks the value as required. Although the XML tags must be * present, some values (like country or city name) may be * omitted * @type: the type of the variable to return * @err: a GError * * Get the value of an XML tag via XPath. * * Returns: (transfer container): a GVariant with the requested value */ static GVariant * get_by_xpath(xmlXPathContextPtr xpath_context, const gchar *uri, const gchar *xpath, gboolean value_required, XmlConvertType type, GError **err) { xmlXPathObjectPtr xpathObj; const gchar *text; char *endptr; GVariant *ret = NULL; gdouble d; gint i; if ((xpathObj = xmlXPathEvalExpression( (const xmlChar *)xpath, xpath_context )) == NULL) { g_set_error( err, AG_CHART_ERROR, AG_CHART_ERROR_LIBXML, "File '%s' could not be parsed due to internal XML error.", uri ); return NULL; } if (xpathObj->nodesetval == NULL) { g_debug("No such node '%s'", xpath); g_set_error( err, AG_CHART_ERROR, AG_CHART_ERROR_CORRUPT_FILE, "File '%s' doesn't look like a valid saved chart: " "missing node: %s", uri, xpath ); xmlXPathFreeObject(xpathObj); return NULL; } if (xpathObj->nodesetval->nodeNr > 1) { g_debug("Too many '%s' nodes", xpath); g_set_error( err, AG_CHART_ERROR, AG_CHART_ERROR_CORRUPT_FILE, "File '%s' doesn't look like a valid saved chart: " "too many node: %s", uri, xpath ); xmlXPathFreeObject(xpathObj); return NULL; } if (xpathObj->nodesetval->nodeNr == 0) { if (value_required) { g_debug("No '%s' nodes", xpath); g_set_error( err, AG_CHART_ERROR, AG_CHART_ERROR_CORRUPT_FILE, "File '%s' doesn't look like a valid saved chart: " "missing node: %s", uri, xpath ); xmlXPathFreeObject(xpathObj); return NULL; } else { GVariant *ret = NULL; switch (type) { case XML_CONVERT_STRING: ret = g_variant_new("ms", NULL); break; case XML_CONVERT_DOUBLE: ret = g_variant_new("md", FALSE, 0); break; case XML_CONVERT_INT: ret = g_variant_new("mi", FALSE, 0); break; } return ret; } } text = (const gchar *)xpathObj->nodesetval->nodeTab[0]->content; switch (type) { case XML_CONVERT_STRING: ret = g_variant_new("ms", text); break; case XML_CONVERT_DOUBLE: d = g_ascii_strtod(text, &endptr); if ((*endptr != 0) || (errno != 0)) { g_set_error( err, AG_CHART_ERROR, AG_CHART_ERROR_CORRUPT_FILE, "File '%s' doesn't look like a valid saved chart: " "Invalid value in node: %s", uri, xpath ); ret = NULL; } else { ret = g_variant_new_double(d); } break; case XML_CONVERT_INT: i = strtol(text, &endptr, 10); if ((*endptr != 0) || (errno != 0)) { g_set_error( err, AG_CHART_ERROR, AG_CHART_ERROR_CORRUPT_FILE, "File '%s' doesn't look like a valid saved chart: " "Invalid value in node: %s", uri, xpath ); ret = NULL; } else { ret = g_variant_new_int32(i); } break; } xmlXPathFreeObject(xpathObj); return ret; } AgChart * ag_chart_load_from_file(GFile *file, GError **err) { AgChart *chart = NULL; gchar *uri, *xml = NULL, *name, *country_name, *city_name; gsize length; xmlDocPtr doc; xmlXPathContextPtr xpath_context; GVariant *chart_name, *country, *city, *longitude, *latitude, *altitude, *year, *month, *day, *hour, *minute, *second, *timezone, *note; GsweTimestamp *timestamp; gboolean found_error = FALSE; uri = g_file_get_uri(file); if (!g_file_load_contents(file, NULL, &xml, &length, NULL, err)) { g_free(uri); return NULL; } if ((doc = xmlReadMemory(xml, length, "chart.xml", NULL, 0)) == NULL) { g_set_error( err, AG_CHART_ERROR, AG_CHART_ERROR_CORRUPT_FILE, "File '%s' can not be read. " \ "Maybe it is corrupt, or not a save file at all", uri ); g_free(xml); g_free(uri); return NULL; } if ((xpath_context = xmlXPathNewContext(doc)) == NULL) { g_set_error( err, AG_CHART_ERROR, AG_CHART_ERROR_LIBXML, "File '%s' could not be loaded due to internal LibXML error", uri ); xmlFreeDoc(doc); g_free(xml); g_free(uri); return NULL; } if ((chart_name = get_by_xpath( xpath_context, uri, "/chartinfo/data/name/text()", TRUE, XML_CONVERT_STRING, err )) == NULL) { found_error = TRUE; } if ((country = get_by_xpath( xpath_context, uri, "/chartinfo/data/place/country/text()", FALSE, XML_CONVERT_STRING, err )) == NULL) { found_error = TRUE; } if ((city = get_by_xpath( xpath_context, uri, "/chartinfo/data/place/city/text()", FALSE, XML_CONVERT_STRING, err )) == NULL) { found_error = TRUE; } if ((longitude = get_by_xpath( xpath_context, uri, "/chartinfo/data/place/longitude/text()", TRUE, XML_CONVERT_DOUBLE, err )) == NULL) { found_error = TRUE; } if ((latitude = get_by_xpath( xpath_context, uri, "/chartinfo/data/place/latitude/text()", TRUE, XML_CONVERT_DOUBLE, err )) == NULL) { found_error = TRUE; } if ((altitude = get_by_xpath( xpath_context, uri, "/chartinfo/data/place/altitude/text()", TRUE, XML_CONVERT_DOUBLE, err )) == NULL) { found_error = TRUE; } if ((year = get_by_xpath( xpath_context, uri, "/chartinfo/data/time/year/text()", TRUE, XML_CONVERT_INT, err )) == NULL) { found_error = TRUE; } if ((month = get_by_xpath( xpath_context, uri, "/chartinfo/data/time/month/text()", TRUE, XML_CONVERT_INT, err )) == NULL) { found_error = TRUE; } if ((day = get_by_xpath( xpath_context, uri, "/chartinfo/data/time/day/text()", TRUE, XML_CONVERT_INT, err )) == NULL) { found_error = TRUE; } if ((hour = get_by_xpath( xpath_context, uri, "/chartinfo/data/time/hour/text()", TRUE, XML_CONVERT_INT, err )) == NULL) { found_error = TRUE; } if ((minute = get_by_xpath( xpath_context, uri, "/chartinfo/data/time/minute/text()", TRUE, XML_CONVERT_INT, err )) == NULL) { found_error = TRUE; } if ((second = get_by_xpath( xpath_context, uri, "/chartinfo/data/time/second/text()", TRUE, XML_CONVERT_INT, err )) == NULL) { found_error = TRUE; } if ((timezone = get_by_xpath( xpath_context, uri, "/chartinfo/data/time/timezone/text()", TRUE, XML_CONVERT_DOUBLE, err )) == NULL) { found_error = TRUE; } note = get_by_xpath( xpath_context, uri, "/chartinfo/note/text()", FALSE, XML_CONVERT_STRING, NULL ); if (found_error) { ag_g_variant_unref(note); ag_g_variant_unref(chart_name); ag_g_variant_unref(country); ag_g_variant_unref(city); ag_g_variant_unref(longitude); ag_g_variant_unref(latitude); ag_g_variant_unref(altitude); ag_g_variant_unref(year); ag_g_variant_unref(month); ag_g_variant_unref(day); ag_g_variant_unref(hour); ag_g_variant_unref(minute); ag_g_variant_unref(second); ag_g_variant_unref(timezone); xmlFreeDoc(doc); g_free(xml); g_free(uri); return NULL; } timestamp = gswe_timestamp_new_from_gregorian_full( g_variant_get_int32(year), g_variant_get_int32(month), g_variant_get_int32(day), g_variant_get_int32(hour), g_variant_get_int32(minute), g_variant_get_int32(second), 0, g_variant_get_double(timezone) ); g_variant_unref(year); g_variant_unref(month); g_variant_unref(day); g_variant_unref(hour); g_variant_unref(minute); g_variant_unref(second); g_variant_unref(timezone); // TODO: Make house system configurable (and saveable) chart = ag_chart_new_full( timestamp, g_variant_get_double(longitude), g_variant_get_double(latitude), g_variant_get_double(altitude), GSWE_HOUSE_SYSTEM_PLACIDUS ); g_variant_unref(longitude); g_variant_unref(latitude); g_variant_unref(altitude); g_variant_get(chart_name, "ms", &name); g_variant_unref(chart_name); ag_chart_set_name(chart, name); g_free(name); g_variant_get(country, "ms", &country_name); g_variant_unref(country); ag_chart_set_country(chart, country_name); g_free(country_name); g_variant_get(city, "ms", &city_name); g_variant_unref(city); ag_chart_set_city(chart, city_name); g_free(city_name); if (note) { gchar *note_text; g_variant_get(note, "ms", ¬e_text); g_variant_unref(note); ag_chart_set_note(chart, note_text); } g_free(xml); g_free(uri); xmlXPathFreeContext(xpath_context); xmlFreeDoc(doc); return chart; } static xmlDocPtr create_save_doc(AgChart *chart) { xmlDocPtr doc = NULL; xmlNodePtr root_node = NULL, data_node = NULL, place_node = NULL, time_node = NULL; gchar *value; GsweCoordinates *coordinates; GsweTimestamp *timestamp; doc = xmlNewDoc(BAD_CAST "1.0"); root_node = xmlNewNode(NULL, BAD_CAST "chartinfo"); xmlDocSetRootElement(doc, root_node); // Begin node data_node = xmlNewChild(root_node, NULL, BAD_CAST "data", NULL); value = ag_chart_get_name(chart); xmlNewChild(data_node, NULL, BAD_CAST "name", BAD_CAST value); g_free(value); // Begin node place_node = xmlNewChild(data_node, NULL, BAD_CAST "place", NULL); value = ag_chart_get_country(chart); xmlNewChild(place_node, NULL, BAD_CAST "country", BAD_CAST value); g_free(value); value = ag_chart_get_city(chart); xmlNewChild(place_node, NULL, BAD_CAST "city", BAD_CAST value); g_free(value); coordinates = gswe_moment_get_coordinates(GSWE_MOMENT(chart)); value = g_malloc0(12); g_ascii_dtostr(value, 12, coordinates->longitude); xmlNewChild(place_node, NULL, BAD_CAST "longitude", BAD_CAST value); g_free(value); value = g_malloc0(12); g_ascii_dtostr(value, 12, coordinates->latitude); xmlNewChild(place_node, NULL, BAD_CAST "latitude", BAD_CAST value); g_free(value); value = g_malloc0(12); g_ascii_dtostr(value, 12, coordinates->altitude); xmlNewChild(place_node, NULL, BAD_CAST "altitude", BAD_CAST value); g_free(value); g_free(coordinates); // Begin