9d8fb1c5972ddc270c70f791ef0f8aa77b3caae3
[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, *ext;
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 p = (char *)data->data;
772    if (!p)
773      {
774         cnp_debug("Couldn't find a file\n");
775         return 0;
776      }
777    cnp_debug("Got %s\n",p);
778    if (strncmp(p, "file://", 7))
779      {
780         /* Try and continue if it looks sane */
781         if (*p != '/') return 0;
782      }
783    else p += strlen("file://");
784    
785    ext = p + strlen(p);
786    if (ext)
787      {
788         Eina_Bool extok = EINA_FALSE;
789         int i;
790         
791         for (i = 0; image_extensions[i]; i++)
792           {
793              pp = ext - strlen(image_extensions[i]);
794              if ((pp >= p) && (!strcasecmp(pp, image_extensions[i])))
795                {
796                   extok = EINA_TRUE;
797                   break;
798                }
799           }
800         if (!extok)
801           {
802              cnp_debug("No known image format extension, ignoring\n");
803              if (savedtypes.textreq) savedtypes.textreq = 0;
804              return 0;
805           }
806      }
807
808    if (savedtypes.pi) pasteimage_free(savedtypes.pi);
809    pi = pasteimage_alloc(p, strlen(p));
810    if (savedtypes.textreq)
811      {
812         savedtypes.textreq = 0;
813         savedtypes.pi = pi;
814      }
815    else
816      {
817         pasteimage_append(pi, sel->requestwidget);
818         savedtypes.pi = NULL;
819      }
820    return 0;
821 }
822
823 /**
824  * Just receieved an vcard, either through cut and paste, or dnd.
825  */
826 static int
827 vcard_receive(Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *notify)
828 {
829    Dropable *dropable;
830    Eina_List *l;
831    Ecore_X_Selection_Data *data;
832
833    data = notify->data;
834    cnp_debug("vcard receive\n");
835
836    if (sel == (selections + ELM_SEL_XDND))
837      {
838         Elm_Selection_Data ddata;
839         
840         cnp_debug("drag & drop\n");
841         /* FIXME: this needs to be generic: Used for all receives */
842         EINA_LIST_FOREACH(drops, l, dropable)
843           {
844              if (dropable->obj == sel->requestwidget) break;
845           }
846         if (!dropable)
847           {
848              cnp_debug("Unable to find drop object");
849              ecore_x_dnd_send_finished();
850              return 0;
851           }
852         dropable = eina_list_data_get(l);
853         ddata.x = savedtypes.x;
854         ddata.y = savedtypes.y;
855         ddata.format = ELM_SEL_FORMAT_VCARD;
856         ddata.data = data->data;
857         ddata.len = data->length;
858         dropable->dropcb(dropable->cbdata, dropable->obj, &ddata);
859         ecore_x_dnd_send_finished();
860      }
861    else if (sel->datacb)
862      {
863         Elm_Selection_Data ddata;
864         ddata.x = ddata.y = 0;
865         ddata.format = ELM_SEL_FORMAT_IMAGE;
866         ddata.data = data->data;
867         ddata.len = data->length;
868         sel->datacb(sel->udata, sel->widget, &ddata);
869      }
870    else
871      {
872         cnp_debug("Paste request\n");
873      }
874    
875    return 0;
876
877 }
878
879
880 static int
881 notify_handler_image(Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *notify)
882 {
883    Ecore_X_Selection_Data *data;
884    Tmp_Info *tmp;
885    Paste_Image *pi;
886    
887    cnp_debug("got a png (or a jpeg)!\n");
888    data = notify->data;
889    
890    cnp_debug("Size if %d\n", data->length);
891
892    if (sel->datacb)
893      {
894         Elm_Selection_Data ddata;
895         
896         ddata.x = ddata.y = 0;
897         ddata.format = ELM_SEL_FORMAT_IMAGE;
898         ddata.data = data->data;
899         ddata.len = data->length;
900         sel->datacb(sel->udata, sel->widget, &ddata);
901         return 0;
902      }
903    
904    /* generate tmp name */
905    tmp = elm_cnp_tempfile_create(data->length);
906    memcpy(tmp->map, data->data, data->length);
907    munmap(tmp->map,data->length);
908    
909    /* FIXME: Add to paste image data to clean up */
910    pi = pasteimage_alloc(tmp->filename, strlen(tmp->filename));
911    pasteimage_append(pi, sel->requestwidget);
912
913    tmpinfo_free(tmp);
914    return 0;
915 }
916
917
918 /**
919  *    Warning: Generic text/html can';t handle it sanely.
920  *    Firefox sends ucs2 (i think).
921  *       chrome sends utf8... blerg
922  */
923 static int
924 notify_handler_html(Cnp_Selection *sel, Ecore_X_Event_Selection_Notify *notify)
925 {
926    Ecore_X_Selection_Data *data;
927    
928    cnp_debug("Got some HTML: Checking encoding is useful\n");
929    data = notify->data;
930    
931    if (sel->datacb)
932      {
933         Elm_Selection_Data ddata;
934         ddata.x = ddata.y = 0;
935         ddata.format = ELM_SEL_FORMAT_HTML;
936         ddata.data = data->data;
937         ddata.len = data->length;
938         sel->datacb(sel->udata, sel->widget, &ddata);
939         return 0;
940      }
941
942    char *stripstr = NULL;
943    stripstr = malloc(sizeof(char) * (data->length + 1));
944    strncpy(stripstr, (char *)data->data, data->length);
945    stripstr[data->length] = '\0';
946    cnp_debug("String is %s (%d bytes)\n", stripstr, data->length);
947    elm_entry_entry_insert(sel->requestwidget, stripstr);
948    free(stripstr);
949    return 0;
950 }
951
952
953 static Eina_Bool
954 text_converter(char *target __UNUSED__, void *data, int size __UNUSED__, void **data_ret, int *size_ret, Ecore_X_Atom *ttype __UNUSED__, int *typesize __UNUSED__)
955 {
956    Cnp_Selection *sel;
957
958    cnp_debug("text converter\n");
959    sel = selections + *((int *)data);
960    if (!sel->active) return EINA_TRUE;
961    
962    if ((sel->format & ELM_SEL_FORMAT_MARKUP) ||
963        (sel->format & ELM_SEL_FORMAT_HTML))
964      {
965         *data_ret = remove_tags(sel->selbuf, size_ret);
966      }
967    else if (sel->format & ELM_SEL_FORMAT_TEXT)
968      {
969         *data_ret = strdup(sel->selbuf);
970         *size_ret = strlen(sel->selbuf);
971      }
972    else if (sel->format & ELM_SEL_FORMAT_IMAGE)
973      {
974         cnp_debug("Image %s\n", evas_object_type_get(sel->widget));
975         cnp_debug("Elm type: %s\n", elm_object_widget_type_get(sel->widget));
976         evas_object_image_file_get(elm_photocam_internal_image_get(sel->widget), (const char **)data_ret, NULL);
977         if (!*data_ret) *data_ret = strdup("No file");
978         else *data_ret = strdup(*data_ret);
979         *size_ret = strlen(*data_ret);
980      }
981    return EINA_TRUE;
982 }
983
984 static Eina_Bool
985 edje_converter(char *target __UNUSED__, void *data, int size __UNUSED__, void **data_ret, int *size_ret, Ecore_X_Atom *ttype __UNUSED__, int *typesize __UNUSED__)
986 {
987    Cnp_Selection *sel;
988
989    sel = selections + *((int *)data);
990    if (data_ret) *data_ret = strdup(sel->selbuf);
991    if (size_ret) *size_ret = strlen(sel->selbuf);
992
993    return EINA_TRUE;
994 }
995
996
997 static Eina_Bool
998 html_converter(char *target __UNUSED__, void *data, int size __UNUSED__, void **data_ret, int *size_ret, Ecore_X_Atom *ttype __UNUSED__, int *typesize __UNUSED__)
999 {
1000    Cnp_Selection *sel;
1001
1002    sel = selections + *(int *)data;
1003    if (data_ret) *data_ret = strdup(sel->selbuf);
1004    if (size_ret) *size_ret = strlen(sel->selbuf);
1005
1006    return EINA_TRUE;
1007 }
1008
1009 static Eina_Bool
1010 uri_converter(char *target __UNUSED__, void *data, int size __UNUSED__, void **data_ret, int *size_ret, Ecore_X_Atom *ttype __UNUSED__, int *typesize __UNUSED__)
1011 {
1012     Cnp_Selection *sel;
1013     sel = selections + *((int *)data);
1014     cnp_debug("Uri converter\n");
1015     if (data_ret) *data_ret = strdup(sel->selbuf);
1016     if (size_ret) *size_ret = strlen(sel->selbuf);
1017     return EINA_TRUE;
1018 }
1019
1020 /*
1021  * Image paste provide
1022  */
1023
1024 /* FIXME: Should add provider for each pasted item: Use data to store it
1025  * much easier */
1026 static Evas_Object *
1027 image_provider(void *images __UNUSED__, Evas_Object *entry, const char *item)
1028 {
1029    Paste_Image *pi;
1030    Eina_List *l;
1031
1032    cnp_debug("image provider for %s called\n", item);
1033    EINA_LIST_FOREACH(pastedimages, l, pi)
1034      {
1035         cnp_debug("is it %s?\n",pi->tag);
1036         if (!strcmp(pi->tag, item))
1037           {
1038              /* Found it */
1039              Evas_Object *o;
1040              o = evas_object_image_filled_add(evas_object_evas_get(entry));
1041              /* FIXME: Handle eets */
1042              cnp_debug("file is %s (object is %p)\n", pi->file, o);
1043              evas_object_image_file_set(o, pi->file, NULL);
1044              evas_object_show(o);
1045              return o;
1046           }
1047      }
1048    return NULL;
1049 }
1050
1051
1052 static Paste_Image *
1053 pasteimage_alloc(const char *file, int pathlen)
1054 {
1055    Paste_Image *pi;
1056    int len;
1057    char *buf, *filebuf;
1058    int prefixlen = strlen("file://");
1059
1060    pi = calloc(1, sizeof(Paste_Image));
1061    if (!pi) return NULL;
1062    
1063    len = snprintf(NULL, 0, "pasteimage-%p", pi);
1064    len++;
1065    buf = malloc(len);
1066    if (!buf)
1067      {
1068         free(pi);
1069         return NULL;
1070      }
1071    snprintf(buf, len, "pasteimage-%p", pi);
1072    pi->tag = buf;
1073    
1074    if (file)
1075      {
1076         if (strstr(file,"file://")) file += prefixlen;
1077         filebuf = alloca(pathlen + 1);
1078         strncpy(filebuf, file, pathlen);
1079         filebuf[pathlen] = 0;
1080         pi->file = strdup(filebuf);
1081      }
1082
1083    return pi;
1084 }
1085
1086 static void
1087 pasteimage_free(Paste_Image *pi)
1088 {
1089    if (!pi) return;
1090    if (pi->file) free((void*)pi->file);
1091    if (pi->tag) free((void*)pi->tag);
1092    free(pi);
1093 }
1094
1095 static Eina_Bool
1096 pasteimage_provider_set(Evas_Object *entry)
1097 {
1098    void *v;
1099    const char *type;
1100    
1101    if (!entry) return EINA_FALSE;
1102    type = elm_widget_type_get(entry);
1103    cnp_debug("type is %s\n", type);
1104    if ((!type) || (strcmp(type, "entry"))) return EINA_FALSE;
1105    
1106    v = evas_object_data_get(entry, PROVIDER_SET);
1107    if (!v)
1108      {
1109         evas_object_data_set(entry, PROVIDER_SET, pasteimage_provider_set);
1110         elm_entry_item_provider_append(entry, image_provider, NULL);
1111         evas_object_event_callback_add(entry, EVAS_CALLBACK_FREE,
1112                                        entry_deleted, NULL);
1113      }
1114    return EINA_TRUE;
1115 }
1116
1117
1118 static Eina_Bool
1119 pasteimage_append(Paste_Image *pi, Evas_Object *entry)
1120 {
1121    char *entrytag;
1122    int len;
1123    static const char *tagstring = "<item absize=240x180 href=file://%s></item>";
1124
1125    if (!pi) return EINA_FALSE;
1126    if (!entry) return EINA_FALSE;
1127
1128    pasteimage_provider_set(entry);
1129
1130    len = strlen(tagstring)+strlen(pi->file);
1131
1132    pastedimages = eina_list_append(pastedimages, pi);
1133    entrytag = alloca(len + 1);
1134    snprintf(entrytag, len + 1, tagstring, pi->file);
1135    elm_entry_entry_insert(entry, entrytag);
1136
1137    return EINA_TRUE;
1138 }
1139
1140 static void
1141 entry_deleted(void *images __UNUSED__, Evas *e __UNUSED__, Evas_Object *entry, void *unused __UNUSED__)
1142 {
1143    Paste_Image *pi;
1144    Eina_List *l,*next;
1145
1146    EINA_LIST_FOREACH_SAFE(pastedimages, l, next, pi)
1147      {
1148         if (pi->entry == entry)
1149            pastedimages = eina_list_remove_list(pastedimages, l);
1150      }
1151 }
1152
1153
1154 static char *
1155 remove_tags(const char *p, int *len)
1156 {
1157    char *q,*ret;
1158    int i;
1159    if (!p) return NULL;
1160    
1161    q = malloc(strlen(p) + 1);
1162    if (!q) return NULL;
1163    ret = q;
1164
1165    while (*p)
1166      {
1167         if ((*p != '<') && (*p != '&')) *q++ = *p++;
1168         else if (*p == '<')
1169           {
1170              if ((p[1] == 'b') && (p[2] == 'r') &&
1171                  ((p[3] == ' ') || (p[3] == '/') || (p[3] == '>')))
1172                 *q++ = '\n';
1173              while ((*p) && (*p != '>')) p++;
1174              p++;
1175           } 
1176         else if (*p == '&')
1177           {
1178              p++;
1179              for (i = 0 ; i < N_ESCAPES ; i++)
1180                {
1181                   if (!strncmp(p,escapes[i].escape, strlen(escapes[i].escape)))
1182                     {
1183                        p += strlen(escapes[i].escape);
1184                        *q = escapes[i].value;
1185                        q++;
1186                        break;
1187                     }
1188                }
1189              if (i == N_ESCAPES) *q ++= '&';
1190           }
1191      }
1192    *q = 0;
1193    if (len) *len = q - ret;
1194    return ret;
1195 }
1196
1197 /* Mark up */
1198 static char *
1199 mark_up(const char *start, int inlen, int *lenp)
1200 {
1201    int l, i;
1202    const char *p;
1203    char *q, *ret;
1204    const char *endp = NULL;
1205    
1206    if (!start) return NULL;
1207    if (inlen >= 0) endp = start + inlen;
1208    /* First pass: Count characters */
1209    for (l = 0, p = start; ((!endp) || (p < endp)) && (*p); p++)
1210      {
1211         for (i = 0 ; i < N_ESCAPES ; i ++)
1212           {
1213              if (*p == escapes[i].value)
1214                {
1215                   l += strlen(escapes[i].escape);
1216                   break;
1217                }
1218           }
1219         if (i == N_ESCAPES) l++;
1220      }
1221    
1222    q = ret = malloc(l + 1);
1223    
1224    /* Second pass: Change characters */
1225   for (p = start; *p; )
1226     {
1227        for (i = 0; i < N_ESCAPES; i++)
1228          {
1229             if (*p == escapes[i].value)
1230               {
1231                  strcpy(q, escapes[i].escape);
1232                  q += strlen(escapes[i].escape);
1233                  p ++;
1234                  break;
1235               }
1236          }
1237        if (i == N_ESCAPES) *q++ = *p++;
1238     }
1239    *q = 0;
1240    
1241    if (lenp) *lenp = l;
1242    return ret;
1243 }
1244
1245
1246 static Eina_Bool
1247 _dnd_enter(void *data __UNUSED__, int etype __UNUSED__, void *ev)
1248 {
1249    Ecore_X_Event_Xdnd_Enter *enter = ev;
1250    int i;
1251
1252    /* Skip it */
1253    if ((!enter) || (!enter->num_types) || (!enter->types)) return EINA_TRUE;
1254
1255    cnp_debug("Types\n");
1256    savedtypes.ntypes = enter->num_types;
1257    if (savedtypes.types) free(savedtypes.types);
1258    savedtypes.types = malloc(sizeof(char *) * enter->num_types);
1259    if (!savedtypes.types) return EINA_FALSE;
1260    
1261    for (i = 0; i < enter->num_types; i++)
1262      {
1263         savedtypes.types[i] = eina_stringshare_add(enter->types[i]);
1264         cnp_debug("Type is %s %p %p\n", enter->types[i],
1265                   savedtypes.types[i],text_uri);
1266         if (savedtypes.types[i] == text_uri)
1267           {
1268              /* Request it, so we know what it is */
1269              cnp_debug("Sending uri request\n");
1270              savedtypes.textreq = 1;
1271              savedtypes.pi = NULL; /* FIXME: Free? */
1272              ecore_x_selection_xdnd_request(enter->win, text_uri);
1273           }
1274      }
1275
1276    /* FIXME: Find an object and make it current */
1277    return EINA_TRUE;
1278 }
1279
1280 static Eina_Bool
1281 _dnd_drop(void *data __UNUSED__, int etype __UNUSED__, void *ev)
1282 {
1283    struct _Ecore_X_Event_Xdnd_Drop *drop;
1284    Dropable *dropable;
1285    Eina_List *l;
1286    Ecore_Evas *ee;
1287    Ecore_X_Window xwin;
1288    Elm_Selection_Data ddata;
1289    int x, y, w, h;
1290    int i, j;
1291
1292    drop = ev;
1293
1294    // check we still have something to drop
1295    if (!drops) return EINA_TRUE;
1296
1297    /* Find any widget in our window; then work out geometry rel to our window */
1298    for (l = drops; l; l = l->next)
1299      {
1300         dropable = l->data;
1301         xwin = (Ecore_X_Window)ecore_evas_window_get
1302            (ecore_evas_ecore_evas_get(evas_object_evas_get
1303                                       (dropable->obj)));
1304         if (xwin == drop->win) break;
1305      }
1306    /* didn't find a window */
1307    if (!l) return EINA_TRUE;
1308
1309    /* Calculate real (widget relative) position */
1310    // - window position
1311    // - widget position
1312    ee = ecore_evas_ecore_evas_get(evas_object_evas_get(dropable->obj));
1313    ecore_evas_geometry_get(ee, &x, &y, NULL, NULL);
1314    savedtypes.x = drop->position.x - x;
1315    savedtypes.y = drop->position.y - y;
1316    
1317    cnp_debug("Drop position is %d,%d\n", savedtypes.x, savedtypes.y);
1318
1319    for (; l; l = l->next)
1320      {
1321         dropable = l->data;
1322         evas_object_geometry_get(dropable->obj, &x, &y, &w, &h);
1323         if ((savedtypes.x >= x) && (savedtypes.y >= y) &&
1324             (savedtypes.x < x + w) && (savedtypes.y < y + h))
1325            break; /* found! */
1326      }
1327    
1328    if (!l) return EINA_TRUE; /* didn't find one */
1329    
1330    evas_object_geometry_get(dropable->obj, &x, &y, NULL, NULL);
1331    savedtypes.x -= x;
1332    savedtypes.y -= y;
1333    
1334    /* Find our type from the previous list */
1335    for (i = 0; i < CNP_N_ATOMS; i++)
1336      {
1337         for (j = 0; j < savedtypes.ntypes; j++)
1338           {
1339              if (!strcmp(savedtypes.types[j], atoms[i].name)) goto found;
1340           }
1341      }
1342    
1343    cnp_debug("Didn't find a target\n");
1344    return EINA_TRUE;
1345    
1346 found:
1347    cnp_debug("Found a target we'd like: %s\n", atoms[i].name);
1348    cnp_debug("0x%x\n",xwin);
1349    
1350    if (i == CNP_ATOM_text_urilist)
1351      {
1352         cnp_debug("We found a URI... (%scached) %s\n",
1353                   savedtypes.pi ? "" : "not ",
1354                   savedtypes.pi->file);
1355         if (savedtypes.pi)
1356           {
1357              char *entrytag;
1358              static const char *tagstring = "<item absize=240x180 href="
1359                                                    "file://%s></item>";
1360              ddata.x = savedtypes.x;
1361              ddata.y = savedtypes.y;
1362
1363              /* If it's markup that also supports images */
1364              if ((dropable->types & ELM_SEL_FORMAT_MARKUP) &&
1365                     (dropable->types & ELM_SEL_FORMAT_IMAGE))
1366                {
1367                   int len;
1368                   ddata.format = ELM_SEL_FORMAT_MARKUP;
1369                   pasteimage_provider_set(dropable->obj);
1370
1371                   pastedimages = eina_list_append(pastedimages, savedtypes.pi);
1372                   len = strlen(tagstring) + strlen(savedtypes.pi->file);
1373                   entrytag = alloca(len + 1);
1374                   snprintf(entrytag, len + 1, tagstring, savedtypes.pi->file);
1375                   ddata.data = entrytag;
1376                   cnp_debug("Insert %s\n", (char *)ddata.data);
1377                   dropable->dropcb(dropable->cbdata, dropable->obj, &ddata);
1378                   ecore_x_dnd_send_finished();
1379                   return EINA_TRUE;
1380                }
1381              else if (dropable->types & ELM_SEL_FORMAT_IMAGE)
1382                {
1383                   cnp_debug("Doing image insert (%s)\n", savedtypes.pi->file);
1384                   ddata.format = ELM_SEL_FORMAT_IMAGE;
1385                   ddata.data = (char *)savedtypes.pi->file;
1386                   dropable->dropcb(dropable->cbdata, dropable->obj, &ddata);
1387                   ecore_x_dnd_send_finished();
1388
1389                   pasteimage_free(savedtypes.pi);
1390                   savedtypes.pi = NULL;
1391
1392                   return EINA_TRUE;
1393                }
1394              else
1395                {
1396                   cnp_debug("Item doesn't support images... passing\n");
1397                   pasteimage_free(savedtypes.pi);
1398                   return EINA_TRUE;
1399                }
1400           }
1401         else if (savedtypes.textreq)
1402           {
1403              /* Already asked: Pretend we asked now, and paste immediately when
1404               * it comes in */
1405              savedtypes.textreq = 0;
1406              ecore_x_dnd_send_finished();
1407              return EINA_TRUE;
1408           }
1409      }
1410
1411    cnp_debug("doing a request then\n");
1412    selections[ELM_SEL_XDND].requestwidget = dropable->obj;
1413    selections[ELM_SEL_XDND].requestformat = ELM_SEL_FORMAT_MARKUP;
1414    selections[ELM_SEL_XDND].active = EINA_TRUE;
1415
1416    ecore_x_selection_xdnd_request(xwin, atoms[i].name);
1417
1418    return EINA_TRUE;
1419 }
1420 static Eina_Bool
1421 _dnd_position(void *data __UNUSED__, int etype __UNUSED__, void *ev)
1422 {
1423    struct _Ecore_X_Event_Xdnd_Position *pos;
1424    Ecore_X_Rectangle rect;
1425
1426    pos = ev;
1427
1428    /* Need to send a status back */
1429    /* FIXME: Should check I can drop here */
1430    /* FIXME: Should highlight widget */
1431    rect.x = pos->position.x - 5;
1432    rect.y = pos->position.y - 5;
1433    rect.width = 10;
1434    rect.height = 10;
1435    ecore_x_dnd_send_status(EINA_TRUE, EINA_FALSE, rect, pos->action);
1436
1437    return EINA_TRUE;
1438 }
1439
1440 /**
1441  * When dragging this is callback response from the destination.
1442  * The important thing we care about: Can we drop; thus update cursor
1443  * appropriately.
1444  */
1445 static Eina_Bool
1446 _dnd_status(void *data __UNUSED__, int etype __UNUSED__, void *ev)
1447 {
1448    struct _Ecore_X_Event_Xdnd_Status *status = ev;
1449
1450    if (!status) return EINA_TRUE;
1451    
1452    /* Only thing we care about: will accept */
1453    if (status->will_accept)
1454      {
1455         cnp_debug("Will accept\n");
1456      }
1457    else
1458      { /* Won't accept */
1459         cnp_debug("Won't accept accept\n");
1460      }
1461    return EINA_TRUE;
1462 }
1463
1464 /**
1465  * Add a widget as drop target.
1466  */
1467 Eina_Bool
1468 elm_drop_target_add(Evas_Object *obj, Elm_Sel_Type format, Elm_Drop_Cb dropcb, void *cbdata)
1469 {
1470    Dropable *drop;
1471    Ecore_X_Window xwin;
1472    Eina_List *item;
1473    int first;
1474
1475    if (!obj) return EINA_FALSE;
1476    if (!_elm_cnp_init_count) _elm_cnp_init();
1477
1478    /* Is this the first? */
1479    first = (!drops) ? 1 : 0;
1480
1481    EINA_LIST_FOREACH(drops, item, drop)
1482      {
1483         if (drop->obj == obj)
1484           {
1485              /* Update: Not a new one */
1486              drop->dropcb = dropcb;
1487              drop->cbdata = cbdata;
1488              drop->types = format;
1489              return EINA_TRUE;
1490           }
1491      }
1492
1493    /* Create new drop */
1494    drop = calloc(1, sizeof(Dropable));
1495    if (!drop) return EINA_FALSE;
1496    /* FIXME: Check for eina's deranged error method */
1497    drops = eina_list_append(drops, drop);
1498
1499    if (!drops/* || or other error */)
1500      {
1501         free(drop);
1502         return EINA_FALSE;
1503      }
1504    drop->dropcb = dropcb;
1505    drop->cbdata = cbdata;
1506    drop->types = format;
1507    drop->obj = obj;
1508
1509    evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL,
1510                                   /* I love C and varargs */
1511                                   (Evas_Object_Event_Cb)elm_drop_target_del,
1512                                   obj);
1513    /* FIXME: Handle resizes */
1514
1515    /* If not the first: We're done */
1516    if (!first) return EINA_TRUE;
1517
1518    xwin = (Ecore_X_Window)ecore_evas_window_get
1519       (ecore_evas_ecore_evas_get(evas_object_evas_get(obj)));
1520    
1521    ecore_x_dnd_aware_set(xwin, EINA_TRUE);
1522    
1523    cnp_debug("Adding drop target calls\n");
1524    handler_enter = ecore_event_handler_add(ECORE_X_EVENT_XDND_ENTER,
1525                                            _dnd_enter, NULL);
1526    handler_pos = ecore_event_handler_add(ECORE_X_EVENT_XDND_POSITION,
1527                                          _dnd_position, NULL);
1528    handler_drop = ecore_event_handler_add(ECORE_X_EVENT_XDND_DROP,
1529                                           _dnd_drop, NULL);
1530    
1531    return EINA_TRUE;
1532 }
1533
1534 Eina_Bool
1535 elm_drop_target_del(Evas_Object *obj)
1536 {
1537    Dropable *drop,*del;
1538    Eina_List *item;
1539    Ecore_X_Window xwin;
1540    
1541    del = NULL;
1542    EINA_LIST_FOREACH(drops, item, drop)
1543      {
1544         if (drop->obj == obj)
1545           {
1546              drops = eina_list_remove_list(drops, item);
1547              del = drop;
1548              break;
1549           }
1550      }
1551    if (!del) return EINA_FALSE;
1552    
1553    evas_object_event_callback_del(obj, EVAS_CALLBACK_FREE,
1554                                   (Evas_Object_Event_Cb)elm_drop_target_del);
1555    free(drop);
1556    /* If still drops there: All fine.. continue */
1557    if (drops) return EINA_TRUE;
1558    
1559    cnp_debug("Disabling DND\n");
1560    xwin = (Ecore_X_Window)ecore_evas_window_get
1561       (ecore_evas_ecore_evas_get(evas_object_evas_get(obj)));
1562    ecore_x_dnd_aware_set(xwin, EINA_FALSE);
1563    
1564    ecore_event_handler_del(handler_pos);
1565    ecore_event_handler_del(handler_drop);
1566    ecore_event_handler_del(handler_enter);
1567    
1568    if (savedtypes.pi)
1569      {
1570         pasteimage_free(savedtypes.pi);
1571         savedtypes.pi = NULL;
1572      }
1573    
1574    return EINA_TRUE;
1575 }
1576
1577
1578 static void
1579 _drag_mouse_up(void *un __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *data __UNUSED__)
1580 {
1581    evas_object_event_callback_del(obj, EVAS_CALLBACK_MOUSE_UP, _drag_mouse_up);
1582    ecore_x_dnd_drop();
1583    if (dragdonecb)
1584      {
1585         dragdonecb(dragdonecb,selections[ELM_SEL_XDND].widget);
1586         dragdonecb = NULL;
1587      }
1588    if (dragwin)
1589      {
1590         evas_object_del(dragwin);
1591         dragwin = NULL;
1592      }
1593 }
1594
1595 static void
1596 _drag_move(void *data __UNUSED__, Ecore_X_Xdnd_Position *pos)
1597 {
1598    evas_object_move(dragwin, 
1599                     pos->position.x - _dragx, 
1600                     pos->position.y - _dragy);
1601 }
1602
1603
1604 Eina_Bool
1605 elm_drag_start(Evas_Object *obj, Elm_Sel_Format format, const char *data, void (*dragdone) (void *data, Evas_Object *), void *donecbdata)
1606 {
1607    Ecore_X_Window xwin;
1608    Cnp_Selection *sel;
1609    Elm_Sel_Type xdnd = ELM_SEL_XDND;
1610    Ecore_Evas *ee;
1611    int x, y, x2, y2, x3, y3;
1612    Evas_Object *icon;
1613    int w, h;
1614    
1615    if (!_elm_cnp_init_count) _elm_cnp_init();
1616    
1617    xwin = elm_win_xwindow_get(obj);
1618
1619    cnp_debug("starting drag...\n");
1620
1621    ecore_x_dnd_type_set(xwin, "text/uri-list", 1);
1622    sel = selections + ELM_SEL_XDND;
1623    sel->active = 1;
1624    sel->widget = obj;
1625    sel->format = format;
1626    sel->selbuf = data ? strdup(data) : NULL;
1627    dragdonecb = dragdone;
1628    dragdonedata = donecbdata;
1629    
1630    ecore_x_dnd_callback_pos_update_set(_drag_move, NULL);
1631    ecore_x_dnd_begin(xwin, (unsigned char *)&xdnd, sizeof(Elm_Sel_Type));
1632    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_UP,
1633                                   _drag_mouse_up, NULL);
1634    
1635    handler_status = ecore_event_handler_add(ECORE_X_EVENT_XDND_STATUS,
1636                                             _dnd_status, NULL);
1637    
1638    dragwin = elm_win_add(NULL, "Elm Drag Object", ELM_WIN_UTILITY);
1639    elm_win_override_set(dragwin, 1);
1640    
1641    /* FIXME: Images only */
1642    icon = elm_icon_add(dragwin);
1643    elm_icon_file_set(icon, data + 7, NULL); /* 7!? "file://" */
1644    elm_win_resize_object_add(dragwin,icon);
1645    evas_object_size_hint_weight_set(icon, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1646    evas_object_size_hint_align_set(icon, EVAS_HINT_FILL, EVAS_HINT_FILL);
1647
1648    /* Position subwindow appropriately */
1649    ee = ecore_evas_ecore_evas_get(evas_object_evas_get(obj));
1650    ecore_evas_geometry_get(ee, &x, &y, NULL, NULL);
1651    evas_object_geometry_get(obj, &x2, &y2, &w, &h);
1652    x += x2;
1653    y += y2;
1654    evas_object_move(dragwin, x, y);
1655    evas_object_resize(icon, w, h);
1656    evas_object_resize(dragwin, w, h);
1657    
1658    evas_object_show(icon);
1659    evas_object_show(dragwin);
1660
1661    evas_pointer_canvas_xy_get(evas_object_evas_get(obj), &x3, &y3);
1662    _dragx = x3 - x2;
1663    _dragy = y3 - y2;
1664
1665    return EINA_TRUE;
1666 }
1667
1668 static Tmp_Info *
1669 elm_cnp_tempfile_create(int size)
1670 {
1671    Tmp_Info *info;
1672    const char *tmppath;
1673    int len;
1674    
1675    info = malloc(sizeof(Tmp_Info));
1676    if (!info) return NULL;
1677    
1678    tmppath = getenv("TMP");
1679    if (!tmppath) tmppath = P_tmpdir;
1680    if (!tmppath) tmppath = "/tmp";
1681    len = snprintf(NULL, 0, "%s/%sXXXXXX", tmppath, "elmcnpitem-");
1682    if (len < 0)
1683      {
1684         free(info);
1685         return NULL;
1686      }
1687    len++;
1688    info->filename = malloc(len);
1689    if (!info->filename)
1690      {
1691         free(info);
1692         return NULL;
1693      }
1694    snprintf(info->filename,len,"%s/%sXXXXXX", tmppath, "elmcnpitem-");
1695    
1696    info->fd = mkstemp(info->filename);
1697    
1698 # ifdef __linux__
1699      {
1700         char *tmp;
1701         /* And before someone says anything see POSIX 1003.1-2008 page 400 */
1702         long pid;
1703         
1704         pid = (long)getpid();
1705         /* Use pid instead of /proc/self: That way if can be passed around */
1706         len = snprintf(NULL,0,"/proc/%li/fd/%i", pid, info->fd);
1707         len++;
1708         tmp = malloc(len);
1709         if (tmp)
1710           {
1711              snprintf(tmp,len, "/proc/%li/fd/%i", pid, info->fd);
1712              unlink(info->filename);
1713              free(info->filename);
1714              info->filename = tmp;
1715           }
1716      }
1717 # endif
1718    
1719    cnp_debug("filename is %s\n", info->filename);
1720    if (size < 1)
1721      {
1722         /* Set map to NULL and return */
1723         info->map = NULL;
1724         info->len = 0;
1725         return info;
1726      }
1727    
1728    /* Map it in */
1729    if (ftruncate(info->fd, size))
1730      {
1731         perror("ftruncate");
1732         info->map = NULL;
1733         info->len = 0;
1734         return info;
1735      }
1736    
1737    info->map = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, info->fd, 0);
1738    if (info->map == MAP_FAILED)
1739      {
1740         perror("mmap");
1741         info->map = NULL;
1742         info->len = 0;
1743      }
1744    
1745    return info;
1746 }
1747
1748
1749 static int
1750 tmpinfo_free(Tmp_Info *info)
1751 {
1752    if (!info) return 0;
1753    free(info->filename);
1754    free(info);
1755    return 0;
1756 }
1757
1758 #else
1759 /* Stubs for windows */
1760 Eina_Bool
1761 elm_drag_start(Evas_Object *o, Elm_Sel_Format f, const char *d, void (*donecb)(void *, Evas_Object *),void *cbdata)
1762 {
1763    return EINA_FALSE;
1764 }
1765
1766 Eina_Bool
1767 elm_drop_target_add(Evas_Object *obj, Elm_Sel_Type format, Elm_Drop_Cb dropcb, void *cbdata)
1768 {
1769    return EINA_FALSE;
1770 }
1771
1772 Eina_Bool
1773 elm_drop_target_del(Evas_Object *o)
1774 {
1775    return EINA_TRUE;
1776 }
1777 #endif
1778
1779 /* vim:set ts=8 sw=3 sts=3 expandtab cino=>5n-2f0^-2{2(0W1st0 :*/