user32: Synthesize text clipboard formats on the user32 side.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2016-09-06 20:26:09 +09:00
parent b1e8ed3e3b
commit 8865f4a4ec
3 changed files with 210 additions and 44 deletions

View File

@ -1336,17 +1336,14 @@ static void test_nonole_clipboard(void)
hr = IEnumFORMATETC_Next(enum_fmt, 1, &fmt, NULL); hr = IEnumFORMATETC_Next(enum_fmt, 1, &fmt, NULL);
ok(hr == S_OK, "got %08x\n", hr); /* User32 adds some synthesised formats */ ok(hr == S_OK, "got %08x\n", hr); /* User32 adds some synthesised formats */
todo_wine ok(fmt.cfFormat == CF_LOCALE, "cf %04x\n", fmt.cfFormat); ok(fmt.cfFormat == CF_LOCALE, "cf %04x\n", fmt.cfFormat);
if(fmt.cfFormat == CF_LOCALE) ok(fmt.ptd == NULL, "ptd %p\n", fmt.ptd);
{ ok(fmt.dwAspect == DVASPECT_CONTENT, "aspect %x\n", fmt.dwAspect);
ok(fmt.ptd == NULL, "ptd %p\n", fmt.ptd); ok(fmt.lindex == -1, "lindex %d\n", fmt.lindex);
ok(fmt.dwAspect == DVASPECT_CONTENT, "aspect %x\n", fmt.dwAspect); todo_wine ok(fmt.tymed == (TYMED_ISTREAM | TYMED_HGLOBAL), "tymed %x\n", fmt.tymed);
ok(fmt.lindex == -1, "lindex %d\n", fmt.lindex);
ok(fmt.tymed == (TYMED_ISTREAM | TYMED_HGLOBAL), "tymed %x\n", fmt.tymed);
hr = IEnumFORMATETC_Next(enum_fmt, 1, &fmt, NULL); hr = IEnumFORMATETC_Next(enum_fmt, 1, &fmt, NULL);
ok(hr == S_OK, "got %08x\n", hr); ok(hr == S_OK, "got %08x\n", hr);
}
ok(fmt.cfFormat == CF_OEMTEXT, "cf %04x\n", fmt.cfFormat); ok(fmt.cfFormat == CF_OEMTEXT, "cf %04x\n", fmt.cfFormat);
ok(fmt.ptd == NULL, "ptd %p\n", fmt.ptd); ok(fmt.ptd == NULL, "ptd %p\n", fmt.ptd);

View File

