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-item.c | 29 ++++++- libmdock/mdock-item.h | 2 + libmdock/mdock-widget.c | 226 ++++++++++++++++++++++++++++++++++++++++++++++-- test/mdock-standalone.c | 2 + 4 files changed, 249 insertions(+), 10 deletions(-) diff --git a/libmdock/mdock-item.c b/libmdock/mdock-item.c index 2399e19..a9d9ad0 100644 --- a/libmdock/mdock-item.c +++ b/libmdock/mdock-item.c @@ -166,6 +166,24 @@ static void mdock_item_size_allocate(GtkWidget *widget, GtkAllocation *allocatio } static gboolean mdock_item_button_press(GtkWidget *widget, GdkEventButton *event) +{ + MDockItem *self = MDOCK_ITEM(widget); + switch (event->button) { + case 1: + // Will activate item during release. + break; + + case 3: + gtk_menu_popup(self->priv->right_menu, + NULL, NULL, NULL, NULL, + event->button, event->time); + break; + } + + return TRUE; +} + +static gboolean mdock_item_button_release(GtkWidget *widget, GdkEventButton *event) { MDockItem *self = MDOCK_ITEM(widget); switch (event->button) { @@ -209,13 +227,10 @@ static gboolean mdock_item_button_press(GtkWidget *widget, GdkEventButton *event } else { g_warning("A MDockItem has no windows and no appinfo to launch"); } - break; case 3: - gtk_menu_popup(self->priv->right_menu, - NULL, NULL, NULL, NULL, - event->button, event->time); + // Popup menu shown during press break; } @@ -353,6 +368,7 @@ mdock_item_class_init(MDockItemClass *klass) widget_class->size_request = mdock_item_size_request; widget_class->size_allocate = mdock_item_size_allocate; widget_class->button_press_event = mdock_item_button_press; + widget_class->button_release_event = mdock_item_button_release; widget_class->expose_event = mdock_item_expose; obj_properties[PROP_PINNED] = g_param_spec_boolean("pinned", @@ -437,6 +453,11 @@ void mdock_item_launch(MDockItem *self) } } +GdkPixbuf *mdock_item_get_icon_pixbuf(MDockItem *self) +{ + return self->priv->icon; +} + void mdock_item_add_window(MDockItem *self, WnckWindow *window) { thumbnailer_enable_for_window(window); diff --git a/libmdock/mdock-item.h b/libmdock/mdock-item.h index 328187e..082805e 100644 --- a/libmdock/mdock-item.h +++ b/libmdock/mdock-item.h @@ -60,6 +60,8 @@ GDesktopAppInfo *mdock_item_get_desktop_app_info(MDockItem *self); void mdock_item_set_desktop_app_info(MDockItem *self, GDesktopAppInfo *app_info); void mdock_item_launch(MDockItem *self); +GdkPixbuf *mdock_item_get_icon_pixbuf(MDockItem *self); + void mdock_item_add_window(MDockItem *self, WnckWindow *window); void mdock_item_remove_window(MDockItem *self, WnckWindow *window); void mdock_item_set_last_active_window(MDockItem *self, WnckWindow *window); 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; diff --git a/test/mdock-standalone.c b/test/mdock-standalone.c index d977dea..53453c5 100644 --- a/test/mdock-standalone.c +++ b/test/mdock-standalone.c @@ -42,6 +42,8 @@ int main(int argc, char **argv) gtk_window_set_keep_above(mainwin, TRUE); gtk_window_set_skip_taskbar_hint(mainwin, TRUE); + gtk_window_set_accept_focus(mainwin, FALSE); + gtk_window_set_resizable(mainwin, FALSE); gtk_widget_show_all(GTK_WIDGET(mainwin)); gtk_main(); -- cgit v1.2.3