// 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
#include
#define VERSION "0.3.0"
GtkWidget *window;
GtkWidget *vbox;
GtkIconTheme *icon_theme;
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);
}
}
GtkButton *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);
return (GtkButton *)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;
GtkButton *button = add_button(ufilename, dragdata, TARGET_TYPE_URI);
GFile *file = g_file_new_for_path(filename);
GFileInfo *fileinfo = g_file_query_info(file, "*", 0, NULL, NULL);
GIcon *icon = g_file_info_get_icon(fileinfo);
GtkIconInfo *icon_info = gtk_icon_theme_lookup_by_gicon(icon_theme,
icon, 48, GTK_ICON_LOOKUP_GENERIC_FALLBACK);
gtk_button_set_image(button,
gtk_image_new_from_pixbuf(
gtk_icon_info_load_icon(icon_info, NULL)
));
gtk_button_set_alignment(button, 0, 0);
gtk_button_set_always_show_image(button, true);
}
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