/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2004-2007 Imendio AB
* Copyright (C) 2010 Collabora Ltd
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY 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, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*
* Authors: Mikael Hallendal <micke@imendio.com>
*/
#include "config.h"
#include <string.h>
#include <libempathy/empathy-utils.h>
#include "empathy-cell-renderer-text.h"
#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyCellRendererText)
typedef struct {
gchar *name;
TpConnectionPresenceType presence_type;
gchar *status;
gboolean is_group;
gboolean is_valid;
gboolean is_selected;
gchar **types;
gboolean compact;
} EmpathyCellRendererTextPriv;
static void cell_renderer_text_finalize (GObject *object);
static void cell_renderer_text_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec);
static void cell_renderer_text_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec);
static void cell_renderer_text_render (GtkCellRenderer *cell,
cairo_t *cr,
GtkWidget *widget,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags);
static void cell_renderer_text_update_text (EmpathyCellRendererText *cell,
GtkWidget *widget,
gboolean selected);
/* Properties */
enum {
PROP_0,
PROP_NAME,
PROP_PRESENCE_TYPE,
PROP_STATUS,
PROP_IS_GROUP,
PROP_COMPACT,
PROP_CLIENT_TYPES
};
G_DEFINE_TYPE (EmpathyCellRendererText, empathy_cell_renderer_text, GTK_TYPE_CELL_RENDERER_TEXT);
static void
cell_renderer_text_get_preferred_height_for_width (GtkCellRenderer *renderer,
GtkWidget *widget,
gint width,
gint *minimum_size,
gint *natural_size)
{
EmpathyCellRendererText *self = EMPATHY_CELL_RENDERER_TEXT (renderer);
EmpathyCellRendererTextPriv *priv = GET_PRIV (self);
/* Only update if not already valid so we get the right size. */
cell_renderer_text_update_text (self, widget, priv->is_selected);
GTK_CELL_RENDERER_CLASS (empathy_cell_renderer_text_parent_class)->
get_preferred_height_for_width (renderer, widget, width,
minimum_size, natural_size);
}
static void
empathy_cell_renderer_text_class_init (EmpathyCellRendererTextClass *klass)
{
GObjectClass *object_class;
GtkCellRendererClass *cell_class;
GParamSpec *spec;
object_class = G_OBJECT_CLASS (klass);
cell_class = GTK_CELL_RENDERER_CLASS (klass);
object_class->finalize = cell_renderer_text_finalize;
object_class->get_property = cell_renderer_text_get_property;
object_class->set_property = cell_renderer_text_set_property;
cell_class->get_preferred_height_for_width = cell_renderer_text_get_preferred_height_for_width;
cell_class->render = cell_renderer_text_render;
spec = g_param_spec_string ("name", "Name", "Contact name", NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_NAME, spec);
spec = g_param_spec_uint ("presence-type", "TpConnectionPresenceType",
"The contact's presence type",
0, G_MAXUINT, /* Telepathy enum, can be extended */
TP_CONNECTION_PRESENCE_TYPE_UNKNOWN,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_PRESENCE_TYPE,
spec);
spec = g_param_spec_string ("status", "Status message",
"Contact's custom status message", NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_STATUS, spec);
spec = g_param_spec_boolean ("is-group", "Is group",
"Whether this cell is a group", FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_IS_GROUP, spec);
spec = g_param_spec_boolean ("compact", "Compact",
"TRUE to show the status alongside the contact name;"
"FALSE to show it on its own line",
FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_COMPACT, spec);
spec = g_param_spec_boxed ("client-types", "Contact client types",
"Client types of the contact",
G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_CLIENT_TYPES, spec);
g_type_class_add_private (object_class, sizeof (EmpathyCellRendererTextPriv));
}
static void
empathy_cell_renderer_text_init (EmpathyCellRendererText *cell)
{
EmpathyCellRendererTextPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (cell,
EMPATHY_TYPE_CELL_RENDERER_TEXT, EmpathyCellRendererTextPriv);
cell->priv = priv;
g_object_set (cell,
"ellipsize", PANGO_ELLIPSIZE_END,
NULL);
priv->name = g_strdup ("");
priv->status = g_strdup ("");
priv->compact = FALSE;
}
static void
cell_renderer_text_finalize (GObject *object)
{
EmpathyCellRendererText *cell;
EmpathyCellRendererTextPriv *priv;
cell = EMPATHY_CELL_RENDERER_TEXT (object);
priv = GET_PRIV (cell);
g_free (priv->name);
g_free (priv->status);
g_strfreev (priv->types);
(G_OBJECT_CLASS (empathy_cell_renderer_text_parent_class)->finalize) (object);
}
static void
cell_renderer_text_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec)
{
EmpathyCellRendererText *cell;
EmpathyCellRendererTextPriv *priv;
cell = EMPATHY_CELL_RENDERER_TEXT (object);
priv = GET_PRIV (cell);
switch (param_id) {
case PROP_NAME:
g_value_set_string (value, priv->name);
break;
case PROP_PRESENCE_TYPE:
g_value_set_uint (value, priv->presence_type);
break;
case PROP_STATUS:
g_value_set_string (value, priv->status);
break;
case PROP_IS_GROUP:
g_value_set_boolean (value, priv->is_group);
break;
case PROP_COMPACT:
g_value_set_boolean (value, priv->compact);
break;
case PROP_CLIENT_TYPES:
g_value_set_boxed (value, priv->types);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
cell_renderer_text_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec)
{
EmpathyCellRendererText *cell;
EmpathyCellRendererTextPriv *priv;
const gchar *str;
cell = EMPATHY_CELL_RENDERER_TEXT (object);
priv = GET_PRIV (cell);
switch (param_id) {
case PROP_NAME:
g_free (priv->name);
str = g_value_get_string (value);
priv->name = g_strdup (str ? str : "");
g_strdelimit (priv->name, "\n\r\t", ' ');
priv->is_valid = FALSE;
break;
case PROP_PRESENCE_TYPE:
priv->presence_type = g_value_get_uint (value);
priv->is_valid = FALSE;
break;
case PROP_STATUS:
g_free (priv->status);
str = g_value_get_string (value);
priv->status = g_strdup (str ? str : "");
g_strdelimit (priv->status, "\n\r\t", ' ');
priv->is_valid = FALSE;
break;
case PROP_IS_GROUP:
priv->is_group = g_value_get_boolean (value);
priv->is_valid = FALSE;
break;
case PROP_COMPACT:
priv->compact = g_value_get_boolean (value);
priv->is_valid = FALSE;
break;
case PROP_CLIENT_TYPES:
priv->types = g_value_dup_boxed (value);
priv->is_valid = FALSE;
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
}
}
static void
cell_renderer_text_render (GtkCellRenderer *cell,
cairo_t *cr,
GtkWidget *widget,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags)
{
EmpathyCellRendererText *celltext;
celltext = EMPATHY_CELL_RENDERER_TEXT (cell);
cell_renderer_text_update_text (celltext,
widget,
(flags & GTK_CELL_RENDERER_SELECTED));
(GTK_CELL_RENDERER_CLASS (empathy_cell_renderer_text_parent_class)->render) (
cell, cr,
widget,
background_area,
cell_area,
flags);
}
static void
cell_renderer_text_update_text (EmpathyCellRendererText *cell,
GtkWidget *widget,
gboolean selected)
{
EmpathyCellRendererTextPriv *priv;
PangoAttrList *attr_list;
PangoAttribute *attr_color = NULL, *attr_size;
GtkStyle *style;
gchar *str;
priv = GET_PRIV (cell);
if (priv->is_valid && priv->is_selected == selected) {
return;
}
if (priv->is_group) {
g_object_set (cell,
"visible", TRUE,
"weight", PANGO_WEIGHT_BOLD,
"text", priv->name,
"attributes", NULL,
"xpad", 1,
"ypad", 1,
NULL);
priv->is_selected = selected;
priv->is_valid = TRUE;
return;
}
style = gtk_widget_get_style (widget);
attr_list = pango_attr_list_new ();
attr_size = pango_attr_size_new (pango_font_description_get_size (style->font_desc) / 1.2);
attr_size->start_index = strlen (priv->name) + 1;
attr_size->end_index = -1;
pango_attr_list_insert (attr_list, attr_size);
if (!selected) {
GdkColor color;
color = style->text_aa[GTK_STATE_NORMAL];
attr_color = pango_attr_foreground_new (color.red, color.green, color.blue);
attr_color->start_index = attr_size->start_index;
attr_color->end_index = -1;
pango_attr_list_insert (attr_list, attr_color);
}
if (priv->compact) {
if (EMP_STR_EMPTY (priv->status)) {
str = g_strdup (priv->name);
} else {
str = g_strdup_printf ("%s %s", priv->name, priv->status);
}
} else {
const gchar *status = priv->status;
gboolean on_a_phone = FALSE;
if (EMP_STR_EMPTY (priv->status)) {
status = empathy_presence_get_default_message (priv->presence_type);
}
if (!priv->is_group && priv->types != NULL && g_strv_length (priv->types) > 0
&& !tp_strdiff (priv->types[0], "phone")) {
on_a_phone = TRUE;
/* We want the phone black. */
if (attr_color)
attr_color->start_index += 3;
}
if (status == NULL)
str = g_strdup (priv->name);
else
str = g_strdup_printf ("%s\n%s%s", priv->name,
on_a_phone ? "☎ " : "",
status);
}
g_object_set (cell,
"visible", TRUE,
"weight", PANGO_WEIGHT_NORMAL,
"text", str,
"attributes", attr_list,
"xpad", 0,
"ypad", 1,
NULL);
g_free (str);
pango_attr_list_unref (attr_list);
priv->is_selected = selected;
priv->is_valid = TRUE;
}
GtkCellRenderer *
empathy_cell_renderer_text_new (void)
{
return g_object_new (EMPATHY_TYPE_CELL_RENDERER_TEXT, NULL);
}