]> Pileus Git - ~andy/gtk/blob - gdk/win32/gdkselection-win32.c
c734b0be46f45a19e7141db851cc8ca4234a4ffc
[~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 = gdk_win32_handle_table_lookup (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 (owner);
353     }
354
355   window = gdk_win32_window_lookup_for_display (display,
356                                                 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 "old fashioned" 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                * bitmaps to in fact have alpha, so we have to convince
597                * it by changing 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                       if (bi->biCompression == BI_BITFIELDS && bi->biBitCount >= 16)
725                         {
726                           /* Screenshots taken with PrintScreen or
727                            * Alt + PrintScreen are found on the clipboard in
728                            * this format. In this case the BITMAPINFOHEADER is
729                            * followed by three DWORD specifying the masks of the
730                            * red green and blue components, so adjust the offset
731                            * accordingly. */
732                           bf->bfOffBits += (3 * sizeof (DWORD));
733                         }
734
735                       memcpy ((char *) data + sizeof (BITMAPFILEHEADER),
736                               bi,
737                               data_length);
738                     }
739
740                   selection_property_store (requestor, _image_bmp, 8,
741                                             data, new_length);
742                 }
743               GlobalUnlock (hdata);
744             }
745       }
746
747       API_CALL (CloseClipboard, ());
748     }
749   else if (selection == GDK_SELECTION_CLIPBOARD)
750     {
751       gchar *mapped_target_name;
752       UINT fmt = 0;
753
754       if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor))))
755         return;
756
757       mapped_target_name = get_mapped_gdk_atom_name (target);
758
759       /* Check if it's available. We could simply call
760        * GetClipboardData (RegisterClipboardFormat (targetname)), but
761        * the global custom format ID space is limited,
762        * (0xC000~0xFFFF), and we better not waste an format ID if we
763        * are just a requestor.
764        */
765       for ( ; 0 != (fmt = EnumClipboardFormats (fmt)); )
766         {
767           char sFormat[80];
768
769           if (GetClipboardFormatName (fmt, sFormat, 80) > 0 && 
770               strcmp (sFormat, mapped_target_name) == 0)
771             {
772               if ((hdata = GetClipboardData (fmt)) != NULL)
773                 {
774                   /* Simply get it without conversion */
775                   guchar *ptr;
776                   gint length;
777
778                   if ((ptr = GlobalLock (hdata)) != NULL)
779                     {
780                       length = GlobalSize (hdata);
781               
782                       GDK_NOTE (DND, g_print ("... %s: %d bytes\n", mapped_target_name, length));
783               
784                       selection_property_store (requestor, target, 8,
785                                                 g_memdup (ptr, length), length);
786                       GlobalUnlock (hdata);
787                       break;
788                     }
789                 }
790             }
791         }
792       g_free (mapped_target_name);
793       API_CALL (CloseClipboard, ());
794     }
795   else if (selection == _gdk_win32_dropfiles)
796     {
797       /* This means he wants the names of the dropped files.
798        * gdk_dropfiles_filter already has stored the text/uri-list
799        * data temporarily in dropfiles_prop.
800        */
801       if (dropfiles_prop != NULL)
802         {
803           selection_property_store
804             (requestor, dropfiles_prop->type, dropfiles_prop->format,
805              dropfiles_prop->data, dropfiles_prop->length);
806           g_free (dropfiles_prop);
807           dropfiles_prop = NULL;
808         }
809     }
810   else
811     property = GDK_NONE;
812
813   /* Generate a selection notify message so that we actually fetch the
814    * data (if property == _gdk_selection) or indicating failure (if
815    * property == GDK_NONE).
816    */
817   generate_selection_notify (requestor, selection, target, property, time);
818 }
819
820 gint
821 _gdk_win32_display_get_selection_property (GdkDisplay *display,
822                                            GdkWindow  *requestor,
823                                            guchar    **data,
824                                            GdkAtom    *ret_type,
825                                            gint       *ret_format)
826 {
827   GdkSelProp *prop;
828
829   g_return_val_if_fail (requestor != NULL, 0);
830   g_return_val_if_fail (GDK_IS_WINDOW (requestor), 0);
831
832   if (GDK_WINDOW_DESTROYED (requestor))
833     return 0;
834   
835   GDK_NOTE (DND, g_print ("gdk_selection_property_get: %p",
836                            GDK_WINDOW_HWND (requestor)));
837
838   prop = g_hash_table_lookup (sel_prop_table, GDK_WINDOW_HWND (requestor));
839
840   if (prop == NULL)
841     {
842       GDK_NOTE (DND, g_print (" (nothing)\n"));
843       *data = NULL;
844
845       return 0;
846     }
847
848   *data = g_malloc (prop->length + 1);
849   (*data)[prop->length] = '\0';
850   if (prop->length > 0)
851     memmove (*data, prop->data, prop->length);
852
853   GDK_NOTE (DND, {
854       gchar *type_name = gdk_atom_name (prop->type);
855
856       g_print (" %s format:%d length:%d\n", type_name, prop->format, prop->length);
857       g_free (type_name);
858     });
859
860   if (ret_type)
861     *ret_type = prop->type;
862
863   if (ret_format)
864     *ret_format = prop->format;
865
866   return prop->length;
867 }
868
869 void
870 _gdk_selection_property_delete (GdkWindow *window)
871 {
872   GDK_NOTE (DND, g_print ("_gdk_selection_property_delete: %p (no-op)\n",
873                            GDK_WINDOW_HWND (window)));
874
875 #if 0
876   prop = g_hash_table_lookup (sel_prop_table, GDK_WINDOW_HWND (window));
877   if (prop != NULL)
878     {
879       g_free (prop->data);
880       g_free (prop);
881       g_hash_table_remove (sel_prop_table, GDK_WINDOW_HWND (window));
882     }
883 #endif
884 }
885
886 void
887 _gdk_win32_display_send_selection_notify (GdkDisplay   *display,
888                                           GdkWindow    *requestor,
889                                           GdkAtom       selection,
890                                           GdkAtom       target,
891                                           GdkAtom       property,
892                                           guint32       time)
893 {
894   g_return_if_fail (display == _gdk_display);
895
896   GDK_NOTE (DND, {
897       gchar *sel_name = gdk_atom_name (selection);
898       gchar *tgt_name = gdk_atom_name (target);
899       gchar *prop_name = gdk_atom_name (property);
900       
901       g_print ("gdk_selection_send_notify_for_display: %p %s %s %s (no-op)\n",
902                requestor, sel_name, tgt_name, prop_name);
903       g_free (sel_name);
904       g_free (tgt_name);
905       g_free (prop_name);
906     });
907 }
908
909 /* It's hard to say whether implementing this actually is of any use
910  * on the Win32 platform? gtk calls only
911  * gdk_text_property_to_utf8_list_for_display().
912  */
913 gint
914 gdk_text_property_to_text_list_for_display (GdkDisplay   *display,
915                                             GdkAtom       encoding,
916                                             gint          format, 
917                                             const guchar *text,
918                                             gint          length,
919                                             gchar      ***list)
920 {
921   gchar *result;
922   const gchar *charset;
923   gchar *source_charset;
924
925   g_return_val_if_fail (display == _gdk_display, 0);
926
927   GDK_NOTE (DND, {
928       gchar *enc_name = gdk_atom_name (encoding);
929       
930       g_print ("gdk_text_property_to_text_list_for_display: %s %d %.20s %d\n",
931                enc_name, format, text, length);
932       g_free (enc_name);
933     });
934     
935   if (!list)
936     return 0;
937
938   if (encoding == GDK_TARGET_STRING)
939     source_charset = g_strdup ("ISO-8859-1");
940   else if (encoding == _utf8_string)
941     source_charset = g_strdup ("UTF-8");
942   else
943     source_charset = gdk_atom_name (encoding);
944     
945   g_get_charset (&charset);
946
947   result = g_convert (text, length, charset, source_charset,
948                       NULL, NULL, NULL);
949   g_free (source_charset);
950
951   if (!result)
952     return 0;
953
954   *list = g_new (gchar *, 1);
955   **list = result;
956   
957   return 1;
958 }
959
960 void
961 gdk_free_text_list (gchar **list)
962 {
963   g_return_if_fail (list != NULL);
964
965   g_free (*list);
966   g_free (list);
967 }
968
969 static gint
970 make_list (const gchar  *text,
971            gint          length,
972            gboolean      latin1,
973            gchar      ***list)
974 {
975   GSList *strings = NULL;
976   gint n_strings = 0;
977   gint i;
978   const gchar *p = text;
979   const gchar *q;
980   GSList *tmp_list;
981   GError *error = NULL;
982
983   while (p < text + length)
984     {
985       gchar *str;
986       
987       q = p;
988       while (*q && q < text + length)
989         q++;
990
991       if (latin1)
992         {
993           str = g_convert (p, q - p,
994                            "UTF-8", "ISO-8859-1",
995                            NULL, NULL, &error);
996
997           if (!str)
998             {
999               g_warning ("Error converting selection from STRING: %s",
1000                          error->message);
1001               g_error_free (error);
1002             }
1003         }
1004       else
1005         str = g_strndup (p, q - p);
1006
1007       if (str)
1008         {
1009           strings = g_slist_prepend (strings, str);
1010           n_strings++;
1011         }
1012
1013       p = q + 1;
1014     }
1015
1016   if (list)
1017     *list = g_new (gchar *, n_strings + 1);
1018
1019   (*list)[n_strings] = NULL;
1020   
1021   i = n_strings;
1022   tmp_list = strings;
1023   while (tmp_list)
1024     {
1025       if (list)
1026         (*list)[--i] = tmp_list->data;
1027       else
1028         g_free (tmp_list->data);
1029
1030       tmp_list = tmp_list->next;
1031     }
1032
1033   g_slist_free (strings);
1034
1035   return n_strings;
1036 }
1037
1038 gint
1039 _gdk_win32_display_text_property_to_utf8_list (GdkDisplay    *display,
1040                                                GdkAtom        encoding,
1041                                                gint           format,
1042                                                const guchar  *text,
1043                                                gint           length,
1044                                                gchar       ***list)
1045 {
1046   g_return_val_if_fail (text != NULL, 0);
1047   g_return_val_if_fail (length >= 0, 0);
1048   g_return_val_if_fail (display == _gdk_display, 0);
1049
1050   if (encoding == GDK_TARGET_STRING)
1051     {
1052       return make_list ((gchar *)text, length, TRUE, list);
1053     }
1054   else if (encoding == _utf8_string)
1055     {
1056       return make_list ((gchar *)text, length, FALSE, list);
1057     }
1058   else
1059     {
1060       gchar *enc_name = gdk_atom_name (encoding);
1061
1062       g_warning ("gdk_text_property_to_utf8_list_for_display: encoding %s not handled\n", enc_name);
1063       g_free (enc_name);
1064
1065       if (list)
1066         *list = NULL;
1067
1068       return 0;
1069     }
1070 }
1071
1072 gint
1073 gdk_string_to_compound_text_for_display (GdkDisplay  *display,
1074                                          const gchar *str,
1075                                          GdkAtom     *encoding,
1076                                          gint        *format,
1077                                          guchar     **ctext,
1078                                          gint        *length)
1079 {
1080   g_return_val_if_fail (str != NULL, 0);
1081   g_return_val_if_fail (length >= 0, 0);
1082   g_return_val_if_fail (display == _gdk_display, 0);
1083
1084   GDK_NOTE (DND, g_print ("gdk_string_to_compound_text_for_display: %.20s\n", str));
1085
1086   /* Always fail on Win32. No COMPOUND_TEXT support. */
1087
1088   if (encoding)
1089     *encoding = GDK_NONE;
1090
1091   if (format)
1092     *format = 0;
1093
1094   if (ctext)
1095     *ctext = NULL;
1096
1097   if (length)
1098     *length = 0;
1099
1100   return -1;
1101 }
1102
1103 gchar *
1104 _gdk_win32_display_utf8_to_string_target (GdkDisplay *display,
1105                                           const gchar *str)
1106 {
1107   return _gdk_utf8_to_string_target_internal (str, strlen (str));
1108 }
1109
1110 gboolean
1111 gdk_utf8_to_compound_text_for_display (GdkDisplay  *display,
1112                                        const gchar *str,
1113                                        GdkAtom     *encoding,
1114                                        gint        *format,
1115                                        guchar     **ctext,
1116                                        gint        *length)
1117 {
1118   g_return_val_if_fail (str != NULL, FALSE);
1119   g_return_val_if_fail (display == _gdk_display, FALSE);
1120
1121   GDK_NOTE (DND, g_print ("gdk_utf8_to_compound_text_for_display: %.20s\n", str));
1122
1123   /* Always fail on Win32. No COMPOUND_TEXT support. */
1124
1125   if (encoding)
1126     *encoding = GDK_NONE;
1127
1128   if (format)
1129     *format = 0;
1130   
1131   if (ctext)
1132     *ctext = NULL;
1133
1134   if (length)
1135     *length = 0;
1136
1137   return FALSE;
1138 }
1139
1140 void
1141 gdk_free_compound_text (guchar *ctext)
1142 {
1143   /* As we never generate anything claimed to be COMPOUND_TEXT, this
1144    * should never be called. Or if it is called, ctext should be the
1145    * NULL returned for conversions to COMPOUND_TEXT above.
1146    */
1147   g_return_if_fail (ctext == NULL);
1148 }
1149
1150 /* This function is called from gtk_selection_add_target() and
1151  * gtk_selection_add_targets() in gtkselection.c. It is this function
1152  * that takes care of setting those clipboard formats for which we use
1153  * delayed rendering. Formats copied directly to the clipboard are
1154  * handled in gdk_property_change() in gdkproperty-win32.c.
1155  */
1156
1157 void
1158 gdk_win32_selection_add_targets (GdkWindow  *owner,
1159                                  GdkAtom     selection,
1160                                  gint        n_targets,
1161                                  GdkAtom    *targets)
1162 {
1163   HWND hwnd = NULL;
1164   gboolean has_image = FALSE;
1165   gint i;
1166
1167   GDK_NOTE (DND, {
1168       gchar *sel_name = gdk_atom_name (selection);
1169       
1170       g_print ("gdk_win32_selection_add_targets: %p: %s: ",
1171                owner ? GDK_WINDOW_HWND (owner) : NULL,
1172                sel_name);
1173       g_free (sel_name);
1174
1175       for (i = 0; i < n_targets; i++)
1176         {
1177           gchar *tgt_name = gdk_atom_name (targets[i]);
1178
1179           g_print ("%s", tgt_name);
1180           g_free (tgt_name);
1181           if (i < n_targets - 1)
1182             g_print (", ");
1183         }
1184       g_print ("\n");
1185     });
1186
1187   if (selection != GDK_SELECTION_CLIPBOARD)
1188     return;
1189
1190   if (owner != NULL)
1191     {
1192       if (GDK_WINDOW_DESTROYED (owner))
1193         return;
1194       hwnd = GDK_WINDOW_HWND (owner);
1195     }
1196
1197   if (!API_CALL (OpenClipboard, (hwnd)))
1198     return;
1199
1200   /* We have a very simple strategy: If some kind of pixmap image
1201    * format is being added, actually advertise just PNG and DIB. PNG
1202    * is our preferred format because it can losslessly represent any
1203    * image that gdk-pixbuf formats in general can, even with alpha,
1204    * unambiguously. CF_DIB is also advertised because of the general
1205    * support for it in Windows software, but note that alpha won't be
1206    * handled.
1207    */
1208   for (i = 0; !has_image && i < n_targets; ++i)
1209     {
1210       UINT cf;
1211       gchar *target_name;
1212       int j;
1213       
1214       for (j = 0; j < n_known_pixbuf_formats; j++)
1215         if (targets[i] == known_pixbuf_formats[j])
1216           {
1217             if (!has_image)
1218               {
1219                 GDK_NOTE (DND, g_print ("... SetClipboardData(PNG,NULL)\n"));
1220                 SetClipboardData (_cf_png, NULL);
1221
1222                 GDK_NOTE (DND, g_print ("... SetClipboardData(CF_DIB,NULL)\n"));
1223                 SetClipboardData (CF_DIB, NULL);
1224
1225                 has_image = TRUE;
1226               }
1227             break;
1228           }
1229       
1230       /* If it is one of the pixmap formats, already handled or not
1231        * needed.
1232        */
1233       if (j < n_known_pixbuf_formats)
1234         continue;
1235
1236       /* We don't bother registering and advertising clipboard formats
1237        * that are X11 specific or no non-GTK+ apps will have ever
1238        * heard of, and when there are equivalent clipboard formats
1239        * that are commonly used.
1240        */
1241       if (targets[i] == _save_targets ||
1242           targets[i] == _utf8_string ||
1243           targets[i] == GDK_TARGET_STRING ||
1244           targets[i] == _compound_text ||
1245           targets[i] == _text ||
1246           targets[i] == text_plain_charset_utf_8 ||
1247           targets[i] == text_plain_charset_CP1252 ||
1248           targets[i] == text_plain)
1249         continue;
1250
1251       target_name = gdk_atom_name (targets[i]);
1252
1253       if (g_str_has_prefix (target_name, "text/plain;charset="))
1254         {
1255           g_free (target_name);
1256           continue;
1257         }
1258
1259       cf = RegisterClipboardFormat (target_name);
1260
1261       g_hash_table_replace (_format_atom_table,
1262                             GINT_TO_POINTER (cf),
1263                             targets[i]);
1264       
1265       GDK_NOTE (DND, g_print ("... SetClipboardData(%s,NULL)\n",
1266                               _gdk_win32_cf_to_string (cf)));
1267       SetClipboardData (cf, NULL);
1268
1269       g_free (target_name);
1270     }
1271   API_CALL (CloseClipboard, ());
1272 }
1273
1274 /* Convert from types such as "image/jpg" or "image/png" to DIB using
1275  * gdk-pixbuf so that image copied from GTK+ apps can be pasted in
1276  * native apps like mspaint.exe
1277  */
1278 HGLOBAL
1279 _gdk_win32_selection_convert_to_dib (HGLOBAL  hdata,
1280                                      GdkAtom  target)
1281 {
1282   GDK_NOTE (DND, {
1283       gchar *target_name = gdk_atom_name (target);
1284
1285       g_print ("_gdk_win32_selection_convert_to_dib: %p %s\n",
1286                hdata, target_name);
1287       g_free (target_name);
1288     });
1289
1290   if (target == _image_bmp)
1291     {
1292       /* No conversion is needed, just strip the BITMAPFILEHEADER */
1293       HGLOBAL hdatanew;
1294       SIZE_T size = GlobalSize (hdata) - sizeof (BITMAPFILEHEADER);
1295       guchar *ptr = GlobalLock (hdata);
1296
1297       memmove (ptr, ptr + sizeof (BITMAPFILEHEADER), size);
1298       GlobalUnlock (hdata);
1299
1300       if ((hdatanew = GlobalReAlloc (hdata, size, GMEM_MOVEABLE)) == NULL)
1301         {
1302           WIN32_API_FAILED ("GlobalReAlloc");
1303           GlobalFree (hdata); /* The old hdata is not freed if error */
1304         }
1305       return hdatanew;
1306     }
1307
1308   g_warning ("Should not happen: We provide some image format but not CF_DIB and CF_DIB is requested.");
1309
1310   return NULL;
1311 }