diff --git a/src/chatview.cpp b/src/chatview.cpp index 3d74df027eb970e0f78908571a10fce7069a4329..af39c7e60f0f6a4047d39679aa0fe31f5c6e9363 100644 --- a/src/chatview.cpp +++ b/src/chatview.cpp @@ -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 diff --git a/src/conversationsview.cpp b/src/conversationsview.cpp index fa2a88317ad3d71823ad47cbe178f086c45f00b8..972d7b9d322af57a1a8c09318a4d271db338c8b1 100644 --- a/src/conversationsview.cpp +++ b/src/conversationsview.cpp @@ -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(); } diff --git a/src/utils/files.cpp b/src/utils/files.cpp index 9d11137639a0f20662dc6a6f52fba7f116375c8b..dedce6e231121bc66a7a6e2fa3e28eaf5210aee8 100644 --- a/src/utils/files.cpp +++ b/src/utils/files.cpp @@ -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); +} diff --git a/src/utils/files.h b/src/utils/files.h index 9f7bc47621db71b58e9d3e27abd0d695414a662d..8f4bd27c5fe90927cf2781a3165e95b47b685070 100644 --- a/src/utils/files.h +++ b/src/utils/files.h @@ -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 */