diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/idoappointmentmenuitem.c | 483 | ||||
-rw-r--r-- | src/idoappointmentmenuitem.h | 79 | ||||
-rw-r--r-- | src/idocalendarmenuitem.c | 177 | ||||
-rw-r--r-- | src/idocalendarmenuitem.h | 7 | ||||
-rw-r--r-- | src/idolocationmenuitem.c | 477 | ||||
-rw-r--r-- | src/idolocationmenuitem.h | 73 | ||||
-rw-r--r-- | src/idomenuitemfactory.c | 14 |
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 (¶meters[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 (¶meters[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); |