]> Pileus Git - ~andy/gtk/blob - gdk/win32/gdkselection-win32.c
Include "config.h" instead of <config.h> Command used: find -name
[~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
37 /* We emulate the GDK_SELECTION window properties of windows (as used
38  * in the X11 backend) by using a hash table from GdkWindows to
39  * GdkSelProp structs.
40  */
41
42 typedef struct {
43   guchar *data;
44   gsize length;
45   gint format;
46   GdkAtom type;
47 } GdkSelProp;
48
49 static GHashTable *sel_prop_table = NULL;
50
51 static GdkSelProp *dropfiles_prop = NULL;
52
53 /* We store the owner of each selection in this table. Obviously, this only
54  * is valid intra-app, and in fact it is necessary for the intra-app DND to work.
55  */
56 static GHashTable *sel_owner_table = NULL;
57
58 void
59 _gdk_win32_selection_init (void)
60 {
61   sel_prop_table = g_hash_table_new (NULL, NULL);
62   sel_owner_table = g_hash_table_new (NULL, NULL);
63   _format_atom_table = g_hash_table_new (NULL, NULL);
64 }
65
66 /* The specifications for COMPOUND_TEXT and STRING specify that C0 and
67  * C1 are not allowed except for \n and \t, however the X conversions
68  * routines for COMPOUND_TEXT only enforce this in one direction,
69  * causing cut-and-paste of \r and \r\n separated text to fail.
70  * This routine strips out all non-allowed C0 and C1 characters
71  * from the input string and also canonicalizes \r, and \r\n to \n
72  */
73 static gchar * 
74 sanitize_utf8 (const gchar *src,
75                gint         length)
76 {
77   GString *result = g_string_sized_new (length + 1);
78   const gchar *p = src;
79   const gchar *endp = src + length;
80
81   while (p < endp)
82     {
83       if (*p == '\r')
84         {
85           p++;
86           if (*p == '\n')
87             p++;
88
89           g_string_append_c (result, '\n');
90         }
91       else
92         {
93           gunichar ch = g_utf8_get_char (p);
94           char buf[7];
95           gint buflen;
96           
97           if (!((ch < 0x20 && ch != '\t' && ch != '\n') || (ch >= 0x7f && ch < 0xa0)))
98             {
99               buflen = g_unichar_to_utf8 (ch, buf);
100               g_string_append_len (result, buf, buflen);
101             }
102
103           p = g_utf8_next_char (p);
104         }
105     }
106   g_string_append_c (result, '\0');
107
108   return g_string_free (result, FALSE);
109 }
110
111 static gchar *
112 _gdk_utf8_to_string_target_internal (const gchar *str,
113                                      gint         length)
114 {
115   GError *error = NULL;
116   
117   gchar *tmp_str = sanitize_utf8 (str, length);
118   gchar *result =  g_convert_with_fallback (tmp_str, -1,
119                                             "ISO-8859-1", "UTF-8",
120                                             NULL, NULL, NULL, &error);
121   if (!result)
122     {
123       g_warning ("Error converting from UTF-8 to STRING: %s",
124                  error->message);
125       g_error_free (error);
126     }
127   
128   g_free (tmp_str);
129   return result;
130 }
131
132 static void
133 _gdk_selection_property_store (GdkWindow *owner,
134                                GdkAtom    type,
135                                gint       format,
136                                guchar    *data,
137                                gint       length)
138 {
139   GdkSelProp *prop;
140   GSList *prop_list;
141
142   prop = g_new (GdkSelProp, 1);
143
144   if (type == GDK_TARGET_STRING)
145     {
146       /* We know that data is UTF-8 */
147       prop->data = _gdk_utf8_to_string_target_internal (data, length);
148       g_free (data);
149
150       if (!prop->data)
151         {
152           g_free (prop);
153
154           return;
155         }
156       else
157         prop->length = strlen (prop->data) + 1;
158     }
159   else
160     {
161       prop->data = data;
162       prop->length = length;
163     }
164   prop->format = format;
165   prop->type = type;
166   prop_list = g_hash_table_lookup (sel_prop_table, GDK_WINDOW_HWND (owner));
167   prop_list = g_slist_append (prop_list, prop);
168   g_hash_table_insert (sel_prop_table, GDK_WINDOW_HWND (owner), prop_list);
169 }
170
171 void
172 _gdk_dropfiles_store (gchar *data)
173 {
174   if (data != NULL)
175     {
176       g_assert (dropfiles_prop == NULL);
177
178       dropfiles_prop = g_new (GdkSelProp, 1);
179       dropfiles_prop->data = data;
180       dropfiles_prop->length = strlen (data) + 1;
181       dropfiles_prop->format = 8;
182       dropfiles_prop->type = _text_uri_list;
183     }
184   else
185     {
186       if (dropfiles_prop != NULL)
187         {
188           g_free (dropfiles_prop->data);
189           g_free (dropfiles_prop);
190         }
191       dropfiles_prop = NULL;
192     }
193 }
194
195 gboolean
196 gdk_selection_owner_set_for_display (GdkDisplay *display,
197                                      GdkWindow  *owner,
198                                      GdkAtom     selection,
199                                      guint32     time,
200                                      gboolean    send_event)
201 {
202   HWND hwnd;
203   GdkEvent tmp_event;
204
205   g_return_val_if_fail (display == _gdk_display, FALSE);
206   g_return_val_if_fail (selection != GDK_NONE, FALSE);
207
208 #ifdef G_ENABLE_DEBUG
209   {
210     gchar *sel_name;
211
212     GDK_NOTE (DND,
213               (sel_name = gdk_atom_name (selection),
214                g_print ("gdk_selection_owner_set_for_display: %p %#x (%s)\n",
215                         (owner ? GDK_WINDOW_HWND (owner) : NULL),
216                         (guint) selection, sel_name),
217                g_free (sel_name)));
218   }
219 #endif
220
221   if (selection != GDK_SELECTION_CLIPBOARD)
222     {
223       if (owner != NULL)
224         g_hash_table_insert (sel_owner_table, selection, GDK_WINDOW_HWND (owner));
225       else
226         g_hash_table_remove (sel_owner_table, selection);
227       return TRUE;
228     }
229
230   /* Rest of this function handles the CLIPBOARD selection */
231   if (owner != NULL)
232     {
233       if (GDK_WINDOW_DESTROYED (owner))
234         return FALSE;
235
236       hwnd = GDK_WINDOW_HWND (owner);
237     }
238   else
239     hwnd = NULL;
240
241   if (!API_CALL (OpenClipboard, (hwnd)))
242     return FALSE;
243
244   _ignore_destroy_clipboard = TRUE;
245   GDK_NOTE (DND, g_print ("... EmptyClipboard()\n"));
246   if (!API_CALL (EmptyClipboard, ()))
247     {
248       _ignore_destroy_clipboard = FALSE;
249       API_CALL (CloseClipboard, ());
250       return FALSE;
251     }
252   _ignore_destroy_clipboard = FALSE;
253
254   if (!API_CALL (CloseClipboard, ()))
255     return FALSE;
256
257   if (owner != NULL)
258     {
259       /* Send ourselves a selection request message so that
260        * gdk_property_change will be called to store the clipboard
261        * data.
262        */
263       GDK_NOTE (DND, g_print ("... sending GDK_SELECTION_REQUEST to ourselves\n"));
264       tmp_event.selection.type = GDK_SELECTION_REQUEST;
265       tmp_event.selection.window = owner;
266       tmp_event.selection.send_event = FALSE;
267       tmp_event.selection.selection = selection;
268       tmp_event.selection.target = _utf8_string;
269       tmp_event.selection.property = _gdk_selection_property;
270       tmp_event.selection.requestor = (guint32) hwnd;
271       tmp_event.selection.time = time;
272
273       gdk_event_put (&tmp_event);
274     }
275
276   return TRUE;
277 }
278
279 GdkWindow*
280 gdk_selection_owner_get_for_display (GdkDisplay *display,
281                                      GdkAtom     selection)
282 {
283   GdkWindow *window;
284
285   g_return_val_if_fail (display == _gdk_display, NULL);
286   g_return_val_if_fail (selection != GDK_NONE, NULL);
287
288   if (selection == GDK_SELECTION_CLIPBOARD)
289     {
290       HWND owner = GetClipboardOwner ();
291
292       if (owner == NULL)
293         return NULL;
294
295       return gdk_win32_handle_table_lookup ((GdkNativeWindow) owner);
296     }
297
298   window = gdk_window_lookup ((GdkNativeWindow) g_hash_table_lookup (sel_owner_table, selection));
299
300 #ifdef G_ENABLE_DEBUG
301   {
302     gchar *sel_name;
303     
304     GDK_NOTE (DND,
305               (sel_name = gdk_atom_name (selection),
306                g_print ("gdk_selection_owner_get: %#x (%s) = %p\n",
307                         (guint) selection, sel_name,
308                         (window ? GDK_WINDOW_HWND (window) : NULL)),
309                g_free (sel_name)));
310   }
311 #endif
312
313   return window;
314 }
315
316 static void
317 generate_selection_notify (GdkWindow *requestor,
318                            GdkAtom    selection,
319                            GdkAtom    target,
320                            GdkAtom    property,
321                            guint32    time)
322 {
323   GdkEvent tmp_event;
324
325   tmp_event.selection.type = GDK_SELECTION_NOTIFY;
326   tmp_event.selection.window = requestor;
327   tmp_event.selection.send_event = FALSE;
328   tmp_event.selection.selection = selection;
329   tmp_event.selection.target = target;
330   tmp_event.selection.property = property;
331   tmp_event.selection.requestor = 0;
332   tmp_event.selection.time = time;
333
334   gdk_event_put (&tmp_event);
335 }
336
337 void
338 gdk_selection_convert (GdkWindow *requestor,
339                        GdkAtom    selection,
340                        GdkAtom    target,
341                        guint32    time)
342 {
343   HGLOBAL hdata;
344   GdkAtom property = _gdk_selection_property;
345
346   g_return_if_fail (selection != GDK_NONE);
347   g_return_if_fail (requestor != NULL);
348
349   if (GDK_WINDOW_DESTROYED (requestor))
350     return;
351
352 #ifdef G_ENABLE_DEBUG
353   {
354     gchar *sel_name, *tgt_name;
355     
356     GDK_NOTE (DND,
357               (sel_name = gdk_atom_name (selection),
358                tgt_name = gdk_atom_name (target),
359                g_print ("gdk_selection_convert: %p %#x (%s) %#x (%s)\n",
360                         GDK_WINDOW_HWND (requestor),
361                         (guint) selection, sel_name,
362                         (guint) target, tgt_name),
363                g_free (sel_name),
364                g_free (tgt_name)));
365   }
366 #endif
367
368   if (selection == GDK_SELECTION_CLIPBOARD && target == _targets)
369     {
370       gint formats_cnt, i, fmt;
371       GdkAtom *data;
372       gboolean has_bmp = FALSE;
373
374       /* He wants to know what formats are on the clipboard. If there
375        * is some kind of text, tell him so.
376        */
377       if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor))))
378         return;
379
380       formats_cnt = CountClipboardFormats ();
381       data = g_new (GdkAtom, formats_cnt + 2);
382       i = 0;
383
384       if (IsClipboardFormatAvailable (CF_UNICODETEXT) ||
385           IsClipboardFormatAvailable (_cf_utf8_string) ||
386           IsClipboardFormatAvailable (CF_TEXT))
387         {
388           data[i++] = _utf8_string;
389         }
390       if (formats_cnt > 0)
391         {
392           /* If there is anything else in the clipboard, enum it all
393            * although we don't offer special conversion services.
394            */
395           for (fmt = 0; 0 != (fmt = EnumClipboardFormats (fmt)); )
396             {
397               gchar sFormat[80];
398
399               if (GetClipboardFormatName (fmt, sFormat, 80) > 0 &&
400                   strcmp (sFormat, "UTF8_STRING"))
401                 {
402                   GdkAtom atom;
403
404                   if (!has_bmp &&
405                       (!strcmp (sFormat, "image/bmp") ||
406                        !strcmp (sFormat, "image/x-bmp") ||
407                        !strcmp (sFormat, "image/x-MS-bmp")))
408                     has_bmp = TRUE;
409                   atom = gdk_atom_intern (sFormat, FALSE);
410                   data[i++] = atom;
411                 }
412             }
413         }
414       if (!has_bmp && (IsClipboardFormatAvailable (CF_BITMAP) ||
415                        IsClipboardFormatAvailable (CF_DIB)))
416         data[i++] = _image_bmp;
417
418       if (i > 0)
419         _gdk_selection_property_store (requestor, GDK_SELECTION_TYPE_ATOM,
420                                        32, (guchar *) data, i * sizeof (GdkAtom));
421       else             
422         property = GDK_NONE;
423
424       API_CALL (CloseClipboard, ());
425     }
426   else if (selection == GDK_SELECTION_CLIPBOARD && target == _utf8_string)
427     {
428       /* Converting the CLIPBOARD selection means he wants the
429        * contents of the clipboard. Get the clipboard data, and store
430        * it for later.
431        */
432       if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor))))
433         return;
434
435       /* Try various formats. First the simplest, CF_UNICODETEXT. */
436       if ((hdata = GetClipboardData (CF_UNICODETEXT)) != NULL)
437         {
438           wchar_t *ptr, *wcs, *p, *q;
439           guchar *data;
440           glong length, wclen;
441
442           if ((ptr = GlobalLock (hdata)) != NULL)
443             {
444               length = GlobalSize (hdata);
445               
446               GDK_NOTE (DND, g_print ("... CF_UNICODETEXT: %ld bytes\n",
447                                       length));
448
449               /* Strip out \r */
450               wcs = g_new (wchar_t, length / 2 + 1);
451               p = ptr;
452               q = wcs;
453               wclen = 0;
454               while (p < ptr + length / 2)
455                 {
456                   if (*p != '\r')
457                     {
458                       *q++ = *p;
459                       wclen++;
460                     }
461                   p++;
462                 }
463
464               data = g_utf16_to_utf8 (wcs, wclen, NULL, NULL, NULL);
465               g_free (wcs);
466
467               if (data)
468                 _gdk_selection_property_store (requestor, target, 8,
469                                                data, strlen (data) + 1);
470               GlobalUnlock (hdata);
471             }
472         }
473       else if ((hdata = GetClipboardData (_cf_utf8_string)) != NULL)
474         {
475           /* UTF8_STRING is a format we store ourselves when necessary */
476           guchar *ptr;
477           gint length;
478
479           if ((ptr = GlobalLock (hdata)) != NULL)
480             {
481               length = GlobalSize (hdata);
482               
483               GDK_NOTE (DND, g_print ("... UTF8_STRING: %d bytes: %.10s\n",
484                                       length, ptr));
485               
486               _gdk_selection_property_store (requestor, target, 8,
487                                              g_memdup (ptr, length), length);
488               GlobalUnlock (hdata);
489             }
490         }
491       else if ((hdata = GetClipboardData (CF_TEXT)) != NULL)
492         {
493           /* We must always assume the data can contain non-ASCII
494            * in either the current code page, or if there is CF_LOCALE
495            * data, in that locale's default code page.
496            */
497           HGLOBAL hlcid;
498           UINT cp = CP_ACP;
499           wchar_t *wcs, *wcs2, *p, *q;
500           guchar *ptr, *data;
501           glong length, wclen, wclen2;
502
503           if ((ptr = GlobalLock (hdata)) != NULL)
504             {
505               length = GlobalSize (hdata);
506               
507               GDK_NOTE (DND, g_print ("... CF_TEXT: %ld bytes: %.10s\n",
508                                        length, ptr));
509               
510               if ((hlcid = GetClipboardData (CF_LOCALE)) != NULL)
511                 {
512                   gchar buf[10];
513                   LCID *lcidptr = GlobalLock (hlcid);
514                   if (GetLocaleInfo (*lcidptr, LOCALE_IDEFAULTANSICODEPAGE,
515                                      buf, sizeof (buf)))
516                     {
517                       cp = atoi (buf);
518                       GDK_NOTE (DND, g_print ("... CF_LOCALE: %#lx cp:%d\n",
519                                               *lcidptr, cp));
520                     }
521                   GlobalUnlock (hlcid);
522                 }
523
524               wcs = g_new (wchar_t, length + 1);
525               wclen = MultiByteToWideChar (cp, 0, ptr, length,
526                                            wcs, length + 1);
527
528               /* Strip out \r */
529               wcs2 = g_new (wchar_t, wclen);
530               p = wcs;
531               q = wcs2;
532               wclen2 = 0;
533               while (p < wcs + wclen)
534                 {
535                   if (*p != '\r')
536                     {
537                       *q++ = *p;
538                       wclen2++;
539                     }
540                   p++;
541                 }
542               g_free (wcs);
543
544               data = g_utf16_to_utf8 (wcs2, wclen2, NULL, &length, NULL);
545               g_free (wcs2);
546
547               if (data)
548                 _gdk_selection_property_store (requestor, target, 8,
549                                                data, length + 1);
550               GlobalUnlock (hdata);
551             }
552         }
553       else
554         property = GDK_NONE;
555
556       API_CALL (CloseClipboard, ());
557     }
558   else if (selection == GDK_SELECTION_CLIPBOARD && target == _image_bmp)
559     {
560       guchar *data;
561
562       if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor))))
563         return;
564       if ((hdata = GetClipboardData (_cf_image_bmp)) != NULL)
565         {
566           /* "image/bmp" is the first choice. */
567           guchar *ptr;
568
569           if ((ptr = GlobalLock (hdata)) != NULL)
570             {
571               gint length = GlobalSize (hdata);
572       
573               GDK_NOTE (DND, g_print ("...BITMAP (from \"image/bmp\": %d bytes\n",
574                                       length));
575       
576               _gdk_selection_property_store (requestor, target, 8,
577                                              g_memdup (ptr, length), length);
578               GlobalUnlock (hdata);
579             }
580         }
581       else if ((hdata = GetClipboardData (CF_DIB)) != NULL)
582         {
583           /* If there's CF_DIB but not "image/bmp", the clipboard
584            * owner is probably a native Win32 application.
585            */
586           BITMAPINFOHEADER *ptr;
587
588           if ((ptr = GlobalLock (hdata)) != NULL)
589             {
590               BITMAPFILEHEADER *hdr; /* Need to add a file header so gdk-pixbuf can load it */
591               gint length = GlobalSize (hdata) + sizeof (BITMAPFILEHEADER);
592               
593               GDK_NOTE (DND, g_print ("... BITMAP (from CF_DIB): %d bytes\n", length));
594               
595               data = g_try_malloc (length);
596               if (data)
597                 {
598                   hdr = (BITMAPFILEHEADER *)data;
599                   hdr->bfType = 0x4d42; /* 0x42 = "B" 0x4d = "M" */
600                   /* Compute the size of the entire file. */
601                   hdr->bfSize = (DWORD) (sizeof (BITMAPFILEHEADER)
602                         + ptr->biSize + ptr->biClrUsed
603                         * sizeof (RGBQUAD) + ptr->biSizeImage);
604                   hdr->bfReserved1 = 0;
605                   hdr->bfReserved2 = 0;
606                   /* Compute the offset to the array of color indices. */
607                   hdr->bfOffBits = (DWORD) sizeof (BITMAPFILEHEADER)
608                         + ptr->biSize + ptr->biClrUsed * sizeof (RGBQUAD);
609                   /* Copy the data behind it */
610                   memcpy (data + sizeof (BITMAPFILEHEADER), ptr, length - sizeof (BITMAPFILEHEADER));
611                   _gdk_selection_property_store (requestor, target, 8,
612                                                  data, length);
613                 }
614               GlobalUnlock (hdata);
615             }
616
617       }
618
619       API_CALL (CloseClipboard, ());
620     }
621   else if (selection == GDK_SELECTION_CLIPBOARD)
622     {
623       char *target_name;
624       UINT fmt = 0;
625
626       if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor))))
627         return;
628
629       target_name = gdk_atom_name (target);
630
631       /* Check if it's available. In fact, we can simply call
632        * GetClipboardData (RegisterClipboardFormat (targetname)), but
633        * the global custom format ID space is limited,
634        * (0xC000~0xFFFF), and we better not waste an format ID if we
635        * are just a requestor.
636        */
637       for ( ; 0 != (fmt = EnumClipboardFormats (fmt)); )
638         {
639           char sFormat[80];
640
641           if (GetClipboardFormatName (fmt, sFormat, 80) > 0 && 
642               strcmp (sFormat, target_name) == 0)
643             {
644               if ((hdata = GetClipboardData (fmt)) != NULL)
645                 {
646                   /* Simply get it without conversion */
647                   guchar *ptr;
648                   gint length;
649
650                   if ((ptr = GlobalLock (hdata)) != NULL)
651                     {
652                       length = GlobalSize (hdata);
653               
654                       GDK_NOTE (DND, g_print ("... %s: %d bytes\n", target_name, length));
655               
656                       _gdk_selection_property_store (requestor, target, 8,
657                                                      g_memdup (ptr, length), length);
658                       GlobalUnlock (hdata);
659                       break;
660                     }
661                 }
662             }
663         }
664       g_free (target_name);
665       API_CALL (CloseClipboard, ());
666     }
667   else if (selection == _gdk_win32_dropfiles)
668     {
669       /* This means he wants the names of the dropped files.
670        * gdk_dropfiles_filter already has stored the text/uri-list
671        * data temporarily in dropfiles_prop.
672        */
673       if (dropfiles_prop != NULL)
674         {
675           _gdk_selection_property_store
676             (requestor, selection, dropfiles_prop->format,
677              dropfiles_prop->data, dropfiles_prop->length);
678           g_free (dropfiles_prop);
679           dropfiles_prop = NULL;
680         }
681     }
682   else
683     property = GDK_NONE;
684
685   /* Generate a selection notify message so that we actually fetch
686    * the data (if property == _gdk_selection_property) or indicating failure
687    * (if property == GDK_NONE).
688    */
689   generate_selection_notify (requestor, selection, target, property, time);
690 }
691
692 gint
693 gdk_selection_property_get (GdkWindow  *requestor,
694                             guchar    **data,
695                             GdkAtom    *ret_type,
696                             gint       *ret_format)
697 {
698   GdkSelProp *prop;
699   GSList *prop_list;
700
701   g_return_val_if_fail (requestor != NULL, 0);
702   g_return_val_if_fail (GDK_IS_WINDOW (requestor), 0);
703
704   if (GDK_WINDOW_DESTROYED (requestor))
705     return 0;
706   
707   GDK_NOTE (DND, g_print ("gdk_selection_property_get: %p\n",
708                            GDK_WINDOW_HWND (requestor)));
709
710   prop_list = g_hash_table_lookup (sel_prop_table, GDK_WINDOW_HWND (requestor));
711   prop = prop_list ? (GdkSelProp *) prop_list->data : NULL;
712
713   if (prop == NULL)
714     {
715       *data = NULL;
716       return 0;
717     }
718
719   *data = g_malloc (prop->length + 1);
720   (*data)[prop->length] = '\0';
721   if (prop->length > 0)
722     memmove (*data, prop->data, prop->length);
723
724   if (ret_type)
725     *ret_type = prop->type;
726
727   if (ret_format)
728     *ret_format = prop->format;
729
730   return prop->length;
731 }
732
733 void
734 _gdk_selection_property_delete (GdkWindow *window)
735 {
736   GdkSelProp *prop;
737   GSList *prop_list;
738   
739   GDK_NOTE (DND, g_print ("_gdk_selection_property_delete: %p\n",
740                            GDK_WINDOW_HWND (window)));
741
742   prop_list = g_hash_table_lookup (sel_prop_table, GDK_WINDOW_HWND (window));
743   if (prop_list && (prop = (GdkSelProp *) prop_list->data) != NULL)
744     {
745       g_free (prop->data);
746       prop_list = g_slist_remove (prop_list, prop);
747       g_free (prop);
748       g_hash_table_insert (sel_prop_table, GDK_WINDOW_HWND (window), prop_list);
749     }
750 }
751
752 void
753 gdk_selection_send_notify_for_display (GdkDisplay *display,
754                                        guint32     requestor,
755                                        GdkAtom     selection,
756                                        GdkAtom     target,
757                                        GdkAtom     property,
758                                        guint32     time)
759 {
760   g_return_if_fail (display == _gdk_display);
761
762 #ifdef G_ENABLE_DEBUG
763   {
764     gchar *sel_name, *tgt_name, *prop_name;
765
766     GDK_NOTE (DND,
767               (sel_name = gdk_atom_name (selection),
768                tgt_name = gdk_atom_name (target),
769                prop_name = gdk_atom_name (property),
770                g_print ("gdk_selection_send_notify_for_display: %p %#x (%s) %#x (%s) %#x (%s)\n",
771                         (gpointer) requestor,
772                         (guint) selection, sel_name,
773                         (guint) target, tgt_name,
774                         (guint) property, prop_name),
775                g_free (sel_name),
776                g_free (tgt_name),
777                g_free (prop_name)));
778   }
779 #endif
780 }
781
782 /* It's hard to say whether implementing this actually is of any use
783  * on the Win32 platform? gtk calls only
784  * gdk_text_property_to_utf8_list_for_display().
785  */
786 gint
787 gdk_text_property_to_text_list_for_display (GdkDisplay   *display,
788                                             GdkAtom       encoding,
789                                             gint          format, 
790                                             const guchar *text,
791                                             gint          length,
792                                             gchar      ***list)
793 {
794   gchar *result;
795   const gchar *charset;
796   gchar *source_charset;
797
798   g_return_val_if_fail (display == _gdk_display, 0);
799
800 #ifdef G_ENABLE_DEBUG
801   {
802     gchar *enc_name;
803
804     GDK_NOTE (DND, (enc_name = gdk_atom_name (encoding),
805                     g_print ("gdk_text_property_to_text_list_for_display: %s %d %.20s %d\n",
806                              enc_name, format, text, length),
807                     g_free (enc_name)));
808   }
809 #endif
810     
811   if (!list)
812     return 0;
813
814   if (encoding == GDK_TARGET_STRING)
815     source_charset = g_strdup ("ISO-8859-1");
816   else if (encoding == _utf8_string)
817     source_charset = g_strdup ("UTF-8");
818   else
819     source_charset = gdk_atom_name (encoding);
820     
821   g_get_charset (&charset);
822
823   result = g_convert (text, length, charset, source_charset,
824                       NULL, NULL, NULL);
825   g_free (source_charset);
826
827   if (!result)
828     return 0;
829
830   *list = g_new (gchar *, 1);
831   **list = result;
832   
833   return 1;
834 }
835
836 void
837 gdk_free_text_list (gchar **list)
838 {
839   g_return_if_fail (list != NULL);
840
841   g_free (*list);
842   g_free (list);
843 }
844
845 static gint
846 make_list (const gchar  *text,
847            gint          length,
848            gboolean      latin1,
849            gchar      ***list)
850 {
851   GSList *strings = NULL;
852   gint n_strings = 0;
853   gint i;
854   const gchar *p = text;
855   const gchar *q;
856   GSList *tmp_list;
857   GError *error = NULL;
858
859   while (p < text + length)
860     {
861       gchar *str;
862       
863       q = p;
864       while (*q && q < text + length)
865         q++;
866
867       if (latin1)
868         {
869           str = g_convert (p, q - p,
870                            "UTF-8", "ISO-8859-1",
871                            NULL, NULL, &error);
872
873           if (!str)
874             {
875               g_warning ("Error converting selection from STRING: %s",
876                          error->message);
877               g_error_free (error);
878             }
879         }
880       else
881         str = g_strndup (p, q - p);
882
883       if (str)
884         {
885           strings = g_slist_prepend (strings, str);
886           n_strings++;
887         }
888
889       p = q + 1;
890     }
891
892   if (list)
893     *list = g_new (gchar *, n_strings + 1);
894
895   (*list)[n_strings] = NULL;
896   
897   i = n_strings;
898   tmp_list = strings;
899   while (tmp_list)
900     {
901       if (list)
902         (*list)[--i] = tmp_list->data;
903       else
904         g_free (tmp_list->data);
905
906       tmp_list = tmp_list->next;
907     }
908
909   g_slist_free (strings);
910
911   return n_strings;
912 }
913
914 gint 
915 gdk_text_property_to_utf8_list_for_display (GdkDisplay    *display,
916                                             GdkAtom        encoding,
917                                             gint           format,
918                                             const guchar  *text,
919                                             gint           length,
920                                             gchar       ***list)
921 {
922   g_return_val_if_fail (text != NULL, 0);
923   g_return_val_if_fail (length >= 0, 0);
924   g_return_val_if_fail (display == _gdk_display, 0);
925
926   if (encoding == GDK_TARGET_STRING)
927     {
928       return make_list ((gchar *)text, length, TRUE, list);
929     }
930   else if (encoding == _utf8_string)
931     {
932       return make_list ((gchar *)text, length, FALSE, list);
933     }
934   else
935     {
936       gchar *enc_name = gdk_atom_name (encoding);
937
938       g_warning ("gdk_text_property_to_utf8_list_for_display: encoding %s not handled\n", enc_name);
939       g_free (enc_name);
940
941       if (list)
942         *list = NULL;
943
944       return 0;
945     }
946 }
947
948 gint
949 gdk_string_to_compound_text_for_display (GdkDisplay  *display,
950                                          const gchar *str,
951                                          GdkAtom     *encoding,
952                                          gint        *format,
953                                          guchar     **ctext,
954                                          gint        *length)
955 {
956   g_return_val_if_fail (str != NULL, 0);
957   g_return_val_if_fail (length >= 0, 0);
958   g_return_val_if_fail (display == _gdk_display, 0);
959
960   GDK_NOTE (DND, g_print ("gdk_string_to_compound_text_for_display: %.20s\n", str));
961
962   /* Always fail on Win32. No COMPOUND_TEXT support. */
963
964   if (encoding)
965     *encoding = GDK_NONE;
966
967   if (format)
968     *format = 0;
969
970   if (ctext)
971     *ctext = NULL;
972
973   if (length)
974     *length = 0;
975
976   return -1;
977 }
978
979 gchar *
980 gdk_utf8_to_string_target (const gchar *str)
981 {
982   return _gdk_utf8_to_string_target_internal (str, strlen (str));
983 }
984
985 gboolean
986 gdk_utf8_to_compound_text_for_display (GdkDisplay  *display,
987                                        const gchar *str,
988                                        GdkAtom     *encoding,
989                                        gint        *format,
990                                        guchar     **ctext,
991                                        gint        *length)
992 {
993   g_return_val_if_fail (str != NULL, FALSE);
994   g_return_val_if_fail (display == _gdk_display, FALSE);
995
996   GDK_NOTE (DND, g_print ("gdk_utf8_to_compound_text_for_display: %.20s\n", str));
997
998   /* Always fail on Win32. No COMPOUND_TEXT support. */
999
1000   if (encoding)
1001     *encoding = GDK_NONE;
1002
1003   if (format)
1004     *format = 0;
1005   
1006   if (ctext)
1007     *ctext = NULL;
1008
1009   if (length)
1010     *length = 0;
1011
1012   return FALSE;
1013 }
1014
1015 void
1016 gdk_free_compound_text (guchar *ctext)
1017 {
1018   /* As we never generate anything claimed to be COMPOUND_TEXT, this
1019    * should never be called. Or if it is called, ctext should be the
1020    * NULL returned for conversions to COMPOUND_TEXT above.
1021    */
1022   g_return_if_fail (ctext == NULL);
1023 }
1024
1025 void
1026 gdk_win32_selection_add_targets (GdkWindow  *owner,
1027                                  GdkAtom     selection,
1028                                  gint        n_targets,
1029                                  GdkAtom    *targets)
1030 {
1031   HWND hwnd = NULL;
1032   guint formatid;
1033   gint i;
1034   GSList *convertable_formats, *format;
1035   gboolean has_set_dib = FALSE, has_real_dib = FALSE;
1036
1037 #ifdef G_ENABLE_DEBUG
1038   if (_gdk_debug_flags & GDK_DEBUG_DND)
1039     {
1040       gchar *sel_name = gdk_atom_name (selection);
1041       
1042       g_print ("gdk_win32_selection_add_targets: %p: %s: ",
1043                owner ? GDK_WINDOW_HWND (owner) : NULL,
1044                sel_name);
1045       g_free (sel_name);
1046
1047       for (i = 0; i < n_targets; i++)
1048         {
1049           gchar *tgt_name = gdk_atom_name (targets[i]);
1050
1051           g_print ("%s ", tgt_name);
1052           g_free (tgt_name);
1053         }
1054       g_print ("\n");
1055     }
1056 #endif
1057
1058   if (selection != GDK_SELECTION_CLIPBOARD)
1059     return;
1060
1061   if (owner != NULL)
1062     {
1063       if (GDK_WINDOW_DESTROYED (owner))
1064         return;
1065       hwnd = GDK_WINDOW_HWND (owner);
1066     }
1067
1068   if (!API_CALL (OpenClipboard, (hwnd)))
1069     return;
1070
1071   convertable_formats = gdk_pixbuf_get_formats ();
1072   for (i = 0; i < n_targets; ++i)
1073     {
1074       gchar *target_name;
1075
1076       if (targets[i] == _utf8_string ||
1077           targets[i] == GDK_TARGET_STRING ||
1078           targets[i] == _text ||
1079           targets[i] == _compound_text ||
1080           targets[i] == _save_targets)
1081         continue;
1082
1083       target_name = gdk_atom_name (targets[i]);
1084       if (!(formatid = RegisterClipboardFormat (target_name)))
1085         {
1086           WIN32_API_FAILED ("RegisterClipboardFormat");
1087           API_CALL (CloseClipboard, ());
1088           g_free (target_name);
1089           return;
1090         }
1091       g_hash_table_replace (_format_atom_table, GINT_TO_POINTER (formatid), targets[i]);
1092
1093       GDK_NOTE (DND, g_print ("... SetClipboardData(%s,NULL)\n",
1094                               _gdk_win32_cf_to_string (formatid)));
1095       SetClipboardData (formatid, NULL);
1096
1097       /* We should replace the previous image format associated with
1098        * CF_DIB with "image/bmp" if we find "image/bmp", "image/x-bmp"
1099        * or "image/x-MS-bmp" is available.
1100        */
1101       if (!has_real_dib &&
1102           (!strcmp (target_name, "image/bmp") ||
1103            !strcmp (target_name, "image/x-bmp") ||
1104            !strcmp (target_name, "image/x-MS-bmp")))
1105         {
1106           g_hash_table_replace (_format_atom_table,
1107                                 GINT_TO_POINTER (CF_DIB),
1108                                 targets[i]);
1109           if (!has_set_dib)
1110             {
1111               GDK_NOTE (DND, g_print ("... SetClipboardData(CF_DIB,NULL)\n"));
1112               SetClipboardData (CF_DIB, NULL);
1113               has_set_dib = TRUE;
1114             }
1115           has_real_dib = TRUE;
1116           g_free (target_name);
1117           continue;
1118         }
1119
1120       for (format = convertable_formats; !has_set_dib && format; format = format->next)
1121         {
1122           gchar **mime_types =
1123             gdk_pixbuf_format_get_mime_types ((GdkPixbufFormat *) format->data);
1124           gchar **mime_type;
1125
1126           for (mime_type = mime_types; *mime_type; ++mime_type)
1127             {
1128               if (!strcmp (target_name, *mime_type))
1129                 {
1130                   g_hash_table_replace (_format_atom_table,
1131                                         GINT_TO_POINTER (CF_DIB),
1132                                         targets[i]);
1133                   GDK_NOTE (DND, g_print ("... SetClipboardData(CF_DIB,NULL)\n"));
1134                   SetClipboardData (CF_DIB, NULL);
1135                   has_set_dib = TRUE;
1136                   break;
1137                 }
1138             }
1139           g_strfreev(mime_types);
1140         }
1141       g_free (target_name);
1142     }
1143   g_slist_free (convertable_formats);
1144
1145   API_CALL (CloseClipboard, ());
1146 }
1147
1148 /* Convert from types such as "image/jpg" or "image/png" to DIB using
1149  * gdk-pixbuf so that image copied from GTK+ apps can be pasted in
1150  * native apps like mspaint.exe
1151  */
1152 HGLOBAL
1153 _gdk_win32_selection_convert_to_dib (HGLOBAL  hdata,
1154                                      GdkAtom  target)
1155 {
1156   GdkPixbufLoader *loader;
1157   GdkPixbuf *pixbuf;
1158   gchar *target_name;
1159   guchar *ptr;
1160   gchar *bmp_buf;
1161   gsize size;
1162   gboolean ok;
1163
1164   if (!(target_name = gdk_atom_name (target)))
1165     {
1166       GlobalFree (hdata);
1167       return NULL;
1168     }
1169
1170   if (!strcmp (target_name, "image/bmp") ||
1171       !strcmp (target_name, "image/x-bmp") ||
1172       !strcmp (target_name, "image/x-MS-bmp"))
1173     {
1174       /* No conversion is needed, just strip the BITMAPFILEHEADER */
1175       HGLOBAL hdatanew;
1176
1177       g_free (target_name);
1178       size = GlobalSize (hdata) - 1 - sizeof (BITMAPFILEHEADER);
1179       ptr = GlobalLock (hdata);
1180       memmove (ptr, ptr + sizeof (BITMAPFILEHEADER), size);
1181       GlobalUnlock (hdata);
1182       if (!(hdatanew = GlobalReAlloc (hdata, size, 0)))
1183         {
1184           WIN32_API_FAILED ("GlobalReAlloc");
1185           GlobalFree (hdata); /* the old hdata is not freed if error */
1186         }
1187       return hdatanew;
1188     }
1189
1190   /* We actually provide image formats -other than- "image/bmp" etc
1191    * and the requestor is either a native Win32 application or a GTK+
1192    * client that requested "image/bmp".
1193    */
1194   if (!(loader = gdk_pixbuf_loader_new_with_mime_type (target_name, NULL)))
1195     {
1196       GlobalFree (hdata);
1197       g_free (target_name);
1198       return NULL;
1199     }
1200   g_free (target_name);
1201
1202   ptr = GlobalLock (hdata);
1203   ok = gdk_pixbuf_loader_write (loader, ptr, GlobalSize (hdata) - 1, NULL) &&
1204        gdk_pixbuf_loader_close (loader, NULL);
1205
1206   GlobalUnlock (hdata);
1207   GlobalFree (hdata);
1208   hdata = NULL;
1209
1210   if (ok && (pixbuf = gdk_pixbuf_loader_get_pixbuf (loader)) != NULL)
1211     g_object_ref (pixbuf);
1212
1213   g_object_unref (loader);
1214
1215   if (ok && gdk_pixbuf_save_to_buffer (pixbuf, &bmp_buf, &size, "bmp", NULL, NULL))
1216     {
1217       size -= sizeof (BITMAPFILEHEADER);
1218       if (!(hdata = GlobalAlloc (GMEM_MOVEABLE, size)))
1219         {
1220           WIN32_API_FAILED ("GlobalAlloc");
1221           ok = FALSE;
1222         }
1223
1224       if (ok)
1225         {
1226           ptr = GlobalLock (hdata);
1227           memcpy (ptr, bmp_buf + sizeof (BITMAPFILEHEADER), size);
1228           GlobalUnlock (hdata);
1229         }
1230
1231       g_free (bmp_buf);
1232       g_object_unref (pixbuf);
1233     }
1234
1235   return hdata;
1236 }