Leaked source code of windows server 2003
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.

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