#include static GtkWidget *main_window = NULL; #define DND_INFO_TEXT_URI_LIST 10 #define DND_INFO_STRING 20 static GtkTargetEntry drop_targets[] = { { "text/uri-list", 0, DND_INFO_TEXT_URI_LIST }, { "text/plain", 0, DND_INFO_STRING } }; GdkDragAction select_action(GdkDragAction actions) { GtkWidget *dialog; gint value; dialog = gtk_dialog_new(); gtk_window_set_title(GTK_WINDOW(dialog), "どうしますか?"); gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(main_window)); gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE); gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, FALSE); gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE); if (actions & GDK_ACTION_COPY) { gtk_dialog_add_button(GTK_DIALOG(dialog), "コピー", GDK_ACTION_COPY); } if (actions & GDK_ACTION_MOVE) { gtk_dialog_add_button(GTK_DIALOG(dialog), "移動", GDK_ACTION_MOVE); } if (actions & GDK_ACTION_LINK) { gtk_dialog_add_button(GTK_DIALOG(dialog), "リンク", GDK_ACTION_LINK); } gtk_dialog_add_button(GTK_DIALOG(dialog), "キャンセル", 0); value = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); if (value != 0 && value != GDK_ACTION_COPY && value != GDK_ACTION_MOVE && value != GDK_ACTION_LINK) { value = 0; } return value; } gboolean drag_motion(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time_, gpointer user_data) { GList *list; GtkTargetList *target_list; GdkAtom target; gint width; gint height; g_print("<<< DRAG MOTION >>>\n"); for (list = context->targets; list != NULL; list = g_list_next(list)) { g_print("[%s]\n", gdk_atom_name(list->data)); } target_list = gtk_drag_dest_get_target_list(widget); target = gtk_drag_dest_find_target(widget, context, target_list); if (target == GDK_NONE) { gdk_drag_status(context, 0, time_); return FALSE; } gdk_window_get_geometry(widget->window, NULL, NULL, &width, &height, NULL); if (x < width / 2) { if (context->actions & GDK_ACTION_ASK) { g_print("suggested action = ASK\n"); context->suggested_action = GDK_ACTION_ASK; } else { g_print("drop不可\n"); return FALSE; } } else { if (context->actions & GDK_ACTION_COPY) { g_print("suggested action = COPY\n"); context->suggested_action = GDK_ACTION_COPY; } else { g_print("drop不可\n"); return FALSE; } } gdk_drag_status(context, context->suggested_action, time_); gtk_drag_highlight(widget); return TRUE; } void drag_leave(GtkWidget *widget, GdkDragContext *context, guint time_, gpointer user_data) { g_print("<<< DRAG LEAVE >>>\n"); gtk_drag_unhighlight(widget); } gboolean drag_drop(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time_, gpointer user_data) { GtkTargetList *target_list; GdkAtom target; g_print("<<< DRAG DROP >>>\n"); target_list = gtk_drag_dest_get_target_list(widget); target = gtk_drag_dest_find_target(widget, context, target_list); if (target == GDK_NONE) { gtk_drag_finish(context, FALSE, FALSE, time_); return FALSE; } gtk_drag_get_data(widget, context, target, time_); return TRUE; } void drag_data_received(GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *data, guint info, guint time_, gpointer user_data) { gboolean result = FALSE; gboolean delete = FALSE; g_print("<<< DRAG DATA RECEIVED >>>\n"); if (data->length == 0 || data->format != 8) { goto ERROR_EXIT; } if (info == DND_INFO_TEXT_URI_LIST) { gchar **uris; gchar **files; gchar *hostname; GError *error = NULL; gint i; uris = gtk_selection_data_get_uris(data); if (uris == NULL) { g_print("error\n"); goto ERROR_EXIT; } for (i = 0; uris[i] != NULL; i++) ; files = g_new0(gchar *, i + 1); for (i = 0; uris[i] != NULL; i++) { files[i] = g_filename_from_uri(uris[i], &hostname, &error); if (files[i] == NULL) { g_print("error\n"); g_clear_error(&error); g_free(hostname); g_strfreev(files); g_strfreev(uris); goto ERROR_EXIT; } g_free(hostname); } g_strfreev(uris); if (context->suggested_action == GDK_ACTION_ASK) { GdkDragAction action; /* ユーザーに質問する */ action = select_action(context->actions); if (action == 0) { /* キャンセル */ goto ERROR_EXIT; } context->action = action; } if (context->action == GDK_ACTION_COPY) { /* copy */ for (i = 0; files[i] != NULL; i++) { g_print("copy %s\n", files[i]); } result = TRUE; } else if (context->action == GDK_ACTION_MOVE) { /* move */ for (i = 0; files[i] != NULL; i++) { g_print("move %s\n", files[i]); } result = TRUE; } else if (context->action == GDK_ACTION_LINK) { /* symlink, boockmark, ... */ for (i = 0; files[i] != NULL; i++) { g_print("link %s\n", files[i]); } result = TRUE; } else { g_print("error\n"); } g_strfreev(files); } else if (info == DND_INFO_STRING) { if (context->actions & GDK_ACTION_COPY) { context->action = GDK_ACTION_COPY; } if (context->action == GDK_ACTION_COPY) { g_print("string = %*s\n", data->length, data->data); result = TRUE; } } else { g_print("error\n"); } ERROR_EXIT: gtk_drag_finish(context, result, delete, time_); } int main(int argc, char ** argv) { GtkWidget *window; GtkWidget *label; gtk_init(&argc, &argv); main_window = window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "dnd drop test"); gtk_window_set_default_size(GTK_WINDOW(window), 300, 150); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); label = gtk_label_new("ここに落せ"); gtk_widget_set_size_request(label, 200, 100); g_signal_connect(G_OBJECT(label), "drag-motion", G_CALLBACK(drag_motion), NULL); g_signal_connect(G_OBJECT(label), "drag-leave", G_CALLBACK(drag_leave), NULL); g_signal_connect(G_OBJECT(label), "drag-drop", G_CALLBACK(drag_drop), NULL); g_signal_connect(G_OBJECT(label), "drag-data-received", G_CALLBACK(drag_data_received), NULL); gtk_drag_dest_set(label, /*GTK_DEST_DEFAULT_HIGHLIGHT*/0, drop_targets, sizeof(drop_targets) / sizeof(drop_targets[0]), GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK); gtk_container_add(GTK_CONTAINER(window), label); gtk_widget_show_all(window); gtk_main(); return 0; }