Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2502 lines
66 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module LDTE.C - RichEdit Light Data Transfer Engine |
  5. *
  6. * This file contains data transfer code using IDataObject
  7. *
  8. * Author: <nl>
  9. * alexgo (4/25/95)
  10. *
  11. * Revisions: <nl>
  12. * murrays (7/6/95) auto-doc'd and added RTF support
  13. *
  14. * FUTURE (AlexGo): <nl>
  15. * Maybe merge this class with CTxtRange to make more efficient use of
  16. * the this ptr. All but two methods use a CTxtRange and one of these
  17. * could be global. The two are:
  18. *
  19. * GetDropTarget( IDropTarget **ppDropTarget )
  20. * GetDataObjectInfo(IDataObject *pdo, DWORD *pDOIFlags) // Can be global
  21. *
  22. * In general, a range can spawn data objects, which need to have a clone
  23. * of the range in case the range is moved around. The contained range
  24. * is used for delayed rendering. A prenotification is sent to the data
  25. * object just before the data object's data is to be changed. The data
  26. * object then renders the data in its contained range, whereupon the
  27. * object becomes independent of the range and destroys the range.
  28. *
  29. * @devnote
  30. * We use the word ANSI in a general way to mean any multibyte character
  31. * system as distinguished from 16-bit Unicode. Technically, ANSI refers
  32. * to a specific single-byte character system (SBCS). We translate
  33. * between "ANSI" and Unicode text using the Win32
  34. * MultiByteToWideChar() and WideCharToMultiByte() APIs.
  35. *
  36. * Copyright (c) 1995-2000, Microsoft Corporation. All rights reserved.
  37. */
  38. #include "_common.h"
  39. #include "_range.h"
  40. #include "_ldte.h"
  41. #include "_m_undo.h"
  42. #include "_antievt.h"
  43. #include "_edit.h"
  44. #include "_disp.h"
  45. #include "_select.h"
  46. #include "_dragdrp.h"
  47. #include "_dxfrobj.h"
  48. #include "_rtfwrit.h"
  49. #include "_rtfread.h"
  50. #include "_urlsup.h"
  51. ASSERTDATA
  52. //Local Prototypes
  53. DWORD CALLBACK WriteHGlobal(WRITEHGLOBAL *pwhg, LPBYTE pbBuff, LONG cb, LONG *pcb);
  54. #define SFF_ADJUSTENDEOP 0x80000000
  55. #define FMITAL (FMATHITAL >> 24)
  56. #define FMBOLD (FMATHBOLD >> 24)
  57. #define FMSCRP (FMATHSCRP >> 24)
  58. #define FMFRAK (FMATHFRAK >> 24)
  59. #define FMOPEN (FMATHOPEN >> 24)
  60. #define FMSANS (FMATHSANS >> 24)
  61. #define FMMONO (FMATHMONO >> 24)
  62. #if FMITAL != 0x02 || FMBOLD != 0x04 || FMSCRP != 0x08 || FMFRAK != 0x10 || FMOPEN != 0x20 || FMSANS != 0x40 || FMMONO != 0x80
  63. #error "Need to change >> 24 (and << 24) below"
  64. #endif
  65. //
  66. // LOCAL METHODS
  67. //
  68. /*
  69. * ReadHGlobal(dwCookie, pbBuff, cb, pcb)
  70. *
  71. * @func
  72. * EDITSTREAM callback for reading from an hglobal
  73. *
  74. * @rdesc
  75. * es.dwError
  76. */
  77. DWORD CALLBACK ReadHGlobal(
  78. DWORD_PTR dwCookie, // @parm dwCookie
  79. LPBYTE pbBuff, // @parm Buffer to fill
  80. LONG cb, // @parm Buffer length
  81. LONG * pcb) // @parm Out parm for # bytes stored
  82. {
  83. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "ReadHGlobal");
  84. READHGLOBAL * const prhg = (READHGLOBAL *)dwCookie;
  85. cb = min(cb, prhg->cbLeft);
  86. CopyMemory(pbBuff, prhg->ptext, cb);
  87. prhg->cbLeft -= cb;
  88. prhg->ptext += cb;
  89. if(pcb)
  90. *pcb = cb;
  91. return NOERROR;
  92. }
  93. /*
  94. * WriteHGlobal(pwhg, pbBuff, cb, pcb)
  95. *
  96. * @func
  97. * EDITSTREAM callback for writing ASCII to an hglobal
  98. *
  99. * @rdesc
  100. * error (E_OUTOFMEMORY or NOERROR)
  101. */
  102. DWORD CALLBACK WriteHGlobal(
  103. DWORD_PTR dwCookie, // @parm dwCookie
  104. LPBYTE pbBuff, // @parm Buffer to write from
  105. LONG cb, // @parm Buffer length
  106. LONG * pcb) // @parm Out parm for # bytes written
  107. {
  108. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "WriteHGlobal");
  109. WRITEHGLOBAL * const pwhg = (WRITEHGLOBAL *)dwCookie;
  110. HGLOBAL hglobal = pwhg->hglobal;
  111. LPSTR pstr;
  112. if(pwhg->cch + cb > pwhg->cb) // Less than requested cb in
  113. { // current Alloc
  114. ULONG cbNewSize = GROW_BUFFER(pwhg->cb, cb);
  115. hglobal = GlobalReAlloc(hglobal, cbNewSize, GMEM_MOVEABLE);
  116. if(!hglobal)
  117. return (DWORD)E_OUTOFMEMORY;
  118. pwhg->hglobal = hglobal; // May be superfluous...
  119. pwhg->cb = cbNewSize;
  120. }
  121. pstr = (LPSTR)GlobalLock(hglobal);
  122. if(!pstr)
  123. return (DWORD)E_OUTOFMEMORY;
  124. CopyMemory(pstr + pwhg->cch, pbBuff, cb);
  125. GlobalUnlock(hglobal);
  126. pwhg->cch += cb;
  127. if(pcb)
  128. *pcb = cb;
  129. return NOERROR;
  130. }
  131. //
  132. // PUBLIC METHODS
  133. //
  134. /*
  135. * GetCharFlags(pch, cchPch, iCharRepDefault)
  136. *
  137. * @func
  138. * Return principle character flags corresponding to character *pch.
  139. * Note that if *pch is a Unicode surrogate-pair lead word, then
  140. * *(pch + 1) is used as the trail word. Note also that only math
  141. * fonts return more than one flag. The values follow the Unicode
  142. * Standard Version 3.0 with some extensions beyond that standard
  143. * (limited plane-2 support and plane-1 math alphanumerics).
  144. *
  145. * @rdesc
  146. * Principle char flag corresponding to *pch.
  147. *
  148. * =FUTURE= should be constructed as a 2-level lookup.
  149. */
  150. QWORD GetCharFlags(
  151. const WCHAR *pch, //@parm Gives char to get flag for
  152. LONG cch, //@parm cch of valid chars in pch
  153. BYTE iCharRepDefault) //@parm Default charrep in FE heuristics
  154. {
  155. WCHAR ch = *pch;
  156. if(ch < 0x100) // Latin1: divide into 3 bits
  157. return ch > 0x7F ? FHILATIN1 :
  158. ch < 0x40 ? FBELOWX40 : FASCIIUPR;
  159. if(ch < 0x590)
  160. {
  161. if(ch >= 0x530)
  162. return FARMENIAN;
  163. if(ch >= 0x492)
  164. return FOTHER; // Extended Cyrillic
  165. if(ch >= 0x400)
  166. return FCYRILLIC;
  167. if(ch >= 0x370)
  168. return FGREEK;
  169. if(ch >= 0x300) // Combining diacritical marks
  170. return FCOMBINING;
  171. return (ch < 0x250) ? FLATIN2 : FOTHER;
  172. }
  173. // Complex scripts start at 0x590 with Hebrew (aside from combining)
  174. if(ch <= 0x11FF) // Complex scripts end at 0x11FF
  175. { // (at least in July, 2000)
  176. if(ch < 0x900)
  177. {
  178. return (ch < 0x600 ? FHEBREW :
  179. ch < 0x700 ? FARABIC :
  180. ch < 0x750 ? FSYRIAC :
  181. ch < 0x780 ? FRTL :
  182. ch < 0x7C0 ? FTHAANA : FRTL);
  183. }
  184. if(ch < 0xE00) // 0x900 - 0xDFF: Indic scripts
  185. return FontSigFromCharRep(DEVANAGARI_INDEX + ((ch - 0x900) >> 7));
  186. if(ch < 0xF00)
  187. return ch < 0xE80 ? FTHAI : FLAO;
  188. if(ch < 0x1000)
  189. return ch < 0xFC0 ? FTIBETAN : FOTHER;
  190. if(ch < 0x1100)
  191. return ch < 0x10A0 ? FMYANMAR : FGEORGIAN;
  192. return FJAMO;
  193. }
  194. DWORD dwCharFlags = 0;
  195. if(ch < 0x3100)
  196. {
  197. if(ch >= 0x3000)
  198. {
  199. if(ch > 0x3040)
  200. return FKANA;
  201. goto CLASSIFY_CHINESE;
  202. }
  203. if(ch < 0x18AF)
  204. {
  205. Assert(ch >= 0x1200);
  206. if(ch <= 0x137F)
  207. return FETHIOPIC;
  208. if(IN_RANGE(0x13A0, ch, 0x16F0))
  209. {
  210. return ch < 0x1400 ? FCHEROKEE :
  211. ch < 0x1680 ? FABORIGINAL :
  212. ch < 0x16A0 ? FOGHAM : FRUNIC;
  213. }
  214. if(IN_RANGE(0x1780, ch, 0x18AF))
  215. return ch < 0x1800 ? FKHMER : FMONGOLIAN;
  216. return FOTHER;
  217. }
  218. if(IN_RANGE(RTLMARK, ch, 0x202E) && (ch == RTLMARK ||
  219. IN_RANGE(0x202A, ch, 0x202E)))
  220. {
  221. return FRTL;
  222. }
  223. if(ch == EURO || ch == 0x2122) // Euro or TM
  224. return FHILATIN1;
  225. if(ch == 0x20AA) // Hebrew currency sign
  226. return FHEBREW;
  227. if(W32->IsFESystem() || IsFECharRep(iCharRepDefault) || IN_RANGE(0x2460, ch, 0x24FF))
  228. goto CLASSIFY_CHINESE;
  229. if(IN_RANGE(0x200b, ch, 0x200d)) // ZWSP, ZWNJ, ZWJ
  230. return FUNIC_CTRL;
  231. if(ch == 0x2016 || ch == 0x2236)
  232. {
  233. // Some hack to make Word2000 happy
  234. if(VerifyFEString(CP_CHINESE_TRAD, &ch, 1, TRUE) == CP_CHINESE_TRAD)
  235. return FBIG5;
  236. if(VerifyFEString(CP_CHINESE_SIM, &ch, 1, TRUE) == CP_CHINESE_SIM)
  237. return FCHINESE;
  238. }
  239. if(IN_RANGE(0x2800, ch, 0x28FF))
  240. return FBRAILLE;
  241. return FOTHER;
  242. }
  243. if(ch < 0xD800)
  244. {
  245. Assert(ch >= 0x3100);
  246. if(ch < 0x3400)
  247. {
  248. if(ch < 0x3130) // Bopomofo annotation chars
  249. { // used in Chinese text
  250. return (W32->GetFEFontInfo() == BIG5_INDEX)
  251. ? FBIG5 : FCHINESE;
  252. }
  253. if (ch < 0x3190 || // Hangul Compatibility Jamo
  254. IN_RANGE(0x3200, ch, 0x321F) || // Parenthesized Hangul
  255. IN_RANGE(0x3260, ch, 0x327F)) // Circled Hangul
  256. {
  257. return FHANGUL;
  258. }
  259. if(IN_RANGE(0x32D0, ch, 0x337F)) // Circled & Squared Katakana words
  260. return FKANA;
  261. }
  262. if(ch < 0x04DFF)
  263. return FOTHER;
  264. if(ch < 0xA000)
  265. goto CLASSIFY_CHINESE;
  266. if(ch < 0xA4D0)
  267. return FYI;
  268. if(ch < 0xAC00)
  269. goto CLASSIFY_CHINESE;
  270. return FHANGUL;
  271. }
  272. if(ch < 0xE000)
  273. {
  274. if(cch > 1 && IN_RANGE(UTF16_LEAD, ch, UTF16_TRAIL - 1))
  275. {
  276. pch++;
  277. if(IN_RANGE(UTF16_TRAIL, *pch, UTF16_TRAIL + 1023))
  278. {
  279. LONG lch = (*pch & 0x3FF) + ((ch & 0x3FF) << 10) + 0x10000;
  280. /* For testing purposes, implement math alphanumerics. Block
  281. * starts with thirteen 52-char English alphabets, five 58-char
  282. * Greek alphabets (2*24 + 10 variants), and ended with five
  283. * 10-char digits as given by the following table (E - English,
  284. * D - digit, G - Greek):
  285. *
  286. * 1) Math bold (EGD) 2) Math italic (E)
  287. * 3) Math bold italic (EG) 4) Math script (E)
  288. * 5) Math script bold (E) 6) Math fraktur (E)
  289. * 7) Math fraktur bold (E) 8) Math open-face (ED)
  290. * 9) Math sans (ED) 10) Math sans bold (EGD)
  291. * 11) Math sans italic (E) 12) Math sans bold italic (EG)
  292. * 13) Math monospace (ED)
  293. */
  294. static BYTE rgFsE[] = {
  295. FMBOLD, FMITAL, FMBOLD + FMITAL, FMSCRP, FMSCRP + FMBOLD,
  296. FMFRAK, FMFRAK + FMBOLD, FMOPEN, FMSANS, FMSANS + FMBOLD,
  297. FMSANS + FMITAL, FMSANS + FMBOLD + FMITAL, FMMONO};
  298. static BYTE rgFsG[] = {
  299. FMBOLD, FMITAL, FMBOLD + FMITAL, FMSANS + FMBOLD,
  300. FMSANS + FMBOLD + FMITAL};
  301. static BYTE rgFsD[] = {
  302. FMBOLD, FMOPEN, FMSANS, FMSANS + FMBOLD, FMMONO};
  303. if(IN_RANGE(0x1D400, lch, 0x1D7FF))
  304. {
  305. lch -= 0x1D400; // Sub math-alphanumerics origin
  306. if(lch < 13*52) // 13 English alphabets
  307. return FSURROGATE + FASCIIUPR + (rgFsE[lch/52] << 24);
  308. lch -= 13*52;
  309. if(lch < 5*58) // 5 Greek alphabets
  310. return FSURROGATE + FGREEK + (rgFsG[lch/58] << 24);
  311. lch -= 5*58 + (1024 - 5*58 - 13*52 - 5*10);
  312. if(lch >= 0) // 5 digit groups
  313. return FSURROGATE + FBELOWX40 + (rgFsD[lch/10] << 24);
  314. }
  315. if(IN_RANGE(0x20000, lch, 0x2FFFF)) // Plane 2: Extension B
  316. { // CJK characters
  317. dwCharFlags = FSURROGATE;
  318. goto CLASSIFY_CHINESE;
  319. }
  320. }
  321. }
  322. return FSURROGATE; // Surrogate
  323. }
  324. if(ch < 0xF900) // Private Use Area
  325. {
  326. if(IN_RANGE(0xF020, ch, 0xF0FF))
  327. return FSYMBOL;
  328. return FOTHER;
  329. }
  330. if(ch < 0xFF00)
  331. {
  332. if(IN_RANGE(0xFE30, ch, 0xFE4F)) // CJK Vertical variants
  333. goto CLASSIFY_CHINESE;
  334. if(IN_RANGE(0xF900, ch, 0xFAFF)) // CJK characters
  335. goto CLASSIFY_CHINESE;
  336. return FOTHER;
  337. }
  338. if(IN_RANGE(0xFF00, ch, 0xFFEF))
  339. {
  340. if (ch < 0xFF60 || ch >= 0xFFE0 || // Fullwidth ASCII or Fullwidth symbols
  341. ch == 0xFF64) // special case Half-width ideographic comma
  342. goto CLASSIFY_CHINESE;
  343. return ch < 0xFFA0 ? FKANA : FHANGUL; // Halfwidth Katakana/Hangul
  344. }
  345. return FOTHER;
  346. CLASSIFY_CHINESE:
  347. if(IN_RANGE(JPN2_INDEX, iCharRepDefault, CHT2_INDEX))
  348. iCharRepDefault -= JPN2_INDEX - SHIFTJIS_INDEX;
  349. if(IN_RANGE(SHIFTJIS_INDEX, iCharRepDefault, BIG5_INDEX))
  350. {
  351. CLASS2:
  352. LONG dIndex = iCharRepDefault - SHIFTJIS_INDEX;
  353. if(!dwCharFlags)
  354. return (DWORD)FKANA << dIndex;
  355. union // Plane 2
  356. { // Endian-dependent way to
  357. QWORD qw; // avoid 64-bit left shift
  358. DWORD dw[2];
  359. };
  360. dw[0] = FSURROGATE;
  361. dw[1] = (DWORD)(FJPN2 >> 32) << dIndex;
  362. return qw;
  363. }
  364. iCharRepDefault = W32->GetFEFontInfo();
  365. if(IN_RANGE(SHIFTJIS_INDEX, iCharRepDefault, BIG5_INDEX))
  366. goto CLASS2;
  367. return FCHINESE;
  368. }
  369. /*
  370. * CLightDTEngine::CLightDTEngine()
  371. *
  372. * @mfunc
  373. * Constructor for Light Data Transfer Engine
  374. */
  375. CLightDTEngine::CLightDTEngine()
  376. {
  377. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::CLightDTEngine");
  378. _ped = NULL;
  379. _pdt = NULL;
  380. _pdo = NULL;
  381. _fUseLimit = FALSE;
  382. _fOleless = FALSE;
  383. }
  384. /*
  385. * CLightDTEngine::~CLightDTEngine
  386. *
  387. * @mfunc
  388. * Handles all necessary clean up for the object.
  389. */
  390. CLightDTEngine::~CLightDTEngine()
  391. {
  392. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::~CLightDTEngine");
  393. if( _pdt )
  394. {
  395. _pdt->Zombie();
  396. _pdt->Release();
  397. _pdt = NULL;
  398. }
  399. Assert(_pdo == NULL);
  400. }
  401. /*
  402. * CLightDTEngine::Destroy()
  403. *
  404. * @mfunc
  405. * Deletes this instance
  406. */
  407. void CLightDTEngine::Destroy()
  408. {
  409. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::Destroy");
  410. delete this;
  411. }
  412. /*
  413. * CLightDTEngine::CopyRangeToClipboard ( prg, lStreamFormat )
  414. *
  415. * @mfunc
  416. * Copy the text of the range prg to the clipboard using Win32 APIs
  417. *
  418. * @rdesc
  419. * HRESULT
  420. */
  421. HRESULT CLightDTEngine::CopyRangeToClipboard(
  422. CTxtRange *prg, //@parm Range to copy to clipboard
  423. LONG lStreamFormat) //@parm Stream format
  424. {
  425. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::CopyRangeToClipboard");
  426. HRESULT hresult = E_FAIL;
  427. IDataObject *pdo = NULL;
  428. IRichEditOleCallback * precall = _ped->GetRECallback();
  429. BOOL fSingleObject;
  430. CHARRANGE chrg;
  431. prg->GetRange(chrg.cpMin, chrg.cpMost);
  432. if (chrg.cpMin >= chrg.cpMost)
  433. {
  434. // We can't copy an insertion point to the clipboard so we are done.
  435. return NOERROR;
  436. }
  437. fSingleObject = chrg.cpMost - chrg.cpMin == 1 &&
  438. _ped->HasObjects() &&
  439. _ped->_pobjmgr->CountObjectsInRange(chrg.cpMin, chrg.cpMost);
  440. if(precall)
  441. {
  442. // Give the callback a chance to give us its own IDataObject
  443. hresult = precall->GetClipboardData(&chrg, RECO_COPY, &pdo);
  444. }
  445. // If we didn't get an IDataObject from the callback, build our own
  446. if(hresult != NOERROR)
  447. {
  448. // If the range is empty, don't bother creating it. Just
  449. // leave the clipboard alone and return
  450. if( prg->GetCch() == 0 )
  451. {
  452. _ped->Beep();
  453. return NOERROR;
  454. }
  455. hresult = RangeToDataObject(prg, SF_TEXT | SF_RTF | lStreamFormat, &pdo);
  456. }
  457. // NB: it's important to check both hresult && pdo; it is legal for
  458. // our client to say "yep, I handled the copy, but there was nothing
  459. // to copy".
  460. if( hresult == NOERROR && pdo )
  461. {
  462. hresult = OleSetClipboard(pdo);
  463. if( hresult != NOERROR )
  464. {
  465. HWND hwnd;
  466. _fOleless = TRUE;
  467. // Ole less clipboard support
  468. if (_ped->TxGetWindow(&hwnd) == NOERROR &&
  469. ::OpenClipboard(hwnd) &&
  470. ::EmptyClipboard()
  471. )
  472. {
  473. ::SetClipboardData(cf_RTF, NULL);
  474. ::SetClipboardData(CF_UNICODETEXT, NULL);
  475. if(_ped->IsDocMoreThanLatin1Symbol())
  476. {
  477. ::SetClipboardData(cf_RTFUTF8, NULL);
  478. ::SetClipboardData(cf_RTFNCRFORNONASCII, NULL);
  479. }
  480. ::SetClipboardData(CF_TEXT, NULL);
  481. if (fSingleObject)
  482. ::SetClipboardData(CF_DIB, NULL);
  483. ::CloseClipboard();
  484. hresult = NOERROR; // To cause replace range to happen
  485. }
  486. }
  487. if(_pdo)
  488. _pdo->Release();
  489. _pdo = pdo;
  490. }
  491. return hresult;
  492. }
  493. /*
  494. * CLightDTEngine::CutRangeToClipboard( prg, lStreamFormat, publdr );
  495. *
  496. * @mfunc
  497. * Cut text of the range prg to the clipboard
  498. *
  499. * @devnote
  500. * If publdr is non-NULL, antievents for the cut operation should be
  501. * stuffed into this collection
  502. *
  503. * @rdesc
  504. * HRESULT from CopyRangeToClipboard()
  505. *
  506. * @devnote
  507. * First copy the text to the clipboard, then delete it from the range
  508. */
  509. HRESULT CLightDTEngine::CutRangeToClipboard(
  510. CTxtRange * prg, //@parm Range to cut to clipboard
  511. LONG lStreamFormat, //@parm Stream format
  512. IUndoBuilder *publdr ) //@parm Undo builder to receive antievents
  513. {
  514. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::CutRangeToClipboard");
  515. Assert(!_ped->TxGetReadOnly());
  516. prg->AdjustEndEOP(NONEWCHARS); // Don't include trailing EOP
  517. // in some selection cases
  518. HRESULT hr = CopyRangeToClipboard(prg, lStreamFormat);
  519. if( publdr )
  520. {
  521. publdr->SetNameID(UID_CUT);
  522. publdr->StopGroupTyping();
  523. }
  524. if(hr == NOERROR) // Delete contents of range
  525. prg->Delete(publdr, SELRR_REMEMBERRANGE);
  526. return hr;
  527. }
  528. /*
  529. * CLightDTEngine::FlushClipboard()
  530. *
  531. * @mfunc flushes the clipboard (if needed). Typically called during
  532. * shutdown.
  533. */
  534. void CLightDTEngine::FlushClipboard()
  535. {
  536. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::FlushClipboard");
  537. ENSAVECLIPBOARD ens;
  538. if( _pdo )
  539. {
  540. if( OleIsCurrentClipboard(_pdo) == NOERROR )
  541. {
  542. CDataTransferObj *pdo = NULL;
  543. // check to see if we have to flush the clipboard.
  544. ZeroMemory(&ens, sizeof(ENSAVECLIPBOARD));
  545. // check to make sure the object is one of ours before accessing
  546. // the memory. EVIL HACK ALERT. 'nuff said.
  547. if( _pdo->QueryInterface(IID_IRichEditDO, (void **)&pdo )
  548. == NOERROR && pdo )
  549. {
  550. ens.cObjectCount = pdo->_cObjs;
  551. ens.cch = pdo->_cch;
  552. pdo->Release();
  553. }
  554. if( _ped->TxNotify(EN_SAVECLIPBOARD, &ens) == NOERROR )
  555. OleFlushClipboard();
  556. else
  557. OleSetClipboard(NULL);
  558. }
  559. _pdo->Release();
  560. _pdo = NULL;
  561. }
  562. }
  563. /*
  564. * CLightDTEngine::CanPaste(pdo, cf, flags)
  565. *
  566. * @mfunc
  567. * Determines if clipboard format cf is one we can paste.
  568. *
  569. * @rdesc
  570. * BOOL - true if we can paste cf into range prg OR DF_CLIENTCONTROL
  571. * if the client is going to handle this one.
  572. *
  573. * @devnote
  574. * we check the clipboard ourselves if cf is 0. Primarily, this
  575. * is for backwards compatibility with Richedit1.0's EM_CANPASTE
  576. * message.
  577. *
  578. */
  579. DWORD CLightDTEngine::CanPaste(
  580. IDataObject *pdo, // @parm Data object to check; if NULL use clipboard
  581. CLIPFORMAT cf, // @parm Clipboard format to query about; if 0, use
  582. // best available.
  583. DWORD flags) // @parm Flags
  584. {
  585. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::CanPaste");
  586. IRichEditOleCallback *precall = _ped->GetRECallback();
  587. CLIPFORMAT cf0 = cf;
  588. DWORD cFETC = CFETC;
  589. HRESULT hr = NOERROR;
  590. DWORD ret = FALSE;
  591. if( pdo == NULL && precall )
  592. {
  593. // don't worry about errors
  594. OleGetClipboard(&pdo);
  595. }
  596. else if( pdo )
  597. {
  598. // So we can make just one 'Release' call below
  599. pdo->AddRef();
  600. }
  601. if( precall && pdo )
  602. {
  603. hr = precall->QueryAcceptData(pdo, &cf, flags, 0, NULL);
  604. if( SUCCEEDED(hr) && (hr != S_OK && hr != DATA_S_SAMEFORMATETC ) )
  605. {
  606. ret = DF_CLIENTCONTROL;
  607. goto Exit;
  608. }
  609. else if( FAILED(hr) && hr != E_NOTIMPL )
  610. goto Exit;
  611. else if(SUCCEEDED(hr))
  612. {
  613. // We should go on and check ourselves unless the client
  614. // modified the format when it shouldn't have
  615. if(cf0 && cf0 != cf)
  616. goto Exit;
  617. }
  618. // Otherwise, continue with our normal checks
  619. }
  620. if(_ped->TxGetReadOnly()) // Can't paste if read only
  621. goto Exit;
  622. while(cFETC--) // Does cf = format we can paste or
  623. { // is selection left up to us?
  624. cf0 = g_rgFETC[cFETC].cfFormat;
  625. if( cf == cf0 || !cf )
  626. {
  627. // Either we hit the format requested, or no format was
  628. // requested. Now see if the format matches what we can
  629. // handle in principle. There are three basic categories:
  630. //
  631. // 1. Rich text with OLE callback:
  632. // can handle pretty much everything.
  633. // 2. Rich text with no OLE callback:
  634. // can handle anything but OLE specific formats.
  635. // 3. Plain text only:
  636. // can only handle plain text formats.
  637. if ((_ped->_fRich || (g_rgDOI[cFETC] & DOI_CANPASTEPLAIN)) &&
  638. (precall || !(g_rgDOI[cFETC] & DOI_CANPASTEOLE)))
  639. {
  640. // once we get this far, make sure the data format
  641. // is actually available.
  642. if( (pdo && pdo->QueryGetData(&g_rgFETC[cFETC]) == NOERROR ) ||
  643. (!pdo && IsClipboardFormatAvailable(cf0)) )
  644. {
  645. ret = TRUE; // Return arbitrary non zero value.
  646. break;
  647. }
  648. }
  649. }
  650. }
  651. Exit:
  652. if(pdo)
  653. pdo->Release();
  654. return ret;
  655. }
  656. /*
  657. * CLightDTEngine::RangeToDataObject (prg, lStreamFormat, ppdo)
  658. *
  659. * @mfunc
  660. * Create data object (with no OLE-formats) for the range prg
  661. *
  662. * @rdesc
  663. * HRESULT = !ppdo ? E_INVALIDARG :
  664. * pdo ? NOERROR : E_OUTOFMEMORY
  665. */
  666. HRESULT CLightDTEngine::RangeToDataObject(
  667. CTxtRange * prg, // @parm Range to get DataObject for
  668. LONG lStreamFormat, // @parm Stream format to use for loading
  669. IDataObject ** ppdo) // @parm Out parm for DataObject
  670. {
  671. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::RangeToDataObject");
  672. if(!ppdo)
  673. return E_INVALIDARG;
  674. // Use SFF_SELECTION to indicate that this isn't used to write the whole doc.
  675. // Need to rethink if we add ITextDocument::Copy(), which would be used to
  676. // copy the whole doc.
  677. CDataTransferObj *pdo = CDataTransferObj::Create(_ped, prg, lStreamFormat | SFF_SELECTION);
  678. *ppdo = pdo;
  679. return pdo ? NOERROR : E_OUTOFMEMORY;
  680. }
  681. /*
  682. * CLightDTEngine::RenderClipboardFormat(wFmt)
  683. *
  684. * @mfunc
  685. * Renders current clipboard data object in specified format. (Ole less transfer)
  686. *
  687. * @rdesc
  688. * HRESULT
  689. */
  690. HRESULT CLightDTEngine::RenderClipboardFormat(
  691. WPARAM wFmt)
  692. {
  693. HRESULT hr = S_OK;
  694. if (_fOleless &&
  695. (wFmt == cf_RTF || wFmt == CF_UNICODETEXT || wFmt == CF_DIB || wFmt == CF_TEXT))
  696. {
  697. Assert(_pdo);
  698. STGMEDIUM med;
  699. DWORD iFETC = iUnicodeFETC;
  700. if (wFmt == cf_RTF)
  701. iFETC = iRtfFETC;
  702. else if (wFmt == CF_DIB)
  703. iFETC = iDIB;
  704. else if (wFmt == CF_TEXT)
  705. iFETC = iAnsiFETC;
  706. med.tymed = TYMED_HGLOBAL;
  707. med.pUnkForRelease = NULL;
  708. med.hGlobal = NULL;
  709. hr = _pdo->GetData(&g_rgFETC[iFETC], &med);
  710. hr = hr || ::SetClipboardData(wFmt, med.hGlobal) == NULL;
  711. }
  712. return hr; // Pretend we did the right thing.
  713. }
  714. /*
  715. * CLightDTEngine::RenderAllClipboardFormats()
  716. *
  717. * @mfunc
  718. * Renders current clipboard data object (text and RTF). (Ole less transfer)
  719. *
  720. * @rdesc
  721. * HRESULT
  722. */
  723. HRESULT CLightDTEngine::RenderAllClipboardFormats()
  724. {
  725. HRESULT hr;
  726. if(_fOleless)
  727. {
  728. HWND howner = ::GetClipboardOwner();
  729. HWND hwnd;
  730. if (howner &&
  731. _ped->TxGetWindow(&hwnd) == NOERROR &&
  732. howner == hwnd &&
  733. ::OpenClipboard(hwnd))
  734. {
  735. ::EmptyClipboard();
  736. hr = RenderClipboardFormat(cf_RTF);
  737. hr = hr || RenderClipboardFormat(CF_UNICODETEXT);
  738. hr = hr || RenderClipboardFormat(CF_DIB);
  739. hr = hr || RenderClipboardFormat(CF_TEXT);
  740. ::CloseClipboard();
  741. return hr;
  742. }
  743. }
  744. return S_OK; // Pretend we did the right thing.
  745. }
  746. /*
  747. * CLightDTEngine::DestroyClipboard()
  748. *
  749. * @mfunc
  750. * Destroys the clipboard data object
  751. *
  752. * @rdesc
  753. * HRESULT
  754. *
  755. */
  756. HRESULT CLightDTEngine::DestroyClipboard()
  757. {
  758. // Nothing to do. This should work together with our Flush clipboard logic
  759. return S_OK;
  760. }
  761. /*
  762. * CLightDTEngine::HGlobalToRange(dwFormatIndex, hGlobal, ptext, prg, publdr)
  763. *
  764. * @mfunc
  765. * Copies the contents of the given string (ptext) to the given range.
  766. * The global memory handle may or may not point to the string depending
  767. * on the format
  768. *
  769. * @rdesc
  770. * HRESULT
  771. */
  772. HRESULT CLightDTEngine::HGlobalToRange(
  773. DWORD dwFormatIndex,
  774. HGLOBAL hGlobal,
  775. LPTSTR ptext,
  776. CTxtRange * prg,
  777. IUndoBuilder *publdr)
  778. {
  779. READHGLOBAL rhg;
  780. EDITSTREAM es;
  781. HCURSOR hcur = NULL;
  782. // If RTF, wrap EDITSTREAM around hGlobal & delegate to LoadFromEs()
  783. if (dwFormatIndex == iRtfNoObjs || dwFormatIndex == iRtfFETC ||
  784. dwFormatIndex == iRtfUtf8 || dwFormatIndex == iRtfNCRforNonASCII)
  785. {
  786. Assert(hGlobal != NULL);
  787. rhg.ptext = (LPSTR)ptext; // Start at beginning
  788. rhg.cbLeft = GlobalSize(hGlobal); // with full length
  789. es.dwCookie = (DWORD_PTR)&rhg; // The read "this" ptr
  790. es.dwError = NOERROR; // No errors yet
  791. es.pfnCallback = ReadHGlobal; // The read method
  792. // Want wait cursor to display sooner
  793. bool fSetCursor = rhg.cbLeft > NUMPASTECHARSWAITCURSOR;
  794. if (fSetCursor)
  795. hcur = _ped->TxSetCursor(LoadCursor(NULL, IDC_WAIT));
  796. LONG cchLoad = LoadFromEs(prg, SFF_SELECTION | SF_RTF, &es, TRUE, publdr);
  797. if (fSetCursor)
  798. _ped->TxSetCursor(hcur);
  799. return (es.dwError != NOERROR) ? es.dwError :
  800. cchLoad ? NOERROR : S_FALSE;
  801. }
  802. Assert( dwFormatIndex == iRtfAsTextFETC ||
  803. dwFormatIndex == iAnsiFETC ||
  804. dwFormatIndex == iUnicodeFETC );
  805. BOOL fTRDsInvolved;
  806. prg->CheckTableSelection(FALSE, TRUE, &fTRDsInvolved, 0);
  807. LONG cchEOP = 0;
  808. LONG cchMove = 0;
  809. if(fTRDsInvolved)
  810. cchEOP = prg->DeleteWithTRDCheck(publdr, SELRR_REMEMBERRANGE, &cchMove,
  811. RR_NEW_CHARS | RR_NO_LP_CHECK);
  812. LONG cchNew = prg->CleanseAndReplaceRange(-1, ptext, TRUE, publdr, NULL, NULL,
  813. RR_NEW_CHARS | RR_ITMZ_NONE | RR_NO_LP_CHECK | RR_UNHIDE);
  814. if(prg->GetCch() && prg->IsSel())
  815. return E_FAIL; // Paste failed due to UI rules
  816. if (_ped->IsRich() &&
  817. (!_ped->Get10Mode() || cchEOP)) // If rich text & not 1.0 mode, &
  818. { // new text ends with EOP, delete
  819. prg->DeleteTerminatingEOP(publdr); // that EOP to agree with Word
  820. }
  821. prg->ItemizeReplaceRange(cchNew + cchEOP, cchMove, publdr, TRUE);// Itemize w/ UnicodeBidi
  822. return NOERROR;
  823. }
  824. /*
  825. * CLightDTEngine::DIBToRange(hGlobal, prg, publdr)
  826. *
  827. * @mfunc
  828. * Inserts dib data from the clipboard into range in the control
  829. *
  830. * @rdesc
  831. * HRESULT
  832. */
  833. HRESULT CLightDTEngine::DIBToRange(
  834. HGLOBAL hGlobal,
  835. CTxtRange * prg,
  836. IUndoBuilder * publdr)
  837. {
  838. HRESULT hresult = DV_E_FORMATETC;
  839. REOBJECT reobj = { 0 };
  840. LPBITMAPINFO pbmi = (LPBITMAPINFO) GlobalLock(hGlobal);
  841. reobj.clsid = CLSID_StaticDib;
  842. reobj.sizel.cx = _ped->_pdp->DUtoHimetricU(pbmi->bmiHeader.biWidth);
  843. reobj.sizel.cy = _ped->_pdp->DVtoHimetricV(pbmi->bmiHeader.biHeight);
  844. _ped->GetClientSite(&reobj.polesite);
  845. COleObject *pobj = (COleObject *)reobj.polesite;
  846. COleObject::ImageInfo *pimageinfo = new COleObject::ImageInfo;
  847. pobj->SetHdata(hGlobal);
  848. pimageinfo->xScale = 100;
  849. pimageinfo->yScale = 100;
  850. pimageinfo->xExtGoal = reobj.sizel.cx;
  851. pimageinfo->yExtGoal = reobj.sizel.cy;
  852. pimageinfo->cBytesPerLine = 0;
  853. pobj->SetImageInfo(pimageinfo);
  854. // FUTURE: Why are we not testing for NULL earlier before we assign it to pobj? v-honwch
  855. // Also, do we need to release interfaces inside reobj (poleobj, polesite, pstg) before exit?
  856. if (!reobj.polesite )
  857. return hresult;
  858. // Put object into the edit control
  859. reobj.cbStruct = sizeof(REOBJECT);
  860. reobj.cp = prg->GetCp();
  861. reobj.dvaspect = DVASPECT_CONTENT;
  862. reobj.dwFlags = REO_RESIZABLE;
  863. // Since we are loading an object, it shouldn't be blank
  864. reobj.dwFlags &= ~REO_BLANK;
  865. prg->Set_iCF(-1);
  866. hresult = _ped->GetObjectMgr()->InsertObject(prg, &reobj, NULL);
  867. return hresult;
  868. }
  869. /*
  870. * CLightDTEngine::PasteDataObjectToRange (pdo, prg, cf, rps, pubdlr, dwFlags)
  871. *
  872. * @mfunc
  873. * Inserts data from the data object pdo into the range prg. If the
  874. * clipboard format cf is not NULL, that format is used; else the highest
  875. * priority clipboard format is used. In either case, any text that
  876. * already existed in the range is replaced. If pdo is NULL, the
  877. * clipboard is used.
  878. *
  879. * @rdesc
  880. * HRESULT
  881. */
  882. HRESULT CLightDTEngine::PasteDataObjectToRange(
  883. IDataObject * pdo, // @parm Data object to paste
  884. CTxtRange * prg, // @parm Range into which to paste
  885. CLIPFORMAT cf, // @parm ClipBoard format to paste
  886. REPASTESPECIAL *rps, // @parm Special paste info
  887. IUndoBuilder * publdr, // @parm Undo builder to receive antievents
  888. DWORD dwFlags) // @parm DWORD packed flags
  889. {
  890. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::PasteDataObjectToRange");
  891. BOOL f10Mode = _ped->Get10Mode();
  892. HGLOBAL hGlobal = NULL;
  893. HRESULT hresult = DV_E_FORMATETC;
  894. HGLOBAL hUnicode = NULL;
  895. DWORD i;
  896. STGMEDIUM medium = {0, NULL};
  897. IDataObject *pdoSave = pdo;
  898. FORMATETC * pfetc = g_rgFETC;
  899. LPTSTR ptext = NULL;
  900. LPRICHEDITOLECALLBACK const precall = _ped->GetRECallback();
  901. BOOL fThawDisplay = FALSE;
  902. BOOL bFormatFound = FALSE; // flag which determines if a matching cf format
  903. // was found in g_rgFETC (1.0 compatibility)
  904. if(!pdo) // No data object: use clipboard
  905. {
  906. hresult = OleGetClipboard(&pdo);
  907. if(FAILED(hresult))
  908. {
  909. // Ooops. No Ole clipboard support
  910. // Need to use direct clipboard access
  911. HWND howner = ::GetClipboardOwner();
  912. HWND hwnd;
  913. if (howner &&
  914. _ped->TxGetWindow(&hwnd) == NOERROR &&
  915. howner == hwnd)
  916. {
  917. // We are cut/pasting within the same richedit instance
  918. // Use our cached clipboard data object
  919. pdo = _pdo;
  920. if(!pdo) // Some failure
  921. {
  922. _ped->Beep();
  923. return hresult;
  924. }
  925. pdo->AddRef();
  926. }
  927. else
  928. {
  929. // Oh Oh We need to transfer from clipboard without data object
  930. // Data must be coming from another window instance
  931. if (_ped->TxGetWindow(&hwnd) == NOERROR &&
  932. ::OpenClipboard(hwnd)
  933. )
  934. {
  935. HGLOBAL hUnicode = NULL;
  936. DWORD dwFmt = iRtfUtf8; // Try for UTF8 RTF
  937. _ped->_pdp->Freeze();
  938. if(!f10Mode)
  939. {
  940. hGlobal = ::GetClipboardData(cf_RTFUTF8);
  941. if (hGlobal == NULL) // Wasn't there, so
  942. { // try for RTF
  943. hGlobal = ::GetClipboardData(cf_RTFNCRFORNONASCII);
  944. dwFmt = iRtfNCRforNonASCII;
  945. }
  946. }
  947. if (hGlobal == NULL) // Wasn't there, so
  948. { // try for RTF
  949. hGlobal = ::GetClipboardData(cf_RTF);
  950. dwFmt = iRtfFETC;
  951. }
  952. if (hGlobal == NULL && !f10Mode) // Wasn't there either
  953. { // so try for plain
  954. hGlobal = ::GetClipboardData(CF_UNICODETEXT);
  955. dwFmt = iUnicodeFETC;
  956. }
  957. if (hGlobal == NULL) // Wasn't there either
  958. { // so try for plain text
  959. hGlobal = ::GetClipboardData(CF_TEXT);
  960. dwFmt = iAnsiFETC;
  961. }
  962. if (hGlobal)
  963. {
  964. if (dwFmt == iAnsiFETC)
  965. {
  966. // Convert Ansi plain text to Unicode
  967. hUnicode = TextHGlobalAtoW(hGlobal);
  968. if (hUnicode)
  969. ptext = (LPTSTR)GlobalLock(hUnicode);
  970. }
  971. else
  972. ptext = (LPTSTR)GlobalLock(hGlobal);
  973. if (ptext)
  974. hresult = HGlobalToRange(dwFmt, hGlobal, ptext, prg, publdr);
  975. else
  976. hresult = E_OUTOFMEMORY;
  977. if (hUnicode)
  978. {
  979. // Free plain text buffer
  980. GlobalUnlock(hUnicode);
  981. GlobalFree(hUnicode);
  982. }
  983. else
  984. GlobalUnlock(hGlobal);
  985. }
  986. else // hGlobal == NULL Try for bitmaps
  987. {
  988. hGlobal = ::GetClipboardData(CF_DIB);
  989. if (hGlobal)
  990. hresult = DIBToRange(hGlobal, prg, publdr);
  991. }
  992. _ped->_pdp->Thaw();
  993. ::CloseClipboard();
  994. }
  995. if (FAILED(hresult))
  996. _ped->Beep();
  997. return hresult;
  998. }
  999. }
  1000. }
  1001. // Paste an object uses the limit text calculation
  1002. _fUseLimit = TRUE;
  1003. //Call QueryAcceptData unless caller has specified otherwise
  1004. if(!(dwFlags & PDOR_NOQUERY) && precall)
  1005. {
  1006. CLIPFORMAT cfReq = cf;
  1007. HGLOBAL hmeta = NULL;
  1008. if(rps)
  1009. hmeta = (HGLOBAL)((rps->dwAspect == DVASPECT_ICON) ? rps->dwParam : NULL);
  1010. // Ask callback if it likes the data object and cfReq.
  1011. hresult = precall->QueryAcceptData(
  1012. pdo,
  1013. &cfReq,
  1014. (dwFlags & PDOR_DROP) ? RECO_DROP : RECO_PASTE,
  1015. TRUE,
  1016. hmeta);
  1017. if(hresult == DATA_S_SAMEFORMATETC)
  1018. {
  1019. // Allow callback to return DATA_S_SAMEFORMATETC if it only
  1020. // wants cf as passed in - we don't really care because
  1021. // any non-zero CLIPFORMAT causes us to only accept that format.
  1022. hresult = S_OK;
  1023. }
  1024. if(hresult == S_OK || hresult == E_NOTIMPL)
  1025. {
  1026. // Callback either liked it or didn't implement the method.
  1027. // It may have changed the format while it was at it.
  1028. // Treat a change of cf to zero as acceptance of the original.
  1029. // In any event, we will try to handle it.
  1030. // If a specific CLIPFORMAT was originally requested and the
  1031. // callback changed it, don't accept it.
  1032. if(cfReq && cf && (cf != cfReq))
  1033. {
  1034. hresult = DV_E_FORMATETC;
  1035. goto Exit;
  1036. }
  1037. // If a specific CLIPFORMAT was originally requested and the
  1038. // callback either left it alone or changed it to zero,
  1039. // make sure we use the original. If no CLIPFORMAT was
  1040. // originally requested, make sure we use what came back
  1041. // from the callback.
  1042. if(!cf)
  1043. cf = cfReq;
  1044. }
  1045. else
  1046. {
  1047. // Some success other than S_OK && DATA_S_SAMEFORMATETC.
  1048. // The callback has handled the paste. OR some error
  1049. // was returned.
  1050. goto Exit;
  1051. }
  1052. }
  1053. // Even if the RichEdit client wants CF_TEXT, if the data object
  1054. // supports CF_UNICODETEXT, we should prefer it as long as we are
  1055. // not in 1.0 or single-codepage modes.
  1056. if(cf == CF_TEXT && !f10Mode && !_ped->_fSingleCodePage)
  1057. {
  1058. FORMATETC fetc = {CF_UNICODETEXT, NULL, 0, -1, TYMED_NULL};
  1059. if(pdo->QueryGetData(&fetc) == S_OK)
  1060. cf = CF_UNICODETEXT;
  1061. else
  1062. {
  1063. // One more try - CDataTransferObj::QueryGetData is checking
  1064. // the tymed format
  1065. fetc.tymed = TYMED_HGLOBAL;
  1066. if(pdo->QueryGetData(&fetc) == S_OK)
  1067. cf = CF_UNICODETEXT;
  1068. }
  1069. }
  1070. if(cf == CF_UNICODETEXT && _ped->_fSingleCodePage)
  1071. cf = CF_TEXT;
  1072. if (_ped->TxGetReadOnly()) // Should check for range protection
  1073. {
  1074. hresult = E_ACCESSDENIED;
  1075. goto Exit;
  1076. }
  1077. // At this point we freeze the display
  1078. fThawDisplay = TRUE;
  1079. _ped->_pdp->Freeze();
  1080. if( publdr )
  1081. {
  1082. publdr->StopGroupTyping();
  1083. publdr->SetNameID(UID_PASTE);
  1084. }
  1085. for( i = 0; i < CFETC; i++, pfetc++ )
  1086. {
  1087. // Make sure the format is either 1.) a plain text format
  1088. // if we are in plain text mode or 2.) a rich text format
  1089. // or 3.) matches the requested format.
  1090. if( cf && cf != pfetc->cfFormat )
  1091. continue;
  1092. if( _ped->IsRich() || (g_rgDOI[i] & DOI_CANPASTEPLAIN) )
  1093. {
  1094. // Make sure format is available
  1095. if( pdo->QueryGetData(pfetc) != NOERROR )
  1096. continue;
  1097. if(i == iUnicodeFETC && _ped->_fSingleCodePage)
  1098. continue;
  1099. // If we have a format that uses an hGlobal get and lock it
  1100. if (i == iRtfFETC || i == iRtfAsTextFETC ||
  1101. i == iAnsiFETC || i == iRtfNoObjs ||
  1102. !f10Mode && (i == iUnicodeFETC || i == iRtfUtf8 || i == iRtfNCRforNonASCII))
  1103. {
  1104. if( pdo->GetData(pfetc, &medium) != NOERROR )
  1105. continue;
  1106. hGlobal = medium.hGlobal;
  1107. ptext = (LPTSTR)GlobalLock(hGlobal);
  1108. if( !ptext )
  1109. {
  1110. ReleaseStgMedium(&medium);
  1111. hresult = E_OUTOFMEMORY;
  1112. goto Exit;
  1113. }
  1114. // 1.0 COMPATBILITY HACK ALERT! RichEdit 1.0 has a bit of
  1115. // "error recovery" for parsing rtf files; if they aren't
  1116. // valid rtf, it treats them as just plain text.
  1117. // Unfortunately, apps like Exchange depend on this behavior,
  1118. // i.e., they give RichEdit plain text data, but call it rich
  1119. // text anyway. Accordingly, we emulate 1.0 behavior here by
  1120. // checking for an rtf signature.
  1121. if ((i == iRtfFETC || i == iRtfNoObjs || i == iRtfUtf8) &&
  1122. !IsRTF((char *)ptext, GlobalSize(hGlobal)))
  1123. {
  1124. i = iAnsiFETC; // Not RTF, make it ANSI text
  1125. }
  1126. }
  1127. else if (f10Mode && (i == iUnicodeFETC || i == iRtfUtf8))
  1128. {
  1129. // This else handles the case where we want to keep searching
  1130. // for a goood format. i.e. Unicode in 10 Mode
  1131. continue;
  1132. }
  1133. // Don't delete trail EOP in some cases
  1134. prg->AdjustEndEOP(NONEWCHARS);
  1135. // Found a format we want.
  1136. bFormatFound = TRUE;
  1137. switch(i)
  1138. {
  1139. case iRtfNoObjs:
  1140. case iRtfFETC:
  1141. case iRtfUtf8:
  1142. case iRtfNCRforNonASCII:
  1143. hresult = HGlobalToRange(i, hGlobal, ptext, prg, publdr);
  1144. break;
  1145. case iRtfAsTextFETC:
  1146. case iAnsiFETC: // ANSI plain text
  1147. hUnicode = TextHGlobalAtoW(hGlobal);
  1148. ptext = (LPTSTR)GlobalLock(hUnicode);
  1149. if(!ptext)
  1150. {
  1151. hresult = E_OUTOFMEMORY; // Unless out of RAM,
  1152. break; // fall thru to
  1153. } // Unicode case
  1154. case iUnicodeFETC: // Unicode plain text
  1155. // Ok to pass in NULL for hglobal since argument won't be used
  1156. hresult = HGlobalToRange(i, NULL, ptext, prg, publdr);
  1157. if(hUnicode) // For iAnsiFETC case
  1158. {
  1159. GlobalUnlock(hUnicode);
  1160. GlobalFree(hUnicode);
  1161. }
  1162. break;
  1163. case iObtDesc: // Object Descriptor
  1164. continue; // To search for a good format.
  1165. // the object descriptor hints will be used
  1166. // when the format is found.
  1167. case iEmbObj: // Embedded Object
  1168. case iEmbSrc: // Embed Source
  1169. case iLnkSrc: // Link Source
  1170. case iMfPict: // Metafile
  1171. case iDIB: // DIB
  1172. case iBitmap: // Bitmap
  1173. case iFilename: // Filename
  1174. hresult = CreateOleObjFromDataObj(pdo, prg, rps, i, publdr);
  1175. break;
  1176. // COMPATIBILITY ISSUE (v-richa) iTxtObj is needed by Exchange and
  1177. // as a flag for Wordpad. iRichEdit doesn't seem to be needed by
  1178. // anyone but might consider implementing as a flag.
  1179. case iRichEdit: // RichEdit
  1180. case iTxtObj: // Text with Objects
  1181. break;
  1182. default:
  1183. // Ooops didn't find a format after all
  1184. bFormatFound = FALSE;
  1185. break;
  1186. }
  1187. _ped->_ClipboardFormat = bFormatFound ? i + 1 : 0;
  1188. //If we used the hGlobal unlock it and free it.
  1189. if(hGlobal)
  1190. {
  1191. GlobalUnlock(hGlobal);
  1192. ReleaseStgMedium(&medium);
  1193. }
  1194. break; //Break out of for loop
  1195. }
  1196. }
  1197. // richedit 1.0 returned an error if an unsupported FORMATETC was
  1198. // found. This behaviour is expected by ccMail so it can handle the
  1199. // format itself
  1200. if (!bFormatFound && f10Mode)
  1201. hresult = DV_E_FORMATETC;
  1202. Exit:
  1203. if (fThawDisplay)
  1204. _ped->_pdp->Thaw();
  1205. if(!pdoSave) // Release data object
  1206. pdo->Release(); // used for clipboard
  1207. return hresult;
  1208. }
  1209. /*
  1210. * CLightDTEngine::GetDropTarget (ppDropTarget)
  1211. *
  1212. * @mfunc
  1213. * creates an OLE drop target
  1214. *
  1215. * @rdesc
  1216. * HRESULT
  1217. *
  1218. * @devnote The caller is responsible for AddRef'ing this object
  1219. * if appropriate.
  1220. */
  1221. HRESULT CLightDTEngine::GetDropTarget(
  1222. IDropTarget **ppDropTarget) // @parm outparm for drop target
  1223. {
  1224. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::GetDropTarget");
  1225. if(!_pdt)
  1226. {
  1227. _pdt = new CDropTarget(_ped);
  1228. // the AddRef done by the constructor will be
  1229. // undone by the destructor of this object
  1230. }
  1231. if(ppDropTarget)
  1232. *ppDropTarget = _pdt;
  1233. return _pdt ? NOERROR : E_OUTOFMEMORY;
  1234. }
  1235. /*
  1236. * CLightDTEngine::StartDrag (psel, publdr)
  1237. *
  1238. * @mfunc
  1239. * starts the main drag drop loop
  1240. *
  1241. */
  1242. HRESULT CLightDTEngine::StartDrag(
  1243. CTxtSelection *psel, // @parm Selection to drag from
  1244. IUndoBuilder *publdr) // @parm undo builder to receive antievents
  1245. {
  1246. #ifndef NODRAGDROP
  1247. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::StartDrag");
  1248. LONG cch, cch1;
  1249. LONG cp1, cpMin, cpMost;
  1250. DWORD dwEffect = 0;
  1251. HRESULT hr;
  1252. IDataObject * pdo = NULL;
  1253. IDropSource * pds;
  1254. IRichEditOleCallback * precall = _ped->GetRECallback();
  1255. // If we're doing drag drop's, we should have our own drop target
  1256. // It's possible that _pdt will be NULL at this point--some clients
  1257. // will delay instantiation of our drop target until a drop target
  1258. // in the parent window decides that ours is needed. However, since
  1259. // we need it just to initiate drag drop, go ahead and create one
  1260. // here.
  1261. if( _pdt == NULL )
  1262. {
  1263. hr = GetDropTarget(NULL);
  1264. if(hr != NOERROR)
  1265. return hr;
  1266. }
  1267. psel->CheckTableSelection(FALSE, TRUE, NULL, 0);
  1268. if(precall)
  1269. {
  1270. CHARRANGE chrg;
  1271. // give the callback a chance to give us its own IDataObject
  1272. psel->GetRange(chrg.cpMin, chrg.cpMost);
  1273. hr = precall->GetClipboardData(&chrg, RECO_COPY, &pdo);
  1274. }
  1275. else
  1276. {
  1277. // we need to build our own data object.
  1278. hr = S_FALSE;
  1279. }
  1280. // If we didn't get an IDataObject from the callback, build our own
  1281. if(hr != NOERROR || pdo == NULL)
  1282. { // Don't include trailing EOP
  1283. psel->AdjustEndEOP(NONEWCHARS); // in some selection cases
  1284. hr = RangeToDataObject(psel, SF_TEXT | SF_RTF, &pdo);
  1285. if(hr != NOERROR)
  1286. return hr;
  1287. }
  1288. cch = psel->GetRange(cpMin, cpMost); // NB: prg is the selection
  1289. cp1 = psel->GetCp(); // Save active end and signed
  1290. cch1 = psel->GetCch(); // length for Undo antievent
  1291. CTxtRange rg(_ped, cpMost, cch); // Use range copy to float over
  1292. // mods made to backing store
  1293. // The floating range that we just created on the stack needs to
  1294. // think that it's protected, so it won't change size.
  1295. rg.SetDragProtection(TRUE);
  1296. pds = new CDropSource();
  1297. if(pds == NULL)
  1298. {
  1299. pdo->Release();
  1300. return E_OUTOFMEMORY;
  1301. }
  1302. // Cache some info with our own drop target
  1303. _pdt->SetDragInfo(publdr, cpMin, cpMost);
  1304. // Set allowable effects
  1305. dwEffect = DROPEFFECT_COPY;
  1306. if(!_ped->TxGetReadOnly())
  1307. dwEffect |= DROPEFFECT_MOVE;
  1308. // Let the client decide what it wants.
  1309. if(precall)
  1310. hr = precall->GetDragDropEffect(TRUE, 0, &dwEffect);
  1311. if(!FAILED(hr) || hr == E_NOTIMPL)
  1312. {
  1313. // Start drag-drop operation
  1314. psel->AddRef(); // Stabilize Selection around DoDragDrop
  1315. hr = DoDragDrop(pdo, pds, dwEffect, &dwEffect);
  1316. psel->Release();
  1317. }
  1318. // Clear drop target
  1319. _pdt->SetDragInfo(NULL, -1, -1);
  1320. // Handle 'move' operations
  1321. if( hr == DRAGDROP_S_DROP && (dwEffect & DROPEFFECT_MOVE) )
  1322. {
  1323. // We're going to delete the dragged range, so turn off protection.
  1324. rg.SetDragProtection(FALSE);
  1325. if( publdr )
  1326. {
  1327. LONG cpNext, cchNext;
  1328. if(_ped->GetCallMgr()->GetChangeEvent() )
  1329. {
  1330. cpNext = cchNext = -1;
  1331. }
  1332. else
  1333. {
  1334. cpNext = rg.GetCpMin();
  1335. cchNext = 0;
  1336. }
  1337. HandleSelectionAEInfo(_ped, publdr, cp1, cch1, cpNext, cchNext,
  1338. SELAE_FORCEREPLACE);
  1339. }
  1340. // Delete the data that was moved. The selection will float
  1341. // to the new correct location.
  1342. rg.Delete(publdr, SELRR_IGNORE);
  1343. // The update that happens implicitly by the update of the range may
  1344. // have the effect of scrolling the window. This in turn may have the
  1345. // effect in the drag drop case of scrolling non-inverted text into
  1346. // the place where the selection was. The logic in the selection
  1347. // assumes that the selection is inverted and so reinverts it to turn
  1348. // off the selection. Of course, it is obvious what happens in the
  1349. // case where non-inverted text is scrolled into the selection area.
  1350. // To simplify the processing here, we just say the whole window is
  1351. // invalid so we are guaranteed to get the right painting for the
  1352. // selection.
  1353. // FUTURE: (ricksa) This solution does have the disadvantage of causing
  1354. // a flash during drag and drop. We probably want to come back and
  1355. // investigate a better way to update the screen.
  1356. _ped->TxInvalidate();
  1357. // Display is updated via notification from the range
  1358. // Update the caret
  1359. psel->Update(TRUE);
  1360. }
  1361. else if( hr == DRAGDROP_S_DROP && _ped->GetCallMgr()->GetChangeEvent() &&
  1362. (dwEffect & DROPEFFECT_COPY) && publdr)
  1363. {
  1364. // if we copied to ourselves, we want to restore the selection to
  1365. // the original drag origin on undo
  1366. HandleSelectionAEInfo(_ped, publdr, cp1, cch1, -1, -1,
  1367. SELAE_FORCEREPLACE);
  1368. }
  1369. if(SUCCEEDED(hr))
  1370. hr = NOERROR;
  1371. pdo->Release();
  1372. pds->Release();
  1373. // we do this last since we may have re-used some 'paste' code which
  1374. // will stomp the undo name to be UID_PASTE.
  1375. if( publdr )
  1376. publdr->SetNameID(UID_DRAGDROP);
  1377. if(_ped->GetEventMask() & ENM_DRAGDROPDONE)
  1378. {
  1379. NMHDR hdr;
  1380. ZeroMemory(&hdr, sizeof(NMHDR));
  1381. _ped->TxNotify(EN_DRAGDROPDONE, &hdr);
  1382. }
  1383. return hr;
  1384. #else
  1385. return 0;
  1386. #endif // NODRAGDROP
  1387. }
  1388. /*
  1389. * CLightDTEngine::LoadFromEs (prg, lStreamFormat, pes, fTestLimit, publdr)
  1390. *
  1391. * @mfunc
  1392. * Load data from the stream pes into the range prg according to the
  1393. * format lStreamFormat
  1394. *
  1395. * @rdesc
  1396. * LONG -- count of characters read
  1397. */
  1398. LONG CLightDTEngine::LoadFromEs(
  1399. CTxtRange * prg, // @parm Range to load into
  1400. LONG lStreamFormat, // @parm Stream format to use for loading
  1401. EDITSTREAM * pes, // @parm Edit stream to load from
  1402. BOOL fTestLimit, // @parm Whether to test text limit
  1403. IUndoBuilder *publdr) // @parm Undo builder to receive antievents
  1404. {
  1405. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::LoadFromEs");
  1406. #ifdef DEBUG
  1407. // FUTURE: Currently freezing the display prior to loading text
  1408. // is simply an optimization. This may become a requirement in the
  1409. // future. If this does become a requirement then we'll want to
  1410. // exit with an error.
  1411. if( !_ped->_pdp->IsFrozen() )
  1412. {
  1413. TRACEWARNSZ("CLightDTEngine::LoadFromEs display not frozen");
  1414. }
  1415. #endif // DEBUG
  1416. LONG cch = 0; // Default no chars read
  1417. IAntiEvent *pae = NULL;
  1418. if(publdr)
  1419. publdr->StopGroupTyping();
  1420. // Other components, such as the display and backing store, will
  1421. // be able to make optimizations if they know that we are streaming
  1422. // in text or RTF data.
  1423. prg->CheckTableSelection(FALSE, TRUE, NULL, 0);
  1424. if(lStreamFormat & SF_RTF) // RTF case must precede
  1425. { // TEXT case (see SF_x values)
  1426. if(!_ped->IsRich())
  1427. return 0;
  1428. LONG cpMin, cpMost;
  1429. // Here we do something a bit unusual for performance reasons.
  1430. // Instead of letting the rtf reader generate its own undo actions,
  1431. // we take care of it ourselves. Instead of generating actions
  1432. // for each little operation, we simply generate a "big" antievent
  1433. // for the whole shebang
  1434. // There is a subtlty w.r.t. to paragraph format runs. By inserting
  1435. // text with para formatting, it's possible that we will modify the
  1436. // para formatting of the _current_ paragraph. Thus, it's necessary
  1437. // to remember what the formatting currently is for undo. Note that
  1438. // it may actually not be changed; but we go ahead and generate an
  1439. // antievent anyways. Note that we only need to do this if cpMin is
  1440. // the middle of a paragraph
  1441. CTxtPtr tp(prg->_rpTX);
  1442. if(prg->GetCch() > 0)
  1443. tp.Move(-prg->GetCch());
  1444. if(publdr && !tp.IsAfterEOP())
  1445. {
  1446. tp.FindEOP(tomBackward);
  1447. cpMin = tp.GetCp();
  1448. tp.FindEOP(tomForward);
  1449. cpMost = tp.GetCp();
  1450. // We must be in rich text mode, so we must be able to always
  1451. // find a paragraph.
  1452. Assert(cpMost > cpMin);
  1453. if (prg->_rpPF.IsValid())
  1454. {
  1455. CFormatRunPtr rpPF(prg->_rpPF);
  1456. rpPF.Move(cpMin - prg->GetCp());
  1457. pae = gAEDispenser.CreateReplaceFormattingAE(_ped, cpMin, rpPF,
  1458. cpMost - cpMin, GetParaFormatCache(), ParaFormat);
  1459. if(pae)
  1460. publdr->AddAntiEvent(pae);
  1461. }
  1462. // Also create the charformat antievent for the current paragraph
  1463. // to preserve BiDi level. We cannot check fBiDi here since we may be running
  1464. // on US platform inserting a BiDi rtf.
  1465. if (prg->_rpCF.IsValid())
  1466. {
  1467. CFormatRunPtr rpCF(prg->_rpCF);
  1468. rpCF.Move(cpMin - prg->GetCp());
  1469. pae = gAEDispenser.CreateReplaceFormattingAE(_ped, cpMin, rpCF,
  1470. cpMost - cpMin, GetCharFormatCache(), CharFormat);
  1471. if(pae)
  1472. publdr->AddAntiEvent(pae);
  1473. }
  1474. }
  1475. // First, clear range
  1476. LONG cchEOP = prg->DeleteWithTRDCheck(publdr, SELRR_REMEMBERRANGE, NULL,
  1477. RR_NO_LP_CHECK | RR_NO_CHECK_TABLE_SEL);
  1478. if(prg->GetCch())
  1479. return 0; // Deletion suppressed, so
  1480. // can't insert text
  1481. cpMin = prg->GetCp();
  1482. _ped->SetStreaming(TRUE);
  1483. CRTFRead rtfRead(prg, pes, lStreamFormat);
  1484. cch = rtfRead.ReadRtf();
  1485. if(prg->_rpTX.IsAfterEOP() && (cchEOP ||// Inserted text ends with EOP
  1486. prg->_rpTX.GetChar() == CELL && // OK to delete EOP if we
  1487. cch && !prg->_rpTX.IsAfterTRD(0))) // inserted one or if before
  1488. { // CELL
  1489. if(cchEOP)
  1490. {
  1491. Assert(prg->_rpTX.GetChar() == CR);
  1492. prg->AdvanceCRLF(CSC_NORMAL, TRUE);
  1493. }
  1494. else
  1495. prg->BackupCRLF(CSC_NORMAL, TRUE);
  1496. prg->ReplaceRange(0, NULL, NULL, SELRR_REMEMBERRANGE);
  1497. }
  1498. cpMost = prg->GetCp();
  1499. Assert(pes->dwError != 0 || cpMost >= cpMin);
  1500. // If nothing changed, get rid of any antievents (like the formatting
  1501. // one) that we may have "speculatively" added
  1502. if(publdr && !_ped->GetCallMgr()->GetChangeEvent())
  1503. publdr->Discard();
  1504. if(publdr && cpMost > cpMin)
  1505. {
  1506. // If some text was added, create an antievent for
  1507. // it and add it in.
  1508. AssertSz(_ped->GetCallMgr()->GetChangeEvent(),
  1509. "Something changed, but nobody set the change flag");
  1510. pae = gAEDispenser.CreateReplaceRangeAE(_ped, cpMin, cpMost, 0,
  1511. NULL, NULL, NULL);
  1512. HandleSelectionAEInfo(_ped, publdr, -1, -1, cpMost, 0,
  1513. SELAE_FORCEREPLACE);
  1514. if(pae)
  1515. publdr->AddAntiEvent(pae);
  1516. }
  1517. }
  1518. else if(lStreamFormat & SF_TEXT)
  1519. {
  1520. _ped->SetStreaming(TRUE);
  1521. cch = ReadPlainText(prg, pes, fTestLimit, publdr, lStreamFormat);
  1522. }
  1523. _ped->SetStreaming(FALSE);
  1524. // Before updating the selection, try the auto-URL detect. This makes
  1525. // two cases better: 1. a long drag drop is now faster and 2. the
  1526. // selection _iFormat will now be udpated correctly for cases of
  1527. // copy/paste of a URL.
  1528. if(_ped->GetDetectURL())
  1529. _ped->GetDetectURL()->ScanAndUpdate(publdr);
  1530. // The caret belongs in one of two places:
  1531. // 1. if we loaded into a selection, at the end of the new text
  1532. // 2. otherwise, we loaded an entire document, set it to cp 0
  1533. //
  1534. // ReadPlainText() and ReadRtf() set prg to an insertion point
  1535. // at the end, so if we loaded a whole document, reset it.
  1536. CTxtSelection *psel = _ped->GetSelNC();
  1537. if(psel)
  1538. {
  1539. if(!(lStreamFormat & SFF_SELECTION))
  1540. {
  1541. psel->Set(0,0);
  1542. psel->Update(FALSE);
  1543. }
  1544. psel->Update_iFormat(-1);
  1545. }
  1546. if (!fTestLimit)
  1547. {
  1548. // If we don't limit the text then we adjust the text limit
  1549. // if we have exceeded it.
  1550. _ped->TxSetMaxToMaxText();
  1551. }
  1552. return cch;
  1553. }
  1554. /*
  1555. * CLightDTEngine::SaveToEs (prg, lStreamFormat, pes)
  1556. *
  1557. * @mfunc
  1558. * save data into the given stream
  1559. *
  1560. * @rdesc
  1561. * LONG -- count of characters written
  1562. */
  1563. LONG CLightDTEngine::SaveToEs(
  1564. CTxtRange * prg, //@parm Range to drag from
  1565. LONG lStreamFormat, //@parm Stream format to use for saving
  1566. EDITSTREAM *pes ) //@parm Edit stream to save to
  1567. {
  1568. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::SaveToEs");
  1569. LONG cch = 0; // Default no chars written
  1570. prg->AdjustCRLF(1);
  1571. if(lStreamFormat & SF_RTF) // Be sure to check for SF_RTF
  1572. { // before checking for SF_TEXT
  1573. if(prg->CheckTableSelection(FALSE, TRUE, NULL, 0))
  1574. lStreamFormat |= SFF_WRITEXTRAPAR; // Signal to write \par for CELL
  1575. CRTFWrite rtfWrite( prg, pes, lStreamFormat );
  1576. cch = rtfWrite.WriteRtf();
  1577. }
  1578. else if(lStreamFormat & (SF_TEXT | SF_TEXTIZED))
  1579. cch = WritePlainText(prg, pes, lStreamFormat);
  1580. else
  1581. {
  1582. Assert(FALSE);
  1583. }
  1584. return cch;
  1585. }
  1586. /*
  1587. * CLightDTEngine::UnicodePlainTextFromRange (prg)
  1588. *
  1589. * @mfunc
  1590. * Fetch plain text from a range and put it in an hglobal
  1591. *
  1592. * @rdesc
  1593. * an allocated HGLOBAL.
  1594. *
  1595. * @devnote
  1596. * FUTURE: Export bullets as does Word for plain text
  1597. */
  1598. HGLOBAL CLightDTEngine::UnicodePlainTextFromRange(
  1599. CTxtRange *prg) // @parm range to get text from
  1600. {
  1601. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::UnicodePlainTextFromRange");
  1602. LONG cpMin, cpMost;
  1603. LONG cch = prg->GetRange(cpMin, cpMost);
  1604. LONG cchT = 2*(cch + 1); // Allocate 2* in case all CRs
  1605. HGLOBAL hText = GlobalAlloc(GMEM_FIXED, cchT*sizeof(WCHAR));
  1606. if(!hText)
  1607. return NULL;
  1608. WCHAR *pText = (WCHAR *)GlobalLock(hText);
  1609. if(!pText)
  1610. return NULL;
  1611. if(cch)
  1612. {
  1613. CRchTxtPtr rtp(*prg);
  1614. rtp.SetCp(cpMin);
  1615. cch = rtp.GetPlainText(cchT, pText, cpMost, FALSE, TRUE);
  1616. AssertSz(cch <= cchT,
  1617. "CLightDTEngine::UnicodePlainTextFromRange: got too much text");
  1618. }
  1619. *(pText + cch) = 0;
  1620. GlobalUnlock(hText);
  1621. HGLOBAL hTemp = GlobalReAlloc(hText, 2*(cch + 1), GMEM_MOVEABLE);
  1622. if(!hTemp)
  1623. GlobalFree(hText);
  1624. return hTemp;
  1625. }
  1626. /*
  1627. * CLightDTEngine::AnsiPlainTextFromRange (prg)
  1628. *
  1629. * @mfunc
  1630. * Retrieve an ANSI copy of the text in the range prg
  1631. *
  1632. * @rdesc
  1633. * HRESULT
  1634. */
  1635. HGLOBAL CLightDTEngine::AnsiPlainTextFromRange(
  1636. CTxtRange *prg) // @parm range to get text from
  1637. {
  1638. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::AnsiPlainTextFromRange");
  1639. HGLOBAL hUnicode;
  1640. HGLOBAL hAnsi;
  1641. // FUTURE (alexgo): if we implement the option to store text as 8-bit
  1642. // chars, then we can make this routine more efficient
  1643. hUnicode = UnicodePlainTextFromRange(prg);
  1644. hAnsi = TextHGlobalWtoA(hUnicode);
  1645. GlobalFree(hUnicode);
  1646. return hAnsi;
  1647. }
  1648. /*
  1649. * CLightDTEngine::RtfFromRange (prg, lStreamFormat)
  1650. *
  1651. * @mfunc
  1652. * Fetch RTF text from a range and put it in an hglobal
  1653. *
  1654. * @rdesc
  1655. * an allocated HGLOBAL.
  1656. */
  1657. HGLOBAL CLightDTEngine::RtfFromRange(
  1658. CTxtRange * prg, // @parm Range to get RTF from
  1659. LONG lStreamFormat) // @parm stream format to use for loading
  1660. {
  1661. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::RtfFromRange");
  1662. WRITEHGLOBAL whg;
  1663. EDITSTREAM es = {(DWORD_PTR)&whg, NOERROR, WriteHGlobal};
  1664. DWORD cb = 2*abs(prg->GetCch()) + 100; // Rough estimate
  1665. whg.cb = cb;
  1666. whg.hglobal = GlobalAlloc(GMEM_FIXED, cb);
  1667. if(!whg.hglobal)
  1668. return NULL;
  1669. whg.cch = 0; // Nothing written yet
  1670. SaveToEs(prg, lStreamFormat & ~SF_TEXT, &es);
  1671. if(es.dwError)
  1672. {
  1673. GlobalFree(whg.hglobal);
  1674. return NULL;
  1675. }
  1676. HGLOBAL hTemp = GlobalReAlloc(whg.hglobal, whg.cch, GMEM_MOVEABLE);
  1677. if (!hTemp)
  1678. GlobalFree(whg.hglobal); // Fail ReAlloc...
  1679. return hTemp;
  1680. }
  1681. //
  1682. // PROTECTED METHODS
  1683. //
  1684. #define READSIZE 4096 - 2
  1685. #define WRITESIZE 2048
  1686. /*
  1687. * CLightDTEngine::ReadPlainText (prg, pes, publdr, lStreamFormat)
  1688. *
  1689. * @mfunc
  1690. * Replaces contents of the range prg with the data given in the edit
  1691. * stream pes. Handles multibyte sequences that overlap stream buffers.
  1692. *
  1693. * @rdesc
  1694. * Count of bytes read (to be compatible with RichEdit 1.0)
  1695. *
  1696. * @devnote
  1697. * prg is modified; at the return of the call, it will be a degenerate
  1698. * range at the end of the read in text.
  1699. *
  1700. * Three kinds of multibyte/char sequences can overlap stream buffers:
  1701. * DBCS, UTF-8, and CRLF/CRCRLF combinations. DBCS and UTF-8 streams are
  1702. * converted by MultiByteToWideChar(), which cannot convert a lead byte
  1703. * (DBCS and UTF-8) that occurs at the end of the buffer, since the
  1704. * corresponding trail byte(s) will be in the next buffer. Similarly,
  1705. * in RichEdit 2.0 mode, we convert CRLFs to CRs and CRCRLFs to blanks,
  1706. * so one or two CRs at the end of the buffer require knowledge of the
  1707. * following char to determine if they are part of a CRLF or CRCRLF.
  1708. *
  1709. * To handle these overlapped buffer cases, we move the ambiguous chars
  1710. * to the start of the next buffer, rather than keeping them as part of
  1711. * the current buffer. At the start of the buffer, the extra char(s)
  1712. * needed for translation follow immediately.
  1713. */
  1714. LONG CLightDTEngine::ReadPlainText(
  1715. CTxtRange * prg, // @parm range to read to
  1716. EDITSTREAM * pes, // @parm edit stream to read from
  1717. BOOL fTestLimit, // @parm whether limit testing is needed
  1718. IUndoBuilder *publdr, // @parm undo builder to receive antievents
  1719. LONG lStreamFormat)// @parm Stream format
  1720. {
  1721. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::ReadPlainText");
  1722. CTxtEdit *ped = _ped;
  1723. LONG cbRead;
  1724. LONG cbReadTotal = 0; // No bytes read yet
  1725. LONG cchConv;
  1726. LONG cchMove = 0;
  1727. LONG cCR = 0; // Count of CRs from preceding buffer
  1728. LONG cCRPrev = 0; // Count used while calc'ing new cCR
  1729. LONG cpMin;
  1730. BOOL fContinue = TRUE; // Keep reading so long as TRUE
  1731. BYTE * pb; // Byte ptr to szBuf or wszBuf
  1732. CCallMgr *pCallMgr = ped->GetCallMgr();
  1733. WCHAR * pch; // Ptr to wszBuf
  1734. UINT uCpg = GetStreamCodePage(lStreamFormat);
  1735. CFreezeDisplay fd(ped->_pdp);
  1736. // Just put a big buffer on the stack. Thankfully, we only
  1737. // run on 32bit OS's. 4K is a good read size for NT file caching.
  1738. char szBuf[READSIZE];
  1739. WCHAR wszBuf[READSIZE+2]; // Allow for moving end CRs to start
  1740. // Empty the range
  1741. prg->DeleteWithTRDCheck(publdr, SELRR_REMEMBERRANGE, &cchMove, 0);
  1742. cpMin = prg->GetCp(); // Save initial cp for
  1743. // BreakRuns() at end
  1744. pb = (IsUnicodeCP(uCpg)) ? (BYTE *)(wszBuf + 2) // Setup Unicode or MBCS
  1745. : (BYTE *)szBuf;
  1746. LONG j = 0; // Haven't read anything,
  1747. // so no lead byte left
  1748. while(fContinue) // from previous read
  1749. {
  1750. LONG jPrev = j; // Save byte(s) left over
  1751. LONG cbSkip = 0; // from previous read
  1752. pes->dwError = (*pes->pfnCallback)( // Read next bufferful,
  1753. pes->dwCookie, pb + j, // bypassing any lead
  1754. READSIZE - j, &cbRead); // bytes
  1755. if(pes->dwError || !cbRead && !cCR)
  1756. break; // Error or done
  1757. if(!cbReadTotal && cbRead >= 3 && W32->IsUTF8BOM(pb))
  1758. {
  1759. uCpg = CP_UTF8;
  1760. cbSkip = 3; // Bypass 3 bytes
  1761. }
  1762. // Adjust cbRead with previous leading byte(s)
  1763. cbRead += j;
  1764. j = 0;
  1765. cchConv = cbRead/2; // Default Unicode cch
  1766. if(uCpg == CP_UBE) // Big Endian Unicode
  1767. {
  1768. WORD *pch = &wszBuf[2];
  1769. for(LONG j = 0; j < cchConv; j++) // Convert to little endian
  1770. *pch++ = (*pch >> 8) + (*pch << 8);
  1771. }
  1772. else if(uCpg != CP_ULE && cbRead) // Multibyte of some kind
  1773. {
  1774. Assert(pb == (BYTE *)szBuf && !j); // Just in case...
  1775. // Check if last byte is a leading byte
  1776. if(uCpg == CP_UTF8)
  1777. {
  1778. // Note: Unlike UTF-8, UTF-7 can be in the middle of a long
  1779. // sequence, so it can't be converted effectively in chunks
  1780. // and we don't handle it
  1781. LONG cb = cbRead - 1;
  1782. BYTE b;
  1783. BYTE bLeadMax = 0xDF;
  1784. // Find UTF-8 lead byte
  1785. while((b = (BYTE)szBuf[cb - j]) >= 0x80)
  1786. {
  1787. j++;
  1788. if(b >= 0xC0) // Break on UTF-8 lead
  1789. { // byte
  1790. if(j > 1 && (b <= bLeadMax || b >= 0xF8))
  1791. j = 0; // Full UTF-8 char or
  1792. break; // illegal sequence
  1793. }
  1794. if(j > 1)
  1795. {
  1796. if(j == 5) // Illegal UTF-8
  1797. {
  1798. j = 0;
  1799. break;
  1800. }
  1801. *(char *)&bLeadMax >>= 1;
  1802. }
  1803. }
  1804. }
  1805. else
  1806. {
  1807. LONG temp = cbRead - 1;
  1808. // GetTrailBytesCount() can return 1 for some trail bytes
  1809. // esp. for GBX. So, we need to keep on checking until
  1810. // we hit a non-lead byte character. Then, based on
  1811. // how many bytes we went back, we can determine if the
  1812. // last byte is really a Lead byte.
  1813. while (temp && GetTrailBytesCount((BYTE)szBuf[temp], uCpg))
  1814. temp--;
  1815. if(temp && ((cbRead-1-temp) & 1))
  1816. j = 1;
  1817. }
  1818. // We don't want to pass the lead byte or partial UTF-8 to
  1819. // MultiByteToWideChar() because it will return bad char.
  1820. cchConv = MBTWC(uCpg, 0, szBuf + cbSkip, cbRead - j - cbSkip,
  1821. &wszBuf[2], READSIZE, NULL);
  1822. for(LONG i = j; i; i--) // Copy down partial
  1823. szBuf[j - i] = szBuf[cbRead - i]; // multibyte sequence
  1824. }
  1825. cbReadTotal += cbRead - j - jPrev;
  1826. // Cleanse (CRLFs -> CRs, etc.), limit, and insert the data. Have
  1827. // to handle CRLFs and CRCRLFs that overlap two successive buffers.
  1828. Assert(cCR <= 2);
  1829. pch = &wszBuf[2 - cCR]; // Include CRs from prev
  1830. if(!ped->_pdp->IsMultiLine()) // Single-line control
  1831. {
  1832. Assert(!cCR);
  1833. }
  1834. else
  1835. {
  1836. wszBuf[0] = wszBuf[1] = CR; // Store CRs for cchCR > 0
  1837. cCRPrev = cCR; // Save prev cchCR
  1838. cCR = 0; // Default no CR this buf
  1839. Assert(ARRAY_SIZE(wszBuf) >= cchConv + 2);
  1840. // Need to +2 since we are moving data into wszBuf[2]
  1841. if(cchConv && wszBuf[cchConv + 2 - 1] == CR)
  1842. { // There's at least one
  1843. cCR++; // Set it up for next buf
  1844. if (cchConv > 1 && // in case CR of CRLF
  1845. wszBuf[cchConv + 2 - 2] == CR) // Got 2nd CR; might be
  1846. { // first CR of CRCRLF so
  1847. cCR++; // setup for next buffer
  1848. }
  1849. }
  1850. cchConv += cCRPrev - cCR; // Add in count from prev
  1851. } // next
  1852. Assert(!prg->GetCch()); // Range is IP
  1853. prg->CleanseAndReplaceRange(cchConv, pch, fTestLimit, publdr, pch, NULL, RR_ITMZ_NONE);
  1854. if(pCallMgr->GetMaxText() || pCallMgr->GetOutOfMemory())
  1855. {
  1856. // Out of memory or reached the max size of our text control.
  1857. // In either case, return STG_E_MEDIUMFULL (for compatibility
  1858. // with RichEdit 1.0)
  1859. pes->dwError = (DWORD)STG_E_MEDIUMFULL;
  1860. break;
  1861. }
  1862. }
  1863. prg->ItemizeReplaceRange(prg->GetCp() - cpMin, cchMove, publdr, TRUE);
  1864. return cbReadTotal;
  1865. }
  1866. /*
  1867. * CLightDTEngine::WritePlainText (prg, pes, lStreamFormat)
  1868. *
  1869. * @mfunc
  1870. * Writes plain text from the range into the given edit stream
  1871. *
  1872. * @rdesc
  1873. * Count of bytes written
  1874. */
  1875. LONG CLightDTEngine::WritePlainText(
  1876. CTxtRange * prg, // @parm range to write from
  1877. EDITSTREAM *pes, // @parm edit stream to write to
  1878. LONG lStreamFormat) // @parm Stream format
  1879. {
  1880. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::WritePlainText");
  1881. LONG cbConverted; // Bytes for output stream
  1882. LONG cbWrite; // Incremental byte count
  1883. LONG cbWriteTotal = 0; // No chars written yet
  1884. LONG cpMin, cpMost;
  1885. LONG cch = prg->GetRange(cpMin, cpMost);
  1886. BOOL fTextize = lStreamFormat & SF_TEXTIZED;
  1887. LPBYTE pb; // Byte ptr to szBuf or wszBuf
  1888. COleObject *pobj; // Ptr to embedded object
  1889. CRchTxtPtr rtp(*prg); // rtp to walk prg with
  1890. UINT uCpg = GetStreamCodePage(lStreamFormat);
  1891. // DBCS has up to 2 times as many chars as WCHARs. UTF-8 has 3 BYTES for
  1892. // all codes above 0x7ff. UTF-7 has even more due to shift in/out codes.
  1893. // We don't support UTF-7, since can't use WCTMB with UTF-7 chunks
  1894. char szBuf[3*WRITESIZE]; // Factor of 2 works with DBCS, 3 with UTF-8
  1895. WCHAR wszBuf[WRITESIZE];
  1896. pes->dwError = NOERROR; // No error yet
  1897. pb = IsUnicodeCP(uCpg) ? (BYTE *)wszBuf // Setup Unicode or MBCS
  1898. : (BYTE *)szBuf;
  1899. LONG cchText = _ped->GetAdjustedTextLength();
  1900. cpMost = min(cpMost, cchText); // Don't write final CR
  1901. rtp.SetCp(cpMin);
  1902. while(rtp.GetCp() < cpMost)
  1903. {
  1904. if (fTextize && rtp.GetChar() == WCH_EMBEDDING)
  1905. {
  1906. Assert(_ped->GetObjectCount());
  1907. pobj = _ped->GetObjectMgr()->GetObjectFromCp(rtp.GetCp());
  1908. rtp.Move(1); // Move past object
  1909. if(pobj)
  1910. {
  1911. cbWriteTotal += pobj->WriteTextInfoToEditStream(pes, uCpg);
  1912. continue; // If no object at cp,
  1913. } // just ignore char
  1914. }
  1915. cch = rtp.GetPlainText(WRITESIZE, wszBuf, cpMost, fTextize, TRUE);
  1916. if(!cch)
  1917. break; // No more to do
  1918. cbConverted = 2*cch; // Default Unicode byte ct
  1919. if(uCpg == CP_UBE) // Big Endian Unicode
  1920. {
  1921. WORD *pch = &wszBuf[0];
  1922. for(LONG j = 0; j < cch; j++) // Convert to little endian
  1923. *pch++ = (*pch >> 8) + (*pch << 8);
  1924. }
  1925. else if(uCpg != CP_ULE) // Multibyte of some kind
  1926. {
  1927. cbConverted = MbcsFromUnicode(szBuf, 3*WRITESIZE, wszBuf, cch, uCpg,
  1928. UN_CONVERT_WCH_EMBEDDING);
  1929. // FUTURE: report some kind of error if default char used,
  1930. // i.e., data lost in conversion
  1931. // Did the conversion completely fail? As a fallback, we might try
  1932. // the system code page, or just plain ANSI...
  1933. if (!cbConverted)
  1934. {
  1935. uCpg = CodePageFromCharRep(GetLocaleCharRep());
  1936. cbConverted = MbcsFromUnicode(szBuf, 3*WRITESIZE, wszBuf, cch, uCpg,
  1937. UN_CONVERT_WCH_EMBEDDING);
  1938. }
  1939. if (!cbConverted)
  1940. {
  1941. uCpg = CP_ACP;
  1942. cbConverted = MbcsFromUnicode(szBuf, 3*WRITESIZE, wszBuf, cch, uCpg,
  1943. UN_CONVERT_WCH_EMBEDDING);
  1944. }
  1945. }
  1946. pes->dwError = (*pes->pfnCallback)(pes->dwCookie, pb,
  1947. cbConverted, &cbWrite);
  1948. if(!pes->dwError && cbConverted != cbWrite) // Error or ran out of
  1949. pes->dwError = (DWORD)STG_E_MEDIUMFULL; // target storage
  1950. if(pes->dwError)
  1951. break;
  1952. cbWriteTotal += cbWrite;
  1953. }
  1954. AssertSz(rtp.GetCp() >= cpMost,
  1955. "CLightDTEngine::WritePlainText: not all text written");
  1956. return cbWriteTotal;
  1957. }
  1958. /*
  1959. * CLightDTEngine::GetStreamCodePage (lStreamFormat)
  1960. *
  1961. * @mfunc
  1962. * Returns code page given by lStreamFormat or CTxtEdit::_pDocInfo
  1963. *
  1964. * @rdesc
  1965. * HRESULT
  1966. */
  1967. LONG CLightDTEngine::GetStreamCodePage(
  1968. LONG lStreamFormat)
  1969. {
  1970. if(lStreamFormat & SF_UNICODE)
  1971. return CP_ULE;
  1972. if(lStreamFormat & SF_USECODEPAGE)
  1973. return HIWORD(lStreamFormat);
  1974. if (W32->IsFESystem())
  1975. return GetACP();
  1976. return CP_ACP;
  1977. }
  1978. /*
  1979. * CLightDTEngine::CreateOleObjFromDataObj ( pdo, prg, rps, iformatetc, pubdlr )
  1980. *
  1981. * @mfunc
  1982. * Creates an ole object based on the data object pdo, and
  1983. * pastes the object into the range prg. Any text that already
  1984. * existed in the range is replaced.
  1985. *
  1986. * @rdesc
  1987. * HRESULT
  1988. */
  1989. HRESULT CLightDTEngine::CreateOleObjFromDataObj(
  1990. IDataObject * pdo, // @parm Data object from which to create
  1991. CTxtRange * prg, // @parm Range in which to place
  1992. REPASTESPECIAL *rps, // @parm Special paste info
  1993. INT iformatetc, // @parm Index in g_rgFETC
  1994. IUndoBuilder * publdr) // @parm Undo builder to receive antievents
  1995. {
  1996. TRACEBEGIN(TRCSUBSYSDTE, TRCSCOPEINTERN, "CLightDTEngine::CreateOleObjFromDataObj");
  1997. HRESULT hr = NOERROR;
  1998. REOBJECT reobj;
  1999. SIZEL sizel;
  2000. FORMATETC formatetc;
  2001. DWORD dwDrawAspect = 0;
  2002. HGLOBAL hMetaPict = NULL;
  2003. LPRICHEDITOLECALLBACK const precall = _ped->GetRECallback();
  2004. LPOBJECTDESCRIPTOR lpod = NULL;
  2005. STGMEDIUM medObjDesc;
  2006. BOOL fStatic = (iformatetc == iMfPict || iformatetc == iDIB ||
  2007. iformatetc == iBitmap);
  2008. BOOL fFilename = (iformatetc == iFilename);
  2009. DUAL_FORMATETC tmpFormatEtc;
  2010. if(!precall)
  2011. return E_NOINTERFACE;
  2012. ZeroMemory(&medObjDesc, sizeof(STGMEDIUM));
  2013. ZeroMemory(&sizel, sizeof(SIZEL));
  2014. ZeroMemory(&reobj, sizeof(REOBJECT));
  2015. if(fStatic)
  2016. dwDrawAspect = DVASPECT_CONTENT;
  2017. if(fFilename)
  2018. dwDrawAspect = DVASPECT_ICON;
  2019. if(rps && !dwDrawAspect)
  2020. {
  2021. dwDrawAspect = rps->dwAspect;
  2022. if(rps->dwAspect == DVASPECT_ICON)
  2023. hMetaPict = (HGLOBAL)rps->dwParam;
  2024. }
  2025. // If no aspect was specified, pick up the object descriptor hints
  2026. if(!dwDrawAspect)
  2027. {
  2028. // Define ObjectDescriptor data
  2029. formatetc.cfFormat = cf_OBJECTDESCRIPTOR;
  2030. formatetc.ptd = NULL;
  2031. formatetc.dwAspect = DVASPECT_CONTENT;
  2032. formatetc.lindex = -1;
  2033. formatetc.tymed = TYMED_HGLOBAL;
  2034. if(pdo->GetData(&formatetc, &medObjDesc) == NOERROR)
  2035. {
  2036. HANDLE hGlobal = medObjDesc.hGlobal;
  2037. lpod = (LPOBJECTDESCRIPTOR)GlobalLock(hGlobal);
  2038. if(lpod)
  2039. {
  2040. dwDrawAspect = lpod->dwDrawAspect;
  2041. }
  2042. GlobalUnlock(hGlobal);
  2043. ReleaseStgMedium(&medObjDesc);
  2044. }
  2045. }
  2046. if(!dwDrawAspect)
  2047. dwDrawAspect = DVASPECT_CONTENT;
  2048. if(fStatic)
  2049. {
  2050. reobj.clsid = ((iformatetc == iMfPict) ?
  2051. CLSID_StaticMetafile : CLSID_StaticDib);
  2052. }
  2053. // COMPATIBILITY ISSUE: Compatibility Issue from Richedit 1.0 - Raid 16456:
  2054. // Don't call GetData(CF_EMBEDSOURCE)
  2055. // on 32-bit Excel. Also clsidPictPub.
  2056. // if(iformatetc == iformatetcEmbSrc && (ObFIsExcel(&clsid) ||
  2057. // IsEqualCLSID(&clsid, &clsidPictPub)))
  2058. // else
  2059. // ObGetStgFromDataObj(pdataobj, &medEmbed, iformatetc);
  2060. // Get storage for the object from the application
  2061. hr = precall->GetNewStorage(&reobj.pstg);
  2062. if(hr)
  2063. {
  2064. TRACEERRORSZ("GetNewStorage() failed.");
  2065. goto err;
  2066. }
  2067. // Create an object site for the new object
  2068. hr = _ped->GetClientSite(&reobj.polesite);
  2069. if(!reobj.polesite)
  2070. {
  2071. TRACEERRORSZ("GetClientSite() failed.");
  2072. goto err;
  2073. }
  2074. ZeroMemory(&tmpFormatEtc, sizeof(DUAL_FORMATETC));
  2075. tmpFormatEtc.ptd = NULL;
  2076. tmpFormatEtc.dwAspect = dwDrawAspect;
  2077. tmpFormatEtc.lindex = -1;
  2078. //Create the object
  2079. if(fStatic)
  2080. {
  2081. hr = OleCreateStaticFromData(pdo, IID_IOleObject, OLERENDER_DRAW,
  2082. &tmpFormatEtc, NULL, reobj.pstg, (LPVOID*)&reobj.poleobj);
  2083. }
  2084. else if(iformatetc == iLnkSrc || (_ped->Get10Mode() && iformatetc == iFilename))
  2085. {
  2086. hr = OleCreateLinkFromData(pdo, IID_IOleObject, OLERENDER_DRAW,
  2087. &tmpFormatEtc, NULL, reobj.pstg, (LPVOID*)&reobj.poleobj);
  2088. }
  2089. else
  2090. {
  2091. hr = OleCreateFromData(pdo, IID_IOleObject, OLERENDER_DRAW,
  2092. &tmpFormatEtc, NULL, reobj.pstg, (LPVOID*)&reobj.poleobj);
  2093. }
  2094. if(hr)
  2095. {
  2096. TRACEERRORSZ("Failure creating object.");
  2097. goto err;
  2098. }
  2099. //Get the clsid of the object.
  2100. if(!fStatic)
  2101. {
  2102. hr = reobj.poleobj->GetUserClassID(&reobj.clsid);
  2103. if(hr)
  2104. {
  2105. TRACEERRORSZ("GetUserClassID() failed.");
  2106. goto err;
  2107. }
  2108. }
  2109. //Deal with iconic aspect if specified.
  2110. if(hMetaPict)
  2111. {
  2112. BOOL fUpdate;
  2113. hr = OleStdSwitchDisplayAspect(reobj.poleobj, &dwDrawAspect,
  2114. DVASPECT_ICON, hMetaPict, FALSE,
  2115. FALSE, NULL, &fUpdate);
  2116. if(hr)
  2117. {
  2118. TRACEERRORSZ("OleStdSwitchDisplayAspect() failed.");
  2119. goto err;
  2120. }
  2121. // If we successully changed the aspect, recompute the size.
  2122. hr = reobj.poleobj->GetExtent(dwDrawAspect, &sizel);
  2123. if(hr)
  2124. {
  2125. TRACEERRORSZ("GetExtent() failed.");
  2126. goto err;
  2127. }
  2128. }
  2129. // Try to retrieve the previous saved RichEdit site flags.
  2130. if( ObjectReadSiteFlags(&reobj) != NOERROR )
  2131. {
  2132. // Set default for site flags
  2133. reobj.dwFlags = REO_RESIZABLE;
  2134. }
  2135. // First, clear the range
  2136. prg->Delete(publdr, SELRR_REMEMBERRANGE);
  2137. reobj.cbStruct = sizeof(REOBJECT);
  2138. reobj.cp = prg->GetCp();
  2139. reobj.dvaspect = dwDrawAspect;
  2140. reobj.sizel = sizel;
  2141. //COMPATIBILITY ISSUE: from Richedit 1.0 - don't Set the Extent,
  2142. //instead Get the Extent below in ObFAddObjectSite
  2143. //hr = reobj.poleobj->SetExtent(dwDrawAspect, &sizel);
  2144. hr = reobj.poleobj->SetClientSite(reobj.polesite);
  2145. if(hr)
  2146. {
  2147. TRACEERRORSZ("SetClientSite() failed.");
  2148. goto err;
  2149. }
  2150. if(hr = _ped->InsertObject(&reobj))
  2151. {
  2152. TRACEERRORSZ("InsertObject() failed.");
  2153. }
  2154. err:
  2155. if(reobj.poleobj)
  2156. reobj.poleobj->Release();
  2157. if(reobj.polesite)
  2158. reobj.polesite->Release();
  2159. if(reobj.pstg)
  2160. reobj.pstg->Release();
  2161. return hr;
  2162. }