[download]
local/src/dragon/dragon.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 GtkWidget *window;
31 GtkWidget *vbox;
32 GtkIconTheme *icon_theme;
33
34 char *progname;
35 bool verbose = false;
36 int mode = 0;
37 bool and_exit;
38 bool keep;
39 bool print_path = false;
40 bool icons_only = false;
41 bool always_on_top = false;
42
43
44
45
46
47
48
49
50 struct draggable_thing {
51 char *text;
52 char *uri;
53 };
54
55
56
57 char** uri_collection;
58 int uri_count;
59 bool drag_all = false;
60
61
62 void add_target_button();
63
64 void do_quit(GtkWidget *widget, gpointer data) {
65 exit(0);
66 }
67
68 void button_clicked(GtkWidget *widget, gpointer user_data) {
69 struct draggable_thing *dd = (struct draggable_thing *)user_data;
70 if (0 == fork()) {
71 execlp("xdg-open", "xdg-open", dd->uri, NULL);
72 }
73 }
74
75 void drag_data_get(GtkWidget *widget,
76 GdkDragContext *context,
77 GtkSelectionData *data,
78 guint info,
79 guint time,
80 gpointer user_data) {
81 struct draggable_thing *dd = (struct draggable_thing *)user_data;
82 if (info == TARGET_TYPE_URI) {
83
84 char** uris;
85 char* single_uri_data[2] = {dd->uri, NULL};
86 if (drag_all) {
87 uri_collection[uri_count] = NULL;
88 uris = uri_collection;
89 } else {
90 uris = single_uri_data;
91 }
92 if (verbose) {
93 if (drag_all)
94 fputs("Sending all as URI\n", stderr);
95 else
96 fprintf(stderr, "Sending as URI: %s\n", dd->uri);
97 }
98
99 gtk_selection_data_set_uris(data, uris);
100 g_signal_stop_emission_by_name(widget, "drag-data-get");
101 } else if (info == TARGET_TYPE_TEXT) {
102 if (verbose)
103 fprintf(stderr, "Sending as TEXT: %s\n", dd->text);
104 gtk_selection_data_set_text(data, dd->text, -1);
105 } else {
106 fprintf(stderr, "Error: bad target type %i\n", info);
107 }
108 }
109
110 void drag_end(GtkWidget *widget, GdkDragContext *context, gpointer user_data) {
111 if (verbose) {
112 gboolean succeeded = gdk_drag_drop_succeeded(context);
113 GdkDragAction action = gdk_drag_context_get_selected_action (context);
114 char* action_str;
115 switch (action) {
116 case GDK_ACTION_COPY:
117 action_str = "COPY"; break;
118 case GDK_ACTION_MOVE:
119 action_str = "MOVE"; break;
120 case GDK_ACTION_LINK:
121 action_str = "LINK"; break;
122 case GDK_ACTION_ASK:
123 action_str = "ASK"; break;
124 default:
125 action_str = malloc(sizeof(char) * 20);
126 snprintf(action_str, 20, "invalid (%d)", action);
127 break;
128 }
129 fprintf(stderr, "Selected drop action: %s; Succeeded: %d\n", action_str, succeeded);
130 if (action_str[0] == 'i')
131 free(action_str);
132 }
133 if (and_exit)
134 gtk_main_quit();
135 }
136
137 GtkButton *add_button(char *label, struct draggable_thing *dragdata, int type) {
138 GtkWidget *button;
139
140 if (icons_only) {
141 button = gtk_button_new();
142 } else {
143 button = gtk_button_new_with_label(label);
144 }
145
146 GtkTargetList *targetlist = gtk_drag_source_get_target_list(GTK_WIDGET(button));
147 if (targetlist)
148 gtk_target_list_ref(targetlist);
149 else
150 targetlist = gtk_target_list_new(NULL, 0);
151 if (type == TARGET_TYPE_URI)
152 gtk_target_list_add_uri_targets(targetlist, TARGET_TYPE_URI);
153 else
154 gtk_target_list_add_text_targets(targetlist, TARGET_TYPE_TEXT);
155
156 gtk_drag_source_set(GTK_WIDGET(button), GDK_BUTTON1_MASK, NULL, 0,
157 GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK);
158 gtk_drag_source_set_target_list(GTK_WIDGET(button), targetlist);
159 g_signal_connect(GTK_WIDGET(button), "drag-data-get",
160 G_CALLBACK(drag_data_get), dragdata);
161 g_signal_connect(GTK_WIDGET(button), "clicked",
162 G_CALLBACK(button_clicked), dragdata);
163 g_signal_connect(GTK_WIDGET(button), "drag-end",
164 G_CALLBACK(drag_end), dragdata);
165
166 gtk_container_add(GTK_CONTAINER(vbox), button);
167
168 if (drag_all) {
169 if (uri_count < MAX_SIZE) {
170 uri_collection[uri_count++] = dragdata->uri;
171 } else {
172 fprintf(stderr, "Exceeded maximum number of files for drag_all (%d)\n", MAX_SIZE);
173 }
174 }
175
176 return (GtkButton *)button;
177 }
178
179 void left_align_button(GtkButton *button) {
180 GList *child = g_list_first(
181 gtk_container_get_children(GTK_CONTAINER(button)));
182 if (child)
183 gtk_widget_set_halign(GTK_WIDGET(child->data), GTK_ALIGN_START);
184 }
185
186 GtkIconInfo* icon_info_from_content_type(char *content_type) {
187 GIcon *icon = g_content_type_get_icon(content_type);
188 return gtk_icon_theme_lookup_by_gicon(icon_theme, icon, 48, 0);
189 }
190
191 void add_file_button(GFile *file) {
192 char *filename = g_file_get_path(file);
193 if(!g_file_query_exists(file, NULL)) {
194 fprintf(stderr, "The file `%s' does not exist.\n",
195 filename);
196 exit(1);
197 }
198 char *uri = g_file_get_uri(file);
199 struct draggable_thing *dragdata = malloc(sizeof(struct draggable_thing));
200 dragdata->text = filename;
201 dragdata->uri = uri;
202
203 GtkButton *button = add_button(filename, dragdata, TARGET_TYPE_URI);
204 GFileInfo *fileinfo = g_file_query_info(file, "*", 0, NULL, NULL);
205 GIcon *icon = g_file_info_get_icon(fileinfo);
206 GtkIconInfo *icon_info = gtk_icon_theme_lookup_by_gicon(icon_theme,
207 icon, 48, 0);
208
209
210 if (!icon_info)
211 icon_info = icon_info_from_content_type("application/octet-stream");
212 if (!icon_info)
213 icon_info = icon_info_from_content_type("text/x-generic");
214 if (!icon_info)
215 icon_info = icon_info_from_content_type("text/plain");
216
217 if (icon_info) {
218 GtkWidget *image = gtk_image_new_from_pixbuf(
219 gtk_icon_info_load_icon(icon_info, NULL));
220 gtk_button_set_image(button, image);
221 gtk_button_set_always_show_image(button, true);
222 }
223
224 if (!icons_only)
225 left_align_button(button);
226 }
227
228 void add_filename_button(char *filename) {
229 GFile *file = g_file_new_for_path(filename);
230 add_file_button(file);
231 }
232
233 void add_uri_button(char *uri) {
234 struct draggable_thing *dragdata = malloc(sizeof(struct draggable_thing));
235 dragdata->text = uri;
236 dragdata->uri = uri;
237 GtkButton *button = add_button(uri, dragdata, TARGET_TYPE_URI);
238 left_align_button(button);
239 }
240
241 bool is_uri(char *uri) {
242 for (int i=0; uri[i]; i++)
243 if (uri[i] == '/')
244 return false;
245 else if (uri[i] == ':')
246 return true;
247 return false;
248 }
249
250 bool is_file_uri(char *uri) {
251 char *prefix = "file:";
252 return strncmp(prefix, uri, strlen(prefix)) == 0;
253 }
254
255 gboolean drag_drop (GtkWidget *widget,
256 GdkDragContext *context,
257 gint x,
258 gint y,
259 guint time,
260 gpointer user_data) {
261 GtkTargetList *targetlist = gtk_drag_dest_get_target_list(widget);
262 GList *list = gdk_drag_context_list_targets(context);
263 if (list) {
264 while (list) {
265 GdkAtom atom = (GdkAtom)g_list_nth_data(list, 0);
266 if (gtk_target_list_find(targetlist,
267 GDK_POINTER_TO_ATOM(g_list_nth_data(list, 0)), NULL)) {
268 gtk_drag_get_data(widget, context, atom, time);
269 return true;
270 }
271 list = g_list_next(list);
272 }
273 }
274 gtk_drag_finish(context, false, false, time);
275 return true;
276 }
277
278 void
279 drag_data_received (GtkWidget *widget,
280 GdkDragContext *context,
281 gint x,
282 gint y,
283 GtkSelectionData *data,
284 guint info,
285 guint time) {
286 gchar **uris = gtk_selection_data_get_uris(data);
287 unsigned char *text = gtk_selection_data_get_text(data);
288 if (!uris && !text)
289 gtk_drag_finish (context, FALSE, FALSE, time);
290 if (uris) {
291 if (verbose)
292 fputs("Received URIs\n", stderr);
293 gtk_container_remove(GTK_CONTAINER(vbox), widget);
294 for (; *uris; uris++) {
295 if (is_file_uri(*uris)) {
296 GFile *file = g_file_new_for_uri(*uris);
297 if (print_path) {
298 char *filename = g_file_get_path(file);
299 printf("%s\n", filename);
300 } else
301 printf("%s\n", *uris);
302 if (keep)
303 add_file_button(file);
304
305 } else {
306 printf("%s\n", *uris);
307 if (keep)
308 add_uri_button(*uris);
309 }
310 }
311 add_target_button();
312 gtk_widget_show_all(window);
313 } else if (text) {
314 if (verbose)
315 fputs("Received Text\n", stderr);
316 printf("%s\n", text);
317 } else if (verbose)
318 fputs("Received nothing\n", stderr);
319 gtk_drag_finish (context, TRUE, FALSE, time);
320 if (and_exit)
321 gtk_main_quit();
322 }
323
324 void add_target_button() {
325 GtkWidget *label = gtk_button_new();
326 gtk_button_set_label(GTK_BUTTON(label), "Drag something here...");
327 gtk_container_add(GTK_CONTAINER(vbox), label);
328 GtkTargetList *targetlist = gtk_drag_dest_get_target_list(GTK_WIDGET(label));
329 if (targetlist)
330 gtk_target_list_ref(targetlist);
331 else
332 targetlist = gtk_target_list_new(NULL, 0);
333 gtk_target_list_add_text_targets(targetlist, TARGET_TYPE_TEXT);
334 gtk_target_list_add_uri_targets(targetlist, TARGET_TYPE_URI);
335 gtk_drag_dest_set(GTK_WIDGET(label),
336 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT, NULL, 0,
337 GDK_ACTION_COPY);
338 gtk_drag_dest_set_target_list(GTK_WIDGET(label), targetlist);
339 g_signal_connect(GTK_WIDGET(label), "drag-drop",
340 G_CALLBACK(drag_drop), NULL);
341 g_signal_connect(GTK_WIDGET(label), "drag-data-received",
342 G_CALLBACK(drag_data_received), NULL);
343 }
344
345 void target_mode() {
346 add_target_button();
347 gtk_widget_show_all(window);
348 gtk_main();
349 }
350
351 int main (int argc, char **argv) {
352 progname = argv[0];
353 char *filename = NULL;
354 for (int i=1; i<argc; i++) {
355 if (strcmp(argv[i], "--help") == 0) {
356 mode = MODE_HELP;
357 printf("dragon - lightweight DnD source/target\n");
358 printf("Usage: %s [OPTION] [FILENAME]\n", argv[0]);
359 printf(" --and-exit, -x exit after a single completed drop\n");
360 printf(" --target, -t act as a target instead of source\n");
361 printf(" --keep, -k with --target, keep files to drag out\n");
362 printf(" --print-path, -p with --target, print file paths"
363 " instead of URIs\n");
364 printf(" --all, -a drag all files at once\n");
365 printf(" --on-top, -T make window always-on-top\n");
366 printf(" --verbose, -v be verbose\n");
367 printf(" --help show help\n");
368 printf(" --version show version details\n");
369 exit(0);
370 } else if (strcmp(argv[i], "--version") == 0) {
371 mode = MODE_VERSION;
372 puts("dragon " VERSION);
373 puts("Copyright (C) 2014-2018 Michael Homer");
374 puts("This program comes with ABSOLUTELY NO WARRANTY.");
375 puts("See the source for copying conditions.");
376 exit(0);
377 } else if (strcmp(argv[i], "-v") == 0
378 || strcmp(argv[i], "--verbose") == 0) {
379 verbose = true;
380 } else if (strcmp(argv[i], "-t") == 0
381 || strcmp(argv[i], "--target") == 0) {
382 mode = MODE_TARGET;
383 } else if (strcmp(argv[i], "-x") == 0
384 || strcmp(argv[i], "--and-exit") == 0) {
385 and_exit = true;
386 } else if (strcmp(argv[i], "-k") == 0
387 || strcmp(argv[i], "--keep") == 0) {
388 keep = true;
389 } else if (strcmp(argv[i], "-p") == 0
390 || strcmp(argv[i], "--print-path") == 0) {
391 print_path = true;
392 } else if (strcmp(argv[i], "-a") == 0
393 || strcmp(argv[i], "--all") == 0) {
394 drag_all = true;
395 } else if (strcmp(argv[i], "-i") == 0
396 || strcmp(argv[i], "--icon-only") == 0) {
397 icons_only = true;
398 } else if (strcmp(argv[i], "-T") == 0
399 || strcmp(argv[i], "--on-top") == 0) {
400 always_on_top = true;
401 } else if (argv[i][0] == '-') {
402 fprintf(stderr, "%s: error: unknown option `%s'.\n",
403 progname, argv[i]);
404 }
405 }
406 setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
407
408 GtkAccelGroup *accelgroup;
409 GClosure *closure;
410
411 gtk_init(&argc, &argv);
412
413 icon_theme = gtk_icon_theme_get_default();
414
415 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
416
417 closure = g_cclosure_new(G_CALLBACK(do_quit), NULL, NULL);
418 accelgroup = gtk_accel_group_new();
419 gtk_accel_group_connect(accelgroup, GDK_KEY_Escape, 0, 0, closure);
420 closure = g_cclosure_new(G_CALLBACK(do_quit), NULL, NULL);
421 gtk_accel_group_connect(accelgroup, GDK_KEY_q, 0, 0, closure);
422 gtk_window_add_accel_group(GTK_WINDOW(window), accelgroup);
423
424 gtk_window_set_title(GTK_WINDOW(window), "Run");
425 gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
426 gtk_window_set_keep_above(GTK_WINDOW(window), always_on_top);
427
428 g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
429
430 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
431
432 gtk_container_add(GTK_CONTAINER(window), vbox);
433
434 gtk_window_set_title(GTK_WINDOW(window), "dragon");
435
436 if (mode == MODE_TARGET) {
437 target_mode();
438 exit(0);
439 }
440
441 if (drag_all) {
442 uri_collection = malloc(sizeof(char*) * ((argc > MAX_SIZE ? argc : MAX_SIZE) + 1));
443 uri_count = 0;
444 }
445
446 bool had_filename = false;
447 for (int i=1; i<argc; i++) {
448 if (argv[i][0] != '-') {
449 filename = argv[i];
450 if (!is_uri(filename)) {
451 add_filename_button(filename);
452 } else if (is_file_uri(filename)) {
453 GFile *file = g_file_new_for_uri(filename);
454 add_file_button(file);
455 } else {
456 add_uri_button(filename);
457 }
458 had_filename = true;
459 }
460 }
461 if (!had_filename) {
462 printf("Usage: %s [OPTIONS] FILENAME\n", progname);
463 exit(0);
464 }
465
466 gtk_widget_show_all(window);
467
468 gtk_main();
469
470 return 0;
471 }
|