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