aboutsummaryrefslogtreecommitdiffstats
path: root/libempathy-gtk
diff options
context:
space:
mode:
authorGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>2009-11-30 23:08:24 +0800
committerGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>2009-11-30 23:08:24 +0800
commit740a34efd932ad1bf6d5cd1d4580fd3b6af0eacc (patch)
tree0e9497b8a933cab09ee209d92097c5f273612ded /libempathy-gtk
parent8d21537a22e1e4a1e6224d5e70190e0be2ef9a40 (diff)
parent9ec54fdc072173c6676ec2d8ea0b8eabe6936cef (diff)
downloadgsoc2013-empathy-740a34efd932ad1bf6d5cd1d4580fd3b6af0eacc.tar
gsoc2013-empathy-740a34efd932ad1bf6d5cd1d4580fd3b6af0eacc.tar.gz
gsoc2013-empathy-740a34efd932ad1bf6d5cd1d4580fd3b6af0eacc.tar.bz2
gsoc2013-empathy-740a34efd932ad1bf6d5cd1d4580fd3b6af0eacc.tar.lz
gsoc2013-empathy-740a34efd932ad1bf6d5cd1d4580fd3b6af0eacc.tar.xz
gsoc2013-empathy-740a34efd932ad1bf6d5cd1d4580fd3b6af0eacc.tar.zst
gsoc2013-empathy-740a34efd932ad1bf6d5cd1d4580fd3b6af0eacc.zip
Merge commit 'shaunm/dndfiles2'
Diffstat (limited to 'libempathy-gtk')
-rw-r--r--libempathy-gtk/empathy-chat.c6
-rw-r--r--libempathy-gtk/empathy-contact-list-view.c267
-rw-r--r--libempathy-gtk/empathy-ui-utils.c66
-rw-r--r--libempathy-gtk/empathy-ui-utils.h4
4 files changed, 266 insertions, 77 deletions
diff --git a/libempathy-gtk/empathy-chat.c b/libempathy-gtk/empathy-chat.c
index d9a72d3e9..9d8b89c26 100644
--- a/libempathy-gtk/empathy-chat.c
+++ b/libempathy-gtk/empathy-chat.c
@@ -2010,6 +2010,12 @@ chat_create_ui (EmpathyChat *chat)
/* Add message view. */
chat->view = empathy_theme_manager_create_view (empathy_theme_manager_get ());
+ /* If this is a GtkTextView, it's set as a drag destination for text/plain
+ and other types, even though it's non-editable and doesn't accept any
+ drags. This steals drag motion for anything inside the scrollbars,
+ making drag destinations on chat windows far less useful.
+ */
+ gtk_drag_dest_unset (GTK_WIDGET (chat->view));
g_signal_connect (chat->view, "focus_in_event",
G_CALLBACK (chat_text_view_focus_in_event_cb),
chat);
diff --git a/libempathy-gtk/empathy-contact-list-view.c b/libempathy-gtk/empathy-contact-list-view.c
index c19ad32df..6b25cc10f 100644
--- a/libempathy-gtk/empathy-contact-list-view.c
+++ b/libempathy-gtk/empathy-contact-list-view.c
@@ -65,6 +65,7 @@ typedef struct {
EmpathyContactListFeatureFlags list_features;
EmpathyContactFeatureFlags contact_features;
GtkWidget *tooltip_widget;
+ GtkTargetList *file_targets;
} EmpathyContactListViewPriv;
typedef struct {
@@ -88,17 +89,21 @@ enum {
enum DndDragType {
DND_DRAG_TYPE_CONTACT_ID,
- DND_DRAG_TYPE_URL,
+ DND_DRAG_TYPE_URI_LIST,
DND_DRAG_TYPE_STRING,
};
static const GtkTargetEntry drag_types_dest[] = {
+ { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
{ "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
- { "text/uri-list", 0, DND_DRAG_TYPE_URL },
{ "text/plain", 0, DND_DRAG_TYPE_STRING },
{ "STRING", 0, DND_DRAG_TYPE_STRING },
};
+static const GtkTargetEntry drag_types_dest_file[] = {
+ { "text/uri-list", 0, DND_DRAG_TYPE_URI_LIST },
+};
+
static const GtkTargetEntry drag_types_source[] = {
{ "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
};
@@ -234,74 +239,51 @@ contact_list_view_drag_got_contact (EmpathyTpContactFactory *factory,
}
}
-static void
-contact_list_view_drag_data_received (GtkWidget *view,
- GdkDragContext *context,
- gint x,
- gint y,
- GtkSelectionData *selection,
- guint info,
- guint time_)
+static gboolean
+contact_list_view_contact_drag_received (GtkWidget *view,
+ GdkDragContext *context,
+ GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkSelectionData *selection)
{
EmpathyContactListViewPriv *priv;
TpAccountManager *account_manager;
EmpathyTpContactFactory *factory = NULL;
- TpAccount *account = NULL;
- GtkTreeModel *model;
- GtkTreeViewDropPosition position;
- GtkTreePath *path;
- const gchar *id;
- gchar **strv = NULL;
- const gchar *account_id = NULL;
- const gchar *contact_id = NULL;
- gchar *new_group = NULL;
- gchar *old_group = NULL;
+ TpAccount *account;
DndGetContactData *data;
- gboolean is_row;
- gboolean success = TRUE;
+ GtkTreePath *source_path;
+ const gchar *sel_data;
+ gchar **strv = NULL;
+ const gchar *account_id = NULL;
+ const gchar *contact_id = NULL;
+ gchar *new_group = NULL;
+ gchar *old_group = NULL;
+ gboolean success = TRUE;
priv = GET_PRIV (view);
- model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
-
- /* Get destination group information. */
- is_row = gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (view),
- x,
- y,
- &path,
- &position);
- if (is_row) {
- new_group = empathy_contact_list_store_get_parent_group (model,
- path, NULL);
- gtk_tree_path_free (path);
- }
+ sel_data = (const gchar *) gtk_selection_data_get_data (selection);
+ new_group = empathy_contact_list_store_get_parent_group (model,
+ path, NULL);
/* Get source group information. */
if (priv->drag_row) {
- path = gtk_tree_row_reference_get_path (priv->drag_row);
- if (path) {
+ source_path = gtk_tree_row_reference_get_path (priv->drag_row);
+ if (source_path) {
old_group = empathy_contact_list_store_get_parent_group (
- model, path, NULL);
- gtk_tree_path_free (path);
+ model, source_path, NULL);
+ gtk_tree_path_free (source_path);
}
}
if (!tp_strdiff (old_group, new_group)) {
g_free (new_group);
g_free (old_group);
- goto OUT;
+ return FALSE;
}
- id = (const gchar*) gtk_selection_data_get_data (selection);
- DEBUG ("Received %s%s drag & drop contact from roster with id:'%s'",
- context->action == GDK_ACTION_MOVE ? "move" : "",
- context->action == GDK_ACTION_COPY ? "copy" : "",
- id);
-
- /* FIXME: should probably make sure the account manager is prepared
- * before calling _ensure_account on it. See bug 600115. */
account_manager = tp_account_manager_dup ();
- strv = g_strsplit (id, ":", 2);
+ strv = g_strsplit (sel_data, ":", 2);
if (g_strv_length (strv) == 2) {
account_id = strv[0];
contact_id = strv[1];
@@ -322,7 +304,7 @@ contact_list_view_drag_data_received (GtkWidget *view,
success = FALSE;
g_free (new_group);
g_free (old_group);
- goto OUT;
+ return FALSE;
}
data = g_slice_new0 (DndGetContactData);
@@ -333,14 +315,85 @@ contact_list_view_drag_data_received (GtkWidget *view,
/* FIXME: We should probably wait for the cb before calling
* gtk_drag_finish */
empathy_tp_contact_factory_get_from_id (factory, contact_id,
- contact_list_view_drag_got_contact,
- data, (GDestroyNotify) contact_list_view_dnd_get_contact_free,
- G_OBJECT (view));
-
+ contact_list_view_drag_got_contact,
+ data, (GDestroyNotify) contact_list_view_dnd_get_contact_free,
+ G_OBJECT (view));
+ g_strfreev (strv);
g_object_unref (factory);
-OUT:
- g_strfreev (strv);
+ return TRUE;
+}
+
+static gboolean
+contact_list_view_file_drag_received (GtkWidget *view,
+ GdkDragContext *context,
+ GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkSelectionData *selection)
+{
+ GtkTreeIter iter;
+ const gchar *sel_data;
+ EmpathyContact *contact;
+
+ sel_data = (const gchar *) gtk_selection_data_get_data (selection);
+
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
+ -1);
+ if (!contact) {
+ return FALSE;
+ }
+
+ empathy_send_file_from_uri_list (contact, sel_data);
+
+ g_object_unref (contact);
+
+ return TRUE;
+}
+
+static void
+contact_list_view_drag_data_received (GtkWidget *view,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time_)
+{
+ GtkTreeModel *model;
+ gboolean is_row;
+ GtkTreeViewDropPosition position;
+ GtkTreePath *path;
+ gboolean success = TRUE;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+
+ /* Get destination group information. */
+ is_row = gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (view),
+ x,
+ y,
+ &path,
+ &position);
+ if (!is_row) {
+ success = FALSE;
+ }
+ else if (info == DND_DRAG_TYPE_CONTACT_ID || info == DND_DRAG_TYPE_STRING) {
+ success = contact_list_view_contact_drag_received (view,
+ context,
+ model,
+ path,
+ selection);
+ }
+ else if (info == DND_DRAG_TYPE_URI_LIST) {
+ success = contact_list_view_file_drag_received (view,
+ context,
+ model,
+ path,
+ selection);
+ }
+
+ gtk_tree_path_free (path);
gtk_drag_finish (context, success, FALSE, GDK_CURRENT_TIME);
}
@@ -363,12 +416,19 @@ contact_list_view_drag_motion (GtkWidget *widget,
gint y,
guint time_)
{
+ EmpathyContactListViewPriv *priv;
+ GtkTreeModel *model;
+ GdkAtom target;
+ GtkTreeIter iter;
static DragMotionData *dm = NULL;
GtkTreePath *path;
gboolean is_row;
gboolean is_different = FALSE;
gboolean cleanup = TRUE;
- int action = 0;
+ gboolean retval = TRUE;
+
+ priv = GET_PRIV (EMPATHY_CONTACT_LIST_VIEW (widget));
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
is_row = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
x,
@@ -387,15 +447,82 @@ contact_list_view_drag_motion (GtkWidget *widget,
cleanup &= FALSE;
}
- if (context->actions == GDK_ACTION_COPY) {
- action = context->suggested_action;
- } else if (context->actions & GDK_ACTION_MOVE) {
- action = GDK_ACTION_MOVE;
+ if (path == NULL) {
+ /* Coordinates don't point to an actual row, so make sure the pointer
+ and highlighting don't indicate that a drag is possible.
+ */
+ gdk_drag_status (context, GDK_ACTION_DEFAULT, time_);
+ gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), NULL, 0);
+ return FALSE;
+ }
+ target = gtk_drag_dest_find_target (widget, context, priv->file_targets);
+ gtk_tree_model_get_iter (model, &iter, path);
+
+ if (target == GDK_NONE) {
+ /* If target == GDK_NONE, then we don't have a target that can be
+ dropped on a contact. This means a contact drag. If we're
+ pointing to a group, highlight it. Otherwise, if the contact
+ we're pointing to is in a group, highlight that. Otherwise,
+ set the drag position to before the first row for a drag into
+ the "non-group" at the top.
+ */
+ GtkTreeIter group_iter;
+ gboolean is_group;
+ GtkTreePath *group_path;
+ gtk_tree_model_get (model, &iter,
+ EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
+ -1);
+ if (is_group) {
+ group_iter = iter;
+ }
+ else {
+ if (gtk_tree_model_iter_parent (model, &group_iter, &iter))
+ gtk_tree_model_get (model, &group_iter,
+ EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
+ -1);
+ }
+ if (is_group) {
+ gdk_drag_status (context, GDK_ACTION_MOVE, time_);
+ group_path = gtk_tree_model_get_path (model, &group_iter);
+ gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
+ group_path,
+ GTK_TREE_VIEW_DROP_INTO_OR_BEFORE);
+ gtk_tree_path_free (group_path);
+ }
+ else {
+ group_path = gtk_tree_path_new_first ();
+ gdk_drag_status (context, GDK_ACTION_MOVE, time_);
+ gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
+ group_path,
+ GTK_TREE_VIEW_DROP_BEFORE);
+ }
+ }
+ else {
+ /* This is a file drag, and it can only be dropped on contacts,
+ not groups.
+ */
+ EmpathyContact *contact;
+ gtk_tree_model_get (model, &iter,
+ EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
+ -1);
+ if (contact != NULL &&
+ empathy_contact_is_online (contact) &&
+ (empathy_contact_get_capabilities (contact) & EMPATHY_CAPABILITIES_FT)) {
+ gdk_drag_status (context, GDK_ACTION_COPY, time_);
+ gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
+ path,
+ GTK_TREE_VIEW_DROP_INTO_OR_BEFORE);
+ g_object_unref (contact);
+ }
+ else {
+ gdk_drag_status (context, 0, time_);
+ gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), NULL, 0);
+ retval = FALSE;
+ }
}
- gdk_drag_status (context, action, time_);
if (!is_different && !cleanup) {
- return TRUE;
+ return retval;
}
if (dm) {
@@ -420,7 +547,7 @@ contact_list_view_drag_motion (GtkWidget *widget,
dm);
}
- return TRUE;
+ return retval;
}
static void
@@ -971,6 +1098,11 @@ contact_list_view_setup (EmpathyContactListView *view)
GTK_TREE_MODEL (priv->store));
/* Setup view */
+ /* Setting reorderable is a hack that gets us row previews as drag icons
+ for free. We override all the drag handlers. It's tricky to get the
+ position of the drag icon right in drag_begin. GtkTreeView has special
+ voodoo for it, so we let it do the voodoo that he do.
+ */
g_object_set (view,
"headers-visible", FALSE,
"reorderable", TRUE,
@@ -1131,6 +1263,9 @@ contact_list_view_finalize (GObject *object)
if (priv->tooltip_widget) {
gtk_widget_destroy (priv->tooltip_widget);
}
+ if (priv->file_targets) {
+ gtk_target_list_unref (priv->file_targets);
+ }
G_OBJECT_CLASS (empathy_contact_list_view_parent_class)->finalize (object);
}
@@ -1261,6 +1396,10 @@ empathy_contact_list_view_init (EmpathyContactListView *view)
empathy_contact_list_store_row_separator_func,
NULL, NULL);
+ /* Set up drag target lists. */
+ priv->file_targets = gtk_target_list_new (drag_types_dest_file,
+ G_N_ELEMENTS (drag_types_dest_file));
+
/* Connect to tree view signals rather than override. */
g_signal_connect (view, "button-press-event",
G_CALLBACK (contact_list_view_button_press_event_cb),
diff --git a/libempathy-gtk/empathy-ui-utils.c b/libempathy-gtk/empathy-ui-utils.c
index c737873d1..6906d8228 100644
--- a/libempathy-gtk/empathy-ui-utils.c
+++ b/libempathy-gtk/empathy-ui-utils.c
@@ -1454,30 +1454,70 @@ empathy_toggle_button_set_state_quietly (GtkWidget *widget,
g_signal_handlers_unblock_by_func (widget, callback, user_data);
}
+void
+empathy_send_file (EmpathyContact *contact, GFile *file)
+{
+ EmpathyFTFactory *factory;
+ GtkRecentManager *manager;
+ gchar *uri;
+
+ g_return_if_fail (EMPATHY_IS_CONTACT (contact));
+ g_return_if_fail (G_IS_FILE (file));
+
+ factory = empathy_ft_factory_dup_singleton ();
+
+ empathy_ft_factory_new_transfer_outgoing (factory, contact, file);
+
+ uri = g_file_get_uri (file);
+ manager = gtk_recent_manager_get_default ();
+ gtk_recent_manager_add_item (manager, uri);
+ g_free (uri);
+
+ g_object_unref (factory);
+}
+
+void
+empathy_send_file_from_uri_list (EmpathyContact *contact, const gchar *uri_list)
+{
+ const gchar *nl;
+ GFile *file;
+
+ /* Only handle a single file for now. It would be wicked cool to be
+ able to do multiple files, offering to zip them or whatever like
+ nautilus-sendto does. Note that text/uri-list is defined to have
+ each line terminated by \r\n, but we can be tolerant of applications
+ that only use \n or don't terminate single-line entries.
+ */
+ nl = strstr (uri_list, "\r\n");
+ if (!nl) {
+ nl = strchr (uri_list, '\n');
+ }
+ if (nl) {
+ gchar *uri = g_strndup (uri_list, nl - uri_list);
+ file = g_file_new_for_uri (uri);
+ g_free (uri);
+ }
+ else {
+ file = g_file_new_for_uri (uri_list);
+ }
+
+ empathy_send_file (contact, file);
+
+ g_object_unref (file);
+}
+
static void
file_manager_send_file_response_cb (GtkDialog *widget,
gint response_id,
EmpathyContact *contact)
{
- EmpathyFTFactory *factory;
GFile *file;
- gchar *uri;
- GtkRecentManager *manager;
if (response_id == GTK_RESPONSE_OK) {
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (widget));
- uri = g_file_get_uri (file);
-
- factory = empathy_ft_factory_dup_singleton ();
-
- empathy_ft_factory_new_transfer_outgoing (factory, contact,
- file);
- manager = gtk_recent_manager_get_default ();
- gtk_recent_manager_add_item (manager, uri);
+ empathy_send_file (contact, file);
- g_free (uri);
- g_object_unref (factory);
g_object_unref (file);
}
diff --git a/libempathy-gtk/empathy-ui-utils.h b/libempathy-gtk/empathy-ui-utils.h
index 65fc9ac0e..0eacd49b8 100644
--- a/libempathy-gtk/empathy-ui-utils.h
+++ b/libempathy-gtk/empathy-ui-utils.h
@@ -110,6 +110,10 @@ void empathy_toggle_button_set_state_quietly (GtkWidget *widge
GtkWidget * empathy_link_button_new (const gchar *url,
const gchar *title);
+void empathy_send_file (EmpathyContact *contact,
+ GFile *file);
+void empathy_send_file_from_uri_list (EmpathyContact *contact,
+ const gchar *uri_list);
void empathy_send_file_with_file_chooser (EmpathyContact *contact);
void empathy_receive_file_with_file_chooser (EmpathyFTHandler *handler);