]> Pileus Git - ~andy/gtk/blob - gdk/win32/gdkproperty-win32.c
gdk/win32/gdkdrawable-win32.c (gdk_win32_draw_text)
[~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
203   /* Convert to Windows wide chars into temp buf */
204   wcs = g_utf8_to_utf16 (data, nelements, NULL, NULL, NULL);
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   glong wclen;
256   enum { SYSTEM_CODEPAGE, 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 && GetACP () == 1252) ||
282           type == _utf8_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       if (type == _utf8_string)
293         {
294           /* Check if only ASCII */
295           for (i = 0; i < nelements; i++)
296             if (data[i] >= 0200)
297               break;
298         }
299       else /* if (type == GDK_TARGET_STRING) */
300         {
301           /* Check that no 0200..0240 chars present, as they
302            * differ between ISO-8859-1 and CP1252.
303            */
304           for (i = 0; i < nelements; i++)
305             if (data[i] >= 0200 && data[i] < 0240)
306               break;
307         }
308       nchars = g_utf8_strlen (data, nelements);
309
310       if (i == nelements)
311         {
312           /* If UTF-8 and only ASCII, or if STRING (ISO-8859-1) and
313            * system codepage is CP1252, use CF_TEXT and the data as
314            * such.
315            */
316           method = SYSTEM_CODEPAGE;
317           size = nelements;
318           for (i = 0; i < nelements; i++)
319             if (data[i] == '\n')
320               size++;
321           size++;
322           GDK_NOTE (DND, g_print ("...as text: %.40s\n", data));
323         }
324       else if (IS_WIN_NT ())
325         {
326           /* On NT, use CF_UNICODETEXT if any non-system codepage char
327            * present.
328            */
329           method = UNICODE_TEXT;
330
331           wcptr = g_utf8_to_utf16 (data, nelements, NULL, &wclen, NULL);
332
333           wclen++;              /* Terminating 0 */
334           size = wclen * 2;
335           GDK_NOTE (DND, g_print ("...as Unicode\n"));
336         }
337       else if (find_common_locale (data, nelements, nchars, &lcid, &buf, &size))
338         {
339           /* On Win9x, if all chars are in the default code page of
340            * some installed locale, use CF_TEXT and CF_LOCALE.
341            */
342           method = SINGLE_LOCALE;
343           GDK_NOTE (DND, g_print ("...as text in locale %#lx %d bytes\n",
344                                   (gulong) lcid, size));
345         }
346       else
347         {
348           /* On Win9x, otherwise use RTF */
349
350           const guchar *p = data;
351
352           method = RICH_TEXT;
353           rtf = g_string_new ("{\\rtf1\\uc0 ");
354
355           while (p < data + nelements)
356             {
357               if (*p == '{' ||
358                   *p == '\\' ||
359                   *p == '}')
360                 {
361                   rtf = g_string_append_c (rtf, '\\');
362                   rtf = g_string_append_c (rtf, *p);
363                   p++;
364                 }
365               else if (*p < 0200 && *p >= ' ')
366                 {
367                   rtf = g_string_append_c (rtf, *p);
368                   p++;
369                 }
370               else
371                 {
372                   guchar *q;
373                   gint n;
374                   
375                   rtf = g_string_append (rtf, "\\uNNNNN ");
376                   rtf->len -= 6; /* five digits and a space */
377                   q = rtf->str + rtf->len;
378                   n = g_sprintf (q, "%d ", g_utf8_get_char (p));
379                   g_assert (n <= 6);
380                   rtf->len += n;
381                   
382                   p = g_utf8_next_char (p);
383                 }
384             }
385           rtf = g_string_append (rtf, "}");
386           size = rtf->len + 1;
387           GDK_NOTE (DND, g_print ("...as RTF: %.40s\n", rtf->str));
388         }
389           
390       if (!(hdata = GlobalAlloc (GMEM_MOVEABLE, size)))
391         {
392           WIN32_API_FAILED ("GlobalAlloc");
393           if (!CloseClipboard ())
394             WIN32_API_FAILED ("CloseClipboard");
395           if (buf != NULL)
396             g_free (buf);
397           if (rtf != NULL)
398             g_string_free (rtf, TRUE);
399           return;
400         }
401
402       ucptr = GlobalLock (hdata);
403
404       switch (method)
405         {
406         case SYSTEM_CODEPAGE:
407           cf = CF_TEXT;
408           for (i = 0; i < nelements; i++)
409             {
410               if (data[i] == '\n')
411                 *ucptr++ = '\r';
412               *ucptr++ = data[i];
413             }
414           *ucptr++ = '\0';
415           break;
416
417         case UNICODE_TEXT:
418           cf = CF_UNICODETEXT;
419           memmove (ucptr, wcptr, size);
420           g_free (wcptr);
421           break;
422
423         case SINGLE_LOCALE:
424           cf = CF_TEXT;
425           memmove (ucptr, buf, size);
426           g_free (buf);
427
428           /* Set the CF_LOCALE clipboard data, too */
429           if (!(hlcid = GlobalAlloc (GMEM_MOVEABLE, sizeof (LCID))))
430             WIN32_API_FAILED ("GlobalAlloc"), ok = FALSE;
431           if (ok)
432             {
433               lcidptr = GlobalLock (hlcid);
434               *lcidptr = lcid;
435               GlobalUnlock (hlcid);
436               if (!SetClipboardData (CF_LOCALE, hlcid))
437                 WIN32_API_FAILED ("SetClipboardData (CF_LOCALE)"), ok = FALSE;
438             }
439           break;
440
441         case RICH_TEXT:
442           cf = _cf_rtf;
443           memmove (ucptr, rtf->str, size);
444           g_string_free (rtf, TRUE);
445
446           /* Set the UTF8_STRING clipboard data, too, for other
447            * GTK+ apps to use (won't bother reading RTF).
448            */
449           if (!(hutf8 = GlobalAlloc (GMEM_MOVEABLE, nelements)))
450             WIN32_API_FAILED ("GlobalAlloc");
451           else
452             {
453               guchar *utf8ptr = GlobalLock (hutf8);
454               memmove (utf8ptr, data, nelements);
455               GlobalUnlock (hutf8);
456               if (!SetClipboardData (_cf_utf8_string, hutf8))
457                 WIN32_API_FAILED ("SetClipboardData (UTF8_STRING)");
458             }
459           break;
460
461         default:
462           g_assert_not_reached ();
463         }
464
465       GlobalUnlock (hdata);
466       if (ok && !SetClipboardData (cf, hdata))
467         WIN32_API_FAILED ("SetClipboardData"), ok = FALSE;
468       
469       if (!CloseClipboard ())
470         WIN32_API_FAILED ("CloseClipboard");
471     }
472   else
473     g_warning ("gdk_property_change: General case not implemented");
474 }
475
476 void
477 gdk_property_delete (GdkWindow *window,
478                      GdkAtom    property)
479 {
480   gchar *prop_name;
481
482   g_return_if_fail (window != NULL);
483   g_return_if_fail (GDK_IS_WINDOW (window));
484
485   GDK_NOTE (DND,
486             (prop_name = gdk_atom_name (property),
487              g_print ("gdk_property_delete: %p %#x (%s)\n",
488                       GDK_WINDOW_HWND (window),
489                       (guint) property, prop_name),
490              g_free (prop_name)));
491
492   if (property == _gdk_selection_property)
493     _gdk_selection_property_delete (window);
494   else if (property == _wm_transient_for)
495     gdk_window_set_transient_for (window, _gdk_parent_root);
496   else
497     {
498       prop_name = gdk_atom_name (property);
499       g_warning ("gdk_property_delete: General case (%s) not implemented",
500                  prop_name);
501       g_free (prop_name);
502     }
503 }
504
505 /*
506   for reference copied from gdk/x11/gdkevents-x11.c
507
508   { "Net/DoubleClickTime", "gtk-double-click-time" },
509   { "Net/DoubleClickDistance", "gtk-double-click-distance" },
510   { "Net/DndDragThreshold", "gtk-dnd-drag-threshold" },
511   { "Gtk/CanChangeAccels", "gtk-can-change-accels" },
512   { "Gtk/ColorPalette", "gtk-color-palette" },
513   { "Gtk/FontName", "gtk-font-name" },
514   { "Gtk/IconSizes", "gtk-icon-sizes" },
515   { "Gtk/KeyThemeName", "gtk-key-theme-name" },
516   { "Gtk/ToolbarStyle", "gtk-toolbar-style" },
517   { "Gtk/ToolbarIconSize", "gtk-toolbar-icon-size" },
518   { "Gtk/IMPreeditStyle", "gtk-im-preedit-style" },
519   { "Gtk/IMStatusStyle", "gtk-im-status-style" },
520   { "Net/CursorBlink", "gtk-cursor-blink" },
521   { "Net/CursorBlinkTime", "gtk-cursor-blink-time" },
522   { "Net/ThemeName", "gtk-theme-name" },
523   { "Net/IconThemeName", "gtk-icon-theme-name" },
524   { "Gtk/ButtonImages", "gtk-button-images" },
525   { "Gtk/MenuImages", "gtk-menu-images" },
526   { "Xft/Antialias", "gtk-xft-antialias" },
527   { "Xft/Hinting", "gtk-xft-hinting" },
528   { "Xft/HintStyle", "gtk-xft-hintstyle" },
529   { "Xft/RGBA", "gtk-xft-rgba" },
530   { "Xft/DPI", "gtk-xft-dpi" },
531
532   // more spread in gtk sources
533   gtk-entry-select-on-focus
534   gtk-cursor-blink
535   gtk-cursor-blink-time
536   gtk-split-cursor
537
538 */
539 gboolean
540 gdk_screen_get_setting (GdkScreen   *screen,
541                         const gchar *name,
542                         GValue      *value)
543 {
544   g_return_val_if_fail (screen == gdk_screen_get_default (), FALSE);
545
546   /*
547    * XXX : if these values get changed through the Windoze UI the
548    *       respective gdk_events are not generated yet.
549    */
550   if (strcmp ("gtk-double-click-time", name) == 0)
551     {
552       gint i = GetDoubleClickTime ();
553       GDK_NOTE(MISC, g_print("gdk_screen_get_setting(\"%s\") : %d\n", name, i));
554       g_value_set_int (value, i);
555       return TRUE;
556     }
557   else if (strcmp ("gtk-double-click-distance", name) == 0)
558     {
559       gint i = MAX(GetSystemMetrics (SM_CXDOUBLECLK), GetSystemMetrics (SM_CYDOUBLECLK));
560       GDK_NOTE(MISC, g_print("gdk_screen_get_setting(\"%s\") : %d\n", name, i));
561       g_value_set_int (value, i);
562       return TRUE;
563     }
564   else if (strcmp ("gtk-dnd-drag-threshold", name) == 0)
565     {
566       gint i = MAX(GetSystemMetrics (SM_CXDRAG), GetSystemMetrics (SM_CYDRAG));
567       GDK_NOTE(MISC, g_print("gdk_screen_get_setting(\"%s\") : %d\n", name, i));
568       g_value_set_int (value, i);
569       return TRUE;
570     }
571   else if (strcmp ("gtk-split-cursor", name) == 0)
572     {
573       GDK_NOTE(MISC, g_print("gdk_screen_get_setting(\"%s\") : FALSE\n", name));
574       g_value_set_boolean (value, FALSE);
575       return TRUE;
576     }
577 #if 0
578   /*
579    * With 'MS Sans Serif' as windows menu font (default on win98se) you'll get a 
580    * bunch of :
581    *   WARNING **: Couldn't load font "MS Sans Serif 8" falling back to "Sans 8"
582    * at least with testfilechooser (regardless of the bitmap check below)
583    * so just disabling this code seems to be the best we can do --hb
584    */
585   else if (strcmp ("gtk-font-name", name) == 0)
586     {
587       NONCLIENTMETRICS ncm;
588       ncm.cbSize = sizeof(NONCLIENTMETRICS);
589       if (SystemParametersInfo (SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, FALSE))
590         {
591           /* Pango finally uses GetDeviceCaps to scale, we use simple approximation here */
592           int nHeight = (0 > ncm.lfMenuFont.lfHeight ? -3*ncm.lfMenuFont.lfHeight/4 : 10);
593           if (OUT_STRING_PRECIS == ncm.lfMenuFont.lfOutPrecision)
594             GDK_NOTE(MISC, g_print("gdk_screen_get_setting(%s) : ignoring bitmap font '%s'\n", 
595                                    name, ncm.lfMenuFont.lfFaceName));
596           else if (ncm.lfMenuFont.lfFaceName && strlen(ncm.lfMenuFont.lfFaceName) > 0 &&
597                    /* avoid issues like those described in bug #135098 */
598                    g_utf8_validate (ncm.lfMenuFont.lfFaceName, -1, NULL))
599             {
600               char* s = g_strdup_printf ("%s %d", ncm.lfMenuFont.lfFaceName, nHeight);
601               GDK_NOTE(MISC, g_print("gdk_screen_get_setting(%s) : %s\n", name, s));
602               g_value_set_string (value, s);
603
604               g_free(s);
605               return TRUE;
606             }
607         }
608     }
609 #endif
610
611   GDK_NOTE(MISC, g_print("gdk_screen_get_setting(%s) not handled\n", name));
612   return FALSE;
613 }