2 * Richedit clipboard handling
4 * Copyright (C) 2006 Kevin Koltzau
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define NONAMELESSUNION
25 WINE_DEFAULT_DEBUG_CHANNEL(richedit
);
27 static UINT cfRTF
= 0;
29 typedef struct DataObjectImpl
{
30 IDataObject IDataObject_iface
;
40 typedef struct EnumFormatImpl
{
41 IEnumFORMATETC IEnumFORMATETC_iface
;
50 static HRESULT
EnumFormatImpl_Create(const FORMATETC
*fmtetc
, UINT size
, LPENUMFORMATETC
*lplpformatetc
);
52 static inline DataObjectImpl
*impl_from_IDataObject(IDataObject
*iface
)
54 return CONTAINING_RECORD(iface
, DataObjectImpl
, IDataObject_iface
);
57 static inline EnumFormatImpl
*impl_from_IEnumFORMATETC(IEnumFORMATETC
*iface
)
59 return CONTAINING_RECORD(iface
, EnumFormatImpl
, IEnumFORMATETC_iface
);
62 static HRESULT WINAPI
EnumFormatImpl_QueryInterface(IEnumFORMATETC
*iface
, REFIID riid
, LPVOID
*ppvObj
)
64 EnumFormatImpl
*This
= impl_from_IEnumFORMATETC(iface
);
65 TRACE("%p %s\n", This
, debugstr_guid(riid
));
67 if (IsEqualGUID(riid
, &IID_IUnknown
) || IsEqualGUID(riid
, &IID_IEnumFORMATETC
)) {
68 IEnumFORMATETC_AddRef(iface
);
76 static ULONG WINAPI
EnumFormatImpl_AddRef(IEnumFORMATETC
*iface
)
78 EnumFormatImpl
*This
= impl_from_IEnumFORMATETC(iface
);
79 LONG ref
= InterlockedIncrement(&This
->ref
);
80 TRACE("(%p) ref=%d\n", This
, ref
);
84 static ULONG WINAPI
EnumFormatImpl_Release(IEnumFORMATETC
*iface
)
86 EnumFormatImpl
*This
= impl_from_IEnumFORMATETC(iface
);
87 ULONG ref
= InterlockedDecrement(&This
->ref
);
88 TRACE("(%p) ref=%d\n", This
, ref
);
91 GlobalFree(This
->fmtetc
);
98 static HRESULT WINAPI
EnumFormatImpl_Next(IEnumFORMATETC
*iface
, ULONG celt
,
99 FORMATETC
*rgelt
, ULONG
*pceltFetched
)
101 EnumFormatImpl
*This
= impl_from_IEnumFORMATETC(iface
);
103 TRACE("(%p)->(%d %p %p)\n", This
, celt
, rgelt
, pceltFetched
);
108 count
= min(celt
, This
->fmtetc_cnt
-This
->cur
);
110 memcpy(rgelt
, This
->fmtetc
+This
->cur
, count
*sizeof(FORMATETC
));
114 *pceltFetched
= count
;
115 return count
== celt
? S_OK
: S_FALSE
;
118 static HRESULT WINAPI
EnumFormatImpl_Skip(IEnumFORMATETC
*iface
, ULONG celt
)
120 EnumFormatImpl
*This
= impl_from_IEnumFORMATETC(iface
);
122 TRACE("(%p)->(%d)\n", This
, celt
);
124 count
= min(celt
, This
->fmtetc_cnt
-This
->cur
);
126 return count
== celt
? S_OK
: S_FALSE
;
129 static HRESULT WINAPI
EnumFormatImpl_Reset(IEnumFORMATETC
*iface
)
131 EnumFormatImpl
*This
= impl_from_IEnumFORMATETC(iface
);
132 TRACE("(%p)\n", This
);
138 static HRESULT WINAPI
EnumFormatImpl_Clone(IEnumFORMATETC
*iface
, IEnumFORMATETC
**ppenum
)
140 EnumFormatImpl
*This
= impl_from_IEnumFORMATETC(iface
);
142 TRACE("(%p)->(%p)\n", This
, ppenum
);
146 hr
= EnumFormatImpl_Create(This
->fmtetc
, This
->fmtetc_cnt
, ppenum
);
148 hr
= IEnumFORMATETC_Skip(*ppenum
, This
->cur
);
152 static const IEnumFORMATETCVtbl VT_EnumFormatImpl
= {
153 EnumFormatImpl_QueryInterface
,
154 EnumFormatImpl_AddRef
,
155 EnumFormatImpl_Release
,
158 EnumFormatImpl_Reset
,
162 static HRESULT
EnumFormatImpl_Create(const FORMATETC
*fmtetc
, UINT fmtetc_cnt
, IEnumFORMATETC
**lplpformatetc
)
167 ret
= heap_alloc(sizeof(EnumFormatImpl
));
168 ret
->IEnumFORMATETC_iface
.lpVtbl
= &VT_EnumFormatImpl
;
171 ret
->fmtetc_cnt
= fmtetc_cnt
;
172 ret
->fmtetc
= GlobalAlloc(GMEM_ZEROINIT
, fmtetc_cnt
*sizeof(FORMATETC
));
173 memcpy(ret
->fmtetc
, fmtetc
, fmtetc_cnt
*sizeof(FORMATETC
));
174 *lplpformatetc
= (LPENUMFORMATETC
)ret
;
178 static HRESULT WINAPI
DataObjectImpl_QueryInterface(IDataObject
*iface
, REFIID riid
, LPVOID
*ppvObj
)
180 DataObjectImpl
*This
= impl_from_IDataObject(iface
);
181 TRACE("(%p)->(%s)\n", This
, debugstr_guid(riid
));
183 if (IsEqualGUID(riid
, &IID_IUnknown
) || IsEqualGUID(riid
, &IID_IDataObject
)) {
184 IDataObject_AddRef(iface
);
189 return E_NOINTERFACE
;
192 static ULONG WINAPI
DataObjectImpl_AddRef(IDataObject
* iface
)
194 DataObjectImpl
*This
= impl_from_IDataObject(iface
);
195 ULONG ref
= InterlockedIncrement(&This
->ref
);
196 TRACE("(%p) ref=%d\n", This
, ref
);
200 static ULONG WINAPI
DataObjectImpl_Release(IDataObject
* iface
)
202 DataObjectImpl
*This
= impl_from_IDataObject(iface
);
203 ULONG ref
= InterlockedDecrement(&This
->ref
);
204 TRACE("(%p) ref=%d\n",This
, ref
);
207 if(This
->unicode
) GlobalFree(This
->unicode
);
208 if(This
->rtf
) GlobalFree(This
->rtf
);
209 if(This
->fmtetc
) GlobalFree(This
->fmtetc
);
216 static HRESULT WINAPI
DataObjectImpl_GetData(IDataObject
* iface
, FORMATETC
*pformatetc
, STGMEDIUM
*pmedium
)
218 DataObjectImpl
*This
= impl_from_IDataObject(iface
);
219 TRACE("(%p)->(fmt=0x%08x tym=0x%08x)\n", This
, pformatetc
->cfFormat
, pformatetc
->tymed
);
221 if(pformatetc
->lindex
!= -1)
224 if(!(pformatetc
->tymed
& TYMED_HGLOBAL
))
227 if(This
->unicode
&& pformatetc
->cfFormat
== CF_UNICODETEXT
)
228 pmedium
->u
.hGlobal
= This
->unicode
;
229 else if(This
->rtf
&& pformatetc
->cfFormat
== cfRTF
)
230 pmedium
->u
.hGlobal
= This
->rtf
;
232 return DV_E_FORMATETC
;
234 pmedium
->tymed
= TYMED_HGLOBAL
;
235 pmedium
->pUnkForRelease
= (LPUNKNOWN
)iface
;
236 IUnknown_AddRef(pmedium
->pUnkForRelease
);
240 static HRESULT WINAPI
DataObjectImpl_GetDataHere(IDataObject
* iface
, FORMATETC
*pformatetc
, STGMEDIUM
*pmedium
)
242 DataObjectImpl
*This
= impl_from_IDataObject(iface
);
243 FIXME("(%p): stub\n", This
);
247 static HRESULT WINAPI
DataObjectImpl_QueryGetData(IDataObject
* iface
, FORMATETC
*pformatetc
)
249 DataObjectImpl
*This
= impl_from_IDataObject(iface
);
251 BOOL foundFormat
= FALSE
;
252 TRACE("(%p)->(fmt=0x%08x tym=0x%08x)\n", This
, pformatetc
->cfFormat
, pformatetc
->tymed
);
254 if(pformatetc
->lindex
!= -1)
257 for(i
=0; i
<This
->fmtetc_cnt
; i
++) {
258 if(This
->fmtetc
[i
].cfFormat
== pformatetc
->cfFormat
) {
260 if(This
->fmtetc
[i
].tymed
== pformatetc
->tymed
)
264 return foundFormat
?DV_E_FORMATETC
:DV_E_TYMED
;
267 static HRESULT WINAPI
DataObjectImpl_GetCanonicalFormatEtc(IDataObject
* iface
, FORMATETC
*pformatetcIn
,
268 FORMATETC
*pformatetcOut
)
270 DataObjectImpl
*This
= impl_from_IDataObject(iface
);
271 TRACE("(%p)->(%p,%p)\n", This
, pformatetcIn
, pformatetcOut
);
274 *pformatetcOut
= *pformatetcIn
;
275 pformatetcOut
->ptd
= NULL
;
277 return DATA_S_SAMEFORMATETC
;
280 static HRESULT WINAPI
DataObjectImpl_SetData(IDataObject
* iface
, FORMATETC
*pformatetc
,
281 STGMEDIUM
*pmedium
, BOOL fRelease
)
283 DataObjectImpl
*This
= impl_from_IDataObject(iface
);
284 FIXME("(%p): stub\n", This
);
288 static HRESULT WINAPI
DataObjectImpl_EnumFormatEtc(IDataObject
* iface
, DWORD dwDirection
,
289 IEnumFORMATETC
**ppenumFormatEtc
)
291 DataObjectImpl
*This
= impl_from_IDataObject(iface
);
292 TRACE("(%p)->(%d)\n", This
, dwDirection
);
294 if(dwDirection
!= DATADIR_GET
) {
295 FIXME("Unsupported direction: %d\n", dwDirection
);
296 /* WinXP riched20 also returns E_NOTIMPL in this case */
297 *ppenumFormatEtc
= NULL
;
300 return EnumFormatImpl_Create(This
->fmtetc
, This
->fmtetc_cnt
, ppenumFormatEtc
);
303 static HRESULT WINAPI
DataObjectImpl_DAdvise(IDataObject
* iface
, FORMATETC
*pformatetc
, DWORD advf
,
304 IAdviseSink
*pAdvSink
, DWORD
*pdwConnection
)
306 DataObjectImpl
*This
= impl_from_IDataObject(iface
);
307 FIXME("(%p): stub\n", This
);
311 static HRESULT WINAPI
DataObjectImpl_DUnadvise(IDataObject
* iface
, DWORD dwConnection
)
313 DataObjectImpl
*This
= impl_from_IDataObject(iface
);
314 FIXME("(%p): stub\n", This
);
318 static HRESULT WINAPI
DataObjectImpl_EnumDAdvise(IDataObject
* iface
, IEnumSTATDATA
**ppenumAdvise
)
320 DataObjectImpl
*This
= impl_from_IDataObject(iface
);
321 FIXME("(%p): stub\n", This
);
325 static const IDataObjectVtbl VT_DataObjectImpl
=
327 DataObjectImpl_QueryInterface
,
328 DataObjectImpl_AddRef
,
329 DataObjectImpl_Release
,
330 DataObjectImpl_GetData
,
331 DataObjectImpl_GetDataHere
,
332 DataObjectImpl_QueryGetData
,
333 DataObjectImpl_GetCanonicalFormatEtc
,
334 DataObjectImpl_SetData
,
335 DataObjectImpl_EnumFormatEtc
,
336 DataObjectImpl_DAdvise
,
337 DataObjectImpl_DUnadvise
,
338 DataObjectImpl_EnumDAdvise
341 static HGLOBAL
get_unicode_text(ME_TextEditor
*editor
, const ME_Cursor
*start
, int nChars
)
346 ME_DisplayItem
*para
;
347 int nEnd
= ME_GetCursorOfs(start
) + nChars
;
349 /* count paragraphs in range */
351 while((para
= para
->member
.para
.next_para
) &&
352 para
->member
.para
.nCharOfs
<= nEnd
)
355 ret
= GlobalAlloc(GMEM_MOVEABLE
, sizeof(WCHAR
) * (nChars
+ pars
+ 1));
356 data
= GlobalLock(ret
);
357 ME_GetTextW(editor
, data
, nChars
+ pars
, start
, nChars
, TRUE
, FALSE
);
362 typedef struct tagME_GlobalDestStruct
366 } ME_GlobalDestStruct
;
368 static DWORD CALLBACK
ME_AppendToHGLOBAL(DWORD_PTR dwCookie
, LPBYTE lpBuff
, LONG cb
, LONG
*pcb
)
370 ME_GlobalDestStruct
*pData
= (ME_GlobalDestStruct
*)dwCookie
;
374 nMaxSize
= GlobalSize(pData
->hData
);
375 if (pData
->nLength
+cb
+1 >= cb
) {
376 /* round up to 2^17 */
377 int nNewSize
= (((nMaxSize
+cb
+1)|0x1FFFF)+1) & 0xFFFE0000;
378 pData
->hData
= GlobalReAlloc(pData
->hData
, nNewSize
, 0);
380 pDest
= GlobalLock(pData
->hData
);
381 memcpy(pDest
+ pData
->nLength
, lpBuff
, cb
);
382 pData
->nLength
+= cb
;
383 pDest
[pData
->nLength
] = '\0';
384 GlobalUnlock(pData
->hData
);
390 static HGLOBAL
get_rtf_text(ME_TextEditor
*editor
, const ME_Cursor
*start
, int nChars
)
393 ME_GlobalDestStruct gds
;
395 gds
.hData
= GlobalAlloc(GMEM_MOVEABLE
, 0);
397 es
.dwCookie
= (DWORD_PTR
)&gds
;
398 es
.pfnCallback
= ME_AppendToHGLOBAL
;
399 ME_StreamOutRange(editor
, SF_RTF
, start
, nChars
, &es
);
400 GlobalReAlloc(gds
.hData
, gds
.nLength
+1, 0);
404 HRESULT
ME_GetDataObject(ME_TextEditor
*editor
, const ME_Cursor
*start
,
405 int nChars
, LPDATAOBJECT
*lplpdataobj
)
408 TRACE("(%p,%d,%d)\n", editor
, ME_GetCursorOfs(start
), nChars
);
410 obj
= heap_alloc(sizeof(DataObjectImpl
));
412 cfRTF
= RegisterClipboardFormatA("Rich Text Format");
414 obj
->IDataObject_iface
.lpVtbl
= &VT_DataObjectImpl
;
416 obj
->unicode
= get_unicode_text(editor
, start
, nChars
);
420 if(editor
->mode
& TM_RICHTEXT
)
422 obj
->fmtetc
= GlobalAlloc(GMEM_ZEROINIT
, obj
->fmtetc_cnt
*sizeof(FORMATETC
));
423 InitFormatEtc(obj
->fmtetc
[0], CF_UNICODETEXT
, TYMED_HGLOBAL
);
424 if(editor
->mode
& TM_RICHTEXT
) {
425 obj
->rtf
= get_rtf_text(editor
, start
, nChars
);
426 InitFormatEtc(obj
->fmtetc
[1], cfRTF
, TYMED_HGLOBAL
);
429 *lplpdataobj
= (LPDATAOBJECT
)obj
;