diff options
Diffstat (limited to 'libempathy-gtk')
-rw-r--r-- | libempathy-gtk/empathy-conf.h | 1 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-dialogs.c | 3 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-list-store.c | 220 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-list-store.h | 9 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-list-view.c | 129 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-list-view.h | 3 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-menu.c | 49 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-menu.h | 1 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-selector-dialog.c | 76 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-selector-dialog.h | 7 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-widget.c | 86 | ||||
-rw-r--r-- | libempathy-gtk/empathy-contact-widget.h | 1 | ||||
-rw-r--r-- | libempathy-gtk/empathy-string-parser.c | 8 | ||||
-rw-r--r-- | libempathy-gtk/empathy-ui-utils.c | 22 | ||||
-rw-r--r-- | libempathy-gtk/empathy-ui-utils.h | 3 |
15 files changed, 529 insertions, 89 deletions
diff --git a/libempathy-gtk/empathy-conf.h b/libempathy-gtk/empathy-conf.h index 2a74c1da5..21e3b3810 100644 --- a/libempathy-gtk/empathy-conf.h +++ b/libempathy-gtk/empathy-conf.h @@ -82,6 +82,7 @@ struct _EmpathyConfClass { #define EMPATHY_PREFS_USE_CONN EMPATHY_PREFS_PATH "/use_conn" #define EMPATHY_PREFS_AUTOCONNECT EMPATHY_PREFS_PATH "/autoconnect" #define EMPATHY_PREFS_IMPORT_ASKED EMPATHY_PREFS_PATH "/import_asked" +#define EMPATHY_PREFS_BUTTERFLY_LOGS_MIGRATED EMPATHY_PREFS_PATH "/butterfly_logs_migrated" #define EMPATHY_PREFS_FILE_TRANSFER_DEFAULT_FOLDER EMPATHY_PREFS_PATH "/file_transfer/default_folder" #define EMPATHY_PREFS_LOCATION_PUBLISH EMPATHY_PREFS_PATH "/location/publish" #define EMPATHY_PREFS_LOCATION_RESOURCE_NETWORK EMPATHY_PREFS_PATH "/location/resource_network" diff --git a/libempathy-gtk/empathy-contact-dialogs.c b/libempathy-gtk/empathy-contact-dialogs.c index 52e43e163..fed8d04cb 100644 --- a/libempathy-gtk/empathy-contact-dialogs.c +++ b/libempathy-gtk/empathy-contact-dialogs.c @@ -261,7 +261,8 @@ empathy_contact_edit_dialog_show (EmpathyContact *contact, /* Contact info widget */ contact_widget = empathy_contact_widget_new (contact, EMPATHY_CONTACT_WIDGET_EDIT_ALIAS | - EMPATHY_CONTACT_WIDGET_EDIT_GROUPS); + EMPATHY_CONTACT_WIDGET_EDIT_GROUPS | + EMPATHY_CONTACT_WIDGET_EDIT_FAVOURITE); gtk_container_set_border_width (GTK_CONTAINER (contact_widget), 8); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), contact_widget, diff --git a/libempathy-gtk/empathy-contact-list-store.c b/libempathy-gtk/empathy-contact-list-store.c index 53a0934b3..9feb4461f 100644 --- a/libempathy-gtk/empathy-contact-list-store.c +++ b/libempathy-gtk/empathy-contact-list-store.c @@ -28,6 +28,7 @@ #include <string.h> #include <glib.h> +#include <glib/gi18n-lib.h> #include <gtk/gtk.h> #include <telepathy-glib/util.h> @@ -105,6 +106,10 @@ static void contact_list_store_members_changed_cb (EmpathyCon gchar *message, gboolean is_member, EmpathyContactListStore *store); +static void contact_list_store_favourites_changed_cb (EmpathyContactList *list_iface, + EmpathyContact *contact, + gboolean is_favourite, + EmpathyContactListStore *store); static void contact_list_store_member_renamed_cb (EmpathyContactList *list_iface, EmpathyContact *old_contact, EmpathyContact *new_contact, @@ -142,7 +147,8 @@ static void contact_list_store_get_group (EmpathyCon const gchar *name, GtkTreeIter *iter_group_to_set, GtkTreeIter *iter_separator_to_set, - gboolean *created); + gboolean *created, + gboolean is_fake_group); static gint contact_list_store_state_sort_func (GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b, @@ -192,6 +198,10 @@ contact_list_store_iface_setup (gpointer user_data) G_CALLBACK (contact_list_store_members_changed_cb), store); g_signal_connect (priv->list, + "favourites-changed", + G_CALLBACK (contact_list_store_favourites_changed_cb), + store); + g_signal_connect (priv->list, "groups-changed", G_CALLBACK (contact_list_store_groups_changed_cb), store); @@ -338,6 +348,9 @@ contact_list_store_dispose (GObject *object) G_CALLBACK (contact_list_store_members_changed_cb), object); g_signal_handlers_disconnect_by_func (priv->list, + G_CALLBACK (contact_list_store_favourites_changed_cb), + object); + g_signal_handlers_disconnect_by_func (priv->list, G_CALLBACK (contact_list_store_groups_changed_cb), object); g_object_unref (priv->list); @@ -715,11 +728,13 @@ empathy_contact_list_store_row_separator_func (GtkTreeModel *model, gchar * empathy_contact_list_store_get_parent_group (GtkTreeModel *model, GtkTreePath *path, - gboolean *path_is_group) + gboolean *path_is_group, + gboolean *is_fake_group) { GtkTreeIter parent_iter, iter; gchar *name = NULL; gboolean is_group; + gboolean fake; g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL); @@ -749,6 +764,7 @@ empathy_contact_list_store_get_parent_group (GtkTreeModel *model, gtk_tree_model_get (model, &iter, EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group, EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name, + EMPATHY_CONTACT_LIST_STORE_COL_IS_FAKE_GROUP, &fake, -1); if (!is_group) { g_free (name); @@ -760,6 +776,9 @@ empathy_contact_list_store_get_parent_group (GtkTreeModel *model, *path_is_group = TRUE; } + if (is_fake_group != NULL) + *is_fake_group = fake; + return name; } @@ -824,6 +843,7 @@ contact_list_store_setup (EmpathyContactListStore *store) G_TYPE_BOOLEAN, /* Can make audio calls */ G_TYPE_BOOLEAN, /* Can make video calls */ EMPATHY_TYPE_CONTACT_LIST_FLAGS, /* Flags */ + G_TYPE_BOOLEAN, /* Is a fake group */ }; priv = GET_PRIV (store); @@ -917,6 +937,25 @@ contact_list_store_members_changed_cb (EmpathyContactList *list_iface, } static void +contact_list_store_favourites_changed_cb (EmpathyContactList *list_iface, + EmpathyContact *contact, + gboolean is_favourite, + EmpathyContactListStore *store) +{ + EmpathyContactListStorePriv *priv; + + priv = GET_PRIV (store); + + DEBUG ("Contact %s (%d) is %s a favourite", + empathy_contact_get_id (contact), + empathy_contact_get_handle (contact), + is_favourite ? "now" : "no longer"); + + contact_list_store_remove_contact (store, contact); + contact_list_store_add_contact (store, contact); +} + +static void contact_list_store_member_renamed_cb (EmpathyContactList *list_iface, EmpathyContact *old_contact, EmpathyContact *new_contact, @@ -969,6 +1008,27 @@ contact_list_store_groups_changed_cb (EmpathyContactList *list_iface, } static void +add_contact_to_store (GtkTreeStore *store, + GtkTreeIter *iter, + EmpathyContact *contact, + EmpathyContactListFlags flags) +{ + gtk_tree_store_set (store, iter, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, empathy_contact_get_name (contact), + EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, contact, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, FALSE, + EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, FALSE, + EMPATHY_CONTACT_LIST_STORE_COL_CAN_AUDIO_CALL, + empathy_contact_get_capabilities (contact) & + EMPATHY_CAPABILITIES_AUDIO, + EMPATHY_CONTACT_LIST_STORE_COL_CAN_VIDEO_CALL, + empathy_contact_get_capabilities (contact) & + EMPATHY_CAPABILITIES_VIDEO, + EMPATHY_CONTACT_LIST_STORE_COL_FLAGS, flags, + -1); +} + +static void contact_list_store_add_contact (EmpathyContactListStore *store, EmpathyContact *contact) { @@ -994,8 +1054,18 @@ contact_list_store_add_contact (EmpathyContactListStore *store, flags = empathy_contact_manager_get_flags_for_connection ( EMPATHY_CONTACT_MANAGER (priv->list), connection); } - /* If no groups just add it at the top level. */ if (!groups) { +#if HAVE_FAVOURITE_CONTACTS + GtkTreeIter iter_group; + + contact_list_store_get_group (store, EMPATHY_CONTACT_LIST_STORE_UNGROUPED, + &iter_group, NULL, NULL, TRUE); + + gtk_tree_store_insert_after (GTK_TREE_STORE (store), &iter, + &iter_group, NULL); +#else + /* FIXME: remove this in 2.31.x */ + /* If no groups just add it at the top level. */ GtkTreeModel *model = GTK_TREE_MODEL (store); if (gtk_tree_model_get_iter_first (model, &iter)) do { @@ -1014,48 +1084,41 @@ contact_list_store_add_contact (EmpathyContactListStore *store, } while (gtk_tree_model_iter_next (model, &iter)); gtk_tree_store_append (GTK_TREE_STORE (store), &iter, NULL); - gtk_tree_store_set (GTK_TREE_STORE (store), &iter, - EMPATHY_CONTACT_LIST_STORE_COL_NAME, empathy_contact_get_name (contact), - EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, contact, - EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, FALSE, - EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, FALSE, - EMPATHY_CONTACT_LIST_STORE_COL_CAN_AUDIO_CALL, - empathy_contact_get_capabilities (contact) & - EMPATHY_CAPABILITIES_AUDIO, - EMPATHY_CONTACT_LIST_STORE_COL_CAN_VIDEO_CALL, - empathy_contact_get_capabilities (contact) & - EMPATHY_CAPABILITIES_VIDEO, - EMPATHY_CONTACT_LIST_STORE_COL_FLAGS, flags, - -1); +#endif + + add_contact_to_store (GTK_TREE_STORE (store), &iter, contact, flags); } /* Else add to each group. */ for (l = groups; l; l = l->next) { GtkTreeIter iter_group; - contact_list_store_get_group (store, l->data, &iter_group, NULL, NULL); + contact_list_store_get_group (store, l->data, &iter_group, NULL, NULL, FALSE); gtk_tree_store_insert_after (GTK_TREE_STORE (store), &iter, &iter_group, NULL); - gtk_tree_store_set (GTK_TREE_STORE (store), &iter, - EMPATHY_CONTACT_LIST_STORE_COL_NAME, empathy_contact_get_name (contact), - EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, contact, - EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, FALSE, - EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, FALSE, - EMPATHY_CONTACT_LIST_STORE_COL_CAN_AUDIO_CALL, - empathy_contact_get_capabilities (contact) & - EMPATHY_CAPABILITIES_AUDIO, - EMPATHY_CONTACT_LIST_STORE_COL_CAN_VIDEO_CALL, - empathy_contact_get_capabilities (contact) & - EMPATHY_CAPABILITIES_VIDEO, - EMPATHY_CONTACT_LIST_STORE_COL_FLAGS, flags, - -1); + + add_contact_to_store (GTK_TREE_STORE (store), &iter, contact, flags); g_free (l->data); } g_list_free (groups); - contact_list_store_contact_update (store, contact); +#ifdef HAVE_FAVOURITE_CONTACTS + if (empathy_contact_list_is_favourite (priv->list, contact)) { + /* Add contact to the fake 'Favorites' group */ + GtkTreeIter iter_group; + + contact_list_store_get_group (store, EMPATHY_CONTACT_LIST_STORE_FAVORITE, + &iter_group, NULL, NULL, TRUE); + + gtk_tree_store_insert_after (GTK_TREE_STORE (store), &iter, + &iter_group, NULL); + add_contact_to_store (GTK_TREE_STORE (store), &iter, contact, flags); + } +#endif + + contact_list_store_contact_update (store, contact); } static void @@ -1389,7 +1452,8 @@ contact_list_store_get_group (EmpathyContactListStore *store, const gchar *name, GtkTreeIter *iter_group_to_set, GtkTreeIter *iter_separator_to_set, - gboolean *created) + gboolean *created, + gboolean is_fake_group) { EmpathyContactListStorePriv *priv; GtkTreeModel *model; @@ -1420,6 +1484,7 @@ contact_list_store_get_group (EmpathyContactListStore *store, EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, TRUE, EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, FALSE, EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, FALSE, + EMPATHY_CONTACT_LIST_STORE_COL_IS_FAKE_GROUP, is_fake_group, -1); if (iter_group_to_set) { @@ -1462,44 +1527,77 @@ contact_list_store_get_group (EmpathyContactListStore *store, } static gint +compare_separator_and_groups (gboolean is_separator_a, + gboolean is_separator_b, + const gchar *name_a, + const gchar *name_b, + EmpathyContact *contact_a, + EmpathyContact *contact_b, + gboolean fake_group_a, + gboolean fake_group_b) +{ + if (is_separator_a || is_separator_b) { + /* We have at least one separator */ + if (is_separator_a) { + return -1; + } else if (is_separator_b) { + return 1; + } + } + + /* One group and one contact */ + if (!contact_a && contact_b) { + return 1; + } else if (contact_a && !contact_b) { + return -1; + } else if (!contact_a && !contact_b) { + /* Two groups. The 'Ungrouped' fake group is display at the bottom of the + * contact list and the 'Favorites' at the top. */ + if (fake_group_a && !tp_strdiff (name_a, EMPATHY_CONTACT_LIST_STORE_UNGROUPED)) + return 1; + else if (fake_group_b && !tp_strdiff (name_b, EMPATHY_CONTACT_LIST_STORE_UNGROUPED)) + return -1; + else if (fake_group_a && !tp_strdiff (name_a, EMPATHY_CONTACT_LIST_STORE_FAVORITE)) + return -1; + else if (fake_group_b && !tp_strdiff (name_b, EMPATHY_CONTACT_LIST_STORE_FAVORITE)) + return 1; + else + return g_utf8_collate (name_a, name_b); + } + + /* Two contacts, ordering depends of the sorting policy */ + return 0; +} + +static gint contact_list_store_state_sort_func (GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b, gpointer user_data) { - gint ret_val = 0; + gint ret_val; gchar *name_a, *name_b; gboolean is_separator_a, is_separator_b; EmpathyContact *contact_a, *contact_b; + gboolean fake_group_a, fake_group_b; gtk_tree_model_get (model, iter_a, EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_a, EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_a, EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_a, + EMPATHY_CONTACT_LIST_STORE_COL_IS_FAKE_GROUP, &fake_group_a, -1); gtk_tree_model_get (model, iter_b, EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_b, EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_b, EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_b, + EMPATHY_CONTACT_LIST_STORE_COL_IS_FAKE_GROUP, &fake_group_b, -1); - /* Separator or group? */ - if (is_separator_a || is_separator_b) { - if (is_separator_a) { - ret_val = -1; - } else if (is_separator_b) { - ret_val = 1; - } - } else if (!contact_a && contact_b) { - ret_val = 1; - } else if (contact_a && !contact_b) { - ret_val = -1; - } else if (!contact_a && !contact_b) { - /* Handle groups */ - ret_val = g_utf8_collate (name_a, name_b); - } + ret_val = compare_separator_and_groups (is_separator_a, is_separator_b, + name_a, name_b, contact_a, contact_b, fake_group_a, fake_group_b); - if (ret_val) { + if (ret_val != 0) { goto free_and_out; } @@ -1538,38 +1636,28 @@ contact_list_store_name_sort_func (GtkTreeModel *model, { gchar *name_a, *name_b; EmpathyContact *contact_a, *contact_b; - gboolean is_separator_a, is_separator_b; + gboolean is_separator_a = FALSE, is_separator_b = FALSE; gint ret_val; + gboolean fake_group_a, fake_group_b; gtk_tree_model_get (model, iter_a, EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_a, EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_a, EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_a, + EMPATHY_CONTACT_LIST_STORE_COL_IS_FAKE_GROUP, &fake_group_a, -1); gtk_tree_model_get (model, iter_b, EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_b, EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_b, EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_b, + EMPATHY_CONTACT_LIST_STORE_COL_IS_FAKE_GROUP, &fake_group_b, -1); - /* If contact is NULL it means it's a group. */ + ret_val = compare_separator_and_groups (is_separator_a, is_separator_b, + name_a, name_b, contact_a, contact_b, fake_group_a, fake_group_b); - if (is_separator_a || is_separator_b) { - if (is_separator_a) { - ret_val = -1; - } else if (is_separator_b) { - ret_val = 1; - } - } else if (!contact_a && contact_b) { - ret_val = 1; - } else if (contact_a && !contact_b) { - ret_val = -1; - } else { + if (ret_val == 0) ret_val = g_utf8_collate (name_a, name_b); - } - - g_free (name_a); - g_free (name_b); if (contact_a) { g_object_unref (contact_a); diff --git a/libempathy-gtk/empathy-contact-list-store.h b/libempathy-gtk/empathy-contact-list-store.h index afefd28cf..1b36ea651 100644 --- a/libempathy-gtk/empathy-contact-list-store.h +++ b/libempathy-gtk/empathy-contact-list-store.h @@ -63,9 +63,13 @@ typedef enum { EMPATHY_CONTACT_LIST_STORE_COL_CAN_AUDIO_CALL, EMPATHY_CONTACT_LIST_STORE_COL_CAN_VIDEO_CALL, EMPATHY_CONTACT_LIST_STORE_COL_FLAGS, - EMPATHY_CONTACT_LIST_STORE_COL_COUNT + EMPATHY_CONTACT_LIST_STORE_COL_IS_FAKE_GROUP, + EMPATHY_CONTACT_LIST_STORE_COL_COUNT, } EmpathyContactListStoreCol; +#define EMPATHY_CONTACT_LIST_STORE_UNGROUPED _("Ungrouped") +#define EMPATHY_CONTACT_LIST_STORE_FAVORITE _("Favorite People") + struct _EmpathyContactListStore { GtkTreeStore parent; gpointer priv; @@ -101,7 +105,8 @@ gboolean empathy_contact_list_store_row_separator_func (GtkTre gpointer data); gchar * empathy_contact_list_store_get_parent_group (GtkTreeModel *model, GtkTreePath *path, - gboolean *path_is_group); + gboolean *path_is_group, + gboolean *is_fake_group); gboolean empathy_contact_list_store_search_equal_func (GtkTreeModel *model, gint column, const gchar *key, diff --git a/libempathy-gtk/empathy-contact-list-view.c b/libempathy-gtk/empathy-contact-list-view.c index e8fddf0ac..8e8342275 100644 --- a/libempathy-gtk/empathy-contact-list-view.c +++ b/libempathy-gtk/empathy-contact-list-view.c @@ -157,6 +157,11 @@ contact_list_view_query_tooltip_cb (EmpathyContactListView *view, } running++; + /* Don't show the tooltip if there's already a popup menu */ + if (gtk_menu_get_for_attach_widget (GTK_WIDGET (view)) != NULL) { + goto OUT; + } + if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (view), &x, &y, keyboard_mode, &model, &path, &iter)) { @@ -235,6 +240,21 @@ contact_list_view_drag_got_contact (EmpathyTpContactFactory *factory, data->old_group, data->new_group); list = empathy_contact_list_store_get_list_iface (priv->store); + + if (!tp_strdiff (data->new_group, EMPATHY_CONTACT_LIST_STORE_FAVORITE)) { + /* Mark contact as favourite */ + empathy_contact_list_add_to_favourites (list, contact); + return; + } + + if (!tp_strdiff (data->old_group, EMPATHY_CONTACT_LIST_STORE_FAVORITE)) { + /* Remove contact as favourite */ + empathy_contact_list_remove_from_favourites (list, contact); + /* Don't try to remove it */ + g_free (data->old_group); + data->old_group = NULL; + } + if (data->new_group) { empathy_contact_list_add_to_group (list, contact, data->new_group); } @@ -244,6 +264,27 @@ contact_list_view_drag_got_contact (EmpathyTpContactFactory *factory, } static gboolean +group_can_be_modified (const gchar *name, + gboolean is_fake_group, + gboolean adding) +{ + /* Real groups can always be modified */ + if (!is_fake_group) + return TRUE; + + /* The favorite fake group can be modified so users can + * add/remove favorites using DnD */ + if (!tp_strdiff (name, EMPATHY_CONTACT_LIST_STORE_FAVORITE)) + return TRUE; + + /* We can remove contacts from the 'ungrouped' fake group */ + if (!adding && !tp_strdiff (name, EMPATHY_CONTACT_LIST_STORE_UNGROUPED)) + return TRUE; + + return FALSE; +} + +static gboolean contact_list_view_contact_drag_received (GtkWidget *view, GdkDragContext *context, GtkTreeModel *model, @@ -263,23 +304,30 @@ contact_list_view_contact_drag_received (GtkWidget *view, gchar *new_group = NULL; gchar *old_group = NULL; gboolean success = TRUE; + gboolean new_group_is_fake, old_group_is_fake = TRUE; priv = GET_PRIV (view); sel_data = (const gchar *) gtk_selection_data_get_data (selection); new_group = empathy_contact_list_store_get_parent_group (model, - path, NULL); + path, NULL, &new_group_is_fake); + + if (!group_can_be_modified (new_group, new_group_is_fake, TRUE)) + return FALSE; /* Get source group information. */ if (priv->drag_row) { source_path = gtk_tree_row_reference_get_path (priv->drag_row); if (source_path) { old_group = empathy_contact_list_store_get_parent_group ( - model, source_path, NULL); + model, source_path, NULL, &old_group_is_fake); gtk_tree_path_free (source_path); } } + if (!group_can_be_modified (old_group, old_group_is_fake, FALSE)) + return FALSE; + if (!tp_strdiff (old_group, new_group)) { g_free (new_group); g_free (old_group); @@ -681,6 +729,10 @@ contact_list_view_popup_menu_idle_cb (gpointer user_data) } if (menu) { + g_signal_connect (menu, "deactivate", + G_CALLBACK (gtk_menu_detach), NULL); + gtk_menu_attach_to_widget (GTK_MENU (menu), + GTK_WIDGET (data->view), NULL); gtk_widget_show (menu); gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, @@ -796,6 +848,10 @@ contact_list_view_call_activated_cb ( gtk_menu_shell_append (shell, item); gtk_widget_show (item); + g_signal_connect (menu, "deactivate", + G_CALLBACK (gtk_menu_detach), NULL); + gtk_menu_attach_to_widget (GTK_MENU (menu), + GTK_WIDGET (view), NULL); gtk_widget_show (menu); gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, event->button, event->time); @@ -868,6 +924,43 @@ contact_list_view_pixbuf_cell_data_func (GtkTreeViewColumn *tree_column, } static void +contact_list_view_group_icon_cell_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + EmpathyContactListView *view) +{ + GdkPixbuf *pixbuf = NULL; + gboolean is_group; + gchar *name; + + gtk_tree_model_get (model, iter, + EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group, + EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name, + -1); + + if (!is_group) + goto out; + + if (tp_strdiff (name, EMPATHY_CONTACT_LIST_STORE_FAVORITE)) + goto out; + + pixbuf = empathy_pixbuf_from_icon_name ("emblem-favorite", + GTK_ICON_SIZE_MENU); + +out: + g_object_set (cell, + "visible", pixbuf != NULL, + "pixbuf", pixbuf, + NULL); + + if (pixbuf != NULL) + g_object_unref (pixbuf); + + g_free (name); +} + +static void contact_list_view_audio_call_cell_data_func ( GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, @@ -1104,6 +1197,22 @@ contact_list_view_setup (EmpathyContactListView *view) "visible", FALSE, NULL); + /* Group icon */ + cell = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (col, cell, FALSE); + gtk_tree_view_column_set_cell_data_func ( + col, cell, + (GtkTreeCellDataFunc) contact_list_view_group_icon_cell_data_func, + view, NULL); + + g_object_set (cell, + "xpad", 0, + "ypad", 0, + "visible", FALSE, + "width", 16, + "height", 16, + NULL); + /* Name */ cell = empathy_cell_renderer_text_new (); gtk_tree_view_column_pack_start (col, cell, TRUE); @@ -1446,7 +1555,8 @@ empathy_contact_list_view_get_flags (EmpathyContactListView *view) } gchar * -empathy_contact_list_view_get_selected_group (EmpathyContactListView *view) +empathy_contact_list_view_get_selected_group (EmpathyContactListView *view, + gboolean *is_fake_group) { EmpathyContactListViewPriv *priv; GtkTreeSelection *selection; @@ -1454,6 +1564,7 @@ empathy_contact_list_view_get_selected_group (EmpathyContactListView *view) GtkTreeModel *model; gboolean is_group; gchar *name; + gboolean fake; g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL); @@ -1467,6 +1578,7 @@ empathy_contact_list_view_get_selected_group (EmpathyContactListView *view) gtk_tree_model_get (model, &iter, EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group, EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name, + EMPATHY_CONTACT_LIST_STORE_COL_IS_FAKE_GROUP, &fake, -1); if (!is_group) { @@ -1474,6 +1586,9 @@ empathy_contact_list_view_get_selected_group (EmpathyContactListView *view) return NULL; } + if (is_fake_group != NULL) + *is_fake_group = fake; + return name; } @@ -1510,7 +1625,7 @@ contact_list_view_group_remove_activate_cb (GtkMenuItem *menuitem, EmpathyContactListViewPriv *priv = GET_PRIV (view); gchar *group; - group = empathy_contact_list_view_get_selected_group (view); + group = empathy_contact_list_view_get_selected_group (view, NULL); if (group) { gchar *text; GtkWindow *parent; @@ -1538,6 +1653,7 @@ empathy_contact_list_view_get_group_menu (EmpathyContactListView *view) GtkWidget *menu; GtkWidget *item; GtkWidget *image; + gboolean is_fake_group; g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL); @@ -1546,8 +1662,9 @@ empathy_contact_list_view_get_group_menu (EmpathyContactListView *view) return NULL; } - group = empathy_contact_list_view_get_selected_group (view); - if (!group) { + group = empathy_contact_list_view_get_selected_group (view, &is_fake_group); + if (!group || is_fake_group) { + /* We can't alter fake groups */ return NULL; } diff --git a/libempathy-gtk/empathy-contact-list-view.h b/libempathy-gtk/empathy-contact-list-view.h index 17de5239b..41b968dbf 100644 --- a/libempathy-gtk/empathy-contact-list-view.h +++ b/libempathy-gtk/empathy-contact-list-view.h @@ -73,7 +73,8 @@ EmpathyContactListView * empathy_contact_list_view_new (Empathy EmpathyContactFeatureFlags contact_features); EmpathyContact * empathy_contact_list_view_dup_selected (EmpathyContactListView *view); EmpathyContactListFlags empathy_contact_list_view_get_flags (EmpathyContactListView *view); -gchar * empathy_contact_list_view_get_selected_group (EmpathyContactListView *view); +gchar * empathy_contact_list_view_get_selected_group (EmpathyContactListView *view, + gboolean *is_fake_group); GtkWidget * empathy_contact_list_view_get_contact_menu (EmpathyContactListView *view); GtkWidget * empathy_contact_list_view_get_group_menu (EmpathyContactListView *view); diff --git a/libempathy-gtk/empathy-contact-menu.c b/libempathy-gtk/empathy-contact-menu.c index 54bb4b286..23522d40e 100644 --- a/libempathy-gtk/empathy-contact-menu.c +++ b/libempathy-gtk/empathy-contact-menu.c @@ -134,6 +134,13 @@ empathy_contact_menu_new (EmpathyContact *contact, gtk_widget_show (item); } +#if HAVE_FAVOURITE_CONTACTS + /* Favorite checkbox */ + item = empathy_contact_favourite_menu_item_new (contact); + gtk_menu_shell_append (shell, item); + gtk_widget_show (item); +#endif + return menu; } @@ -396,6 +403,48 @@ empathy_contact_share_my_desktop_menu_item_new (EmpathyContact *contact) return item; } +#if HAVE_FAVOURITE_CONTACTS +static void +favourite_menu_item_toggled_cb (GtkCheckMenuItem *item, + EmpathyContact *contact) +{ + EmpathyContactManager *manager; + EmpathyContactList *list; + + manager = empathy_contact_manager_dup_singleton (); + list = EMPATHY_CONTACT_LIST (manager); + + if (gtk_check_menu_item_get_active (item)) { + empathy_contact_list_add_to_favourites (list, contact); + } else { + empathy_contact_list_remove_from_favourites (list, contact); + } + + g_object_unref (manager); +} + +GtkWidget * +empathy_contact_favourite_menu_item_new (EmpathyContact *contact) +{ + GtkWidget *item; + EmpathyContactManager *manager; + + item = gtk_check_menu_item_new_with_label (_("Favorite")); + + manager = empathy_contact_manager_dup_singleton (); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), + empathy_contact_list_is_favourite (EMPATHY_CONTACT_LIST (manager), + contact)); + + g_signal_connect (item, "toggled", + G_CALLBACK (favourite_menu_item_toggled_cb), + contact); + + g_object_unref (manager); + return item; +} +#endif + static void contact_info_menu_item_activate_cb (EmpathyContact *contact) { diff --git a/libempathy-gtk/empathy-contact-menu.h b/libempathy-gtk/empathy-contact-menu.h index 2c623e6ba..25f18e03f 100644 --- a/libempathy-gtk/empathy-contact-menu.h +++ b/libempathy-gtk/empathy-contact-menu.h @@ -50,6 +50,7 @@ GtkWidget * empathy_contact_edit_menu_item_new (EmpathyContact *cont GtkWidget * empathy_contact_invite_menu_item_new (EmpathyContact *contact); GtkWidget * empathy_contact_file_transfer_menu_item_new (EmpathyContact *contact); GtkWidget * empathy_contact_share_my_desktop_menu_item_new (EmpathyContact *contact); +GtkWidget * empathy_contact_favourite_menu_item_new (EmpathyContact *contact); G_END_DECLS diff --git a/libempathy-gtk/empathy-contact-selector-dialog.c b/libempathy-gtk/empathy-contact-selector-dialog.c index 3c59061ae..c09b25cef 100644 --- a/libempathy-gtk/empathy-contact-selector-dialog.c +++ b/libempathy-gtk/empathy-contact-selector-dialog.c @@ -55,6 +55,7 @@ struct _EmpathyContactSelectorDialogPriv { GtkWidget *account_chooser; GtkWidget *entry_id; EmpathyContactManager *contact_manager; + TpAccount *filter_account; gboolean show_account_chooser; }; @@ -65,7 +66,8 @@ struct _EmpathyContactSelectorDialogPriv { enum { PROP_0, - PROP_SHOW_ACCOUNT_CHOOSER + PROP_SHOW_ACCOUNT_CHOOSER, + PROP_FILTER_ACCOUNT }; enum { @@ -103,8 +105,25 @@ contact_selector_dialog_account_changed_cb (GtkWidget *widget, } else { - members = empathy_contact_list_get_members ( - EMPATHY_CONTACT_LIST (priv->contact_manager)); + if (priv->filter_account != NULL) + { + EmpathyTpContactList *contact_list; + + connection = tp_account_get_connection (priv->filter_account); + if (connection == NULL) + return; + + contact_list = empathy_contact_manager_get_list ( + priv->contact_manager, connection); + + members = empathy_contact_list_get_members ( + EMPATHY_CONTACT_LIST (contact_list)); + } + else + { + members = empathy_contact_list_get_members ( + EMPATHY_CONTACT_LIST (priv->contact_manager)); + } } /* Add members to the completion */ @@ -361,6 +380,11 @@ empathy_contact_selector_dialog_get_property (GObject *self, empathy_contact_selector_dialog_get_show_account_chooser (dialog)); break; + case PROP_FILTER_ACCOUNT: + g_value_set_object (value, + empathy_contact_selector_dialog_get_filter_account (dialog)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); break; @@ -382,6 +406,11 @@ empathy_contact_selector_dialog_set_property (GObject *self, g_value_get_boolean (value)); break; + case PROP_FILTER_ACCOUNT: + empathy_contact_selector_dialog_set_filter_account (dialog, + g_value_get_object (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); break; @@ -419,6 +448,11 @@ empathy_contact_selector_dialog_dispose (GObject *object) priv->contact_manager = NULL; } + if (priv->filter_account != NULL) { + g_object_unref (priv->filter_account); + priv->filter_account = NULL; + } + if (G_OBJECT_CLASS (empathy_contact_selector_dialog_parent_class)->dispose) G_OBJECT_CLASS (empathy_contact_selector_dialog_parent_class)->dispose ( object); @@ -445,6 +479,14 @@ empathy_contact_selector_dialog_class_init ( "Whether or not this dialog should show an account chooser", TRUE, G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_FILTER_ACCOUNT, + g_param_spec_object ("filter-account", + "Account to filter contacts", + "if 'show-account-chooser' is unset, only the contacts from this " + "account are displayed", + TP_TYPE_ACCOUNT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } const gchar * @@ -502,3 +544,31 @@ empathy_contact_selector_dialog_get_show_account_chooser ( priv = GET_PRIV (self); return priv->show_account_chooser; } + +void +empathy_contact_selector_dialog_set_filter_account ( + EmpathyContactSelectorDialog *self, + TpAccount *account) +{ + EmpathyContactSelectorDialogPriv *priv; + + g_return_if_fail (EMPATHY_IS_CONTACT_SELECTOR_DIALOG (self)); + + priv = GET_PRIV (self); + priv->filter_account = g_object_ref (account); + + g_object_notify (G_OBJECT (self), "filter-account"); +} + +TpAccount * +empathy_contact_selector_dialog_get_filter_account ( + EmpathyContactSelectorDialog *self) +{ + EmpathyContactSelectorDialogPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONTACT_SELECTOR_DIALOG (self), NULL); + + priv = GET_PRIV (self); + return priv->filter_account; + +} diff --git a/libempathy-gtk/empathy-contact-selector-dialog.h b/libempathy-gtk/empathy-contact-selector-dialog.h index 87b2812f4..318a9bcac 100644 --- a/libempathy-gtk/empathy-contact-selector-dialog.h +++ b/libempathy-gtk/empathy-contact-selector-dialog.h @@ -62,6 +62,13 @@ void empathy_contact_selector_dialog_set_show_account_chooser ( gboolean empathy_contact_selector_dialog_get_show_account_chooser ( EmpathyContactSelectorDialog *self); +void empathy_contact_selector_dialog_set_filter_account ( + EmpathyContactSelectorDialog *self, + TpAccount *account); + +TpAccount * empathy_contact_selector_dialog_get_filter_account ( + EmpathyContactSelectorDialog *self); + /* TYPE MACROS */ #define EMPATHY_TYPE_CONTACT_SELECTOR_DIALOG \ (empathy_contact_selector_dialog_get_type ()) diff --git a/libempathy-gtk/empathy-contact-widget.c b/libempathy-gtk/empathy-contact-widget.c index 696a8cda4..82a5ac703 100644 --- a/libempathy-gtk/empathy-contact-widget.c +++ b/libempathy-gtk/empathy-contact-widget.c @@ -82,6 +82,7 @@ typedef struct EmpathyContact *contact; EmpathyContactWidgetFlags flags; guint widget_id_timeout; + gulong fav_sig_id; GtkWidget *vbox_contact_widget; @@ -100,6 +101,7 @@ typedef struct GtkWidget *label_status; GtkWidget *table_contact; GtkWidget *vbox_avatar; + GtkWidget *favourite_checkbox; /* Location */ GtkWidget *vbox_location; @@ -448,7 +450,6 @@ contact_widget_groups_setup (EmpathyContactWidget *information) { if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_GROUPS) { - information->manager = empathy_contact_manager_dup_singleton (); contact_widget_model_setup (information); } } @@ -964,6 +965,21 @@ contact_widget_presence_notify_cb (EmpathyContactWidget *information) gtk_widget_show (information->image_state); } +#if HAVE_FAVOURITE_CONTACTS +static void +contact_widget_favourites_changed_cb (EmpathyContactManager *manager, + EmpathyContact *contact, + gboolean is_favourite, + EmpathyContactWidget *information) +{ + if (contact != information->contact) + return; + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON ( + information->favourite_checkbox), is_favourite); +} +#endif + static void contact_widget_remove_contact (EmpathyContactWidget *information) { @@ -1052,6 +1068,20 @@ contact_widget_contact_update (EmpathyContactWidget *information) contact_widget_presence_notify_cb (information); contact_widget_avatar_notify_cb (information); +#if HAVE_FAVOURITE_CONTACTS + if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_FAVOURITE) + { + gboolean is_favourite; + + is_favourite = empathy_contact_list_is_favourite ( + EMPATHY_CONTACT_LIST (information->manager), + information->contact); + + contact_widget_favourites_changed_cb (information->manager, + information->contact, is_favourite, information); + } +#endif + gtk_widget_show (information->label_alias); gtk_widget_show (information->widget_alias); gtk_widget_show (information->hbox_presence); @@ -1181,6 +1211,28 @@ contact_widget_id_focus_out_cb (GtkWidget *widget, return FALSE; } +#if HAVE_FAVOURITE_CONTACTS +static void +favourite_toggled_cb (GtkToggleButton *button, + EmpathyContactWidget *information) +{ + gboolean active; + + active = gtk_toggle_button_get_active (button); + + if (active) + { + empathy_contact_list_add_to_favourites ( + EMPATHY_CONTACT_LIST (information->manager), information->contact); + } + else + { + empathy_contact_list_remove_from_favourites ( + EMPATHY_CONTACT_LIST (information->manager), information->contact); + } +} +#endif + static void contact_widget_contact_setup (EmpathyContactWidget *information) { @@ -1318,6 +1370,27 @@ contact_widget_contact_setup (EmpathyContactWidget *information) gtk_label_set_selectable (GTK_LABEL (information->label_status), FALSE); } gtk_widget_show (information->widget_alias); + +#if HAVE_FAVOURITE_CONTACTS + /* Favorite */ + if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_FAVOURITE) + { + information->favourite_checkbox = gtk_check_button_new_with_label ( + _("Favorite")); + + g_signal_connect (information->favourite_checkbox, "toggled", + G_CALLBACK (favourite_toggled_cb), information); + + gtk_table_attach_defaults (GTK_TABLE (information->table_contact), + information->favourite_checkbox, 0, 2, 3, 4); + + information->fav_sig_id = g_signal_connect (information->manager, + "favourites-changed", + G_CALLBACK (contact_widget_favourites_changed_cb), information); + + gtk_widget_show (information->favourite_checkbox); + } +#endif } static void @@ -1330,10 +1403,11 @@ contact_widget_destroy_cb (GtkWidget *widget, { g_source_remove (information->widget_id_timeout); } - if (information->manager) - { - g_object_unref (information->manager); - } + + if (information->fav_sig_id != 0) + g_signal_handler_disconnect (information->manager, information->fav_sig_id); + + g_object_unref (information->manager); g_slice_free (EmpathyContactWidget, information); } @@ -1401,6 +1475,8 @@ empathy_contact_widget_new (EmpathyContact *contact, "EmpathyContactWidget", information); + information->manager = empathy_contact_manager_dup_singleton (); + /* Create widgets */ contact_widget_contact_setup (information); contact_widget_groups_setup (information); diff --git a/libempathy-gtk/empathy-contact-widget.h b/libempathy-gtk/empathy-contact-widget.h index 04b567f4d..af669477e 100644 --- a/libempathy-gtk/empathy-contact-widget.h +++ b/libempathy-gtk/empathy-contact-widget.h @@ -61,6 +61,7 @@ typedef enum EMPATHY_CONTACT_WIDGET_FOR_TOOLTIP = 1 << 5, EMPATHY_CONTACT_WIDGET_SHOW_LOCATION = 1 << 6, EMPATHY_CONTACT_WIDGET_NO_SET_ALIAS = 1 << 7, + EMPATHY_CONTACT_WIDGET_EDIT_FAVOURITE = 1 << 8, } EmpathyContactWidgetFlags; GtkWidget * empathy_contact_widget_new (EmpathyContact *contact, diff --git a/libempathy-gtk/empathy-string-parser.c b/libempathy-gtk/empathy-string-parser.c index 5c3fb1972..a661cd477 100644 --- a/libempathy-gtk/empathy-string-parser.c +++ b/libempathy-gtk/empathy-string-parser.c @@ -203,6 +203,14 @@ empathy_add_link_markup (const gchar *text) g_return_val_if_fail (text != NULL, NULL); + /* GtkLabel with links could make infinite loop because of + * GNOME bug #612066. It is fixed in GTK >= 2.18.8 and GTK >= 2.19.7. + * FIXME: Remove this check once we have an hard dep on GTK 2.20 */ + if (gtk_check_version (2, 18, 8) != NULL || + (gtk_minor_version == 19 && gtk_micro_version < 7)) { + return g_markup_escape_text (text, -1); + } + string = g_string_sized_new (strlen (text)); empathy_string_parser_substr (text, -1, parsers, string); diff --git a/libempathy-gtk/empathy-ui-utils.c b/libempathy-gtk/empathy-ui-utils.c index 807b6afaf..4503a39cc 100644 --- a/libempathy-gtk/empathy-ui-utils.c +++ b/libempathy-gtk/empathy-ui-utils.c @@ -1385,10 +1385,10 @@ empathy_window_iconify (GtkWindow *window, GtkStatusIcon *status_icon) /* Takes care of moving the window to the current workspace. */ void -empathy_window_present (GtkWindow *window, - gboolean steal_focus) +empathy_window_present (GtkWindow *window) { GdkWindow *gdk_window; + guint32 timestamp; g_return_if_fail (GTK_IS_WINDOW (window)); @@ -1410,7 +1410,12 @@ empathy_window_present (GtkWindow *window, gtk_widget_hide (GTK_WIDGET (window)); } - gtk_window_present_with_time (window, G_MAXINT32); + timestamp = gtk_get_current_event_time (); + if (timestamp == 0) + /* No event, fallback to _NET_WM_USER_TIME */ + timestamp = gdk_x11_display_get_user_time (gdk_display_get_default ()); + + gtk_window_present_with_time (window, timestamp); gtk_window_set_skip_taskbar_hint (window, FALSE); gtk_window_deiconify (window); } @@ -1631,6 +1636,9 @@ empathy_send_file_with_file_chooser (EmpathyContact *contact) gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), FALSE); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget), + g_get_home_dir ()); + g_signal_connect (widget, "response", G_CALLBACK (file_manager_send_file_response_cb), contact); @@ -1669,6 +1677,7 @@ void empathy_receive_file_with_file_chooser (EmpathyFTHandler *handler) { GtkWidget *widget; + const gchar *dir; widget = gtk_file_chooser_dialog_new (_("Select a destination"), NULL, @@ -1683,6 +1692,13 @@ empathy_receive_file_with_file_chooser (EmpathyFTHandler *handler) gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (widget), TRUE); + dir = g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD); + if (dir == NULL) + /* Fallback to $HOME if $XDG_DOWNLOAD_DIR is not set */ + dir = g_get_home_dir (); + + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget), dir); + g_signal_connect (widget, "response", G_CALLBACK (file_manager_receive_file_response_cb), handler); diff --git a/libempathy-gtk/empathy-ui-utils.h b/libempathy-gtk/empathy-ui-utils.h index 38732c7f9..8834cb4c9 100644 --- a/libempathy-gtk/empathy-ui-utils.h +++ b/libempathy-gtk/empathy-ui-utils.h @@ -104,8 +104,7 @@ gboolean empathy_text_iter_backward_search (const GtkTextIter*iter, const GtkTextIter*limit); /* Windows */ gboolean empathy_window_get_is_visible (GtkWindow *window); -void empathy_window_present (GtkWindow *window, - gboolean steal_focus); +void empathy_window_present (GtkWindow *window); void empathy_window_iconify (GtkWindow *window, GtkStatusIcon *status_icon); GtkWindow * empathy_get_toplevel_window (GtkWidget *widget); |