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