/*
* 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);
}