From ea8f779f7342d749a116ae5ae697c3af72f42c13 Mon Sep 17 00:00:00 2001 From: Javier Date: Sat, 24 Jan 2015 02:55:02 +0100 Subject: drag and drop --- libmdock/mdock-widget.c | 226 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 220 insertions(+), 6 deletions(-) (limited to 'libmdock/mdock-widget.c') diff --git a/libmdock/mdock-widget.c b/libmdock/mdock-widget.c index 1ce9a4d..5e3fbbd 100644 --- a/libmdock/mdock-widget.c +++ b/libmdock/mdock-widget.c @@ -50,6 +50,18 @@ enum { static GParamSpec *obj_properties[N_PROPERTIES] = { NULL }; +G_DEFINE_QUARK(mdock-widget-item-iter, mdock_widget_item_iter) + +enum { + DRAG_TYPE_ITEM, + DRAG_TYPE_URILIST +}; + +static const GtkTargetEntry drag_types[] = { + "application/x-mdock-item", GTK_TARGET_SAME_APP, DRAG_TYPE_ITEM, + "text/uri-list", 0, DRAG_TYPE_URILIST +}; + static void save_items_to_settings(MDockWidget *self) { GSettings *settings = self->priv->settings; @@ -76,6 +88,14 @@ static void save_items_to_settings(MDockWidget *self) self->priv->loading_settings = FALSE; } +static void move_item_to_position(MDockWidget *self, GSequenceIter *iter, GSequenceIter *position) +{ + g_debug("moving item to position %d", g_sequence_iter_get_position(position)); + gtk_box_reorder_child(GTK_BOX(self), GTK_WIDGET(g_sequence_get(iter)), + g_sequence_iter_get_position(position)); + g_sequence_move(iter, position); +} + static void handle_item_pinned_changed(MDockWidget *self, GParamSpec *spec, MDockItem *item) { if (!self->priv->loading_settings) { @@ -83,8 +103,171 @@ static void handle_item_pinned_changed(MDockWidget *self, GParamSpec *spec, MDoc } } -static void connect_item(MDockWidget *self, MDockItem *item) +static void handle_item_drag_begin(MDockWidget *self, GdkDragContext *dc, MDockItem *item) +{ + g_debug("drag begin"); + gtk_drag_set_icon_pixbuf(dc, mdock_item_get_icon_pixbuf(item), 0, 0); + gtk_widget_hide(GTK_WIDGET(item)); +} + +static void handle_item_drag_end(MDockWidget *self, GdkDragContext *dc, MDockItem *item) +{ + g_debug("drag end"); + gtk_widget_show(GTK_WIDGET(item)); +} + +static void handle_item_drag_data_get(MDockWidget *self, GdkDragContext *dc, + GtkSelectionData *selection_data, guint target_type, guint timestamp, MDockItem *item) +{ + g_debug("data get"); + GSequenceIter *iter = g_object_get_qdata(G_OBJECT(item), mdock_widget_item_iter_quark()); + GDesktopAppInfo *appinfo = mdock_item_get_desktop_app_info(item); + gint32 position = g_sequence_iter_get_position(iter); + + switch (target_type) { + case DRAG_TYPE_ITEM: + g_debug("data get item"); + gtk_selection_data_set(selection_data, gtk_selection_data_get_target(selection_data), + sizeof(gint32)*8, (guchar*)&position, sizeof(gint32)); + break; + case DRAG_TYPE_URILIST: + g_debug("data get urilist"); + if (appinfo) { + const gchar *strv[2] = { g_desktop_app_info_get_filename(appinfo), NULL }; + gtk_selection_data_set_uris(selection_data, (gchar**) strv); + } + break; + } +} + +static void handle_item_drag_data_delete(MDockWidget *self, GdkDragContext *dc, MDockItem *item) +{ + g_debug("data delete"); +} + +static GSequenceIter * find_drop_position_at_coordinates(MDockWidget *self, gint x, gint y) +{ + const guint icon_size = g_settings_get_uint(self->priv->settings, "icon-size"); + const guint spacing = gtk_box_get_spacing(GTK_BOX(self)); + const guint offset = icon_size / 4; + if (gtk_orientable_get_orientation(GTK_ORIENTABLE(self)) == GTK_ORIENTATION_HORIZONTAL) { + g_debug("dropped in position %d", x / icon_size); + return g_sequence_get_iter_at_pos(self->priv->items, (x + offset) / (icon_size + spacing)) ; + } else { + return g_sequence_get_iter_at_pos(self->priv->items, (y + offset) / (icon_size + spacing)); + } +} + +static void mdock_widget_drag_data_received(GtkWidget *widget, GdkDragContext *context, + gint x, gint y, GtkSelectionData *selection_data, + guint info, guint timestamp) { + g_debug("data received"); + MDockWidget *self = MDOCK_WIDGET(widget); + const guchar *data = gtk_selection_data_get_data(selection_data); + gchar **uris; + + switch (info) { + case DRAG_TYPE_ITEM: + g_debug("Got item"); + if (data) { + gint32 position = *(gint32*)data; + g_debug("Got position %u", position); + GSequenceIter *iter = g_sequence_get_iter_at_pos(self->priv->items, position); + g_warn_if_fail(iter); + GSequenceIter *pos_iter = find_drop_position_at_coordinates(self, x, y); + move_item_to_position(self, iter, pos_iter); + if (mdock_item_get_pinned(MDOCK_ITEM(g_sequence_get(iter)))) { + save_items_to_settings(self); + } + } + gtk_drag_finish(context, TRUE, TRUE, timestamp); + break; + case DRAG_TYPE_URILIST: + uris = gtk_selection_data_get_uris(selection_data); + if (uris) { + for (int i = 0; uris[i]; i++) { + gchar *uri = uris[i]; + g_debug("Got uri %s", uri); + } + g_strfreev(uris); + } + + gtk_drag_finish(context, TRUE, FALSE, timestamp); + break; + default: + g_message("Drag target type not found"); + gtk_drag_finish(context, FALSE, FALSE, timestamp); + break; + } +} + +static gboolean mdock_widget_drag_drop(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint timestamp) +{ + GtkTargetList *target_list = gtk_drag_dest_get_target_list(widget); + GdkAtom target = gtk_drag_dest_find_target(widget, context, target_list); + guint info; + gboolean drag_ok = FALSE; + + if (target != GDK_NONE && gtk_target_list_find(target_list, target, &info)) { + switch (info) { + case DRAG_TYPE_ITEM: + case DRAG_TYPE_URILIST: + drag_ok = TRUE; + break; + default: + break; + } + } else { + g_message("Drag target type not found"); + } + + if (drag_ok) { + gtk_drag_get_data(widget, context, target, timestamp); + } else { + gtk_drag_finish(context, FALSE, FALSE, timestamp); + } + + return TRUE; +} + +static gboolean mdock_widget_drag_motion(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint timestamp) +{ + GtkTargetList *target_list = gtk_drag_dest_get_target_list(widget); + GdkAtom target = gtk_drag_dest_find_target(widget, context, target_list); + guint info; + gboolean drag_ok = FALSE; + + if (target != GDK_NONE && gtk_target_list_find(target_list, target, &info)) { + switch (info) { + case DRAG_TYPE_ITEM: + gdk_drag_status(context, GDK_ACTION_MOVE, timestamp); + drag_ok = TRUE; + break; + case DRAG_TYPE_URILIST: + gdk_drag_status(context, GDK_ACTION_COPY, timestamp); + drag_ok = TRUE; + break; + default: + break; + } + } else { + g_message("Drag target type not found"); + } + + if (drag_ok) { + // TODO Draw placeholder + } else { + gdk_drag_status(context, 0, timestamp); + } + + return TRUE; +} + +static void connect_item(MDockWidget *self, MDockItem *item, GSequenceIter *position) +{ + g_object_set_qdata(G_OBJECT(item), mdock_widget_item_iter_quark(), position); + g_signal_connect_object(item, "notify::pinned", G_CALLBACK(handle_item_pinned_changed), self, G_CONNECT_SWAPPED); @@ -92,6 +275,22 @@ static void connect_item(MDockWidget *self, MDockItem *item) g_settings_bind(self->priv->settings, "icon-size", item, "icon-size", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_NO_SENSITIVITY); + + gtk_drag_source_set(GTK_WIDGET(item), GDK_BUTTON1_MASK, + drag_types, G_N_ELEMENTS(drag_types), + GDK_ACTION_MOVE); + g_signal_connect_object(item, "drag-begin", + G_CALLBACK(handle_item_drag_begin), self, + G_CONNECT_SWAPPED); + g_signal_connect_object(item, "drag-end", + G_CALLBACK(handle_item_drag_end), self, + G_CONNECT_SWAPPED); + g_signal_connect_object(item, "drag-data-get", + G_CALLBACK(handle_item_drag_data_get), self, + G_CONNECT_SWAPPED); + g_signal_connect_object(item, "drag-data-delete", + G_CALLBACK(handle_item_drag_data_delete), self, + G_CONNECT_SWAPPED); } @@ -123,12 +322,18 @@ static void reload_items_from_settings(MDockWidget *self) mdock_item_set_desktop_app_info(item, set_appinfo); mdock_item_set_pinned(item, TRUE); - connect_item(self, item); - iter = g_sequence_insert_before(seq_iter, item); g_hash_table_insert(self->priv->desktopid_to_item, g_strdup(set_desktopid), iter); + + connect_item(self, item, iter); + gtk_widget_show(GTK_WIDGET(item)); - gtk_box_pack_start(GTK_BOX(self), GTK_WIDGET(item), TRUE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(self), GTK_WIDGET(item), FALSE, FALSE, 0); + if (!g_sequence_iter_is_end(seq_iter)) { + // Fix the order! + gtk_box_reorder_child(GTK_BOX(self), GTK_WIDGET(item), + g_sequence_iter_get_position(iter)); + } } g_object_unref(set_appinfo); @@ -212,7 +417,7 @@ static void handle_window_opened(MDockWidget *self, WnckWindow *window, WnckScre g_object_unref(app_info); } - connect_item(self, item); + connect_item(self, item, iter); mdock_item_add_window(item, window); @@ -223,7 +428,7 @@ static void handle_window_opened(MDockWidget *self, WnckWindow *window, WnckScre g_hash_table_insert(self->priv->window_to_item, window, iter); gtk_widget_show(GTK_WIDGET(item)); - gtk_box_pack_start(GTK_BOX(self), GTK_WIDGET(item), TRUE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(self), GTK_WIDGET(item), FALSE, FALSE, 0); } static void handle_settings_items_changed(MDockWidget *self, gchar *key, GSettings *settings) @@ -327,10 +532,19 @@ static void mdock_widget_get_property(GObject *object, static void mdock_widget_init(MDockWidget *self) { self->priv = MDOCK_WIDGET_GET_PRIVATE(self); + + gtk_drag_dest_set(GTK_WIDGET(self), 0, + drag_types, G_N_ELEMENTS(drag_types), + GDK_ACTION_MOVE); } static void mdock_widget_class_init(MDockWidgetClass *klass) { + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + widget_class->drag_data_received = mdock_widget_drag_data_received; + widget_class->drag_drop = mdock_widget_drag_drop; + widget_class->drag_motion = mdock_widget_drag_motion; + GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->constructed = mdock_widget_constructed; obj_class->dispose = mdock_widget_dispose; -- cgit v1.2.3