Elm: DragNDrop: Don't check extensions on drags
[framework/uifw/elementary.git] / src / lib / elm_cnp_helper.c
1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4 #include <Elementary.h>
5 #include "elm_priv.h"
6
7 #include <sys/mman.h>
8
9 #ifdef HAVE_ELEMENTARY_X
10
11 #define ARRAYINIT(foo)  [foo] =
12
13 //#define DEBUGON 1
14
15
16 #ifdef DEBUGON
17 # define cnp_debug(x...) fprintf(stderr, __FILE__": " x)
18 #else
19 # define cnp_debug(x...)
20 #endif
21
22 #define PROVIDER_SET "__elm_cnp_provider_set"
23
24 typedef struct _Paste_Image   Paste_Image;
25 typedef struct _Cnp_Selection Cnp_Selection;
26 typedef struct _Escape        Escape;
27 typedef struct _Tmp_Info      Tmp_Info;
28 typedef struct _Cnp_Atom      Cnp_Atom;
29 typedef struct _Saved_Type    Saved_Type;
30 typedef struct _Dropable      Dropable;
31
32 typedef Eina_Bool (*Converter_Fn_Cb)     (char *target, void *data, int size, void **data_ret, int *size_ret, Ecore_X_Atom *ttype, int *typesize);
33 typedef int       (*Response_Handler_Cb) (Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *);
34 typedef int       (*Notify_Handler_Cb)   (Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *);
35
36 enum
37 {
38    CNP_ATOM_TARGETS = 0,
39    CNP_ATOM_text_uri,
40    CNP_ATOM_text_urilist,
41    CNP_ATOM_text_x_vcard,
42    CNP_ATOM_image_png,
43    CNP_ATOM_image_jpeg,
44    CNP_ATOM_image_bmp,
45    CNP_ATOM_image_gif,
46    CNP_ATOM_image_tiff,
47    CNP_ATOM_image_svg,
48    CNP_ATOM_image_xpm,
49    CNP_ATOM_image_tga,
50    CNP_ATOM_image_ppm,
51    CNP_ATOM_XELM,
52    CNP_ATOM_text_html_utf8,
53    CNP_ATOM_text_html,
54    CNP_ATOM_UTF8STRING,
55    CNP_ATOM_STRING,
56    CNP_ATOM_TEXT,
57    CNP_ATOM_text_plain_utf8,
58    CNP_ATOM_text_plain,
59    
60    CNP_N_ATOMS,
61 };
62
63 struct _Paste_Image
64 {
65    Evas_Object *entry;
66    const char  *tag;
67    const char  *file;
68    Evas_Object *img;
69 };
70
71 struct _Cnp_Selection
72 {
73    const char      *debug;
74    Evas_Object     *widget;
75    char            *selbuf;
76    Evas_Object     *requestwidget;
77    void            *udata;
78    Elm_Sel_Format   requestformat;
79    Elm_Drop_Cb      datacb;
80    Eina_Bool      (*set)     (Ecore_X_Window, const void *data, int size);
81    Eina_Bool      (*clear)   (void);
82    void           (*request) (Ecore_X_Window, const char *target);
83
84    Elm_Sel_Format    format;
85    Ecore_X_Selection ecore_sel;
86    
87    Eina_Bool         active : 1;
88 };
89
90 struct _Escape
91 {
92    const char *escape;
93    const char  value;
94 };
95
96 struct _Tmp_Info
97 {
98    char *filename;
99    void *map;
100    int   fd;
101    int   len;
102 };
103
104 struct _Cnp_Atom
105 {
106    const char          *name;
107    Elm_Sel_Format       formats;
108    /* Called by ecore to do conversion */
109    Converter_Fn_Cb      converter;
110    Response_Handler_Cb  response;
111    Notify_Handler_Cb    notify;
112    /* Atom */
113    Ecore_X_Atom         atom;
114 };
115
116 struct _Saved_Type
117 {
118    const char  **types;
119    Paste_Image  *pi;
120    int           ntypes;
121    int           x, y;
122    Eina_Bool     textreq: 1;
123 };
124
125 struct _Dropable
126 {
127    Evas_Object     *obj;
128    /* FIXME: Cache window */
129    Elm_Sel_Format   types;
130    Elm_Drop_Cb      dropcb;
131    void            *cbdata;
132 };
133
134 static Tmp_Info *elm_cnp_tempfile_create(int size);
135 static int tmpinfo_free(Tmp_Info *tmp);
136
137 static Eina_Bool _elm_cnp_init(void);
138 static Eina_Bool selection_clear(void *udata __UNUSED__, int type, void *event);
139 static Eina_Bool selection_notify(void *udata __UNUSED__, int type, void *event);
140 static char *remove_tags(const char *p, int *len);
141 static char *mark_up(const char *start, int inlen, int *lenp);
142
143 static Evas_Object *image_provider(void *images, Evas_Object *entry, const char *item);
144 static void entry_deleted(void *images, Evas *e, Evas_Object *entry, void *unused);
145
146
147 static Eina_Bool targets_converter(char *target, void *data, int size, void **data_ret, int *size_ret, Ecore_X_Atom *ttype, int *typesize);
148 static Eina_Bool text_converter(char *target, void *data, int size, void **data_ret, int *size_ret, Ecore_X_Atom *ttype, int *typesize);
149 static Eina_Bool html_converter(char *target, void *data, int size, void **data_ret, int *size_ret, Ecore_X_Atom *ttype, int *typesize);
150 static Eina_Bool edje_converter(char *target, void *data, int size, void **data_ret, int *size_ret, Ecore_X_Atom *ttype, int *typesize);
151 static Eina_Bool uri_converter(char *target, void *data, int size, void **data_ret, int *size_ret, Ecore_X_Atom *ttype, int *typesize);
152 static Eina_Bool image_converter(char *target, void *data, int size, void **data_ret, int *size_ret, Ecore_X_Atom *ttype, int *typesize);
153 static Eina_Bool vcard_send(char *target, void *data, int size, void **data_ret, int *size_ret, Ecore_X_Atom *ttype, int *typesize);
154
155 static int response_handler_targets(Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *);
156
157 static int notify_handler_targets(Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *notify);
158 static int notify_handler_text(Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *notify);
159 static int notify_handler_image(Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *notify);
160 static int notify_handler_uri(Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *notify);
161 static int notify_handler_html(Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *notify);
162 static int vcard_receive(Cnp_Selection *sed, Ecore_X_Event_Selection_Notify *notify);
163
164 static Paste_Image *pasteimage_alloc(const char *file, int pathlen);
165 static Eina_Bool pasteimage_append(Paste_Image *pi, Evas_Object *entry);
166 static void pasteimage_free(Paste_Image *pi);
167
168 /* Optimisation: Turn this into a 256 byte table:
169  *      then can lookup in one index, not N checks */
170 static const Escape escapes[] = {
171    { "<br>",   '\n' },
172    { "<\t>",   '\t' },
173    { "gt;",    '>'  },
174    { "lt;",    '<'  },
175    { "amp;",   '&'  },
176    { "quot;",  '\'' },
177    { "dquot;", '"'  }
178 };
179 #define N_ESCAPES ((int)(sizeof(escapes) / sizeof(escapes[0])))
180
181 static const char *image_extensions[] =
182 {
183    ".png",
184    ".jpg", ".jpeg", ".jpe", ".jfif", ".jfi",
185    ".bmp",
186    ".xpm",
187    ".ppm", "pgm", ".pbm", ".pnm",
188    ".gif",
189    ".tif", ".tiff",
190    ".svg", ".svg.gz",
191    ".tga", ".targa",
192    
193    NULL
194 };
195
196 static Cnp_Atom atoms[CNP_N_ATOMS] = {
197    [CNP_ATOM_TARGETS] = {
198       "TARGETS",
199       (Elm_Sel_Format) -1, // everything
200       targets_converter,
201       response_handler_targets,
202       notify_handler_targets,
203       0
204    },
205    [CNP_ATOM_XELM] =  {
206       "application/x-elementary-markup",
207       ELM_SEL_FORMAT_MARKUP,
208       edje_converter,
209       NULL,
210       NULL,
211       0
212    },
213    [CNP_ATOM_text_uri] = {
214       "text/uri",
215       ELM_SEL_FORMAT_MARKUP | ELM_SEL_FORMAT_IMAGE, /* Either images or entries */
216       uri_converter,
217       NULL,
218       notify_handler_uri,
219       0
220    },
221    [CNP_ATOM_text_urilist] = {
222       "text/uri-list",
223       ELM_SEL_FORMAT_IMAGE,
224       uri_converter,
225       NULL,
226       notify_handler_uri,
227       0
228    },
229    [CNP_ATOM_text_x_vcard] = {
230       "text/x-vcard",
231       ELM_SEL_FORMAT_VCARD,
232       vcard_send, NULL,
233       vcard_receive, 0
234    },
235    [CNP_ATOM_image_png] = {
236       "image/png",
237       ELM_SEL_FORMAT_IMAGE,
238       image_converter,
239       NULL,
240       notify_handler_image,
241       0
242    },
243    [CNP_ATOM_image_jpeg] = {
244       "image/jpeg",
245       ELM_SEL_FORMAT_IMAGE,
246       image_converter,
247       NULL,
248       notify_handler_image,/* Raw image data is the same */
249       0
250    },
251    [CNP_ATOM_image_bmp] = {
252       "image/x-ms-bmp",
253       ELM_SEL_FORMAT_IMAGE,
254       image_converter,
255       NULL,
256       notify_handler_image,/* Raw image data is the same */
257       0
258    },
259    [CNP_ATOM_image_gif] = {
260       "image/gif",
261       ELM_SEL_FORMAT_IMAGE,
262       image_converter,
263       NULL,
264       notify_handler_image,/* Raw image data is the same */
265       0
266    },
267    [CNP_ATOM_image_tiff] = {
268       "image/tiff",
269       ELM_SEL_FORMAT_IMAGE,
270       image_converter,
271       NULL,
272       notify_handler_image,/* Raw image data is the same */
273       0
274    },
275    [CNP_ATOM_image_svg] = {
276       "image/svg+xml",
277       ELM_SEL_FORMAT_IMAGE,
278       image_converter,
279       NULL,
280       notify_handler_image,/* Raw image data is the same */
281       0
282    },
283    [CNP_ATOM_image_xpm] = {
284       "image/x-xpixmap",
285       ELM_SEL_FORMAT_IMAGE,
286       image_converter,
287       NULL,
288       notify_handler_image,/* Raw image data is the same */
289       0
290    },
291    [CNP_ATOM_image_tga] = {
292       "image/x-tga",
293       ELM_SEL_FORMAT_IMAGE,
294       image_converter,
295       NULL,
296       notify_handler_image,/* Raw image data is the same */
297       0
298    },
299    [CNP_ATOM_image_ppm] = {
300       "image/x-portable-pixmap",
301       ELM_SEL_FORMAT_IMAGE,
302       image_converter,
303       NULL,
304       notify_handler_image,/* Raw image data is the same */
305       0
306    },
307    [CNP_ATOM_text_html_utf8] = {
308       "text/html;charset=utf-8",
309       ELM_SEL_FORMAT_HTML,
310       html_converter,
311       NULL,
312       notify_handler_html,
313       0
314    },
315    [CNP_ATOM_text_html] = {
316       "text/html",
317       ELM_SEL_FORMAT_HTML,
318       html_converter,
319       NULL,
320       notify_handler_html, /* No encoding: Webkit only */
321       0
322    },
323    [CNP_ATOM_UTF8STRING] = {
324       "UTF8_STRING",
325       ELM_SEL_FORMAT_TEXT | ELM_SEL_FORMAT_MARKUP | ELM_SEL_FORMAT_HTML,
326       text_converter,
327       NULL,
328       notify_handler_text,
329       0
330    },
331    [CNP_ATOM_STRING] = {
332       "STRING",
333       ELM_SEL_FORMAT_TEXT | ELM_SEL_FORMAT_MARKUP | ELM_SEL_FORMAT_HTML,
334       text_converter,
335       NULL,
336       notify_handler_text,
337       0
338    },
339    [CNP_ATOM_TEXT] = {
340       "TEXT",
341       ELM_SEL_FORMAT_TEXT | ELM_SEL_FORMAT_MARKUP | ELM_SEL_FORMAT_HTML,
342       text_converter,
343       NULL,
344       NULL,
345       0
346    },
347    [CNP_ATOM_text_plain_utf8] = {
348       "text/plain;charset=utf-8",
349       ELM_SEL_FORMAT_TEXT | ELM_SEL_FORMAT_MARKUP | ELM_SEL_FORMAT_HTML,
350       text_converter,
351       NULL,
352       NULL,
353       0
354    },
355    [CNP_ATOM_text_plain] = {
356       "text/plain",
357       ELM_SEL_FORMAT_TEXT | ELM_SEL_FORMAT_MARKUP | ELM_SEL_FORMAT_HTML,
358       text_converter,
359       NULL,
360       NULL,
361       0
362    },
363 };
364
365 static Cnp_Selection selections[ELM_SEL_MAX] = {
366    ARRAYINIT(ELM_SEL_PRIMARY) {
367       .debug = "Primary",
368       .ecore_sel = ECORE_X_SELECTION_PRIMARY,
369       .set = ecore_x_selection_primary_set,
370       .clear = ecore_x_selection_primary_clear,
371       .request = ecore_x_selection_primary_request,
372    },
373    ARRAYINIT(ELM_SEL_SECONDARY) {
374       .debug = "Secondary",
375       .ecore_sel = ECORE_X_SELECTION_SECONDARY,
376       .set = ecore_x_selection_secondary_set,
377       .clear = ecore_x_selection_secondary_clear,
378       .request = ecore_x_selection_secondary_request,
379    },
380    ARRAYINIT(ELM_SEL_CLIPBOARD) {
381       .debug = "Clipboard",
382       .ecore_sel = ECORE_X_SELECTION_CLIPBOARD,
383       .set = ecore_x_selection_clipboard_set,
384       .clear = ecore_x_selection_clipboard_clear,
385       .request = ecore_x_selection_clipboard_request,
386    },
387    ARRAYINIT(ELM_SEL_XDND) {
388       .debug = "XDnD",
389       .ecore_sel = ECORE_X_SELECTION_XDND,
390       .request = ecore_x_selection_xdnd_request,
391    },
392 };
393
394 /* Data for DND in progress */
395 static Saved_Type savedtypes =  { NULL, NULL, 0, 0, 0, EINA_FALSE };
396
397 static void (*dragdonecb) (void *data, Evas_Object *obj) = NULL;
398 static void *dragdonedata = NULL;
399
400 static int _elm_cnp_init_count = 0;
401 /* FIXME: who left this out of XAtoms.h */
402 static Ecore_X_Atom clipboard_atom;
403
404 static Eina_List *pastedimages = NULL;
405
406 /**
407  * Drag & Drop functions
408  */
409
410 /* FIXME: Way too many globals */
411 static Eina_List *drops = NULL;
412 static Evas_Object *dragwin = NULL;
413 static int _dragx = 0, _dragy = 0;
414 static Ecore_Event_Handler *handler_pos = NULL;
415 static Ecore_Event_Handler *handler_drop = NULL;
416 static Ecore_Event_Handler *handler_enter = NULL;
417 static Ecore_Event_Handler *handler_status = NULL;
418
419 #endif
420
421 /* Stringshared, so I can just compare pointers later */
422 static const char *text_uri;
423
424 Eina_Bool
425 elm_selection_set(Elm_Sel_Type selection, Evas_Object *widget, Elm_Sel_Format format, const char *selbuf)
426 {
427 #ifdef HAVE_ELEMENTARY_X
428    Cnp_Selection *sel;
429
430    if ((unsigned int)selection >= (unsigned int)ELM_SEL_MAX) return EINA_FALSE;
431    if (!_elm_cnp_init_count) _elm_cnp_init();
432    if ((!selbuf) && (format != ELM_SEL_FORMAT_IMAGE))
433       return elm_selection_clear(selection, widget);
434
435    sel = selections + selection;
436
437    sel->active = 1;
438    sel->widget = widget;
439
440    sel->set(elm_win_xwindow_get(widget),&selection,sizeof(Elm_Sel_Type));
441    sel->format = format;
442    sel->selbuf = selbuf ? strdup(selbuf) : NULL;
443
444    return EINA_TRUE;
445 #else
446    return EINA_FALSE;
447 #endif
448 }
449
450 Eina_Bool
451 elm_selection_clear(Elm_Sel_Type selection, Evas_Object *widget)
452 {
453 #ifdef HAVE_ELEMENTARY_X
454    Cnp_Selection *sel;
455
456    if ((unsigned int)selection >= (unsigned int)ELM_SEL_MAX) return EINA_FALSE;
457    if (!_elm_cnp_init_count) _elm_cnp_init();
458
459    sel = selections + selection;
460
461    /* No longer this selection: Consider it gone! */
462    if ((!sel->active) || (sel->widget != widget)) return EINA_TRUE;
463
464    sel->active = 0;
465    sel->widget = NULL;
466    sel->clear();
467
468    return EINA_TRUE;
469 #else
470    return EINA_FALSE;
471 #endif
472 }
473
474 Eina_Bool
475 elm_selection_get(Elm_Sel_Type selection, Elm_Sel_Format format,
476                         Evas_Object *widget, Elm_Drop_Cb datacb, void *udata)
477 {
478 #ifdef HAVE_ELEMENTARY_X
479    Evas_Object *top;
480    Cnp_Selection *sel;
481
482    if ((unsigned int)selection >= (unsigned int)ELM_SEL_MAX) return EINA_FALSE;
483    if (!_elm_cnp_init_count) _elm_cnp_init();
484
485    sel = selections + selection;
486    top = elm_widget_top_get(widget);
487    if (!top) return EINA_FALSE;
488
489    sel->requestformat = format;
490    sel->requestwidget = widget;
491    sel->request(elm_win_xwindow_get(top), ECORE_X_SELECTION_TARGET_TARGETS);
492    sel->datacb = datacb;
493    sel->udata = udata;
494
495    return EINA_TRUE;
496 #else
497    return EINA_FALSE;
498 #endif
499 }
500
501 #ifdef HAVE_ELEMENTARY_X
502
503 static Eina_Bool
504 _elm_cnp_init(void)
505 {
506    int i;
507    
508    if (_elm_cnp_init_count++) return EINA_TRUE;
509    for (i = 0; i < CNP_N_ATOMS; i++)
510      {
511         atoms[i].atom = ecore_x_atom_get(atoms[i].name);
512         ecore_x_selection_converter_atom_add(atoms[i].atom,
513                                              atoms[i].converter);
514      }
515    clipboard_atom = ecore_x_atom_get("CLIPBOARD");
516
517    ecore_event_handler_add(ECORE_X_EVENT_SELECTION_CLEAR, selection_clear, NULL);
518    ecore_event_handler_add(ECORE_X_EVENT_SELECTION_NOTIFY, selection_notify, NULL);
519    
520    text_uri = eina_stringshare_add("text/uri-list");
521    return EINA_TRUE;
522 }
523
524 static Eina_Bool
525 selection_clear(void *udata __UNUSED__, int type __UNUSED__, void *event)
526 {
527    Ecore_X_Event_Selection_Clear *ev = event;
528    Cnp_Selection *sel;
529    int i;
530    
531    for (i = 0; i < ELM_SEL_MAX; i++)
532      {
533         if (selections[i].ecore_sel == ev->selection) break;
534      }
535    cnp_debug("selection %d clear\n", i);
536    /* Not me... Don't care */
537    if (i == ELM_SEL_MAX) return ECORE_CALLBACK_PASS_ON;
538
539    sel = selections + i;
540    sel->active = 0;
541    sel->widget = NULL;
542    sel->selbuf = NULL;
543
544    return ECORE_CALLBACK_PASS_ON;
545 }
546
547
548 /*
549  * Response to a selection notify:
550  *      - So we have asked for the selection list.
551  *      - If it's the targets list, parse it, and fire of what we want,
552  *      else it's the data we want.
553  */
554 static Eina_Bool
555 selection_notify(void *udata __UNUSED__, int type __UNUSED__, void *event)
556 {
557    Ecore_X_Event_Selection_Notify *ev = event;
558    Cnp_Selection *sel;
559    int i;
560    
561    cnp_debug("selection notify callback: %d\n",ev->selection);
562    switch (ev->selection)
563      {
564       case ECORE_X_SELECTION_CLIPBOARD:
565         sel = selections + ELM_SEL_CLIPBOARD;
566         break;
567       case ECORE_X_SELECTION_PRIMARY:
568         sel = selections + ELM_SEL_PRIMARY;
569         break;
570       case ECORE_X_SELECTION_SECONDARY:
571         sel = selections + ELM_SEL_SECONDARY;
572         break;
573       case ECORE_X_SELECTION_XDND:
574         sel = selections + ELM_SEL_XDND;
575         break;
576       default:
577         return ECORE_CALLBACK_PASS_ON;
578      }
579    cnp_debug("Target is %s\n", ev->target);
580    
581    for (i = 0; i < CNP_N_ATOMS; i++)
582      {
583         if (!strcmp(ev->target, atoms[i].name))
584           {
585              if (atoms[i].notify)
586                {
587                   cnp_debug("Found something: %s\n", atoms[i].name);
588                   atoms[i].notify(sel, ev);
589                } 
590              else 
591                {
592                   cnp_debug("Ignored: No handler!\n");
593                }
594           }
595      }
596
597    return ECORE_CALLBACK_PASS_ON;
598 }
599
600
601
602 static Eina_Bool
603 targets_converter(char *target __UNUSED__, void *data, int size __UNUSED__, void **data_ret, int *size_ret, Ecore_X_Atom *ttype, int *typesize)
604 {
605    int i,count;
606    Ecore_X_Atom *aret;
607    Cnp_Selection *sel;
608    
609    if (!data_ret) return EINA_FALSE;
610
611    sel = selections + *((int *)data);
612
613    for (i = 0, count = 0; i < CNP_N_ATOMS ; i++)
614      {
615         if (sel->format & atoms[i].formats) count++;
616      }
617
618    aret = malloc(sizeof(Ecore_X_Atom) * count);
619    for (i = 0, count = 0; i < CNP_N_ATOMS; i++)
620      {
621         if (sel->format & atoms[i].formats) aret[count ++] = atoms[i].atom;
622      }
623
624    *data_ret = aret;
625    if (typesize) *typesize = 32 /* urk */;
626    if (ttype) *ttype = ECORE_X_ATOM_ATOM;
627    if (size_ret) *size_ret = count;
628
629    return EINA_TRUE;
630 }
631
632 static Eina_Bool
633 image_converter(char *target __UNUSED__, void *data __UNUSED__, int size __UNUSED__, void **data_ret __UNUSED__, int *size_ret __UNUSED__, Ecore_X_Atom *ttype __UNUSED__, int *typesize __UNUSED__)
634 {
635    cnp_debug("Image converter called\n");
636    return EINA_TRUE;
637 }
638
639 static Eina_Bool
640 vcard_send(char *target __UNUSED__, void *data __UNUSED__, int size __UNUSED__, void **data_ret, int *size_ret, Ecore_X_Atom *ttype __UNUSED__, int *typesize __UNUSED__)
641 {
642    Cnp_Selection *sel;
643
644    cnp_debug("Vcard send called\n");
645    
646    sel = selections + *((int *)data);
647
648    if (data_ret) *data_ret = strdup(sel->selbuf);
649    if (size_ret) *size_ret = strlen(sel->selbuf);
650
651    return EINA_TRUE;
652 }
653 /*
654  * Callback to handle a targets response on a selection request:
655  * So pick the format we'd like; and then request it.
656  */
657 static int
658 notify_handler_targets(Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *notify)
659 {
660    Ecore_X_Selection_Data_Targets *targets;
661    Ecore_X_Atom *atomlist;
662    int i, j;
663    
664    targets = notify->data;
665    atomlist = (Ecore_X_Atom *)(targets->data.data);
666
667    for (j = 1; j < CNP_N_ATOMS; j++)
668      {
669         cnp_debug("\t%s %d\n", atoms[j].name, atoms[j].atom);
670         if (!(atoms[j].formats & sel->requestformat)) continue;
671         for (i = 0; i < targets->data.length; i++)
672           {
673              if ((atoms[j].atom == atomlist[i]) && (atoms[j].notify))
674                {
675                   cnp_debug("Atom %s matches\n",atoms[j].name);
676                   goto done;
677                }
678           }
679      }
680
681    cnp_debug("Couldn't find anything that matches\n");
682    return ECORE_CALLBACK_PASS_ON;
683
684    done:
685    cnp_debug("Sending request for %s\n",atoms[j].name);
686    sel->request(elm_win_xwindow_get(sel->requestwidget), atoms[j].name);
687
688    return ECORE_CALLBACK_PASS_ON;
689 }
690
691 static int
692 response_handler_targets(Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *notify)
693 {
694    Ecore_X_Selection_Data_Targets *targets;
695    Ecore_X_Atom *atomlist;
696    Evas_Object *top;
697    int i,j;
698
699    targets = notify->data;
700    atomlist = (Ecore_X_Atom *)(targets->data.data);
701
702    /* Start from 1: Skip targets */
703    for (j = 1 ; j < CNP_N_ATOMS ; j ++)
704      {
705         if (!(atoms[j].formats & sel->requestformat)) continue;
706         for (i = 0 ; i < targets->data.length ; i ++)
707           {
708              if ((atoms[j].atom == atomlist[i]) && (atoms[j].response))
709                {
710                   /* Found a match: Use it */
711                   goto found;
712                }
713           }
714      }
715 found:
716    if (j == CNP_N_ATOMS)
717      {
718         cnp_debug("No matching type found\n");
719         return 0;
720      }
721    
722    top = elm_widget_top_get(sel->requestwidget);
723    if (!top) return 0;
724    
725    sel->request(elm_win_xwindow_get(top), atoms[j].name);
726    return 0;
727 }
728
729
730 static int
731 notify_handler_text(Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *notify)
732 {
733    Ecore_X_Selection_Data *data;
734    char *str;
735    
736    data = notify->data;
737    cnp_debug("Notify handler text %d %d %p\n", data->format,data->length, data->data);
738    str = mark_up((char *)data->data, data->length, NULL);
739    cnp_debug("String is %s (from %s)\n", str, data->data);
740    elm_entry_entry_insert(sel->requestwidget, str);
741    free(str);
742    return 0;
743 }
744
745
746 /**
747  * So someone is pasting an image into my entry or widget...
748  */
749 static int
750 notify_handler_uri(Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *notify)
751 {
752    Ecore_X_Selection_Data *data;
753    Ecore_X_Selection_Data_Files *files;
754    Paste_Image *pi;
755    char *p, *pp;
756
757    data = notify->data;
758    cnp_debug("data->format is %d %p %p\n", data->format, notify, data);
759    if (data->content == ECORE_X_SELECTION_CONTENT_FILES)
760      {
761         cnp_debug("got a files list\n");
762         files = notify->data;
763         if (files->num_files > 1)
764           {
765              /* Don't handle many items */
766              cnp_debug("more then one file: Bailing\n");
767              return 0;
768           }
769         p = files->files[0];
770      }
771    else
772      {
773         p = (char *)data->data;
774      }
775
776    if (!p)
777      {
778         cnp_debug("Couldn't find a file\n");
779         return 0;
780      }
781    cnp_debug("Got %s\n",p);
782    if (strncmp(p, "file://", 7))
783      {
784         /* Try and continue if it looks sane */
785         if (*p != '/') return 0;
786      }
787    else
788      {
789         p += strlen("file://");
790      }
791
792    if (savedtypes.pi) pasteimage_free(savedtypes.pi);
793    pi = pasteimage_alloc(p, strlen(p));
794    if (savedtypes.textreq)
795      {
796         savedtypes.textreq = 0;
797         savedtypes.pi = pi;
798      }
799    else
800      {
801         pasteimage_append(pi, sel->requestwidget);
802         savedtypes.pi = NULL;
803      }
804    return 0;
805 }
806
807 /**
808  * Just receieved an vcard, either through cut and paste, or dnd.
809  */
810 static int
811 vcard_receive(Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *notify)
812 {
813    Dropable *dropable;
814    Eina_List *l;
815    Ecore_X_Selection_Data *data;
816
817    data = notify->data;
818    cnp_debug("vcard receive\n");
819
820    if (sel == (selections + ELM_SEL_XDND))
821      {
822         Elm_Selection_Data ddata;
823         
824         cnp_debug("drag & drop\n");
825         /* FIXME: this needs to be generic: Used for all receives */
826         EINA_LIST_FOREACH(drops, l, dropable)
827           {
828              if (dropable->obj == sel->requestwidget) break;
829           }
830         if (!dropable)
831           {
832              cnp_debug("Unable to find drop object");
833              ecore_x_dnd_send_finished();
834              return 0;
835           }
836         dropable = eina_list_data_get(l);
837         ddata.x = savedtypes.x;
838         ddata.y = savedtypes.y;
839         ddata.format = ELM_SEL_FORMAT_VCARD;
840         ddata.data = data->data;
841         ddata.len = data->length;
842         dropable->dropcb(dropable->cbdata, dropable->obj, &ddata);
843         ecore_x_dnd_send_finished();
844      }
845    else if (sel->datacb)
846      {
847         Elm_Selection_Data ddata;
848         ddata.x = ddata.y = 0;
849         ddata.format = ELM_SEL_FORMAT_IMAGE;
850         ddata.data = data->data;
851         ddata.len = data->length;
852         sel->datacb(sel->udata, sel->widget, &ddata);
853      }
854    else
855      {
856         cnp_debug("Paste request\n");
857      }
858    
859    return 0;
860
861 }
862
863
864 static int
865 notify_handler_image(Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *notify)
866 {
867    Ecore_X_Selection_Data *data;
868    Tmp_Info *tmp;
869    Paste_Image *pi;
870    
871    cnp_debug("got a png (or a jpeg)!\n");
872    data = notify->data;
873    
874    cnp_debug("Size if %d\n", data->length);
875
876    if (sel->datacb)
877      {
878         Elm_Selection_Data ddata;
879         
880         ddata.x = ddata.y = 0;
881         ddata.format = ELM_SEL_FORMAT_IMAGE;
882         ddata.data = data->data;
883         ddata.len = data->length;
884         sel->datacb(sel->udata, sel->widget, &ddata);
885         return 0;
886      }
887    
888    /* generate tmp name */
889    tmp = elm_cnp_tempfile_create(data->length);
890    memcpy(tmp->map, data->data, data->length);
891    munmap(tmp->map,data->length);
892    
893    /* FIXME: Add to paste image data to clean up */
894    pi = pasteimage_alloc(tmp->filename, strlen(tmp->filename));
895    pasteimage_append(pi, sel->requestwidget);
896
897    tmpinfo_free(tmp);
898    return 0;
899 }
900
901
902 /**
903  *    Warning: Generic text/html can';t handle it sanely.
904  *    Firefox sends ucs2 (i think).
905  *       chrome sends utf8... blerg
906  */
907 static int
908 notify_handler_html(Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *notify)
909 {
910    Ecore_X_Selection_Data *data;
911    
912    cnp_debug("Got some HTML: Checking encoding is useful\n");
913    data = notify->data;
914    
915    if (sel->datacb)
916      {
917         Elm_Selection_Data ddata;
918         ddata.x = ddata.y = 0;
919         ddata.format = ELM_SEL_FORMAT_HTML;
920         ddata.data = data->data;
921         ddata.len = data->length;
922         sel->datacb(sel->udata, sel->widget, &ddata);
923         return 0;
924      }
925
926    char *stripstr = NULL;
927    stripstr = malloc(sizeof(char) * (data->length + 1));
928    strncpy(stripstr, (char *)data->data, data->length);
929    stripstr[data->length] = '\0';
930    cnp_debug("String is %s (%d bytes)\n", stripstr, data->length);
931    elm_entry_entry_insert(sel->requestwidget, stripstr);
932    free(stripstr);
933    return 0;
934 }
935
936
937 static Eina_Bool
938 text_converter(char *target __UNUSED__, void *data, int size __UNUSED__, void **data_ret, int *size_ret, Ecore_X_Atom *ttype __UNUSED__, int *typesize __UNUSED__)
939 {
940    Cnp_Selection *sel;
941
942    cnp_debug("text converter\n");
943    sel = selections + *((int *)data);
944    if (!sel->active) return EINA_TRUE;
945    
946    if ((sel->format & ELM_SEL_FORMAT_MARKUP) ||
947        (sel->format & ELM_SEL_FORMAT_HTML))
948      {
949         *data_ret = remove_tags(sel->selbuf, size_ret);
950      }
951    else if (sel->format & ELM_SEL_FORMAT_TEXT)
952      {
953         *data_ret = strdup(sel->selbuf);
954         *size_ret = strlen(sel->selbuf);
955      }
956    else if (sel->format & ELM_SEL_FORMAT_IMAGE)
957      {
958         cnp_debug("Image %s\n", evas_object_type_get(sel->widget));
959         cnp_debug("Elm type: %s\n", elm_object_widget_type_get(sel->widget));
960         evas_object_image_file_get(elm_photocam_internal_image_get(sel->widget), (const char **)data_ret, NULL);
961         if (!*data_ret) *data_ret = strdup("No file");
962         else *data_ret = strdup(*data_ret);
963         *size_ret = strlen(*data_ret);
964      }
965    return EINA_TRUE;
966 }
967
968 static Eina_Bool
969 edje_converter(char *target __UNUSED__, void *data, int size __UNUSED__, void **data_ret, int *size_ret, Ecore_X_Atom *ttype __UNUSED__, int *typesize __UNUSED__)
970 {
971    Cnp_Selection *sel;
972
973    sel = selections + *((int *)data);
974    if (data_ret) *data_ret = strdup(sel->selbuf);
975    if (size_ret) *size_ret = strlen(sel->selbuf);
976
977    return EINA_TRUE;
978 }
979
980
981 static Eina_Bool
982 html_converter(char *target __UNUSED__, void *data, int size __UNUSED__, void **data_ret, int *size_ret, Ecore_X_Atom *ttype __UNUSED__, int *typesize __UNUSED__)
983 {
984    Cnp_Selection *sel;
985
986    sel = selections + *(int *)data;
987    if (data_ret) *data_ret = strdup(sel->selbuf);
988    if (size_ret) *size_ret = strlen(sel->selbuf);
989
990    return EINA_TRUE;
991 }
992
993 static Eina_Bool
994 uri_converter(char *target __UNUSED__, void *data, int size __UNUSED__, void **data_ret, int *size_ret, Ecore_X_Atom *ttype __UNUSED__, int *typesize __UNUSED__)
995 {
996     Cnp_Selection *sel;
997     sel = selections + *((int *)data);
998     cnp_debug("Uri converter\n");
999     if (data_ret) *data_ret = strdup(sel->selbuf);
1000     if (size_ret) *size_ret = strlen(sel->selbuf);
1001     return EINA_TRUE;
1002 }
1003
1004 /*
1005  * Image paste provide
1006  */
1007
1008 /* FIXME: Should add provider for each pasted item: Use data to store it
1009  * much easier */
1010 static Evas_Object *
1011 image_provider(void *images __UNUSED__, Evas_Object *entry, const char *item)
1012 {
1013    Paste_Image *pi;
1014    Eina_List *l;
1015
1016    cnp_debug("image provider for %s called\n", item);
1017    EINA_LIST_FOREACH(pastedimages, l, pi)
1018      {
1019         cnp_debug("is it %s?\n",pi->tag);
1020         if (!strcmp(pi->tag, item))
1021           {
1022              /* Found it */
1023              Evas_Object *o;
1024              o = evas_object_image_filled_add(evas_object_evas_get(entry));
1025              /* FIXME: Handle eets */
1026              cnp_debug("file is %s (object is %p)\n", pi->file, o);
1027              evas_object_image_file_set(o, pi->file, NULL);
1028              evas_object_show(o);
1029              return o;
1030           }
1031      }
1032    return NULL;
1033 }
1034
1035
1036 static Paste_Image *
1037 pasteimage_alloc(const char *file, int pathlen)
1038 {
1039    Paste_Image *pi;
1040    int len;
1041    char *buf, *filebuf;
1042    int prefixlen = strlen("file://");
1043
1044    pi = calloc(1, sizeof(Paste_Image));
1045    if (!pi) return NULL;
1046    
1047    len = snprintf(NULL, 0, "pasteimage-%p", pi);
1048    len++;
1049    buf = malloc(len);
1050    if (!buf)
1051      {
1052         free(pi);
1053         return NULL;
1054      }
1055    snprintf(buf, len, "pasteimage-%p", pi);
1056    pi->tag = buf;
1057    
1058    if (file)
1059      {
1060         if (strstr(file,"file://")) file += prefixlen;
1061         filebuf = alloca(pathlen + 1);
1062         strncpy(filebuf, file, pathlen);
1063         filebuf[pathlen] = 0;
1064         pi->file = strdup(filebuf);
1065      }
1066
1067    return pi;
1068 }
1069
1070 static void
1071 pasteimage_free(Paste_Image *pi)
1072 {
1073    if (!pi) return;
1074    if (pi->file) free((void*)pi->file);
1075    if (pi->tag) free((void*)pi->tag);
1076    free(pi);
1077 }
1078
1079 static Eina_Bool
1080 pasteimage_provider_set(Evas_Object *entry)
1081 {
1082    void *v;
1083    const char *type;
1084    
1085    if (!entry) return EINA_FALSE;
1086    type = elm_widget_type_get(entry);
1087    cnp_debug("type is %s\n", type);
1088    if ((!type) || (strcmp(type, "entry"))) return EINA_FALSE;
1089    
1090    v = evas_object_data_get(entry, PROVIDER_SET);
1091    if (!v)
1092      {
1093         evas_object_data_set(entry, PROVIDER_SET, pasteimage_provider_set);
1094         elm_entry_item_provider_append(entry, image_provider, NULL);
1095         evas_object_event_callback_add(entry, EVAS_CALLBACK_FREE,
1096                                        entry_deleted, NULL);
1097      }
1098    return EINA_TRUE;
1099 }
1100
1101
1102 static Eina_Bool
1103 pasteimage_append(Paste_Image *pi, Evas_Object *entry)
1104 {
1105    char *entrytag;
1106    int len;
1107    static const char *tagstring = "<item absize=240x180 href=file://%s></item>";
1108
1109    if (!pi) return EINA_FALSE;
1110    if (!entry) return EINA_FALSE;
1111
1112    pasteimage_provider_set(entry);
1113
1114    len = strlen(tagstring)+strlen(pi->file);
1115
1116    pastedimages = eina_list_append(pastedimages, pi);
1117    entrytag = alloca(len + 1);
1118    snprintf(entrytag, len + 1, tagstring, pi->file);
1119    elm_entry_entry_insert(entry, entrytag);
1120
1121    return EINA_TRUE;
1122 }
1123
1124 static void
1125 entry_deleted(void *images __UNUSED__, Evas *e __UNUSED__, Evas_Object *entry, void *unused __UNUSED__)
1126 {
1127    Paste_Image *pi;
1128    Eina_List *l,*next;
1129
1130    EINA_LIST_FOREACH_SAFE(pastedimages, l, next, pi)
1131      {
1132         if (pi->entry == entry)
1133            pastedimages = eina_list_remove_list(pastedimages, l);
1134      }
1135 }
1136
1137
1138 static char *
1139 remove_tags(const char *p, int *len)
1140 {
1141    char *q,*ret;
1142    int i;
1143    if (!p) return NULL;
1144    
1145    q = malloc(strlen(p) + 1);
1146    if (!q) return NULL;
1147    ret = q;
1148
1149    while (*p)
1150      {
1151         if ((*p != '<') && (*p != '&')) *q++ = *p++;
1152         else if (*p == '<')
1153           {
1154              if ((p[1] == 'b') && (p[2] == 'r') &&
1155                  ((p[3] == ' ') || (p[3] == '/') || (p[3] == '>')))
1156                 *q++ = '\n';
1157              while ((*p) && (*p != '>')) p++;
1158              p++;
1159           } 
1160         else if (*p == '&')
1161           {
1162              p++;
1163              for (i = 0 ; i < N_ESCAPES ; i++)
1164                {
1165                   if (!strncmp(p,escapes[i].escape, strlen(escapes[i].escape)))
1166                     {
1167                        p += strlen(escapes[i].escape);
1168                        *q = escapes[i].value;
1169                        q++;
1170                        break;
1171                     }
1172                }
1173              if (i == N_ESCAPES) *q ++= '&';
1174           }
1175      }
1176    *q = 0;
1177    if (len) *len = q - ret;
1178    return ret;
1179 }
1180
1181 /* Mark up */
1182 static char *
1183 mark_up(const char *start, int inlen, int *lenp)
1184 {
1185    int l, i;
1186    const char *p;
1187    char *q, *ret;
1188    const char *endp = NULL;
1189    
1190    if (!start) return NULL;
1191    if (inlen >= 0) endp = start + inlen;
1192    /* First pass: Count characters */
1193    for (l = 0, p = start; ((!endp) || (p < endp)) && (*p); p++)
1194      {
1195         for (i = 0 ; i < N_ESCAPES ; i ++)
1196           {
1197              if (*p == escapes[i].value)
1198                {
1199                   l += strlen(escapes[i].escape);
1200                   break;
1201                }
1202           }
1203         if (i == N_ESCAPES) l++;
1204      }
1205    
1206    q = ret = malloc(l + 1);
1207    
1208    /* Second pass: Change characters */
1209   for (p = start; *p; )
1210     {
1211        for (i = 0; i < N_ESCAPES; i++)
1212          {
1213             if (*p == escapes[i].value)
1214               {
1215                  strcpy(q, escapes[i].escape);
1216                  q += strlen(escapes[i].escape);
1217                  p ++;
1218                  break;
1219               }
1220          }
1221        if (i == N_ESCAPES) *q++ = *p++;
1222     }
1223    *q = 0;
1224    
1225    if (lenp) *lenp = l;
1226    return ret;
1227 }
1228
1229
1230 static Eina_Bool
1231 _dnd_enter(void *data __UNUSED__, int etype __UNUSED__, void *ev)
1232 {
1233    Ecore_X_Event_Xdnd_Enter *enter = ev;
1234    int i;
1235
1236    /* Skip it */
1237    if ((!enter) || (!enter->num_types) || (!enter->types)) return EINA_TRUE;
1238
1239    cnp_debug("Types\n");
1240    savedtypes.ntypes = enter->num_types;
1241    if (savedtypes.types) free(savedtypes.types);
1242    savedtypes.types = malloc(sizeof(char *) * enter->num_types);
1243    if (!savedtypes.types) return EINA_FALSE;
1244    
1245    for (i = 0; i < enter->num_types; i++)
1246      {
1247         savedtypes.types[i] = eina_stringshare_add(enter->types[i]);
1248         cnp_debug("Type is %s %p %p\n", enter->types[i],
1249                   savedtypes.types[i],text_uri);
1250         if (savedtypes.types[i] == text_uri)
1251           {
1252              /* Request it, so we know what it is */
1253              cnp_debug("Sending uri request\n");
1254              savedtypes.textreq = 1;
1255              savedtypes.pi = NULL; /* FIXME: Free? */
1256              ecore_x_selection_xdnd_request(enter->win, text_uri);
1257           }
1258      }
1259
1260    /* FIXME: Find an object and make it current */
1261    return EINA_TRUE;
1262 }
1263
1264 static Eina_Bool
1265 _dnd_drop(void *data __UNUSED__, int etype __UNUSED__, void *ev)
1266 {
1267    struct _Ecore_X_Event_Xdnd_Drop *drop;
1268    Dropable *dropable;
1269    Eina_List *l;
1270    Ecore_Evas *ee;
1271    Ecore_X_Window xwin;
1272    Elm_Selection_Data ddata;
1273    int x, y, w, h;
1274    int i, j;
1275
1276    drop = ev;
1277
1278    // check we still have something to drop
1279    if (!drops) return EINA_TRUE;
1280
1281    /* Find any widget in our window; then work out geometry rel to our window */
1282    for (l = drops; l; l = l->next)
1283      {
1284         dropable = l->data;
1285         xwin = (Ecore_X_Window)ecore_evas_window_get
1286            (ecore_evas_ecore_evas_get(evas_object_evas_get
1287                                       (dropable->obj)));
1288         if (xwin == drop->win) break;
1289      }
1290    /* didn't find a window */
1291    if (!l) return EINA_TRUE;
1292
1293    /* Calculate real (widget relative) position */
1294    // - window position
1295    // - widget position
1296    ee = ecore_evas_ecore_evas_get(evas_object_evas_get(dropable->obj));
1297    ecore_evas_geometry_get(ee, &x, &y, NULL, NULL);
1298    savedtypes.x = drop->position.x - x;
1299    savedtypes.y = drop->position.y - y;
1300    
1301    cnp_debug("Drop position is %d,%d\n", savedtypes.x, savedtypes.y);
1302
1303    for (; l; l = l->next)
1304      {
1305         dropable = l->data;
1306         evas_object_geometry_get(dropable->obj, &x, &y, &w, &h);
1307         if ((savedtypes.x >= x) && (savedtypes.y >= y) &&
1308             (savedtypes.x < x + w) && (savedtypes.y < y + h))
1309            break; /* found! */
1310      }
1311    
1312    if (!l) return EINA_TRUE; /* didn't find one */
1313    
1314    evas_object_geometry_get(dropable->obj, &x, &y, NULL, NULL);
1315    savedtypes.x -= x;
1316    savedtypes.y -= y;
1317    
1318    /* Find our type from the previous list */
1319    for (i = 0; i < CNP_N_ATOMS; i++)
1320      {
1321         for (j = 0; j < savedtypes.ntypes; j++)
1322           {
1323              if (!strcmp(savedtypes.types[j], atoms[i].name)) goto found;
1324           }
1325      }
1326    
1327    cnp_debug("Didn't find a target\n");
1328    return EINA_TRUE;
1329    
1330 found:
1331    cnp_debug("Found a target we'd like: %s\n", atoms[i].name);
1332    cnp_debug("0x%x\n",xwin);
1333    
1334    if (i == CNP_ATOM_text_urilist)
1335      {
1336         cnp_debug("We found a URI... (%scached) %s\n",
1337                   savedtypes.pi ? "" : "not ",
1338                   savedtypes.pi->file);
1339         if (savedtypes.pi)
1340           {
1341              char *entrytag;
1342              static const char *tagstring = "<item absize=240x180 href="
1343                                                    "file://%s></item>";
1344              ddata.x = savedtypes.x;
1345              ddata.y = savedtypes.y;
1346
1347              /* If it's markup that also supports images */
1348              if ((dropable->types & ELM_SEL_FORMAT_MARKUP) &&
1349                     (dropable->types & ELM_SEL_FORMAT_IMAGE))
1350                {
1351                   int len;
1352                   ddata.format = ELM_SEL_FORMAT_MARKUP;
1353                   pasteimage_provider_set(dropable->obj);
1354
1355                   pastedimages = eina_list_append(pastedimages, savedtypes.pi);
1356                   len = strlen(tagstring) + strlen(savedtypes.pi->file);
1357                   entrytag = alloca(len + 1);
1358                   snprintf(entrytag, len + 1, tagstring, savedtypes.pi->file);
1359                   ddata.data = entrytag;
1360                   cnp_debug("Insert %s\n", (char *)ddata.data);
1361                   dropable->dropcb(dropable->cbdata, dropable->obj, &ddata);
1362                   ecore_x_dnd_send_finished();
1363                   return EINA_TRUE;
1364                }
1365              else if (dropable->types & ELM_SEL_FORMAT_IMAGE)
1366                {
1367                   cnp_debug("Doing image insert (%s)\n", savedtypes.pi->file);
1368                   ddata.format = ELM_SEL_FORMAT_IMAGE;
1369                   ddata.data = (char *)savedtypes.pi->file;
1370                   dropable->dropcb(dropable->cbdata, dropable->obj, &ddata);
1371                   ecore_x_dnd_send_finished();
1372
1373                   pasteimage_free(savedtypes.pi);
1374                   savedtypes.pi = NULL;
1375
1376                   return EINA_TRUE;
1377                }
1378              else
1379                {
1380                   cnp_debug("Item doesn't support images... passing\n");
1381                   pasteimage_free(savedtypes.pi);
1382                   return EINA_TRUE;
1383                }
1384           }
1385         else if (savedtypes.textreq)
1386           {
1387              /* Already asked: Pretend we asked now, and paste immediately when
1388               * it comes in */
1389              savedtypes.textreq = 0;
1390              ecore_x_dnd_send_finished();
1391              return EINA_TRUE;
1392           }
1393      }
1394
1395    cnp_debug("doing a request then\n");
1396    selections[ELM_SEL_XDND].requestwidget = dropable->obj;
1397    selections[ELM_SEL_XDND].requestformat = ELM_SEL_FORMAT_MARKUP;
1398    selections[ELM_SEL_XDND].active = EINA_TRUE;
1399
1400    ecore_x_selection_xdnd_request(xwin, atoms[i].name);
1401
1402    return EINA_TRUE;
1403 }
1404 static Eina_Bool
1405 _dnd_position(void *data __UNUSED__, int etype __UNUSED__, void *ev)
1406 {
1407    struct _Ecore_X_Event_Xdnd_Position *pos;
1408    Ecore_X_Rectangle rect;
1409
1410    pos = ev;
1411
1412    /* Need to send a status back */
1413    /* FIXME: Should check I can drop here */
1414    /* FIXME: Should highlight widget */
1415    rect.x = pos->position.x - 5;
1416    rect.y = pos->position.y - 5;
1417    rect.width = 10;
1418    rect.height = 10;
1419    ecore_x_dnd_send_status(EINA_TRUE, EINA_FALSE, rect, pos->action);
1420
1421    return EINA_TRUE;
1422 }
1423
1424 /**
1425  * When dragging this is callback response from the destination.
1426  * The important thing we care about: Can we drop; thus update cursor
1427  * appropriately.
1428  */
1429 static Eina_Bool
1430 _dnd_status(void *data __UNUSED__, int etype __UNUSED__, void *ev)
1431 {
1432    struct _Ecore_X_Event_Xdnd_Status *status = ev;
1433
1434    if (!status) return EINA_TRUE;
1435    
1436    /* Only thing we care about: will accept */
1437    if (status->will_accept)
1438      {
1439         cnp_debug("Will accept\n");
1440      }
1441    else
1442      { /* Won't accept */
1443         cnp_debug("Won't accept accept\n");
1444      }
1445    return EINA_TRUE;
1446 }
1447
1448 /**
1449  * Add a widget as drop target.
1450  */
1451 Eina_Bool
1452 elm_drop_target_add(Evas_Object *obj, Elm_Sel_Type format, Elm_Drop_Cb dropcb, void *cbdata)
1453 {
1454    Dropable *drop;
1455    Ecore_X_Window xwin;
1456    Eina_List *item;
1457    int first;
1458
1459    if (!obj) return EINA_FALSE;
1460    if (!_elm_cnp_init_count) _elm_cnp_init();
1461
1462    /* Is this the first? */
1463    first = (!drops) ? 1 : 0;
1464
1465    EINA_LIST_FOREACH(drops, item, drop)
1466      {
1467         if (drop->obj == obj)
1468           {
1469              /* Update: Not a new one */
1470              drop->dropcb = dropcb;
1471              drop->cbdata = cbdata;
1472              drop->types = format;
1473              return EINA_TRUE;
1474           }
1475      }
1476
1477    /* Create new drop */
1478    drop = calloc(1, sizeof(Dropable));
1479    if (!drop) return EINA_FALSE;
1480    /* FIXME: Check for eina's deranged error method */
1481    drops = eina_list_append(drops, drop);
1482
1483    if (!drops/* || or other error */)
1484      {
1485         free(drop);
1486         return EINA_FALSE;
1487      }
1488    drop->dropcb = dropcb;
1489    drop->cbdata = cbdata;
1490    drop->types = format;
1491    drop->obj = obj;
1492
1493    evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL,
1494                                   /* I love C and varargs */
1495                                   (Evas_Object_Event_Cb)elm_drop_target_del,
1496                                   obj);
1497    /* FIXME: Handle resizes */
1498
1499    /* If not the first: We're done */
1500    if (!first) return EINA_TRUE;
1501
1502    xwin = (Ecore_X_Window)ecore_evas_window_get
1503       (ecore_evas_ecore_evas_get(evas_object_evas_get(obj)));
1504    
1505    ecore_x_dnd_aware_set(xwin, EINA_TRUE);
1506    
1507    cnp_debug("Adding drop target calls\n");
1508    handler_enter = ecore_event_handler_add(ECORE_X_EVENT_XDND_ENTER,
1509                                            _dnd_enter, NULL);
1510    handler_pos = ecore_event_handler_add(ECORE_X_EVENT_XDND_POSITION,
1511                                          _dnd_position, NULL);
1512    handler_drop = ecore_event_handler_add(ECORE_X_EVENT_XDND_DROP,
1513                                           _dnd_drop, NULL);
1514    
1515    return EINA_TRUE;
1516 }
1517
1518 Eina_Bool
1519 elm_drop_target_del(Evas_Object *obj)
1520 {
1521    Dropable *drop,*del;
1522    Eina_List *item;
1523    Ecore_X_Window xwin;
1524    
1525    del = NULL;
1526    EINA_LIST_FOREACH(drops, item, drop)
1527      {
1528         if (drop->obj == obj)
1529           {
1530              drops = eina_list_remove_list(drops, item);
1531              del = drop;
1532              break;
1533           }
1534      }
1535    if (!del) return EINA_FALSE;
1536    
1537    evas_object_event_callback_del(obj, EVAS_CALLBACK_FREE,
1538                                   (Evas_Object_Event_Cb)elm_drop_target_del);
1539    free(drop);
1540    /* If still drops there: All fine.. continue */
1541    if (drops) return EINA_TRUE;
1542    
1543    cnp_debug("Disabling DND\n");
1544    xwin = (Ecore_X_Window)ecore_evas_window_get
1545       (ecore_evas_ecore_evas_get(evas_object_evas_get(obj)));
1546    ecore_x_dnd_aware_set(xwin, EINA_FALSE);
1547    
1548    ecore_event_handler_del(handler_pos);
1549    ecore_event_handler_del(handler_drop);
1550    ecore_event_handler_del(handler_enter);
1551    
1552    if (savedtypes.pi)
1553      {
1554         pasteimage_free(savedtypes.pi);
1555         savedtypes.pi = NULL;
1556      }
1557    
1558    return EINA_TRUE;
1559 }
1560
1561
1562 static void
1563 _drag_mouse_up(void *un __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *data __UNUSED__)
1564 {
1565    evas_object_event_callback_del(obj, EVAS_CALLBACK_MOUSE_UP, _drag_mouse_up);
1566    ecore_x_dnd_drop();
1567    if (dragdonecb)
1568      {
1569         dragdonecb(dragdonecb,selections[ELM_SEL_XDND].widget);
1570         dragdonecb = NULL;
1571      }
1572    if (dragwin)
1573      {
1574         evas_object_del(dragwin);
1575         dragwin = NULL;
1576      }
1577 }
1578
1579 static void
1580 _drag_move(void *data __UNUSED__, Ecore_X_Xdnd_Position *pos)
1581 {
1582    evas_object_move(dragwin, 
1583                     pos->position.x - _dragx, 
1584                     pos->position.y - _dragy);
1585 }
1586
1587
1588 Eina_Bool
1589 elm_drag_start(Evas_Object *obj, Elm_Sel_Format format, const char *data, void (*dragdone) (void *data, Evas_Object *), void *donecbdata)
1590 {
1591    Ecore_X_Window xwin;
1592    Cnp_Selection *sel;
1593    Elm_Sel_Type xdnd = ELM_SEL_XDND;
1594    Ecore_Evas *ee;
1595    int x, y, x2, y2, x3, y3;
1596    Evas_Object *icon;
1597    int w, h;
1598    
1599    if (!_elm_cnp_init_count) _elm_cnp_init();
1600    
1601    xwin = elm_win_xwindow_get(obj);
1602
1603    cnp_debug("starting drag...\n");
1604
1605    ecore_x_dnd_type_set(xwin, "text/uri-list", 1);
1606    sel = selections + ELM_SEL_XDND;
1607    sel->active = 1;
1608    sel->widget = obj;
1609    sel->format = format;
1610    sel->selbuf = data ? strdup(data) : NULL;
1611    dragdonecb = dragdone;
1612    dragdonedata = donecbdata;
1613    
1614    ecore_x_dnd_callback_pos_update_set(_drag_move, NULL);
1615    ecore_x_dnd_begin(xwin, (unsigned char *)&xdnd, sizeof(Elm_Sel_Type));
1616    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_UP,
1617                                   _drag_mouse_up, NULL);
1618    
1619    handler_status = ecore_event_handler_add(ECORE_X_EVENT_XDND_STATUS,
1620                                             _dnd_status, NULL);
1621    
1622    dragwin = elm_win_add(NULL, "Elm Drag Object", ELM_WIN_UTILITY);
1623    elm_win_override_set(dragwin, 1);
1624    
1625    /* FIXME: Images only */
1626    icon = elm_icon_add(dragwin);
1627    elm_icon_file_set(icon, data + 7, NULL); /* 7!? "file://" */
1628    elm_win_resize_object_add(dragwin,icon);
1629    evas_object_size_hint_weight_set(icon, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1630    evas_object_size_hint_align_set(icon, EVAS_HINT_FILL, EVAS_HINT_FILL);
1631
1632    /* Position subwindow appropriately */
1633    ee = ecore_evas_ecore_evas_get(evas_object_evas_get(obj));
1634    ecore_evas_geometry_get(ee, &x, &y, NULL, NULL);
1635    evas_object_geometry_get(obj, &x2, &y2, &w, &h);
1636    x += x2;
1637    y += y2;
1638    evas_object_move(dragwin, x, y);
1639    evas_object_resize(icon, w, h);
1640    evas_object_resize(dragwin, w, h);
1641    
1642    evas_object_show(icon);
1643    evas_object_show(dragwin);
1644
1645    evas_pointer_canvas_xy_get(evas_object_evas_get(obj), &x3, &y3);
1646    _dragx = x3 - x2;
1647    _dragy = y3 - y2;
1648
1649    return EINA_TRUE;
1650 }
1651
1652 static Tmp_Info *
1653 elm_cnp_tempfile_create(int size)
1654 {
1655    Tmp_Info *info;
1656    const char *tmppath;
1657    int len;
1658    
1659    info = malloc(sizeof(Tmp_Info));
1660    if (!info) return NULL;
1661    
1662    tmppath = getenv("TMP");
1663    if (!tmppath) tmppath = P_tmpdir;
1664    if (!tmppath) tmppath = "/tmp";
1665    len = snprintf(NULL, 0, "%s/%sXXXXXX", tmppath, "elmcnpitem-");
1666    if (len < 0)
1667      {
1668         free(info);
1669         return NULL;
1670      }
1671    len++;
1672    info->filename = malloc(len);
1673    if (!info->filename)
1674      {
1675         free(info);
1676         return NULL;
1677      }
1678    snprintf(info->filename,len,"%s/%sXXXXXX", tmppath, "elmcnpitem-");
1679    
1680    info->fd = mkstemp(info->filename);
1681    
1682 # ifdef __linux__
1683      {
1684         char *tmp;
1685         /* And before someone says anything see POSIX 1003.1-2008 page 400 */
1686         long pid;
1687         
1688         pid = (long)getpid();
1689         /* Use pid instead of /proc/self: That way if can be passed around */
1690         len = snprintf(NULL,0,"/proc/%li/fd/%i", pid, info->fd);
1691         len++;
1692         tmp = malloc(len);
1693         if (tmp)
1694           {
1695              snprintf(tmp,len, "/proc/%li/fd/%i", pid, info->fd);
1696              unlink(info->filename);
1697              free(info->filename);
1698              info->filename = tmp;
1699           }
1700      }
1701 # endif
1702    
1703    cnp_debug("filename is %s\n", info->filename);
1704    if (size < 1)
1705      {
1706         /* Set map to NULL and return */
1707         info->map = NULL;
1708         info->len = 0;
1709         return info;
1710      }
1711    
1712    /* Map it in */
1713    if (ftruncate(info->fd, size))
1714      {
1715         perror("ftruncate");
1716         info->map = NULL;
1717         info->len = 0;
1718         return info;
1719      }
1720    
1721    info->map = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, info->fd, 0);
1722    if (info->map == MAP_FAILED)
1723      {
1724         perror("mmap");
1725         info->map = NULL;
1726         info->len = 0;
1727      }
1728    
1729    return info;
1730 }
1731
1732
1733 static int
1734 tmpinfo_free(Tmp_Info *info)
1735 {
1736    if (!info) return 0;
1737    free(info->filename);
1738    free(info);
1739    return 0;
1740 }
1741
1742 #else
1743 /* Stubs for windows */
1744 Eina_Bool
1745 elm_drag_start(Evas_Object *o, Elm_Sel_Format f, const char *d, void (*donecb)(void *, Evas_Object *),void *cbdata)
1746 {
1747    return EINA_FALSE;
1748 }
1749
1750 Eina_Bool
1751 elm_drop_target_add(Evas_Object *obj, Elm_Sel_Type format, Elm_Drop_Cb dropcb, void *cbdata)
1752 {
1753    return EINA_FALSE;
1754 }
1755
1756 Eina_Bool
1757 elm_drop_target_del(Evas_Object *o)
1758 {
1759    return EINA_TRUE;
1760 }
1761 #endif
1762
1763 /* vim:set ts=8 sw=3 sts=3 expandtab cino=>5n-2f0^-2{2(0W1st0 :*/