// dragon - very lightweight DnD file source/target // Copyright 2014 Michael Homer. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #define _POSIX_C_SOURCE 200809L #define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #define VERSION "0.3.0" GtkWidget *window; GtkWidget *vbox; char *progname; bool verbose = false; int mode = 0; bool and_exit; bool keep; #define MODE_HELP 1 #define MODE_TARGET 2 #define MODE_VERSION 4 #define TARGET_TYPE_TEXT 1 #define TARGET_TYPE_URI 2 struct draggable_thing { char *text; char *uri; guint last_time; }; void add_target_button(); void do_quit(GtkWidget *widget, gpointer data) { exit(0); } void button_clicked(GtkWidget *widget, gpointer user_data) { struct draggable_thing *dd = (struct draggable_thing *)user_data; if (0 == fork()) { execlp("xdg-open", "xdg-open", dd->uri, NULL); } } void drag_end(GtkWidget *widget, GdkDragContext *context, gpointer user_data) { if (and_exit) gtk_main_quit(); } void drag_data_get(GtkWidget *widget, GdkDragContext *context, GtkSelectionData *data, guint info, guint time, gpointer user_data) { struct draggable_thing *dd = (struct draggable_thing *)user_data; if (info == TARGET_TYPE_URI) { if (verbose) printf("Writing as URI: %s\n", dd->uri); char *uris[] = {dd->uri, NULL}; gtk_selection_data_set_uris(data, uris); g_signal_stop_emission_by_name(widget, "drag-data-get"); } else if (info == TARGET_TYPE_TEXT) { if (verbose) printf("Writing as TEXT: %s\n", dd->text); gtk_selection_data_set_text(data, dd->text, -1); } else { fprintf(stderr, "Error: bad target type %i\n", info); } } void add_button(char *label, struct draggable_thing *dragdata, int type) { GtkWidget *button = gtk_button_new(); gtk_button_set_label(GTK_BUTTON(button), label); GtkTargetList *targetlist = gtk_drag_source_get_target_list(GTK_WIDGET(button)); if (targetlist) gtk_target_list_ref(targetlist); else targetlist = gtk_target_list_new(NULL, 0); if (type == TARGET_TYPE_URI) gtk_target_list_add_uri_targets(targetlist, TARGET_TYPE_URI); else gtk_target_list_add_text_targets(targetlist, TARGET_TYPE_TEXT); gtk_drag_source_set(GTK_WIDGET(button), GDK_BUTTON1_MASK, NULL, 0, GDK_ACTION_DEFAULT | GDK_ACTION_LINK | GDK_ACTION_COPY); gtk_drag_source_set_target_list(GTK_WIDGET(button), targetlist); g_signal_connect(GTK_WIDGET(button), "drag-data-get", G_CALLBACK(drag_data_get), dragdata); g_signal_connect(GTK_WIDGET(button), "clicked", G_CALLBACK(button_clicked), dragdata); g_signal_connect(GTK_WIDGET(button), "drag-end", G_CALLBACK(drag_end), dragdata); gtk_container_add(GTK_CONTAINER(vbox), button); } void add_file_button(char *ufilename, char *filename) { char *uri = malloc(strlen(filename) + 8); // file:// + NULL strcpy(uri, "file://"); strcat(uri, filename); struct draggable_thing *dragdata = malloc(sizeof(struct draggable_thing)); dragdata->text = filename; dragdata->uri = uri; add_button(ufilename, dragdata, TARGET_TYPE_URI); } void add_uri_button(char *uri) { struct draggable_thing *dragdata = malloc(sizeof(struct draggable_thing)); dragdata->text = uri; dragdata->uri = uri; add_button(uri, dragdata, TARGET_TYPE_URI); } bool is_uri(char *uri) { for (int i=0; uri[i]; i++) if (uri[i] == '/') return false; else if (uri[i] == ':') return true; return false; } gboolean drag_drop (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gpointer user_data) { GtkTargetList *targetlist = gtk_drag_dest_get_target_list(widget); GList *list = gdk_drag_context_list_targets(context); if (list) { while (list) { GdkAtom atom = (GdkAtom)g_list_nth_data(list, 0); if (gtk_target_list_find(targetlist, GDK_POINTER_TO_ATOM(g_list_nth_data(list, 0)), NULL)) { gtk_drag_get_data(widget, context, atom, time); return true; } list = g_list_next(list); } } gtk_drag_finish(context, false, false, time); return true; } void drag_data_received (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *data, guint info, guint time) { gchar **uris = gtk_selection_data_get_uris(data); unsigned char *text = gtk_selection_data_get_text(data); if (!uris && !text) gtk_drag_finish (context, FALSE, FALSE, time); if (uris) { gtk_container_remove(GTK_CONTAINER(vbox), widget); for (; *uris; uris++) { printf("%s\n", *uris); if (keep) add_uri_button(*uris); } add_target_button(); gtk_widget_show_all(window); } else if (text) { printf("%s\n", text); } gtk_drag_finish (context, TRUE, FALSE, time); if (and_exit) gtk_main_quit(); } void add_target_button() { GtkWidget *label = gtk_button_new(); gtk_button_set_label(GTK_BUTTON(label), "Drag something here..."); gtk_container_add(GTK_CONTAINER(vbox), label); GtkTargetList *targetlist = gtk_drag_dest_get_target_list(GTK_WIDGET(label)); if (targetlist) gtk_target_list_ref(targetlist); else targetlist = gtk_target_list_new(NULL, 0); gtk_target_list_add_text_targets(targetlist, TARGET_TYPE_TEXT); gtk_target_list_add_uri_targets(targetlist, TARGET_TYPE_URI); gtk_drag_dest_set(GTK_WIDGET(label), GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT, NULL, 0, GDK_ACTION_COPY); gtk_drag_dest_set_target_list(GTK_WIDGET(label), targetlist); g_signal_connect(GTK_WIDGET(label), "drag-drop", G_CALLBACK(drag_drop), NULL); g_signal_connect(GTK_WIDGET(label), "drag-data-received", G_CALLBACK(drag_data_received), NULL); } void target_mode() { add_target_button(); gtk_widget_show_all(window); gtk_main(); } int main (int argc, char **argv) { progname = argv[0]; char *filename = NULL; char *ufilename = NULL; for (int i=1; i