/* * Copyright 2012 Canonical Ltd. * * 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 . * * Authors: * Lars Uebernickel */ #include "messaging-menu-message.h" /** * SECTION:messaging-menu-message * @title: MessagingMenuMessage * @short_description: A single message in the messaging menu * @include: messaging-menu.h */ typedef GObjectClass MessagingMenuMessageClass; struct _MessagingMenuMessage { GObject parent; gchar *id; GIcon *icon; gchar *title; gchar *subtitle; gchar *body; gint64 time; gboolean draws_attention; GSList *actions; }; G_DEFINE_TYPE (MessagingMenuMessage, messaging_menu_message, G_TYPE_OBJECT); enum { PROP_0, PROP_ID, PROP_ICON, PROP_TITLE, PROP_SUBTITLE, PROP_BODY, PROP_TIME, PROP_DRAWS_ATTENTION, NUM_PROPERTIES }; static GParamSpec *properties[NUM_PROPERTIES]; typedef struct { gchar *id; gchar *label; GVariantType *parameter_type; GVariant *parameter_hint; } Action; static void action_free (gpointer data) { Action *action = data; g_free (action->id); g_free (action->label); if (action->parameter_type) g_variant_type_free (action->parameter_type); if (action->parameter_hint) g_variant_unref (action->parameter_hint); g_slice_free (Action, action); } static void messaging_menu_message_dispose (GObject *object) { MessagingMenuMessage *msg = MESSAGING_MENU_MESSAGE (object); g_clear_object (&msg->icon); G_OBJECT_CLASS (messaging_menu_message_parent_class)->dispose (object); } static void messaging_menu_message_finalize (GObject *object) { MessagingMenuMessage *msg = MESSAGING_MENU_MESSAGE (object); g_free (msg->id); g_free (msg->title); g_free (msg->subtitle); g_free (msg->body); g_slist_free_full (msg->actions, action_free); msg->actions = NULL; G_OBJECT_CLASS (messaging_menu_message_parent_class)->finalize (object); } static void messaging_menu_message_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MessagingMenuMessage *msg = MESSAGING_MENU_MESSAGE (object); switch (property_id) { case PROP_ID: g_value_set_string (value, msg->id); break; case PROP_ICON: g_value_set_object (value, msg->icon); break; case PROP_TITLE: g_value_set_string (value, msg->title); break; case PROP_SUBTITLE: g_value_set_string (value, msg->subtitle); break; case PROP_BODY: g_value_set_string (value, msg->body); break; case PROP_TIME: g_value_set_int64 (value, msg->time); break; case PROP_DRAWS_ATTENTION: g_value_set_boolean (value, msg->draws_attention); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void messaging_menu_message_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MessagingMenuMessage *msg = MESSAGING_MENU_MESSAGE (object); switch (property_id) { case PROP_ID: msg->id = g_value_dup_string (value); break; case PROP_ICON: msg->icon = g_value_dup_object (value); break; case PROP_TITLE: msg->title = g_value_dup_string (value); break; case PROP_SUBTITLE: msg->subtitle = g_value_dup_string (value); break; case PROP_BODY: msg->body = g_value_dup_string (value); break; case PROP_TIME: msg->time = g_value_get_int64 (value); break; case PROP_DRAWS_ATTENTION: messaging_menu_message_set_draws_attention (msg, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void messaging_menu_message_class_init (MessagingMenuMessageClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = messaging_menu_message_dispose; object_class->finalize = messaging_menu_message_finalize; object_class->get_property = messaging_menu_message_get_property; object_class->set_property = messaging_menu_message_set_property; properties[PROP_ID] = g_param_spec_string ("id", "Id", "Unique id of the message", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_ICON] = g_param_spec_object ("icon", "Icon", "Icon of the message", G_TYPE_ICON, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_TITLE] = g_param_spec_string ("title", "Title", "Title of the message", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_SUBTITLE] = g_param_spec_string ("subtitle", "Subtitle", "Subtitle of the message", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_BODY] = g_param_spec_string ("body", "Body", "First lines of the body of the message", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_TIME] = g_param_spec_int64 ("time", "Time", "Time the message was sent, in microseconds", 0, G_MAXINT64, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_DRAWS_ATTENTION] = g_param_spec_boolean ("draws-attention", "Draws attention", "Whether the message should draw attention", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (klass, NUM_PROPERTIES, properties); /** * MessagingMenuMessage::activate: * @msg: the #MessagingMenuMessage * @action: (allow-none): the id of activated action, or %NULL * @parameter: (allow-none): activation parameter, or %NULL * * Emitted when the user has activated the message. The message is * immediately removed from the application's menu, handlers of this * signal do not need to call messaging_menu_app_remove_message(). */ g_signal_new ("activate", MESSAGING_MENU_TYPE_MESSAGE, G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_VARIANT); } static void messaging_menu_message_init (MessagingMenuMessage *self) { self->draws_attention = TRUE; } /** * messaging_menu_message_new: * @id: unique id of the message * @icon: (transfer full) (allow-none): a #GIcon representing the message * @title: the title of the message * @subtitle: (allow-none): the subtitle of the message * @body: (allow-none): the message body * @time: the time the message was received * * Creates a new #MessagingMenuMessage. * * Returns: (transfer full): a new #MessagingMenuMessage */ MessagingMenuMessage * messaging_menu_message_new (const gchar *id, GIcon *icon, const gchar *title, const gchar *subtitle, const gchar *body, gint64 time) { g_return_val_if_fail (id != NULL, NULL); g_return_val_if_fail (title != NULL, NULL); return g_object_new (MESSAGING_MENU_TYPE_MESSAGE, "id", id, "icon", icon, "title", title, "subtitle", subtitle, "body", body, "time", time, NULL); } /** * messaging_menu_message_get_id: * @msg: a #MessagingMenuMessage * * Returns: the unique id of @msg */ const gchar * messaging_menu_message_get_id (MessagingMenuMessage *msg) { g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL); return msg->id; } /** * messaging_menu_message_get_icon: * @msg: a #MessagingMenuMessage * * Returns: (transfer none): the icon of @msg */ GIcon * messaging_menu_message_get_icon (MessagingMenuMessage *msg) { g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL); return msg->icon; } /** * messaging_menu_message_get_title: * @msg: a #MessagingMenuMessage * * Returns: the title of @msg */ const gchar * messaging_menu_message_get_title (MessagingMenuMessage *msg) { g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL); return msg->title; } /** * messaging_menu_message_get_subtitle: * @msg: a #MessagingMenuMessage * * Returns: the subtitle of @msg */ const gchar * messaging_menu_message_get_subtitle (MessagingMenuMessage *msg) { g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL); return msg->subtitle; } /** * messaging_menu_message_get_body: * @msg: a #MessagingMenuMessage * * Returns: the body of @msg */ const gchar * messaging_menu_message_get_body (MessagingMenuMessage *msg) { g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL); return msg->body; } /** * messaging_menu_message_get_time: * @msg: a #MessagingMenuMessage * * Returns: the time at which @msg was received */ gint64 messaging_menu_message_get_time (MessagingMenuMessage *msg) { g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), 0); return msg->time; } /** * messaging_menu_message_get_draws_attention: * @msg: a #MessagingMenuMessage * * Returns: whether @msg is drawing attention */ gboolean messaging_menu_message_get_draws_attention (MessagingMenuMessage *msg) { g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), FALSE); return msg->draws_attention; } /** * messaging_menu_message_set_draws_attention: * @msg: a #MessagingMenuMessage * @draws_attention: whether @msg should draw attention * * Sets whether @msg is drawing attention. */ void messaging_menu_message_set_draws_attention (MessagingMenuMessage *msg, gboolean draws_attention) { g_return_if_fail (MESSAGING_MENU_IS_MESSAGE (msg)); msg->draws_attention = draws_attention; g_object_notify_by_pspec (G_OBJECT (msg), properties[PROP_DRAWS_ATTENTION]); } /** * messaging_menu_message_add_action: * @msg: a #MessagingMenuMessage * @id: unique id of the action * @label: (allow-none): label of the action * @parameter_type: (allow-none): a #GVariantType * @parameter_hint: (allow-none): a #GVariant suggesting a valid range * for parameters * * Adds an action with @id and @label to @message. Actions are an * alternative way for users to activate a message. Note that messages * can still be activated without an action. * * If @parameter_type is non-%NULL, the action is able to receive user * input in addition to simply activating the action. Currently, only * string parameters are supported. * * A list of predefined parameters can be supplied as a #GVariant array * of @parameter_type in @parameter_hint. If @parameter_hint is * floating, it will be consumed. * * It is recommended to add at most two actions to a message. */ void messaging_menu_message_add_action (MessagingMenuMessage *msg, const gchar *id, const gchar *label, const GVariantType *parameter_type, GVariant *parameter_hint) { Action *action; g_return_if_fail (MESSAGING_MENU_IS_MESSAGE (msg)); g_return_if_fail (id != NULL); action = g_slice_new (Action); action->id = g_strdup (id); action->label = g_strdup (label); action->parameter_type = parameter_type ? g_variant_type_copy (parameter_type) : NULL; action->parameter_hint = parameter_hint ? g_variant_ref_sink (parameter_hint) : NULL; msg->actions = g_slist_append (msg->actions, action); } static GVariant * action_to_variant (Action *action) { GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&builder, "{sv}", "name", g_variant_new_string (action->id)); if (action->label) g_variant_builder_add (&builder, "{sv}", "label", g_variant_new_string (action->label)); if (action->parameter_type) { gchar *type = g_variant_type_dup_string (action->parameter_type); g_variant_builder_add (&builder, "{sv}", "parameter-type", g_variant_new_signature (type)); g_free (type); } if (action->parameter_hint) g_variant_builder_add (&builder, "{sv}", "parameter-hint", action->parameter_hint); return g_variant_builder_end (&builder); } /* * _messaging_menu_message_to_variant: * @msg: a #MessagingMenuMessage * * Serializes @msg to a #GVariant of the form (savsssxaa{sv}b): * * id * icon (fake-maybe) * title * subtitle * body * time * array of action dictionaries * draws_attention * * Returns: a new floating #GVariant instance */ GVariant * _messaging_menu_message_to_variant (MessagingMenuMessage *msg) { GVariantBuilder builder; GSList *it; GVariant *serialized_icon; GVariantBuilder icon_builder; g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL); serialized_icon = msg->icon ? g_icon_serialize (msg->icon) : NULL; g_variant_builder_init (&icon_builder, G_VARIANT_TYPE ("av")); if (serialized_icon) { g_variant_builder_add (&icon_builder, "v", serialized_icon); g_variant_unref (serialized_icon); } g_variant_builder_init (&builder, G_VARIANT_TYPE ("(savsssxaa{sv}b)")); g_variant_builder_add (&builder, "s", msg->id); g_variant_builder_add (&builder, "av", &icon_builder); g_variant_builder_add (&builder, "s", msg->title ? msg->title : ""); g_variant_builder_add (&builder, "s", msg->subtitle ? msg->subtitle : ""); g_variant_builder_add (&builder, "s", msg->body ? msg->body : ""); g_variant_builder_add (&builder, "x", msg->time); g_variant_builder_open (&builder, G_VARIANT_TYPE ("aa{sv}")); for (it = msg->actions; it; it = it->next) g_variant_builder_add_value (&builder, action_to_variant (it->data)); g_variant_builder_close (&builder); g_variant_builder_add (&builder, "b", msg->draws_attention); return g_variant_builder_end (&builder); }