#include "empathy-individual-menu.h"
#include "empathy-individual-store.h"
#include "empathy-images.h"
+#include "empathy-linking-dialog.h"
#include "empathy-cell-renderer-expander.h"
#include "empathy-cell-renderer-text.h"
#include "empathy-cell-renderer-activatable.h"
GtkWidget *tooltip_widget;
gboolean show_offline;
+ gboolean show_untrusted;
GtkTreeModelFilter *filter;
GtkWidget *search_widget;
guint expand_groups_idle_handler;
/* owned string (group name) -> bool (whether to expand/contract) */
GHashTable *expand_groups;
+
+ /* Auto scroll */
+ guint auto_scroll_timeout_id;
+ /* Distance between mouse pointer and the nearby border. Negative when
+ scrolling updards.*/
+ gint distance;
} EmpathyIndividualViewPriv;
typedef struct
PROP_VIEW_FEATURES,
PROP_INDIVIDUAL_FEATURES,
PROP_SHOW_OFFLINE,
+ PROP_SHOW_UNTRUSTED,
};
/* TODO: re-add DRAG_TYPE_CONTACT_ID, for the case that we're dragging around
{
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",
GAsyncResult *result,
gpointer user_data)
{
- FolksGroups *groups = FOLKS_GROUPS (source);
+ FolksGroupable *groupable = FOLKS_GROUPABLE (source);
GError *error = NULL;
- folks_groups_change_group_finish (groups, result, &error);
+ folks_groupable_change_group_finish (groupable, result, &error);
if (error != NULL)
{
g_warning ("failed to change group: %s", error->message);
if (new_group != NULL)
{
- folks_groups_change_group (FOLKS_GROUPS (individual), new_group, TRUE,
+ folks_groupable_change_group (FOLKS_GROUPABLE (individual), new_group, TRUE,
groups_change_group_cb, NULL);
}
if (old_group != NULL && action == GDK_ACTION_MOVE)
{
- folks_groups_change_group (FOLKS_GROUPS (individual), old_group,
+ folks_groupable_change_group (FOLKS_GROUPABLE (individual), old_group,
FALSE, groups_change_group_cb, NULL);
}
}
return FALSE;
}
+/* Minimum distance between the mouse pointer and a horizontal border when we
+ start auto scrolling. */
+#define AUTO_SCROLL_MARGIN_SIZE 20
+/* How far to scroll per one tick. */
+#define AUTO_SCROLL_PITCH 10
+
+static gboolean
+individual_view_auto_scroll_cb (EmpathyIndividualView *self)
+{
+ EmpathyIndividualViewPriv *priv = GET_PRIV (self);
+ GtkAdjustment *adj;
+ gdouble new_value;
+
+ adj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self));
+
+ if (priv->distance < 0)
+ new_value = gtk_adjustment_get_value (adj) - AUTO_SCROLL_PITCH;
+ else
+ new_value = gtk_adjustment_get_value (adj) + AUTO_SCROLL_PITCH;
+
+ new_value = CLAMP (new_value, gtk_adjustment_get_lower (adj),
+ gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj));
+
+ gtk_adjustment_set_value (adj, new_value);
+
+ return TRUE;
+}
+
static gboolean
individual_view_drag_motion (GtkWidget *widget,
GdkDragContext *context,
gboolean is_different = FALSE;
gboolean cleanup = TRUE;
gboolean retval = TRUE;
+ GtkAllocation allocation;
priv = GET_PRIV (EMPATHY_INDIVIDUAL_VIEW (widget));
model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+
+ if (priv->auto_scroll_timeout_id != 0)
+ {
+ g_source_remove (priv->auto_scroll_timeout_id);
+ priv->auto_scroll_timeout_id = 0;
+ }
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ if (y < AUTO_SCROLL_MARGIN_SIZE ||
+ y > (allocation.height - AUTO_SCROLL_MARGIN_SIZE))
+ {
+ if (y < AUTO_SCROLL_MARGIN_SIZE)
+ priv->distance = MIN (-y, -1);
+ else
+ priv->distance = MAX (allocation.height - y, 1);
+
+ priv->auto_scroll_timeout_id = g_timeout_add (10 * ABS (priv->distance),
+ (GSourceFunc) individual_view_auto_scroll_cb, widget);
+ }
+
is_row = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
x, y, &path, NULL, NULL, NULL);
}
if (individual != NULL &&
- folks_individual_is_online (individual) &&
+ folks_presence_is_online (FOLKS_PRESENCE (individual)) &&
(caps & EMPATHY_CAPABILITIES_FT))
{
gdk_drag_status (context, GDK_ACTION_COPY, time_);
if (menu != NULL)
{
- 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);
GdkEventKey *event,
gpointer user_data)
{
- if (event->keyval == GDK_Menu)
+ if (event->keyval == GDK_KEY_Menu)
{
MenuPopupData *data;
{
EmpathyIndividualViewPriv *priv = GET_PRIV (view);
FolksIndividual *individual;
- EmpathyContact *contact = NULL;
+ EmpathyContact *contact;
GtkTreeModel *model;
GtkTreeIter iter;
if (individual == NULL)
return;
- contact = empathy_contact_dup_from_folks_individual (individual);
+ /* Determine which Persona to chat to, by choosing the most available one. */
+ contact = empathy_contact_dup_best_for_action (individual,
+ EMPATHY_ACTION_CHAT);
+
if (contact != NULL)
{
DEBUG ("Starting a chat");
const gchar *path_string,
EmpathyIndividualView *view)
{
+ EmpathyIndividualViewPriv *priv = GET_PRIV (view);
GtkWidget *menu;
GtkTreeModel *model;
GtkTreeIter iter;
GtkMenuShell *shell;
GtkWidget *item;
+ if (!(priv->view_features & EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_CALL))
+ return;
+
model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
if (!gtk_tree_model_get_iter_from_string (model, &iter, path_string))
return;
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,
if (priv->search_widget == NULL)
return FALSE;
- if (gtk_widget_get_visible (GTK_WIDGET (priv->search_widget)))
- gtk_widget_grab_focus (GTK_WIDGET (priv->search_widget));
- else
- gtk_widget_show (GTK_WIDGET (priv->search_widget));
+ empathy_individual_view_start_search (view);
return TRUE;
}
GdkEventKey *eventkey = ((GdkEventKey *) event);
gboolean ret = FALSE;
- if (eventkey->keyval == GDK_Up || eventkey->keyval == GDK_Down)
+ if (eventkey->keyval == GDK_KEY_Up || eventkey->keyval == GDK_KEY_Down)
{
GdkEvent *new_event;
g_signal_handlers_unblock_by_func (self,
individual_view_row_expand_or_collapse_cb, GINT_TO_POINTER (TRUE));
- g_object_unref (self);
+ /* Empty the table of groups to expand/contract, since it may contain groups
+ * which no longer exist in the tree view. This can happen after going
+ * offline, for example. */
+ g_hash_table_remove_all (priv->expand_groups);
priv->expand_groups_idle_handler = 0;
+ g_object_unref (self);
return FALSE;
}
* a hash table, and expand or contract them as appropriate all at once in
* an idle handler which iterates over all the group rows. */
if (g_hash_table_lookup_extended (priv->expand_groups, name, NULL,
- &will_expand) == FALSE &&
+ &will_expand) == FALSE ||
GPOINTER_TO_INT (will_expand) != should_expand)
{
g_hash_table_insert (priv->expand_groups, g_strdup (name),
/* We're only giving the visibility wrt filtering here, not things like
* presence. */
+ if (priv->show_untrusted == FALSE &&
+ folks_individual_get_trust_level (individual) == FOLKS_TRUST_LEVEL_NONE)
+ {
+ return FALSE;
+ }
+
if (is_searching == FALSE)
return (priv->show_offline || is_online);
/* check alias name */
- str = folks_individual_get_alias (individual);
+ str = folks_aliasable_get_alias (FOLKS_ALIASABLE (individual));
if (empathy_live_search_match (live, str))
return TRUE;
"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 ();
{
EmpathyIndividualViewPriv *priv = GET_PRIV (object);
+ if (priv->expand_groups_idle_handler != 0)
+ g_source_remove (priv->expand_groups_idle_handler);
g_hash_table_destroy (priv->expand_groups);
G_OBJECT_CLASS (empathy_individual_view_parent_class)->finalize (object);
case PROP_SHOW_OFFLINE:
g_value_set_boolean (value, priv->show_offline);
break;
+ case PROP_SHOW_UNTRUSTED:
+ g_value_set_boolean (value, priv->show_untrusted);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
empathy_individual_view_set_show_offline (view,
g_value_get_boolean (value));
break;
+ case PROP_SHOW_UNTRUSTED:
+ empathy_individual_view_set_show_untrusted (view,
+ g_value_get_boolean (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break;
"Show Offline",
"Whether contact list should display "
"offline contacts", FALSE, G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_SHOW_UNTRUSTED,
+ g_param_spec_boolean ("show-untrusted",
+ "Show Untrusted Individuals",
+ "Whether the view should display untrusted individuals; "
+ "those who could not be who they say they are.",
+ TRUE, G_PARAM_READWRITE));
g_type_class_add_private (object_class, sizeof (EmpathyIndividualViewPriv));
}
EMPATHY_TYPE_INDIVIDUAL_VIEW, EmpathyIndividualViewPriv);
view->priv = priv;
+
+ priv->show_untrusted = TRUE;
+
/* Get saved group states. */
empathy_contact_groups_get_all ();
return individual;
}
-EmpathyIndividualManagerFlags
-empathy_individual_view_get_flags (EmpathyIndividualView *view)
-{
- EmpathyIndividualViewPriv *priv;
- GtkTreeSelection *selection;
- GtkTreeIter iter;
- GtkTreeModel *model;
- EmpathyIndividualFeatureFlags flags;
-
- g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_VIEW (view), 0);
-
- priv = GET_PRIV (view);
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
- if (!gtk_tree_selection_get_selected (selection, &model, &iter))
- return 0;
-
- gtk_tree_model_get (model, &iter,
- EMPATHY_INDIVIDUAL_STORE_COL_FLAGS, &flags, -1);
-
- return flags;
-}
-
-gchar *
-empathy_individual_view_get_selected_group (EmpathyIndividualView *view,
+static gchar *
+empathy_individual_view_dup_selected_group (EmpathyIndividualView *view,
gboolean *is_fake_group)
{
EmpathyIndividualViewPriv *priv;
{
gchar *group;
- group = empathy_individual_view_get_selected_group (view, NULL);
+ group = empathy_individual_view_dup_selected_group (view, NULL);
if (group != NULL)
{
gchar *text;
EMPATHY_INDIVIDUAL_VIEW_FEATURE_GROUPS_REMOVE)))
return NULL;
- group = empathy_individual_view_get_selected_group (view, &is_fake_group);
+ group = empathy_individual_view_dup_selected_group (view, &is_fake_group);
if (!group || is_fake_group)
{
/* We can't alter fake groups */
+ g_free (group);
return NULL;
}
text =
g_strdup_printf (_
("Do you really want to remove the contact '%s'?"),
- folks_individual_get_alias (individual));
+ folks_aliasable_get_alias (FOLKS_ALIASABLE (individual)));
if (individual_view_remove_dialog_show (parent, _("Removing contact"),
text))
{
}
}
+static void
+individual_menu_link_contacts_activated_cb (EmpathyIndividualMenu *menu,
+ EmpathyLinkingDialog *linking_dialog,
+ EmpathyIndividualView *self)
+{
+ EmpathyIndividualViewPriv *priv = GET_PRIV (self);
+ EmpathyIndividualLinker *linker;
+
+ linker = empathy_linking_dialog_get_individual_linker (linking_dialog);
+ empathy_individual_linker_set_search_text (linker,
+ empathy_live_search_get_text (EMPATHY_LIVE_SEARCH (priv->search_widget)));
+}
+
GtkWidget *
empathy_individual_view_get_individual_menu (EmpathyIndividualView *view)
{
GtkWidget *menu = NULL;
GtkWidget *item;
GtkWidget *image;
- EmpathyIndividualManagerFlags flags;
+ gboolean can_remove = FALSE;
+ GList *l;
g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_VIEW (view), NULL);
if (individual == NULL)
return NULL;
- flags = empathy_individual_view_get_flags (view);
+ /* If any of the Individual's personas can be removed, add an option to
+ * remove. This will act as a best-effort option. If any Personas cannot be
+ * removed from the server, then this option will just be inactive upon
+ * subsequent menu openings */
+ for (l = folks_individual_get_personas (individual); l != NULL; l = l->next)
+ {
+ FolksPersona *persona = FOLKS_PERSONA (l->data);
+ FolksPersonaStore *store = folks_persona_get_store (persona);
+ FolksMaybeBool maybe_can_remove =
+ folks_persona_store_get_can_remove_personas (store);
+
+ if (maybe_can_remove == FOLKS_MAYBE_BOOL_TRUE)
+ {
+ can_remove = TRUE;
+ break;
+ }
+ }
menu = empathy_individual_menu_new (individual, priv->individual_features);
/* Remove contact */
- if (priv->view_features &
- EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_REMOVE &&
- flags & EMPATHY_INDIVIDUAL_MANAGER_CAN_REMOVE)
+ if ((priv->view_features &
+ EMPATHY_INDIVIDUAL_VIEW_FEATURE_INDIVIDUAL_REMOVE) &&
+ can_remove)
{
-
/* create the menu if required, or just add a separator */
if (menu == NULL)
menu = gtk_menu_new ();
G_CALLBACK (individual_view_remove_activate_cb), view);
}
+ /* Connect to EmpathyIndividualMenu::link-contacts-activated so that we can
+ * set the live search text on the new linking dialogue to be the same as
+ * our own. */
+ g_signal_connect (menu, "link-contacts-activated",
+ (GCallback) individual_menu_link_contacts_activated_cb, view);
+
g_object_unref (individual);
return menu;
gtk_tree_model_filter_refilter (priv->filter);
}
+gboolean
+empathy_individual_view_get_show_untrusted (EmpathyIndividualView *self)
+{
+ g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_VIEW (self), FALSE);
+
+ return GET_PRIV (self)->show_untrusted;
+}
+
+void
+empathy_individual_view_set_show_untrusted (EmpathyIndividualView *self,
+ gboolean show_untrusted)
+{
+ EmpathyIndividualViewPriv *priv;
+
+ g_return_if_fail (EMPATHY_IS_INDIVIDUAL_VIEW (self));
+
+ priv = GET_PRIV (self);
+
+ priv->show_untrusted = show_untrusted;
+
+ g_object_notify (G_OBJECT (self), "show-untrusted");
+ gtk_tree_model_filter_refilter (priv->filter);
+}
+
EmpathyIndividualStore *
empathy_individual_view_get_store (EmpathyIndividualView *self)
{
G_CALLBACK (individual_view_store_row_deleted_cb), self, 0);
}
}
+
+void
+empathy_individual_view_start_search (EmpathyIndividualView *self)
+{
+ EmpathyIndividualViewPriv *priv = GET_PRIV (self);
+
+ g_return_if_fail (EMPATHY_IS_INDIVIDUAL_VIEW (self));
+ g_return_if_fail (priv->search_widget != NULL);
+
+ if (gtk_widget_get_visible (GTK_WIDGET (priv->search_widget)))
+ gtk_widget_grab_focus (GTK_WIDGET (priv->search_widget));
+ else
+ gtk_widget_show (GTK_WIDGET (priv->search_widget));
+}