1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 // FIX: Restore onThreadException
4 
5 module dfl.data;
6 
7 import core.sys.windows.com;
8 import core.sys.windows.objidl; // for CLIPFORMAT
9 import core.sys.windows.shlobj;
10 import core.sys.windows.windows;
11 import core.sys.windows.wtypes; // for DVASPECT
12 
13 import dfl.base;
14 import dfl.exception;
15 import dfl.internal.com;
16 import dfl.internal.dlib;
17 import dfl.internal.utf;
18 
19 class DataFormats {
20    static class Format {
21       /// Data format ID number.
22       package int _id;
23       final @property int id() {
24          return _id;
25       }
26 
27       /// Data format name.
28       package Dstring _name;
29       final @property Dstring name() {
30          return _name;
31       }
32 
33       package this() { }
34    }
35 
36 
37 static:
38 
39    /// Predefined data formats.
40    @property Dstring bitmap() {
41       return getFormat(CF_BITMAP).name;
42    }
43 
44    /*
45 
46       @property Dstring commaSeparatedValue() {
47       return getFormat(?).name;
48       }
49     */
50 
51    @property Dstring dib() {
52       return getFormat(CF_DIB).name;
53    }
54 
55    @property Dstring dif() {
56       return getFormat(CF_DIF).name;
57    }
58 
59    @property Dstring enhandedMetaFile() {
60       return getFormat(CF_ENHMETAFILE).name;
61    }
62 
63    @property Dstring fileDrop() {
64       return getFormat(CF_HDROP).name;
65    }
66 
67    @property Dstring html() {
68       return getFormat("HTML Format").name;
69    }
70 
71    @property Dstring locale() {
72       return getFormat(CF_LOCALE).name;
73    }
74 
75    @property Dstring metafilePict() {
76       return getFormat(CF_METAFILEPICT).name;
77    }
78 
79    @property Dstring oemText() {
80       return getFormat(CF_OEMTEXT).name;
81    }
82 
83    @property Dstring palette() {
84       return getFormat(CF_PALETTE).name;
85    }
86 
87    @property Dstring penData() {
88       return getFormat(CF_PENDATA).name;
89    }
90 
91    @property Dstring riff() {
92       return getFormat(CF_RIFF).name;
93    }
94 
95    @property Dstring rtf() {
96       return getFormat("Rich Text Format").name;
97    }
98 
99    /*
100 
101       @property Dstring serializable() {
102       return getFormat(?).name;
103       }
104     */
105 
106    @property Dstring stringFormat() {
107       return utf8; // ?
108    }
109 
110    @property Dstring utf8() {
111       return getFormat("UTF-8").name;
112    }
113 
114    @property Dstring symbolicLink() {
115       return getFormat(CF_SYLK).name;
116    }
117 
118    @property Dstring text() {
119       return getFormat(CF_TEXT).name;
120    }
121 
122    @property Dstring tiff() {
123       return getFormat(CF_TIFF).name;
124    }
125 
126    @property Dstring unicodeText() {
127       return getFormat(CF_UNICODETEXT).name;
128    }
129 
130    @property Dstring waveAudio() {
131       return getFormat(CF_WAVE).name;
132    }
133 
134    // Assumes _init() was already called and
135    // -id- is not in -fmts-.
136    private Format _didntFindId(int id) {
137       Format result;
138       result = new Format;
139       result._id = id;
140       result._name = getName(id);
141       //synchronized // _init() would need to be synchronized with it.
142       {
143          fmts[id] = result;
144       }
145       return result;
146    }
147 
148 
149    Format getFormat(int id) {
150       _init();
151 
152       if(id in fmts) {
153          return fmts[id];
154       }
155 
156       return _didntFindId(id);
157    }
158 
159    // Creates the format name if it doesn't exist.
160    Format getFormat(Dstring name) {
161       _init();
162       foreach(Format onfmt; fmts) {
163          if(!stringICmp(name, onfmt.name)) {
164             return onfmt;
165          }
166       }
167       // Didn't find it.
168       return _didntFindId(dfl.internal.utf.registerClipboardFormat(name));
169    }
170 
171    // Extra.
172    Format getFormat(TypeInfo type) {
173       return getFormatFromType(type);
174    }
175 
176 
177    private:
178    Format[int] fmts; // Indexed by identifier. Must _init() before accessing!
179 
180 
181    void _init() {
182       if(fmts.length) {
183          return;
184       }
185 
186 
187       void initfmt(int id, Dstring name)
188          in {
189             assert(!(id in fmts));
190          }
191       body {
192          Format fmt;
193          fmt = new Format;
194          fmt._id = id;
195          fmt._name = name;
196          fmts[id] = fmt;
197       }
198 
199       initfmt(CF_BITMAP, "Bitmap");
200       initfmt(CF_DIB, "DeviceIndependentBitmap");
201       initfmt(CF_DIF, "DataInterchangeFormat");
202       initfmt(CF_ENHMETAFILE, "EnhancedMetafile");
203       initfmt(CF_HDROP, "FileDrop");
204       initfmt(CF_LOCALE, "Locale");
205       initfmt(CF_METAFILEPICT, "MetaFilePict");
206       initfmt(CF_OEMTEXT, "OEMText");
207       initfmt(CF_PALETTE, "Palette");
208       initfmt(CF_PENDATA, "PenData");
209       initfmt(CF_RIFF, "RiffAudio");
210       initfmt(CF_SYLK, "SymbolicLink");
211       initfmt(CF_TEXT, "Text");
212       initfmt(CF_TIFF, "TaggedImageFileFormat");
213       initfmt(CF_UNICODETEXT, "UnicodeText");
214       initfmt(CF_WAVE, "WaveAudio");
215 
216       fmts.rehash;
217    }
218 
219 
220    // Does not get the name of one of the predefined constant ones.
221    Dstring getName(int id) {
222       Dstring result;
223       result = dfl.internal.utf.getClipboardFormatName(id);
224       if(!result.length) {
225          throw new DflException("Unable to get format");
226       }
227       return result;
228    }
229 
230    package Format getFormatFromType(TypeInfo type) {
231       if(type == typeid(ubyte[])) {
232          return getFormat(text);
233       }
234       if(type == typeid(Dstring)) {
235          return getFormat(stringFormat);
236       }
237       if(type == typeid(Dwstring)) {
238          return getFormat(unicodeText);
239       }
240       //if(type == typeid(Bitmap))
241       // return getFormat(bitmap);
242 
243       if(cast(TypeInfo_Class)type) {
244          throw new DflException("Unknown data format");
245       }
246 
247       return getFormat(getObjectString(type)); // ?
248    }
249 
250    private Dstring[] getHDropStrings(void[] value) {
251       /+
252          if(value.length != HDROP.sizeof) {
253             return null;
254          }
255 
256       HDROP hd;
257       UINT num;
258       Dstring[] result;
259       size_t iw;
260 
261       hd = *cast(HDROP*)value.ptr;
262       num = dragQueryFile(hd);
263       if(!num) {
264          return null;
265       }
266       result = new Dstring[num];
267       for(iw = 0; iw != num; iw++) {
268          result[iw] = dragQueryFile(hd, iw);
269       }
270       return result;
271       +/
272 
273          if(value.length <= DROPFILES.sizeof) {
274             return null;
275          }
276 
277       Dstring[] result;
278       DROPFILES* df;
279       size_t iw, startiw;
280 
281       df = cast(DROPFILES*)value.ptr;
282       if(df.pFiles < DROPFILES.sizeof || df.pFiles >= value.length) {
283          return null;
284       }
285 
286       if(df.fWide) { // Unicode.
287          Dwstring uni = cast(Dwstring)((value.ptr + df.pFiles)[0 .. value.length]);
288          for(iw = startiw = 0;; iw++) {
289             if(!uni[iw]) {
290                if(startiw == iw) {
291                   break;
292                }
293                result ~= fromUnicode(uni.ptr + startiw, iw - startiw);
294                assert(result[result.length - 1].length);
295                startiw = iw + 1;
296             }
297          }
298       } else { // ANSI.
299          Dstring ansi = cast(Dstring)((value.ptr + df.pFiles)[0 .. value.length]);
300          for(iw = startiw = 0;; iw++) {
301             if(!ansi[iw]) {
302                if(startiw == iw) {
303                   break;
304                }
305                result ~= fromAnsi(ansi.ptr + startiw, iw - startiw);
306                assert(result[result.length - 1].length);
307                startiw = iw + 1;
308             }
309          }
310       }
311 
312       return result;
313    }
314 
315    // Convert clipboard -value- to Data.
316    Data getDataFromFormat(int id, void[] value) {
317       switch(id) {
318          case CF_TEXT:
319             return Data(stopAtNull!(ubyte)(cast(ubyte[])value));
320 
321          case CF_UNICODETEXT:
322             return Data(stopAtNull!(Dwchar)(cast(Dwstring)value));
323 
324          case CF_HDROP:
325             return Data(getHDropStrings(value));
326 
327          default:
328             if(id == getFormat(stringFormat).id) {
329                return Data(stopAtNull!(Dchar)(cast(Dstring)value));
330             }
331       }
332 
333       //throw new DflException("Unknown data format");
334       return Data(value); // ?
335    }
336 
337    void[] getCbFileDrop(Dstring[] fileNames) {
338       size_t sz = DROPFILES.sizeof;
339       void* p;
340       DROPFILES* df;
341 
342       foreach(fn; fileNames) {
343          sz += (dfl.internal.utf.toUnicodeLength(fn) + 1) << 1;
344       }
345       sz += 2;
346 
347       p = (new byte[sz]).ptr;
348       df = cast(DROPFILES*)p;
349 
350       df.pFiles = DROPFILES.sizeof;
351       df.fWide = TRUE;
352 
353       wchar* ws = cast(wchar*)(p + DROPFILES.sizeof);
354       foreach(fn; fileNames) {
355          foreach(wchar wch; fn) {
356             *ws++ = wch;
357          }
358          *ws++ = 0;
359       }
360       *ws++ = 0;
361 
362       return p[0 .. sz];
363    }
364 
365 
366    // Value the clipboard wants.
367    void[] getClipboardValueFromData(int id, Data data) {
368       //if(data.info == typeid(ubyte[]))
369       if(CF_TEXT == id) {
370          // ANSI text.
371          enum ubyte[] UBYTE_ZERO = [0];
372          return data.getText() ~ UBYTE_ZERO;
373       }
374       //else if(data.info == typeid(Dstring))
375       //else if(getFormat(stringFormat).id == id)
376       else if((getFormat(stringFormat).id == id) || (data.info == typeid(Dstring))) {
377          // UTF-8 string.
378          Dstring str;
379          str = data.getString();
380          //return toStringz(str)[0 .. str.length + 1];
381          //return unsafeStringz(str)[0 .. str.length + 1]; // ?
382          return cast(void[])unsafeStringz(str)[0 .. str.length + 1]; // ? Needed in D2.
383       }
384       //else if(data.info == typeid(Dwstring))
385       //else if(CF_UNICODETEXT == id)
386       else if((CF_UNICODETEXT == id) || (data.info == typeid(Dwstring))) {
387          // Unicode string.
388          //return data.getUnicodeText() ~ cast(Dwstring)"\0";
389          //return cast(void[])(data.getUnicodeText() ~ cast(Dwstring)"\0"); // Needed in D2. Not guaranteed safe.
390          return (data.getUnicodeText() ~ cast(Dwstring)"\0").dup; // Needed in D2.
391       } else if(data.info == typeid(Ddstring)) {
392          //return (*cast(Ddstring*)data.value) ~ "\0";
393          //return cast(void[])((*cast(Ddstring*)data.value) ~ "\0"); // Needed in D2. Not guaranteed safe.
394          return ((*cast(Ddstring*)data.value) ~ "\0").dup; // Needed in D2.
395       } else if(CF_HDROP == id) {
396          return getCbFileDrop(data.getStrings());
397       } else if(data.info == typeid(void[]) || data.info == typeid(Dstring)
398             || data.info == typeid(ubyte[]) || data.info == typeid(byte[])) { // Hack ?
399          return *cast(void[]*)data.value; // Save the array elements, not the reference.
400       } else {
401          return data.value; // ?
402       }
403    }
404 
405 
406    this() { }
407 }
408 
409 
410 private template stopAtNull(T) {
411    T[] stopAtNull(T[] array) {
412       int i;
413       for(i = 0; i != array.length; i++) {
414          if(!array[i]) {
415             return array[0 .. i];
416          }
417       }
418       //return null;
419       throw new DflException("Invalid data"); // ?
420    }
421 }
422 
423 
424 /// Data structure for holding data in a raw format with type information.
425 struct Data {
426    /// Information about the data type.
427    @property TypeInfo info() {
428       return _info;
429    }
430 
431    /// The data's raw value.
432    @property void[] value() {
433       return _value[0 .. _info.tsize()];
434    }
435 
436 
437    /// Construct a new Data structure.
438    static Data opCall(...)
439       in {
440          assert(_arguments.length == 1);
441       }
442    body {
443       Data result;
444       result._info = _arguments[0];
445       result._value = _argptr[0 .. result._info.tsize()].dup.ptr;
446       return result;
447    }
448 
449 
450 
451    T getValue(T)() {
452       assert(_info.tsize == T.sizeof);
453       return *cast(T*)_value;
454    }
455 
456 
457    // UTF-8.
458    Dstring getString() {
459       assert(_info == typeid(Dstring) || _info == typeid(void[]));
460       return *cast(Dstring*)_value;
461    }
462 
463 
464    alias getString getUtf8;
465 
466    deprecated alias getString getUTF8;
467 
468 
469    // ANSI text.
470    ubyte[] getText() {
471       assert(_info == typeid(ubyte[]) || _info == typeid(byte[]) || _info == typeid(void[]));
472       return *cast(ubyte[]*)_value;
473    }
474 
475 
476    Dwstring getUnicodeText() {
477       assert(_info == typeid(Dwstring) || _info == typeid(void[]));
478       return *cast(Dwstring*)_value;
479    }
480 
481 
482    int getInt() {
483       return getValue!(int)();
484    }
485 
486 
487    int getUint() {
488       return getValue!(uint)();
489    }
490 
491 
492    Dstring[] getStrings() {
493       assert(_info == typeid(Dstring[]));
494       return *cast(Dstring[]*)_value;
495    }
496 
497 
498    Object getObject() {
499       assert(!(cast(TypeInfo_Class)_info is null));
500       return cast(Object)*cast(Object**)_value;
501    }
502 
503 
504    private:
505    TypeInfo _info;
506    void* _value;
507 }
508 
509 
510 /// Interface to a data object. The data can have different formats by setting different formats.
511 interface IDataObjectD {
512 
513    Data getData(Dstring fmt);
514 
515    Data getData(TypeInfo type);
516 
517    Data getData(Dstring fmt, bool doConvert);
518 
519 
520    bool getDataPresent(Dstring fmt); // Check.
521 
522    bool getDataPresent(TypeInfo type); // Check.
523 
524    bool getDataPresent(Dstring fmt, bool canConvert); // Check.
525 
526 
527    Dstring[] getFormats();
528    //Dstring[] getFormats(bool onlyNative);
529 
530 
531    void setData(Data obj);
532 
533    void setData(Dstring fmt, Data obj);
534 
535    void setData(TypeInfo type, Data obj);
536 
537    void setData(Dstring fmt, bool canConvert, Data obj);
538 }
539 
540 class DataObject: IDataObjectD {
541    Data getData(Dstring fmt) {
542       return getData(fmt, true);
543    }
544 
545 
546    Data getData(TypeInfo type) {
547       return getData(DataFormats.getFormat(type).name);
548    }
549 
550 
551    Data getData(Dstring fmt, bool doConvert) {
552       // doConvert ...
553 
554       //cprintf("Looking for format '%.*s'.\n", fmt);
555       int i;
556       i = find(fmt);
557       if(i == -1) {
558          throw new DflException("Data format not present");
559       }
560       return all[i].obj;
561    }
562 
563 
564    bool getDataPresent(Dstring fmt) {
565       return getDataPresent(fmt, true);
566    }
567 
568    bool getDataPresent(TypeInfo type) {
569       return getDataPresent(DataFormats.getFormat(type).name);
570    }
571 
572    bool getDataPresent(Dstring fmt, bool canConvert) {
573       // canConvert ...
574       return find(fmt) != -1;
575    }
576 
577    Dstring[] getFormats() {
578       Dstring[] result;
579       result = new Dstring[all.length];
580       foreach(int i, ref Dstring fmt; result) {
581          fmt = all[i].fmt;
582       }
583       return result;
584    }
585 
586    // TO-DO: remove...
587    deprecated final Dstring[] getFormats(bool onlyNative) {
588       return getFormats();
589    }
590 
591    package final void _setData(Dstring fmt, Data obj, bool replace = true) {
592       int i;
593       i = find(fmt, false);
594       if(i != -1) {
595          if(replace) {
596             all[i].obj = obj;
597          }
598       } else {
599          Pair pair;
600          pair.fmt = fmt;
601          pair.obj = obj;
602          all ~= pair;
603       }
604    }
605 
606    void setData(Data obj) {
607       setData(DataFormats.getFormat(obj.info).name, obj);
608    }
609 
610    void setData(Dstring fmt, Data obj) {
611       setData(fmt, true, obj);
612    }
613 
614    void setData(TypeInfo type, Data obj) {
615       setData(DataFormats.getFormatFromType(type).name, true, obj);
616    }
617 
618    void setData(Dstring fmt, bool canConvert, Data obj) {
619       /+
620          if(obj.info == typeid(Data)) {
621             void[] objv;
622             objv = obj.value;
623             assert(objv.length == Data.sizeof);
624             obj = *(cast(Data*)objv.ptr);
625          }
626       +/
627 
628          _setData(fmt, obj);
629       if(canConvert) {
630          Data cdat;
631          cdat = Data(*(cast(_DataConvert*)&obj));
632          _canConvertFormats(fmt,
633                (Dstring cfmt) {
634                _setData(cfmt, cdat, false);
635                });
636       }
637    }
638 
639 
640    private:
641    struct Pair {
642       Dstring fmt;
643       Data obj;
644    }
645 
646 
647    Pair[] all;
648 
649 
650    void fixPairEntry(ref Pair pr) {
651       assert(pr.obj.info == typeid(_DataConvert));
652       Data obj;
653       void[] objv;
654       objv = pr.obj.value;
655       assert(objv.length == Data.sizeof);
656       obj = *(cast(Data*)objv.ptr);
657       pr.obj = _doConvertFormat(obj, pr.fmt);
658    }
659 
660 
661    int find(Dstring fmt, bool fix = true) {
662       int i;
663       for(i = 0; i != all.length; i++) {
664          if(!stringICmp(all[i].fmt, fmt)) {
665             if(fix && all[i].obj.info == typeid(_DataConvert)) {
666                fixPairEntry(all[i]);
667             }
668             return i;
669          }
670       }
671       return -1;
672    }
673 }
674 
675 
676 private struct _DataConvert {
677    Data data;
678 }
679 
680 
681 package void _canConvertFormats(Dstring fmt, void delegate(Dstring cfmt) callback) {
682    //if(!stringICmp(fmt, DataFormats.utf8))
683    if(!stringICmp(fmt, "UTF-8")) {
684       callback(DataFormats.unicodeText);
685       callback(DataFormats.text);
686    } else if(!stringICmp(fmt, DataFormats.unicodeText)) {
687       //callback(DataFormats.utf8);
688       callback("UTF-8");
689       callback(DataFormats.text);
690    } else if(!stringICmp(fmt, DataFormats.text)) {
691       //callback(DataFormats.utf8);
692       callback("UTF-8");
693       callback(DataFormats.unicodeText);
694    }
695 }
696 
697 package Data _doConvertFormat(Data dat, Dstring toFmt) {
698    Data result;
699    //if(!stringICmp(toFmt, DataFormats.utf8))
700    if(!stringICmp(toFmt, "UTF-8")) {
701       if(typeid(Dwstring) == dat.info) {
702          result = Data(utf16stringtoUtf8string(dat.getUnicodeText()));
703       } else if(typeid(ubyte[]) == dat.info) {
704          ubyte[] ubs;
705          ubs = dat.getText();
706          result = Data(dfl.internal.utf.fromAnsi(cast(Dstringz)ubs.ptr, ubs.length));
707       }
708    } else if(!stringICmp(toFmt, DataFormats.unicodeText)) {
709       if(typeid(Dstring) == dat.info) {
710          result = Data(utf8stringtoUtf16string(dat.getString()));
711       } else if(typeid(ubyte[]) == dat.info) {
712          ubyte[] ubs;
713          ubs = dat.getText();
714          result = Data(dfl.internal.utf.ansiToUnicode(cast(Dstringz)ubs.ptr, ubs.length));
715       }
716    } else if(!stringICmp(toFmt, DataFormats.text)) {
717       if(typeid(Dstring) == dat.info) {
718          result = Data(cast(ubyte[])dfl.internal.utf.toAnsi(dat.getString()));
719       } else if(typeid(Dwstring) == dat.info) {
720          Dwstring wcs;
721          wcs = dat.getUnicodeText();
722          result = Data(cast(ubyte[])unicodeToAnsi(wcs.ptr, wcs.length));
723       }
724    }
725    return result;
726 }
727 
728 class ComToDdataObject: IDataObjectD {
729    private IDataObject dataObj;
730    this(IDataObject dataObj) {
731       this.dataObj = dataObj;
732       dataObj.AddRef();
733    }
734 
735    ~this() {
736       dataObj.Release(); // Must get called...
737    }
738 
739    private Data _getData(int id) {
740       FORMATETC fmte;
741       STGMEDIUM stgm;
742       void[] mem;
743       void* plock;
744 
745       fmte.cfFormat = cast(CLIPFORMAT)id;
746       fmte.ptd = null;
747       fmte.dwAspect = DVASPECT.DVASPECT_CONTENT; // ?
748       fmte.lindex = -1;
749       fmte.tymed = TYMED.TYMED_HGLOBAL; // ?
750 
751       if(S_OK != dataObj.GetData(&fmte, &stgm)) {
752          throw new DflException("Unable to get data");
753       }
754 
755       void release() {
756          //ReleaseStgMedium(&stgm);
757          if(stgm.pUnkForRelease) {
758             stgm.pUnkForRelease.Release();
759          } else {
760             GlobalFree(stgm.hGlobal);
761          }
762       }
763 
764 
765       plock = GlobalLock(stgm.hGlobal);
766       if(!plock) {
767          release();
768          throw new DflException("Error obtaining data");
769       }
770 
771       mem = new ubyte[GlobalSize(stgm.hGlobal)];
772       mem[] = plock[0 .. mem.length];
773       GlobalUnlock(stgm.hGlobal);
774       release();
775 
776       return DataFormats.getDataFromFormat(id, mem);
777    }
778 
779    Data getData(Dstring fmt) {
780       return _getData(DataFormats.getFormat(fmt).id);
781    }
782 
783    Data getData(TypeInfo type) {
784       return _getData(DataFormats.getFormatFromType(type).id);
785    }
786 
787    Data getData(Dstring fmt, bool doConvert) {
788       return getData(fmt); // ?
789    }
790 
791    private bool _getDataPresent(int id) {
792       FORMATETC fmte;
793 
794       fmte.cfFormat = cast(CLIPFORMAT)id;
795       fmte.ptd = null;
796       fmte.dwAspect = DVASPECT.DVASPECT_CONTENT; // ?
797       fmte.lindex = -1;
798       fmte.tymed = TYMED.TYMED_HGLOBAL; // ?
799 
800       return S_OK == dataObj.QueryGetData(&fmte);
801    }
802 
803    bool getDataPresent(Dstring fmt) {
804       return _getDataPresent(DataFormats.getFormat(fmt).id);
805    }
806 
807    bool getDataPresent(TypeInfo type) {
808       return _getDataPresent(DataFormats.getFormatFromType(type).id);
809    }
810 
811    bool getDataPresent(Dstring fmt, bool canConvert) {
812       return getDataPresent(fmt); // ?
813    }
814 
815    Dstring[] getFormats() {
816       IEnumFORMATETC fenum;
817       FORMATETC fmte;
818       Dstring[] result;
819       ULONG nfetched = 1; // ?
820 
821       if(S_OK != dataObj.EnumFormatEtc(1, &fenum)) {
822          throw new DflException("Unable to get formats");
823       }
824 
825       fenum.AddRef(); // ?
826       for(;;) {
827          if(S_OK != fenum.Next(1, &fmte, &nfetched)) {
828             break;
829          }
830          if(!nfetched) {
831             break;
832          }
833          //cprintf("\t\t{getFormats:%d}\n", fmte.cfFormat);
834          result ~= DataFormats.getFormat(fmte.cfFormat).name;
835       }
836       fenum.Release(); // ?
837 
838       return result;
839    }
840 
841 
842    // TO-DO: remove...
843    deprecated final Dstring[] getFormats(bool onlyNative) {
844       return getFormats();
845    }
846 
847    private void _setData(int id, Data obj) {
848       /+
849          FORMATETC fmte;
850       STGMEDIUM stgm;
851       HANDLE hmem;
852       void[] mem;
853       void* pmem;
854 
855       mem = DataFormats.getClipboardValueFromData(id, obj);
856 
857       hmem = GlobalAlloc(GMEM_SHARE, mem.length);
858       if(!hmem) {
859          //cprintf("Unable to GlobalAlloc().\n");
860 err_set:
861          throw new DflException("Unable to set data");
862       }
863       pmem = GlobalLock(hmem);
864       if(!pmem) {
865          //cprintf("Unable to GlobalLock().\n");
866          GlobalFree(hmem);
867          goto err_set;
868       }
869       pmem[0 .. mem.length] = mem;
870       GlobalUnlock(hmem);
871 
872       fmte.cfFormat = cast(CLIPFORMAT)id;
873       fmte.ptd = null;
874       fmte.dwAspect = DVASPECT.DVASPECT_CONTENT; // ?
875       fmte.lindex = -1;
876       fmte.tymed = TYMED.TYMED_HGLOBAL;
877 
878       stgm.tymed = TYMED.TYMED_HGLOBAL;
879       stgm.hGlobal = hmem;
880       stgm.pUnkForRelease = null;
881 
882       // -dataObj- now owns the handle.
883       HRESULT hr = dataObj.SetData(&fmte, &stgm, true);
884       if(S_OK != hr) {
885          //cprintf("Unable to IDataObject::SetData() = %d (0x%X).\n", hr, hr);
886          // Failed, need to free it..
887          GlobalFree(hmem);
888          goto err_set;
889       }
890       +/
891          // Don't set stuff in someone else's data object.
892    }
893 
894    void setData(Data obj) {
895       _setData(DataFormats.getFormatFromType(obj.info).id, obj);
896    }
897 
898    void setData(Dstring fmt, Data obj) {
899       _setData(DataFormats.getFormat(fmt).id, obj);
900    }
901 
902    void setData(TypeInfo type, Data obj) {
903       _setData(DataFormats.getFormatFromType(type).id, obj);
904    }
905 
906    void setData(Dstring fmt, bool canConvert, Data obj) {
907       setData(fmt, obj); // ?
908    }
909 
910    final bool isSameDataObject(IDataObject dataObj) {
911       return dataObj is this.dataObj;
912    }
913 }
914 
915 package class EnumDataObjectFORMATETC: DflComObject, IEnumFORMATETC {
916    this(IDataObjectD dataObj, Dstring[] fmts, ULONG start) {
917       this.dataObj = dataObj;
918       this.fmts = fmts;
919       idx = start;
920    }
921 
922 
923    this(IDataObjectD dataObj) {
924       this(dataObj, dataObj.getFormats(), 0);
925    }
926 
927 
928    extern(Windows):
929       override HRESULT QueryInterface(IID* riid, void** ppv) {
930          if(*riid == IID_IEnumFORMATETC) {
931             *ppv = cast(void*)cast(IEnumFORMATETC)this;
932             AddRef();
933             return S_OK;
934          } else if(*riid == IID_IUnknown) {
935             *ppv = cast(void*)cast(IUnknown)this;
936             AddRef();
937             return S_OK;
938          } else {
939             *ppv = null;
940             return E_NOINTERFACE;
941          }
942       }
943 
944 
945    HRESULT Next(ULONG celt, FORMATETC* rgelt, ULONG* pceltFetched) {
946       HRESULT result;
947 
948       try {
949          if(idx < fmts.length) {
950             ULONG end;
951             end = idx + celt;
952             if(end > fmts.length) {
953                result = S_FALSE; // ?
954                end = fmts.length;
955 
956                if(pceltFetched) {
957                   *pceltFetched = end - idx;
958                }
959             } else {
960                result = S_OK;
961 
962                if(pceltFetched) {
963                   *pceltFetched = celt;
964                }
965             }
966 
967             for(; idx != end; idx++) {
968                rgelt.cfFormat = cast(CLIPFORMAT)DataFormats.getFormat(fmts[idx]).id;
969                rgelt.ptd = null;
970                rgelt.dwAspect = DVASPECT.DVASPECT_CONTENT; // ?
971                rgelt.lindex = -1;
972                //rgelt.tymed = TYMED.TYMED_NULL;
973                rgelt.tymed = TYMED.TYMED_HGLOBAL;
974 
975                rgelt++;
976             }
977          } else {
978             if(pceltFetched) {
979                *pceltFetched = 0;
980             }
981             result = S_FALSE;
982          }
983       } catch(DThrowable e) {
984          // FIX: Application.onThreadException(e);
985 
986          result = E_UNEXPECTED;
987       }
988 
989       return result;
990    }
991 
992 
993    HRESULT Skip(ULONG celt) {
994       idx += celt;
995       return (idx > fmts.length) ? S_FALSE : S_OK;
996    }
997 
998 
999    HRESULT Reset() {
1000       HRESULT result;
1001 
1002       try {
1003          idx = 0;
1004          fmts = dataObj.getFormats();
1005 
1006          result = S_OK;
1007       } catch(DThrowable e) {
1008          // FIX: Application.onThreadException(e);
1009 
1010          result = E_UNEXPECTED;
1011       }
1012 
1013       return result;
1014    }
1015 
1016 
1017    HRESULT Clone(IEnumFORMATETC* ppenum) {
1018       HRESULT result;
1019 
1020       try {
1021          *ppenum = new EnumDataObjectFORMATETC(dataObj, fmts, idx);
1022          result = S_OK;
1023       } catch(DThrowable e) {
1024          // FIX: Application.onThreadException(e);
1025 
1026          result = E_UNEXPECTED;
1027       }
1028 
1029       return result;
1030    }
1031 
1032 
1033    extern(D) {
1034 
1035       private:
1036          IDataObjectD dataObj;
1037          Dstring[] fmts;
1038          ULONG idx;
1039    }
1040 }
1041 
1042 
1043 class DtoComDataObject: DflComObject, IDataObject {
1044    this(IDataObjectD dataObj) {
1045       this.dataObj = dataObj;
1046    }
1047 
1048    extern(Windows) {
1049       override HRESULT QueryInterface(IID* riid, void** ppv) {
1050          if(*riid == IID_IDataObject) {
1051             *ppv = cast(void*)cast(IDataObject)this;
1052             AddRef();
1053             return S_OK;
1054          } else if(*riid == IID_IUnknown) {
1055             *ppv = cast(void*)cast(IUnknown)this;
1056             AddRef();
1057             return S_OK;
1058          } else {
1059             *ppv = null;
1060             return E_NOINTERFACE;
1061          }
1062       }
1063 
1064 
1065       HRESULT GetData(FORMATETC* pFormatetc, STGMEDIUM* pmedium) {
1066          Dstring fmt;
1067          HRESULT result = S_OK;
1068          Data data;
1069 
1070          try {
1071             if(pFormatetc.lindex != -1) {
1072                result = DV_E_LINDEX;
1073             } else if(!(pFormatetc.tymed & TYMED.TYMED_HGLOBAL)) {
1074                // Unsupported medium type.
1075                result = DV_E_TYMED;
1076             } else if(!(pFormatetc.dwAspect & DVASPECT.DVASPECT_CONTENT)) {
1077                // What about the other aspects?
1078                result = DV_E_DVASPECT;
1079             } else {
1080                DataFormats.Format dfmt;
1081                dfmt = DataFormats.getFormat(pFormatetc.cfFormat);
1082                fmt = dfmt.name;
1083                data = dataObj.getData(fmt, true); // Should this be convertable?
1084 
1085                HGLOBAL hg;
1086                void* pmem;
1087                void[] src;
1088 
1089                //src = data.value;
1090                src = DataFormats.getClipboardValueFromData(dfmt.id, data);
1091                // FIX: obsolete:
1092                enum GMEM_SHARE=8192;
1093 
1094                hg = GlobalAlloc(GMEM_SHARE, src.length);
1095                if(!hg) {
1096                   result = STG_E_MEDIUMFULL;
1097                } else {
1098                   pmem = GlobalLock(hg);
1099                   if(!hg) {
1100                      result = E_UNEXPECTED;
1101                      GlobalFree(hg);
1102                   } else {
1103                      pmem[0 .. src.length] = src[];
1104                      GlobalUnlock(hg);
1105 
1106                      pmedium.tymed = TYMED.TYMED_HGLOBAL;
1107                      pmedium.hGlobal = hg;
1108                      pmedium.pUnkForRelease = null; // ?
1109                   }
1110                }
1111             }
1112          } catch(DflException e) {
1113             //Application.onThreadException(e);
1114 
1115             result = DV_E_FORMATETC;
1116          } catch(OomException e) {
1117             // FIX: Application.onThreadException(e);
1118 
1119             result = E_OUTOFMEMORY;
1120          } catch(DThrowable e) {
1121             // FIX: Application.onThreadException(e);
1122 
1123             result = E_UNEXPECTED;
1124          }
1125 
1126          return result;
1127       }
1128 
1129 
1130       HRESULT GetDataHere(FORMATETC* pFormatetc, STGMEDIUM* pmedium) {
1131          return E_UNEXPECTED; // TODO: finish.
1132       }
1133 
1134 
1135       HRESULT QueryGetData(FORMATETC* pFormatetc) {
1136          Dstring fmt;
1137          HRESULT result = S_OK;
1138 
1139          try {
1140             if(pFormatetc.lindex != -1) {
1141                result = DV_E_LINDEX;
1142             } else if(!(pFormatetc.tymed & TYMED.TYMED_HGLOBAL)) {
1143                // Unsupported medium type.
1144                result = DV_E_TYMED;
1145             } else if(!(pFormatetc.dwAspect & DVASPECT.DVASPECT_CONTENT)) {
1146                // What about the other aspects?
1147                result = DV_E_DVASPECT;
1148             } else {
1149                fmt = DataFormats.getFormat(pFormatetc.cfFormat).name;
1150 
1151                if(!dataObj.getDataPresent(fmt)) {
1152                   result = S_FALSE;   // ?
1153                }
1154             }
1155          } catch(DflException e) {
1156             //Application.onThreadException(e);
1157 
1158             result = DV_E_FORMATETC;
1159          } catch(OomException e) {
1160             // FIX: Application.onThreadException(e);
1161 
1162             result = E_OUTOFMEMORY;
1163          } catch(DThrowable e) {
1164             // FIX: Application.onThreadException(e);
1165 
1166             result = E_UNEXPECTED;
1167          }
1168 
1169          return result;
1170       }
1171 
1172 
1173       HRESULT GetCanonicalFormatEtc(FORMATETC* pFormatetcIn, FORMATETC* pFormatetcOut) {
1174          // TODO: finish.
1175 
1176          pFormatetcOut.ptd = null;
1177          return E_NOTIMPL;
1178       }
1179 
1180 
1181       HRESULT SetData(FORMATETC* pFormatetc, STGMEDIUM* pmedium, BOOL fRelease) {
1182          return E_UNEXPECTED; // TODO: finish.
1183       }
1184 
1185 
1186       HRESULT EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC* ppenumFormatetc) {
1187          // SHCreateStdEnumFmtEtc() requires Windows 2000 +
1188 
1189          HRESULT result;
1190 
1191          try {
1192             if(dwDirection == DATADIR.DATADIR_GET) {
1193                *ppenumFormatetc = new EnumDataObjectFORMATETC(dataObj);
1194                result = S_OK;
1195             } else {
1196                result = E_NOTIMPL;
1197             }
1198          } catch(DThrowable e) {
1199             // FIX: Application.onThreadException(e);
1200 
1201             result = E_UNEXPECTED;
1202          }
1203 
1204          return result;
1205       }
1206 
1207 
1208       HRESULT DAdvise(FORMATETC* pFormatetc, DWORD advf, IAdviseSink pAdvSink, DWORD* pdwConnection) {
1209          return E_UNEXPECTED; // TODO: finish.
1210       }
1211 
1212 
1213       HRESULT DUnadvise(DWORD dwConnection) {
1214          return E_UNEXPECTED; // TODO: finish.
1215       }
1216 
1217 
1218       HRESULT EnumDAdvise(IEnumSTATDATA* ppenumAdvise) {
1219          return E_UNEXPECTED; // TODO: finish.
1220       }
1221    }
1222    extern(D) private IDataObjectD dataObj;
1223 }