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