1 #ifdef COPYRIGHT_INFORMATION
2 #include "gplv3.h"
3 #endif
4 /* This file is included by rodent_mouse.c */
5 /*
6  * Copyright (C) 2002-2012 Edscott Wilson Garcia
7  * EMail: edscott@users.sf.net
8  *
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program;
22  */
23 
24 /*************************************************************************/
25 /******************   dnd functions *************************************/
26 /************************************************************************/
27 
28 #define MAXURILEN 4096          /* Longest URI to allow */
29 
30 #define DRAG_TYPE_UNDEFINED	0
31 #define DRAG_TYPE_LOCAL		0x01
32 #define DRAG_TYPE_NET		0x02
33 #define DRAG_TYPE_INCONSISTENT	0x04
34 
35 
36 static GtkTargetEntry target_table[] = {
37     {"text/uri-list", 0, TARGET_URI_LIST},
38     {"text/x-moz-url", 0, TARGET_MOZ_URL},
39     {"text/plain", 0, TARGET_PLAIN},
40     {"UTF8_STRING", 0, TARGET_UTF8},
41     {"STRING", 0, TARGET_STRING}
42 };
43 
44 #define NUM_TARGETS (sizeof(target_table)/sizeof(GtkTargetEntry))
45 static gchar *dnd_data = NULL;
46 static view_t *drag_view_p = NULL;
47 
48 extern RfmRWLock drag_info_lock;
49 
50 #define DND_SHM_NAME "/rfm-dnd"
51 
52 /*************   core *****************/
53 #if 0
54 void
55 on_drag_data_delete (GtkWidget * widget, GdkDragContext * context, gpointer data) {
56     NOOP("rodent_mouse: on_drag_data_delete!\n\n");
57 
58     return;
59 }
60 #endif
61 
62 
63 static void
read_drag_info(gchar ** path_p,gint * type_p)64 read_drag_info(gchar **path_p, gint *type_p) {
65     fprintf(stderr,"read_drag_info\n");
66     rfm_rw_lock_reader_lock(&drag_info_lock);
67     // get shared dnd-info pointer
68     gint fd = shm_open (DND_SHM_NAME, O_RDONLY, S_IRUSR | S_IWUSR);
69     if(fd < 0){
70 	NOOP("rodent_mouse: unable to get shm-dnd-info. Assuming local...\n");
71     } else {
72 	// Figure out the size.
73 	void *p = mmap (NULL, sizeof(gint), PROT_READ, MAP_SHARED, fd, 0);
74         gint length = *((gint *)p);
75 	if(msync (p, sizeof(gint), MS_SYNC) < 0){
76 	    DBG ("msync(%s): %s\n", DND_SHM_NAME, strerror (errno));
77 	}
78 	munmap (p, sizeof(gint));
79 
80 	p = mmap (NULL, length, PROT_READ, MAP_SHARED, fd, 0);
81 	close(fd);
82 	if (type_p) *type_p = *((gint *)(p + sizeof(gint)));
83 	if (path_p) *path_p = g_strdup((gchar *)(p + (2*sizeof(gint))));
84 	munmap (p, length);
85 
86     }
87     rfm_rw_lock_reader_unlock(&drag_info_lock);
88 
89     return;
90 }
write_drag_info(const gchar * path,const gint type)91 static void write_drag_info(const gchar *path, const gint type){
92     fprintf(stderr,"write_drag_info\n");
93     gint size = sizeof(gint)*2 + strlen(path) +1;
94    fprintf(stderr,"rodent_mouse: DND>> rodent_signal_drag_begin: size=%d (type:0x%x) %s\n",
95 	    size, type, path);
96     rfm_rw_lock_writer_lock(&drag_info_lock);
97 
98 
99     // Remove old MIT-shm  dnd info (if any)
100     shm_unlink (DND_SHM_NAME);
101 
102     gint fd = shm_open (DND_SHM_NAME, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
103     if(fd < 0){
104         g_error ("rodent_signal_drag_begin(): shm_open(%s): %s", DND_SHM_NAME, strerror (errno));
105     }
106 
107     // Truncate to necessary memory size to allocate.
108     if(ftruncate (fd, size) < 0) {
109         g_error ("rodent_signal_drag_begin(): ftruncate(%s): %s", DND_SHM_NAME, strerror (errno));
110     }
111 
112     // Get a shared memory pointer.
113    void *p = mmap (NULL, sizeof(size),
114 	    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
115     // Initialize to zero
116     memset(p, 0, sizeof(size));
117 
118     // Save record size.
119     memcpy(p, &size, sizeof(gint));
120     // Save source type.
121     memcpy(p+sizeof(gint), &type, sizeof(gint));
122     // Save source path (null byte is set in initialization)
123     memcpy(p+(2*sizeof(gint)), path, strlen(path));
124     // Put in shared memory.
125     if(msync (p, sizeof(size), MS_SYNC) < 0){
126         DBG ("rodent_signal_drag_begin(): msync(%s): %s\n", DND_SHM_NAME, strerror (errno));
127     }
128     // Release map so other processes may shm_unlink.
129     munmap (p, sizeof(size));
130     // release writelock on shm_name file descriptor
131 
132     // Close file descriptor
133     close(fd);
134     rfm_rw_lock_writer_unlock(&drag_info_lock);
135 
136 }
137 /*
138  * DND sender: prepare data for the remote receiver.
139  * event: drag_data_get
140  */
141 static void
gui_drag_data_get(widgets_t * widgets_p,GSList * drag_entry_list,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time)142 gui_drag_data_get (widgets_t * widgets_p,
143                    GSList * drag_entry_list,
144 		   GdkDragContext * context,
145 		   GtkSelectionData * selection_data,
146 		   guint info,
147 		   guint time) {
148     fprintf(stderr,"gui_drag_data_get\n");
149     char *files;
150     GSList *tmp;
151     record_entry_t *en;
152     int selection_len;
153     //int drag_type;
154     gchar *format = NULL;
155     gchar *me, *she;
156 
157     if(!drag_entry_list || !g_slist_length (drag_entry_list)
158        || !drag_entry_list->data) {
159 	// This should never happen.
160 	DBG("gui_drag_data_get(): no selection list\n");
161         return;
162     }
163     en = (record_entry_t *) drag_entry_list->data;
164 
165     me = g_strdup (g_get_host_name ());
166     if (!me) me = g_strdup("localhost");
167 
168 #if GTK_MAJOR_VERSION==2
169     she = rfm_host_name (GDK_WINDOW_XID (context->dest_window));
170 #else
171     GdkWindow *w = gdk_drag_context_get_dest_window (context);
172     // Bug workaround. Function may return NULL (it happens).
173     if (w == NULL) she = g_strdup (g_get_host_name ());
174     else she = rfm_host_name (GDK_WINDOW_XID (w));
175 #endif
176     if (!she) she = g_strdup("localhost");
177 
178     if (strcmp(me,she)){
179 	DBG("DnD between clients running on different hosts is not supported.\n");
180         if(dnd_data) {
181             g_free (dnd_data);
182             dnd_data = NULL;
183         }
184         return;
185     }
186     view_t *view_p = widgets_p->view_p;
187     if(view_p->en) {
188         NOOP("rodent_mouse: DND send, (%s), me=%s --> she=%s\n", view_p->en->path, me, she);
189     }
190 
191     if(en->module) {
192         const gchar *fmt = rfm_natural (PLUGIN_DIR, en->module, en, "get_dnd_format");
193         if(fmt)
194             format = g_strdup (fmt);
195         NOOP("rodent_mouse: DND send, module format=%s\n", (format ? format : "null"));
196     } else
197         NOOP("rodent_mouse: DND send, not module format\n");
198     if(!format) {
199         //drag_type = DRAG_TYPE_LOCAL;
200         if(strcmp (me, she)) {
201             struct passwd *pw = getpwuid (getuid ());
202             if(pw) {
203                 format = g_strdup_printf ("file://%s@%s", pw->pw_name, me);
204             } else {
205                 format = g_strdup_printf ("file://%s", me);
206             }
207         } else
208             format = g_strdup ("file:");
209     }
210     g_free (me);
211     g_free (she);
212     NOOP("rodent_mouse: DND send, format=%s\n", (format ? format : "null"));
213 
214     /* prepare data for the receiver */
215     switch (info) {
216 #if 0
217     case TARGET_RAW:
218         DBG ("rodent_mouse: DND send, TARGET_RAW\n");
219     case TARGET_UTF8:
220         DBG ("rodent_mouse: DND send, TARGET_UTF8\n");
221     case TARGET_URI_LIST:
222         DBG("rodent_mouse: DND send, TARGET_URI_LIST\n");
223 #endif
224     default:
225         selection_len = 0;
226         if(dnd_data) {
227             g_free (dnd_data);
228             dnd_data = NULL;
229         }
230         /* count length of bytes to be allocated */
231         for(tmp = drag_entry_list; tmp; tmp = tmp->next) {
232             const gchar *dndpath;
233             en = (record_entry_t *) tmp->data;
234             if(!en || !en->path || !strlen (en->path))
235                 continue;
236             if(en->module && rfm_natural (PLUGIN_DIR, en->module, en, "get_dnd_path")) {
237                 dndpath = rfm_natural (PLUGIN_DIR, en->module, en, "get_dnd_path");
238             } else {
239                 dndpath = en->path;
240             }
241             /* 2 is added for the \r\n */
242             selection_len += (strlen (dndpath) + strlen (format) + 2);
243         }
244         /* 1 is added for terminating null character */
245         dnd_data = files = g_malloc (selection_len + 1);
246 	if (!dnd_data) g_error("malloc: %s", strerror(errno));
247         memset (files, 0, selection_len + 1);
248         for(tmp = drag_entry_list; tmp; tmp = tmp->next) {
249             const gchar *dndpath;
250             en = (record_entry_t *) tmp->data;
251             if(!en || !en->path || !strlen (en->path))
252                 continue;
253             if(en->module && rfm_natural (PLUGIN_DIR, en->module, en, "get_dnd_path")) {
254                 dndpath = rfm_natural (PLUGIN_DIR, en->module, en, "get_dnd_path");
255             } else {
256                 dndpath = en->path;
257             }
258             sprintf (files, "%s%s\r\n", format, dndpath);
259             files += (strlen (format) + strlen (dndpath) + 2);
260         }
261         break;
262     }
263     NOOP("rodent_mouse: DND send, drag data is:%s\n", dnd_data);
264     gtk_selection_data_set (selection_data,
265 	    gtk_selection_data_get_selection(selection_data),
266 	    8, (const guchar *)dnd_data, selection_len);
267     g_free (format);
268 }
269 
270 static gboolean
gui_drag_data(widgets_t * widgets_p,record_entry_t * target_en,GdkDragContext * context,gint x,gint y,GtkSelectionData * data,guint info,guint time)271 gui_drag_data (widgets_t * widgets_p,
272                record_entry_t * target_en,
273                GdkDragContext * context,
274 	       gint x, gint y,
275 	       GtkSelectionData * data,
276 	       guint info,
277 	       guint time) {
278     fprintf(stderr,"gui_drag_data\n");
279     int the_mode = TR_MOVE;
280     int nitems, action;
281     gchar *url;
282     int mode = 0;
283     GList *list = NULL;
284     gboolean result = FALSE;
285     gchar *he=NULL;
286     gchar *me=NULL;
287 
288     //if(!target_en || data->length < 0 || data->format != 8|| !data->data) {
289     if(!target_en) {
290 	// We should never get this warning. If we do, something is
291 	// terribly wrong.
292         DBG ("gui_drag_data(): !target_en || data->length < 0 || data->format != 8 || !data->data\n");
293         goto drag_over;         /* of course */
294     }
295 
296     me = g_strdup (g_get_host_name ());
297 #if GTK_MAJOR_VERSION==2
298     he = rfm_host_name (GDK_WINDOW_XID (context->source_window));
299 #else
300     he = rfm_host_name (
301 	    GDK_WINDOW_XID (gdk_drag_context_get_source_window (context)));
302 #endif
303 
304     view_t *view_p = widgets_p->view_p;
305     if(view_p->en) {
306         NOOP("rodent_mouse: *DND receive, (%s), me=%s --> she=%s\n", view_p->en->path, me, he);
307     }
308 
309     /* remote instance may have full format specification,
310      * or borked specification. Here we must consider
311      * both cases */
312 
313 
314 #if GTK_MAJOR_VERSION==2
315     if(context->action <= GDK_ACTION_DEFAULT) {
316 #else
317     if(gdk_drag_context_get_selected_action(context) <= GDK_ACTION_DEFAULT) {
318 #endif
319         if(getenv ("RFM_DRAG_DOES_MOVE")
320            && strlen (getenv ("RFM_DRAG_DOES_MOVE"))) {
321             action = GDK_ACTION_MOVE;
322         } else {
323             action = GDK_ACTION_COPY;
324         }
325     } else {
326 #if GTK_MAJOR_VERSION==2
327         action = context->action;
328 #else
329         action = gdk_drag_context_get_selected_action(context);
330 #endif
331     }
332 
333     NOOP("rodent_mouse: DND receive, info=%d (%d,%d)\n", info, TARGET_STRING, TARGET_URI_LIST);
334     if(!(info == TARGET_STRING) && !(info == TARGET_URI_LIST)
335        && !(info == TARGET_MOZ_URL)) {
336 	// Here we have something unknown in the drag.
337         goto drag_over;         /* of course */
338     }
339 
340     NOOP("rodent_mouse: DND receive, action=%d\n", action);
341     if(action == GDK_ACTION_MOVE)
342         the_mode = mode = TR_MOVE;
343     else if(action == GDK_ACTION_COPY)
344         the_mode = mode = TR_COPY;
345     else if(action == GDK_ACTION_LINK)
346         the_mode = mode = TR_LINK;
347     else {
348 	DBG("Drag drop mode is not GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK\n");
349         goto drag_over;         /* of course */
350     }
351 
352     NOOP("rodent_mouse: DND receive, drag data=%s\n", (const char *)gtk_selection_data_get_data (data));
353 
354     nitems = rfm_uri_parse_list ((const char *)gtk_selection_data_get_data (data), &list);
355 
356     NOOP("rodent_mouse: DND receive, nitems=%d\n", nitems);
357     if(!nitems) {
358 	DBG("number of items in drag is zero!\n");
359         goto drag_over;         /* of course */
360     }
361 
362     /***/
363 
364 
365     /* if target is a plugin, let the plugin take care of business. */
366     if(target_en->module) {
367         rfm_uri_remove_file_prefix_from_list (list, he, me);
368         NOOP("rodent_mouse: DND receive, en->module=%s\n", target_en->module);
369         if(rfm_natural (PLUGIN_DIR, target_en->module, target_en, "valid_drop_site")) {
370             NOOP("rodent_mouse: DND receive, module: valid_drop_site for %s\n", target_en->module);
371             rfm_natural (PLUGIN_DIR, target_en->module, target_en, "set_drop_entry");
372             if(rfm_complex (PLUGIN_DIR, target_en->module, widgets_p, target_en->path, list,  "process_drop")) {
373                 NOOP("rodent_mouse: DND receive, module: process_drop ok\n");
374                 result = TRUE;
375             }
376             rfm_void (PLUGIN_DIR, target_en->module, "clear_drop_entry");
377             list = rfm_uri_free_list (list);
378             goto drag_over;
379         }
380     }
381 
382 
383     /* now determine whether cp or scp should be used
384      * (we ignore the format for this determination,
385      * because other applications may bork this)*/
386     rfm_uri_remove_file_prefix_from_list (list, he, me);
387 
388     /* if target is dotdesktop type, take individual action */
389     if (target_en->mimetype &&
390 		strcmp(target_en->mimetype, "application/x-desktop")==0){
391 	if (rfm_complex(PLUGIN_DIR, "dotdesktop", widgets_p, target_en->path, list, "process_drop")) {
392 	    NOOP("rodent_mouse: DND receive, Target is dotdesktop: %s\n", target_en->path);
393 	}
394 	list = rfm_uri_free_list (list);
395 	result = TRUE;
396 	goto drag_over;
397     }
398 
399 
400     /* local file cp/ln/mv */
401     url = list->data;
402 
403     /* nonsense check */
404     struct stat st;
405     if (lstat (url, &st)==0){
406 	// Here we check if the file source and destination is actually
407 	// the same thing, this time by stat information instead of
408 	// path string.
409 	// This is a more robust test. We must test *both* st_ino and
410 	// st_dev, because stuff on different devices may (and do) have
411 	// equal st_ino.
412         if(target_en->st &&
413 		st.st_ino == target_en->st->st_ino &&
414 		st.st_dev != target_en->st->st_dev)
415 	{
416             list = rfm_uri_free_list (list);
417             rfm_diagnostics(&(view_p->widgets),"xffm/stock_dialog-warning",NULL);
418             rfm_diagnostics (widgets_p, "xffm_tag/stderr", " ", strerror (EEXIST), ": ", target_en->path, "\n", NULL);
419             goto drag_over;
420         }
421     }
422 
423 
424     gint type=0;
425     gboolean local_target = TRUE;
426     gboolean local_source = TRUE;
427     read_drag_info(NULL, &type);
428     if (!IS_LOCAL_TYPE(type))local_source = FALSE;
429     if (!IS_LOCAL_TYPE(target_en->type))local_target = FALSE;
430 
431     NOOP("rodent_mouse: DND receive, local target = %s\n",
432 	    (local_target)?"TRUE":"FALSE");
433     if (!local_target){
434 	switch (mode){
435 	    case TR_COPY:
436 		mode = TR_COPY_REMOTE;
437 
438 		break;
439 	    case TR_MOVE:
440 		mode = TR_MOVE_REMOTE;
441 		break;
442 	    case TR_LINK:
443 		mode = TR_LINK_REMOTE;
444 		break;
445 	}
446 
447     }
448     gchar *text=NULL;
449     const gchar *icon=NULL;
450     if (!local_target){
451       switch (mode){
452         case TR_COPY_REMOTE:
453 	case TR_MOVE_REMOTE:
454 	    icon = "xffm/emblem_network/compositeSW/stock_go-forward";
455 	    text = g_strdup_printf(_("Uploading file %s"), "...");
456 	    break;
457 	default:
458 	    break;
459       }
460     } else if (!local_source){
461       switch (mode){
462         case TR_COPY:
463 	case TR_MOVE:
464 	    icon = "xffm/emblem_network/compositeSW/go-back";
465 	    text = g_strdup_printf(_("Downloading file %s..."), "");
466 	    break;
467 	default:
468 	    break;
469       }
470     }
471     if (text) {
472 	rfm_diagnostics(widgets_p, "xffm/emblem_network/compositeSW/go-last", NULL);
473 	rfm_diagnostics(widgets_p, icon, NULL);
474 	rfm_diagnostics(widgets_p, "xffm_tag/red", text, "\n", NULL);
475 	g_free(text);
476     }
477     rfm_complex(RFM_MODULE_DIR, "callbacks", GINT_TO_POINTER(mode), list, target_en->path, "cp");
478     // deprecated:rodent_cp (mode, widgets_p, list, target_en->path);
479 
480     list = rfm_uri_free_list (list);
481     result = TRUE;
482   drag_over:
483     g_free (me);
484     g_free (he);
485     gtk_drag_finish (context, TRUE, (the_mode & TR_MOVE) ? TRUE : FALSE, time);
486     NOOP("rodent_mouse: DND receive, drag_over\n");
487     return result;
488 }
489 
490 
491 
492 static void setup_drag_state (view_t * view_p, GdkEventButton * event);
493 
494 static void rubber_band (view_t * view_p, int x, int y, gboolean draw);
495 
496 static gchar *atoms[]={
497         "UTF8_STRING",
498         "STRING",
499         "text/plain",
500         NULL};
501 
502 static void
503 enter_drag_state (view_t * view_p) {
504     NOOP("rodent_mouse: enter dragstate \n");
505 
506     if(view_p->mouse_event.dragstate) {
507         NOOP("rodent_mouse: dragstate true\n");
508         return;
509     }
510     fprintf(stderr,"rodent_mouse: now entering dragstate: event=0x%lx\n", (unsigned long)(&(view_p->mouse_event.drag_event)));
511     //NOOP("rodent_mouse: now entering dragstate: G_IS_OBJECT (event)=%d\n",G_IS_OBJECT (&(view_p->mouse_event.drag_event)));
512 
513 //GDK_AVAILABLE_IN_3_20
514 //GdkDragContext * gdk_drag_begin_from_point  (GdkWindow      *window,
515 //                                             GdkDevice      *device,
516 //                                             GList          *targets,
517 //                                             gint            x_root,
518 //                                             gint            y_root);
519 
520     rfm_global_t *rfm_global_p = rfm_global();
521     static GList *target_list=NULL;
522     if (! target_list) {
523         gchar **p = atoms;
524         for (;p && *p; p++){
525             //XInternAtom(rfm_global_p->Xdisplay, *p, FALSE);
526             GdkAtom g_atom = gdk_atom_intern_static_string (*p);
527             target_list = g_list_append(target_list, g_atom);
528         }
529     }
530 #if  GTK_MAJOR_VERSION>2 && GTK_MINOR_VERSION>=20
531     /*view_p->mouse_event.drag_event.context = gdk_drag_begin(
532             gtk_widget_get_parent_window (view_p->widgets.paper),
533             target_list);*/
534 
535     view_p->mouse_event.drag_event.context = gdk_drag_begin_from_point (
536             gtk_widget_get_parent_window (view_p->widgets.paper),
537             rfm_global_p->pointer, // device
538             target_list,
539             view_p->mouse_event.old_X,
540             view_p->mouse_event.old_Y);
541 #else
542 # if GTK_MAJOR_VERSION>2 && GTK_MINOR_VERSION>9
543     view_p->mouse_event.drag_event.context = gtk_drag_begin_with_coordinates (
544             view_p->widgets.paper,
545             view_p->mouse_event.target_list,
546             GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK,
547 	    1, //drag button
548 	    (GdkEvent *) (&(view_p->mouse_event.drag_event)),
549             -1, -1);
550 
551 # else
552 
553     view_p->mouse_event.drag_event.context = gtk_drag_begin (view_p->widgets.paper,
554                                                  view_p->mouse_event.target_list,
555                                                  GDK_ACTION_MOVE |
556                                                  GDK_ACTION_COPY |
557                                                  GDK_ACTION_LINK,
558 						 1, //drag button
559 						 (GdkEvent *) (&(view_p->mouse_event.drag_event)));
560     //view_p->mouse_event.drag_event.context->dest_window = gtk_widget_get_parent_window (view_p->widgets.paper);
561 
562 # endif
563 #endif
564 
565     fprintf(stderr,"rodent_mouse: drag begun...\n");
566     if(!view_p->mouse_event.drag_event.context)
567         return;
568     gdk_drag_status (view_p->mouse_event.drag_event.context, view_p->mouse_event.drag_action, view_p->mouse_event.drag_event.time);
569     fprintf(stderr,"rodent_mouse: drag status...\n");
570 
571 
572     gchar *plural_text=g_strdup_printf (
573         ngettext ("%'u item", "%'u items",g_slist_length(view_p->selection_list)),
574 	g_slist_length(view_p->selection_list));
575     gchar *g = g_strdup_printf ("%s: %s", _("Selection"), plural_text);
576     g_free(plural_text);
577 
578     rfm_status (&(view_p->widgets), "xffm/stock_dialog-info", g, NULL);
579     g_free (g);
580 
581     if(g_slist_length(view_p->selection_list) > 1) {
582         NOOP("rodent_mouse: selection_count > 1\n");
583         gtk_drag_set_icon_name (view_p->mouse_event.drag_event.context, "edit-copy", 0, 0);
584         //gtk_drag_set_icon_stock (view_p->mouse_event.drag_event.context, GTK_STOCK_DND_MULTIPLE, 0, 0);
585     } else if(view_p->mouse_event.dnd_pixbuf) {
586         NOOP("rodent_mouse: setting view_p->mouse_event.dnd_pixbuf\n");
587         gtk_drag_set_icon_pixbuf (view_p->mouse_event.drag_event.context, view_p->mouse_event.dnd_pixbuf, 0, 0);
588     } else {
589         gtk_drag_set_icon_name (view_p->mouse_event.drag_event.context, "edit-copy", 0, 0);
590         //gtk_drag_set_icon_name (view_p->mouse_event.drag_event.context, "document", 0, 0);
591         // stock icons are deprecated:
592         //gtk_drag_set_icon_stock (view_p->mouse_event.drag_event.context, GTK_STOCK_DND, 0, 0);
593     }
594     view_p->mouse_event.dragstate = TRUE;
595     fprintf(stderr,"rodent_mouse: enter dragstate done\n");
596 
597 }
598 
599 static void
600 setup_drag_state (view_t * view_p, GdkEventButton * event) {
601     fprintf(stderr,"rodent_mouse: +DND>>setup_drag_state\n");
602 
603     NOOP("rodent_mouse: +DND>>setup_drag_state\n");
604     view_p->mouse_event.drag_event.type = GDK_DRAG_ENTER;
605     view_p->mouse_event.drag_event.x_root = event->x;
606     view_p->mouse_event.drag_event.y_root = event->y;
607     view_p->mouse_event.drag_event.time = event->time + 2;
608     view_p->mouse_event.drag_event.window = event->window;
609     view_p->mouse_event.drag_event.send_event = event->send_event;
610 
611     fprintf(stderr,"rodent_mouse: event time=0x%x\n", event->time);
612 }
613 
614 /*************************************************************************/
615 /******************   end of dnd functions *******************************/
616 /*************************************************************************/
617 
618 
619 
620 static void
621 unsaturate_label (view_t *view_p) {
622     if (view_p->mouse_event.label_p){
623         population_t *p = (population_t *)view_p->mouse_event.label_p;
624 	p->flags &= (LABEL_SATURATED ^ 0xffffffff);
625         rfm_expose_label(view_p, view_p->mouse_event.label_p);
626 	view_p->mouse_event.label_p = NULL;
627     }
628 }
629 
630 static void
631 saturate_label (view_t *view_p, population_t * population_p) {
632     if(!view_p || !population_p) return;
633     //overkill: if (!rodent_valid_population_p(view_p, population_p)) return;
634     if (!population_p->en || !population_p->en->path) return;
635     if (!rfm_population_try_read_lock(view_p, "saturate_label")) {
636 	NOOP (stderr, "rodent_mouse: >> rodent_label_event: !rfm_population_try_read_lock\n");
637 	return;
638     }
639 
640     if(view_p->mouse_event.label_p != population_p) {
641 	if (view_p->mouse_event.label_p){
642 	    population_t *p = (population_t *)view_p->mouse_event.label_p;
643 	    // Turn unsaturated item off
644 	    p->flags &= (LABEL_SATURATED ^ 0xffffffff);
645 	    rfm_expose_label(view_p, view_p->mouse_event.label_p);
646 	}
647 	// saturate new item
648 	population_p->flags |= LABEL_SATURATED;
649 	view_p->mouse_event.label_p = population_p;
650 
651 	gboolean do_label_tip =  getenv("RFM_ENABLE_LABEL_TIPS") &&
652 	    strlen(getenv("RFM_ENABLE_LABEL_TIPS"));
653 	if (do_label_tip) {
654 	    rodent_activate_tip(view_p, population_p, FALSE);
655 	}
656 	rfm_expose_label (view_p, view_p->mouse_event.label_p);
657 	view_p->flags.saturation_serial++;
658     }
659     rfm_population_read_unlock(view_p, "saturate_label");
660 
661     return;
662 }
663 
664 
665 static void
666 unsaturate_icon (view_t *view_p) {
667     if (view_p->mouse_event.saturated_p){
668 	population_t *p = (population_t *)view_p->mouse_event.saturated_p;
669 	p->flags &= (POPULATION_SATURATED ^ 0xffffffff);
670 	// Label has also a different view with icon saturation.
671 	// This is why e expose the whole item.
672 	rfm_expose_item(view_p, view_p->mouse_event.saturated_p);
673 	view_p->mouse_event.saturated_p = NULL;
674     }
675 }
676 
677 static void
678 saturate_icon (view_t *view_p, population_t * population_p) {
679     NOOP( "saturate icon...\n");
680     unsaturate_label (view_p);
681     if(population_p->pixbuf) {
682 	view_p->mouse_event.dnd_pixbuf=population_p->pixbuf;
683     }
684     if(view_p->mouse_event.saturated_p != population_p) {
685 	if (view_p->mouse_event.saturated_p){
686 	    population_t *p = (population_t *)view_p->mouse_event.saturated_p;
687 	    // Turn unsaturated item off
688 	    p->flags &= (POPULATION_SATURATED ^ 0xffffffff);
689 	    rfm_expose_item(view_p, view_p->mouse_event.saturated_p);
690 	}
691 	// saturate new item
692 	population_p->flags |= POPULATION_SATURATED;
693 	view_p->mouse_event.saturated_p = population_p;
694 	rfm_expose_item(view_p, view_p->mouse_event.saturated_p);
695 	view_p->flags.saturation_serial++;
696     }
697 }
698 
699 
700 static void
701 unselect_all_pixbuf(view_t * view_p) {
702 
703     NOOP( "rodent_mouse: >> unselect_all_pixbuf\n");
704     population_t **tmp;
705     GSList *list = NULL;
706     for(tmp = view_p->population_pp; tmp && *tmp; tmp++) {
707         population_t *population_p = *tmp;
708         if(!population_p)
709             continue;
710         if(population_p == view_p->mouse_event.doing_drag_p)
711             continue;
712 
713         if (population_p->flags  & POPULATION_SELECTED) {
714 	    rfm_unselect_pixbuf (view_p, population_p);
715 	    rfm_expose_item(view_p, population_p);
716 	}
717         if (population_p->flags  & LABEL_SATURATED) {
718 	    population_p->flags  &= (LABEL_SATURATED ^ 0xffffffff);
719 	    rfm_expose_label(view_p, population_p);
720         }
721     }
722     if(view_p->selection_list){
723 	list=view_p->selection_list;
724 	for (;list && list->data; list=list->next){
725 	    record_entry_t *en=list->data;
726 	    rfm_destroy_entry(en);
727 	}
728         g_slist_free (view_p->selection_list);
729     }
730     view_p->selection_list = NULL;
731 }
732 
733 
734 static gboolean
735 scroll_business(view_t *view_p, gint y, GdkRectangle *area){
736     GtkScrolledWindow *scrolled_window = g_object_get_data(G_OBJECT(view_p->widgets.paper), "scrolled_window");
737 
738     if (!GTK_IS_SCROLLED_WINDOW (scrolled_window)) return FALSE;
739     double upper = gtk_adjustment_get_upper (
740 	    gtk_scrolled_window_get_vadjustment (scrolled_window));
741     double page = gtk_adjustment_get_page_size (
742 	    gtk_scrolled_window_get_vadjustment (scrolled_window));
743     double value = gtk_adjustment_get_value  (
744 	    gtk_scrolled_window_get_vadjustment (scrolled_window));
745     gboolean set_scroll=FALSE;
746     gdouble new_value = 0.0;
747     if (y > value + page && value + page < upper) {
748         NOOP("rodent_mouse: scrolldown: y= %d value =%lf, upper=%lf page=%lf \n",
749 		 y, value, upper, page);
750 	new_value = (y - page < upper - page)?
751 	    y - page : upper - page;
752 	NOOP("rodent_mouse: scrolldown to %lf\n", new_value );
753 	set_scroll=TRUE;
754     } else if (y < value) {
755 	new_value = y;
756 	NOOP("rodent_mouse: scrollup to %lf\n", new_value);
757         set_scroll=TRUE;
758     } else {
759          NOOP("rodent_mouse: scroll noop: y= %d value =%lf, upper=%lf page=%lf \n",
760 		 y, value, upper, page);
761    }
762     if (set_scroll){
763         gtk_adjustment_set_value (
764 		gtk_scrolled_window_get_vadjustment (
765 		    scrolled_window),
766 		new_value);
767     }
768     return set_scroll;
769 }
770 
771 static void
772 reset_reselect_list(view_t *view_p){
773     GSList *list = view_p->reselect_list;
774     for (;list && list->data; list=list->next) g_free(list->data);
775     if (view_p->reselect_list) g_slist_free(view_p->reselect_list);
776     view_p->reselect_list =NULL;
777     list = view_p->selection_list;
778     for (; list && list->data; list=list->next){
779 	record_entry_t *en = list->data;
780 	if (!en || !en->path) continue;
781 	view_p->reselect_list =
782 	    g_slist_prepend(view_p->reselect_list, g_strdup(en->path));
783     }
784 }
785 
786 
787 static void
788 rubber_band (view_t * view_p, int x, int y, gboolean draw) {
789     NOOP(stderr, "rodent_mouse: >> rubber_band: %d\n", draw);
790     if ((view_p->mouse_event.boxX == -1 && view_p->mouse_event.boxY == -1)
791 	    ||
792        (view_p->mouse_event.old_X == -1 && view_p->mouse_event.old_Y == -1)){
793 	// disactivate lpterm.
794 	if (view_p->widgets.status) {
795 	    g_object_set_data (G_OBJECT (view_p->widgets.status), "active", NULL);
796 	}
797 	if(view_p->mouse_event.old_X == -1 && view_p->mouse_event.old_Y == -1) {
798 	    view_p->mouse_event.old_X = view_p->mouse_event.boxX;
799 	    view_p->mouse_event.old_Y = view_p->mouse_event.boxY;
800 	}
801 	gchar *string = g_strdup_printf("%s: %s", _("Selection"), _("None"));
802 	rfm_status(&(view_p->widgets), "xffm/stock_dialog-info", string, NULL);
803 	g_free(string);
804 	return;
805     }
806 
807     // old rectangle
808     gint lowX = (view_p->mouse_event.old_X > view_p->mouse_event.boxX) ? view_p->mouse_event.boxX : view_p->mouse_event.old_X;
809     gint lowY = (view_p->mouse_event.old_Y > view_p->mouse_event.boxY) ? view_p->mouse_event.boxY : view_p->mouse_event.old_Y;
810     gint highX = (view_p->mouse_event.old_X < view_p->mouse_event.boxX) ? view_p->mouse_event.boxX : view_p->mouse_event.old_X;
811     gint highY = (view_p->mouse_event.old_Y < view_p->mouse_event.boxY) ? view_p->mouse_event.boxY : view_p->mouse_event.old_Y;
812 
813     /*if(view_p->mouse_event.rubberbanding == FALSE) {
814 	GdkRectangle rect;
815 	rect.x = lowX;
816 	rect.y = lowY;
817 	rect.width = highX - lowX + 1;
818 	rect.height = highY - lowY + 1;
819 	rfm_expose_rect (&rect);
820 	return;
821     }*/
822 
823     // new rectangle
824     view_p->mouse_event.old_X = x;
825     view_p->mouse_event.old_Y = y;
826     gint new_lowX = (view_p->mouse_event.old_X > view_p->mouse_event.boxX) ? view_p->mouse_event.boxX : view_p->mouse_event.old_X;
827     gint new_lowY = (view_p->mouse_event.old_Y > view_p->mouse_event.boxY) ? view_p->mouse_event.boxY : view_p->mouse_event.old_Y;
828     gint new_highX = (view_p->mouse_event.old_X < view_p->mouse_event.boxX) ? view_p->mouse_event.boxX : view_p->mouse_event.old_X;
829     gint new_highY = (view_p->mouse_event.old_Y < view_p->mouse_event.boxY) ? view_p->mouse_event.boxY : view_p->mouse_event.old_Y;
830 
831 
832     /* clean old rectangle */
833     GdkEventExpose old_event;
834     old_event.area.x=lowX;
835     old_event.area.y=lowY;
836     old_event.area.width=highX - lowX;
837     old_event.area.height=highY - lowY;
838 
839 
840     GSList *tmp;
841     // find all items in old rectangle (restricted to icons)
842     GSList *old_list=rodent_find_icons_in_rectangle(view_p, &(old_event.area));
843     //gint old_items=g_slist_length(old_list);
844 
845 
846     GdkEventExpose new_event;
847     new_event.area.x=new_lowX;
848     new_event.area.y=new_lowY;
849     new_event.area.width=new_highX - new_lowX;
850     new_event.area.height=new_highY - new_lowY;
851 
852 
853     // find all items in new rectangle (restricted to icons)
854     GSList *new_list=rodent_find_icons_in_rectangle(view_p, &(new_event.area));
855     gint new_items=g_slist_length(new_list);
856     NOOP(stderr, "items=%d (%d, %d, %d, %d)\n",
857 	    g_slist_length(new_list),
858 	    new_event.area.x, new_event.area.y, new_event.area.width, new_event.area.height);
859 
860     guint64 oldsum = 0;
861     guint64 newsum = 0;
862     // unselect all unselected items
863     for (tmp=old_list; tmp && tmp->data; tmp=tmp->next){
864 	if (!g_slist_find(new_list, tmp->data)){
865 	    population_t *population_p = tmp->data;
866             NOOP( "rubberband, unselect all pixbuf\n");
867 	    rfm_unselect_pixbuf (view_p, population_p);
868 	}
869 	if(!draw) oldsum += GPOINTER_TO_INT(tmp->data);
870     }
871 
872 
873     // this will do any scrolling
874     scroll_business(view_p, y, &(new_event.area));
875 
876     // select all selected items
877     NOOP(stderr, "selection items=%d\n",g_slist_length(new_list));
878 
879     for (tmp=new_list; tmp && tmp->data; tmp=tmp->next){
880 	population_t *pop_p = tmp->data;
881 	if (pop_p->en && IS_UP_TYPE(pop_p->en->type)) continue;
882 	rfm_select_pixbuf (view_p, pop_p);
883     }
884     g_slist_free(new_list);
885     new_list=NULL;
886     new_list=rodent_find_icons_in_rectangle(view_p, &(new_event.area));
887 
888     if(!draw) for (tmp=new_list; tmp && tmp->data; tmp=tmp->next){
889 	newsum += GPOINTER_TO_INT(tmp->data);
890     }
891 
892     // This if() is buggy...
893     if (draw || newsum != oldsum)
894     {
895 	GSList *tmp=old_list;
896 	for (;tmp && tmp->data; tmp=tmp->next){
897 	    if(!g_slist_find(new_list, tmp->data)){
898 		new_list =g_slist_prepend(new_list, tmp->data);
899 	    }
900 	}
901 	NOOP("rodent_mouse: expose necessary: items=%d (old=%d)\n",new_items,old_items);
902 	NOOP("rodent_mouse: expose lists items=%d\n", g_slist_length(new_list));
903 
904 	tmp=new_list;
905 
906 	for (;tmp && tmp->data; tmp=tmp->next){
907 	    population_t *population_p=tmp->data;
908 
909 	    // here we need a realtime expose, but will the grab
910 	    // affect? is a read lock set?
911 	    rodent_redraw_item(view_p, population_p);
912 	    //rfm_expose_item(view_p, population_p);
913 	}
914 
915 
916 
917 	gchar *string=NULL;
918 	if (new_items) {
919 	    string = g_strdup_printf (ngettext ("%'d item selected", "%'d items selected", new_items), new_items);
920 	} else {
921 	    string = g_strdup_printf("%s: %s", _("Selection"), _("None"));
922 	}
923 	rfm_status(&(view_p->widgets), "xffm/stock_dialog-info", string, NULL);
924 	g_free(string);
925 
926 	// get new reselect list.
927 	reset_reselect_list(view_p);
928 	// disactivate lpterm.
929 	if (view_p->widgets.status) {
930 	    g_object_set_data (G_OBJECT (view_p->widgets.status), "active", NULL);
931 	}
932     }
933     g_slist_free(old_list);
934     g_slist_free(new_list);
935 
936 }
937 
938 /* button press */
939 static void
940 button_popup (GdkEventButton * event, view_t * view_p, const population_t * population_p) {
941     if(population_p) {
942         if(!(population_p->flags  & POPULATION_SELECTED)) {
943             rodent_unselect_all_pixbuf (view_p);
944         }
945         rfm_select_pixbuf (view_p, population_p);
946 	rfm_expose_item (view_p, population_p);
947     }
948     rodent_pop_menu (MAIN_POPUP_MENU_ID, event);
949 }
950 /*
951 static void
952 remove_ghost_selections(view_t * view_p){
953     GSList *ghosts=NULL;
954     GList *tmp=view_p->selection_list;
955     for (; tmp && tmp->data; tmp=tmp->next){
956 	record_entry_t *en=tmp->data;
957 	if (en->path && !rfm_g_file_test(en->path, G_FILE_TEST_EXISTS)){
958 	    ghosts = g_slist_prepend(ghosts, tmp->data);
959 	}
960     }
961     GSList *stmp = ghosts;
962     for (; stmp && stmp->data; stmp=stmp->next){
963 	record_entry_t *en=stmp->data;
964 	view_p->selection_list = g_list_remove(view_p->selection_list, stmp->data);
965 	rfm_destroy_entry(en);
966     }
967     g_slist_free(ghosts);
968     if (g_list_length(view_p->selection_list)==0){
969 	g_list_free(view_p->selection_list);
970 	view_p->selection_list = NULL;
971     }
972 }
973 */
974 
975