1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3  * GData Client
4  * Copyright (C) Philip Withnall 2009–2010 <philip@tecnocode.co.uk>
5  *
6  * GData Client is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * GData Client is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with GData Client.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 /**
21  * SECTION:gdata-gd-reminder
22  * @short_description: GData reminder element
23  * @stability: Stable
24  * @include: gdata/gd/gdata-gd-reminder.h
25  *
26  * #GDataGDReminder represents a "reminder" element from the
27  * <ulink type="http" url="http://code.google.com/apis/gdata/docs/2.0/elements.html#gdReminder">GData specification</ulink>.
28  *
29  * Since: 0.4.0
30  */
31 
32 #include <glib.h>
33 #include <libxml/parser.h>
34 
35 #include "gdata-gd-reminder.h"
36 #include "gdata-parsable.h"
37 #include "gdata-parser.h"
38 #include "gdata-types.h"
39 #include "gdata-comparable.h"
40 
41 static void gdata_gd_reminder_comparable_init (GDataComparableIface *iface);
42 static void gdata_gd_reminder_finalize (GObject *object);
43 static void gdata_gd_reminder_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
44 static void gdata_gd_reminder_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
45 static gboolean pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
46 static void pre_get_xml (GDataParsable *parsable, GString *xml_string);
47 static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);
48 
49 struct _GDataGDReminderPrivate {
50 	gchar *method;
51 	gint64 absolute_time;
52 	gint relative_time;
53 };
54 
55 enum {
56 	PROP_METHOD = 1,
57 	PROP_ABSOLUTE_TIME,
58 	PROP_IS_ABSOLUTE_TIME,
59 	PROP_RELATIVE_TIME
60 };
61 
G_DEFINE_TYPE_WITH_CODE(GDataGDReminder,gdata_gd_reminder,GDATA_TYPE_PARSABLE,G_IMPLEMENT_INTERFACE (GDATA_TYPE_COMPARABLE,gdata_gd_reminder_comparable_init))62 G_DEFINE_TYPE_WITH_CODE (GDataGDReminder, gdata_gd_reminder, GDATA_TYPE_PARSABLE,
63                          G_IMPLEMENT_INTERFACE (GDATA_TYPE_COMPARABLE, gdata_gd_reminder_comparable_init))
64 
65 static void
66 gdata_gd_reminder_class_init (GDataGDReminderClass *klass)
67 {
68 	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
69 	GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
70 
71 	g_type_class_add_private (klass, sizeof (GDataGDReminderPrivate));
72 
73 	gobject_class->get_property = gdata_gd_reminder_get_property;
74 	gobject_class->set_property = gdata_gd_reminder_set_property;
75 	gobject_class->finalize = gdata_gd_reminder_finalize;
76 
77 	parsable_class->pre_parse_xml = pre_parse_xml;
78 	parsable_class->pre_get_xml = pre_get_xml;
79 	parsable_class->get_namespaces = get_namespaces;
80 	parsable_class->element_name = "reminder";
81 	parsable_class->element_namespace = "gd";
82 
83 	/**
84 	 * GDataGDReminder:method:
85 	 *
86 	 * The notification method the reminder should use. For example: %GDATA_GD_REMINDER_ALERT or %GDATA_GD_REMINDER_EMAIL.
87 	 *
88 	 * For more information, see the
89 	 * <ulink type="http" url="http://code.google.com/apis/gdata/docs/2.0/elements.html#gdReminder">GData specification</ulink>.
90 	 *
91 	 * Since: 0.4.0
92 	 */
93 	g_object_class_install_property (gobject_class, PROP_METHOD,
94 	                                 g_param_spec_string ("method",
95 	                                                      "Method", "The notification method the reminder should use.",
96 	                                                      NULL,
97 	                                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
98 
99 	/**
100 	 * GDataGDReminder:absolute-time:
101 	 *
102 	 * Absolute time at which the reminder should be issued.
103 	 *
104 	 * For more information, see the
105 	 * <ulink type="http" url="http://code.google.com/apis/gdata/docs/2.0/elements.html#gdReminder">GData specification</ulink>.
106 	 *
107 	 * Since: 0.4.0
108 	 */
109 	g_object_class_install_property (gobject_class, PROP_ABSOLUTE_TIME,
110 	                                 g_param_spec_int64 ("absolute-time",
111 	                                                     "Absolute time", "Absolute time at which the reminder should be issued.",
112 	                                                     -1, G_MAXINT64, -1,
113 	                                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
114 
115 	/**
116 	 * GDataGDReminder:is-absolute-time:
117 	 *
118 	 * Whether the reminder is specified as an absolute or relative time.
119 	 *
120 	 * For more information, see the
121 	 * <ulink type="http" url="http://code.google.com/apis/gdata/docs/2.0/elements.html#gdReminder">GData specification</ulink>.
122 	 *
123 	 * Since: 0.4.0
124 	 */
125 	g_object_class_install_property (gobject_class, PROP_IS_ABSOLUTE_TIME,
126 	                                 g_param_spec_boolean ("is-absolute-time",
127 	                                                       "Absolute time?", "Whether the reminder is specified as an absolute or relative time.",
128 	                                                       FALSE,
129 	                                                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
130 
131 	/**
132 	 * GDataGDReminder:relative-time:
133 	 *
134 	 * Time at which the reminder should be issued, in minutes relative to the start time of the corresponding event.
135 	 *
136 	 * For more information, see the
137 	 * <ulink type="http" url="http://code.google.com/apis/gdata/docs/2.0/elements.html#gdReminder">GData specification</ulink>.
138 	 *
139 	 * Since: 0.4.0
140 	 */
141 	g_object_class_install_property (gobject_class, PROP_RELATIVE_TIME,
142 	                                 g_param_spec_int ("relative-time",
143 	                                                   "Relative time", "Time at which the reminder should be issued, in minutes.",
144 	                                                   -1, G_MAXINT, -1,
145 	                                                   G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
146 }
147 
148 static gint
compare_with(GDataComparable * self,GDataComparable * other)149 compare_with (GDataComparable *self, GDataComparable *other)
150 {
151 	gint method_cmp, time_cmp;
152 	GDataGDReminder *a = (GDataGDReminder*) self, *b = (GDataGDReminder*) other;
153 
154 	if (gdata_gd_reminder_is_absolute_time (a) != gdata_gd_reminder_is_absolute_time (b))
155 		return 1;
156 
157 	method_cmp = g_strcmp0 (a->priv->method, b->priv->method);
158 	if (gdata_gd_reminder_is_absolute_time (a) == TRUE) {
159 		time_cmp = a->priv->absolute_time - b->priv->absolute_time;
160 	} else {
161 		time_cmp = a->priv->relative_time - b->priv->relative_time;
162 	}
163 
164 	if (method_cmp == 0)
165 		return time_cmp;
166 	else
167 		return method_cmp;
168 }
169 
170 static void
gdata_gd_reminder_comparable_init(GDataComparableIface * iface)171 gdata_gd_reminder_comparable_init (GDataComparableIface *iface)
172 {
173 	iface->compare_with = compare_with;
174 }
175 
176 static void
gdata_gd_reminder_init(GDataGDReminder * self)177 gdata_gd_reminder_init (GDataGDReminder *self)
178 {
179 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_GD_REMINDER, GDataGDReminderPrivate);
180 	self->priv->absolute_time = -1;
181 }
182 
183 static void
gdata_gd_reminder_finalize(GObject * object)184 gdata_gd_reminder_finalize (GObject *object)
185 {
186 	GDataGDReminderPrivate *priv = GDATA_GD_REMINDER (object)->priv;
187 
188 	g_free (priv->method);
189 
190 	/* Chain up to the parent class */
191 	G_OBJECT_CLASS (gdata_gd_reminder_parent_class)->finalize (object);
192 }
193 
194 static void
gdata_gd_reminder_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)195 gdata_gd_reminder_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
196 {
197 	GDataGDReminderPrivate *priv = GDATA_GD_REMINDER (object)->priv;
198 
199 	switch (property_id) {
200 		case PROP_METHOD:
201 			g_value_set_string (value, priv->method);
202 			break;
203 		case PROP_ABSOLUTE_TIME:
204 			g_value_set_int64 (value, priv->absolute_time);
205 			break;
206 		case PROP_IS_ABSOLUTE_TIME:
207 			g_value_set_boolean (value, gdata_gd_reminder_is_absolute_time (GDATA_GD_REMINDER (object)));
208 			break;
209 		case PROP_RELATIVE_TIME:
210 			g_value_set_int (value, priv->relative_time);
211 			break;
212 		default:
213 			/* We don't have any other property... */
214 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
215 			break;
216 	}
217 }
218 
219 static void
gdata_gd_reminder_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)220 gdata_gd_reminder_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
221 {
222 	GDataGDReminder *self = GDATA_GD_REMINDER (object);
223 
224 	switch (property_id) {
225 		case PROP_METHOD:
226 			gdata_gd_reminder_set_method (self, g_value_get_string (value));
227 			break;
228 		case PROP_ABSOLUTE_TIME:
229 			gdata_gd_reminder_set_absolute_time (self, g_value_get_int64 (value));
230 			break;
231 		case PROP_RELATIVE_TIME:
232 			gdata_gd_reminder_set_relative_time (self, g_value_get_int (value));
233 			break;
234 		default:
235 			/* We don't have any other property... */
236 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
237 			break;
238 	}
239 }
240 
241 static gboolean
pre_parse_xml(GDataParsable * parsable,xmlDoc * doc,xmlNode * root_node,gpointer user_data,GError ** error)242 pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error)
243 {
244 	GDataGDReminderPrivate *priv = GDATA_GD_REMINDER (parsable)->priv;
245 	xmlChar *absolute_time, *relative_time;
246 	gint64 absolute_time_int64;
247 	gint relative_time_int = -1;
248 	gboolean is_absolute_time = FALSE;
249 
250 	/* Absolute time */
251 	absolute_time = xmlGetProp (root_node, (xmlChar*) "absoluteTime");
252 	if (absolute_time != NULL) {
253 		is_absolute_time = TRUE;
254 		if (gdata_parser_int64_from_iso8601 ((gchar*) absolute_time, &absolute_time_int64) == FALSE) {
255 			/* Error */
256 			gdata_parser_error_not_iso8601_format (root_node, (gchar*) absolute_time, error);
257 			xmlFree (absolute_time);
258 			return FALSE;
259 		}
260 		xmlFree (absolute_time);
261 	}
262 
263 	/* Relative time */
264 	relative_time = xmlGetProp (root_node, (xmlChar*) "days");
265 	if (relative_time != NULL) {
266 		relative_time_int = g_ascii_strtoll ((gchar*) relative_time, NULL, 10) * 60 * 24;
267 	} else {
268 		relative_time = xmlGetProp (root_node, (xmlChar*) "hours");
269 		if (relative_time != NULL) {
270 			relative_time_int = g_ascii_strtoll ((gchar*) relative_time, NULL, 10) * 60;
271 		} else {
272 			relative_time = xmlGetProp (root_node, (xmlChar*) "minutes");
273 			if (relative_time != NULL)
274 				relative_time_int = g_ascii_strtoll ((gchar*) relative_time, NULL, 10);
275 		}
276 	}
277 	xmlFree (relative_time);
278 
279 	if (is_absolute_time == TRUE) {
280 		priv->absolute_time = absolute_time_int64;
281 		priv->relative_time = -1;
282 	} else {
283 		priv->absolute_time = -1;
284 		priv->relative_time = relative_time_int;
285 	}
286 
287 	priv->method = (gchar*) xmlGetProp (root_node, (xmlChar*) "method");
288 
289 	return TRUE;
290 }
291 
292 static void
pre_get_xml(GDataParsable * parsable,GString * xml_string)293 pre_get_xml (GDataParsable *parsable, GString *xml_string)
294 {
295 	GDataGDReminderPrivate *priv = GDATA_GD_REMINDER (parsable)->priv;
296 
297 	if (priv->relative_time == -1) {
298 		gchar *absolute_time = gdata_parser_int64_to_iso8601 (priv->absolute_time);
299 		g_string_append_printf (xml_string, " absoluteTime='%s'", absolute_time);
300 		g_free (absolute_time);
301 	} else {
302 		g_string_append_printf (xml_string, " minutes='%i'", priv->relative_time);
303 	}
304 
305 	if (priv->method != NULL)
306 		gdata_parser_string_append_escaped (xml_string, " method='", priv->method, "'");
307 }
308 
309 static void
get_namespaces(GDataParsable * parsable,GHashTable * namespaces)310 get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
311 {
312 	g_hash_table_insert (namespaces, (gchar*) "gd", (gchar*) "http://schemas.google.com/g/2005");
313 }
314 
315 /**
316  * gdata_gd_reminder_new:
317  * @method: (allow-none): the notification method the reminder should use, or %NULL
318  * @absolute_time: the absolute time for the reminder, or <code class="literal">-1</code>
319  * @relative_time: the relative time for the reminder, in minutes, or <code class="literal">-1</code>
320  *
321  * Creates a new #GDataGDReminder. More information is available in the <ulink type="http"
322  * url="http://code.google.com/apis/gdata/docs/2.0/elements.html#gdReminder">GData specification</ulink>.
323  *
324  * Return value: a new #GDataGDReminder, or %NULL; unref with g_object_unref()
325  *
326  * Since: 0.2.0
327  */
328 GDataGDReminder *
gdata_gd_reminder_new(const gchar * method,gint64 absolute_time,gint relative_time)329 gdata_gd_reminder_new (const gchar *method, gint64 absolute_time, gint relative_time)
330 {
331 	g_return_val_if_fail (absolute_time == -1 || relative_time == -1, NULL);
332 	g_return_val_if_fail (absolute_time >= -1, NULL);
333 	g_return_val_if_fail (relative_time >= -1, NULL);
334 	return g_object_new (GDATA_TYPE_GD_REMINDER, "absolute-time", absolute_time, "relative-time", relative_time, "method", method, NULL);
335 }
336 
337 /**
338  * gdata_gd_reminder_get_method:
339  * @self: a #GDataGDReminder
340  *
341  * Gets the #GDataGDReminder:method property.
342  *
343  * Return value: the method, or %NULL
344  *
345  * Since: 0.4.0
346  */
347 const gchar *
gdata_gd_reminder_get_method(GDataGDReminder * self)348 gdata_gd_reminder_get_method (GDataGDReminder *self)
349 {
350 	g_return_val_if_fail (GDATA_IS_GD_REMINDER (self), NULL);
351 	return self->priv->method;
352 }
353 
354 /**
355  * gdata_gd_reminder_set_method:
356  * @self: a #GDataGDReminder
357  * @method: (allow-none): the new method, or %NULL
358  *
359  * Sets the #GDataGDReminder:method property to @method.
360  *
361  * Set @method to %NULL to unset the property.
362  *
363  * Since: 0.4.0
364  */
365 void
gdata_gd_reminder_set_method(GDataGDReminder * self,const gchar * method)366 gdata_gd_reminder_set_method (GDataGDReminder *self, const gchar *method)
367 {
368 	g_return_if_fail (GDATA_IS_GD_REMINDER (self));
369 
370 	g_free (self->priv->method);
371 	self->priv->method = g_strdup (method);
372 	g_object_notify (G_OBJECT (self), "method");
373 }
374 
375 /**
376  * gdata_gd_reminder_get_absolute_time:
377  * @self: a #GDataGDReminder
378  *
379  * Gets the #GDataGDReminder:absolute-time property. If the property is unset, <code class="literal">-1</code> will be returned.
380  *
381  * Return value: the UNIX timestamp of the absolute time for the reminder, or <code class="literal">-1</code>
382  *
383  * Since: 0.4.0
384  */
385 gint64
gdata_gd_reminder_get_absolute_time(GDataGDReminder * self)386 gdata_gd_reminder_get_absolute_time (GDataGDReminder *self)
387 {
388 	g_return_val_if_fail (GDATA_IS_GD_REMINDER (self), -1);
389 	return self->priv->absolute_time;
390 }
391 
392 /**
393  * gdata_gd_reminder_set_absolute_time:
394  * @self: a #GDataGDReminder
395  * @absolute_time: the new absolute time, or <code class="literal">-1</code>
396  *
397  * Sets the #GDataGDReminder:absolute-time property to @absolute_time.
398  *
399  * Set @absolute_time to <code class="literal">-1</code> to unset the property.
400  *
401  * Since: 0.4.0
402  */
403 void
gdata_gd_reminder_set_absolute_time(GDataGDReminder * self,gint64 absolute_time)404 gdata_gd_reminder_set_absolute_time (GDataGDReminder *self, gint64 absolute_time)
405 {
406 	g_return_if_fail (GDATA_IS_GD_REMINDER (self));
407 	g_return_if_fail (absolute_time >= -1);
408 
409 	self->priv->absolute_time = absolute_time;
410 	g_object_notify (G_OBJECT (self), "absolute-time");
411 }
412 
413 /**
414  * gdata_gd_reminder_is_absolute_time:
415  * @self: a #GDataGDReminder
416  *
417  * Returns whether the reminder is specified as an absolute time, or as a number of minutes after
418  * the corresponding event's start time.
419  *
420  * Return value: %TRUE if the reminder is absolute, %FALSE otherwise
421  *
422  * Since: 0.4.0
423  */
424 gboolean
gdata_gd_reminder_is_absolute_time(GDataGDReminder * self)425 gdata_gd_reminder_is_absolute_time (GDataGDReminder *self)
426 {
427 	g_return_val_if_fail (GDATA_IS_GD_REMINDER (self), FALSE);
428 	return (self->priv->relative_time == -1) ? TRUE : FALSE;
429 }
430 
431 /**
432  * gdata_gd_reminder_get_relative_time:
433  * @self: a #GDataGDReminder
434  *
435  * Gets the #GDataGDReminder:relative-time property.
436  *
437  * Return value: the relative time, or <code class="literal">-1</code>
438  *
439  * Since: 0.4.0
440  */
441 gint
gdata_gd_reminder_get_relative_time(GDataGDReminder * self)442 gdata_gd_reminder_get_relative_time (GDataGDReminder *self)
443 {
444 	g_return_val_if_fail (GDATA_IS_GD_REMINDER (self), -1);
445 	return self->priv->relative_time;
446 }
447 
448 /**
449  * gdata_gd_reminder_set_relative_time:
450  * @self: a #GDataGDReminder
451  * @relative_time: the new relative time, or <code class="literal">-1</code>
452  *
453  * Sets the #GDataGDReminder:relative-time property to @relative_time.
454  *
455  * Set @relative_time to <code class="literal">-1</code> to unset the property.
456  *
457  * Since: 0.4.0
458  */
459 void
gdata_gd_reminder_set_relative_time(GDataGDReminder * self,gint relative_time)460 gdata_gd_reminder_set_relative_time (GDataGDReminder *self, gint relative_time)
461 {
462 	g_return_if_fail (GDATA_IS_GD_REMINDER (self));
463 	g_return_if_fail (relative_time >= -1);
464 
465 	self->priv->relative_time = relative_time;
466 	g_object_notify (G_OBJECT (self), "method");
467 }
468