diff options
author | Charles Kerr <charles.kerr@canonical.com> | 2013-06-15 17:37:22 +0000 |
---|---|---|
committer | Tarmac <Unknown> | 2013-06-15 17:37:22 +0000 |
commit | 83e473561eea890bbd47476d7dc221c9e1c300c4 (patch) | |
tree | bc2833dd7a99494a348dd9ebaa3d2704460c8e90 | |
parent | 761013209f13b68e49a4bd0992c4bf5485ab3e2f (diff) | |
parent | 53ee722f7352e25ac1899339105685c8a3d82557 (diff) | |
download | ayatana-ido-83e473561eea890bbd47476d7dc221c9e1c300c4.tar.gz ayatana-ido-83e473561eea890bbd47476d7dc221c9e1c300c4.tar.bz2 ayatana-ido-83e473561eea890bbd47476d7dc221c9e1c300c4.zip |
adds the ido widgets needed for indicator-datetime.
http://bazaar.launchpad.net/~charlesk/indicator-datetime/gmenuify/view/head:/README documents the actions & menuitems.
Approved by Ted Gould, PS Jenkins bot.
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | debian/libido3-0.1-0.symbols | 34 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/idoappointmentmenuitem.c | 415 | ||||
-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 | 432 | ||||
-rw-r--r-- | src/idolocationmenuitem.h | 73 | ||||
-rw-r--r-- | src/idomenuitemfactory.c | 14 |
10 files changed, 1221 insertions, 16 deletions
diff --git a/configure.ac b/configure.ac index 706919f..ad525d7 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ # m4_define([ido_major_version], [13]) m4_define([ido_minor_version], [10]) -m4_define([ido_micro_version], [0]) +m4_define([ido_micro_version], [1]) m4_define([ido_api_version], [ido_major_version.ido_minor_version]) diff --git a/debian/libido3-0.1-0.symbols b/debian/libido3-0.1-0.symbols index b03c9da..b3495ac 100644 --- a/debian/libido3-0.1-0.symbols +++ b/debian/libido3-0.1-0.symbols @@ -1,4 +1,17 @@ libido3-0.1.so.0 libido3-0.1-0 #MINVER# + ido_action_helper_activate@Base 0replaceme + ido_action_helper_change_action_state@Base 0replaceme + ido_action_helper_get_action_target@Base 0replaceme + ido_action_helper_get_type@Base 0replaceme + ido_action_helper_get_widget@Base 0replaceme + ido_action_helper_new@Base 0replaceme + ido_appointment_menu_item_get_type@Base 0replaceme + ido_appointment_menu_item_new@Base 0replaceme + ido_appointment_menu_item_new_from_model@Base 0replaceme + ido_appointment_menu_item_set_color@Base 0replaceme + ido_appointment_menu_item_set_format@Base 0replaceme + ido_appointment_menu_item_set_summary@Base 0replaceme + ido_appointment_menu_item_set_time@Base 0replaceme ido_calendar_menu_item_clear_marks@Base 0.2.1 ido_calendar_menu_item_get_calendar@Base 0.1.10 ido_calendar_menu_item_get_date@Base 0.2.1 @@ -6,12 +19,21 @@ libido3-0.1.so.0 libido3-0.1-0 #MINVER# ido_calendar_menu_item_get_type@Base 0.1.10 ido_calendar_menu_item_mark_day@Base 0.2.1 ido_calendar_menu_item_new@Base 0.1.10 + ido_calendar_menu_item_new_from_model@Base 0replaceme ido_calendar_menu_item_set_date@Base 0.2.2 ido_calendar_menu_item_set_display_options@Base 0.2.1 ido_calendar_menu_item_unmark_day@Base 0.2.1 ido_entry_menu_item_get_entry@Base 0.1.0 ido_entry_menu_item_get_type@Base 0.1.0 ido_entry_menu_item_new@Base 0.1.0 + ido_init@Base 0replaceme + ido_location_menu_item_get_type@Base 0replaceme + ido_location_menu_item_new@Base 0replaceme + ido_location_menu_item_new_from_model@Base 0replaceme + ido_location_menu_item_set_format@Base 0replaceme + ido_location_menu_item_set_name@Base 0replaceme + ido_location_menu_item_set_timezone@Base 0replaceme + ido_menu_item_factory_get_type@Base 0replaceme ido_message_dialog_get_type@Base 0.1.8 ido_message_dialog_new@Base 0.1.8 ido_message_dialog_new_with_markup@Base 0.1.8 @@ -26,6 +48,7 @@ libido3-0.1.so.0 libido3-0.1-0 #MINVER# ido_scale_menu_item_get_style@Base 0.1.9 ido_scale_menu_item_get_type@Base 0.1.0 ido_scale_menu_item_new@Base 0.1.0 + ido_scale_menu_item_new_from_model@Base 0replaceme ido_scale_menu_item_new_with_range@Base 0.1.0 ido_scale_menu_item_primary_clicked@Base 0.3.3 ido_scale_menu_item_secondary_clicked@Base 0.3.3 @@ -33,7 +56,6 @@ libido3-0.1.so.0 libido3-0.1-0 #MINVER# ido_scale_menu_item_set_secondary_label@Base 0.1.9 ido_scale_menu_item_set_style@Base 0.1.9 ido_scale_menu_item_style_get_type@Base 0.1.9 - ido_scale_menu_item_new_from_model@Base 0replaceme ido_switch_menu_item_get_content_area@Base 12.10.0 ido_switch_menu_item_get_type@Base 12.10.0 ido_switch_menu_item_new@Base 12.10.0 @@ -59,18 +81,10 @@ libido3-0.1.so.0 libido3-0.1-0 #MINVER# ido_timeline_set_progress@Base 0.1.10 ido_timeline_set_screen@Base 0.1.8 ido_timeline_start@Base 0.1.8 - ido_action_helper_activate@Base 0replaceme - ido_action_helper_change_action_state@Base 0replaceme - ido_action_helper_get_action_target@Base 0replaceme - ido_action_helper_get_type@Base 0replaceme - ido_action_helper_get_widget@Base 0replaceme - ido_action_helper_new@Base 0replaceme ido_user_menu_item_get_type@Base 0replaceme ido_user_menu_item_new@Base 0replaceme + ido_user_menu_item_new_from_model@Base 0replaceme ido_user_menu_item_set_current_user@Base 0replaceme ido_user_menu_item_set_icon@Base 0replaceme ido_user_menu_item_set_label@Base 0replaceme ido_user_menu_item_set_logged_in@Base 0replaceme - ido_user_menu_item_new_from_model@Base 0replaceme - ido_init@Base 0replaceme - ido_menu_item_factory_get_type@Base 0replaceme diff --git a/src/Makefile.am b/src/Makefile.am index 0a4dbab..7b2e51e 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 @@ -72,6 +74,8 @@ libido_0_1_la_SOURCES = \ idotimeline.c \ idomenuitemfactory.c \ idoactionhelper.c \ + idoappointmentmenuitem.c \ + idolocationmenuitem.c \ idousermenuitem.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..864081b --- /dev/null +++ b/src/idoappointmentmenuitem.c @@ -0,0 +1,415 @@ +/** + * 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); +} + +/*** +**** +***/ + +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; + + str = g_date_time_format (p->date_time, p->format); + gtk_label_set_text (GTK_LABEL(p->timestamp_label), str); + g_free (str); +} + +/*** +**** Public API +***/ + +GtkWidget * +ido_appointment_menu_item_new (void) +{ + return GTK_WIDGET (g_object_new (IDO_APPOINTMENT_MENU_ITEM_TYPE, NULL)); +} + +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)); +} + +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); +} + +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); +} + +/** + * @strftime_fmt: the format string used to build the appointment's time 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); +} + +GtkMenuItem * +ido_appointment_menu_item_new_from_model (GMenuItem * menu_item, + GActionGroup * actions) +{ + gint64 i64; + gchar * str; + IdoAppointmentMenuItem * ido_appointment; + + ido_appointment = IDO_APPOINTMENT_MENU_ITEM (ido_appointment_menu_item_new()); + + if (g_menu_item_get_attribute (menu_item, "label", "s", &str)) + { + ido_appointment_menu_item_set_summary (ido_appointment, str); + g_free (str); + } + + if (g_menu_item_get_attribute (menu_item, "x-canonical-color", "s", &str)) + { + ido_appointment_menu_item_set_color (ido_appointment, str); + g_free (str); + } + + if (g_menu_item_get_attribute (menu_item, "x-canonical-time", "x", &i64)) + { + ido_appointment_menu_item_set_time (ido_appointment, (time_t)i64); + } + + if (g_menu_item_get_attribute (menu_item, "x-canonical-time-format", "s", &str)) + { + ido_appointment_menu_item_set_format (ido_appointment, str); + g_free (str); + } + + 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..34e5de3 --- /dev/null +++ b/src/idolocationmenuitem.c @@ -0,0 +1,432 @@ +/** + * 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 +***/ + +GtkWidget * +ido_location_menu_item_new (void) +{ + return GTK_WIDGET (g_object_new (IDO_LOCATION_MENU_ITEM_TYPE, NULL)); +} + +/** + * @name: human-readable name, such as a city (eg: "Oklahoma City") + */ +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); +} + +/** + * @timezone: timezone identifier (eg: "America/Chicago") + */ +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); +} + +/** + * @strftime_fmt: the format string used to build the location's time string + */ +void +ido_location_menu_item_set_format (IdoLocationMenuItem * self, + const char * strftime_fmt) +{ + priv_t * p; + + g_return_if_fail (IDO_IS_LOCATION_MENU_ITEM (self)); + p = self->priv; + + g_free (p->format); + p->format = g_strdup (strftime_fmt); + update_timestamp_label (self); + start_timestamp_timer (self); +} + +GtkMenuItem * +ido_location_menu_item_new_from_model (GMenuItem * menu_item, + GActionGroup * actions) +{ + gchar * str; + IdoLocationMenuItem * ido_location; + + ido_location = IDO_LOCATION_MENU_ITEM (ido_location_menu_item_new ()); + + if (g_menu_item_get_attribute (menu_item, "label", "s", &str)) + { + ido_location_menu_item_set_name (ido_location, str); + g_free (str); + } + + if (g_menu_item_get_attribute (menu_item, "x-canonical-timezone", "s", &str)) + { + ido_location_menu_item_set_timezone (ido_location, str); + g_free (str); + } + + if (g_menu_item_get_attribute (menu_item, "x-canonical-time-format", "s", &str)) + { + ido_location_menu_item_set_format (ido_location, str); + g_free (str); + } + + 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 0ff84a8..aef08e6 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" #define IDO_TYPE_MENU_ITEM_FACTORY (ido_menu_item_factory_get_type ()) #define IDO_MENU_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), IDO_TYPE_MENU_ITEM_FACTORY, IdoMenuItemFactory)) @@ -49,6 +52,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); |