aboutsummaryrefslogtreecommitdiffstats
path: root/libempathy-gtk
diff options
context:
space:
mode:
Diffstat (limited to 'libempathy-gtk')
-rw-r--r--libempathy-gtk/empathy-cell-renderer-text.c34
-rw-r--r--libempathy-gtk/empathy-individual-store.c65
-rw-r--r--libempathy-gtk/empathy-individual-store.h1
-rw-r--r--libempathy-gtk/empathy-individual-view.c5
-rw-r--r--libempathy-gtk/empathy-individual-widget.c163
-rw-r--r--libempathy-gtk/empathy-individual-widget.h1
-rw-r--r--libempathy-gtk/empathy-individual-widget.ui29
7 files changed, 244 insertions, 54 deletions
diff --git a/libempathy-gtk/empathy-cell-renderer-text.c b/libempathy-gtk/empathy-cell-renderer-text.c
index f64ee6b0c..d0590ef8f 100644
--- a/libempathy-gtk/empathy-cell-renderer-text.c
+++ b/libempathy-gtk/empathy-cell-renderer-text.c
@@ -38,6 +38,8 @@ typedef struct {
gboolean is_valid;
gboolean is_selected;
+ gchar **types;
+
gboolean compact;
} EmpathyCellRendererTextPriv;
@@ -67,7 +69,8 @@ enum {
PROP_PRESENCE_TYPE,
PROP_STATUS,
PROP_IS_GROUP,
- PROP_COMPACT
+ PROP_COMPACT,
+ PROP_CLIENT_TYPES
};
G_DEFINE_TYPE (EmpathyCellRendererText, empathy_cell_renderer_text, GTK_TYPE_CELL_RENDERER_TEXT);
@@ -137,6 +140,11 @@ empathy_cell_renderer_text_class_init (EmpathyCellRendererTextClass *klass)
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));
}
@@ -167,6 +175,7 @@ cell_renderer_text_finalize (GObject *object)
g_free (priv->name);
g_free (priv->status);
+ g_strfreev (priv->types);
(G_OBJECT_CLASS (empathy_cell_renderer_text_parent_class)->finalize) (object);
}
@@ -199,6 +208,9 @@ cell_renderer_text_get_property (GObject *object,
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;
@@ -245,6 +257,11 @@ cell_renderer_text_set_property (GObject *object,
priv->compact = g_value_get_boolean (value);
priv->is_valid = FALSE;
break;
+ case PROP_CLIENT_TYPES:
+ g_strfreev (priv->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;
@@ -282,7 +299,7 @@ cell_renderer_text_update_text (EmpathyCellRendererText *cell,
{
EmpathyCellRendererTextPriv *priv;
PangoAttrList *attr_list;
- PangoAttribute *attr_color, *attr_size;
+ PangoAttribute *attr_color = NULL, *attr_size;
GtkStyle *style;
gchar *str;
@@ -335,15 +352,26 @@ cell_renderer_text_update_text (EmpathyCellRendererText *cell,
}
} 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", priv->name, status);
+ str = g_strdup_printf ("%s\n%s%s", priv->name,
+ on_a_phone ? "☎ " : "",
+ status);
}
g_object_set (cell,
diff --git a/libempathy-gtk/empathy-individual-store.c b/libempathy-gtk/empathy-individual-store.c
index 807afb2a2..750a6f98d 100644
--- a/libempathy-gtk/empathy-individual-store.c
+++ b/libempathy-gtk/empathy-individual-store.c
@@ -154,6 +154,39 @@ individual_can_audio_video_call (FolksIndividual *individual,
*can_video_call = can_video;
}
+static const gchar * const *
+individual_get_client_types (FolksIndividual *individual)
+{
+ GList *personas, *l;
+ const gchar * const *types = NULL;
+ FolksPresenceType presence_type = FOLKS_PRESENCE_TYPE_UNSET;
+
+ personas = folks_individual_get_personas (individual);
+ for (l = personas; l != NULL; l = l->next)
+ {
+ FolksPresence *presence;
+
+ /* We only want personas which implement FolksPresence */
+ if (!FOLKS_IS_PRESENCE (l->data))
+ continue;
+
+ presence = FOLKS_PRESENCE (l->data);
+
+ if (folks_presence_typecmp (folks_presence_get_presence_type (presence),
+ presence_type) > 0)
+ {
+ TpContact *tp_contact;
+
+ presence_type = folks_presence_get_presence_type (presence);
+
+ tp_contact = tpf_persona_get_contact (TPF_PERSONA (l->data));
+ types = tp_contact_get_client_types (tp_contact);
+ }
+ }
+
+ return types;
+}
+
static void
add_individual_to_store (GtkTreeStore *self,
GtkTreeIter *iter,
@@ -161,10 +194,13 @@ add_individual_to_store (GtkTreeStore *self,
FolksIndividual *individual)
{
gboolean can_audio_call, can_video_call;
+ const gchar * const *types;
individual_can_audio_video_call (individual, &can_audio_call,
&can_video_call);
+ types = individual_get_client_types (individual);
+
gtk_tree_store_insert_with_values (self, iter, parent, 0,
EMPATHY_INDIVIDUAL_STORE_COL_NAME,
folks_aliasable_get_alias (FOLKS_ALIASABLE (individual)),
@@ -173,6 +209,7 @@ add_individual_to_store (GtkTreeStore *self,
EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, FALSE,
EMPATHY_INDIVIDUAL_STORE_COL_CAN_AUDIO_CALL, can_audio_call,
EMPATHY_INDIVIDUAL_STORE_COL_CAN_VIDEO_CALL, can_video_call,
+ EMPATHY_INDIVIDUAL_STORE_COL_CLIENT_TYPES, types,
-1);
}
@@ -325,6 +362,13 @@ individual_store_find_contact (EmpathyIndividualStore *self,
}
static void
+free_iters (GList *iters)
+{
+ g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
+ g_list_free (iters);
+}
+
+static void
individual_store_remove_individual (EmpathyIndividualStore *self,
FolksIndividual *individual)
{
@@ -360,8 +404,7 @@ individual_store_remove_individual (EmpathyIndividualStore *self,
}
}
- g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
- g_list_free (iters);
+ free_iters (iters);
}
static void
@@ -483,9 +526,7 @@ individual_store_contact_set_active (EmpathyIndividualStore *self,
}
}
- g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
- g_list_free (iters);
-
+ free_iters (iters);
}
static void individual_store_contact_active_free (ShowActiveData *data);
@@ -610,6 +651,8 @@ individual_avatar_pixbuf_received_cb (FolksIndividual *individual,
EMPATHY_INDIVIDUAL_STORE_COL_PIXBUF_AVATAR, pixbuf,
-1);
}
+
+ free_iters (iters);
}
/* Free things */
@@ -738,10 +781,13 @@ individual_store_contact_update (EmpathyIndividualStore *self,
for (l = iters; l && set_model; l = l->next)
{
gboolean can_audio_call, can_video_call;
+ const gchar * const *types;
individual_can_audio_video_call (individual, &can_audio_call,
&can_video_call);
+ types = individual_get_client_types (individual);
+
gtk_tree_store_set (GTK_TREE_STORE (self), l->data,
EMPATHY_INDIVIDUAL_STORE_COL_ICON_STATUS, pixbuf_status,
EMPATHY_INDIVIDUAL_STORE_COL_PIXBUF_AVATAR_VISIBLE, show_avatar,
@@ -757,6 +803,7 @@ individual_store_contact_update (EmpathyIndividualStore *self,
EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, FALSE,
EMPATHY_INDIVIDUAL_STORE_COL_CAN_AUDIO_CALL, can_audio_call,
EMPATHY_INDIVIDUAL_STORE_COL_CAN_VIDEO_CALL, can_video_call,
+ EMPATHY_INDIVIDUAL_STORE_COL_CLIENT_TYPES, types,
-1);
}
@@ -780,8 +827,7 @@ individual_store_contact_update (EmpathyIndividualStore *self,
* timeout removes the user from the contact list, really we
* should remove the first timeout.
*/
- g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
- g_list_free (iters);
+ free_iters (iters);
}
static void
@@ -859,6 +905,8 @@ individual_personas_changed_cb (FolksIndividual *individual,
g_object_set_data (G_OBJECT (contact), "individual", individual);
g_signal_connect (contact, "notify::capabilities",
(GCallback) individual_store_contact_updated_cb, self);
+ g_signal_connect (contact, "notify::client-types",
+ (GCallback) individual_store_contact_updated_cb, self);
g_object_unref (contact);
}
@@ -1470,6 +1518,8 @@ individual_store_name_sort_func (GtkTreeModel *model,
tp_clear_object (&individual_a);
tp_clear_object (&individual_b);
+ g_free (name_a);
+ g_free (name_b);
return ret_val;
}
@@ -1494,6 +1544,7 @@ individual_store_setup (EmpathyIndividualStore *self)
G_TYPE_BOOLEAN, /* Can make audio calls */
G_TYPE_BOOLEAN, /* Can make video calls */
G_TYPE_BOOLEAN, /* Is a fake group */
+ G_TYPE_STRV, /* Client types */
};
priv = GET_PRIV (self);
diff --git a/libempathy-gtk/empathy-individual-store.h b/libempathy-gtk/empathy-individual-store.h
index debb218ad..246c73b91 100644
--- a/libempathy-gtk/empathy-individual-store.h
+++ b/libempathy-gtk/empathy-individual-store.h
@@ -64,6 +64,7 @@ typedef enum
EMPATHY_INDIVIDUAL_STORE_COL_CAN_AUDIO_CALL,
EMPATHY_INDIVIDUAL_STORE_COL_CAN_VIDEO_CALL,
EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP,
+ EMPATHY_INDIVIDUAL_STORE_COL_CLIENT_TYPES,
EMPATHY_INDIVIDUAL_STORE_COL_COUNT,
} EmpathyIndividualStoreCol;
diff --git a/libempathy-gtk/empathy-individual-view.c b/libempathy-gtk/empathy-individual-view.c
index 6eb301d34..a04682569 100644
--- a/libempathy-gtk/empathy-individual-view.c
+++ b/libempathy-gtk/empathy-individual-view.c
@@ -214,7 +214,8 @@ individual_view_query_tooltip_cb (EmpathyIndividualView *view,
{
priv->tooltip_widget = empathy_individual_widget_new (individual,
EMPATHY_INDIVIDUAL_WIDGET_FOR_TOOLTIP |
- EMPATHY_INDIVIDUAL_WIDGET_SHOW_LOCATION);
+ EMPATHY_INDIVIDUAL_WIDGET_SHOW_LOCATION |
+ EMPATHY_INDIVIDUAL_WIDGET_SHOW_CLIENT_TYPES);
gtk_container_set_border_width (GTK_CONTAINER (priv->tooltip_widget), 8);
g_object_ref (priv->tooltip_widget);
g_signal_connect (priv->tooltip_widget, "destroy",
@@ -1832,6 +1833,8 @@ individual_view_constructed (GObject *object)
"is_group", EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP);
gtk_tree_view_column_add_attribute (col, cell,
"compact", EMPATHY_INDIVIDUAL_STORE_COL_COMPACT);
+ gtk_tree_view_column_add_attribute (col, cell,
+ "client-types", EMPATHY_INDIVIDUAL_STORE_COL_CLIENT_TYPES);
/* Audio Call Icon */
cell = empathy_cell_renderer_activatable_new ();
diff --git a/libempathy-gtk/empathy-individual-widget.c b/libempathy-gtk/empathy-individual-widget.c
index 5f180c0dc..863ce021e 100644
--- a/libempathy-gtk/empathy-individual-widget.c
+++ b/libempathy-gtk/empathy-individual-widget.c
@@ -78,7 +78,7 @@ typedef struct {
EmpathyIndividualWidgetFlags flags;
/* weak pointer to the contact whose contact details we're displaying */
- TpContact *contact_info_contact;
+ TpContact *contact;
/* unowned Persona (borrowed from priv->individual) -> GtkTable child */
GHashTable *persona_tables;
@@ -106,6 +106,9 @@ typedef struct {
/* Groups */
GtkWidget *groups_widget;
+ /* Client types */
+ GtkWidget *hbox_client_types;
+
/* Details */
GtkWidget *vbox_details;
GtkWidget *table_details;
@@ -122,6 +125,9 @@ enum {
PROP_FLAGS
};
+static void client_types_update (EmpathyIndividualWidget *self);
+static void remove_weak_contact (EmpathyIndividualWidget *self);
+
static void
details_set_up (EmpathyIndividualWidget *self)
{
@@ -193,6 +199,53 @@ contact_info_field_cmp (TpContactInfoField *field1,
return contact_info_field_name_cmp (field1->field_name, field2->field_name);
}
+static void
+update_weak_contact (EmpathyIndividualWidget *self)
+{
+ EmpathyIndividualWidgetPriv *priv = GET_PRIV (self);
+ TpContact *tp_contact = NULL;
+
+ remove_weak_contact (self);
+
+ if (priv->individual != NULL)
+ {
+ /* FIXME: We take the most available TpContact we find and only
+ * use its details. It would be a lot better if we would get the
+ * details for every TpContact in the Individual and merge them
+ * all, but that requires vCard support in libfolks for it to
+ * not be hideously complex. (bgo#627399) */
+ GList *personas, *l;
+ FolksPresenceType presence_type = FOLKS_PRESENCE_TYPE_UNSET;
+
+ personas = folks_individual_get_personas (priv->individual);
+ for (l = personas; l != NULL; l = l->next)
+ {
+ FolksPresence *presence;
+
+ /* We only want personas which implement FolksPresence */
+ if (!FOLKS_IS_PRESENCE (l->data))
+ continue;
+
+ presence = FOLKS_PRESENCE (l->data);
+
+ if (folks_presence_typecmp (folks_presence_get_presence_type (presence),
+ presence_type) > 0
+ && TPF_IS_PERSONA (presence))
+ {
+ presence_type = folks_presence_get_presence_type (presence);
+ tp_contact = tpf_persona_get_contact (TPF_PERSONA (l->data));
+ }
+ }
+ }
+
+ if (tp_contact != NULL)
+ {
+ priv->contact = tp_contact;
+ g_object_add_weak_pointer (G_OBJECT (tp_contact),
+ (gpointer *) &priv->contact);
+ }
+}
+
typedef struct {
EmpathyIndividualWidget *widget; /* weak */
TpContact *contact; /* owned */
@@ -324,18 +377,6 @@ details_request_cb (TpContact *contact,
tp_clear_object (&priv->details_cancellable);
- /* We need a (weak) pointer to the contact so that we can disconnect the
- * signal handler on deconstruction. */
- if (priv->contact_info_contact != NULL)
- {
- g_object_remove_weak_pointer (G_OBJECT (priv->contact_info_contact),
- (gpointer *) &priv->contact_info_contact);
- }
-
- priv->contact_info_contact = contact;
- g_object_add_weak_pointer (G_OBJECT (contact),
- (gpointer *) &priv->contact_info_contact);
-
g_signal_connect (contact, "notify::contact-info",
(GCallback) details_notify_cb, self);
}
@@ -387,28 +428,10 @@ details_update (EmpathyIndividualWidget *self)
gtk_widget_hide (priv->vbox_details);
- if (priv->individual != NULL)
- {
- /* FIXME: We take the first TpContact we find and only use its details.
- * It would be a lot better if we would get the details for every
- * TpContact in the Individual and merge them all, but that requires
- * vCard support in libfolks for it to not be hideously complex.
- * (bgo#627399) */
- GList *personas, *l;
+ if (priv->contact == NULL)
+ update_weak_contact (self);
- personas = folks_individual_get_personas (priv->individual);
- for (l = personas; l != NULL; l = l->next)
- {
- if (TPF_IS_PERSONA (l->data))
- {
- tp_contact = tpf_persona_get_contact (TPF_PERSONA (l->data));
- if (tp_contact != NULL)
- break;
- }
- }
- }
-
- if (tp_contact != NULL)
+ if (priv->contact != NULL)
{
GQuark features[] = { TP_CONNECTION_FEATURE_CONTACT_INFO, 0 };
TpConnection *connection;
@@ -417,7 +440,7 @@ details_update (EmpathyIndividualWidget *self)
data = g_slice_new (DetailsData);
data->widget = self;
g_object_add_weak_pointer (G_OBJECT (self), (gpointer *) &data->widget);
- data->contact = g_object_ref (tp_contact);
+ data->contact = g_object_ref (priv->contact);
/* First, make sure the CONTACT_INFO feature is ready on the connection */
connection = tp_contact_get_connection (tp_contact);
@@ -785,6 +808,64 @@ location_update (EmpathyIndividualWidget *self)
gtk_widget_show (priv->vbox_location);
}
+static void
+client_types_notify_cb (TpContact *contact,
+ GParamSpec *pspec,
+ EmpathyIndividualWidget *self)
+{
+ client_types_update (self);
+}
+
+static void
+client_types_update (EmpathyIndividualWidget *self)
+{
+ EmpathyIndividualWidgetPriv *priv = GET_PRIV (self);
+ const gchar * const *types;
+
+ if (!(priv->flags & EMPATHY_INDIVIDUAL_WIDGET_SHOW_CLIENT_TYPES) ||
+ priv->individual == NULL)
+ {
+ gtk_widget_hide (priv->hbox_client_types);
+ return;
+ }
+
+ if (priv->contact == NULL)
+ update_weak_contact (self);
+
+ /* let's try that again... */
+ if (priv->contact == NULL)
+ return;
+
+ types = tp_contact_get_client_types (priv->contact);
+
+ if (types != NULL
+ && g_strv_length ((gchar **) types) > 0
+ && !tp_strdiff (types[0], "phone"))
+ {
+ gtk_widget_show (priv->hbox_client_types);
+ }
+ else
+ {
+ gtk_widget_hide (priv->hbox_client_types);
+ }
+
+ g_signal_connect (priv->contact, "notify::client-types",
+ (GCallback) client_types_notify_cb, self);
+}
+
+static void
+remove_weak_contact (EmpathyIndividualWidget *self)
+{
+ EmpathyIndividualWidgetPriv *priv = GET_PRIV (self);
+
+ if (priv->contact == NULL)
+ return;
+
+ g_object_remove_weak_pointer (G_OBJECT (priv->contact),
+ (gpointer *) &priv->contact);
+ priv->contact = NULL;
+}
+
static EmpathyAvatar *
persona_dup_avatar (FolksPersona *persona)
{
@@ -1741,14 +1822,8 @@ remove_individual (EmpathyIndividualWidget *self)
remove_persona (self, FOLKS_PERSONA (l->data));
individual_table_destroy (self);
- if (priv->contact_info_contact != NULL)
- {
- g_signal_handlers_disconnect_by_func (priv->contact_info_contact,
- details_notify_cb, self);
- g_object_remove_weak_pointer (G_OBJECT (priv->contact_info_contact),
- (gpointer *) &priv->contact_info_contact);
- priv->contact_info_contact = NULL;
- }
+ if (priv->contact != NULL)
+ remove_weak_contact (self);
tp_clear_object (&priv->individual);
}
@@ -1847,6 +1922,7 @@ empathy_individual_widget_init (EmpathyIndividualWidget *self)
"vbox_details", &priv->vbox_details,
"table_details", &priv->table_details,
"hbox_details_requested", &priv->hbox_details_requested,
+ "hbox_client_types", &priv->hbox_client_types,
NULL);
g_free (filename);
@@ -2080,4 +2156,5 @@ empathy_individual_widget_set_individual (EmpathyIndividualWidget *self,
groups_update (self);
details_update (self);
location_update (self);
+ client_types_update (self);
}
diff --git a/libempathy-gtk/empathy-individual-widget.h b/libempathy-gtk/empathy-individual-widget.h
index a968d14f3..24a7f3dc3 100644
--- a/libempathy-gtk/empathy-individual-widget.h
+++ b/libempathy-gtk/empathy-individual-widget.h
@@ -63,6 +63,7 @@ typedef enum
EMPATHY_INDIVIDUAL_WIDGET_SHOW_LOCATION = 1 << 4,
EMPATHY_INDIVIDUAL_WIDGET_SHOW_DETAILS = 1 << 5,
EMPATHY_INDIVIDUAL_WIDGET_SHOW_PERSONAS = 1 << 6,
+ EMPATHY_INDIVIDUAL_WIDGET_SHOW_CLIENT_TYPES = 1 << 7,
} EmpathyIndividualWidgetFlags;
#define EMPATHY_TYPE_INDIVIDUAL_WIDGET (empathy_individual_widget_get_type ())
diff --git a/libempathy-gtk/empathy-individual-widget.ui b/libempathy-gtk/empathy-individual-widget.ui
index 5fb382363..dcfc83a47 100644
--- a/libempathy-gtk/empathy-individual-widget.ui
+++ b/libempathy-gtk/empathy-individual-widget.ui
@@ -88,6 +88,35 @@
</packing>
</child>
<child>
+ <object class="GtkHBox" id="hbox_client_types">
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkImage" id="image_phone">
+ <property name="visible">True</property>
+ <property name="icon_name">phone</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Online from a phone or mobile device</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
<object class="EmpathyGroupsWidget" id="groups_widget"/>
<packing>
<property name="position">2</property>