]> Pileus Git - ~andy/gtk/blob - gdk/win32/gdkselection-win32.c
Fix win32 build
[~andy/gtk] / gdk / win32 / gdkselection-win32.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  * Copyright (C) 1998-2002 Tor Lillqvist
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /*
22  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
26  */
27
28 #include "config.h"
29 #include <string.h>
30 #include <stdlib.h>
31
32 #include "gdkproperty.h"
33 #include "gdkselection.h"
34 #include "gdkdisplay.h"
35 #include "gdkprivate-win32.h"
36 #include "gdkwin32.h"
37
38 /* We emulate the GDK_SELECTION window properties of windows (as used
39  * in the X11 backend) by using a hash table from window handles to
40  * GdkSelProp structs.
41  */
42
43 typedef struct {
44   guchar *data;
45   gsize length;
46   gint format;
47   GdkAtom type;
48 } GdkSelProp;
49
50 static GHashTable *sel_prop_table = NULL;
51
52 static GdkSelProp *dropfiles_prop = NULL;
53
54 /* We store the owner of each selection in this table. Obviously, this only
55  * is valid intra-app, and in fact it is necessary for the intra-app DND to work.
56  */
57 static GHashTable *sel_owner_table = NULL;
58
59 /* GdkAtoms for well-known image formats */
60 static GdkAtom *known_pixbuf_formats;
61 static int n_known_pixbuf_formats;
62
63 /* GdkAtoms for well-known text formats */
64 static GdkAtom text_plain;
65 static GdkAtom text_plain_charset_utf_8;
66 static GdkAtom text_plain_charset_CP1252;
67
68 void
69 _gdk_win32_selection_init (void)
70 {
71   GSList *pixbuf_formats;
72   GSList *rover;
73
74   sel_prop_table = g_hash_table_new (NULL, NULL);
75   sel_owner_table = g_hash_table_new (NULL, NULL);
76   _format_atom_table = g_hash_table_new (NULL, NULL);
77
78   pixbuf_formats = gdk_pixbuf_get_formats ();
79
80   n_known_pixbuf_formats = 0;
81   for (rover = pixbuf_formats; rover != NULL; rover = rover->next)
82     {
83       gchar **mime_types =
84         gdk_pixbuf_format_get_mime_types ((GdkPixbufFormat *) rover->data);
85
86       gchar **mime_type;
87
88       for (mime_type = mime_types; *mime_type != NULL; mime_type++)
89         n_known_pixbuf_formats++;
90     }
91
92   known_pixbuf_formats = g_new (GdkAtom, n_known_pixbuf_formats);
93
94   n_known_pixbuf_formats = 0;
95   for (rover = pixbuf_formats; rover != NULL; rover = rover->next)
96     {
97       gchar **mime_types =
98         gdk_pixbuf_format_get_mime_types ((GdkPixbufFormat *) rover->data);
99
100       gchar **mime_type;
101
102       for (mime_type = mime_types; *mime_type != NULL; mime_type++)
103         known_pixbuf_formats[n_known_pixbuf_formats++] = gdk_atom_intern (*mime_type, FALSE);
104     }
105
106   g_slist_free (pixbuf_formats);
107   
108   text_plain = gdk_atom_intern ("text/plain", FALSE);
109   text_plain_charset_utf_8= gdk_atom_intern ("text/plain;charset=utf-8", FALSE);
110   text_plain_charset_CP1252 = gdk_atom_intern ("text/plain;charset=CP1252", FALSE);
111
112   g_hash_table_replace (_format_atom_table,
113                         GINT_TO_POINTER (_cf_png),
114                         _image_png);
115
116   g_hash_table_replace (_format_atom_table,
117                         GINT_TO_POINTER (CF_DIB),
118                         _image_bmp);
119 }
120
121 /* The specifications for COMPOUND_TEXT and STRING specify that C0 and
122  * C1 are not allowed except for \n and \t, however the X conversions
123  * routines for COMPOUND_TEXT only enforce this in one direction,
124  * causing cut-and-paste of \r and \r\n separated text to fail.
125  * This routine strips out all non-allowed C0 and C1 characters
126  * from the input string and also canonicalizes \r, and \r\n to \n
127  */
128 static gchar * 
129 sanitize_utf8 (const gchar *src,
130                gint         length)
131 {
132   GString *result = g_string_sized_new (length + 1);
133   const gchar *p = src;
134   const gchar *endp = src + length;
135
136   while (p < endp)
137     {
138       if (*p == '\r')
139         {
140           p++;
141           if (*p == '\n')
142             p++;
143
144           g_string_append_c (result, '\n');
145         }
146       else
147         {
148           gunichar ch = g_utf8_get_char (p);
149           char buf[7];
150           gint buflen;
151           
152           if (!((ch < 0x20 && ch != '\t' && ch != '\n') || (ch >= 0x7f && ch < 0xa0)))
153             {
154               buflen = g_unichar_to_utf8 (ch, buf);
155               g_string_append_len (result, buf, buflen);
156             }
157
158           p = g_utf8_next_char (p);
159         }
160     }
161   g_string_append_c (result, '\0');
162
163   return g_string_free (result, FALSE);
164 }
165
166 static gchar *
167 _gdk_utf8_to_string_target_internal (const gchar *str,
168                                      gint         length)
169 {
170   GError *error = NULL;
171   
172   gchar *tmp_str = sanitize_utf8 (str, length);
173   gchar *result =  g_convert_with_fallback (tmp_str, -1,
174                                             "ISO-8859-1", "UTF-8",
175                                             NULL, NULL, NULL, &error);
176   if (!result)
177     {
178       g_warning ("Error converting from UTF-8 to STRING: %s",
179                  error->message);
180       g_error_free (error);
181     }
182   
183   g_free (tmp_str);
184   return result;
185 }
186
187 static void
188 selection_property_store (GdkWindow *owner,
189                           GdkAtom    type,
190                           gint       format,
191                           guchar    *data,
192                           gint       length)
193 {
194   GdkSelProp *prop;
195
196   g_return_if_fail (type != GDK_TARGET_STRING);
197
198   prop = g_hash_table_lookup (sel_prop_table, GDK_WINDOW_HWND (owner));
199
200   if (prop != NULL)
201     {
202       g_free (prop->data);
203       g_free (prop);
204       g_hash_table_remove (sel_prop_table, GDK_WINDOW_HWND (owner));
205     }
206
207   prop = g_new (GdkSelProp, 1);
208
209   prop->data = data;
210   prop->length = length;
211   prop->format = format;
212   prop->type = type;
213
214   g_hash_table_insert (sel_prop_table, GDK_WINDOW_HWND (owner), prop);
215 }
216
217 void
218 _gdk_dropfiles_store (gchar *data)
219 {
220   if (data != NULL)
221     {
222       g_assert (dropfiles_prop == NULL);
223
224       dropfiles_prop = g_new (GdkSelProp, 1);
225       dropfiles_prop->data = (guchar *) data;
226       dropfiles_prop->length = strlen (data) + 1;
227       dropfiles_prop->format = 8;
228       dropfiles_prop->type = _text_uri_list;
229     }
230   else
231     {
232       if (dropfiles_prop != NULL)
233         {
234           g_free (dropfiles_prop->data);
235           g_free (dropfiles_prop);
236         }
237       dropfiles_prop = NULL;
238     }
239 }
240
241 static gchar *
242 get_mapped_gdk_atom_name (GdkAtom gdk_target)
243 {
244   if (gdk_target == _image_png)
245     return g_strdup ("PNG");
246
247   if (gdk_target == _image_jpeg)
248     return g_strdup ("JFIF");
249   
250   if (gdk_target == _image_gif)
251     return g_strdup ("GIF");
252   
253   return gdk_atom_name (gdk_target);
254 }
255
256 gboolean
257 _gdk_win32_display_set_selection_owner (GdkDisplay *display,
258                                         GdkWindow  *owner,
259                                         GdkAtom     selection,
260                                         guint32     time,
261                                         gboolean    send_event)
262 {
263   HWND hwnd;
264   GdkEvent tmp_event;
265
266   g_return_val_if_fail (display == _gdk_display, FALSE);
267   g_return_val_if_fail (selection != GDK_NONE, FALSE);
268
269   GDK_NOTE (DND, {
270       gchar *sel_name = gdk_atom_name (selection);
271
272       g_print ("gdk_selection_owner_set_for_display: %p %s\n",
273                (owner ? GDK_WINDOW_HWND (owner) : NULL),
274                sel_name);
275       g_free (sel_name);
276     });
277
278   if (selection != GDK_SELECTION_CLIPBOARD)
279     {
280       if (owner != NULL)
281         g_hash_table_insert (sel_owner_table, selection, GDK_WINDOW_HWND (owner));
282       else
283         g_hash_table_remove (sel_owner_table, selection);
284       return TRUE;
285     }
286
287   /* Rest of this function handles the CLIPBOARD selection */
288   if (owner != NULL)
289     {
290       if (GDK_WINDOW_DESTROYED (owner))
291         return FALSE;
292
293       hwnd = GDK_WINDOW_HWND (owner);
294     }
295   else
296     hwnd = NULL;
297
298   if (!API_CALL (OpenClipboard, (hwnd)))
299     return FALSE;
300
301   _ignore_destroy_clipboard = TRUE;
302   GDK_NOTE (DND, g_print ("... EmptyClipboard()\n"));
303   if (!API_CALL (EmptyClipboard, ()))
304     {
305       _ignore_destroy_clipboard = FALSE;
306       API_CALL (CloseClipboard, ());
307       return FALSE;
308     }
309   _ignore_destroy_clipboard = FALSE;
310
311   if (!API_CALL (CloseClipboard, ()))
312     return FALSE;
313
314   if (owner != NULL)
315     {
316       /* Send ourselves a selection request message so that
317        * gdk_property_change will be called to store the clipboard
318        * data.
319        */
320       GDK_NOTE (DND, g_print ("... sending GDK_SELECTION_REQUEST to ourselves\n"));
321       tmp_event.selection.type = GDK_SELECTION_REQUEST;
322       tmp_event.selection.window = owner;
323       tmp_event.selection.send_event = FALSE;
324       tmp_event.selection.selection = selection;
325       tmp_event.selection.target = _utf8_string;
326       tmp_event.selection.property = _gdk_selection;
327       tmp_event.selection.requestor = hwnd;
328       tmp_event.selection.time = time;
329
330       gdk_event_put (&tmp_event);
331     }
332
333   return TRUE;
334 }
335
336 GdkWindow*
337 _gdk_win32_display_get_selection_owner (GdkDisplay *display,
338                                         GdkAtom     selection)
339 {
340   GdkWindow *window;
341
342   g_return_val_if_fail (display == _gdk_display, NULL);
343   g_return_val_if_fail (selection != GDK_NONE, NULL);
344
345   if (selection == GDK_SELECTION_CLIPBOARD)
346     {
347       HWND owner = GetClipboardOwner ();
348
349       if (owner == NULL)
350         return NULL;
351
352       return gdk_win32_handle_table_lookup ((GdkNativeWindow) owner);
353     }
354
355   window = gdk_win32_window_lookup_for_display (display,
356                                                 (GdkNativeWindow) g_hash_table_lookup (sel_owner_table, selection));
357
358   GDK_NOTE (DND, {
359       gchar *sel_name = gdk_atom_name (selection);
360       
361       g_print ("gdk_selection_owner_get: %s = %p\n",
362                sel_name,
363                (window ? GDK_WINDOW_HWND (window) : NULL));
364       g_free (sel_name);
365     });
366
367   return window;
368 }
369
370 static void
371 generate_selection_notify (GdkWindow *requestor,
372                            GdkAtom    selection,
373                            GdkAtom    target,
374                            GdkAtom    property,
375                            guint32    time)
376 {
377   GdkEvent tmp_event;
378
379   tmp_event.selection.type = GDK_SELECTION_NOTIFY;
380   tmp_event.selection.window = requestor;
381   tmp_event.selection.send_event = FALSE;
382   tmp_event.selection.selection = selection;
383   tmp_event.selection.target = target;
384   tmp_event.selection.property = property;
385   tmp_event.selection.requestor = 0;
386   tmp_event.selection.time = time;
387
388   gdk_event_put (&tmp_event);
389 }
390
391 void
392 _gdk_win32_display_convert_selection (GdkDisplay *display,
393                                       GdkWindow *requestor,
394                                       GdkAtom    selection,
395                                       GdkAtom    target,
396                                       guint32    time)
397 {
398   HGLOBAL hdata;
399   GdkAtom property = _gdk_selection;
400
401   g_return_if_fail (selection != GDK_NONE);
402   g_return_if_fail (requestor != NULL);
403
404   if (GDK_WINDOW_DESTROYED (requestor))
405     return;
406
407   GDK_NOTE (DND, {
408       gchar *sel_name = gdk_atom_name (selection);
409       gchar *tgt_name = gdk_atom_name (target);
410       
411       g_print ("gdk_selection_convert: %p %s %s\n",
412                GDK_WINDOW_HWND (requestor),
413                sel_name, tgt_name);
414       g_free (sel_name);
415       g_free (tgt_name);
416     });
417
418   if (selection == GDK_SELECTION_CLIPBOARD && target == _targets)
419     {
420       gint ntargets, fmt;
421       GdkAtom *targets;
422       gboolean has_text = FALSE;
423       gboolean has_png = FALSE;
424       gboolean has_bmp = FALSE;
425
426       if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor))))
427         return;
428
429       targets = g_new (GdkAtom, CountClipboardFormats ());
430       ntargets = 0;
431
432       for (fmt = 0; 0 != (fmt = EnumClipboardFormats (fmt)); )
433         {
434           if (fmt == _cf_png)
435             {
436               targets[ntargets++] = _image_png;
437               has_png = TRUE;
438             }
439         }
440
441       for (fmt = 0; 0 != (fmt = EnumClipboardFormats (fmt)); )
442         {
443           gchar sFormat[80];
444
445           if (fmt == CF_UNICODETEXT || fmt == CF_TEXT)
446             {
447               /* Advertise text to GDK always as UTF8_STRING */
448               if (!has_text)
449                 targets[ntargets++] = _utf8_string;
450               has_text = TRUE;
451             }
452           else if (fmt == _cf_png)
453             {
454               /* Already handled above */
455             }
456           else if (fmt == CF_DIB ||
457                    fmt == CF_DIBV5)
458             {
459               /* Don't bother telling that a bitmap is present if there is
460                * also PNG, which is much more reliable in transferring
461                * transparency.
462                */
463               if (!has_bmp && !has_png)
464                 targets[ntargets++] = _image_bmp;
465               has_bmp = TRUE;
466             }
467           else if (fmt == _cf_jfif)
468             {
469               /* Ditto for JPEG */
470               if (!has_png)
471                 targets[ntargets++] = _image_jpeg;
472             }
473           else if (fmt == _cf_gif)
474             {
475               /* Ditto for GIF.
476                */
477               if (!has_png)
478                 targets[ntargets++] = _image_gif;
479             }
480           else if (GetClipboardFormatName (fmt, sFormat, 80) > 0)
481             {
482               if (strcmp (sFormat, "image/bmp") == 0 ||
483                   strcmp (sFormat, "image/x-bmp") == 0 ||
484                   strcmp (sFormat, "image/x-MS-bmp") == 0 ||
485                   strcmp (sFormat, "image/x-icon") == 0 ||
486                   strcmp (sFormat, "image/x-ico") == 0 ||
487                   strcmp (sFormat, "image/x-win-bitmap") == 0)
488                 {
489                   /* Ignore these (from older GTK+ versions
490                    * presumably), as the same image in the CF_DIB
491                    * format will also be on the clipboard anyway.
492                    */
493                 }
494               else
495                 targets[ntargets++] = gdk_atom_intern (sFormat, FALSE);
496             }
497         }
498
499       GDK_NOTE (DND, {
500           int i;
501           
502           g_print ("... ");
503           for (i = 0; i < ntargets; i++)
504             {
505               gchar *atom_name = gdk_atom_name (targets[i]);
506
507               g_print ("%s", atom_name);
508               g_free (atom_name);
509               if (i < ntargets - 1)
510                 g_print (", ");
511             }
512           g_print ("\n");
513         });
514
515       if (ntargets > 0)
516         selection_property_store (requestor, GDK_SELECTION_TYPE_ATOM,
517                                   32, (guchar *) targets,
518                                   ntargets * sizeof (GdkAtom));
519       else
520         property = GDK_NONE;
521
522       API_CALL (CloseClipboard, ());
523     }
524   else if (selection == GDK_SELECTION_CLIPBOARD && target == _utf8_string)
525     {
526       /* Converting the CLIPBOARD selection means he wants the
527        * contents of the clipboard. Get the clipboard data, and store
528        * it for later.
529        */
530       if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor))))
531         return;
532
533       if ((hdata = GetClipboardData (CF_UNICODETEXT)) != NULL)
534         {
535           wchar_t *ptr, *wcs, *p, *q;
536           guchar *data;
537           glong length, wclen;
538
539           if ((ptr = GlobalLock (hdata)) != NULL)
540             {
541               length = GlobalSize (hdata);
542               
543               GDK_NOTE (DND, g_print ("... CF_UNICODETEXT: %ld bytes\n",
544                                       length));
545
546               /* Strip out \r */
547               wcs = g_new (wchar_t, length / 2 + 1);
548               p = ptr;
549               q = wcs;
550               wclen = 0;
551               while (p < ptr + length / 2)
552                 {
553                   if (*p != '\r')
554                     {
555                       *q++ = *p;
556                       wclen++;
557                     }
558                   p++;
559                 }
560
561               data = g_utf16_to_utf8 (wcs, wclen, NULL, NULL, NULL);
562               g_free (wcs);
563
564               if (data)
565                 selection_property_store (requestor, _utf8_string, 8,
566                                           data, strlen (data) + 1);
567               GlobalUnlock (hdata);
568             }
569         }
570       else
571         property = GDK_NONE;
572
573       API_CALL (CloseClipboard, ());
574     }
575   else if (selection == GDK_SELECTION_CLIPBOARD && target == _image_bmp)
576     {
577       if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor))))
578         return;
579
580       if ((hdata = GetClipboardData (CF_DIB)) != NULL)
581         {
582           BITMAPINFOHEADER *bi;
583
584           if ((bi = GlobalLock (hdata)) != NULL)
585             {
586               /* Need to add a BMP file header so gdk-pixbuf can load
587                * it.
588                *
589                * If the data is from Mozilla Firefox or IE7, and
590                * starts with an "oldfashioned" BITMAPINFOHEADER,
591                * i.e. with biSize==40, and biCompression == BI_RGB and
592                * biBitCount==32, we assume that the "extra" byte in
593                * each pixel in fact is alpha.
594                *
595                * The gdk-pixbuf bmp loader doesn't trust 32-bit BI_RGB
596                * nitmaps to in fact have alpha, so we have to convince
597                * it by changint the bitmap header to a version 5
598                * BI_BITFIELDS one with explicit alpha mask indicated.
599                *
600                * The RGB bytes that are in bitmaps on the clipboard
601                * originating from Firefox or IE7 seem to be
602                * premultiplied with alpha. The gdk-pixbuf bmp loader
603                * of course doesn't expect that, so we have to undo the
604                * premultiplication before feeding the bitmap to the
605                * bmp loader.
606                *
607                * Note that for some reason the bmp loader used to want
608                * the alpha bytes in its input to actually be
609                * 255-alpha, but here we assume that this has been
610                * fixed before this is committed.
611                */
612               BITMAPFILEHEADER *bf;
613               gpointer data;
614               gint data_length = GlobalSize (hdata);
615               gint new_length;
616               gboolean make_dibv5 = FALSE;
617
618               GDK_NOTE (DND, g_print ("... CF_DIB: %d bytes\n", data_length));
619
620               if (bi->biSize == sizeof (BITMAPINFOHEADER) &&
621                   bi->biPlanes == 1 &&
622                   bi->biBitCount == 32 &&
623                   bi->biCompression == BI_RGB &&
624 #if 0
625                   /* Maybe check explicitly for Mozilla or IE7?
626                    *
627                    * If the clipboard format
628                    * application/x-moz-nativeimage is present, that is
629                    * a reliable indicator that the data is offered by
630                    * Mozilla one would think. For IE7,
631                    * UniformResourceLocatorW is presumably not that
632                    * uniqie, so probably need to do some
633                    * GetClipboardOwner(), GetWindowThreadProcessId(),
634                    * OpenProcess(), GetModuleFileNameEx() dance to
635                    * check?
636                    */
637                   (IsClipboardFormatAvailable
638                    (RegisterClipboardFormat ("application/x-moz-nativeimage")) ||
639                    IsClipboardFormatAvailable
640                    (RegisterClipboardFormat ("UniformResourceLocatorW"))) &&
641 #endif
642                   TRUE)
643                 {
644                   /* We turn the BITMAPINFOHEADER into a
645                    * BITMAPV5HEADER before feeding it to gdk-pixbuf.
646                    */
647                   new_length = (data_length +
648                                 sizeof (BITMAPFILEHEADER) +
649                                 (sizeof (BITMAPV5HEADER) - sizeof (BITMAPINFOHEADER)));
650                   make_dibv5 = TRUE;
651                 }
652               else
653                 {
654                   new_length = data_length + sizeof (BITMAPFILEHEADER);
655                 }
656               
657               data = g_try_malloc (new_length);
658
659               if (data)
660                 {
661                   bf = (BITMAPFILEHEADER *)data;
662                   bf->bfType = 0x4d42; /* "BM" */
663                   bf->bfSize = new_length;
664                   bf->bfReserved1 = 0;
665                   bf->bfReserved2 = 0;
666
667                   if (make_dibv5)
668                     {
669                       BITMAPV5HEADER *bV5 = (BITMAPV5HEADER *) ((char *) data + sizeof (BITMAPFILEHEADER));
670                       guchar *p;
671                       int i;
672
673                       bV5->bV5Size = sizeof (BITMAPV5HEADER);
674                       bV5->bV5Width = bi->biWidth;
675                       bV5->bV5Height = bi->biHeight;
676                       bV5->bV5Planes = 1;
677                       bV5->bV5BitCount = 32;
678                       bV5->bV5Compression = BI_BITFIELDS;
679                       bV5->bV5SizeImage = 4 * bV5->bV5Width * ABS (bV5->bV5Height);
680                       bV5->bV5XPelsPerMeter = bi->biXPelsPerMeter;
681                       bV5->bV5YPelsPerMeter = bi->biYPelsPerMeter;
682                       bV5->bV5ClrUsed = 0;
683                       bV5->bV5ClrImportant = 0;
684                       /* Now the added mask fields */
685                       bV5->bV5RedMask   = 0x00ff0000;
686                       bV5->bV5GreenMask = 0x0000ff00;
687                       bV5->bV5BlueMask  = 0x000000ff;
688                       bV5->bV5AlphaMask = 0xff000000;
689                       ((char *) &bV5->bV5CSType)[3] = 's';
690                       ((char *) &bV5->bV5CSType)[2] = 'R';
691                       ((char *) &bV5->bV5CSType)[1] = 'G';
692                       ((char *) &bV5->bV5CSType)[0] = 'B';
693                       /* Ignore colorspace and profile fields */
694                       bV5->bV5Intent = LCS_GM_GRAPHICS;
695                       bV5->bV5Reserved = 0;
696
697                       bf->bfOffBits = (sizeof (BITMAPFILEHEADER) +
698                                        bV5->bV5Size);
699
700                       p = ((guchar *) data) + sizeof (BITMAPFILEHEADER) + sizeof (BITMAPV5HEADER);
701                       memcpy (p, ((char *) bi) + bi->biSize,
702                               data_length - sizeof (BITMAPINFOHEADER));
703
704                       for (i = 0; i < bV5->bV5SizeImage/4; i++)
705                         {
706                           if (p[3] != 0)
707                             {
708                               gdouble inverse_alpha = 255./p[3];
709                               
710                               p[0] = p[0] * inverse_alpha + 0.5;
711                               p[1] = p[1] * inverse_alpha + 0.5;
712                               p[2] = p[2] * inverse_alpha + 0.5;
713                             }
714
715                           p += 4;
716                         }
717                     }
718                   else
719                     {
720                       bf->bfOffBits = (sizeof (BITMAPFILEHEADER) +
721                                        bi->biSize +
722                                        bi->biClrUsed * sizeof (RGBQUAD));
723
724                       memcpy ((char *) data + sizeof (BITMAPFILEHEADER),
725                               bi,
726                               data_length);
727                     }
728
729                   selection_property_store (requestor, _image_bmp, 8,
730                                             data, new_length);
731                 }
732               GlobalUnlock (hdata);
733             }
734       }
735
736       API_CALL (CloseClipboard, ());
737     }
738   else if (selection == GDK_SELECTION_CLIPBOARD)
739     {
740       gchar *mapped_target_name;
741       UINT fmt = 0;
742
743       if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor))))
744         return;
745
746       mapped_target_name = get_mapped_gdk_atom_name (target);
747
748       /* Check if it's available. We could simply call
749        * GetClipboardData (RegisterClipboardFormat (targetname)), but
750        * the global custom format ID space is limited,
751        * (0xC000~0xFFFF), and we better not waste an format ID if we
752        * are just a requestor.
753        */
754       for ( ; 0 != (fmt = EnumClipboardFormats (fmt)); )
755         {
756           char sFormat[80];
757
758           if (GetClipboardFormatName (fmt, sFormat, 80) > 0 && 
759               strcmp (sFormat, mapped_target_name) == 0)
760             {
761               if ((hdata = GetClipboardData (fmt)) != NULL)
762                 {
763                   /* Simply get it without conversion */
764                   guchar *ptr;
765                   gint length;
766
767                   if ((ptr = GlobalLock (hdata)) != NULL)
768                     {
769                       length = GlobalSize (hdata);
770               
771                       GDK_NOTE (DND, g_print ("... %s: %d bytes\n", mapped_target_name, length));
772               
773                       selection_property_store (requestor, target, 8,
774                                                 g_memdup (ptr, length), length);
775                       GlobalUnlock (hdata);
776                       break;
777                     }
778                 }
779             }
780         }
781       g_free (mapped_target_name);
782       API_CALL (CloseClipboard, ());
783     }
784   else if (selection == _gdk_win32_dropfiles)
785     {
786       /* This means he wants the names of the dropped files.
787        * gdk_dropfiles_filter already has stored the text/uri-list
788        * data temporarily in dropfiles_prop.
789        */
790       if (dropfiles_prop != NULL)
791         {
792           selection_property_store
793             (requestor, dropfiles_prop->type, dropfiles_prop->format,
794              dropfiles_prop->data, dropfiles_prop->length);
795           g_free (dropfiles_prop);
796           dropfiles_prop = NULL;
797         }
798     }
799   else
800     property = GDK_NONE;
801
802   /* Generate a selection notify message so that we actually fetch the
803    * data (if property == _gdk_selection) or indicating failure (if
804    * property == GDK_NONE).
805    */
806   generate_selection_notify (requestor, selection, target, property, time);
807 }
808
809 gint
810 _gdk_win32_display_get_selection_property (GdkDisplay *display,
811                                            GdkWindow  *requestor,
812                                            guchar    **data,
813                                            GdkAtom    *ret_type,
814                                            gint       *ret_format)
815 {
816   GdkSelProp *prop;
817
818   g_return_val_if_fail (requestor != NULL, 0);
819   g_return_val_if_fail (GDK_IS_WINDOW (requestor), 0);
820
821   if (GDK_WINDOW_DESTROYED (requestor))
822     return 0;
823   
824   GDK_NOTE (DND, g_print ("gdk_selection_property_get: %p",
825                            GDK_WINDOW_HWND (requestor)));
826
827   prop = g_hash_table_lookup (sel_prop_table, GDK_WINDOW_HWND (requestor));
828
829   if (prop == NULL)
830     {
831       GDK_NOTE (DND, g_print (" (nothing)\n"));
832       *data = NULL;
833
834       return 0;
835     }
836
837   *data = g_malloc (prop->length + 1);
838   (*data)[prop->length] = '\0';
839   if (prop->length > 0)
840     memmove (*data, prop->data, prop->length);
841
842   GDK_NOTE (DND, {
843       gchar *type_name = gdk_atom_name (prop->type);
844
845       g_print (" %s format:%d length:%d\n", type_name, prop->format, prop->length);
846       g_free (type_name);
847     });
848
849   if (ret_type)
850     *ret_type = prop->type;
851
852   if (ret_format)
853     *ret_format = prop->format;
854
855   return prop->length;
856 }
857
858 void
859 _gdk_selection_property_delete (GdkWindow *window)
860 {
861   GDK_NOTE (DND, g_print ("_gdk_selection_property_delete: %p (no-op)\n",
862                            GDK_WINDOW_HWND (window)));
863
864 #if 0
865   prop = g_hash_table_lookup (sel_prop_table, GDK_WINDOW_HWND (window));
866   if (prop != NULL)
867     {
868       g_free (prop->data);
869       g_free (prop);
870       g_hash_table_remove (sel_prop_table, GDK_WINDOW_HWND (window));
871     }
872 #endif
873 }
874
875 void
876 _gdk_win32_display_send_selection_notify (GdkDisplay      *display,
877                                           GdkNativeWindow  requestor,
878                                           GdkAtom       selection,
879                                           GdkAtom       target,
880                                           GdkAtom       property,
881                                           guint32       time)
882 {
883   g_return_if_fail (display == _gdk_display);
884
885   GDK_NOTE (DND, {
886       gchar *sel_name = gdk_atom_name (selection);
887       gchar *tgt_name = gdk_atom_name (target);
888       gchar *prop_name = gdk_atom_name (property);
889       
890       g_print ("gdk_selection_send_notify_for_display: %p %s %s %s (no-op)\n",
891                requestor, sel_name, tgt_name, prop_name);
892       g_free (sel_name);
893       g_free (tgt_name);
894       g_free (prop_name);
895     });
896 }
897
898 /* It's hard to say whether implementing this actually is of any use
899  * on the Win32 platform? gtk calls only
900  * gdk_text_property_to_utf8_list_for_display().
901  */
902 gint
903 gdk_text_property_to_text_list_for_display (GdkDisplay   *display,
904                                             GdkAtom       encoding,
905                                             gint          format, 
906                                             const guchar *text,
907                                             gint          length,
908                                             gchar      ***list)
909 {
910   gchar *result;
911   const gchar *charset;
912   gchar *source_charset;
913
914   g_return_val_if_fail (display == _gdk_display, 0);
915
916   GDK_NOTE (DND, {
917       gchar *enc_name = gdk_atom_name (encoding);
918       
919       g_print ("gdk_text_property_to_text_list_for_display: %s %d %.20s %d\n",
920                enc_name, format, text, length);
921       g_free (enc_name);
922     });
923     
924   if (!list)
925     return 0;
926
927   if (encoding == GDK_TARGET_STRING)
928     source_charset = g_strdup ("ISO-8859-1");
929   else if (encoding == _utf8_string)
930     source_charset = g_strdup ("UTF-8");
931   else
932     source_charset = gdk_atom_name (encoding);
933     
934   g_get_charset (&charset);
935
936   result = g_convert (text, length, charset, source_charset,
937                       NULL, NULL, NULL);
938   g_free (source_charset);
939
940   if (!result)
941     return 0;
942
943   *list = g_new (gchar *, 1);
944   **list = result;
945   
946   return 1;
947 }
948
949 void
950 gdk_free_text_list (gchar **list)
951 {
952   g_return_if_fail (list != NULL);
953
954   g_free (*list);
955   g_free (list);
956 }
957
958 static gint
959 make_list (const gchar  *text,
960            gint          length,
961            gboolean      latin1,
962            gchar      ***list)
963 {
964   GSList *strings = NULL;
965   gint n_strings = 0;
966   gint i;
967   const gchar *p = text;
968   const gchar *q;
969   GSList *tmp_list;
970   GError *error = NULL;
971
972   while (p < text + length)
973     {
974       gchar *str;
975       
976       q = p;
977       while (*q && q < text + length)
978         q++;
979
980       if (latin1)
981         {
982           str = g_convert (p, q - p,
983                            "UTF-8", "ISO-8859-1",
984                            NULL, NULL, &error);
985
986           if (!str)
987             {
988               g_warning ("Error converting selection from STRING: %s",
989                          error->message);
990               g_error_free (error);
991             }
992         }
993       else
994         str = g_strndup (p, q - p);
995
996       if (str)
997         {
998           strings = g_slist_prepend (strings, str);
999           n_strings++;
1000         }
1001
1002       p = q + 1;
1003     }
1004
1005   if (list)
1006     *list = g_new (gchar *, n_strings + 1);
1007
1008   (*list)[n_strings] = NULL;
1009   
1010   i = n_strings;
1011   tmp_list = strings;
1012   while (tmp_list)
1013     {
1014       if (list)
1015         (*list)[--i] = tmp_list->data;
1016       else
1017         g_free (tmp_list->data);
1018
1019       tmp_list = tmp_list->next;
1020     }
1021
1022   g_slist_free (strings);
1023
1024   return n_strings;
1025 }
1026
1027 gint
1028 _gdk_win32_display_text_property_to_utf8_list (GdkDisplay    *display,
1029                                                GdkAtom        encoding,
1030                                                gint           format,
1031                                                const guchar  *text,
1032                                                gint           length,
1033                                                gchar       ***list)
1034 {
1035   g_return_val_if_fail (text != NULL, 0);
1036   g_return_val_if_fail (length >= 0, 0);
1037   g_return_val_if_fail (display == _gdk_display, 0);
1038
1039   if (encoding == GDK_TARGET_STRING)
1040     {
1041       return make_list ((gchar *)text, length, TRUE, list);
1042     }
1043   else if (encoding == _utf8_string)
1044     {
1045       return make_list ((gchar *)text, length, FALSE, list);
1046     }
1047   else
1048     {
1049       gchar *enc_name = gdk_atom_name (encoding);
1050
1051       g_warning ("gdk_text_property_to_utf8_list_for_display: encoding %s not handled\n", enc_name);
1052       g_free (enc_name);
1053
1054       if (list)
1055         *list = NULL;
1056
1057       return 0;
1058     }
1059 }
1060
1061 gint
1062 gdk_string_to_compound_text_for_display (GdkDisplay  *display,
1063                                          const gchar *str,
1064                                          GdkAtom     *encoding,
1065                                          gint        *format,
1066                                          guchar     **ctext,
1067                                          gint        *length)
1068 {
1069   g_return_val_if_fail (str != NULL, 0);
1070   g_return_val_if_fail (length >= 0, 0);
1071   g_return_val_if_fail (display == _gdk_display, 0);
1072
1073   GDK_NOTE (DND, g_print ("gdk_string_to_compound_text_for_display: %.20s\n", str));
1074
1075   /* Always fail on Win32. No COMPOUND_TEXT support. */
1076
1077   if (encoding)
1078     *encoding = GDK_NONE;
1079
1080   if (format)
1081     *format = 0;
1082
1083   if (ctext)
1084     *ctext = NULL;
1085
1086   if (length)
1087     *length = 0;
1088
1089   return -1;
1090 }
1091
1092 gchar *
1093 _gdk_win32_display_utf8_to_string_target (GdkDisplay *display,
1094                                           const gchar *str)
1095 {
1096   return _gdk_utf8_to_string_target_internal (str, strlen (str));
1097 }
1098
1099 gboolean
1100 gdk_utf8_to_compound_text_for_display (GdkDisplay  *display,
1101                                        const gchar *str,
1102                                        GdkAtom     *encoding,
1103                                        gint        *format,
1104                                        guchar     **ctext,
1105                                        gint        *length)
1106 {
1107   g_return_val_if_fail (str != NULL, FALSE);
1108   g_return_val_if_fail (display == _gdk_display, FALSE);
1109
1110   GDK_NOTE (DND, g_print ("gdk_utf8_to_compound_text_for_display: %.20s\n", str));
1111
1112   /* Always fail on Win32. No COMPOUND_TEXT support. */
1113
1114   if (encoding)
1115     *encoding = GDK_NONE;
1116
1117   if (format)
1118     *format = 0;
1119   
1120   if (ctext)
1121     *ctext = NULL;
1122
1123   if (length)
1124     *length = 0;
1125
1126   return FALSE;
1127 }
1128
1129 void
1130 gdk_free_compound_text (guchar *ctext)
1131 {
1132   /* As we never generate anything claimed to be COMPOUND_TEXT, this
1133    * should never be called. Or if it is called, ctext should be the
1134    * NULL returned for conversions to COMPOUND_TEXT above.
1135    */
1136   g_return_if_fail (ctext == NULL);
1137 }
1138
1139 /* This function is called from gtk_selection_add_target() and
1140  * gtk_selection_add_targets() in gtkselection.c. It is this function
1141  * that takes care of setting those clipboard formats for which we use
1142  * delayed rendering. Formats copied directly to the clipboard are
1143  * handled in gdk_property_change() in gdkproperty-win32.c.
1144  */
1145
1146 void
1147 gdk_win32_selection_add_targets (GdkWindow  *owner,
1148                                  GdkAtom     selection,
1149                                  gint        n_targets,
1150                                  GdkAtom    *targets)
1151 {
1152   HWND hwnd = NULL;
1153   gboolean has_image = FALSE;
1154   gint i;
1155
1156   GDK_NOTE (DND, {
1157       gchar *sel_name = gdk_atom_name (selection);
1158       
1159       g_print ("gdk_win32_selection_add_targets: %p: %s: ",
1160                owner ? GDK_WINDOW_HWND (owner) : NULL,
1161                sel_name);
1162       g_free (sel_name);
1163
1164       for (i = 0; i < n_targets; i++)
1165         {
1166           gchar *tgt_name = gdk_atom_name (targets[i]);
1167
1168           g_print ("%s", tgt_name);
1169           g_free (tgt_name);
1170           if (i < n_targets - 1)
1171             g_print (", ");
1172         }
1173       g_print ("\n");
1174     });
1175
1176   if (selection != GDK_SELECTION_CLIPBOARD)
1177     return;
1178
1179   if (owner != NULL)
1180     {
1181       if (GDK_WINDOW_DESTROYED (owner))
1182         return;
1183       hwnd = GDK_WINDOW_HWND (owner);
1184     }
1185
1186   if (!API_CALL (OpenClipboard, (hwnd)))
1187     return;
1188
1189   /* We have a very simple strategy: If some kind of pixmap image
1190    * format is being added, actually advertise just PNG and DIB. PNG
1191    * is our preferred format because it can losslessly represent any
1192    * image that gdk-pixbuf formats in general can, even with alpha,
1193    * unambiguously. CF_DIB is also advertised because of the general
1194    * support for it in Windows software, but note that alpha won't be
1195    * handled.
1196    */
1197   for (i = 0; !has_image && i < n_targets; ++i)
1198     {
1199       UINT cf;
1200       gchar *target_name;
1201       int j;
1202       
1203       for (j = 0; j < n_known_pixbuf_formats; j++)
1204         if (targets[i] == known_pixbuf_formats[j])
1205           {
1206             if (!has_image)
1207               {
1208                 GDK_NOTE (DND, g_print ("... SetClipboardData(PNG,NULL)\n"));
1209                 SetClipboardData (_cf_png, NULL);
1210
1211                 GDK_NOTE (DND, g_print ("... SetClipboardData(CF_DIB,NULL)\n"));
1212                 SetClipboardData (CF_DIB, NULL);
1213
1214                 has_image = TRUE;
1215               }
1216             break;
1217           }
1218       
1219       /* If it is one of the pixmap formats, already handled or not
1220        * needed.
1221        */
1222       if (j < n_known_pixbuf_formats)
1223         continue;
1224
1225       /* We don't bother registering and advertising clipboard formats
1226        * that are X11 specific or no non-GTK+ apps will have ever
1227        * heard of, and when there are equivalent clipboard formats
1228        * that are commonly used.
1229        */
1230       if (targets[i] == _save_targets ||
1231           targets[i] == _utf8_string ||
1232           targets[i] == GDK_TARGET_STRING ||
1233           targets[i] == _compound_text ||
1234           targets[i] == _text ||
1235           targets[i] == text_plain_charset_utf_8 ||
1236           targets[i] == text_plain_charset_CP1252 ||
1237           targets[i] == text_plain)
1238         continue;
1239
1240       target_name = gdk_atom_name (targets[i]);
1241
1242       if (g_str_has_prefix (target_name, "text/plain;charset="))
1243         {
1244           g_free (target_name);
1245           continue;
1246         }
1247
1248       cf = RegisterClipboardFormat (target_name);
1249
1250       g_hash_table_replace (_format_atom_table,
1251                             GINT_TO_POINTER (cf),
1252                             targets[i]);
1253       
1254       GDK_NOTE (DND, g_print ("... SetClipboardData(%s,NULL)\n",
1255                               _gdk_win32_cf_to_string (cf)));
1256       SetClipboardData (cf, NULL);
1257
1258       g_free (target_name);
1259     }
1260   API_CALL (CloseClipboard, ());
1261 }
1262
1263 /* Convert from types such as "image/jpg" or "image/png" to DIB using
1264  * gdk-pixbuf so that image copied from GTK+ apps can be pasted in
1265  * native apps like mspaint.exe
1266  */
1267 HGLOBAL
1268 _gdk_win32_selection_convert_to_dib (HGLOBAL  hdata,
1269                                      GdkAtom  target)
1270 {
1271   GDK_NOTE (DND, {
1272       gchar *target_name = gdk_atom_name (target);
1273
1274       g_print ("_gdk_win32_selection_convert_to_dib: %p %s\n",
1275                hdata, target_name);
1276       g_free (target_name);
1277     });
1278
1279   if (target == _image_bmp)
1280     {
1281       /* No conversion is needed, just strip the BITMAPFILEHEADER */
1282       HGLOBAL hdatanew;
1283       SIZE_T size = GlobalSize (hdata) - sizeof (BITMAPFILEHEADER);
1284       guchar *ptr = GlobalLock (hdata);
1285
1286       memmove (ptr, ptr + sizeof (BITMAPFILEHEADER), size);
1287       GlobalUnlock (hdata);
1288
1289       if ((hdatanew = GlobalReAlloc (hdata, size, GMEM_MOVEABLE)) == NULL)
1290         {
1291           WIN32_API_FAILED ("GlobalReAlloc");
1292           GlobalFree (hdata); /* The old hdata is not freed if error */
1293         }
1294       return hdatanew;
1295     }
1296
1297   g_warning ("Should not happen: We provide some image format but not CF_DIB and CF_DIB is requested.");
1298
1299   return NULL;
1300 }