--- a/ui/gtk/list.c Tue Nov 26 11:38:10 2024 +0100 +++ b/ui/gtk/list.c Wed Nov 27 13:27:30 2024 +0100 @@ -35,9 +35,12 @@ #include "../common/object.h" #include "container.h" +#include <cx/array_list.h> + #include "list.h" #include "icon.h" #include "menu.h" +#include "dnd.h" void* ui_strmodel_getvalue(void *elm, int column) { @@ -366,6 +369,12 @@ tableview->widget = view; tableview->var = var; tableview->model = model; + tableview->ondragstart = args.ondragstart; + tableview->ondragstartdata = args.ondragstartdata; + tableview->ondragcomplete = args.ondragcomplete; + tableview->ondragcompletedata = args.ondragcompletedata; + tableview->ondrop = args.ondrop; + tableview->ondropdata = args.ondropsdata; g_signal_connect( view, "destroy", @@ -403,6 +412,13 @@ } // TODO: destroy callback + + if(args.ondragstart) { + ui_listview_add_dnd(tableview, &args); + } + if(args.ondrop) { + ui_listview_enable_drop(tableview, &args); + } GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view)); if(args.multiselection) { @@ -432,6 +448,234 @@ return scroll_area; } +#if GTK_MAJOR_VERSION >= 4 + +static GdkContentProvider *ui_listview_dnd_prepare(GtkDragSource *source, double x, double y, void *data) { + //printf("drag prepare\n"); + UiListView *listview = data; + + UiDnD *dnd = ui_create_dnd(); + GdkContentProvider *provider = NULL; + + + if(listview->ondragstart) { + UiEvent event; + event.obj = listview->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = dnd; + event.intval = 0; + listview->ondragstart(&event, listview->ondragstartdata); + } + + size_t numproviders = cxListSize(dnd->providers); + if(numproviders > 0) { + GdkContentProvider **providers = (GdkContentProvider**)cxListAt(dnd->providers, 0); + provider = gdk_content_provider_new_union(providers, numproviders); + } + ui_dnd_free(dnd); + + return provider; +} + +static void ui_listview_drag_begin(GtkDragSource *self, GdkDrag *drag, gpointer userdata) { + //printf("drag begin\n"); +} + +static void ui_listview_drag_end(GtkDragSource *self, GdkDrag *drag, gboolean delete_data, gpointer user_data) { + //printf("drag end\n"); + UiListView *listview = user_data; + if(listview->ondragcomplete) { + UiDnD dnd; + dnd.target = NULL; + dnd.value = NULL; + dnd.providers = NULL; + dnd.selected_action = gdk_drag_get_selected_action(drag); + dnd.delete = delete_data; + dnd.accept = FALSE; + + UiEvent event; + event.obj = listview->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = &dnd; + event.intval = 0; + listview->ondragcomplete(&event, listview->ondragcompletedata); + } +} + +static gboolean ui_listview_drop( + GtkDropTarget *target, + const GValue* value, + gdouble x, + gdouble y, + gpointer user_data) +{ + UiListView *listview = user_data; + UiDnD dnd; + dnd.providers = NULL; + dnd.target = target; + dnd.value = value; + dnd.selected_action = 0; + dnd.delete = FALSE; + dnd.accept = FALSE; + + if(listview->ondrop) { + dnd.accept = TRUE; + UiEvent event; + event.obj = listview->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = &dnd; + event.intval = 0; + listview->ondrop(&event, listview->ondropdata); + } + + return dnd.accept; +} + +void ui_listview_add_dnd(UiListView *listview, UiListArgs *args) { + GtkDragSource *dragsource = gtk_drag_source_new(); + gtk_widget_add_controller(listview->widget, GTK_EVENT_CONTROLLER(dragsource)); + g_signal_connect (dragsource, "prepare", G_CALLBACK (ui_listview_dnd_prepare), listview); + g_signal_connect( + dragsource, + "drag-begin", + G_CALLBACK(ui_listview_drag_begin), + listview); + g_signal_connect( + dragsource, + "drag-end", + G_CALLBACK(ui_listview_drag_end), + listview); +} + +void ui_listview_enable_drop(UiListView *listview, UiListArgs *args) { + GtkDropTarget *target = gtk_drop_target_new(G_TYPE_INVALID, GDK_ACTION_COPY); + gtk_widget_add_controller(listview->widget, GTK_EVENT_CONTROLLER(target)); + GType default_types[2] = { GDK_TYPE_FILE_LIST, G_TYPE_STRING }; + gtk_drop_target_set_gtypes(target, default_types, 2); + g_signal_connect(target, "drop", G_CALLBACK(ui_listview_drop), listview); +} + +#else + +static GtkTargetEntry targetentries[] = +{ + { "STRING", 0, 0 }, + { "text/plain", 0, 1 }, + { "text/uri-list", 0, 2 }, +}; + +static void ui_listview_drag_getdata( + GtkWidget* self, + GdkDragContext* context, + GtkSelectionData* data, + guint info, + guint time, + gpointer user_data) +{ + UiListView *listview = user_data; + UiDnD dnd; + dnd.context = context; + dnd.data = data; + dnd.selected_action = 0; + dnd.delete = FALSE; + dnd.accept = FALSE; + + if(listview->ondragstart) { + UiEvent event; + event.obj = listview->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = &dnd; + event.intval = 0; + listview->ondragstart(&event, listview->ondragstartdata); + } +} + +static void ui_listview_drag_end( + GtkWidget *widget, + GdkDragContext *context, + guint time, + gpointer user_data) +{ + UiListView *listview = user_data; + UiDnD dnd; + dnd.context = context; + dnd.data = NULL; + dnd.selected_action = gdk_drag_context_get_selected_action(context); + dnd.delete = dnd.selected_action == UI_DND_ACTION_MOVE ? TRUE : FALSE; + dnd.accept = FALSE; + if(listview->ondragcomplete) { + UiEvent event; + event.obj = listview->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = &dnd; + event.intval = 0; + listview->ondragcomplete(&event, listview->ondragcompletedata); + } +} + +void ui_listview_add_dnd(UiListView *listview, UiListArgs *args) { + gtk_tree_view_enable_model_drag_source( + GTK_TREE_VIEW(listview->widget), + GDK_BUTTON1_MASK, + targetentries, + 2, + GDK_ACTION_COPY); + + g_signal_connect(listview->widget, "drag-data-get", G_CALLBACK(ui_listview_drag_getdata), listview); + g_signal_connect(listview->widget, "drag-end", G_CALLBACK(ui_listview_drag_end), listview); +} + + + + +static void ui_listview_drag_data_received( + GtkWidget *self, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint time, + gpointer user_data) +{ + UiListView *listview = user_data; + UiDnD dnd; + dnd.context = context; + dnd.data = data; + dnd.selected_action = 0; + dnd.delete = FALSE; + dnd.accept = FALSE; + + if(listview->ondrop) { + dnd.accept = TRUE; + UiEvent event; + event.obj = listview->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = &dnd; + event.intval = 0; + listview->ondrop(&event, listview->ondropdata); + } +} + +void ui_listview_enable_drop(UiListView *listview, UiListArgs *args) { + gtk_tree_view_enable_model_drag_dest( + GTK_TREE_VIEW(listview->widget), + targetentries, + 3, + GDK_ACTION_COPY); + if(listview->ondrop) { + g_signal_connect(listview->widget, "drag_data_received", G_CALLBACK(ui_listview_drag_data_received), listview); + } +} + +#endif + GtkWidget* ui_get_tree_widget(UIWIDGET widget) { return SCROLLEDWINDOW_GET_CHILD(widget);