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