]> Pileus Git - ~andy/gtk/blob - gdk/win32/gdkproperty-win32.c
If the WM_TRANSIENT_FOR property is being deleted, set the owner of the
[~andy/gtk] / gdk / win32 / gdkproperty-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 <string.h>
29 #include <stdlib.h>
30 #include <glib/gprintf.h>
31
32 #include "gdkscreen.h"
33 #include "gdkproperty.h"
34 #include "gdkselection.h"
35 #include "gdkprivate-win32.h"
36
37 GdkAtom
38 gdk_atom_intern (const gchar *atom_name,
39                  gint         only_if_exists)
40 {
41   ATOM win32_atom;
42   GdkAtom retval;
43   static GHashTable *atom_hash = NULL;
44   
45   if (!atom_hash)
46     atom_hash = g_hash_table_new (g_str_hash, g_str_equal);
47
48   retval = g_hash_table_lookup (atom_hash, atom_name);
49   if (!retval)
50     {
51       if (strcmp (atom_name, "PRIMARY") == 0)
52         retval = GDK_SELECTION_PRIMARY;
53       else if (strcmp (atom_name, "SECONDARY") == 0)
54         retval = GDK_SELECTION_SECONDARY;
55       else if (strcmp (atom_name, "CLIPBOARD") == 0)
56         retval = GDK_SELECTION_CLIPBOARD;
57       else if (strcmp (atom_name, "ATOM") == 0)
58         retval = GDK_SELECTION_TYPE_ATOM;
59       else if (strcmp (atom_name, "BITMAP") == 0)
60         retval = GDK_SELECTION_TYPE_BITMAP;
61       else if (strcmp (atom_name, "COLORMAP") == 0)
62         retval = GDK_SELECTION_TYPE_COLORMAP;
63       else if (strcmp (atom_name, "DRAWABLE") == 0)
64         retval = GDK_SELECTION_TYPE_DRAWABLE;
65       else if (strcmp (atom_name, "INTEGER") == 0)
66         retval = GDK_SELECTION_TYPE_INTEGER;
67       else if (strcmp (atom_name, "PIXMAP") == 0)
68         retval = GDK_SELECTION_TYPE_PIXMAP;
69       else if (strcmp (atom_name, "WINDOW") == 0)
70         retval = GDK_SELECTION_TYPE_WINDOW;
71       else if (strcmp (atom_name, "STRING") == 0)
72         retval = GDK_SELECTION_TYPE_STRING;
73       else
74         {
75           win32_atom = GlobalAddAtom (atom_name);
76           retval = GUINT_TO_POINTER ((guint) win32_atom);
77         }
78       g_hash_table_insert (atom_hash, 
79                            g_strdup (atom_name), 
80                            retval);
81     }
82
83   return retval;
84 }
85
86 gchar *
87 gdk_atom_name (GdkAtom atom)
88 {
89   ATOM win32_atom;
90   gchar name[256];
91
92   if (GDK_SELECTION_PRIMARY == atom) return g_strdup ("PRIMARY");
93   else if (GDK_SELECTION_SECONDARY == atom) return g_strdup ("SECONDARY");
94   else if (GDK_SELECTION_CLIPBOARD == atom) return g_strdup ("CLIPBOARD");
95   else if (GDK_SELECTION_TYPE_ATOM == atom) return g_strdup ("ATOM");
96   else if (GDK_SELECTION_TYPE_BITMAP == atom) return g_strdup ("BITMAP");
97   else if (GDK_SELECTION_TYPE_COLORMAP == atom) return g_strdup ("COLORMAP");
98   else if (GDK_SELECTION_TYPE_DRAWABLE == atom) return g_strdup ("DRAWABLE");
99   else if (GDK_SELECTION_TYPE_INTEGER == atom) return g_strdup ("INTEGER");
100   else if (GDK_SELECTION_TYPE_PIXMAP == atom) return g_strdup ("PIXMAP");
101   else if (GDK_SELECTION_TYPE_WINDOW == atom) return g_strdup ("WINDOW");
102   else if (GDK_SELECTION_TYPE_STRING == atom) return g_strdup ("STRING");
103   
104   win32_atom = GPOINTER_TO_UINT (atom);
105   
106   if (win32_atom < 0xC000)
107     return g_strdup_printf ("#%p", atom);
108   else if (GlobalGetAtomName (win32_atom, name, sizeof (name)) == 0)
109     return NULL;
110   return g_strdup (name);
111 }
112
113 gint
114 gdk_property_get (GdkWindow   *window,
115                   GdkAtom      property,
116                   GdkAtom      type,
117                   gulong       offset,
118                   gulong       length,
119                   gint         pdelete,
120                   GdkAtom     *actual_property_type,
121                   gint        *actual_format_type,
122                   gint        *actual_length,
123                   guchar     **data)
124 {
125   g_return_val_if_fail (window != NULL, FALSE);
126   g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
127
128   if (GDK_WINDOW_DESTROYED (window))
129     return FALSE;
130
131   g_warning ("gdk_property_get: Not implemented");
132
133   return FALSE;
134 }
135
136 static gboolean
137 find_common_locale (const guchar  *data,
138                     gint           nelements,
139                     gint           nchars,
140                     LCID          *lcidp,
141                     guchar       **bufp,
142                     gint          *sizep)
143 {
144   static struct {
145     LCID lcid;
146     UINT cp;
147   } locales[] = {
148 #define ENTRY(lang, sublang) \
149  { MAKELCID (MAKELANGID (LANG_##lang, SUBLANG_##sublang), SORT_DEFAULT), 0 }
150     ENTRY (ENGLISH, DEFAULT),
151     ENTRY (POLISH, DEFAULT),
152     ENTRY (CZECH, DEFAULT),
153     ENTRY (LITHUANIAN, DEFAULT),
154     ENTRY (RUSSIAN, DEFAULT),
155     ENTRY (GREEK, DEFAULT),
156     ENTRY (TURKISH, DEFAULT),
157     ENTRY (HEBREW, DEFAULT),
158     ENTRY (ARABIC, DEFAULT),
159     ENTRY (THAI, DEFAULT),
160     ENTRY (JAPANESE, DEFAULT),
161     ENTRY (CHINESE, CHINESE_SIMPLIFIED),
162     ENTRY (CHINESE, CHINESE_TRADITIONAL),
163     ENTRY (KOREAN, DEFAULT),
164 #undef ENTRY
165   };
166
167   static gboolean been_here = FALSE;
168   gint i;
169   wchar_t *wcs;
170
171   /* For each installed locale: Get the locale's default code page,
172    * and store the list of locales and code pages.
173    */
174   if (!been_here)
175     {
176       been_here = TRUE;
177       for (i = 0; i < G_N_ELEMENTS (locales); i++)
178         if (IsValidLocale (locales[i].lcid, LCID_INSTALLED))
179           {
180             gchar buf[10];
181             if (GetLocaleInfo (locales[i].lcid, LOCALE_IDEFAULTANSICODEPAGE,
182                                buf, sizeof (buf)))
183               {
184                 gchar name[100];
185                 locales[i].cp = atoi (buf);
186                 GDK_NOTE (DND, (GetLocaleInfo (locales[i].lcid,
187                                                LOCALE_SENGLANGUAGE,
188                                                name, sizeof (name)),
189                                 g_print ("locale %#lx: %s: CP%d\n",
190                                          (gulong) locales[i].lcid, name,
191                                          locales[i].cp)));
192               }
193           }
194     }
195   
196   /* Allocate bufp big enough to store data in any code page.  Two
197    * bytes for each Unicode char should be enough, Windows code pages
198    * are either single- or double-byte.
199    */
200   *bufp = g_malloc ((nchars+1) * 2);
201   wcs = g_new (wchar_t, nchars+1);
202
203   /* Convert to Windows wide chars into temp buf */
204   _gdk_utf8_to_ucs2 (wcs, data, nelements, nchars);
205   wcs[nchars] = 0;
206
207   /* For each code page that is the default for an installed locale: */
208   for (i = 0; i < G_N_ELEMENTS (locales); i++)
209     {
210       BOOL used_default;
211       int nbytes;
212
213       if (locales[i].cp == 0)
214         continue;
215
216       /* Convert to that code page into bufp */
217       
218       nbytes = WideCharToMultiByte (locales[i].cp, 0, wcs, -1,
219                                     *bufp, (nchars+1)*2,
220                                     NULL, &used_default);
221
222       if (!used_default)
223         {
224           /* This locale is good for the string */
225           g_free (wcs);
226           *lcidp = locales[i].lcid;
227           *sizep = nbytes;
228           return TRUE;
229         }
230     }
231
232   g_free (*bufp);
233   g_free (wcs);
234
235   return FALSE;
236 }
237
238 void
239 gdk_property_change (GdkWindow    *window,
240                      GdkAtom       property,
241                      GdkAtom       type,
242                      gint          format,
243                      GdkPropMode   mode,
244                      const guchar *data,
245                      gint          nelements)
246 {
247   HGLOBAL hdata, hlcid, hutf8;
248   UINT cf = 0;
249   LCID lcid;
250   LCID *lcidptr;
251   GString *rtf = NULL;
252   gint i, size, nchars;
253   gchar *prop_name, *type_name;
254   guchar *ucptr, *buf = NULL;
255   wchar_t *wcptr;
256   enum { PLAIN_ASCII, UNICODE_TEXT, SINGLE_LOCALE, RICH_TEXT } method;
257   gboolean ok = TRUE;
258
259   g_return_if_fail (window != NULL);
260   g_return_if_fail (GDK_IS_WINDOW (window));
261
262   if (GDK_WINDOW_DESTROYED (window))
263     return;
264
265   GDK_NOTE (DND,
266             (prop_name = gdk_atom_name (property),
267              type_name = gdk_atom_name (type),
268              g_print ("gdk_property_change: %p %#x (%s) %#x (%s) %s %d*%d bytes %.10s\n",
269                       GDK_WINDOW_HWND (window),
270                       (guint) property, prop_name,
271                       (guint) type, type_name,
272                       (mode == GDK_PROP_MODE_REPLACE ? "REPLACE" :
273                        (mode == GDK_PROP_MODE_PREPEND ? "PREPEND" :
274                         (mode == GDK_PROP_MODE_APPEND ? "APPEND" :
275                          "???"))),
276                       format, nelements, data),
277              g_free (prop_name),
278              g_free (type_name)));
279
280   if (property == _gdk_selection_property
281       && type == GDK_TARGET_STRING
282       && format == 8
283       && mode == GDK_PROP_MODE_REPLACE)
284     {
285       if (!OpenClipboard (GDK_WINDOW_HWND (window)))
286         {
287           WIN32_API_FAILED ("OpenClipboard");
288           return;
289         }
290
291       /* Check if only ASCII */
292       for (i = 0; i < nelements; i++)
293         if (data[i] >= 0200)
294           break;
295
296       if (i == nelements)
297         nchars = nelements;
298       else
299         nchars = g_utf8_strlen (data, nelements);
300
301       GDK_NOTE (DND, g_print ("...nchars:%d\n", nchars));
302       
303       if (i == nelements)
304         {
305           /* If only ASCII, use CF_TEXT and the data as such. */
306           method = PLAIN_ASCII;
307           size = nelements;
308           for (i = 0; i < nelements; i++)
309             if (data[i] == '\n')
310               size++;
311           size++;
312           GDK_NOTE (DND, g_print ("...as text: %.40s\n", data));
313         }
314       else if (IS_WIN_NT ())
315         {
316           /* On NT, use CF_UNICODETEXT if any non-ASCII char present */
317           method = UNICODE_TEXT;
318           size = (nchars + 1) * 2;
319           GDK_NOTE (DND, g_print ("...as Unicode\n"));
320         }
321       else if (find_common_locale (data, nelements, nchars, &lcid, &buf, &size))
322         {
323           /* On Win9x, if all chars are in the default code page of
324            * some installed locale, use CF_TEXT and CF_LOCALE.
325            */
326           method = SINGLE_LOCALE;
327           GDK_NOTE (DND, g_print ("...as text in locale %#lx %d bytes\n",
328                                   (gulong) lcid, size));
329         }
330       else
331         {
332           /* On Win9x, otherwise use RTF */
333
334           const guchar *p = data;
335
336           method = RICH_TEXT;
337           rtf = g_string_new ("{\\rtf1\\uc0 ");
338
339           while (p < data + nelements)
340             {
341               if (*p == '{' ||
342                   *p == '\\' ||
343                   *p == '}')
344                 {
345                   rtf = g_string_append_c (rtf, '\\');
346                   rtf = g_string_append_c (rtf, *p);
347                   p++;
348                 }
349               else if (*p < 0200)
350                 {
351                   rtf = g_string_append_c (rtf, *p);
352                   p++;
353                 }
354               else
355                 {
356                   guchar *q;
357                   gint n;
358                   
359                   rtf = g_string_append (rtf, "\\uNNNNN ");
360                   rtf->len -= 6; /* five digits and a space */
361                   q = rtf->str + rtf->len;
362                   n = g_sprintf (q, "%d ", g_utf8_get_char (p));
363                   g_assert (n <= 6);
364                   rtf->len += n;
365                   
366                   p = g_utf8_next_char (p);
367                 }
368             }
369           rtf = g_string_append (rtf, "}");
370           size = rtf->len + 1;
371           GDK_NOTE (DND, g_print ("...as RTF: %.40s\n", rtf->str));
372         }
373           
374       if (!(hdata = GlobalAlloc (GMEM_MOVEABLE, size)))
375         {
376           WIN32_API_FAILED ("GlobalAlloc");
377           if (!CloseClipboard ())
378             WIN32_API_FAILED ("CloseClipboard");
379           if (buf != NULL)
380             g_free (buf);
381           if (rtf != NULL)
382             g_string_free (rtf, TRUE);
383           return;
384         }
385
386       ucptr = GlobalLock (hdata);
387
388       switch (method)
389         {
390         case PLAIN_ASCII:
391           cf = CF_TEXT;
392           for (i = 0; i < nelements; i++)
393             {
394               if (data[i] == '\n')
395                 *ucptr++ = '\r';
396               *ucptr++ = data[i];
397             }
398           *ucptr++ = '\0';
399           break;
400
401         case UNICODE_TEXT:
402           cf = CF_UNICODETEXT;
403           wcptr = (wchar_t *) ucptr;
404           if (_gdk_utf8_to_ucs2 (wcptr, data, nelements, nchars) == -1)
405             g_warning ("_gdk_utf8_to_ucs2() failed");
406           wcptr[nchars] = 0;
407           break;
408
409         case SINGLE_LOCALE:
410           cf = CF_TEXT;
411           memmove (ucptr, buf, size);
412           g_free (buf);
413
414           /* Set the CF_LOCALE clipboard data, too */
415           if (!(hlcid = GlobalAlloc (GMEM_MOVEABLE, sizeof (LCID))))
416             WIN32_API_FAILED ("GlobalAlloc"), ok = FALSE;
417           if (ok)
418             {
419               lcidptr = GlobalLock (hlcid);
420               *lcidptr = lcid;
421               GlobalUnlock (hlcid);
422               if (!SetClipboardData (CF_LOCALE, hlcid))
423                 WIN32_API_FAILED ("SetClipboardData (CF_LOCALE)"), ok = FALSE;
424             }
425           break;
426
427         case RICH_TEXT:
428           cf = _cf_rtf;
429           memmove (ucptr, rtf->str, size);
430           g_string_free (rtf, TRUE);
431
432           /* Set the UTF8_STRING clipboard data, too, for other
433            * GTK+ apps to use (won't bother reading RTF).
434            */
435           if (!(hutf8 = GlobalAlloc (GMEM_MOVEABLE, nelements)))
436             WIN32_API_FAILED ("GlobalAlloc");
437           else
438             {
439               guchar *utf8ptr = GlobalLock (hutf8);
440               memmove (utf8ptr, data, nelements);
441               GlobalUnlock (hutf8);
442               if (!SetClipboardData (_cf_utf8_string, hutf8))
443                 WIN32_API_FAILED ("SetClipboardData (UTF8_STRING)");
444             }
445           break;
446
447         default:
448           g_assert_not_reached ();
449         }
450
451       GlobalUnlock (hdata);
452       if (ok && !SetClipboardData (cf, hdata))
453         WIN32_API_FAILED ("SetClipboardData"), ok = FALSE;
454       
455       if (!CloseClipboard ())
456         WIN32_API_FAILED ("CloseClipboard");
457     }
458   else
459     g_warning ("gdk_property_change: General case not implemented");
460 }
461
462 void
463 gdk_property_delete (GdkWindow *window,
464                      GdkAtom    property)
465 {
466   gchar *prop_name;
467
468   g_return_if_fail (window != NULL);
469   g_return_if_fail (GDK_IS_WINDOW (window));
470
471   GDK_NOTE (DND,
472             (prop_name = gdk_atom_name (property),
473              g_print ("gdk_property_delete: %p %#x (%s)\n",
474                       GDK_WINDOW_HWND (window),
475                       (guint) property, prop_name),
476              g_free (prop_name)));
477
478   if (property == _gdk_selection_property)
479     _gdk_selection_property_delete (window);
480   else if (property == _wm_transient_for)
481     gdk_window_set_transient_for (window, _gdk_parent_root);
482   else
483     {
484       prop_name = gdk_atom_name (property);
485       g_warning ("gdk_property_delete: General case (%s) not implemented",
486                  prop_name);
487       g_free (prop_name);
488     }
489 }
490
491 gboolean
492 gdk_screen_get_setting (GdkScreen   *screen,
493                         const gchar *name,
494                         GValue      *value)
495 {
496   g_return_val_if_fail (screen == gdk_screen_get_default (), FALSE);
497
498   /*
499    * XXX : if these values get changed through the Windoze UI the
500    *       respective gdk_events are not generated yet.
501    */
502   if (strcmp ("double-click-timeout", name) == 0)
503     {
504       g_value_set_int (value, GetDoubleClickTime ());
505       return TRUE;
506     }
507   else if (strcmp ("drag-threshold", name) == 0)
508     {
509       g_value_set_int (value, MAX(GetSystemMetrics (SM_CXDRAG), GetSystemMetrics (SM_CYDRAG)));
510       return TRUE;
511     }
512   else
513     return FALSE;
514 }