dragon

Fork of dragon, a GUI utility for drag-and-drop file transfers
git clone git://git.laack.co/dragon.git
Log | Files | Refs | README

dragon.c (23890B)


      1 // dragon - very lightweight DnD file source/target
      2 // Copyright 2014 Michael Homer.
      3 //
      4 // This program is free software: you can redistribute it and/or modify
      5 // it under the terms of the GNU General Public License as published by
      6 // the Free Software Foundation, either version 3 of the License, or
      7 // (at your option) any later version.
      8 //
      9 // This program is distributed in the hope that it will be useful,
     10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 // GNU General Public License for more details.
     13 //
     14 // You should have received a copy of the GNU General Public License
     15 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
     16 #define _POSIX_C_SOURCE 200809L
     17 #define _XOPEN_SOURCE 500
     18 #include <gtk/gtk.h>
     19 #include <gdk/gdk.h>
     20 #include <gdk/gdkkeysyms.h>
     21 #include <gio/gio.h>
     22 #include <stdio.h>
     23 #include <stdlib.h>
     24 #include <stdbool.h>
     25 #include <string.h>
     26 
     27 #define VERSION "1.2.0"
     28 
     29 // Provides an absolute minimum width and height.
     30 #define USE_GEOMETRY_HINTS false
     31 #define MIN_WIDTH 640
     32 #define MIN_HEIGHT 480
     33 
     34 // Top-level window.
     35 GtkWidget *window;
     36 
     37 // Container providing the scrolling.
     38 GtkWidget *panel;
     39 
     40 // Child of panel, where all the children are added.
     41 GtkWidget *vbox;
     42 
     43 GtkIconTheme *icon_theme;
     44 GFile *currentDir;
     45 
     46 char *progname;
     47 bool verbose = false;
     48 int mode = 0;
     49 int thumb_size = 96;
     50 bool and_exit;
     51 bool keep;
     52 bool print_path = false;
     53 bool icons_only = false;
     54 int name_style = 0; // 0: relative 1: basename 2: absolute
     55 bool always_on_top = false;
     56 
     57 static char *stdin_files;
     58 
     59 #define MODE_HELP 1
     60 #define MODE_TARGET 2
     61 #define MODE_VERSION 4
     62 
     63 #define TARGET_TYPE_TEXT 1
     64 #define TARGET_TYPE_URI 2
     65 
     66 struct draggable_thing {
     67     char *text;
     68     char *uri;
     69 };
     70 
     71 // MODE_ALL
     72 #define MAX_SIZE 100
     73 char** uri_collection;
     74 int uri_count = 0;
     75 bool drag_all = false;
     76 bool all_compact = false;
     77 char file_num_label[10];
     78 struct draggable_thing fake_dragdata;
     79 GtkWidget *all_button;
     80 // ---
     81 
     82 void add_target_button();
     83 
     84 void do_quit(GtkWidget *widget, gpointer data) {
     85     exit(0);
     86 }
     87 
     88 void button_clicked(GtkWidget *widget, gpointer user_data) {
     89     struct draggable_thing *dd = (struct draggable_thing *)user_data;
     90     if (0 == fork()) {
     91         execlp("xdg-open", "xdg-open", dd->uri, NULL);
     92     }
     93 }
     94 
     95 void drag_data_get(GtkWidget    *widget,
     96                GdkDragContext   *context,
     97                GtkSelectionData *data,
     98                guint             info,
     99                guint             time,
    100                gpointer          user_data) {
    101     struct draggable_thing *dd = (struct draggable_thing *)user_data;
    102     if (info == TARGET_TYPE_URI) {
    103 
    104         char** uris;
    105         char* single_uri_data[2] = {dd->uri, NULL};
    106         if (drag_all) {
    107             uri_collection[uri_count] = NULL;
    108             uris = uri_collection;
    109         } else {
    110             uris = single_uri_data;
    111         }
    112         if (verbose) {
    113             if (drag_all)
    114                 fputs("Sending all as URI\n", stderr);
    115             else
    116                 fprintf(stderr, "Sending as URI: %s\n", dd->uri);
    117         }
    118 
    119         gtk_selection_data_set_uris(data, uris);
    120         g_signal_stop_emission_by_name(widget, "drag-data-get");
    121     } else if (info == TARGET_TYPE_TEXT) {
    122         if (verbose)
    123             fprintf(stderr, "Sending as TEXT: %s\n", dd->text);
    124         gtk_selection_data_set_text(data, dd->text, -1);
    125     } else {
    126         fprintf(stderr, "Error: bad target type %i\n", info);
    127     }
    128 }
    129 
    130 void drag_end(GtkWidget *widget, GdkDragContext *context, gpointer user_data) {
    131     if (verbose) {
    132         gboolean succeeded = gdk_drag_drop_succeeded(context);
    133         GdkDragAction action = gdk_drag_context_get_selected_action (context);
    134         char* action_str;
    135         switch (action) {
    136             case GDK_ACTION_COPY:
    137                 action_str = "COPY"; break;
    138             case GDK_ACTION_MOVE:
    139                 action_str = "MOVE"; break;
    140             case GDK_ACTION_LINK:
    141                 action_str = "LINK"; break;
    142             case GDK_ACTION_ASK:
    143                 action_str = "ASK"; break;
    144             default:
    145                 action_str = malloc(sizeof(char) * 20);
    146                 snprintf(action_str, 20, "invalid (%d)", action);
    147                 break;
    148         }
    149         fprintf(stderr, "Selected drop action: %s; Succeeded: %d\n", action_str, succeeded);
    150         if (action_str[0] == 'i')
    151             free(action_str);
    152     }
    153     if (and_exit)
    154         gtk_main_quit();
    155 }
    156 
    157 void add_uri(char *uri) {
    158     if (uri_count < MAX_SIZE) {
    159         uri_collection[uri_count] = uri;
    160         uri_count++;
    161     } else {
    162         fprintf(stderr, "Exceeded maximum number of files for drag_all (%d)\n", MAX_SIZE);
    163     }
    164 }
    165 
    166 GtkButton *add_button(char *label, struct draggable_thing *dragdata, int type, char *tooltip) {
    167     GtkWidget *button;
    168 
    169     if (icons_only) {
    170         button = gtk_button_new();
    171     } else {
    172         button = gtk_button_new_with_label(label);
    173     }
    174 
    175     // Show a tooltip based on `tooltip` or the button's intended label.
    176     // Ideally, the value is the file's full path.
    177     gtk_widget_set_tooltip_text(button, tooltip != NULL ? tooltip : label);
    178 
    179     GtkTargetList *targetlist = gtk_drag_source_get_target_list(GTK_WIDGET(button));
    180     if (targetlist)
    181         gtk_target_list_ref(targetlist);
    182     else
    183         targetlist = gtk_target_list_new(NULL, 0);
    184     if (type == TARGET_TYPE_URI)
    185         gtk_target_list_add_uri_targets(targetlist, TARGET_TYPE_URI);
    186     else
    187         gtk_target_list_add_text_targets(targetlist, TARGET_TYPE_TEXT);
    188 
    189     gtk_drag_source_set(GTK_WIDGET(button), GDK_BUTTON1_MASK, NULL, 0,
    190             GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK);
    191     gtk_drag_source_set_target_list(GTK_WIDGET(button), targetlist);
    192     g_signal_connect(GTK_WIDGET(button), "drag-data-get",
    193             G_CALLBACK(drag_data_get), dragdata);
    194     g_signal_connect(GTK_WIDGET(button), "clicked",
    195             G_CALLBACK(button_clicked), dragdata);
    196     g_signal_connect(GTK_WIDGET(button), "drag-end",
    197             G_CALLBACK(drag_end), dragdata);
    198 
    199     gtk_container_add(GTK_CONTAINER(vbox), button);
    200 
    201     if (drag_all)
    202       add_uri(dragdata->uri);
    203     else
    204       uri_count++;
    205 
    206     return (GtkButton *)button;
    207 }
    208 
    209 void left_align_button(GtkButton *button) {
    210     GList *child = g_list_first(
    211             gtk_container_get_children(GTK_CONTAINER(button)));
    212     if (child)
    213         gtk_widget_set_halign(GTK_WIDGET(child->data), GTK_ALIGN_START);
    214 }
    215 
    216 GtkIconInfo* icon_info_from_content_type(char *content_type) {
    217     GIcon *icon = g_content_type_get_icon(content_type);
    218     return gtk_icon_theme_lookup_by_gicon(icon_theme, icon, 48, 0);
    219 }
    220 
    221 void add_file_button(GFile *file) {
    222     char *filename;
    223     // Default is relative path to file from ., if file is within .
    224     filename = g_file_get_relative_path(currentDir, file);
    225     // When the filename only option is set, only the file's basename including
    226     // extension is displayed, not the entire path. The button's label is
    227     // obtained from the file argument and depending on `filename_only`, may
    228     // be either the basename or the full path. However, the full path is
    229     // necessary to get the pixel buffer for image as well as for the button's
    230     // tooltip.
    231     char *path = g_file_get_path(file);
    232 
    233     if (name_style == 1) {
    234       filename = g_path_get_basename(path);
    235     } else if (name_style == 2 || !filename) {
    236       filename = g_file_get_path(file);
    237     }
    238 
    239     if(!g_file_query_exists(file, NULL)) {
    240         fprintf(stderr, "The file `%s' does not exist.\n",
    241                 filename);
    242         exit(1);
    243     }
    244     char *uri = g_file_get_uri(file);
    245     if (all_compact) {
    246       add_uri(uri);
    247       return;
    248     }
    249     struct draggable_thing *dragdata = malloc(sizeof(struct draggable_thing));
    250     dragdata->text = filename;
    251     dragdata->uri = uri;
    252 
    253     GtkButton *button = add_button(filename, dragdata, TARGET_TYPE_URI, path);
    254     GdkPixbuf *pb = gdk_pixbuf_new_from_file_at_size(path, thumb_size, thumb_size, NULL);
    255     if (pb) {
    256         GtkWidget *image = gtk_image_new_from_pixbuf(pb);
    257         gtk_button_set_always_show_image(button, true);
    258         gtk_button_set_image(button, image);
    259         gtk_button_set_always_show_image(button, true);
    260     } else {
    261         GFileInfo *fileinfo = g_file_query_info(file, "*", 0, NULL, NULL);
    262         GIcon *icon = g_file_info_get_icon(fileinfo);
    263         GtkIconInfo *icon_info = gtk_icon_theme_lookup_by_gicon(icon_theme,
    264                 icon, 48, 0);
    265 
    266         // Try a few fallback mimetypes if no icon can be found
    267         if (!icon_info)
    268             icon_info = icon_info_from_content_type("application/octet-stream");
    269         if (!icon_info)
    270             icon_info = icon_info_from_content_type("text/x-generic");
    271         if (!icon_info)
    272             icon_info = icon_info_from_content_type("text/plain");
    273 
    274         if (icon_info) {
    275             GtkWidget *image = gtk_image_new_from_pixbuf(
    276                     gtk_icon_info_load_icon(icon_info, NULL));
    277             gtk_button_set_image(button, image);
    278             gtk_button_set_always_show_image(button, true);
    279         }
    280     }
    281 
    282     if (!icons_only)
    283         left_align_button(button);
    284 }
    285 
    286 void add_filename_button(char *filename) {
    287     GFile *file = g_file_new_for_path(filename);
    288     add_file_button(file);
    289 }
    290 
    291 void add_uri_button(char *uri) {
    292     if (all_compact) {
    293       add_uri(uri);
    294       return;
    295     }
    296     struct draggable_thing *dragdata = malloc(sizeof(struct draggable_thing));
    297     dragdata->text = uri;
    298     dragdata->uri = uri;
    299     GtkButton *button = add_button(uri, dragdata, TARGET_TYPE_URI, NULL);
    300     left_align_button(button);
    301 }
    302 
    303 bool is_uri(char *uri) {
    304     for (int i=0; uri[i]; i++)
    305         if (uri[i] == '/')
    306             return false;
    307         else if (uri[i] == ':' && i > 0)
    308             return true;
    309         else if (!(    (uri[i] >= 'a' && uri[i] <= 'z')
    310                     || (uri[i] >= 'A' && uri[i] <= 'Z')
    311                     || (uri[i] >= '0' && uri[i] <= '9' && i > 0)
    312                     || (i > 0 && (uri[i] == '+' || uri[i] == '.' || uri[i] == '-'))
    313                   )) // RFC3986 URI scheme syntax
    314             return false;
    315     return false;
    316 }
    317 
    318 bool is_file_uri(char *uri) {
    319     char *prefix = "file:";
    320     return strncmp(prefix, uri, strlen(prefix)) == 0;
    321 }
    322 
    323 gboolean drag_drop (GtkWidget *widget,
    324                GdkDragContext *context,
    325                gint            x,
    326                gint            y,
    327                guint           time,
    328                gpointer        user_data) {
    329     GtkTargetList *targetlist = gtk_drag_dest_get_target_list(widget);
    330     GList *list = gdk_drag_context_list_targets(context);
    331     if (list) {
    332         while (list) {
    333             GdkAtom atom = (GdkAtom)g_list_nth_data(list, 0);
    334             if (gtk_target_list_find(targetlist,
    335                         GDK_POINTER_TO_ATOM(g_list_nth_data(list, 0)), NULL)) {
    336                 gtk_drag_get_data(widget, context, atom, time);
    337                 return true;
    338             }
    339             list = g_list_next(list);
    340         }
    341     }
    342     gtk_drag_finish(context, false, false, time);
    343     return true;
    344 }
    345 
    346 void update_all_button() {
    347     sprintf(file_num_label, "%d files", uri_count);
    348     gtk_button_set_label((GtkButton *)all_button, file_num_label);
    349 }
    350 
    351 void
    352 drag_data_received (GtkWidget          *widget,
    353                     GdkDragContext     *context,
    354                     gint                x,
    355                     gint                y,
    356                     GtkSelectionData   *data,
    357                     guint               info,
    358                     guint               time) {
    359     gchar **uris = gtk_selection_data_get_uris(data);
    360     unsigned char *text = gtk_selection_data_get_text(data);
    361     if (!uris && !text)
    362         gtk_drag_finish (context, FALSE, FALSE, time);
    363     if (uris) {
    364         if (verbose)
    365             fputs("Received URIs\n", stderr);
    366         gtk_container_remove(GTK_CONTAINER(vbox), widget);
    367         for (; *uris; uris++) {
    368             if (is_file_uri(*uris)) {
    369                 GFile *file = g_file_new_for_uri(*uris);
    370                 if (print_path) {
    371                     char *filename = g_file_get_path(file);
    372                     printf("%s\n", filename);
    373                 } else
    374                     printf("%s\n", *uris);
    375                 if (keep)
    376                     add_file_button(file);
    377 
    378             } else {
    379                 printf("%s\n", *uris);
    380                 if (keep)
    381                     add_uri_button(*uris);
    382             }
    383         }
    384         if (all_compact)
    385             update_all_button();
    386         add_target_button();
    387         gtk_widget_show_all(window);
    388     } else if (text) {
    389         if (verbose)
    390             fputs("Received Text\n", stderr);
    391         printf("%s\n", text);
    392     } else if (verbose)
    393         fputs("Received nothing\n", stderr);
    394     gtk_drag_finish (context, TRUE, FALSE, time);
    395     if (and_exit)
    396         gtk_main_quit();
    397 }
    398 
    399 void add_target_button() {
    400     GtkWidget *label = gtk_button_new();
    401     gtk_button_set_label(GTK_BUTTON(label), "Drag something here...");
    402     gtk_container_add(GTK_CONTAINER(vbox), label);
    403     GtkTargetList *targetlist = gtk_drag_dest_get_target_list(GTK_WIDGET(label));
    404     if (targetlist)
    405         gtk_target_list_ref(targetlist);
    406     else
    407         targetlist = gtk_target_list_new(NULL, 0);
    408     gtk_target_list_add_text_targets(targetlist, TARGET_TYPE_TEXT);
    409     gtk_target_list_add_uri_targets(targetlist, TARGET_TYPE_URI);
    410     gtk_drag_dest_set(GTK_WIDGET(label),
    411             GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT, NULL, 0,
    412             GDK_ACTION_COPY);
    413     gtk_drag_dest_set_target_list(GTK_WIDGET(label), targetlist);
    414     g_signal_connect(GTK_WIDGET(label), "drag-drop",
    415             G_CALLBACK(drag_drop), NULL);
    416     g_signal_connect(GTK_WIDGET(label), "drag-data-received",
    417             G_CALLBACK(drag_data_received), NULL);
    418 }
    419 
    420 void target_mode() {
    421     add_target_button();
    422     gtk_widget_show_all(window);
    423     gtk_main();
    424 }
    425 
    426 void make_btn(char *filename) {
    427     if (!is_uri(filename)) {
    428         add_filename_button(filename);
    429     } else if (is_file_uri(filename)) {
    430         GFile *file = g_file_new_for_uri(filename);
    431         add_file_button(file);
    432     } else {
    433         add_uri_button(filename);
    434     }
    435 }
    436 
    437 static void readstdin(void) {
    438     char *write_pos = stdin_files, *newline;
    439     size_t max_size = BUFSIZ * 2, cur_size = 0;
    440     // read each line from stdin and add it to the item list
    441     while (fgets(write_pos, BUFSIZ, stdin)) {
    442             if (write_pos[0] == '-')
    443                     continue;
    444             if ((newline = strchr(write_pos, '\n')))
    445                     *newline = '\0';
    446             else
    447                     break;
    448             make_btn(write_pos);
    449             cur_size = newline - stdin_files + 1;
    450             if (max_size < cur_size + BUFSIZ) {
    451                     if (!(stdin_files = realloc(stdin_files, (max_size += BUFSIZ))))
    452                             fprintf(stderr, "%s: cannot realloc %lu bytes.\n", progname, max_size);
    453                     newline = stdin_files + cur_size - 1;
    454             }
    455             write_pos = newline + 1;
    456     }
    457 }
    458 
    459 void create_all_button() {
    460     sprintf(file_num_label, "%d files", uri_count);
    461     all_button = gtk_button_new_with_label(file_num_label);
    462 
    463     GtkTargetList *targetlist = gtk_target_list_new(NULL, 0);
    464     gtk_target_list_add_uri_targets(targetlist, TARGET_TYPE_URI);
    465 
    466     // fake uri to avoid segfault when callback deference it
    467     fake_dragdata.uri = file_num_label;
    468 
    469     gtk_drag_source_set(GTK_WIDGET(all_button), GDK_BUTTON1_MASK, NULL, 0,
    470             GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK);
    471     gtk_drag_source_set_target_list(GTK_WIDGET(all_button), targetlist);
    472     g_signal_connect(GTK_WIDGET(all_button), "drag-data-get",
    473             G_CALLBACK(drag_data_get), &fake_dragdata);
    474     g_signal_connect(GTK_WIDGET(all_button), "drag-end",
    475             G_CALLBACK(drag_end), &fake_dragdata);
    476 
    477     gtk_container_add(GTK_CONTAINER(vbox), all_button);
    478 }
    479 
    480 // Sets the minimum content width and height for the ScrolledWindow based on
    481 // the allocated width and height of the input container.
    482 void constrain_window_size(GtkWindow *window, GtkWidget *container, GtkScrolledWindow *scrolled_window) {
    483     // Get the workarea of the monitor to limit the container's size.
    484     GdkDisplay *display = gdk_display_get_default();
    485     GdkMonitor *monitor = gdk_display_get_monitor(display, 0);
    486     GdkRectangle *workarea = malloc(sizeof(GdkRectangle));
    487 
    488     gdk_monitor_get_workarea(monitor, workarea);
    489 
    490     // If enabled, the window's width and height will not be less than the
    491     // defined values, regardless of the container's dimensions.
    492 #if USE_GEOMETRY_HINTS
    493     GdkGeometry *geometry = malloc(sizeof(GdkGeometry));
    494     geometry->min_width = MIN_WIDTH;
    495     geometry->min_height = MIN_HEIGHT;
    496     geometry->max_width = workarea->width;
    497     geometry->max_height = workarea->height;
    498 
    499     gtk_window_set_geometry_hints(window, NULL, geometry, GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
    500 #endif
    501 
    502     int vbox_width = gtk_widget_get_allocated_width(container);
    503     int vbox_height = gtk_widget_get_allocated_height(container);
    504 
    505     // Placeholder until a way is found to have the window including its
    506     // decorations and padding to occupy the workarea and not slightly exceed
    507     // it.
    508     int padded_height = workarea->height - (workarea->height * 0.05);
    509     int padded_width = workarea->width - (workarea->width * 0.05);
    510 
    511     int scroll_width = vbox_width > padded_width ? padded_width : vbox_width;
    512     int scroll_height = vbox_height > padded_height ? padded_height : vbox_height;
    513 
    514     gtk_scrolled_window_set_min_content_width((GtkScrolledWindow*)scrolled_window, scroll_width);
    515     gtk_scrolled_window_set_min_content_height((GtkScrolledWindow*)scrolled_window, scroll_height);
    516 }
    517 
    518 int main (int argc, char **argv) {
    519     bool from_stdin = false;
    520     stdin_files = malloc(BUFSIZ * 2);
    521     progname = argv[0];
    522     for (int i=1; i<argc; i++) {
    523         if (strcmp(argv[i], "--help") == 0) {
    524             mode = MODE_HELP;
    525             printf("dragon - lightweight DnD source/target\n");
    526             printf("Usage: %s [OPTION] [FILENAME]\n", progname);
    527             printf("  --and-exit,    -x  exit after a single completed drop\n");
    528             printf("  --target,      -t  act as a target instead of source\n");
    529             printf("  --keep,        -k  with --target, keep files to drag out\n");
    530             printf("  --print-path,  -p  with --target, print file paths"
    531                     " instead of URIs\n");
    532             printf("  --all,         -a  drag all files at once\n");
    533             printf("  --all-compact, -A  drag all files at once, only displaying"
    534                     " the number of files\n");
    535             printf("  --icon-only,   -i  only show icons in drag-and-drop"
    536                     " windows\n");
    537             printf("  --name-only,   -f  only show the file's basename and"
    538                    " not the full path\n");
    539             printf("  --on-top,      -T  make window always-on-top\n");
    540             printf("  --stdin,       -I  read input from stdin\n");
    541             printf("  --thumb-size,  -s  set thumbnail size (default 96)\n");
    542             printf("  --verbose,     -v  be verbose\n");
    543             printf("  --help            show help\n");
    544             printf("  --version         show version details\n");
    545             exit(0);
    546         } else if (strcmp(argv[i], "--version") == 0) {
    547             mode = MODE_VERSION;
    548             puts("dragon " VERSION);
    549             puts("Copyright (C) 2014-2022 Michael Homer and contributors");
    550             puts("This program comes with ABSOLUTELY NO WARRANTY.");
    551             puts("See the source for copying conditions.");
    552             exit(0);
    553         } else if (strcmp(argv[i], "-v") == 0
    554                 || strcmp(argv[i], "--verbose") == 0) {
    555             verbose = true;
    556         } else if (strcmp(argv[i], "-t") == 0
    557                 || strcmp(argv[i], "--target") == 0) {
    558             mode = MODE_TARGET;
    559         } else if (strcmp(argv[i], "-x") == 0
    560                 || strcmp(argv[i], "--and-exit") == 0) {
    561             and_exit = true;
    562         } else if (strcmp(argv[i], "-k") == 0
    563                 || strcmp(argv[i], "--keep") == 0) {
    564             keep = true;
    565         } else if (strcmp(argv[i], "-p") == 0
    566                 || strcmp(argv[i], "--print-path") == 0) {
    567             print_path = true;
    568         } else if (strcmp(argv[i], "-a") == 0
    569                 || strcmp(argv[i], "--all") == 0) {
    570             drag_all = true;
    571         } else if (strcmp(argv[i], "-A") == 0
    572                 || strcmp(argv[i], "--all-compact") == 0) {
    573             drag_all = true;
    574             all_compact = true;
    575         } else if (strcmp(argv[i], "-i") == 0
    576                 || strcmp(argv[i], "--icon-only") == 0) {
    577             icons_only = true;
    578         } else if (strcmp(argv[i], "-f") == 0
    579                 || strcmp(argv[i], "--name-only") == 0) {
    580             name_style = 1;
    581         } else if (strcmp(argv[i], "-F") == 0
    582                 || strcmp(argv[i], "--full-path") == 0) {
    583             name_style = 2;
    584         } else if (strcmp(argv[i], "-T") == 0
    585                 || strcmp(argv[i], "--on-top") == 0) {
    586             always_on_top = true;
    587         } else if (strcmp(argv[i], "-I") == 0
    588                 || strcmp(argv[i], "--stdin") == 0) {
    589             from_stdin = true;
    590         } else if (strcmp(argv[i], "-s") == 0
    591                 || strcmp(argv[i], "--thumb-size") == 0) {
    592             if (argv[++i] == NULL || (thumb_size = atoi(argv[i])) <= 0) {
    593                 fprintf(stderr, "%s: error: bad argument for %s `%s'.\n",
    594                         progname, argv[i-1], argv[i]);
    595                 exit(1);
    596             }
    597             argv[i][0] = '\0';
    598         } else if (argv[i][0] == '-') {
    599             fprintf(stderr, "%s: error: unknown option `%s'.\n",
    600                     progname, argv[i]);
    601         }
    602     }
    603     setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
    604 
    605     GtkAccelGroup *accelgroup;
    606     GClosure *closure;
    607 
    608     gtk_init(&argc, &argv);
    609 
    610     icon_theme = gtk_icon_theme_get_default();
    611 
    612     window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    613 
    614     // Scrolling Window
    615     panel = gtk_scrolled_window_new(NULL, NULL);
    616 
    617     closure = g_cclosure_new(G_CALLBACK(do_quit), NULL, NULL);
    618     accelgroup = gtk_accel_group_new();
    619     gtk_accel_group_connect(accelgroup, GDK_KEY_Escape, 0, 0, closure);
    620     closure = g_cclosure_new(G_CALLBACK(do_quit), NULL, NULL);
    621     gtk_accel_group_connect(accelgroup, GDK_KEY_q, 0, 0, closure);
    622     gtk_window_add_accel_group(GTK_WINDOW(window), accelgroup);
    623 
    624     gtk_window_set_title(GTK_WINDOW(window), "Run");
    625     gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
    626     gtk_window_set_keep_above(GTK_WINDOW(window), always_on_top);
    627 
    628     g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    629 
    630     vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
    631 
    632     gtk_container_add(GTK_CONTAINER(panel), vbox);
    633 
    634     gtk_container_add(GTK_CONTAINER(window), panel);
    635 
    636     gtk_window_set_title(GTK_WINDOW(window), "dragon");
    637 
    638     currentDir = g_file_new_for_path(".");
    639 
    640     if (all_compact)
    641         create_all_button();
    642 
    643     if (mode == MODE_TARGET) {
    644         if (drag_all)
    645             uri_collection = malloc(sizeof(char*) * (MAX_SIZE  + 1));
    646         target_mode();
    647         exit(0);
    648     }
    649 
    650     if (from_stdin)
    651         uri_collection = malloc(sizeof(char*) * (MAX_SIZE  + 1));
    652     else if (drag_all)
    653         uri_collection = malloc(sizeof(char*) * ((argc > MAX_SIZE ? argc : MAX_SIZE) + 1));
    654 
    655     for (int i=1; i<argc; i++) {
    656         if (argv[i][0] != '-' && argv[i][0] != '\0')
    657            make_btn(argv[i]);
    658     }
    659     if (from_stdin)
    660         readstdin();
    661 
    662     if (!uri_count) {
    663         printf("Usage: %s [OPTIONS] FILENAME\n", progname);
    664         exit(0);
    665     }
    666 
    667     if (all_compact)
    668         update_all_button();
    669 
    670     gtk_widget_show_all(window);
    671     constrain_window_size(GTK_WINDOW(window), vbox, (GtkScrolledWindow*)panel);
    672 
    673     gtk_main();
    674 
    675     return 0;
    676 }