diff --git a/src/icons.c b/src/icons.c index c5cc84f6a..bc261a391 100644 --- a/src/icons.c +++ b/src/icons.c @@ -85,6 +85,16 @@ e_icon_up_cb(void *_data, Evas _e, Evas_Object _o, int _b, int _x, int _y) e_icon_update_state(ic); ecore_window_destroy(ic->view->drag.win); ic->view->drag.started = 0; + if(e->mods & ECORE_EVENT_KEY_MODIFIER_SHIFT) + ic->view->drag.drop_mode = E_DND_COPY; + else + ic->view->drag.drop_mode = E_DND_MOVE; + + /* Handle dnd motion(drop) - dragging==0 */ + ecore_window_dnd_handle_motion( ic->view->win.base, + _x - ic->view->drag.offset.x, + _y - ic->view->drag.offset.y, + 0); D_RETURN; } if (_b == 1) @@ -349,6 +359,10 @@ e_icon_move_cb(void *_data, Evas _e, Evas_Object _o, int _b, int _x, int _y) ecore_window_show(ic->view->drag.win); ecore_pixmap_free(pmap); ecore_pixmap_free(mask); + + /* Initiate dnd */ + ecore_dnd_own_selection(ic->view->win.base); + ic->view->drag.started = 1; } } @@ -363,6 +377,13 @@ e_icon_move_cb(void *_data, Evas _e, Evas_Object _o, int _b, int _x, int _y) ic->view->drag.y = y; ic->view->drag.update = 1; ic->view->changed = 1; + if(e->mods & ECORE_EVENT_KEY_MODIFIER_SHIFT) + ic->view->drag.drop_mode = E_DND_COPY; + else + ic->view->drag.drop_mode = E_DND_MOVE; + + /* Handle dnd motion - dragging==1 */ + ecore_window_dnd_handle_motion( ic->view->win.base, x, y, 1); } D_RETURN; diff --git a/src/util.c b/src/util.c index 4513a9521..1af51862b 100644 --- a/src/util.c +++ b/src/util.c @@ -53,3 +53,50 @@ e_util_glob_matches(char *str, char *glob) D_RETURN_(0); } + +/* + * Function to take a URL of the form + * file://hostname/dir1/dir2/file + * + * Test that 'file://' exists. + * Test that hostname matches passed value + * Return a pointer to /dir1/... + * + * todo: + * - dir or filename which matches hostname will + * fool the hostname match + * - file://dir1/dir2/file is also legal but rejected by + * this presently + */ +char * +e_util_de_url_and_verify( const char *fi, const char *hostn ) +{ + char *wk; + + D_ENTER; + + wk = strstr( fi, "file://" ); + + /* Valid URL contains "file://" */ + if( !wk ) + D_RETURN_ (NULL); + + /* Need some form of hostname to continue */ + if( !hostn ) + D_RETURN_ (NULL); + + /* Do we contain hostname? */ + wk = strstr( fi, hostn ); + + /* Hostname mismatch, reject file */ + if( !wk ) + D_RETURN_ (NULL); + + /* Local file name starts after "hostname" */ + wk = strchr( wk, '/' ); + + if( !wk ) + D_RETURN_ (NULL); + + D_RETURN( wk ); +} diff --git a/src/util.h b/src/util.h index 0d9ee5b6b..ff33c1077 100644 --- a/src/util.h +++ b/src/util.h @@ -7,6 +7,7 @@ void e_util_set_env(char *variable, char *content); char *e_util_get_user_home(void); void *e_util_memdup(void *data, int size); int e_util_glob_matches(char *str, char *glob); +char *e_util_de_url_and_verify( const char *fi, const char *hostn ); #define e_strdup(__dest, __var) \ { \ diff --git a/src/view.c b/src/view.c index fb1e24b86..9e3013a3a 100644 --- a/src/view.c +++ b/src/view.c @@ -14,6 +14,10 @@ static Ecore_Event *current_ev = NULL; +static char **dnd_files = NULL; +static int dnd_num_files = 0; +static E_dnd_enum dnd_pending_mode; + static void e_bg_down_cb(void *_data, Evas _e, Evas_Object _o, int _b, int _x, int _y); static void e_bg_up_cb(void *_data, Evas _e, Evas_Object _o, int _b, int _x, int _y); static void e_bg_move_cb(void *_data, Evas _e, Evas_Object _o, int _b, int _x, int _y); @@ -34,6 +38,14 @@ static void e_visibility(Ecore_Event * ev); static void e_focus_in(Ecore_Event * ev); static void e_focus_out(Ecore_Event * ev); static void e_delete(Ecore_Event * ev); +static void e_dnd_status(Ecore_Event * ev); +static void e_dnd_data_request(Ecore_Event * ev); +static void e_dnd_drop_end(Ecore_Event * ev); +static void e_dnd_drop_position(Ecore_Event * ev); +static void e_dnd_drop(Ecore_Event * ev); +static void e_dnd_drop_request(Ecore_Event * ev); +static void e_dnd_drop_request_free(void); +static void e_dnd_handle_drop( E_View *v, E_dnd_enum dnd_pending_mode ); static void e_view_handle_fs(EfsdEvent *ev); static void e_view_handle_fs_restart(void *data); static void e_view_resort_timeout(int val, void *data); @@ -1031,6 +1043,43 @@ e_delete(Ecore_Event * ev) D_RETURN; } + +/* + * dnd status handler + * + */ +static void +e_dnd_status(Ecore_Event * ev) +{ + Ecore_Event_Dnd_Drop_Status *e; + /* + typedef struct _ecore_event_dnd_drop_status + { + Window win, root, source_win; + int x, y, w, h; + int ok; + } Ecore_Event_Dnd_Drop_Status; + */ + Evas_List l; + + D_ENTER; + + e = ev->event; + for (l = views; l; l = l->next) + { + E_View *v; + + v = l->data; + if (e->win == v->win.base) + { + ecore_window_dnd_ok(e->ok); + } + } + + D_RETURN; +} + + static void e_wheel(Ecore_Event * ev) { @@ -1886,7 +1935,9 @@ e_view_realize(E_View *v) e_scrollbar_resize(v->scrollbar.v, 12, v->size.h - 12); e_scrollbar_move(v->scrollbar.h, 0, v->size.h - 12); e_scrollbar_resize(v->scrollbar.h, v->size.w - 12, 12); - + + /* I support dnd */ + ecore_window_dnd_advertise(v->win.base); ecore_window_show(v->win.main); @@ -2465,8 +2516,355 @@ e_view_init(void) ecore_event_filter_handler_add(ECORE_EVENT_WINDOW_FOCUS_IN, e_focus_in); ecore_event_filter_handler_add(ECORE_EVENT_WINDOW_FOCUS_OUT, e_focus_out); ecore_event_filter_handler_add(ECORE_EVENT_WINDOW_DELETE, e_delete); + /* dnd source handlers */ + ecore_event_filter_handler_add(ECORE_EVENT_DND_DROP_STATUS, e_dnd_status); + ecore_event_filter_handler_add(ECORE_EVENT_DND_DATA_REQUEST, e_dnd_data_request); + ecore_event_filter_handler_add(ECORE_EVENT_DND_DROP_END, e_dnd_drop_end); + /* dnd target handlers */ + ecore_event_filter_handler_add(ECORE_EVENT_DND_DROP_POSITION, e_dnd_drop_position); + ecore_event_filter_handler_add(ECORE_EVENT_DND_DROP, e_dnd_drop); + ecore_event_filter_handler_add(ECORE_EVENT_DND_DROP_REQUEST, e_dnd_drop_request); + ecore_event_filter_idle_handler_add(e_idle, NULL); e_fs_add_event_handler(e_view_handle_fs); D_RETURN; } + + + + +/* + * send the dnd data to the target app + * + * URL formatting per RFC 1738 + */ +static void +e_dnd_data_request(Ecore_Event * ev) +{ + Ecore_Event_Dnd_Data_Request *e; + /* + * typedef struct _ecore_event_dnd_data_request + * { + * Window win, root, source_win; + * int plain_text; + * Atom destination_atom; + * } Ecore_Event_Dnd_Data_Request; + */ + Evas_List l; + char hostname[PATH_MAX]; + + D_ENTER; + + /* Need hostname for URL (file://hostname/...) */ + if(gethostname( hostname, PATH_MAX)) + { + /* failed... Default to 'localhost' */ + strcpy( hostname, "localhost"); + } + + e = ev->event; + for (l = views; l; l = l->next) + { + E_View *v; + Evas_List ll; + char *data = NULL; + int size = 3; + int idx = 0; + + /* Me, my null, and an extra for the end '/r/n'... */ + data = NEW(char, size); + *data = 0; + + v = l->data; + if (e->win == v->win.base) + { + for (ll = v->icons; ll; ll = ll->next) + { + E_Icon *ic; + + ic = ll->data; + if (ic->state.selected) + { + int ic_size; + + /* Size = 'file://' + 3 strings + host delimiter '/' and '\r\n' end. */ + ic_size = 7 + strlen(hostname) + strlen(v->dir) + strlen(ic->file)+3; + size += ic_size; + + + REALLOC(data, char, size); + + sprintf( data+idx, "file://%s%s/%s\r\n", hostname, v->dir, ic->file); + idx += ic_size; + } + } + + if(v->drag.drop_mode == E_DND_COPY) + ecore_dnd_set_mode_copy(); + else + ecore_dnd_set_mode_move(); + ecore_dnd_set_data(e->win); + + + ecore_dnd_send_data( + e->source_win, e->win, + data, size, + e->destination_atom, + /* uri-list, not plain-text */ + 0 + ); + } + FREE(data); + } + + D_RETURN; +} + + + +static void +e_dnd_drop_end(Ecore_Event * ev) +{ + Ecore_Event_Dnd_Drop_End *e; + /* + * typedef struct _ecore_event_dnd_drop_end + * { + * Window win, root, source_win; + * } Ecore_Event_Dnd_Drop_End; + */ + Evas_List l; + + D_ENTER; + + e = ev->event; + for (l = views; l; l = l->next) + { + E_View *v; + + v = l->data; + if (e->win == v->win.base) + { + ecore_window_dnd_finished(); + e_dnd_drop_request_free(); + } + } + + D_RETURN; +} + + + +static void +e_dnd_drop_position(Ecore_Event * ev) +{ + Ecore_Event_Dnd_Drop_Position *e; + /* + * typedef struct _ecore_event_dnd_drop_position + * { + * Window win, root, source_win; + * int x, y; + * } Ecore_Event_Dnd_Drop_Position; + */ + Evas_List l; + + D_ENTER; + + e = ev->event; + for (l = views; l; l = l->next) + { + E_View *v; + + v = l->data; + if (e->win == v->win.base) + { + + if( e->win != e->source_win ) + { + /* send XdndStatus */ + ecore_window_dnd_send_status_ok(v->win.base, e->source_win, + v->location.x, v->location.y, + v->size.w, v->size.h + ); + } + /* todo - cache window extents, don't send again within these extents. */ + } + } + + D_RETURN; +} + + + +static void +e_dnd_drop(Ecore_Event * ev) +{ + Ecore_Event_Dnd_Drop *e; + /* + * typedef struct _ecore_event_dnd_drop + * { + * Window win, root, source_win; + * } Ecore_Event_Dnd_Drop; + */ + Evas_List l; + + D_ENTER; + + e = ev->event; + for (l = views; l; l = l->next) + { + E_View *v; + + v = l->data; + if (e->win == v->win.base) + { + /* Dropped! Handle data */ + e_dnd_handle_drop( v, dnd_pending_mode ); + + ecore_window_dnd_finished(); + + e_dnd_drop_request_free(); + } + } + + D_RETURN; +} + + + +static void +e_dnd_drop_request(Ecore_Event * ev) +{ + Ecore_Event_Dnd_Drop_Request *e; + /* + * typedef struct _ecore_event_dnd_drop_request + * { + * Window win, root, source_win; + * int num_files; + * char **files; + * int copy, link, move; + * } Ecore_Event_Dnd_Drop_Request; + */ + Evas_List l; + + D_ENTER; + + e = ev->event; + for (l = views; l; l = l->next) + { + E_View *v; + + v = l->data; + if (e->win == v->win.base) + { + /* if it exists, we already have the data... */ + if( !dnd_files ) + { + int i; + + dnd_files = NEW_PTR(e->num_files); + + /* copy the file list locally, for use in a dnd_drop */ + for( i=0; i < e->num_files; i++ ) + dnd_files[i] = strdup( e->files[i] ); + + dnd_num_files = e->num_files; + + if( e->copy ) + dnd_pending_mode = E_DND_COPY; + else if( e->move ) + dnd_pending_mode = E_DND_MOVE; + else if( e->link ) + dnd_pending_mode = E_DND_LINK; + else + dnd_pending_mode = E_DND_ASK; + } + /* + printf( "drop-req %d-[c%dm%dl%d]--%s--\n", e->num_files, + e->copy, e->move, e->link, + e->num_files ? e->files[0] : "None" + ); + */ + } + } + + D_RETURN; +} + + + +static void +e_dnd_drop_request_free(void) +{ + + if (dnd_files) + { + int i; + + for (i = 0; i < dnd_num_files; i++) + FREE(dnd_files[i]); + + FREE(dnd_files); + + dnd_num_files = 0; + } +} + + + +static void +e_dnd_handle_drop( E_View *v, E_dnd_enum dnd_pending_mode ) +{ + char hostname[PATH_MAX]; + int in, out; + char *filename; + + D_ENTER; + + /* Need hostname for URL (file://hostname/...) */ + if(gethostname( hostname, PATH_MAX)) + { + /* failed... Default to 'localhost' */ + strcpy( hostname, "localhost"); + } + + /* Make space for destination in file list */ + dnd_num_files++; + REALLOC_PTR(dnd_files, dnd_num_files); + dnd_files[dnd_num_files-1] = NULL; + + /* Verify files are local, convert to non-URL */ + for( in=0, out=0; indir ); + + switch( dnd_pending_mode ) + { + case E_DND_COPY: + /* Copy files */ + efsd_copy( e_fs_get_connection(), out, dnd_files, + efsd_ops(0) ); + break; + case E_DND_MOVE: + efsd_move( e_fs_get_connection(), out, dnd_files, + efsd_ops(0) ); + break; + default: + /* nothing yet */ + break; + } + + D_RETURN; +} + + diff --git a/src/view.h b/src/view.h index 3f7929e28..3e4fbcdc1 100644 --- a/src/view.h +++ b/src/view.h @@ -24,6 +24,16 @@ typedef struct _E_Icon E_Icon; typedef struct _E_Iconbar E_Iconbar; #endif +#ifndef E_DND_TYPEDEF +#define E_DND_TYPEDEF +typedef enum { + E_DND_NONE, + E_DND_COPY, + E_DND_MOVE, + E_DND_LINK, + E_DND_ASK +} E_dnd_enum; +#endif struct _E_View { @@ -117,6 +127,7 @@ struct _E_View int x, y; } offset; int update; + int drop_mode; } drag; struct { int valid;