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