winemac: Implement IsClipboardFormatAvailable() with support for text formats.
[wine] / dlls / winemac.drv / clipboard.c
1 /*
2  * Mac clipboard driver
3  *
4  * Copyright 1994 Martin Ayotte
5  *           1996 Alex Korobka
6  *           1999 Noel Borthwick
7  *           2003 Ulrich Czekalla for CodeWeavers
8  * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24
25 #include "config.h"
26
27 #include "macdrv.h"
28 #include "winuser.h"
29 #include "wine/list.h"
30 #include "wine/unicode.h"
31
32
33 WINE_DEFAULT_DEBUG_CHANNEL(clipboard);
34
35
36 /**************************************************************************
37  *              Types
38  **************************************************************************/
39
40 typedef HANDLE (*DRVIMPORTFUNC)(CFDataRef data);
41
42 typedef struct
43 {
44     struct list     entry;
45     UINT            format_id;
46     CFStringRef     type;
47     DRVIMPORTFUNC   import_func;
48     BOOL            synthesized;
49 } WINE_CLIPFORMAT;
50
51
52 /**************************************************************************
53  *              Constants
54  **************************************************************************/
55
56
57 /**************************************************************************
58  *              Forward Function Declarations
59  **************************************************************************/
60
61 static HANDLE import_clipboard_data(CFDataRef data);
62 static HANDLE import_oemtext_to_text(CFDataRef data);
63 static HANDLE import_oemtext_to_unicodetext(CFDataRef data);
64 static HANDLE import_text_to_oemtext(CFDataRef data);
65 static HANDLE import_text_to_unicodetext(CFDataRef data);
66 static HANDLE import_unicodetext_to_oemtext(CFDataRef data);
67 static HANDLE import_unicodetext_to_text(CFDataRef data);
68 static HANDLE import_utf8_to_oemtext(CFDataRef data);
69 static HANDLE import_utf8_to_text(CFDataRef data);
70 static HANDLE import_utf8_to_unicodetext(CFDataRef data);
71
72
73 /**************************************************************************
74  *              Static Variables
75  **************************************************************************/
76
77 /* Clipboard formats */
78 static struct list format_list = LIST_INIT(format_list);
79
80 /*  There are two naming schemes involved and we want to have a mapping between
81     them.  There are Win32 clipboard format names and there are Mac pasteboard
82     types.
83
84     The Win32 standard clipboard formats don't have names, but they are associated
85     with Mac pasteboard types through the following tables, which are used to
86     initialize the format_list.  Where possible, the standard clipboard formats
87     are mapped to predefined pasteboard type UTIs.  Otherwise, we create Wine-
88     specific types of the form "org.winehq.builtin.<format>", where <format> is
89     the name of the symbolic constant for the format minus "CF_" and lowercased.
90     E.g. CF_BITMAP -> org.winehq.builtin.bitmap.
91
92     Win32 clipboard formats which originate in a Windows program may be registered
93     with an arbitrary name.  We construct a Mac pasteboard type from these by
94     prepending "org.winehq.registered." to the registered name.
95
96     Likewise, Mac pasteboard types which originate in other apps may have
97     arbitrary type strings.  We construct a Win32 clipboard format name from
98     these by prepending "org.winehq.mac-type." to the Mac pasteboard type.
99
100     Summary:
101     Win32 clipboard format names:
102         <none>                              standard clipboard format; maps via
103                                             format_list to either a predefined Mac UTI
104                                             or org.winehq.builtin.<format>.
105         org.winehq.mac-type.<Mac type>      representation of Mac type in Win32 land;
106                                             maps to <Mac type>
107         <other>                             name registered within Win32 land; maps to
108                                             org.winehq.registered.<other>
109     Mac pasteboard type names:
110         org.winehq.builtin.<format ID>      representation of Win32 standard clipboard
111                                             format for which there was no corresponding
112                                             predefined Mac UTI; maps via format_list
113         org.winehq.registered.<format name> representation of Win32 registered
114                                             clipboard format name; maps to <format name>
115         <other>                             Mac pasteboard type originating with system
116                                             or other apps; either maps via format_list
117                                             to a standard clipboard format or maps to
118                                             org.winehq.mac-type.<other>
119 */
120
121 static const struct
122 {
123     UINT          id;
124     CFStringRef   type;
125     DRVIMPORTFUNC import;
126     BOOL          synthesized;
127 } builtin_format_ids[] =
128 {
129     { CF_UNICODETEXT,       CFSTR("org.winehq.builtin.unicodetext"),        import_clipboard_data,          FALSE },
130     { CF_TEXT,              CFSTR("org.winehq.builtin.unicodetext"),        import_unicodetext_to_text,     TRUE },
131     { CF_OEMTEXT,           CFSTR("org.winehq.builtin.unicodetext"),        import_unicodetext_to_oemtext,  TRUE },
132
133     { CF_TEXT,              CFSTR("org.winehq.builtin.text"),               import_clipboard_data,          FALSE },
134     { CF_UNICODETEXT,       CFSTR("org.winehq.builtin.text"),               import_text_to_unicodetext,     TRUE },
135     { CF_OEMTEXT,           CFSTR("org.winehq.builtin.text"),               import_text_to_oemtext,         TRUE },
136
137     { CF_OEMTEXT,           CFSTR("org.winehq.builtin.oemtext"),            import_clipboard_data,          FALSE },
138     { CF_UNICODETEXT,       CFSTR("org.winehq.builtin.oemtext"),            import_oemtext_to_unicodetext,  TRUE },
139     { CF_TEXT,              CFSTR("org.winehq.builtin.oemtext"),            import_oemtext_to_text,         TRUE },
140
141     { CF_UNICODETEXT,       CFSTR("public.utf8-plain-text"),                import_utf8_to_unicodetext,     TRUE },
142     { CF_TEXT,              CFSTR("public.utf8-plain-text"),                import_utf8_to_text,            TRUE },
143     { CF_OEMTEXT,           CFSTR("public.utf8-plain-text"),                import_utf8_to_oemtext,         TRUE },
144 };
145
146 /* The prefix prepended to an external Mac pasteboard type to make a Win32 clipboard format name. org.winehq.mac-type. */
147 static const WCHAR mac_type_name_prefix[] = {'o','r','g','.','w','i','n','e','h','q','.','m','a','c','-','t','y','p','e','.',0};
148
149 /* The prefix prepended to a Win32 clipboard format name to make a Mac pasteboard type. */
150 static const CFStringRef registered_name_type_prefix = CFSTR("org.winehq.registered.");
151
152
153 /**************************************************************************
154  *              Internal Clipboard implementation methods
155  **************************************************************************/
156
157 /*
158  * format_list functions
159  */
160
161 /**************************************************************************
162  *              debugstr_format
163  */
164 static const char *debugstr_format(UINT id)
165 {
166     WCHAR buffer[256];
167
168     if (GetClipboardFormatNameW(id, buffer, 256))
169         return wine_dbg_sprintf("0x%04x %s", id, debugstr_w(buffer));
170
171     switch (id)
172     {
173 #define BUILTIN(id) case id: return #id;
174     BUILTIN(CF_TEXT)
175     BUILTIN(CF_BITMAP)
176     BUILTIN(CF_METAFILEPICT)
177     BUILTIN(CF_SYLK)
178     BUILTIN(CF_DIF)
179     BUILTIN(CF_TIFF)
180     BUILTIN(CF_OEMTEXT)
181     BUILTIN(CF_DIB)
182     BUILTIN(CF_PALETTE)
183     BUILTIN(CF_PENDATA)
184     BUILTIN(CF_RIFF)
185     BUILTIN(CF_WAVE)
186     BUILTIN(CF_UNICODETEXT)
187     BUILTIN(CF_ENHMETAFILE)
188     BUILTIN(CF_HDROP)
189     BUILTIN(CF_LOCALE)
190     BUILTIN(CF_DIBV5)
191     BUILTIN(CF_OWNERDISPLAY)
192     BUILTIN(CF_DSPTEXT)
193     BUILTIN(CF_DSPBITMAP)
194     BUILTIN(CF_DSPMETAFILEPICT)
195     BUILTIN(CF_DSPENHMETAFILE)
196 #undef BUILTIN
197     default: return wine_dbg_sprintf("0x%04x", id);
198     }
199 }
200
201
202 /**************************************************************************
203  *              insert_clipboard_format
204  */
205 static WINE_CLIPFORMAT *insert_clipboard_format(UINT id, CFStringRef type)
206 {
207     WINE_CLIPFORMAT *format;
208
209     format = HeapAlloc(GetProcessHeap(), 0, sizeof(*format));
210
211     if (format == NULL)
212     {
213         WARN("No more memory for a new format!\n");
214         return NULL;
215     }
216     format->format_id = id;
217     format->import_func = import_clipboard_data;
218     format->synthesized = FALSE;
219
220     if (type)
221         format->type = CFStringCreateCopy(NULL, type);
222     else
223     {
224         WCHAR buffer[256];
225
226         GetClipboardFormatNameW(format->format_id, buffer, 256);
227         if (!strncmpW(buffer, mac_type_name_prefix, strlenW(mac_type_name_prefix)))
228         {
229             const WCHAR *p = buffer + strlenW(mac_type_name_prefix);
230             format->type = CFStringCreateWithCharacters(NULL, (UniChar*)p, strlenW(p));
231         }
232         else
233         {
234             format->type = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%S"),
235                                                     registered_name_type_prefix, buffer);
236         }
237     }
238
239     list_add_tail(&format_list, &format->entry);
240
241     TRACE("Registering format %s type %s\n", debugstr_format(format->format_id),
242           debugstr_cf(format->type));
243
244     return format;
245 }
246
247
248 /**************************************************************************
249  *              register_format
250  *
251  * Register a custom Mac clipboard format.
252  */
253 static WINE_CLIPFORMAT* register_format(UINT id, CFStringRef type)
254 {
255     WINE_CLIPFORMAT *format;
256
257     /* walk format chain to see if it's already registered */
258     LIST_FOR_EACH_ENTRY(format, &format_list, WINE_CLIPFORMAT, entry)
259         if (format->format_id == id) return format;
260
261     return insert_clipboard_format(id, type);
262 }
263
264
265 /**************************************************************************
266  *              format_for_type
267  */
268 static WINE_CLIPFORMAT* format_for_type(WINE_CLIPFORMAT *current, CFStringRef type)
269 {
270     struct list *ptr = current ? &current->entry : &format_list;
271     WINE_CLIPFORMAT *format = NULL;
272
273     TRACE("current %p/%s type %s\n", current, debugstr_format(current ? current->format_id : 0), debugstr_cf(type));
274
275     while ((ptr = list_next(&format_list, ptr)))
276     {
277         format = LIST_ENTRY(ptr, WINE_CLIPFORMAT, entry);
278         if (CFEqual(format->type, type))
279         {
280             TRACE(" -> %p/%s\n", format, debugstr_format(format->format_id));
281             return format;
282         }
283     }
284
285     if (!current)
286     {
287         LPWSTR name;
288
289         if (CFStringHasPrefix(type, CFSTR("org.winehq.builtin.")))
290         {
291             ERR("Shouldn't happen. Built-in type %s should have matched something in format list.\n",
292                 debugstr_cf(type));
293             return NULL;
294         }
295         else if (CFStringHasPrefix(type, registered_name_type_prefix))
296         {
297             int len = CFStringGetLength(type) - CFStringGetLength(registered_name_type_prefix);
298             name = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
299             CFStringGetCharacters(type, CFRangeMake(CFStringGetLength(registered_name_type_prefix), len),
300                                   (UniChar*)name);
301             name[len] = 0;
302         }
303         else
304         {
305             int len = strlenW(mac_type_name_prefix) + CFStringGetLength(type);
306             name = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
307             memcpy(name, mac_type_name_prefix, sizeof(mac_type_name_prefix));
308             CFStringGetCharacters(type, CFRangeMake(0, CFStringGetLength(type)),
309                                   (UniChar*)name + strlenW(mac_type_name_prefix));
310             name[len] = 0;
311         }
312
313         format = register_format(RegisterClipboardFormatW(name), type);
314         if (!format)
315             ERR("Failed to register format for type %s name %s\n", debugstr_cf(type), debugstr_w(name));
316
317         HeapFree(GetProcessHeap(), 0, name);
318     }
319
320     TRACE(" -> %p/%s\n", format, debugstr_format(format ? format->format_id : 0));
321     return format;
322 }
323
324
325 /**************************************************************************
326  *              convert_text
327  *
328  *  Convert string data between code pages or to/from wide characters.  The
329  *  special value of (UINT)-1 for a code page indicates to use wide
330  *  characters.
331  */
332 static HANDLE convert_text(const void *src, int src_len, UINT src_cp, UINT dest_cp)
333 {
334     HANDLE ret = NULL;
335     const WCHAR *wstr;
336     int wstr_len;
337     HANDLE handle;
338     char *p;
339
340     if (src_cp == (UINT)-1)
341     {
342         wstr = src;
343         wstr_len = src_len / sizeof(WCHAR);
344     }
345     else
346     {
347         WCHAR *temp;
348
349         wstr_len = MultiByteToWideChar(src_cp, 0, src, src_len, NULL, 0);
350         if (!src_len || ((const char*)src)[src_len - 1]) wstr_len += 1;
351         temp = HeapAlloc(GetProcessHeap(), 0, wstr_len * sizeof(WCHAR));
352         MultiByteToWideChar(src_cp, 0, src, src_len, temp, wstr_len);
353         temp[wstr_len - 1] = 0;
354         wstr = temp;
355     }
356
357     if (dest_cp == (UINT)-1)
358     {
359         handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, wstr_len * sizeof(WCHAR));
360         if (handle && (p = GlobalLock(handle)))
361         {
362             memcpy(p, wstr, wstr_len * sizeof(WCHAR));
363             GlobalUnlock(handle);
364             ret = handle;
365         }
366     }
367     else
368     {
369         INT len;
370
371         len = WideCharToMultiByte(dest_cp, 0, wstr, wstr_len, NULL, 0, NULL, NULL);
372         if (!wstr_len || wstr[wstr_len - 1]) len += 1;
373         handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, len);
374
375         if (handle && (p = GlobalLock(handle)))
376         {
377             WideCharToMultiByte(dest_cp, 0, wstr, wstr_len, p, len, NULL, NULL);
378             p[len - 1] = 0;
379             GlobalUnlock(handle);
380             ret = handle;
381         }
382     }
383
384     return ret;
385 }
386
387
388 /**************************************************************************
389  *              convert_unicodetext_to_codepage
390  */
391 static HANDLE convert_unicodetext_to_codepage(HANDLE unicode_handle, UINT cp)
392 {
393     LPWSTR unicode_string = GlobalLock(unicode_handle);
394     HANDLE ret = NULL;
395
396     if (unicode_string)
397     {
398         ret = convert_text(unicode_string, GlobalSize(unicode_handle), -1, cp);
399         GlobalUnlock(unicode_handle);
400     }
401
402     return ret;
403 }
404
405
406 /**************************************************************************
407  *              import_clipboard_data
408  *
409  *  Generic import clipboard data routine.
410  */
411 static HANDLE import_clipboard_data(CFDataRef data)
412 {
413     HANDLE data_handle = NULL;
414
415     size_t len = CFDataGetLength(data);
416     if (len)
417     {
418         LPVOID p;
419
420         /* Turn on the DDESHARE flag to enable shared 32 bit memory */
421         data_handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, len);
422         if (!data_handle)
423             return NULL;
424
425         if ((p = GlobalLock(data_handle)))
426         {
427             memcpy(p, CFDataGetBytePtr(data), len);
428             GlobalUnlock(data_handle);
429         }
430         else
431         {
432             GlobalFree(data_handle);
433             data_handle = NULL;
434         }
435     }
436
437     return data_handle;
438 }
439
440
441 /**************************************************************************
442  *              import_oemtext_to_text
443  *
444  *  Import CF_OEMTEXT data, converting the string to CF_TEXT.
445  */
446 static HANDLE import_oemtext_to_text(CFDataRef data)
447 {
448     return convert_text(CFDataGetBytePtr(data), CFDataGetLength(data), CP_OEMCP, CP_ACP);
449 }
450
451
452 /**************************************************************************
453  *              import_oemtext_to_unicodetext
454  *
455  *  Import CF_OEMTEXT data, converting the string to CF_UNICODETEXT.
456  */
457 static HANDLE import_oemtext_to_unicodetext(CFDataRef data)
458 {
459     return convert_text(CFDataGetBytePtr(data), CFDataGetLength(data), CP_OEMCP, -1);
460 }
461
462
463 /**************************************************************************
464  *              import_text_to_oemtext
465  *
466  *  Import CF_TEXT data, converting the string to CF_OEMTEXT.
467  */
468 static HANDLE import_text_to_oemtext(CFDataRef data)
469 {
470     return convert_text(CFDataGetBytePtr(data), CFDataGetLength(data), CP_ACP, CP_OEMCP);
471 }
472
473
474 /**************************************************************************
475  *              import_text_to_unicodetext
476  *
477  *  Import CF_TEXT data, converting the string to CF_UNICODETEXT.
478  */
479 static HANDLE import_text_to_unicodetext(CFDataRef data)
480 {
481     return convert_text(CFDataGetBytePtr(data), CFDataGetLength(data), CP_ACP, -1);
482 }
483
484
485 /**************************************************************************
486  *              import_unicodetext_to_oemtext
487  *
488  *  Import a CF_UNICODETEXT string, converting the string to CF_OEMTEXT.
489  */
490 static HANDLE import_unicodetext_to_oemtext(CFDataRef data)
491 {
492     return convert_text(CFDataGetBytePtr(data), CFDataGetLength(data), -1, CP_OEMCP);
493 }
494
495
496 /**************************************************************************
497  *              import_unicodetext_to_text
498  *
499  *  Import a CF_UNICODETEXT string, converting the string to CF_TEXT.
500  */
501 static HANDLE import_unicodetext_to_text(CFDataRef data)
502 {
503     return convert_text(CFDataGetBytePtr(data), CFDataGetLength(data), -1, CP_ACP);
504 }
505
506
507 /**************************************************************************
508  *              import_utf8_to_oemtext
509  *
510  *  Import a UTF-8 string, converting the string to CF_OEMTEXT.
511  */
512 static HANDLE import_utf8_to_oemtext(CFDataRef data)
513 {
514     HANDLE unicode_handle = import_utf8_to_unicodetext(data);
515     HANDLE ret = convert_unicodetext_to_codepage(unicode_handle, CP_OEMCP);
516
517     GlobalFree(unicode_handle);
518     return ret;
519 }
520
521
522 /**************************************************************************
523  *              import_utf8_to_text
524  *
525  *  Import a UTF-8 string, converting the string to CF_TEXT.
526  */
527 static HANDLE import_utf8_to_text(CFDataRef data)
528 {
529     HANDLE unicode_handle = import_utf8_to_unicodetext(data);
530     HANDLE ret = convert_unicodetext_to_codepage(unicode_handle, CP_ACP);
531
532     GlobalFree(unicode_handle);
533     return ret;
534 }
535
536
537 /**************************************************************************
538  *              import_utf8_to_unicodetext
539  *
540  *  Import a UTF-8 string, converting the string to CF_UNICODETEXT.
541  */
542 static HANDLE import_utf8_to_unicodetext(CFDataRef data)
543 {
544     const BYTE *src;
545     unsigned long data_len;
546     unsigned long new_lines = 0;
547     LPSTR dst;
548     unsigned long i, j;
549     HANDLE unicode_handle = NULL;
550
551     src = CFDataGetBytePtr(data);
552     data_len = CFDataGetLength(data);
553     for (i = 0; i < data_len; i++)
554     {
555         if (src[i] == '\n')
556             new_lines++;
557     }
558
559     if ((dst = HeapAlloc(GetProcessHeap(), 0, data_len + new_lines + 1)))
560     {
561         UINT count;
562
563         for (i = 0, j = 0; i < data_len; i++)
564         {
565             if (src[i] == '\n')
566                 dst[j++] = '\r';
567
568             dst[j++] = src[i];
569         }
570         dst[j] = 0;
571
572         count = MultiByteToWideChar(CP_UTF8, 0, dst, -1, NULL, 0);
573         unicode_handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, count * sizeof(WCHAR));
574
575         if (unicode_handle)
576         {
577             WCHAR *textW = GlobalLock(unicode_handle);
578             MultiByteToWideChar(CP_UTF8, 0, dst, -1, textW, count);
579             GlobalUnlock(unicode_handle);
580         }
581
582         HeapFree(GetProcessHeap(), 0, dst);
583     }
584
585     return unicode_handle;
586 }
587
588
589 /**************************************************************************
590  *              Mac User Driver Clipboard Exports
591  **************************************************************************/
592
593
594 /**************************************************************************
595  *              CountClipboardFormats (MACDRV.@)
596  */
597 INT CDECL macdrv_CountClipboardFormats(void)
598 {
599     CFMutableSetRef seen_formats;
600     CFArrayRef types;
601     CFIndex count;
602     CFIndex i;
603     INT ret = 0;
604
605     TRACE("()\n");
606
607     seen_formats = CFSetCreateMutable(NULL, 0, NULL);
608     if (!seen_formats)
609     {
610         WARN("Failed to allocate set to track seen formats\n");
611         return 0;
612     }
613
614     types = macdrv_copy_pasteboard_types();
615     if (!types)
616     {
617         WARN("Failed to copy pasteboard types\n");
618         CFRelease(seen_formats);
619         return 0;
620     }
621
622     count = CFArrayGetCount(types);
623     TRACE("got %ld types\n", count);
624
625     for (i = 0; i < count; i++)
626     {
627         CFStringRef type = CFArrayGetValueAtIndex(types, i);
628         WINE_CLIPFORMAT* format;
629
630         format = NULL;
631         while ((format = format_for_type(format, type)))
632         {
633             TRACE("for type %s got format %p/%s\n", debugstr_cf(type), format, debugstr_format(format->format_id));
634
635             if (!CFSetContainsValue(seen_formats, (void*)format->format_id))
636             {
637                 ret++;
638                 CFSetAddValue(seen_formats, (void*)format->format_id);
639             }
640         }
641     }
642
643     CFRelease(seen_formats);
644     TRACE(" -> %d\n", ret);
645     return ret;
646 }
647
648
649 /**************************************************************************
650  *              IsClipboardFormatAvailable (MACDRV.@)
651  */
652 BOOL CDECL macdrv_IsClipboardFormatAvailable(UINT desired_format)
653 {
654     CFArrayRef types;
655     int count;
656     UINT i;
657     BOOL found = FALSE;
658
659     TRACE("desired_format %s\n", debugstr_format(desired_format));
660
661     types = macdrv_copy_pasteboard_types();
662     if (!types)
663     {
664         WARN("Failed to copy pasteboard types\n");
665         return FALSE;
666     }
667
668     count = CFArrayGetCount(types);
669     TRACE("got %d types\n", count);
670
671     for (i = 0; !found && i < count; i++)
672     {
673         CFStringRef type = CFArrayGetValueAtIndex(types, i);
674         WINE_CLIPFORMAT* format;
675
676         format = NULL;
677         while (!found && (format = format_for_type(format, type)))
678         {
679             TRACE("for type %s got format %s\n", debugstr_cf(type), debugstr_format(format->format_id));
680
681             if (format->format_id == desired_format)
682                 found = TRUE;
683         }
684     }
685
686     CFRelease(types);
687     TRACE(" -> %d\n", found);
688     return found;
689 }
690
691
692 /**************************************************************************
693  *              MACDRV Private Clipboard Exports
694  **************************************************************************/
695
696
697 /**************************************************************************
698  *              macdrv_clipboard_process_attach
699  */
700 void macdrv_clipboard_process_attach(void)
701 {
702     UINT i;
703
704     /* Register built-in formats */
705     for (i = 0; i < sizeof(builtin_format_ids)/sizeof(builtin_format_ids[0]); i++)
706     {
707         WINE_CLIPFORMAT *format;
708
709         if (!(format = HeapAlloc(GetProcessHeap(), 0, sizeof(*format)))) break;
710         format->format_id   = builtin_format_ids[i].id;
711         format->type        = CFRetain(builtin_format_ids[i].type);
712         format->import_func = builtin_format_ids[i].import;
713         format->synthesized = builtin_format_ids[i].synthesized;
714         list_add_tail(&format_list, &format->entry);
715     }
716 }