From 3db9d6ff0a049700b55bb9766ae734fd19010e4a Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 14 Jun 2013 13:02:50 -0500 Subject: add appointment menuitem --- src/idoappointmentmenuitem.c | 414 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 414 insertions(+) create mode 100644 src/idoappointmentmenuitem.c (limited to 'src/idoappointmentmenuitem.c') diff --git a/src/idoappointmentmenuitem.c b/src/idoappointmentmenuitem.c new file mode 100644 index 0000000..9329009 --- /dev/null +++ b/src/idoappointmentmenuitem.c @@ -0,0 +1,414 @@ +/** + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include /* strstr() */ + +#include + +#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); +} -- cgit v1.2.3 From 5988e06c3df519ae2dcce7454ffc597bcc3ce9e5 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 14 Jun 2013 13:22:03 -0500 Subject: add ted's name to idoappointmentmenuitem.c for create_color_icon_pixbuf() --- src/idoappointmentmenuitem.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/idoappointmentmenuitem.c') diff --git a/src/idoappointmentmenuitem.c b/src/idoappointmentmenuitem.c index 9329009..864081b 100644 --- a/src/idoappointmentmenuitem.c +++ b/src/idoappointmentmenuitem.c @@ -3,6 +3,7 @@ * * Authors: * Charles Kerr + * Ted Gould * * 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 -- cgit v1.2.3 From 6de8e0386beb02beafc321aaef82176acaae5424 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 17 Jun 2013 10:04:35 -0500 Subject: in idoappointmentmenuitem.c, fix startup issue arising from updating the timestamp label when the strftime format string hasn't been initialized yet. --- src/idoappointmentmenuitem.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/idoappointmentmenuitem.c') diff --git a/src/idoappointmentmenuitem.c b/src/idoappointmentmenuitem.c index 864081b..929e011 100644 --- a/src/idoappointmentmenuitem.c +++ b/src/idoappointmentmenuitem.c @@ -280,12 +280,14 @@ create_color_icon_pixbuf (const char * color_spec) 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); + if (p->date_time && p->format) + { + char * str = g_date_time_format (p->date_time, p->format); + gtk_label_set_text (GTK_LABEL(p->timestamp_label), str); + g_free (str); + } } /*** -- cgit v1.2.3 From 9205983a6f1a75487abeb32e4fe6df3753980277 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 17 Jun 2013 10:41:25 -0500 Subject: when building location and appointment menuitems from a GMenu, grab all the parameters and then pass them to object_new() as a block to avoid them getting set twice -- once with the constructor's default values, and then once afterwards --- src/idoappointmentmenuitem.c | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) (limited to 'src/idoappointmentmenuitem.c') diff --git a/src/idoappointmentmenuitem.c b/src/idoappointmentmenuitem.c index 929e011..45daeed 100644 --- a/src/idoappointmentmenuitem.c +++ b/src/idoappointmentmenuitem.c @@ -366,35 +366,58 @@ 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]; - ido_appointment = IDO_APPOINTMENT_MENU_ITEM (ido_appointment_menu_item_new()); + /* create the ido_appointment */ + + n = 0; if (g_menu_item_get_attribute (menu_item, "label", "s", &str)) { - ido_appointment_menu_item_set_summary (ido_appointment, str); - g_free (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)) { - ido_appointment_menu_item_set_color (ido_appointment, str); - g_free (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", "x", &i64)) + if (g_menu_item_get_attribute (menu_item, "x-canonical-time-format", "s", &str)) { - ido_appointment_menu_item_set_time (ido_appointment, (time_t)i64); + 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-format", "s", &str)) + if (g_menu_item_get_attribute (menu_item, "x-canonical-time", "x", &i64)) { - ido_appointment_menu_item_set_format (ido_appointment, str); - g_free (str); + 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 Date: Mon, 17 Jun 2013 10:59:26 -0500 Subject: add documentation for the public API calls --- src/idoappointmentmenuitem.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) (limited to 'src/idoappointmentmenuitem.c') diff --git a/src/idoappointmentmenuitem.c b/src/idoappointmentmenuitem.c index 45daeed..a24ab49 100644 --- a/src/idoappointmentmenuitem.c +++ b/src/idoappointmentmenuitem.c @@ -294,12 +294,22 @@ update_timestamp_label (IdoAppointmentMenuItem * self) **** 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) @@ -317,6 +327,12 @@ ido_appointment_menu_item_set_color (IdoAppointmentMenuItem * self, 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) @@ -331,6 +347,13 @@ ido_appointment_menu_item_set_summary (IdoAppointmentMenuItem * self, 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) @@ -346,7 +369,13 @@ ido_appointment_menu_item_set_time (IdoAppointmentMenuItem * self, } /** - * @strftime_fmt: the format string used to build the appointment's time string + * ido_appointment_menu_item_set_format: + * @format: the format string used when showing the appointment'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_appointment_menu_item_set_format (IdoAppointmentMenuItem * self, @@ -362,6 +391,17 @@ ido_appointment_menu_item_set_format (IdoAppointmentMenuItem * self, update_timestamp_label (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_appointment_menu_item_new_from_model (GMenuItem * menu_item, GActionGroup * actions) -- cgit v1.2.3 From 32915975083819f39112f5c4c009b45a3e8ae33b Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 17 Jun 2013 11:09:43 -0500 Subject: in IdoAppointmentMenuItem's update_timestamp_label(), clear the label text if either the time or format properties are unset --- src/idoappointmentmenuitem.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'src/idoappointmentmenuitem.c') diff --git a/src/idoappointmentmenuitem.c b/src/idoappointmentmenuitem.c index a24ab49..26029a7 100644 --- a/src/idoappointmentmenuitem.c +++ b/src/idoappointmentmenuitem.c @@ -236,6 +236,7 @@ ido_appointment_menu_item_init (IdoAppointmentMenuItem *self) **** ***/ +/* creates a menu-sized pixbuf filled with specified color */ static GdkPixbuf * create_color_icon_pixbuf (const char * color_spec) { @@ -280,14 +281,16 @@ create_color_icon_pixbuf (const char * color_spec) static void update_timestamp_label (IdoAppointmentMenuItem * self) { + char * str; priv_t * p = self->priv; if (p->date_time && p->format) - { - char * str = g_date_time_format (p->date_time, p->format); - gtk_label_set_text (GTK_LABEL(p->timestamp_label), str); - g_free (str); - } + 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); } /*** -- cgit v1.2.3 From 0aa36d9cf4e13d1247814122567739b02d67db9c Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 17 Jun 2013 11:14:36 -0500 Subject: copyediting: fix copy/paste errors in the documentation --- src/idoappointmentmenuitem.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/idoappointmentmenuitem.c') diff --git a/src/idoappointmentmenuitem.c b/src/idoappointmentmenuitem.c index 26029a7..2ac518a 100644 --- a/src/idoappointmentmenuitem.c +++ b/src/idoappointmentmenuitem.c @@ -375,7 +375,7 @@ ido_appointment_menu_item_set_time (IdoAppointmentMenuItem * 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 location'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. @@ -395,15 +395,15 @@ ido_appointment_menu_item_set_format (IdoAppointmentMenuItem * self, } /** - * ido_location_menu_item_new_from_model: + * 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 IdoLocationMenuItem with properties initialized from + * 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 IdoLocationMenuItem is activated. + * in @actions when this IdoAppointmentMenuItem is activated. */ GtkMenuItem * ido_appointment_menu_item_new_from_model (GMenuItem * menu_item, -- cgit v1.2.3