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 }