Commit c53cb522 authored by Amin Bandali's avatar Amin Bandali Committed by Sébastien Blin
Browse files

conversationsview: support drag-and-drop to send file

Allow dragging and dropping files onto the conversations view on the
left to send them to a contact.  Handles multiple selected files too.
Remove old code for merging two ongoing calls with drag-and-drop.

files: add `foreach_file()' containing the logic for converting a
string containing the URIs into filenames, and calling the provided
callback on each filename.

chatview: use the new `foreach_file()' from files, much like
conversationsview does.  Reduces code duplication, and enables
sending multiple selected files at once as well.

GitLab: #1200
Change-Id: I87e11abe121db2d1ac7071236f788d77885fb18f
parent 18c74599
......@@ -966,32 +966,12 @@ on_webkit_drag_drop(GtkWidget*, gchar* data, ChatView* self)
if (!priv->conversation_) return;
if (!data) return;
GError *error = nullptr;
auto* filename_uri = g_filename_from_uri(data, nullptr, &error);
if (error) {
g_warning("Unable to exec g_filename_from_uri on %s", data);
g_error_free(error);
return;
}
std::string data_str = filename_uri;
g_free(filename_uri);
// Only take files
if (data_str.find("\r\n") == std::string::npos) return;
const auto LEN_END = std::string("\r\n").length();
if (data_str.length() > LEN_END) {
// remove \r\n from the string
data_str = data_str.substr(0, data_str.length() - LEN_END);
} else {
// Nothing valid to drop, abort.
return;
}
if (auto model = (*priv->accountInfo_)->conversationModel.get()) {
model->sendFile(priv->conversation_->uid,
data_str.c_str(),
g_path_get_basename(data_str.c_str()));
}
foreach_file(data, [&](const char* file) {
if (auto model = (*priv->accountInfo_)->conversationModel.get()) {
model->sendFile(priv->conversation_->uid, file,
g_path_get_basename(file));
}
});
}
static void
......
......@@ -41,9 +41,10 @@
// Gnome client
#include "native/pixbufmanipulator.h"
#include "conversationpopupmenu.h"
#include "utils/files.h"
static constexpr const char* CALL_TARGET = "CALL_TARGET";
static constexpr int CALL_TARGET_ID = 0;
static constexpr const char* TEXT_URI_LIST_TARGET = "text/uri-list";
static constexpr int TEXT_URI_LIST_TARGET_ID = 0;
struct _ConversationsView
{
......@@ -504,66 +505,6 @@ show_popup_menu(ConversationsView *self, GdkEventButton *event)
conversation_popup_menu_show(CONVERSATION_POPUP_MENU(priv->popupMenu_), event);
}
static void
on_drag_data_get(GtkWidget *treeview,
G_GNUC_UNUSED GdkDragContext *context,
GtkSelectionData *data,
G_GNUC_UNUSED guint info,
G_GNUC_UNUSED guint time,
G_GNUC_UNUSED gpointer user_data)
{
g_return_if_fail(IS_CONVERSATIONS_VIEW(treeview));
/* we always drag the selected row */
auto selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
GtkTreeModel *model = NULL;
GtkTreeIter iter;
if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
auto path_str = gtk_tree_model_get_string_from_iter(model, &iter);
gtk_selection_data_set(data,
gdk_atom_intern_static_string(CALL_TARGET),
8, /* bytes */
(guchar *)path_str,
strlen(path_str) + 1);
g_free(path_str);
} else {
g_warning("drag selection not valid");
}
}
static gboolean
on_drag_drop(GtkWidget *treeview,
GdkDragContext *context,
gint x,
gint y,
guint time,
G_GNUC_UNUSED gpointer user_data)
{
g_return_val_if_fail(IS_CONVERSATIONS_VIEW(treeview), FALSE);
GtkTreePath *path = NULL;
GtkTreeViewDropPosition drop_pos;
if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(treeview),
x, y, &path, &drop_pos)) {
GdkAtom target_type = gtk_drag_dest_find_target(treeview, context, NULL);
if (target_type != GDK_NONE) {
g_debug("can drop");
gtk_drag_get_data(treeview, context, target_type, time);
return TRUE;
}
gtk_tree_path_free(path);
}
return FALSE;
}
static gboolean
on_drag_motion(GtkWidget *treeview,
GdkDragContext *context,
......@@ -599,7 +540,7 @@ on_drag_data_received(GtkWidget *treeview,
gint x,
gint y,
GtkSelectionData *data,
G_GNUC_UNUSED guint info,
guint target_type,
guint time,
G_GNUC_UNUSED gpointer user_data)
{
......@@ -612,6 +553,7 @@ on_drag_data_received(GtkWidget *treeview,
auto path_str_source = (gchar *)gtk_selection_data_get_data(data);
auto type = gtk_selection_data_get_data_type(data);
g_debug("data type: %s", gdk_atom_name(type));
if (path_str_source && strlen(path_str_source) > 0) {
g_debug("source path: %s", path_str_source);
......@@ -620,30 +562,27 @@ on_drag_data_received(GtkWidget *treeview,
if (gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(treeview), x, y, &dest_path, NULL)) {
auto model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
GtkTreeIter source, dest;
gtk_tree_model_get_iter_from_string(model, &source, path_str_source);
gtk_tree_model_get_iter(model, &dest, dest_path);
gchar *conversationUidSrc = nullptr;
gchar *conversationUidDest = nullptr;
gtk_tree_model_get(model, &source,
0, &conversationUidSrc,
-1);
gtk_tree_model_get(model, &dest,
0, &conversationUidDest,
-1);
(*priv->accountInfo_)->conversationModel->joinConversations(
conversationUidSrc,
conversationUidDest
);
gtk_tree_path_free(dest_path);
g_free(conversationUidSrc);
g_free(conversationUidDest);
success = TRUE;
switch (target_type) {
case TEXT_URI_LIST_TARGET_ID: {
GtkTreeIter dest;
gtk_tree_model_get_iter(model, &dest, dest_path);
gchar *conversationUid = nullptr;
gtk_tree_model_get(model, &dest, 0, &conversationUid, -1);
foreach_file(path_str_source, [&](const char* file) {
(*priv->accountInfo_)->conversationModel->sendFile(
conversationUid, file, g_path_get_basename(file));
});
gtk_tree_path_free(dest_path);
g_free(conversationUid);
success = TRUE;
break;
}
default:
g_warning("unhandled drag and drop target type");
success = FALSE;
}
}
}
......@@ -826,20 +765,16 @@ build_conversations_view(ConversationsView *self)
g_signal_connect_swapped(self, "button-press-event", G_CALLBACK(show_popup_menu), self);
/* drag and drop */
static GtkTargetEntry targetentries[] = {
{ (gchar *)CALL_TARGET, GTK_TARGET_SAME_WIDGET, CALL_TARGET_ID },
static GtkTargetEntry target_entries[] = {
{ (gchar *)TEXT_URI_LIST_TARGET, GTK_TARGET_OTHER_APP, TEXT_URI_LIST_TARGET_ID },
};
gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(self),
GDK_BUTTON1_MASK, targetentries, 1, (GdkDragAction)(GDK_ACTION_DEFAULT | GDK_ACTION_MOVE));
gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(self),
targetentries, 1, GDK_ACTION_DEFAULT);
target_entries, 1,
(GdkDragAction)(GDK_ACTION_DEFAULT | GDK_ACTION_COPY));
g_signal_connect(self, "drag-data-get", G_CALLBACK(on_drag_data_get), nullptr);
g_signal_connect(self, "drag-drop", G_CALLBACK(on_drag_drop), nullptr);
g_signal_connect(self, "drag-motion", G_CALLBACK(on_drag_motion), nullptr);
g_signal_connect(self, "drag_data_received", G_CALLBACK(on_drag_data_received), nullptr);
g_signal_connect(self, "drag-data-received", G_CALLBACK(on_drag_data_received), nullptr);
priv->cpp = new details::CppImpl();
}
......
......@@ -206,3 +206,25 @@ get_settings_schema()
return schema.get();
}
void
foreach_file(const gchar *uris, const std::function<void(const char*)>& cb)
{
guint i;
gchar **res = g_strsplit(uris, "\r\n", 0);
for (i = 0; res[i] != nullptr; i++) {
if (g_strcmp0(res[i], "") != 0) {
GError *error = nullptr;
auto* filename = g_filename_from_uri(res[i], nullptr, &error);
if (error) {
g_warning("Unable to exec g_filename_from_uri on %s", res[i]);
g_error_free(error);
}
else {
cb(filename);
}
g_free(filename);
}
}
g_strfreev(res);
}
......@@ -20,6 +20,7 @@
#ifndef _FILES_H
#define _FILES_H
#include <functional>
#include <gio/gio.h>
G_BEGIN_DECLS
......@@ -28,6 +29,14 @@ void autostart_symlink(gboolean autostart);
GSettingsSchema *get_settings_schema();
/**
* Split the string `uris' using `g_strsplit()' with "\r\n" as the delimiter,
* passing each split part through `g_filename_from_uri()', and passing the
* result as an argument to the provided `cb' callback function.
*/
void
foreach_file(const gchar *uris, const std::function<void(const char*)>& cb);
G_END_DECLS
#endif /* _FILES_H */
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment