]> Pileus Git - ~andy/gtk/blob - gdk/win32/gdkproperty-win32.c
some more settings from system, also reflect some name changes done a
[~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 <config.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <glib/gprintf.h>
32
33 #include "gdkscreen.h"
34 #include "gdkproperty.h"
35 #include "gdkselection.h"
36 #include "gdkprivate-win32.h"
37
38 GdkAtom
39 gdk_atom_intern (const gchar *atom_name,
40                  gint         only_if_exists)
41 {
42   ATOM win32_atom;
43   GdkAtom retval;
44   static GHashTable *atom_hash = NULL;
45   
46   if (!atom_hash)
47     atom_hash = g_hash_table_new (g_str_hash, g_str_equal);
48
49   retval = g_hash_table_lookup (atom_hash, atom_name);
50   if (!retval)
51     {
52       if (strcmp (atom_name, "PRIMARY") == 0)
53         retval = GDK_SELECTION_PRIMARY;
54       else if (strcmp (atom_name, "SECONDARY") == 0)
55         retval = GDK_SELECTION_SECONDARY;
56       else if (strcmp (atom_name, "CLIPBOARD") == 0)
57         retval = GDK_SELECTION_CLIPBOARD;
58       else if (strcmp (atom_name, "ATOM") == 0)
59         retval = GDK_SELECTION_TYPE_ATOM;
60       else if (strcmp (atom_name, "BITMAP") == 0)
61         retval = GDK_SELECTION_TYPE_BITMAP;
62       else if (strcmp (atom_name, "COLORMAP") == 0)
63         retval = GDK_SELECTION_TYPE_COLORMAP;
64       else if (strcmp (atom_name, "DRAWABLE") == 0)
65         retval = GDK_SELECTION_TYPE_DRAWABLE;
66       else if (strcmp (atom_name, "INTEGER") == 0)
67         retval = GDK_SELECTION_TYPE_INTEGER;
68       else if (strcmp (atom_name, "PIXMAP") == 0)
69         retval = GDK_SELECTION_TYPE_PIXMAP;
70       else if (strcmp (atom_name, "WINDOW") == 0)
71         retval = GDK_SELECTION_TYPE_WINDOW;
72       else if (strcmp (atom_name, "STRING") == 0)
73         retval = GDK_SELECTION_TYPE_STRING;
74       else
75         {
76           win32_atom = GlobalAddAtom (atom_name);
77           retval = GUINT_TO_POINTER ((guint) win32_atom);
78         }
79       g_hash_table_insert (atom_hash, 
80                            g_strdup (atom_name), 
81                            retval);
82     }
83
84   return retval;
85 }
86
87 gchar *
88 gdk_atom_name (GdkAtom atom)
89 {
90   ATOM win32_atom;
91   gchar name[256];
92
93   if (GDK_SELECTION_PRIMARY == atom) return g_strdup ("PRIMARY");
94   else if (GDK_SELECTION_SECONDARY == atom) return g_strdup ("SECONDARY");
95   else if (GDK_SELECTION_CLIPBOARD == atom) return g_strdup ("CLIPBOARD");
96   else if (GDK_SELECTION_TYPE_ATOM == atom) return g_strdup ("ATOM");
97   else if (GDK_SELECTION_TYPE_BITMAP == atom) return g_strdup ("BITMAP");
98   else if (GDK_SELECTION_TYPE_COLORMAP == atom) return g_strdup ("COLORMAP");
99   else if (GDK_SELECTION_TYPE_DRAWABLE == atom) return g_strdup ("DRAWABLE");
100   else if (GDK_SELECTION_TYPE_INTEGER == atom) return g_strdup ("INTEGER");
101   else if (GDK_SELECTION_TYPE_PIXMAP == atom) return g_strdup ("PIXMAP");
102   else if (GDK_SELECTION_TYPE_WINDOW == atom) return g_strdup ("WINDOW");
103   else if (GDK_SELECTION_TYPE_STRING == atom) return g_strdup ("STRING");
104   
105   win32_atom = GPOINTER_TO_UINT (atom);
106   
107   if (win32_atom < 0xC000)
108     return g_strdup_printf ("#%p", atom);
109   else if (GlobalGetAtomName (win32_atom, name, sizeof (name)) == 0)
110     return NULL;
111   return g_strdup (name);
112 }
113
114 gint
115 gdk_property_get (GdkWindow   *window,
116                   GdkAtom      property,
117                   GdkAtom      type,
118                   gulong       offset,
119                   gulong       length,
120                   gint         pdelete,
121                   GdkAtom     *actual_property_type,
122                   gint        *actual_format_type,
123                   gint        *actual_length,
124                   guchar     **data)
125 {
126   g_return_val_if_fail (window != NULL, FALSE);
127   g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
128
129   if (GDK_WINDOW_DESTROYED (window))
130     return FALSE;
131
132   g_warning ("gdk_property_get: Not implemented");
133
134   return FALSE;
135 }
136
137 static gboolean
138 find_common_locale (const guchar  *data,
139                     gint           nelements,
140                     gint           nchars,
141                     LCID          *lcidp,
142                     guchar       **bufp,
143                     gint          *sizep)
144 {
145   static struct {
146     LCID lcid;
147     UINT cp;
148   } locales[] = {
149 #define ENTRY(lang, sublang) \
150  { MAKELCID (MAKELANGID (LANG_##lang, SUBLANG_##sublang), SORT_DEFAULT), 0 }
151     ENTRY (ENGLISH, DEFAULT),
152     ENTRY (POLISH, DEFAULT),
153     ENTRY (CZECH, DEFAULT),
154     ENTRY (LITHUANIAN, DEFAULT),
155     ENTRY (RUSSIAN, DEFAULT),
156     ENTRY (GREEK, DEFAULT),
157     ENTRY (TURKISH, DEFAULT),
158     ENTRY (HEBREW, DEFAULT),
159     ENTRY (ARABIC, DEFAULT),
160     ENTRY (THAI, DEFAULT),
161     ENTRY (JAPANESE, DEFAULT),
162     ENTRY (CHINESE, CHINESE_SIMPLIFIED),
163     ENTRY (CHINESE, CHINESE_TRADITIONAL),
164     ENTRY (KOREAN, DEFAULT),
165 #undef ENTRY
166   };
167
168   static gboolean been_here = FALSE;
169   gint i;
170   wchar_t *wcs;
171
172   /* For each installed locale: Get the locale's default code page,
173    * and store the list of locales and code pages.
174    */
175   if (!been_here)
176     {
177       been_here = TRUE;
178       for (i = 0; i < G_N_ELEMENTS (locales); i++)
179         if (IsValidLocale (locales[i].lcid, LCID_INSTALLED))
180           {
181             gchar buf[10];
182             if (GetLocaleInfo (locales[i].lcid, LOCALE_IDEFAULTANSICODEPAGE,
183                                buf, sizeof (buf)))
184               {
185                 gchar name[100];
186                 locales[i].cp = atoi (buf);
187                 GDK_NOTE (DND, (GetLocaleInfo (locales[i].lcid,
188                                                LOCALE_SENGLANGUAGE,
189                                                name, sizeof (name)),
190                                 g_print ("locale %#lx: %s: CP%d\n",
191                                          (gulong) locales[i].lcid, name,
192                                          locales[i].cp)));
193               }
194           }
195     }
196   
197   /* Allocate bufp big enough to store data in any code page.  Two
198    * bytes for each Unicode char should be enough, Windows code pages
199    * are either single- or double-byte.
200    */
201   *bufp = g_malloc ((nchars+1) * 2);
202   wcs = g_new (wchar_t, nchars+1);
203
204   /* Convert to Windows wide chars into temp buf */
205   _gdk_utf8_to_ucs2 (wcs, data, nelements, nchars);
206   wcs[nchars] = 0;
207
208   /* For each code page that is the default for an installed locale: */
209   for (i = 0; i < G_N_ELEMENTS (locales); i++)
210     {
211       BOOL used_default;
212       int nbytes;
213
214       if (locales[i].cp == 0)
215         continue;
216
217       /* Convert to that code page into bufp */
218       
219       nbytes = WideCharToMultiByte (locales[i].cp, 0, wcs, -1,
220                                     *bufp, (nchars+1)*2,
221                                     NULL, &used_default);
222
223       if (!used_default)
224         {
225           /* This locale is good for the string */
226           g_free (wcs);
227           *lcidp = locales[i].lcid;
228           *sizep = nbytes;
229           return TRUE;
230         }
231     }
232
233   g_free (*bufp);
234   g_free (wcs);
235
236   return FALSE;
237 }
238
239 void
240 gdk_property_change (GdkWindow    *window,
241                      GdkAtom       property,
242                      GdkAtom       type,
243                      gint          format,
244                      GdkPropMode   mode,
245                      const guchar *data,
246                      gint          nelements)
247 {
248   HGLOBAL hdata, hlcid, hutf8;
249   UINT cf = 0;
250   LCID lcid;
251   LCID *lcidptr;
252   GString *rtf = NULL;
253   gint i, size, nchars;
254   gchar *prop_name, *type_name;
255   guchar *ucptr, *buf = NULL;
256   wchar_t *wcptr;
257   enum { PLAIN_ASCII, UNICODE_TEXT, SINGLE_LOCALE, RICH_TEXT } method;
258   gboolean ok = TRUE;
259
260   g_return_if_fail (window != NULL);
261   g_return_if_fail (GDK_IS_WINDOW (window));
262
263   if (GDK_WINDOW_DESTROYED (window))
264     return;
265
266   GDK_NOTE (DND,
267             (prop_name = gdk_atom_name (property),
268              type_name = gdk_atom_name (type),
269              g_print ("gdk_property_change: %p %#x (%s) %#x (%s) %s %d*%d bytes %.10s\n",
270                       GDK_WINDOW_HWND (window),
271                       (guint) property, prop_name,
272                       (guint) type, type_name,
273                       (mode == GDK_PROP_MODE_REPLACE ? "REPLACE" :
274                        (mode == GDK_PROP_MODE_PREPEND ? "PREPEND" :
275                         (mode == GDK_PROP_MODE_APPEND ? "APPEND" :
276                          "???"))),
277                       format, nelements, data),
278              g_free (prop_name),
279              g_free (type_name)));
280
281   if (property == _gdk_selection_property
282       && type == GDK_TARGET_STRING
283       && format == 8
284       && mode == GDK_PROP_MODE_REPLACE)
285     {
286       if (!OpenClipboard (GDK_WINDOW_HWND (window)))
287         {
288           WIN32_API_FAILED ("OpenClipboard");
289           return;
290         }
291
292       /* Check if only ASCII */
293       for (i = 0; i < nelements; i++)
294         if (data[i] >= 0200)
295           break;
296
297       if (i == nelements)
298         nchars = nelements;
299       else
300         nchars = g_utf8_strlen (data, nelements);
301
302       GDK_NOTE (DND, g_print ("...nchars:%d\n", nchars));
303       
304       if (i == nelements)
305         {
306           /* If only ASCII, use CF_TEXT and the data as such. */
307           method = PLAIN_ASCII;
308           size = nelements;
309           for (i = 0; i < nelements; i++)
310             if (data[i] == '\n')
311               size++;
312           size++;
313           GDK_NOTE (DND, g_print ("...as text: %.40s\n", data));
314         }
315       else if (IS_WIN_NT ())
316         {
317           /* On NT, use CF_UNICODETEXT if any non-ASCII char present */
318           method = UNICODE_TEXT;
319           size = (nchars + 1) * 2;
320           GDK_NOTE (DND, g_print ("...as Unicode\n"));
321         }
322       else if (find_common_locale (data, nelements, nchars, &lcid, &buf, &size))
323         {
324           /* On Win9x, if all chars are in the default code page of
325            * some installed locale, use CF_TEXT and CF_LOCALE.
326            */
327           method = SINGLE_LOCALE;
328           GDK_NOTE (DND, g_print ("...as text in locale %#lx %d bytes\n",
329                                   (gulong) lcid, size));
330         }
331       else
332         {
333           /* On Win9x, otherwise use RTF */
334
335           const guchar *p = data;
336
337           method = RICH_TEXT;
338           rtf = g_string_new ("{\\rtf1\\uc0 ");
339
340           while (p < data + nelements)
341             {
342               if (*p == '{' ||
343                   *p == '\\' ||
344                   *p == '}')
345                 {
346                   rtf = g_string_append_c (rtf, '\\');
347                   rtf = g_string_append_c (rtf, *p);
348                   p++;
349                 }
350               else if (*p < 0200)
351                 {
352                   rtf = g_string_append_c (rtf, *p);
353                   p++;
354                 }
355               else
356                 {
357                   guchar *q;
358                   gint n;
359                   
360                   rtf = g_string_append (rtf, "\\uNNNNN ");
361                   rtf->len -= 6; /* five digits and a space */
362                   q = rtf->str + rtf->len;
363                   n = g_sprintf (q, "%d ", g_utf8_get_char (p));
364                   g_assert (n <= 6);
365                   rtf->len += n;
366                   
367                   p = g_utf8_next_char (p);
368                 }
369             }
370           rtf = g_string_append (rtf, "}");
371           size = rtf->len + 1;
372           GDK_NOTE (DND, g_print ("...as RTF: %.40s\n", rtf->str));
373         }
374           
375       if (!(hdata = GlobalAlloc (GMEM_MOVEABLE, size)))
376         {
377           WIN32_API_FAILED ("GlobalAlloc");
378           if (!CloseClipboard ())
379             WIN32_API_FAILED ("CloseClipboard");
380           if (buf != NULL)
381             g_free (buf);
382           if (rtf != NULL)
383             g_string_free (rtf, TRUE);
384           return;
385         }
386
387       ucptr = GlobalLock (hdata);
388
389       switch (method)
390         {
391         case PLAIN_ASCII:
392           cf = CF_TEXT;
393           for (i = 0; i < nelements; i++)
394             {
395               if (data[i] == '\n')
396                 *ucptr++ = '\r';
397               *ucptr++ = data[i];
398             }
399           *ucptr++ = '\0';
400           break;
401
402         case UNICODE_TEXT:
403           cf = CF_UNICODETEXT;
404           wcptr = (wchar_t *) ucptr;
405           if (_gdk_utf8_to_ucs2 (wcptr, data, nelements, nchars) == -1)
406             g_warning ("_gdk_utf8_to_ucs2() failed");
407           wcptr[nchars] = 0;
408           break;
409
410         case SINGLE_LOCALE:
411           cf = CF_TEXT;
412           memmove (ucptr, buf, size);
413           g_free (buf);
414
415           /* Set the CF_LOCALE clipboard data, too */
416           if (!(hlcid = GlobalAlloc (GMEM_MOVEABLE, sizeof (LCID))))
417             WIN32_API_FAILED ("GlobalAlloc"), ok = FALSE;
418           if (ok)
419             {
420               lcidptr = GlobalLock (hlcid);
421               *lcidptr = lcid;
422               GlobalUnlock (hlcid);
423               if (!SetClipboardData (CF_LOCALE, hlcid))
424                 WIN32_API_FAILED ("SetClipboardData (CF_LOCALE)"), ok = FALSE;
425             }
426           break;
427
428         case RICH_TEXT:
429           cf = _cf_rtf;
430           memmove (ucptr, rtf->str, size);
431           g_string_free (rtf, TRUE);
432
433           /* Set the UTF8_STRING clipboard data, too, for other
434            * GTK+ apps to use (won't bother reading RTF).
435            */
436           if (!(hutf8 = GlobalAlloc (GMEM_MOVEABLE, nelements)))
437             WIN32_API_FAILED ("GlobalAlloc");
438           else
439             {
440               guchar *utf8ptr = GlobalLock (hutf8);
441               memmove (utf8ptr, data, nelements);
442               GlobalUnlock (hutf8);
443               if (!SetClipboardData (_cf_utf8_string, hutf8))
444                 WIN32_API_FAILED ("SetClipboardData (UTF8_STRING)");
445             }
446           break;
447
448         default:
449           g_assert_not_reached ();
450         }
451
452       GlobalUnlock (hdata);
453       if (ok && !SetClipboardData (cf, hdata))
454         WIN32_API_FAILED ("SetClipboardData"), ok = FALSE;
455       
456       if (!CloseClipboard ())
457         WIN32_API_FAILED ("CloseClipboard");
458     }
459   else
460     g_warning ("gdk_property_change: General case not implemented");
461 }
462
463 void
464 gdk_property_delete (GdkWindow *window,
465                      GdkAtom    property)
466 {
467   gchar *prop_name;
468
469   g_return_if_fail (window != NULL);
470   g_return_if_fail (GDK_IS_WINDOW (window));
471
472   GDK_NOTE (DND,
473             (prop_name = gdk_atom_name (property),
474              g_print ("gdk_property_delete: %p %#x (%s)\n",
475                       GDK_WINDOW_HWND (window),
476                       (guint) property, prop_name),
477              g_free (prop_name)));
478
479   if (property == _gdk_selection_property)
480     _gdk_selection_property_delete (window);
481   else if (property == _wm_transient_for)
482     gdk_window_set_transient_for (window, _gdk_parent_root);
483   else
484     {
485       prop_name = gdk_atom_name (property);
486       g_warning ("gdk_property_delete: General case (%s) not implemented",
487                  prop_name);
488       g_free (prop_name);
489     }
490 }
491
492 /*
493   for reference copied from gdk/x11/gdkevents-x11.c
494
495   { "Net/DoubleClickTime", "gtk-double-click-time" },
496   { "Net/DoubleClickDistance", "gtk-double-click-distance" },
497   { "Net/DndDragThreshold", "gtk-dnd-drag-threshold" },
498   { "Gtk/CanChangeAccels", "gtk-can-change-accels" },
499   { "Gtk/ColorPalette", "gtk-color-palette" },
500   { "Gtk/FontName", "gtk-font-name" },
501   { "Gtk/IconSizes", "gtk-icon-sizes" },
502   { "Gtk/KeyThemeName", "gtk-key-theme-name" },
503   { "Gtk/ToolbarStyle", "gtk-toolbar-style" },
504   { "Gtk/ToolbarIconSize", "gtk-toolbar-icon-size" },
505   { "Gtk/IMPreeditStyle", "gtk-im-preedit-style" },
506   { "Gtk/IMStatusStyle", "gtk-im-status-style" },
507   { "Net/CursorBlink", "gtk-cursor-blink" },
508   { "Net/CursorBlinkTime", "gtk-cursor-blink-time" },
509   { "Net/ThemeName", "gtk-theme-name" },
510   { "Net/IconThemeName", "gtk-icon-theme-name" },
511   { "Gtk/ButtonImages", "gtk-button-images" },
512   { "Gtk/MenuImages", "gtk-menu-images" },
513   { "Xft/Antialias", "gtk-xft-antialias" },
514   { "Xft/Hinting", "gtk-xft-hinting" },
515   { "Xft/HintStyle", "gtk-xft-hintstyle" },
516   { "Xft/RGBA", "gtk-xft-rgba" },
517   { "Xft/DPI", "gtk-xft-dpi" },
518
519   // more spread in gtk sources
520   gtk-entry-select-on-focus
521   gtk-cursor-blink
522   gtk-cursor-blink-time
523   gtk-split-cursor
524
525 */
526 gboolean
527 gdk_screen_get_setting (GdkScreen   *screen,
528                         const gchar *name,
529                         GValue      *value)
530 {
531   g_return_val_if_fail (screen == gdk_screen_get_default (), FALSE);
532
533   /*
534    * XXX : if these values get changed through the Windoze UI the
535    *       respective gdk_events are not generated yet.
536    */
537   if (strcmp ("gtk-double-click-time", name) == 0)
538     {
539       gint i = GetDoubleClickTime ();
540       GDK_NOTE(MISC, g_print("gdk_screen_get_setting(\"%s\") : %d\n", name, i));
541       g_value_set_int (value, i);
542       return TRUE;
543     }
544   else if (strcmp ("gtk-double-click-distance", name) == 0)
545     {
546       gint i = MAX(GetSystemMetrics (SM_CXDOUBLECLK), GetSystemMetrics (SM_CYDOUBLECLK));
547       GDK_NOTE(MISC, g_print("gdk_screen_get_setting(\"%s\") : %d\n", name, i));
548       g_value_set_int (value, i);
549       return TRUE;
550     }
551   else if (strcmp ("gtk-dnd-drag-threshold", name) == 0)
552     {
553       gint i = MAX(GetSystemMetrics (SM_CXDRAG), GetSystemMetrics (SM_CYDRAG));
554       GDK_NOTE(MISC, g_print("gdk_screen_get_setting(\"%s\") : %d\n", name, i));
555       g_value_set_int (value, i);
556       return TRUE;
557     }
558   else if (strcmp ("gtk-split-cursor", name) == 0)
559     {
560       GDK_NOTE(MISC, g_print("gdk_screen_get_setting(\"%s\") : FALSE\n", name));
561       g_value_set_boolean (value, FALSE);
562       return TRUE;
563     }
564 #if 0
565   /*
566    * With 'MS Sans Serif' as windows menu font (default on win98se) you'll get a 
567    * bunch of :
568    *   WARNING **: Couldn't load font "MS Sans Serif 8" falling back to "Sans 8"
569    * at least with testfilechooser (regardless of the bitmap check below)
570    * so just disabling this code seems to be the best we can do --hb
571    */
572   else if (strcmp ("gtk-font-name", name) == 0)
573     {
574       NONCLIENTMETRICS ncm;
575       ncm.cbSize = sizeof(NONCLIENTMETRICS);
576       if (SystemParametersInfo (SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, FALSE))
577         {
578           /* Pango finally uses GetDeviceCaps to scale, we use simple approximation here */
579           int nHeight = (0 > ncm.lfMenuFont.lfHeight ? -3*ncm.lfMenuFont.lfHeight/4 : 10);
580           if (OUT_STRING_PRECIS == ncm.lfMenuFont.lfOutPrecision)
581             GDK_NOTE(MISC, g_print("gdk_screen_get_setting(%s) : ignoring bitmap font '%s'\n", 
582                                    name, ncm.lfMenuFont.lfFaceName));
583           else if (ncm.lfMenuFont.lfFaceName && strlen(ncm.lfMenuFont.lfFaceName) > 0 &&
584                    /* avoid issues like those described in bug #135098 */
585                    g_utf8_validate (ncm.lfMenuFont.lfFaceName, -1, NULL))
586             {
587               char* s = g_strdup_printf ("%s %d", ncm.lfMenuFont.lfFaceName, nHeight);
588               GDK_NOTE(MISC, g_print("gdk_screen_get_setting(%s) : %s\n", name, s));
589               g_value_set_string (value, s);
590
591               g_free(s);
592               return TRUE;
593             }
594         }
595     }
596 #endif
597
598   GDK_NOTE(MISC, g_print("gdk_screen_get_setting(%s) not handled\n", name));
599   return FALSE;
600 }