From 0c6afec38a3bfcb34d21660e4645fa3cd77cf272 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Mon, 19 Jul 1999 19:03:34 +0000 Subject: [PATCH] r21: Files can now be copied by dragging them between filer windows. Doesn't work yet if the files are on different machines. --- ROX-Filer/pixmaps/filer.xpm | 164 ++++++++++++++++++++++++++++++++ ROX-Filer/src/dnd.c | 225 ++++++++++++++++++++++++++++++++++++++++++-- ROX-Filer/src/filer.c | 22 ++++- ROX-Filer/src/filer.h | 4 +- ROX-Filer/src/main.c | 33 +++++++ ROX-Filer/src/support.c | 27 ++++++ ROX-Filer/src/support.h | 1 + 7 files changed, 468 insertions(+), 8 deletions(-) create mode 100644 ROX-Filer/pixmaps/filer.xpm diff --git a/ROX-Filer/pixmaps/filer.xpm b/ROX-Filer/pixmaps/filer.xpm new file mode 100644 index 00000000..07951232 --- /dev/null +++ b/ROX-Filer/pixmaps/filer.xpm @@ -0,0 +1,164 @@ +/* XPM */ +static char * filer_xpm[] = { +"44 28 133 2", +" c None", +". c #000000", +"+ c #538EED", +"@ c #528DEC", +"# c #518CEB", +"$ c #518AEB", +"% c #5089EA", +"& c #4F88E9", +"* c #4F87E9", +"= c #4E85E8", +"- c #4D84E7", +"; c #4C83E7", +"> c #4C82E6", +", c #4B80E5", +"' c #4A7FE5", +") c #528CEC", +"! c #518BEB", +"~ c #4F88EA", +"{ c #4E86E8", +"] c #4D84E8", +"^ c #4D83E7", +"/ c #4B81E6", +"( c #4A7EE4", +"_ c #497DE4", +": c #508AEA", +"< c #4A7FE4", +"[ c #497CE3", +"} c #487BE2", +"| c #4E86E9", +"1 c #4C81E6", +"2 c #497EE4", +"3 c #487BE3", +"4 c #477AE2", +"5 c #4779E1", +"6 c #487CE3", +"7 c #4779E2", +"8 c #4576E0", +"9 c #4474DF", +"0 c #4373DE", +"a c #4372DE", +"b c #4271DD", +"c c #416FDC", +"d c #416EDC", +"e c #406DDB", +"f c #3F6CDA", +"g c #3E6ADA", +"h c #3E69D9", +"i c #3D68D8", +"j c #3C67D8", +"k c #3C66D7", +"l c #3B64D6", +"m c #3A63D6", +"n c #3962D5", +"o c #3961D4", +"p c #385FD4", +"q c #508AEB", +"r c #4679E1", +"s c #4677E1", +"t c #4475DF", +"u c #4170DD", +"v c #3F6CDB", +"w c #3F6BDA", +"x c #3E6AD9", +"y c #3D68D9", +"z c #3D67D8", +"A c #3B65D7", +"B c #3A62D5", +"C c #3961D5", +"D c #3860D4", +"E c #385ED3", +"F c #375DD3", +"G c #497DE3", +"H c #4678E1", +"I c #4575DF", +"J c #4270DD", +"K c #406EDB", +"L c #406CDB", +"M c #3D69D9", +"N c #3A63D5", +"O c #385FD3", +"P c #375ED3", +"Q c #365CD2", +"R c #365BD1", +"S c #2D4BC9", +"T c #4473DF", +"U c #375DD2", +"V c #355BD1", +"W c #3559D0", +"X c #3A64D6", +"Y c #355AD1", +"Z c #3458D0", +"` c #487AE2", +" . c #406EDC", +".. c #3C66D8", +"+. c #3960D4", +"@. c #365DD2", +"#. c #365BD2", +"$. c #3459D0", +"%. c #5088EA", +"&. c #4D85E8", +"*. c #4677E0", +"=. c #4170DC", +"-. c #3E6BDA", +";. c #3357CF", +">. c #3356CF", +",. c #4B80E6", +"'. c #4577E0", +"). c #4272DD", +"!. c #3255CE", +"~. c #3C65D7", +"{. c #3254CE", +"]. c #4C82E7", +"^. c #3559D1", +"/. c #3256CF", +"(. c #3153CD", +"_. c #3154CD", +":. c #3152CD", +"<. c #4575E0", +"[. c #3154CE", +"}. c #3051CC", +"|. c #4473DE", +"1. c #3052CC", +"2. c #3052CD", +"3. c #2F50CB", +"4. c #3457D0", +"5. c #2F4FCB", +"6. c #2F50CC", +"7. c #2E4ECA", +"8. c #2E4ECB", +"9. c #2E4DCA", +"0. c #2E4FCB", +"a. c #2D4CC9", +"b. c #2D4CCA", +" . . . . . . . . . . . . . . ", +" . + + @ # $ % & * = - ; > , ' . ", +" . + + @ ) ! % ~ * { ] ^ > / ' ( _ . ", +" . + + + ) ! : % * . . - > / , < _ [ } . ", +" . + + + @ ! $ . . | . . ; 1 , ' 2 [ 3 4 5 . ", +". . @ @ @ @ . . . . ; . . ; ; ; ; ; 4 4 4 4 . . . . . . . . . . . . . . . . . . . . ", +". + + @ ) ! . . ; ; ; ^ > / , ( _ 6 } 7 4 4 8 9 0 a b c d e f g h i j k l m n o p . . ", +". + + ) ! q % * | . . . / , ' 2 [ 3 4 r s 8 t 9 a b u c e v w x y z k A m B C D E F . . ", +". + @ # $ . . * . . . . . ' ( G 3 4 5 H 8 I 9 0 b J c K L w x M z k A l N C D O P Q R . ", +". S S S S . . S . . . . . S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S . ", +". ) ! q % * | = . . . . . _ [ 3 4 H s 8 t T a b u d e v w h i j k l m B o D P U Q V W . ", +". ! $ % & | = - ; . . . 2 G 3 4 5 H 8 t 9 0 b J c K v w x M z k A X B C D O F Q V Y Z . ", +". ! % ~ * { ] ^ . . . . . 6 ` 7 H . . 9 0 a J c .e w g h i ..A l m C +.p P @.#.Y $.Z . ", +". : %.* { &.^ > . . . . . } 4 H *.. . 0 . . =.d e f -.h i j k l m n o p P U Q Y $.Z ;.. ", +". % & | = - ; 1 . . . . . 4 5 s 8 . . a . . . . v w x y z k A m B C D E F Q V Y Z ;.>.. ", +". ~ * = ] ; > ,.' . . . 4 7 H '.I 9 0 ).J c . . w x M i k A l N C D O P Q R Y $.;.>.!.. ", +". * { &.^ > / , ( _ 6 } 7 H *.8 9 0 . . . d e f g h i j ~.l m n +.p P U #.Y $.Z ;.!.{.. ", +". | = - ]./ , ' _ [ 3 4 H s 8 t 9 . . . . . v . . y j k A m B C D P F Q V ^.Z ;./.{.(.. ", +". = - ; > , ' ( G 3 4 5 H 8 I 9 0 . . . . . w . . z k A l B C D O F Q R Y Z ;.>.!._.:.. ", +". ] ^ > / ' ( _ 6 ` 7 H '.<.9 0 a . . . . . g h i j A l m n +.p P @.#.Y $.Z >.!.[.(.}.. ", +". - ]./ , < _ [ } 4 H *.8 t |.a b =.. . . w h i j k l m B o p P U Q Y W Z ;.!.{.(.1.}.. ", +". ; 1 , ' 2 [ 3 4 5 s 8 t 9 a b J . . . . . M z k A X B C D O F Q V Y Z ;.>.!.(.2.}.3.. ", +". > / ' ( _ 6 ` 7 H '.<.9 0 a J c . . . . . i ..A l m C +.p P Q #.Y $.4.>.!.[.:.}.3.5.. ", +". / , ( _ 6 } 7 H *.8 9 0 a b c d . . . . . j k l m n o p P U Q Y $.Z ;.!.{.(.1.6.5.7.. ", +". , ' 2 [ 3 4 5 s 8 t 9 a b J c e v . . . z k A m B C D E F Q V ^.Z ;.>.{.(.2.}.5.8.9.. ", +". ' ( G 3 4 5 H 8 I 9 0 b J c K L w x M i k A l N C D O P Q R Y $.;.>.!._.:.}.3.0.9.a.. ", +". ( _ 6 } 7 H *.8 9 0 a b c d e f g h i j ~.l m n +.p P U #.Y $.Z >.!.{.(.}.6.5.7.b.S . ", +". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "}; diff --git a/ROX-Filer/src/dnd.c b/ROX-Filer/src/dnd.c index 63ee42c1..4c1169d1 100644 --- a/ROX-Filer/src/dnd.c +++ b/ROX-Filer/src/dnd.c @@ -36,6 +36,7 @@ enum GdkAtom XdndDirectSave0; GdkAtom text_plain; +GdkAtom text_uri_list; GdkAtom application_octet_stream; /* Static prototypes */ @@ -66,11 +67,18 @@ static void got_data_raw(GtkWidget *widget, GtkSelectionData *selection_data, guint32 time); static gboolean load_file(char *pathname, char **data_out, long *length_out); +static GSList *uri_list_to_gslist(char *uri_list); +static void got_uri_list(GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint32 time); +char *get_local_path(char *uri); void dnd_init() { XdndDirectSave0 = gdk_atom_intern("XdndDirectSave0", FALSE); text_plain = gdk_atom_intern("text/plain", FALSE); + text_uri_list = gdk_atom_intern("text/uri-list", FALSE); application_octet_stream = gdk_atom_intern("application/octet-stream", FALSE); } @@ -119,6 +127,93 @@ static gboolean provides(GdkDragContext *context, GdkAtom target) return targets != NULL; } +/* Convert a URI to a local pathname (or NULL if it isn't local). + * The returned pointer points inside the input string. + * Possible formats: + * /path + * ///path + * //host/path + * file://host/path + */ +char *get_local_path(char *uri) +{ + char *host; + + host = our_host_name(); + + if (*uri == '/') + { + char *path; + + if (uri[1] != '/') + return uri; /* Just a local path - no host part */ + + path = strchr(uri + 2, '/'); + if (!path) + return NULL; /* //something */ + + if (path - uri == 2) + return path; /* ///path */ + if (strlen(host) == path - uri - 2 && + strncmp(uri + 2, host, path - uri - 2) == 0) + return path; /* //myhost/path */ + + return NULL; /* From a different host */ + } + else + { + if (strncasecmp(uri, "file:", 5)) + return NULL; /* Don't know this format */ + + uri += 5; + + if (*uri == '/') + return get_local_path(uri); + + return NULL; + } +} + +/* Convert a list of URIs into a list of strings. + * Lines beginning with # are skipped. + * The text block passed in is zero terminated (after the final CRLF) + */ +static GSList *uri_list_to_gslist(char *uri_list) +{ + GSList *list = NULL; + + while (*uri_list) + { + char *linebreak; + char *uri; + int length; + + linebreak = strchr(uri_list, 13); + + if (!linebreak || linebreak[1] != 10) + { + report_error("uri_list_to_gslist", + "Incorrect or missing line break " + "in text/uri-list data"); + return list; + } + + length = linebreak - uri_list; + + if (length && uri_list[0] != '#') + { + uri = g_malloc(sizeof(char) * (length + 1)); + strncpy(uri, uri_list, length); + uri[length] = 0; + list = g_slist_append(list, uri); + } + + uri_list = linebreak + 2; + } + + return list; +} + /* Append all the URIs in the selection to the string */ static void create_uri_list(GString *string, Collection *collection, @@ -305,7 +400,8 @@ static gboolean load_file(char *pathname, char **data_out, long *length_out) if (ferror(file)) { - report_error("Loading file for DND", g_strerror(errno)); + report_error("Loading file for DND", + g_strerror(errno)); g_free(buffer); } else @@ -411,10 +507,11 @@ static gboolean drag_drop(GtkWidget *widget, "XdndDirectSave0 (type text/plain) did not " "contain a leafname\n"; } + else if (provides(context, text_uri_list)) + target = text_uri_list; else - { - error = "Normal DND"; - } + error = "Sorry - I require a target type of text/uri-list or " + "XdndDirectSave0."; if (error) { @@ -456,8 +553,7 @@ static void drag_data_received(GtkWidget *widget, got_data_raw(widget, context, selection_data, time); break; case TARGET_URI_LIST: - gtk_drag_finish(context, FALSE, FALSE, time); - report_error("drag_data_received", "got URI list"); + got_uri_list(widget, context, selection_data, time); break; default: gtk_drag_finish(context, FALSE, FALSE, time); @@ -578,3 +674,120 @@ static void got_data_raw(GtkWidget *widget, else gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */ } + +/* We've got a list of URIs from somewhere (probably another filer). + * If the files are on the local machine then try to copy them ourselves, + * otherwise, if there was only one file and application/octet-stream was + * provided, get the data via the X server. + */ +static void got_uri_list(GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint32 time) +{ + FilerWindow *filer_window; + GSList *uri_list; + char *error = NULL; + char **argv = NULL; /* Command to exec, or NULL */ + GSList *next_uri; + + filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window"); + g_return_if_fail(filer_window != NULL); + + uri_list = uri_list_to_gslist(selection_data->data); + + if (!uri_list) + { + /* No URIs in the list! */ + error = "No URIs in the text/uri-list (nothing to do!)"; + } + else if ((!uri_list->next) && (!get_local_path(uri_list->data))) + { + /* There is one URI in the list, and it's not on the local + * machine. Get it via the X server if possible. + */ + error = "TODO: Fetch via X Server"; + } + else + { + int local_files = 0; + const char *start_args[] = {"xterm", "-wf", + "-e", "cp", "-Riva"}; + int argc = sizeof(start_args) / sizeof(char *); + + next_uri = uri_list; + + argv = g_malloc(sizeof(start_args) + + sizeof(char *) * (g_slist_length(uri_list) + 2)); + memcpy(argv, start_args, sizeof(start_args)); + + /* Either one local URI, or a list. If anything in the list + * isn't local then we are stuck. + */ + + while (next_uri) + { + char *path; + + path = get_local_path((char *) next_uri->data); + + if (path) + { + argv[argc++] = path; + local_files++; + } + else + error = "Some of these files are on a " + "different machine - they will be " + "ignored - sorry"; + + next_uri = next_uri->next; + } + + if (local_files < 1) + { + error = "None of these files are on the local machine " + "- I can't operate on multiple remote files - " + "sorry."; + g_free(argv); + argv = NULL; + } + else + { + argv[argc++] = filer_window->path; + argv[argc++] = NULL; + } + } + + if (error) + { + gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */ + report_error("Error getting file list", error); + } + else + { + gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */ + } + + if (argv) + { + int child; /* Child process ID */ + + child = spawn(argv); + if (child) + g_hash_table_insert(child_to_filer, + (gpointer) child, filer_window); + else + report_error("ROX-Filer", "Failed to fork() child " + "process"); + g_free(argv); + } + + next_uri = uri_list; + while (next_uri) + { + g_free(next_uri->data); + next_uri = next_uri->next; + } + g_slist_free(uri_list); +} diff --git a/ROX-Filer/src/filer.c b/ROX-Filer/src/filer.c index 331afe6d..19baf982 100644 --- a/ROX-Filer/src/filer.c +++ b/ROX-Filer/src/filer.c @@ -26,7 +26,12 @@ #include "dnd.h" #include "apps.h" -FilerWindow *window_with_focus = NULL; +FilerWindow *window_with_focus = NULL; + +/* When a child process that changes a directory dies we need to know + * which filer window to update. Use this table. + */ +GHashTable *child_to_filer = NULL; static int number_of_windows = 0; static FilerWindow *window_with_selection = NULL; @@ -55,6 +60,19 @@ static gint focus_out(GtkWidget *widget, FilerWindow *filer_window); +void filer_init() +{ + child_to_filer = g_hash_table_new(NULL, NULL); +} + +/* When a filer window is destroyed we call this for each entry in the + * child_to_filer hash table to remove old entries. + */ +static gboolean child_eq(gpointer key, gpointer data, gpointer filer_window) +{ + return data == filer_window; +} + static void filer_window_destroyed(GtkWidget *widget, FilerWindow *filer_window) { @@ -63,6 +81,8 @@ static void filer_window_destroyed(GtkWidget *widget, if (window_with_focus == filer_window) window_with_focus = NULL; + g_hash_table_foreach_remove(child_to_filer, child_eq, filer_window); + if (filer_window->dir) stop_scanning(filer_window); g_free(filer_window->path); diff --git a/ROX-Filer/src/filer.h b/ROX-Filer/src/filer.h index 5b965dd5..aec4b7d2 100644 --- a/ROX-Filer/src/filer.h +++ b/ROX-Filer/src/filer.h @@ -46,9 +46,11 @@ struct _FileItem MaskedPixmap *image; }; -extern FilerWindow *window_with_focus; +extern FilerWindow *window_with_focus; +extern GHashTable *child_to_filer; /* Prototype */ +void filer_init(); void filer_opendir(char *path); void scan_dir(FilerWindow *filer_window); diff --git a/ROX-Filer/src/main.c b/ROX-Filer/src/main.c index dcfce222..9130afb6 100644 --- a/ROX-Filer/src/main.c +++ b/ROX-Filer/src/main.c @@ -6,6 +6,8 @@ */ #include +#include +#include #include #include @@ -14,12 +16,43 @@ #include "menu.h" #include "dnd.h" +static void child_died(int signum) +{ + int status; + int child; + FilerWindow *filer_window; + + /* Find out which children exited. This also has the effect of + * allowing the children to die. + */ + do + { + child = waitpid(-1, &status, WNOHANG | WUNTRACED); + + if (child == 0 || child == -1) + return; + + filer_window = g_hash_table_lookup(child_to_filer, + (gpointer) child); + if (filer_window) + scan_dir(filer_window); + + if (!WIFSTOPPED(status)) + g_hash_table_remove(child_to_filer, + (gpointer) child); + + } while (1); +} + int main(int argc, char **argv) { gtk_init(&argc, &argv); menu_init(); dnd_init(); + filer_init(); + + signal(SIGCHLD, child_died); if (argc < 2) filer_opendir(getenv("HOME")); diff --git a/ROX-Filer/src/support.c b/ROX-Filer/src/support.c index 7c7bc4d2..b7cd48b8 100644 --- a/ROX-Filer/src/support.c +++ b/ROX-Filer/src/support.c @@ -7,7 +7,9 @@ /* support.c - (non-GUI) useful routines */ +#include #include +#include #include #include @@ -69,3 +71,28 @@ char *our_host_name() return name; } + +/* fork() and run a new program. + * Returns the new PID, or 0 on failure. + */ +int spawn(char **argv) +{ + int child; + + child = fork(); + + if (child == -1) + return 0; /* Failure */ + else if (child == 0) + { + /* We are the child process */ + execvp(argv[0], argv); + fprintf(stderr, "execvp() failed: %s\n", + g_strerror(errno)); + _exit(0); + } + + /* We are the parent */ + return child; +} + diff --git a/ROX-Filer/src/support.h b/ROX-Filer/src/support.h index 0ffa5052..f67eedfd 100644 --- a/ROX-Filer/src/support.h +++ b/ROX-Filer/src/support.h @@ -8,3 +8,4 @@ char *pathdup(char *path); GString *make_path(char *dir, char *leaf); char *our_host_name(); +int spawn(char **argv); -- 2.11.4.GIT