aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am6
-rw-r--r--src/idoappointmentmenuitem.c483
-rw-r--r--src/idoappointmentmenuitem.h79
-rw-r--r--src/idocalendarmenuitem.c177
-rw-r--r--src/idocalendarmenuitem.h7
-rw-r--r--src/idolocationmenuitem.c477
-rw-r--r--src/idolocationmenuitem.h73
-rw-r--r--src/idomenuitemfactory.c14
8 files changed, 1310 insertions, 6 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 8b44a09..2791964 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,6 +18,8 @@ sources_h = \
idoscalemenuitem.h \
idoswitchmenuitem.h \
idousermenuitem.h \
+ idoappointmentmenuitem.h \
+ idolocationmenuitem.h \
idotimeline.h \
libido.h \
idoactionhelper.h \
@@ -76,7 +78,9 @@ libido_0_1_la_SOURCES = \
idoactionhelper.c \
idousermenuitem.c \
idomediaplayermenuitem.c \
- idoplaybackmenuitem.c
+ idoplaybackmenuitem.c \
+ idoappointmentmenuitem.c \
+ idolocationmenuitem.c
libido3_0_1_la_SOURCES = $(libido_0_1_la_SOURCES)
diff --git a/src/idoappointmentmenuitem.c b/src/idoappointmentmenuitem.c
new file mode 100644
index 0000000..2ac518a
--- /dev/null
+++ b/src/idoappointmentmenuitem.c
@@ -0,0 +1,483 @@
+/**
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ * Ted Gould <ted@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#include <string.h> /* strstr() */
+
+#include <gtk/gtk.h>
+
+#include "idoactionhelper.h"
+#include "idoappointmentmenuitem.h"
+
+enum
+{
+ PROP_0,
+ PROP_COLOR,
+ PROP_SUMMARY,
+ PROP_TIME,
+ PROP_FORMAT,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+struct _IdoAppointmentMenuItemPrivate
+{
+ char * summary;
+ char * format;
+ char * color_string;
+ GDateTime * date_time;
+
+ GtkWidget * color_image;
+ GtkWidget * summary_label;
+ GtkWidget * timestamp_label;
+};
+
+typedef IdoAppointmentMenuItemPrivate priv_t;
+
+G_DEFINE_TYPE (IdoAppointmentMenuItem,
+ ido_appointment_menu_item,
+ GTK_TYPE_MENU_ITEM);
+
+/***
+**** GObject Virtual Functions
+***/
+
+static void
+my_get_property (GObject * o,
+ guint property_id,
+ GValue * v,
+ GParamSpec * pspec)
+{
+ IdoAppointmentMenuItem * self = IDO_APPOINTMENT_MENU_ITEM (o);
+ priv_t * p = self->priv;
+
+ switch (property_id)
+ {
+ case PROP_COLOR:
+ g_value_set_string (v, p->color_string);
+ break;
+
+ case PROP_SUMMARY:
+ g_value_set_string (v, p->summary);
+ break;
+
+ case PROP_FORMAT:
+ g_value_set_string (v, p->format);
+ break;
+
+ case PROP_TIME:
+ g_value_set_uint64 (v, g_date_time_to_unix (p->date_time));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
+ break;
+ }
+}
+
+static void
+my_set_property (GObject * o,
+ guint property_id,
+ const GValue * v,
+ GParamSpec * pspec)
+{
+ IdoAppointmentMenuItem * self = IDO_APPOINTMENT_MENU_ITEM (o);
+
+ switch (property_id)
+ {
+ case PROP_COLOR:
+ ido_appointment_menu_item_set_color (self, g_value_get_string (v));
+ break;
+
+ case PROP_SUMMARY:
+ ido_appointment_menu_item_set_summary (self, g_value_get_string (v));
+ break;
+
+ case PROP_FORMAT:
+ ido_appointment_menu_item_set_format (self, g_value_get_string (v));
+ break;
+
+ case PROP_TIME:
+ ido_appointment_menu_item_set_time (self, g_value_get_int64 (v));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
+ break;
+ }
+}
+
+static void
+my_dispose (GObject * object)
+{
+ IdoAppointmentMenuItem * self = IDO_APPOINTMENT_MENU_ITEM (object);
+ priv_t * p = self->priv;
+
+ g_clear_pointer (&p->date_time, g_date_time_unref);
+
+ G_OBJECT_CLASS (ido_appointment_menu_item_parent_class)->dispose (object);
+}
+
+static void
+my_finalize (GObject * object)
+{
+ IdoAppointmentMenuItem * self = IDO_APPOINTMENT_MENU_ITEM (object);
+ priv_t * p = self->priv;
+
+ g_free (p->color_string);
+ g_free (p->summary);
+ g_free (p->format);
+
+ G_OBJECT_CLASS (ido_appointment_menu_item_parent_class)->finalize (object);
+}
+
+/***
+**** Instantiation
+***/
+
+static void
+ido_appointment_menu_item_class_init (IdoAppointmentMenuItemClass *klass)
+{
+ GParamFlags prop_flags;
+ GObjectClass * gobject_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (IdoAppointmentMenuItemPrivate));
+
+ gobject_class->get_property = my_get_property;
+ gobject_class->set_property = my_set_property;
+ gobject_class->dispose = my_dispose;
+ gobject_class->finalize = my_finalize;
+
+ prop_flags = G_PARAM_CONSTRUCT
+ | G_PARAM_READWRITE
+ | G_PARAM_STATIC_STRINGS;
+
+ properties[PROP_COLOR] = g_param_spec_string (
+ "color",
+ "Color",
+ "Color coding for the appointment's type",
+ "White",
+ prop_flags);
+
+ properties[PROP_SUMMARY] = g_param_spec_string (
+ "summary",
+ "Summary",
+ "Brief description of the appointment",
+ "",
+ prop_flags);
+
+ properties[PROP_TIME] = g_param_spec_int64 (
+ "time",
+ "Time",
+ "unix time_t specifying when the appointment begins",
+ 0, G_MAXINT64, 0,
+ prop_flags);
+
+ properties[PROP_FORMAT] = g_param_spec_string (
+ "format",
+ "strftime format",
+ "strftime-style format string for the timestamp",
+ "%F %T",
+ prop_flags);
+
+ g_object_class_install_properties (gobject_class, PROP_LAST, properties);
+}
+
+static void
+ido_appointment_menu_item_init (IdoAppointmentMenuItem *self)
+{
+ priv_t * p;
+ GtkBox * box;
+ GtkWidget * w;
+
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ IDO_APPOINTMENT_MENU_ITEM_TYPE,
+ IdoAppointmentMenuItemPrivate);
+
+ p = self->priv;
+
+ p->color_image = gtk_image_new ();
+ p->summary_label = gtk_label_new (NULL);
+ p->timestamp_label = gtk_label_new (NULL);
+ w = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
+
+ gtk_misc_set_alignment (GTK_MISC(p->timestamp_label), 1.0, 0.5);
+ box = GTK_BOX (w);
+ gtk_box_pack_start (box, p->color_image, FALSE, FALSE, 2);
+ gtk_box_pack_start (box, p->summary_label, FALSE, FALSE, 2);
+ gtk_box_pack_end (box, p->timestamp_label, FALSE, FALSE, 5);
+
+ gtk_widget_show_all (w);
+ gtk_container_add (GTK_CONTAINER (self), w);
+}
+
+/***
+****
+***/
+
+/* creates a menu-sized pixbuf filled with specified color */
+static GdkPixbuf *
+create_color_icon_pixbuf (const char * color_spec)
+{
+ static int width = -1;
+ static int height = -1;
+ GdkPixbuf * pixbuf = NULL;
+
+ if (width == -1)
+ {
+ gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, &height);
+ width = CLAMP (width, 10, 30);
+ height = CLAMP (height, 10, 30);
+ }
+
+ if (color_spec && *color_spec)
+ {
+ cairo_surface_t * surface;
+ cairo_t * cr;
+ GdkRGBA rgba;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+ cr = cairo_create (surface);
+
+ if (gdk_rgba_parse (&rgba, color_spec))
+ gdk_cairo_set_source_rgba (cr, &rgba);
+
+ cairo_paint (cr);
+ cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
+ cairo_set_line_width (cr, 1);
+ cairo_rectangle (cr, 0.5, 0.5, width-1, height-1);
+ cairo_stroke (cr);
+
+ pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, width, height);
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+ }
+
+ return pixbuf;
+}
+
+static void
+update_timestamp_label (IdoAppointmentMenuItem * self)
+{
+ char * str;
+ priv_t * p = self->priv;
+
+ if (p->date_time && p->format)
+ str = g_date_time_format (p->date_time, p->format);
+ else
+ str = NULL;
+
+ gtk_label_set_text (GTK_LABEL(p->timestamp_label), str);
+ g_free (str);
+}
+
+/***
+**** Public API
+***/
+
+/* create a new IdoAppointmentMenuItem */
+GtkWidget *
+ido_appointment_menu_item_new (void)
+{
+ return GTK_WIDGET (g_object_new (IDO_APPOINTMENT_MENU_ITEM_TYPE, NULL));
+}
+
+/**
+ * ido_appointment_menu_item_set_color:
+ * @color: parseable color string
+ *
+ * When this is set, the menuitem will include an icon with this color.
+ *
+ * These colors can be set in the end user's calendar app as a quick visual cue
+ * to show what kind of appointment this is.
+ */
+void
+ido_appointment_menu_item_set_color (IdoAppointmentMenuItem * self,
+ const char * color_string)
+{
+ priv_t * p;
+ GdkPixbuf * pixbuf;
+
+ g_return_if_fail (IDO_IS_APPOINTMENT_MENU_ITEM (self));
+ p = self->priv;
+
+ g_free (p->color_string);
+ p->color_string = g_strdup (color_string);
+ pixbuf = create_color_icon_pixbuf (p->color_string);
+ gtk_image_set_from_pixbuf (GTK_IMAGE(p->color_image), pixbuf);
+ g_object_unref (G_OBJECT(pixbuf));
+}
+
+/**
+ * ido_appointment_menu_item_set_summary:
+ * @summary: short string describing the appointment.
+ *
+ * Set the menuitem's primary label with a short description of the appointment
+ */
+void
+ido_appointment_menu_item_set_summary (IdoAppointmentMenuItem * self,
+ const char * summary)
+{
+ priv_t * p;
+
+ g_return_if_fail (IDO_IS_APPOINTMENT_MENU_ITEM (self));
+ p = self->priv;
+
+ g_free (p->summary);
+ p->summary = g_strdup (summary);
+ gtk_label_set_text (GTK_LABEL(p->summary_label), p->summary);
+}
+
+/**
+ * ido_appointment_menu_item_set_time:
+ * @time: the time to be rendered in the appointment's timestamp label.
+ *
+ * Set the time that will be displayed in the menuitem's
+ * right-justified timestamp label
+ */
+void
+ido_appointment_menu_item_set_time (IdoAppointmentMenuItem * self,
+ time_t time)
+{
+ priv_t * p;
+
+ g_return_if_fail (IDO_IS_APPOINTMENT_MENU_ITEM (self));
+ p = self->priv;
+
+ g_clear_pointer (&p->date_time, g_date_time_unref);
+ p->date_time = g_date_time_new_from_unix_local (time);
+ update_timestamp_label (self);
+}
+
+/**
+ * ido_appointment_menu_item_set_format:
+ * @format: the format string used when showing the appointment's time
+ *
+ * Set the format string for rendering the appointment's time
+ * in its right-justified secondary label.
+ *
+ * See strfrtime(3) for more information on the format string.
+ */
+void
+ido_appointment_menu_item_set_format (IdoAppointmentMenuItem * self,
+ const char * strftime_fmt)
+{
+ priv_t * p;
+
+ g_return_if_fail (IDO_IS_APPOINTMENT_MENU_ITEM (self));
+ p = self->priv;
+
+ g_free (p->format);
+ p->format = g_strdup (strftime_fmt);
+ update_timestamp_label (self);
+}
+
+/**
+ * ido_appointment_menu_item_new_from_model:
+ * @menu_item: the corresponding menuitem
+ * @actions: action group to tell when this GtkMenuItem is activated
+ *
+ * Creates a new IdoAppointmentMenuItem with properties initialized from
+ * the menuitem's attributes.
+ *
+ * If the menuitem's 'action' attribute is set, trigger that action
+ * in @actions when this IdoAppointmentMenuItem is activated.
+ */
+GtkMenuItem *
+ido_appointment_menu_item_new_from_model (GMenuItem * menu_item,
+ GActionGroup * actions)
+{
+ guint i;
+ guint n;
+ gint64 i64;
+ gchar * str;
+ IdoAppointmentMenuItem * ido_appointment;
+ GParameter parameters[8];
+
+ /* create the ido_appointment */
+
+ n = 0;
+
+ if (g_menu_item_get_attribute (menu_item, "label", "s", &str))
+ {
+ GParameter p = { "summary", G_VALUE_INIT };
+ g_value_init (&p.value, G_TYPE_STRING);
+ g_value_take_string (&p.value, str);
+ parameters[n++] = p;
+ }
+
+ if (g_menu_item_get_attribute (menu_item, "x-canonical-color", "s", &str))
+ {
+ GParameter p = { "color", G_VALUE_INIT };
+ g_value_init (&p.value, G_TYPE_STRING);
+ g_value_take_string (&p.value, str);
+ parameters[n++] = p;
+ }
+
+ if (g_menu_item_get_attribute (menu_item, "x-canonical-time-format", "s", &str))
+ {
+ GParameter p = { "format", G_VALUE_INIT };
+ g_value_init (&p.value, G_TYPE_STRING);
+ g_value_take_string (&p.value, str);
+ parameters[n++] = p;
+ }
+
+ if (g_menu_item_get_attribute (menu_item, "x-canonical-time", "x", &i64))
+ {
+ GParameter p = { "time", G_VALUE_INIT };
+ g_value_init (&p.value, G_TYPE_INT64);
+ g_value_set_int64 (&p.value, i64);
+ parameters[n++] = p;
+ }
+
+ g_assert (n <= G_N_ELEMENTS (parameters));
+ ido_appointment = g_object_newv (IDO_APPOINTMENT_MENU_ITEM_TYPE, n, parameters);
+
+ for (i=0; i<n; i++)
+ g_value_unset (&parameters[i].value);
+
+
+ /* add an ActionHelper */
+
+ if (g_menu_item_get_attribute (menu_item, "action", "s", &str))
+ {
+ GVariant * target;
+ IdoActionHelper * helper;
+
+ target = g_menu_item_get_attribute_value (menu_item, "target",
+ G_VARIANT_TYPE_ANY);
+ helper = ido_action_helper_new (GTK_WIDGET(ido_appointment), actions,
+ str, target);
+ g_signal_connect_swapped (ido_appointment, "activate",
+ G_CALLBACK (ido_action_helper_activate), helper);
+ g_signal_connect_swapped (ido_appointment, "destroy",
+ G_CALLBACK (g_object_unref), helper);
+
+ g_clear_pointer (&target, g_variant_unref);
+ g_free (str);
+ }
+
+ return GTK_MENU_ITEM (ido_appointment);
+}
diff --git a/src/idoappointmentmenuitem.h b/src/idoappointmentmenuitem.h
new file mode 100644
index 0000000..3a8c853
--- /dev/null
+++ b/src/idoappointmentmenuitem.h
@@ -0,0 +1,79 @@
+/**
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __IDO_APPOINTMENT_MENU_ITEM_H__
+#define __IDO_APPOINTMENT_MENU_ITEM_H__
+
+#include <time.h> /* time_t */
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define IDO_APPOINTMENT_MENU_ITEM_TYPE (ido_appointment_menu_item_get_type ())
+#define IDO_APPOINTMENT_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDO_APPOINTMENT_MENU_ITEM_TYPE, IdoAppointmentMenuItem))
+#define IDO_IS_APPOINTMENT_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDO_APPOINTMENT_MENU_ITEM_TYPE))
+
+typedef struct _IdoAppointmentMenuItem IdoAppointmentMenuItem;
+typedef struct _IdoAppointmentMenuItemClass IdoAppointmentMenuItemClass;
+typedef struct _IdoAppointmentMenuItemPrivate IdoAppointmentMenuItemPrivate;
+
+struct _IdoAppointmentMenuItemClass
+{
+ GtkMenuItemClass parent_class;
+};
+
+/**
+ * A menuitem that indicates a appointment.
+ *
+ * It contains a color-coded icon to indicate the appointment type,
+ * a primary label showing the appointment summary,
+ * and a right-justified secondary label telling when the appointment begins.
+ */
+struct _IdoAppointmentMenuItem
+{
+ /*< private >*/
+ GtkMenuItem parent;
+ IdoAppointmentMenuItemPrivate * priv;
+};
+
+
+GType ido_appointment_menu_item_get_type (void) G_GNUC_CONST;
+
+GtkWidget * ido_appointment_menu_item_new (void);
+
+GtkMenuItem * ido_appointment_menu_item_new_from_model (GMenuItem * menuitem,
+ GActionGroup * actions);
+
+void ido_appointment_menu_item_set_summary (IdoAppointmentMenuItem * menuitem,
+ const char * summary);
+
+void ido_appointment_menu_item_set_time (IdoAppointmentMenuItem * menuitem,
+ time_t time);
+
+void ido_appointment_menu_item_set_color (IdoAppointmentMenuItem * menuitem,
+ const char * color_str);
+
+void ido_appointment_menu_item_set_format (IdoAppointmentMenuItem * menuitem,
+ const char * strftime_fmt);
+
+
+G_END_DECLS
+
+#endif
diff --git a/src/idocalendarmenuitem.c b/src/idocalendarmenuitem.c
index a818c13..94f4f61 100644
--- a/src/idocalendarmenuitem.c
+++ b/src/idocalendarmenuitem.c
@@ -24,6 +24,7 @@
*/
#include <gdk/gdkkeysyms.h>
+#include "idoactionhelper.h"
#include "idocalendarmenuitem.h"
#include "config.h"
@@ -470,8 +471,178 @@ ido_calendar_menu_item_set_date (IdoCalendarMenuItem *menuitem,
guint month,
guint day)
{
- g_return_val_if_fail(IDO_IS_CALENDAR_MENU_ITEM(menuitem), FALSE);
- gtk_calendar_select_month (GTK_CALENDAR (menuitem->priv->calendar), month, year);
- gtk_calendar_select_day (GTK_CALENDAR (menuitem->priv->calendar), day);
+ guint old_y, old_m, old_d;
+
+ g_return_val_if_fail (IDO_IS_CALENDAR_MENU_ITEM(menuitem), FALSE);
+
+ ido_calendar_menu_item_get_date (menuitem, &old_y, &old_m, &old_d);
+
+ if ((old_y != year) || (old_m != month))
+ gtk_calendar_select_month (GTK_CALENDAR (menuitem->priv->calendar), month, year);
+
+ if (old_d != day)
+ gtk_calendar_select_day (GTK_CALENDAR (menuitem->priv->calendar), day);
+
return TRUE;
}
+
+/***
+****
+****
+****
+***/
+
+static void
+activate_current_day (IdoCalendarMenuItem * ido_calendar,
+ const char * action_name_key)
+{
+ GObject * o;
+ const char * action_name;
+ GActionGroup * action_group;
+
+ o = G_OBJECT (ido_calendar);
+ action_name = g_object_get_data (o, action_name_key);
+ action_group = g_object_get_data (o, "ido-action-group");
+
+ if (action_group && action_name)
+ {
+ guint y, m, d;
+ GDateTime * date_time;
+ GVariant * target;
+
+ ido_calendar_menu_item_get_date (ido_calendar, &y, &m, &d);
+ m++; /* adjust month from GtkCalendar (0 based) to GDateTime (1 based) */
+ date_time = g_date_time_new_local (y, m, d, 9, 0, 0);
+ target = g_variant_new_int64 (g_date_time_to_unix (date_time));
+
+ g_action_group_activate_action (action_group, action_name, target);
+
+ g_date_time_unref (date_time);
+ }
+}
+
+static void
+on_day_selected (IdoCalendarMenuItem * ido_calendar)
+{
+ activate_current_day (ido_calendar, "ido-selection-action-name");
+}
+
+static void
+on_day_double_clicked (IdoCalendarMenuItem * ido_calendar)
+{
+ activate_current_day (ido_calendar, "ido-activation-action-name");
+}
+
+static void
+on_action_state_changed (IdoActionHelper * helper,
+ GVariant * state,
+ gpointer unused G_GNUC_UNUSED)
+{
+ GVariant * v;
+ const char * key;
+ IdoCalendarMenuItem * ido_calendar;
+
+ ido_calendar = IDO_CALENDAR_MENU_ITEM (ido_action_helper_get_widget (helper));
+
+ g_return_if_fail (ido_calendar != NULL);
+ g_return_if_fail (g_variant_is_of_type (state, G_VARIANT_TYPE_DICTIONARY));
+
+ /* an int64 representing a time_t indicating which year and month should
+ be visible in the calendar and which day should be given the cursor. */
+ key = "calendar-day";
+ if ((v = g_variant_lookup_value (state, key, G_VARIANT_TYPE_INT64)))
+ {
+ int y, m, d;
+ time_t t;
+ GDateTime * date_time;
+
+ t = g_variant_get_int64 (v);
+ date_time = g_date_time_new_from_unix_local (t);
+ g_date_time_get_ymd (date_time, &y, &m, &d);
+ m--; /* adjust month from GDateTime (1 based) to GtkCalendar (0 based) */
+ ido_calendar_menu_item_set_date (ido_calendar, y, m, d);
+
+ g_date_time_unref (date_time);
+ g_variant_unref (v);
+ }
+
+ /* a boolean value of whether or not to show the week numbers */
+ key = "show-week-numbers";
+ if ((v = g_variant_lookup_value (state, key, G_VARIANT_TYPE_BOOLEAN)))
+ {
+ const GtkCalendarDisplayOptions old_flags = ido_calendar_menu_item_get_display_options (ido_calendar);
+ GtkCalendarDisplayOptions new_flags = old_flags;
+
+ if (g_variant_get_boolean (v))
+ new_flags |= GTK_CALENDAR_SHOW_WEEK_NUMBERS;
+ else
+ new_flags &= ~GTK_CALENDAR_SHOW_WEEK_NUMBERS;
+
+ if (new_flags != old_flags)
+ ido_calendar_menu_item_set_display_options (ido_calendar, new_flags);
+
+ g_variant_unref (v);
+ }
+
+ /* an array of int32 day-of-months denoting days that have appointments */
+ key = "appointment-days";
+ ido_calendar_menu_item_clear_marks (ido_calendar);
+ if ((v = g_variant_lookup_value (state, key, G_VARIANT_TYPE("ai"))))
+ {
+ gint32 day;
+ GVariantIter iter;
+
+ g_variant_iter_init (&iter, v);
+ while (g_variant_iter_next (&iter, "i", &day))
+ ido_calendar_menu_item_mark_day (ido_calendar, day);
+
+ g_variant_unref (v);
+ }
+}
+
+GtkMenuItem *
+ido_calendar_menu_item_new_from_model (GMenuItem * menu_item,
+ GActionGroup * actions)
+{
+ GObject * o;
+ GtkWidget * calendar;
+ IdoCalendarMenuItem * ido_calendar;
+ gchar * selection_action_name;
+ gchar * activation_action_name;
+
+ /* get the select & activate action names */
+ g_menu_item_get_attribute (menu_item, "action", "s", &selection_action_name);
+ g_menu_item_get_attribute (menu_item, "activation-action", "s", &activation_action_name);
+
+ /* remember the action group & action names so that we can poke them
+ when user selects and double-clicks */
+ ido_calendar = IDO_CALENDAR_MENU_ITEM (ido_calendar_menu_item_new ());
+ o = G_OBJECT (ido_calendar);
+ g_object_set_data_full (o, "ido-action-group", g_object_ref(actions), g_object_unref);
+ g_object_set_data_full (o, "ido-selection-action-name", selection_action_name, g_free);
+ g_object_set_data_full (o, "ido-activation-action-name", activation_action_name, g_free);
+ calendar = ido_calendar_menu_item_get_calendar (ido_calendar);
+ g_signal_connect_swapped (calendar, "day-selected",
+ G_CALLBACK(on_day_selected), ido_calendar);
+ g_signal_connect_swapped (calendar, "day-selected-double-click",
+ G_CALLBACK(on_day_double_clicked), ido_calendar);
+
+ /* Use an IdoActionHelper for state updates.
+ Since we have two separate actions for selection & activation,
+ we'll do the activation & targets logic here in ido-calendar */
+ if (selection_action_name != NULL)
+ {
+ IdoActionHelper * helper;
+
+ helper = ido_action_helper_new (GTK_WIDGET(ido_calendar),
+ actions,
+ selection_action_name,
+ NULL);
+ g_signal_connect (helper, "action-state-changed",
+ G_CALLBACK (on_action_state_changed), NULL);
+ g_signal_connect_swapped (ido_calendar, "destroy",
+ G_CALLBACK (g_object_unref), helper);
+ }
+
+ return GTK_MENU_ITEM (ido_calendar);
+}
diff --git a/src/idocalendarmenuitem.h b/src/idocalendarmenuitem.h
index c4833fb..5cd913e 100644
--- a/src/idocalendarmenuitem.h
+++ b/src/idocalendarmenuitem.h
@@ -69,7 +69,12 @@ void ido_calendar_menu_item_get_date (IdoCalendarMenuItem *menu
gboolean ido_calendar_menu_item_set_date (IdoCalendarMenuItem *menuitem,
guint year,
guint month,
- guint day);
+ guint day);
+
+GtkMenuItem * ido_calendar_menu_item_new_from_model (GMenuItem * menuitem,
+ GActionGroup * actions);
+
+
G_END_DECLS
#endif /* __IDO_CALENDAR_MENU_ITEM_H__ */
diff --git a/src/idolocationmenuitem.c b/src/idolocationmenuitem.c
new file mode 100644
index 0000000..347c9e8
--- /dev/null
+++ b/src/idolocationmenuitem.c
@@ -0,0 +1,477 @@
+/**
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#include <string.h> /* strstr() */
+
+#include <gtk/gtk.h>
+
+#include "idoactionhelper.h"
+#include "idolocationmenuitem.h"
+
+enum
+{
+ PROP_0,
+ PROP_NAME,
+ PROP_TIMEZONE,
+ PROP_FORMAT,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+struct _IdoLocationMenuItemPrivate
+{
+ char * name;
+ char * timezone;
+ char * format;
+
+ guint timestamp_timer;
+
+ GtkWidget * name_label;
+ GtkWidget * timestamp_label;
+};
+
+typedef IdoLocationMenuItemPrivate priv_t;
+
+G_DEFINE_TYPE (IdoLocationMenuItem, ido_location_menu_item, GTK_TYPE_MENU_ITEM);
+
+/***
+**** Timestamp Label
+***/
+
+static void
+update_timestamp_label (IdoLocationMenuItem * self)
+{
+ priv_t * p = self->priv;
+
+ if (p->format && *p->format)
+ {
+ GTimeZone * tz;
+ GDateTime * now;
+ char * str;
+
+ tz = g_time_zone_new (p->timezone);
+ if (tz == NULL)
+ tz = g_time_zone_new_local ();
+ now = g_date_time_new_now (tz);
+ str = g_date_time_format (now, p->format);
+
+ gtk_label_set_text (GTK_LABEL(p->timestamp_label), str);
+
+ g_free (str);
+ g_date_time_unref (now);
+ g_time_zone_unref (tz);
+ }
+ else
+ {
+ gtk_label_set_text (GTK_LABEL(p->timestamp_label), "");
+ }
+}
+
+static void
+stop_timestamp_timer (IdoLocationMenuItem * self)
+{
+ priv_t * p = self->priv;
+
+ if (p->timestamp_timer != 0)
+ {
+ g_source_remove (p->timestamp_timer);
+ p->timestamp_timer = 0;
+ }
+}
+
+static void start_timestamp_timer (IdoLocationMenuItem * self);
+
+static gboolean
+on_timestamp_timer (gpointer gself)
+{
+ IdoLocationMenuItem * self = IDO_LOCATION_MENU_ITEM (gself);
+
+ update_timestamp_label (self);
+
+ start_timestamp_timer (self);
+ return G_SOURCE_REMOVE;
+}
+
+static guint
+calculate_seconds_until_next_minute (void)
+{
+ guint seconds;
+ GTimeSpan diff;
+ GDateTime * now;
+ GDateTime * next;
+ GDateTime * start_of_next;
+
+ now = g_date_time_new_now_local ();
+ next = g_date_time_add_minutes (now, 1);
+ start_of_next = g_date_time_new_local (g_date_time_get_year (next),
+ g_date_time_get_month (next),
+ g_date_time_get_day_of_month (next),
+ g_date_time_get_hour (next),
+ g_date_time_get_minute (next),
+ 1);
+
+ diff = g_date_time_difference (start_of_next, now);
+ seconds = (diff + (G_TIME_SPAN_SECOND - 1)) / G_TIME_SPAN_SECOND;
+
+ /* cleanup */
+ g_date_time_unref (start_of_next);
+ g_date_time_unref (next);
+ g_date_time_unref (now);
+
+ return seconds;
+}
+
+static void
+start_timestamp_timer (IdoLocationMenuItem * self)
+{
+ int interval_sec;
+ gboolean timestamp_shows_seconds;
+ priv_t * p = self->priv;
+ const char * const fmt = p->format;
+
+ stop_timestamp_timer (self);
+
+ timestamp_shows_seconds = fmt && (strstr(fmt,"%s") || strstr(fmt,"%S") ||
+ strstr(fmt,"%T") || strstr(fmt,"%X") ||
+ strstr(fmt,"%c"));
+
+ if (timestamp_shows_seconds)
+ interval_sec = 1;
+ else
+ interval_sec = calculate_seconds_until_next_minute();
+
+ p->timestamp_timer = g_timeout_add_seconds (interval_sec,
+ on_timestamp_timer,
+ self);
+}
+
+/***
+**** GObject Virtual Functions
+***/
+
+static void
+my_get_property (GObject * o,
+ guint property_id,
+ GValue * value,
+ GParamSpec * pspec)
+{
+ IdoLocationMenuItem * self = IDO_LOCATION_MENU_ITEM (o);
+ priv_t * p = self->priv;
+
+ switch (property_id)
+ {
+ case PROP_NAME:
+ g_value_set_string (value, p->name);
+ break;
+
+ case PROP_TIMEZONE:
+ g_value_set_string (value, p->timezone);
+ break;
+
+ case PROP_FORMAT:
+ g_value_set_string (value, p->format);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
+ break;
+ }
+}
+
+static void
+my_set_property (GObject * o,
+ guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ IdoLocationMenuItem * self = IDO_LOCATION_MENU_ITEM (o);
+
+ switch (property_id)
+ {
+ case PROP_NAME:
+ ido_location_menu_item_set_name (self, g_value_get_string (value));
+ break;
+
+ case PROP_TIMEZONE:
+ ido_location_menu_item_set_timezone (self, g_value_get_string (value));
+ break;
+
+ case PROP_FORMAT:
+ ido_location_menu_item_set_format (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
+ break;
+ }
+}
+
+static void
+my_dispose (GObject * object)
+{
+ stop_timestamp_timer (IDO_LOCATION_MENU_ITEM (object));
+
+ G_OBJECT_CLASS (ido_location_menu_item_parent_class)->dispose (object);
+}
+
+static void
+my_finalize (GObject * object)
+{
+ IdoLocationMenuItem * self = IDO_LOCATION_MENU_ITEM (object);
+ priv_t * p = self->priv;
+
+ g_free (p->format);
+ g_free (p->name);
+ g_free (p->timezone);
+
+ G_OBJECT_CLASS (ido_location_menu_item_parent_class)->finalize (object);
+}
+
+/***
+**** Instantiation
+***/
+
+static void
+ido_location_menu_item_class_init (IdoLocationMenuItemClass *klass)
+{
+ GParamFlags prop_flags;
+ GObjectClass * gobject_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (IdoLocationMenuItemPrivate));
+
+ gobject_class->get_property = my_get_property;
+ gobject_class->set_property = my_set_property;
+ gobject_class->dispose = my_dispose;
+ gobject_class->finalize = my_finalize;
+
+ prop_flags = G_PARAM_CONSTRUCT
+ | G_PARAM_READWRITE
+ | G_PARAM_STATIC_STRINGS;
+
+ properties[PROP_NAME] = g_param_spec_string (
+ "name",
+ "The location's name",
+ "The name to display; eg, 'Oklahoma City'",
+ "Location",
+ prop_flags);
+
+ properties[PROP_TIMEZONE] = g_param_spec_string (
+ "timezone",
+ "timezone identifier",
+ "string used to identify a timezone; eg, 'America/Chicago'",
+ NULL,
+ prop_flags);
+
+ properties[PROP_FORMAT] = g_param_spec_string (
+ "format",
+ "strftime format",
+ "strftime-style format string for the timestamp",
+ "%T",
+ prop_flags);
+
+ g_object_class_install_properties (gobject_class, PROP_LAST, properties);
+}
+
+static void
+ido_location_menu_item_init (IdoLocationMenuItem *self)
+{
+ priv_t * p;
+ GtkBox * box;
+ GtkWidget * w;
+
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ IDO_LOCATION_MENU_ITEM_TYPE,
+ IdoLocationMenuItemPrivate);
+
+ p = self->priv;
+
+ p->name_label = gtk_label_new (NULL);
+ p->timestamp_label = gtk_label_new (NULL);
+
+ w = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
+ gtk_misc_set_alignment (GTK_MISC(p->timestamp_label), 1.0, 0.5);
+ box = GTK_BOX (w);
+ gtk_box_pack_start (box, p->name_label, FALSE, FALSE, 3);
+ gtk_box_pack_end (box, p->timestamp_label, FALSE, FALSE, 5);
+
+ gtk_widget_show_all (w);
+ gtk_container_add (GTK_CONTAINER (self), w);
+}
+
+
+/***
+**** Public API
+***/
+
+/* create a new IdoLocationMenuItemType */
+GtkWidget *
+ido_location_menu_item_new (void)
+{
+ return GTK_WIDGET (g_object_new (IDO_LOCATION_MENU_ITEM_TYPE, NULL));
+}
+
+/**
+ * ido_location_menu_item_set_name:
+ * @name: human-readable name, such as a city (eg: "Oklahoma City")
+ *
+ * Sets this location's name, for display in the menuitem's primary label.
+ */
+void
+ido_location_menu_item_set_name (IdoLocationMenuItem * self,
+ const char * name)
+{
+ priv_t * p;
+
+ g_return_if_fail (IDO_IS_LOCATION_MENU_ITEM (self));
+ p = self->priv;
+
+ g_free (p->name);
+ p->name = g_strdup (name);
+ gtk_label_set_text (GTK_LABEL(p->name_label), p->name);
+}
+
+/**
+ * ido_location_menu_item_set_timezone:
+ * @timezone: timezone identifier (eg: "America/Chicago")
+ *
+ * Set this location's timezone. This will be used to show the location's
+ * current time in menuitem's right-justified secondary label.
+ */
+void
+ido_location_menu_item_set_timezone (IdoLocationMenuItem * self,
+ const char * timezone)
+{
+ priv_t * p;
+
+ g_return_if_fail (IDO_IS_LOCATION_MENU_ITEM (self));
+ p = self->priv;
+
+ g_free (p->timezone);
+ p->timezone = g_strdup (timezone);
+ update_timestamp_label (self);
+}
+
+/**
+ * ido_location_menu_item_set_format:
+ * @format: the format string used when showing the location's time
+ *
+ * Set the format string for rendering the location's time
+ * in its right-justified secondary label.
+ *
+ * See strfrtime(3) for more information on the format string.
+ */
+void
+ido_location_menu_item_set_format (IdoLocationMenuItem * self,
+ const char * format)
+{
+ priv_t * p;
+
+ g_return_if_fail (IDO_IS_LOCATION_MENU_ITEM (self));
+ p = self->priv;
+
+ g_free (p->format);
+ p->format = g_strdup (format);
+ update_timestamp_label (self);
+ start_timestamp_timer (self);
+}
+
+/**
+ * ido_location_menu_item_new_from_model:
+ * @menu_item: the corresponding menuitem
+ * @actions: action group to tell when this GtkMenuItem is activated
+ *
+ * Creates a new IdoLocationMenuItem with properties initialized from
+ * the menuitem's attributes.
+ *
+ * If the menuitem's 'action' attribute is set, trigger that action
+ * in @actions when this IdoLocationMenuItem is activated.
+ */
+GtkMenuItem *
+ido_location_menu_item_new_from_model (GMenuItem * menu_item,
+ GActionGroup * actions)
+{
+ guint i;
+ guint n;
+ gchar * str;
+ IdoLocationMenuItem * ido_location;
+ GParameter parameters[4];
+
+ /* create the ido_location */
+
+ n = 0;
+
+ if (g_menu_item_get_attribute (menu_item, "label", "s", &str))
+ {
+ GParameter p = { "name", G_VALUE_INIT };
+ g_value_init (&p.value, G_TYPE_STRING);
+ g_value_take_string (&p.value, str);
+ parameters[n++] = p;
+ }
+
+ if (g_menu_item_get_attribute (menu_item, "x-canonical-timezone", "s", &str))
+ {
+ GParameter p = { "timezone", G_VALUE_INIT };
+ g_value_init (&p.value, G_TYPE_STRING);
+ g_value_take_string (&p.value, str);
+ parameters[n++] = p;
+ }
+
+ if (g_menu_item_get_attribute (menu_item, "x-canonical-time-format", "s", &str))
+ {
+ GParameter p = { "format", G_VALUE_INIT };
+ g_value_init (&p.value, G_TYPE_STRING);
+ g_value_take_string (&p.value, str);
+ parameters[n++] = p;
+ }
+
+ g_assert (n <= G_N_ELEMENTS (parameters));
+ ido_location = g_object_newv (IDO_LOCATION_MENU_ITEM_TYPE, n, parameters);
+
+ for (i=0; i<n; i++)
+ g_value_unset (&parameters[i].value);
+
+
+ /* give it an ActionHelper */
+
+ if (g_menu_item_get_attribute (menu_item, "action", "s", &str))
+ {
+ GVariant * target;
+ IdoActionHelper * helper;
+
+ target = g_menu_item_get_attribute_value (menu_item, "target",
+ G_VARIANT_TYPE_ANY);
+ helper = ido_action_helper_new (GTK_WIDGET(ido_location), actions,
+ str, target);
+ g_signal_connect_swapped (ido_location, "activate",
+ G_CALLBACK (ido_action_helper_activate), helper);
+ g_signal_connect_swapped (ido_location, "destroy",
+ G_CALLBACK (g_object_unref), helper);
+
+ if (target)
+ g_variant_unref (target);
+ g_free (str);
+ }
+
+ return GTK_MENU_ITEM (ido_location);
+}
diff --git a/src/idolocationmenuitem.h b/src/idolocationmenuitem.h
new file mode 100644
index 0000000..08a97af
--- /dev/null
+++ b/src/idolocationmenuitem.h
@@ -0,0 +1,73 @@
+/**
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __IDO_LOCATION_MENU_ITEM_H__
+#define __IDO_LOCATION_MENU_ITEM_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define IDO_LOCATION_MENU_ITEM_TYPE (ido_location_menu_item_get_type ())
+#define IDO_LOCATION_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDO_LOCATION_MENU_ITEM_TYPE, IdoLocationMenuItem))
+#define IDO_IS_LOCATION_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDO_LOCATION_MENU_ITEM_TYPE))
+
+typedef struct _IdoLocationMenuItem IdoLocationMenuItem;
+typedef struct _IdoLocationMenuItemClass IdoLocationMenuItemClass;
+typedef struct _IdoLocationMenuItemPrivate IdoLocationMenuItemPrivate;
+
+struct _IdoLocationMenuItemClass
+{
+ GtkMenuItemClass parent_class;
+};
+
+/**
+ * A menuitem that indicates a location.
+ *
+ * It contains a primary label giving the location's name and a
+ * right-aligned secondary label showing the location's current time
+ */
+struct _IdoLocationMenuItem
+{
+ /*< private >*/
+ GtkMenuItem parent;
+ IdoLocationMenuItemPrivate * priv;
+};
+
+
+GType ido_location_menu_item_get_type (void) G_GNUC_CONST;
+
+GtkWidget * ido_location_menu_item_new (void);
+
+GtkMenuItem * ido_location_menu_item_new_from_model (GMenuItem * menuitem,
+ GActionGroup * actions);
+
+void ido_location_menu_item_set_name (IdoLocationMenuItem * menuitem,
+ const char * name);
+
+void ido_location_menu_item_set_timezone (IdoLocationMenuItem * menuitem,
+ const char * timezone);
+
+void ido_location_menu_item_set_format (IdoLocationMenuItem * menuitem,
+ const char * strftime_fmt);
+
+
+G_END_DECLS
+
+#endif
diff --git a/src/idomenuitemfactory.c b/src/idomenuitemfactory.c
index ecb07e0..650c95f 100644
--- a/src/idomenuitemfactory.c
+++ b/src/idomenuitemfactory.c
@@ -20,8 +20,11 @@
#include <gtk/gtk.h>
#include <gtk/ubuntu-private.h>
-#include "idousermenuitem.h"
+#include "idoappointmentmenuitem.h"
+#include "idocalendarmenuitem.h"
+#include "idolocationmenuitem.h"
#include "idoscalemenuitem.h"
+#include "idousermenuitem.h"
#include "idomediaplayermenuitem.h"
#include "idoplaybackmenuitem.h"
@@ -51,6 +54,15 @@ ido_menu_item_factory_create_menu_item (UbuntuMenuItemFactory *factory,
if (g_str_equal (type, "indicator.user-menu-item"))
item = ido_user_menu_item_new_from_model (menuitem, actions);
+ else if (g_str_equal (type, "com.canonical.indicator.calendar"))
+ item = ido_calendar_menu_item_new_from_model (menuitem, actions);
+
+ else if (g_str_equal (type, "com.canonical.indicator.location"))
+ item = ido_location_menu_item_new_from_model (menuitem, actions);
+
+ else if (g_str_equal (type, "com.canonical.indicator.appointment"))
+ item = ido_appointment_menu_item_new_from_model (menuitem, actions);
+
else if (g_str_equal (type, "com.canonical.unity.slider"))
item = ido_scale_menu_item_new_from_model (menuitem, actions);