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