@ -32,6 +32,7 @@
#include "config.h" #include "config.h"
#include "wine/port.h" #include "wine/port.h"
#include <assert.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/types.h> #include <sys/types.h>
@ -61,6 +62,162 @@ WINE_DEFAULT_DEBUG_CHANNEL(clipboard);
static BOOL bCBHasChanged = FALSE; static BOOL bCBHasChanged = FALSE;
/* formats that can be synthesized are: CF_TEXT, CF_OEMTEXT, CF_UNICODETEXT,
CF_BITMAP, CF_DIB, CF_DIBV5, CF_ENHMETAFILE, CF_METAFILEPICT */
static UINT synthesized_formats[CF_MAX];
/* add a synthesized format to the list */
static void add_synthesized_format( UINT format, UINT from )
{
assert( format < CF_MAX );
SetClipboardData( format, 0 );
synthesized_formats[format] = from;
}
/* store the current locale in the CF_LOCALE format */
static void set_clipboard_locale(void)
{
HANDLE data = GlobalAlloc( GMEM_FIXED, sizeof(LCID) );
if (!data) return;
*(LCID *)data = GetUserDefaultLCID();
SetClipboardData( CF_LOCALE, data );
TRACE( "added CF_LOCALE\n" );
}
/* get the clipboard locale stored in the CF_LOCALE format */
static LCID get_clipboard_locale(void)
{
HANDLE data;
LCID lcid = GetUserDefaultLCID();
if ((data = GetClipboardData( CF_LOCALE )))
{
LCID *ptr = GlobalLock( data );
if (ptr && GlobalSize( data ) >= sizeof(*ptr)) lcid = *ptr;
GlobalUnlock( data );
}
return lcid;
}
/* get the codepage to use for text conversions in the specified format (CF_TEXT or CF_OEMTEXT) */
static UINT get_format_codepage( LCID lcid, UINT format )
{
LCTYPE type = (format == CF_TEXT) ? LOCALE_IDEFAULTANSICODEPAGE : LOCALE_IDEFAULTCODEPAGE;
UINT ret;
if (!GetLocaleInfoW( lcid, type | LOCALE_RETURN_NUMBER, (LPWSTR)&ret, sizeof(ret)/sizeof(WCHAR) ))
ret = (format == CF_TEXT) ? CP_ACP : CP_OEMCP;
return ret;
}
/* add synthesized text formats based on what is already in the clipboard */
static void add_synthesized_text(void)
{
BOOL has_text = IsClipboardFormatAvailable( CF_TEXT );
BOOL has_oemtext = IsClipboardFormatAvailable( CF_OEMTEXT );
BOOL has_unicode = IsClipboardFormatAvailable( CF_UNICODETEXT );
if (!has_text && !has_oemtext && !has_unicode) return; /* no text, nothing to do */
if (!IsClipboardFormatAvailable( CF_LOCALE )) set_clipboard_locale();
if (has_unicode)
{
if (!has_text) add_synthesized_format( CF_TEXT, CF_UNICODETEXT );
if (!has_oemtext) add_synthesized_format( CF_OEMTEXT, CF_UNICODETEXT );
}
else if (has_text)
{
if (!has_oemtext) add_synthesized_format( CF_OEMTEXT, CF_TEXT );
if (!has_unicode) add_synthesized_format( CF_UNICODETEXT, CF_TEXT );
}
else
{
if (!has_text) add_synthesized_format( CF_TEXT, CF_OEMTEXT );
if (!has_unicode) add_synthesized_format( CF_UNICODETEXT, CF_OEMTEXT );
}
}
/* render synthesized ANSI text based on the contents of the 'from' format */
static HANDLE render_synthesized_textA( HANDLE data, UINT format, UINT from )
{
void *src;
WCHAR *srcW = NULL;
HANDLE ret = 0;
LCID lcid = get_clipboard_locale();
UINT codepage = get_format_codepage( lcid, format );
UINT len, size = GlobalSize( data );
if (!(src = GlobalLock( data ))) return 0;
if (from != CF_UNICODETEXT) /* first convert incoming format to Unicode */
{
UINT from_codepage = get_format_codepage( lcid, from );
len = MultiByteToWideChar( from_codepage, 0, src, size, NULL, 0 );
if (!(srcW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) goto done;
MultiByteToWideChar( from_codepage, 0, src, size, srcW, len );
src = srcW;
size = len * sizeof(WCHAR);
}
len = WideCharToMultiByte( codepage, 0, src, size / sizeof(WCHAR), NULL, 0, NULL, NULL );
if ((ret = GlobalAlloc( GMEM_FIXED, len )))
WideCharToMultiByte( codepage, 0, src, size / sizeof(WCHAR), ret, len, NULL, NULL );
done:
HeapFree( GetProcessHeap(), 0, srcW );
GlobalUnlock( data );
return ret;
}
/* render synthesized Unicode text based on the contents of the 'from' format */
static HANDLE render_synthesized_textW( HANDLE data, UINT from )
{
char *src;
HANDLE ret;
UINT len, size = GlobalSize( data );
UINT codepage = get_format_codepage( get_clipboard_locale(), from );
if (!(src = GlobalLock( data ))) return 0;
len = MultiByteToWideChar( codepage, 0, src, size, NULL, 0 );
if ((ret = GlobalAlloc( GMEM_FIXED, len * sizeof(WCHAR) )))
MultiByteToWideChar( codepage, 0, src, size, ret, len );
GlobalUnlock( data );
return ret;
}
/* render a synthesized format */
static HANDLE render_synthesized_format( UINT format, UINT from )
{
HANDLE data = GetClipboardData( from );
if (!data) return 0;
TRACE( "rendering %04x from %04x\n", format, from );
switch (format)
{
case CF_TEXT:
case CF_OEMTEXT:
data = render_synthesized_textA( data, format, from );
break;
case CF_UNICODETEXT:
data = render_synthesized_textW( data, from );
break;
default:
assert( 0 );
}
if (data)
{
TRACE( "adding %04x %p\n", format, data );
SetClipboardData( format, data );
}
return data;
}
/************************************************************************** /**************************************************************************
* get_clipboard_flags * get_clipboard_flags
*/ */
@ -154,7 +311,11 @@ BOOL WINAPI OpenClipboard( HWND hwnd )
req->window = wine_server_user_handle( hwnd ); req->window = wine_server_user_handle( hwnd );
if ((ret = !wine_server_call_err( req ))) if ((ret = !wine_server_call_err( req )))
{ {
if (!reply->owner) bCBHasChanged = FALSE; if (!reply->owner)
{
bCBHasChanged = FALSE;
memset( synthesized_formats, 0, sizeof(synthesized_formats) );
}
} }
} }
SERVER_END_REQ; SERVER_END_REQ;
@ -173,6 +334,12 @@ BOOL WINAPI CloseClipboard(void)
TRACE("() Changed=%d\n", bCBHasChanged); TRACE("() Changed=%d\n", bCBHasChanged);
if (bCBHasChanged)
{
memset( synthesized_formats, 0, sizeof(synthesized_formats) );
add_synthesized_text();
}
SERVER_START_REQ( close_clipboard ) SERVER_START_REQ( close_clipboard )
{ {
req->changed = bCBHasChanged; req->changed = bCBHasChanged;
@ -219,6 +386,7 @@ BOOL WINAPI EmptyClipboard(void)
{ {
USER_Driver->pEmptyClipboard(); USER_Driver->pEmptyClipboard();
bCBHasChanged = TRUE; bCBHasChanged = TRUE;
memset( synthesized_formats, 0, sizeof(synthesized_formats) );
} }
return ret; return ret;
} }
@ -337,14 +505,14 @@ BOOL WINAPI ChangeClipboardChain( HWND hwnd, HWND next )
/************************************************************************** /**************************************************************************
* SetClipboardData (USER32.@) * SetClipboardData (USER32.@)
*/ */
HANDLE WINAPI SetClipboardData(UINT wFormat, HANDLE hData) HANDLE WINAPI SetClipboardData( UINT format, HANDLE data )
{ {
HANDLE hResult = 0; HANDLE hResult = 0;
UINT flags; UINT flags;
TRACE("(%04X, %p) !\n", wFormat, hData); TRACE( "%04x %p\n", format, data );
if (!wFormat) if (!format)
{ {
SetLastError( ERROR_CLIPBOARD_NOT_OPEN ); SetLastError( ERROR_CLIPBOARD_NOT_OPEN );
return 0; return 0;
@ -357,10 +525,11 @@ HANDLE WINAPI SetClipboardData(UINT wFormat, HANDLE hData)
return 0; return 0;
} }
if (USER_Driver->pSetClipboardData(wFormat, hData, flags & CB_OWNER)) if (USER_Driver->pSetClipboardData( format, data, flags & CB_OWNER))
{ {
hResult = hData; hResult = data;
bCBHasChanged = TRUE; bCBHasChanged = TRUE;
if (format < CF_MAX) synthesized_formats[format] = 0;
} }
return hResult; return hResult;
@ -439,11 +608,11 @@ BOOL WINAPI GetUpdatedClipboardFormats( UINT *formats, UINT size, UINT *out_size
/************************************************************************** /**************************************************************************
* GetClipboardData (USER32.@) * GetClipboardData (USER32.@)
*/ */
HANDLE WINAPI GetClipboardData(UINT wFormat) HANDLE WINAPI GetClipboardData( UINT format )
{ {
HANDLE hData = 0; HANDLE data = 0;
TRACE("%04x\n", wFormat); TRACE( "%04x\n", format );
if (!(get_clipboard_flags() & CB_OPEN)) if (!(get_clipboard_flags() & CB_OPEN))
{ {
@ -451,11 +620,13 @@ HANDLE WINAPI GetClipboardData(UINT wFormat)
SetLastError(ERROR_CLIPBOARD_NOT_OPEN); SetLastError(ERROR_CLIPBOARD_NOT_OPEN);
return 0; return 0;
} }
if (format < CF_MAX && synthesized_formats[format])
data = render_synthesized_format( format, synthesized_formats[format] );
else
data = USER_Driver->pGetClipboardData( format );
hData = USER_Driver->pGetClipboardData( wFormat ); TRACE( "returning %p\n", data );
return data;
TRACE("returning %p\n", hData);
return hData;
} }

View File

@ -588,9 +588,9 @@ static void test_synthesized(void)
UINT todo; UINT todo;
} tests[] = } tests[] =
{ {
/* 0 */ { CF_TEXT, { CF_TEXT, CF_LOCALE, CF_OEMTEXT, CF_UNICODETEXT }, 1 << 1 }, /* 0 */ { CF_TEXT, { CF_TEXT, CF_LOCALE, CF_OEMTEXT, CF_UNICODETEXT }},
{ CF_OEMTEXT, { CF_OEMTEXT, CF_LOCALE, CF_TEXT, CF_UNICODETEXT }, 1 << 1 }, { CF_OEMTEXT, { CF_OEMTEXT, CF_LOCALE, CF_TEXT, CF_UNICODETEXT }},
{ CF_UNICODETEXT, { CF_UNICODETEXT, CF_LOCALE, CF_TEXT, CF_OEMTEXT }, 1 << 1 }, { CF_UNICODETEXT, { CF_UNICODETEXT, CF_LOCALE, CF_TEXT, CF_OEMTEXT }},
{ CF_ENHMETAFILE, { CF_ENHMETAFILE, CF_METAFILEPICT }}, { CF_ENHMETAFILE, { CF_ENHMETAFILE, CF_METAFILEPICT }},
{ CF_METAFILEPICT, { CF_METAFILEPICT, CF_ENHMETAFILE }}, { CF_METAFILEPICT, { CF_METAFILEPICT, CF_ENHMETAFILE }},
/* 5 */ { CF_BITMAP, { CF_BITMAP, CF_DIB, CF_DIBV5 }, 1 << 2 }, /* 5 */ { CF_BITMAP, { CF_BITMAP, CF_DIB, CF_DIBV5 }, 1 << 2 },
@ -623,11 +623,11 @@ static void test_synthesized(void)
ok(r, "gle %d\n", GetLastError()); ok(r, "gle %d\n", GetLastError());
count = CountClipboardFormats(); count = CountClipboardFormats();
todo_wine ok( count == 6, "count %u\n", count ); ok( count == 6, "count %u\n", count );
r = IsClipboardFormatAvailable( CF_TEXT ); r = IsClipboardFormatAvailable( CF_TEXT );
ok( r, "CF_TEXT not available err %d\n", GetLastError()); ok( r, "CF_TEXT not available err %d\n", GetLastError());
r = IsClipboardFormatAvailable( CF_LOCALE ); r = IsClipboardFormatAvailable( CF_LOCALE );
todo_wine ok( r, "CF_LOCALE not available err %d\n", GetLastError()); ok( r, "CF_LOCALE not available err %d\n", GetLastError());
r = IsClipboardFormatAvailable( CF_OEMTEXT ); r = IsClipboardFormatAvailable( CF_OEMTEXT );
ok( r, "CF_OEMTEXT not available err %d\n", GetLastError()); ok( r, "CF_OEMTEXT not available err %d\n", GetLastError());
r = IsClipboardFormatAvailable( CF_UNICODETEXT ); r = IsClipboardFormatAvailable( CF_UNICODETEXT );
@ -650,13 +650,11 @@ static void test_synthesized(void)
ok(data != NULL, "couldn't get data, cf %08x\n", cf); ok(data != NULL, "couldn't get data, cf %08x\n", cf);
cf = EnumClipboardFormats(cf); cf = EnumClipboardFormats(cf);
todo_wine ok(cf == CF_LOCALE, "cf %08x\n", cf); ok(cf == CF_LOCALE, "cf %08x\n", cf);
if(cf == CF_LOCALE) data = GetClipboardData(cf);
{ ok(data != NULL, "couldn't get data, cf %08x\n", cf);
data = GetClipboardData(cf);
ok(data != NULL, "couldn't get data, cf %08x\n", cf); cf = EnumClipboardFormats(cf);
cf = EnumClipboardFormats(cf);
}
ok(cf == CF_OEMTEXT, "cf %08x\n", cf); ok(cf == CF_OEMTEXT, "cf %08x\n", cf);
data = GetClipboardData(cf); data = GetClipboardData(cf);
ok(data != NULL, "couldn't get data, cf %08x\n", cf); ok(data != NULL, "couldn't get data, cf %08x\n", cf);
@ -690,8 +688,8 @@ static void test_synthesized(void)
cf = EnumClipboardFormats(cf); cf = EnumClipboardFormats(cf);
ok( cf == CF_OEMTEXT, "cf %08x\n", cf ); ok( cf == CF_OEMTEXT, "cf %08x\n", cf );
cf = EnumClipboardFormats(cf); cf = EnumClipboardFormats(cf);
todo_wine ok( cf == CF_LOCALE, "cf %08x\n", cf ); ok( cf == CF_LOCALE, "cf %08x\n", cf );
if (cf == CF_LOCALE) cf = EnumClipboardFormats( cf ); cf = EnumClipboardFormats( cf );
ok( cf == 0, "cf %08x\n", cf ); ok( cf == 0, "cf %08x\n", cf );
r = EmptyClipboard(); r = EmptyClipboard();
@ -1696,7 +1694,7 @@ static void test_handles( HWND hwnd )
data = GetClipboardData( CF_UNICODETEXT ); data = GetClipboardData( CF_UNICODETEXT );
ok( is_fixed( data ), "expected fixed mem %p\n", data ); ok( is_fixed( data ), "expected fixed mem %p\n", data );
data = GetClipboardData( CF_LOCALE ); data = GetClipboardData( CF_LOCALE );
todo_wine ok( is_fixed( data ), "expected fixed mem %p\n", data ); ok( is_fixed( data ), "expected fixed mem %p\n", data );
data = GetClipboardData( CF_BITMAP ); data = GetClipboardData( CF_BITMAP );
ok( data == bitmap, "expected bitmap %p\n", data ); ok( data == bitmap, "expected bitmap %p\n", data );
data = GetClipboardData( CF_PALETTE ); data = GetClipboardData( CF_PALETTE );
@ -1964,11 +1962,11 @@ static void test_GetUpdatedClipboardFormats(void)
memset( formats, 0xcc, sizeof(formats) ); memset( formats, 0xcc, sizeof(formats) );
r = pGetUpdatedClipboardFormats( formats, 256, &count ); r = pGetUpdatedClipboardFormats( formats, 256, &count );
ok( r, "gle %d\n", GetLastError() ); ok( r, "gle %d\n", GetLastError() );
todo_wine ok( count == 4, "wrong count %u\n", count ); ok( count == 4, "wrong count %u\n", count );
ok( formats[0] == CF_UNICODETEXT, "wrong format %u\n", formats[0] ); ok( formats[0] == CF_UNICODETEXT, "wrong format %u\n", formats[0] );
ok( formats[1] == CF_TEXT, "wrong format %u\n", formats[1] ); ok( formats[1] == CF_TEXT, "wrong format %u\n", formats[1] );
todo_wine ok( formats[2] == CF_LOCALE, "wrong format %u\n", formats[2] ); ok( formats[2] == CF_LOCALE, "wrong format %u\n", formats[2] );
todo_wine ok( formats[3] == CF_OEMTEXT, "wrong format %u\n", formats[3] ); ok( formats[3] == CF_OEMTEXT, "wrong format %u\n", formats[3] );
ok( formats[4] == 0xcccccccc, "wrong format %u\n", formats[4] ); ok( formats[4] == 0xcccccccc, "wrong format %u\n", formats[4] );
count = 0xdeadbeef; count = 0xdeadbeef;
@ -1976,20 +1974,20 @@ static void test_GetUpdatedClipboardFormats(void)
r = pGetUpdatedClipboardFormats( formats, 2, &count ); r = pGetUpdatedClipboardFormats( formats, 2, &count );
ok( !r, "gle %d\n", GetLastError() ); ok( !r, "gle %d\n", GetLastError() );
ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER, "wrong error %u\n", GetLastError() ); ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER, "wrong error %u\n", GetLastError() );
todo_wine ok( count == 4, "wrong count %u\n", count ); ok( count == 4, "wrong count %u\n", count );
ok( formats[0] == 0xcccccccc, "wrong format %u\n", formats[0] ); ok( formats[0] == 0xcccccccc, "wrong format %u\n", formats[0] );
count = 0xdeadbeef; count = 0xdeadbeef;
r = pGetUpdatedClipboardFormats( NULL, 256, &count ); r = pGetUpdatedClipboardFormats( NULL, 256, &count );
ok( !r, "gle %d\n", GetLastError() ); ok( !r, "gle %d\n", GetLastError() );
ok( GetLastError() == ERROR_NOACCESS, "wrong error %u\n", GetLastError() ); ok( GetLastError() == ERROR_NOACCESS, "wrong error %u\n", GetLastError() );
todo_wine ok( count == 4, "wrong count %u\n", count ); ok( count == 4, "wrong count %u\n", count );
count = 0xdeadbeef; count = 0xdeadbeef;
r = pGetUpdatedClipboardFormats( NULL, 256, &count ); r = pGetUpdatedClipboardFormats( NULL, 256, &count );
ok( !r, "gle %d\n", GetLastError() ); ok( !r, "gle %d\n", GetLastError() );
ok( GetLastError() == ERROR_NOACCESS, "wrong error %u\n", GetLastError() ); ok( GetLastError() == ERROR_NOACCESS, "wrong error %u\n", GetLastError() );
todo_wine ok( count == 4, "wrong count %u\n", count ); ok( count == 4, "wrong count %u\n", count );
} }
START_TEST(clipboard) START_TEST(clipboard)