From 0b2ac32cf5d8ed96ebf743cbfc112d7e52928c81 Mon Sep 17 00:00:00 2001 From: Tor Lillqvist Date: Mon, 4 Apr 2005 00:12:26 +0000 Subject: [PATCH] New debugging function, to log a clipboard format name symbolically. 2005-04-04 Tor Lillqvist * gdk/win32/gdkmain.c (_gdk_win32_cf_to_string): New debugging function, to log a clipboard format name symbolically. (_gdk_win32_data_to_string): Also new, to log random data bytes. Implement delayed rendering on Win32, specifically for transfering images through the clipboard from GTK+ apps to other apps (#168173, implementation by Ivan Wong): * gdk/win32/gdkevents-win32.c (gdk_event_translate): Handle WM_RENDERFORMAT. * gdk/win32/gdkprivate-win32.h * gdk/win32/gdkglobals-win32.c: Add _format_atom_table, _delayed_rendering_data and _image_bmp. * gdk/win32/gdkmain-win32.c: Initialize _image_bmp. * gdk/win32/gdkproperty-win32.c (gdk_property_change): Accept formats other than GDK_TARGET_STRING or _utf8_string, and assume they are handled through delayed rendering. * gdk/win32/gdkselection-win32.c (gdk_selection_convert): Return all available formats (including those registered by GTK+ apps) on request_targets. (gdk_selection_property_get): We should append a zero byte like X11 does. (gdk_win32_selection_add_targets): New function, for gtkselection's use. Win32 requires that the clipboard owner registers all valid formats even if the owner wants delayed rendering. (_gdk_win32_selection_convert_to_dib): New function. Convert images to DIB using gdk-pixbuf. * gdk/win32/gdkwin32.h: Declare gdk_win32_selection_add_targets(). * gtk/gtkselection.c (gtk_selection_add_target, gtk_selection_add_targets): Call gdk_win32_selection_add_targets() to register target formats. * gdk/gdk.symbols: Add gdk_win32_selection_add_targets(). --- ChangeLog | 43 ++++ ChangeLog.pre-2-10 | 43 ++++ ChangeLog.pre-2-8 | 43 ++++ gdk/gdk.symbols | 6 + gdk/win32/gdkdisplay-win32.c | 4 +- gdk/win32/gdkevents-win32.c | 58 ++++- gdk/win32/gdkglobals-win32.c | 9 +- gdk/win32/gdkmain-win32.c | 71 ++++++- gdk/win32/gdkprivate-win32.h | 21 +- gdk/win32/gdkproperty-win32.c | 374 +++++++++++++++++---------------- gdk/win32/gdkselection-win32.c | 283 +++++++++++++++++++++---- gdk/win32/gdkwin32.h | 5 + gtk/gtkselection.c | 19 ++ 13 files changed, 751 insertions(+), 228 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7286577da0..82c25cf30d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,46 @@ +2005-04-04 Tor Lillqvist + + * gdk/win32/gdkmain.c (_gdk_win32_cf_to_string): New debugging + function, to log a clipboard format name symbolically. + (_gdk_win32_data_to_string): Also new, to log random data bytes. + + Implement delayed rendering on Win32, specifically for transfering + images through the clipboard from GTK+ apps to other + apps (#168173, implementation by Ivan Wong): + + * gdk/win32/gdkevents-win32.c (gdk_event_translate): + Handle WM_RENDERFORMAT. + + * gdk/win32/gdkprivate-win32.h + * gdk/win32/gdkglobals-win32.c: Add _format_atom_table, + _delayed_rendering_data and _image_bmp. + + * gdk/win32/gdkmain-win32.c: Initialize _image_bmp. + + * gdk/win32/gdkproperty-win32.c (gdk_property_change): + Accept formats other than GDK_TARGET_STRING or _utf8_string, and + assume they are handled through delayed rendering. + + * gdk/win32/gdkselection-win32.c (gdk_selection_convert): + Return all available formats (including those registered by GTK+ + apps) on request_targets. + (gdk_selection_property_get): We should append a zero byte like + X11 does. + (gdk_win32_selection_add_targets): New function, for + gtkselection's use. Win32 requires that the clipboard owner + registers all valid formats even if the owner wants delayed + rendering. + (_gdk_win32_selection_convert_to_dib): New function. Convert + images to DIB using gdk-pixbuf. + + * gdk/win32/gdkwin32.h: Declare gdk_win32_selection_add_targets(). + + * gtk/gtkselection.c (gtk_selection_add_target, + gtk_selection_add_targets): Call gdk_win32_selection_add_targets() + to register target formats. + + * gdk/gdk.symbols: Add gdk_win32_selection_add_targets(). + 2005-04-03 Hans Breuer [merged from gtk-2-6 branch] diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 7286577da0..82c25cf30d 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,46 @@ +2005-04-04 Tor Lillqvist + + * gdk/win32/gdkmain.c (_gdk_win32_cf_to_string): New debugging + function, to log a clipboard format name symbolically. + (_gdk_win32_data_to_string): Also new, to log random data bytes. + + Implement delayed rendering on Win32, specifically for transfering + images through the clipboard from GTK+ apps to other + apps (#168173, implementation by Ivan Wong): + + * gdk/win32/gdkevents-win32.c (gdk_event_translate): + Handle WM_RENDERFORMAT. + + * gdk/win32/gdkprivate-win32.h + * gdk/win32/gdkglobals-win32.c: Add _format_atom_table, + _delayed_rendering_data and _image_bmp. + + * gdk/win32/gdkmain-win32.c: Initialize _image_bmp. + + * gdk/win32/gdkproperty-win32.c (gdk_property_change): + Accept formats other than GDK_TARGET_STRING or _utf8_string, and + assume they are handled through delayed rendering. + + * gdk/win32/gdkselection-win32.c (gdk_selection_convert): + Return all available formats (including those registered by GTK+ + apps) on request_targets. + (gdk_selection_property_get): We should append a zero byte like + X11 does. + (gdk_win32_selection_add_targets): New function, for + gtkselection's use. Win32 requires that the clipboard owner + registers all valid formats even if the owner wants delayed + rendering. + (_gdk_win32_selection_convert_to_dib): New function. Convert + images to DIB using gdk-pixbuf. + + * gdk/win32/gdkwin32.h: Declare gdk_win32_selection_add_targets(). + + * gtk/gtkselection.c (gtk_selection_add_target, + gtk_selection_add_targets): Call gdk_win32_selection_add_targets() + to register target formats. + + * gdk/gdk.symbols: Add gdk_win32_selection_add_targets(). + 2005-04-03 Hans Breuer [merged from gtk-2-6 branch] diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 7286577da0..82c25cf30d 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,46 @@ +2005-04-04 Tor Lillqvist + + * gdk/win32/gdkmain.c (_gdk_win32_cf_to_string): New debugging + function, to log a clipboard format name symbolically. + (_gdk_win32_data_to_string): Also new, to log random data bytes. + + Implement delayed rendering on Win32, specifically for transfering + images through the clipboard from GTK+ apps to other + apps (#168173, implementation by Ivan Wong): + + * gdk/win32/gdkevents-win32.c (gdk_event_translate): + Handle WM_RENDERFORMAT. + + * gdk/win32/gdkprivate-win32.h + * gdk/win32/gdkglobals-win32.c: Add _format_atom_table, + _delayed_rendering_data and _image_bmp. + + * gdk/win32/gdkmain-win32.c: Initialize _image_bmp. + + * gdk/win32/gdkproperty-win32.c (gdk_property_change): + Accept formats other than GDK_TARGET_STRING or _utf8_string, and + assume they are handled through delayed rendering. + + * gdk/win32/gdkselection-win32.c (gdk_selection_convert): + Return all available formats (including those registered by GTK+ + apps) on request_targets. + (gdk_selection_property_get): We should append a zero byte like + X11 does. + (gdk_win32_selection_add_targets): New function, for + gtkselection's use. Win32 requires that the clipboard owner + registers all valid formats even if the owner wants delayed + rendering. + (_gdk_win32_selection_convert_to_dib): New function. Convert + images to DIB using gdk-pixbuf. + + * gdk/win32/gdkwin32.h: Declare gdk_win32_selection_add_targets(). + + * gtk/gtkselection.c (gtk_selection_add_target, + gtk_selection_add_targets): Call gdk_win32_selection_add_targets() + to register target formats. + + * gdk/gdk.symbols: Add gdk_win32_selection_add_targets(). + 2005-04-03 Hans Breuer [merged from gtk-2-6 branch] diff --git a/gdk/gdk.symbols b/gdk/gdk.symbols index 07b1412042..34a9f7e027 100644 --- a/gdk/gdk.symbols +++ b/gdk/gdk.symbols @@ -1047,6 +1047,12 @@ gdk_win32_hdc_get gdk_win32_hdc_release #endif #endif + +#if IN_HEADER(__GDK_WIN32_H__) +#if IN_FILE(__GDK_SELECTION_WIN32_C__) +gdk_win32_selection_add_targets +#endif +#endif #endif #ifdef GDK_WINDOWING_X11 diff --git a/gdk/win32/gdkdisplay-win32.c b/gdk/win32/gdkdisplay-win32.c index eca6de6a73..fe08e45841 100644 --- a/gdk/win32/gdkdisplay-win32.c +++ b/gdk/win32/gdkdisplay-win32.c @@ -375,9 +375,7 @@ _win32_on_clipboard_change (HWND hwnd, g_print ("WM_DRAWCLIPBOARD: owner:%p formats: ", hwndOwner); for (; 0 != (nFormat = EnumClipboardFormats (nFormat));) { - char sFormat[80]; - if (GetClipboardFormatName (nFormat, sFormat, G_N_ELEMENTS (sFormat)) > 0) - g_print ("%s ", sFormat); + g_print ("%s ", _gdk_win32_cf_to_string (nFormat)); } g_print ("\n"); CloseClipboard (); diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c index 5bf455c122..222b457c8d 100644 --- a/gdk/win32/gdkevents-win32.c +++ b/gdk/win32/gdkevents-win32.c @@ -2178,6 +2178,7 @@ gdk_event_translate (GdkDisplay *display, static gint update_colors_counter = 0; gint button; + GdkAtom target; gchar buf[256]; gboolean return_val = FALSE; @@ -3362,7 +3363,62 @@ gdk_event_translate (GdkDisplay *display, else return_val = TRUE; break; - + + case WM_RENDERFORMAT: + GDK_NOTE (EVENTS, g_print (" %s", _gdk_win32_cf_to_string (msg->wParam))); + + if (!(target = g_hash_table_lookup (_format_atom_table, GINT_TO_POINTER (msg->wParam)))) + { + GDK_NOTE (EVENTS, g_print (" (target not found)")); + return_val = TRUE; + break; + } + + /* We need to render to clipboard immediately, don't call + * append_event() + */ + if (_gdk_event_func) + { + event = gdk_event_new (GDK_SELECTION_REQUEST); + event->selection.window = window; + event->selection.send_event = FALSE; + event->selection.selection = GDK_SELECTION_CLIPBOARD; + event->selection.target = target; + event->selection.property = _gdk_selection_property; + event->selection.requestor = (guint32) msg->hwnd; + event->selection.time = msg->time; + + fixup_event (event); + GDK_NOTE (EVENTS, g_print (" (calling gdk_event_func)")); + GDK_NOTE (EVENTS, print_event (event)); + (*_gdk_event_func) (event, _gdk_event_data); + gdk_event_free (event); + + /* Now the clipboard owner should have rendered */ + if (!_delayed_rendering_data) + GDK_NOTE (EVENTS, g_print (" (no _delayed_rendering_data?)")); + else + { + if (msg->wParam == CF_DIB) + { + _delayed_rendering_data = + _gdk_win32_selection_convert_to_dib (_delayed_rendering_data, + target); + if (!_delayed_rendering_data) + { + g_warning ("Cannot convert to DIB from delayed rendered image"); + break; + } + } + /* The requestor is holding the clipboard, no + * OpenClipboard() is required/possible + */ + API_CALL (SetClipboardData, (msg->wParam, _delayed_rendering_data)); + _delayed_rendering_data = NULL; + } + } + break; + #ifdef HAVE_WINTAB case WM_ACTIVATE: /* Bring any tablet contexts to the top of the overlap order when diff --git a/gdk/win32/gdkglobals-win32.c b/gdk/win32/gdkglobals-win32.c index 26fb203834..a178ccbf25 100644 --- a/gdk/win32/gdkglobals-win32.c +++ b/gdk/win32/gdkglobals-win32.c @@ -36,7 +36,7 @@ GdkWindow *_gdk_root = NULL; gint _gdk_num_monitors; GdkRectangle *_gdk_monitors = NULL; -gint _gdk_offset_x, _gdk_offset_y; +gint _gdk_offset_x, _gdk_offset_y; HDC _gdk_display_hdc; HINSTANCE _gdk_dll_hinstance; @@ -50,9 +50,11 @@ WORD _cf_rtf; WORD _cf_utf8_string; GdkAtom _utf8_string; -GdkAtom _text_uri_list; GdkAtom _targets; +GdkAtom _text_uri_list; +GdkAtom _image_bmp; + GdkAtom _local_dnd; GdkAtom _gdk_win32_dropfiles; GdkAtom _gdk_ole2_dnd; @@ -66,3 +68,6 @@ gint _gdk_max_colors = 0; gboolean _sizemove_in_progress = FALSE; gboolean _ignore_destroy_clipboard = FALSE; + +HGLOBAL _delayed_rendering_data = NULL; +GHashTable *_format_atom_table = NULL; diff --git a/gdk/win32/gdkmain-win32.c b/gdk/win32/gdkmain-win32.c index d12e56feec..2584a2ada6 100644 --- a/gdk/win32/gdkmain-win32.c +++ b/gdk/win32/gdkmain-win32.c @@ -112,9 +112,11 @@ _gdk_windowing_init (void) _cf_utf8_string = RegisterClipboardFormat ("UTF8_STRING"); _utf8_string = gdk_atom_intern ("UTF8_STRING", FALSE); - _text_uri_list = gdk_atom_intern ("text/uri-list", FALSE); _targets = gdk_atom_intern ("TARGETS", FALSE); + _text_uri_list = gdk_atom_intern ("text/uri-list", FALSE); + _image_bmp = gdk_atom_intern ("image/bmp", FALSE); + _local_dnd = gdk_atom_intern ("LocalDndSelection", FALSE); _gdk_win32_dropfiles = gdk_atom_intern ("DROPFILES_DND", FALSE); _gdk_ole2_dnd = gdk_atom_intern ("OLE2_DND", FALSE); @@ -250,7 +252,7 @@ gdk_notify_startup_complete (void) */ static gchar * static_printf (const gchar *format, - ...) + ...) { static gchar buf[10000]; gchar *msg; @@ -922,6 +924,71 @@ _gdk_win32_key_to_string (LONG lParam) return static_printf ("unk-%#lx", lParam); } +gchar * +_gdk_win32_cf_to_string (UINT format) +{ + char buf[100]; + + switch (format) + { +#define CASE(x) case CF_##x: return #x + CASE (BITMAP); + CASE (DIB); +#ifdef CF_DIBV5 + CASE (DIBV5); +#endif + CASE (DIF); + CASE (DSPBITMAP); + CASE (DSPENHMETAFILE); + CASE (DSPMETAFILEPICT); + CASE (DSPTEXT); + CASE (ENHMETAFILE); + CASE (HDROP); + CASE (LOCALE); + CASE (METAFILEPICT); + CASE (OEMTEXT); + CASE (OWNERDISPLAY); + CASE (PALETTE); + CASE (PENDATA); + CASE (RIFF); + CASE (SYLK); + CASE (TEXT); + CASE (WAVE); + CASE (TIFF); + CASE (UNICODETEXT); + default: + if (format >= CF_GDIOBJFIRST && + format <= CF_GDIOBJLAST) + return static_printf ("CF_GDIOBJ%d", format - CF_GDIOBJFIRST); + if (format >= CF_PRIVATEFIRST && + format <= CF_PRIVATELAST) + return static_printf ("CF_PRIVATE%d", format - CF_PRIVATEFIRST); + if (GetClipboardFormatName (format, buf, sizeof (buf))) + return static_printf ("%s", buf); + else + return static_printf ("unk-%#lx", format); + } +} + +gchar * +_gdk_win32_data_to_string (const guchar *data, + int nbytes) +{ + GString *s = g_string_new (""); + int i; + gchar *retval; + + for (i = 0; i < nbytes; i++) + if (data[i] >=' ' && data[i] <= '~') + g_string_append_printf (s, "%c ", data[i]); + else + g_string_append_printf (s, "%02X ", data[i]); + + retval = static_printf ("%s", s->str); + g_string_free (s, TRUE); + + return retval; +} gchar * _gdk_win32_rect_to_string (const RECT *rect) diff --git a/gdk/win32/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h index 745d1b4caf..0d1ab272de 100644 --- a/gdk/win32/gdkprivate-win32.h +++ b/gdk/win32/gdkprivate-win32.h @@ -401,6 +401,9 @@ gchar *_gdk_win32_psendcap_to_string (DWORD pen_style); gchar *_gdk_win32_psjoin_to_string (DWORD pen_style); gchar *_gdk_win32_message_to_string (UINT msg); gchar *_gdk_win32_key_to_string (LONG lParam); +gchar *_gdk_win32_cf_to_string (UINT format); +gchar *_gdk_win32_data_to_string (const guchar*data, + int nbytes); gchar *_gdk_win32_rect_to_string (const RECT *rect); gchar *_gdk_win32_gdkrectangle_to_string (const GdkRectangle *rect); @@ -473,12 +476,13 @@ extern gboolean _gdk_keyboard_has_altgr; extern WORD _cf_rtf; extern WORD _cf_utf8_string; -/* GdkAtoms: Targets */ +/* GdkAtoms: targets */ extern GdkAtom _utf8_string; -extern GdkAtom _compound_text; -extern GdkAtom _text_uri_list; extern GdkAtom _targets; +extern GdkAtom _text_uri_list; +extern GdkAtom _image_bmp; + /* DND selections */ extern GdkAtom _local_dnd; extern GdkAtom _gdk_win32_dropfiles; @@ -500,6 +504,17 @@ extern gboolean _sizemove_in_progress; /* TRUE when we are emptying the clipboard ourselves */ extern gboolean _ignore_destroy_clipboard; +/* Mapping from registered clipboard format id (native) to + * corresponding GdkAtom + */ +extern GHashTable *_format_atom_table; + +/* Hold the result of a delayed rendering */ +extern HGLOBAL _delayed_rendering_data; + +HGLOBAL _gdk_win32_selection_convert_to_dib (HGLOBAL hdata, + GdkAtom target); + /* Initialization */ void _gdk_windowing_window_init (void); void _gdk_root_window_size_init (void); diff --git a/gdk/win32/gdkproperty-win32.c b/gdk/win32/gdkproperty-win32.c index 5fb34bc5ea..145271216c 100644 --- a/gdk/win32/gdkproperty-win32.c +++ b/gdk/win32/gdkproperty-win32.c @@ -265,7 +265,7 @@ gdk_property_change (GdkWindow *window, GDK_NOTE (DND, (prop_name = gdk_atom_name (property), type_name = gdk_atom_name (type), - g_print ("gdk_property_change: %p %#x (%s) %#x (%s) %s %d*%d bytes %.10s\n", + g_print ("gdk_property_change: %p %#x (%s) %#x (%s) %s %d*%d bytes: %s\n", GDK_WINDOW_HWND (window), (guint) property, prop_name, (guint) type, type_name, @@ -273,201 +273,221 @@ gdk_property_change (GdkWindow *window, (mode == GDK_PROP_MODE_PREPEND ? "PREPEND" : (mode == GDK_PROP_MODE_APPEND ? "APPEND" : "???"))), - format, nelements, data), + format, nelements, + _gdk_win32_data_to_string (data, MIN (10, format*nelements/8))), g_free (prop_name), g_free (type_name))); if (property == _gdk_selection_property - && ((type == GDK_TARGET_STRING && GetACP () == 1252) || - type == _utf8_string) && format == 8 && mode == GDK_PROP_MODE_REPLACE) { - if (!OpenClipboard (GDK_WINDOW_HWND (window))) + if ((type == GDK_TARGET_STRING && GetACP () == 1252) || + type == _utf8_string) { - WIN32_API_FAILED ("OpenClipboard"); - return; - } + if (!OpenClipboard (GDK_WINDOW_HWND (window))) + { + WIN32_API_FAILED ("OpenClipboard"); + return; + } - if (type == _utf8_string) - { - /* Check if only ASCII */ - for (i = 0; i < nelements; i++) - if (data[i] >= 0200) - break; - } - else /* if (type == GDK_TARGET_STRING) */ - { - /* Check that no 0200..0240 chars present, as they - * differ between ISO-8859-1 and CP1252. - */ - for (i = 0; i < nelements; i++) - if (data[i] >= 0200 && data[i] < 0240) - break; - } - nchars = g_utf8_strlen (data, nelements); + if (type == _utf8_string) + { + /* Check if only ASCII */ + for (i = 0; i < nelements; i++) + if (data[i] >= 0200) + break; + } + else /* if (type == GDK_TARGET_STRING) */ + { + /* Check that no 0200..0240 chars present, as they + * differ between ISO-8859-1 and CP1252. + */ + for (i = 0; i < nelements; i++) + if (data[i] >= 0200 && data[i] < 0240) + break; + } + nchars = g_utf8_strlen (data, nelements); - if (i == nelements) - { - /* If UTF-8 and only ASCII, or if STRING (ISO-8859-1) and - * system codepage is CP1252, use CF_TEXT and the data as - * such. - */ - method = SYSTEM_CODEPAGE; - size = nelements; - for (i = 0; i < nelements; i++) - if (data[i] == '\n') + if (i == nelements) + { + /* If UTF-8 and only ASCII, or if STRING (ISO-8859-1) + * and system codepage is CP1252, use CF_TEXT and the + * data as such. + */ + method = SYSTEM_CODEPAGE; + size = nelements; + for (i = 0; i < nelements; i++) + if (data[i] == '\n') + size++; size++; - size++; - GDK_NOTE (DND, g_print ("... as text: %.40s\n", data)); - } - else if (G_WIN32_IS_NT_BASED ()) - { - /* On NT, use CF_UNICODETEXT if any non-system codepage char - * present. - */ - method = UNICODE_TEXT; - - wcptr = g_utf8_to_utf16 (data, nelements, NULL, &wclen, NULL); - - wclen++; /* Terminating 0 */ - size = wclen * 2; - GDK_NOTE (DND, g_print ("... as Unicode\n")); - } - else if (find_common_locale (data, nelements, nchars, &lcid, &buf, &size)) - { - /* On Win9x, if all chars are in the default code page of - * some installed locale, use CF_TEXT and CF_LOCALE. - */ - method = SINGLE_LOCALE; - GDK_NOTE (DND, g_print ("... as text in locale %#lx %d bytes\n", - (gulong) lcid, size)); - } - else - { - /* On Win9x, otherwise use RTF */ - - const guchar *p = data; - - method = RICH_TEXT; - rtf = g_string_new ("{\\rtf1\\uc0 "); - - while (p < data + nelements) - { - if (*p == '{' || - *p == '\\' || - *p == '}') - { - rtf = g_string_append_c (rtf, '\\'); - rtf = g_string_append_c (rtf, *p); - p++; - } - else if (*p < 0200 && *p >= ' ') - { - rtf = g_string_append_c (rtf, *p); - p++; - } - else - { - guchar *q; - gint n; - - rtf = g_string_append (rtf, "\\uNNNNN "); - rtf->len -= 6; /* five digits and a space */ - q = rtf->str + rtf->len; - n = g_sprintf (q, "%d ", g_utf8_get_char (p)); - g_assert (n <= 6); - rtf->len += n; - - p = g_utf8_next_char (p); - } + GDK_NOTE (DND, g_print ("... as text: %.40s\n", data)); } - rtf = g_string_append (rtf, "}"); - size = rtf->len + 1; - GDK_NOTE (DND, g_print ("... as RTF: %.40s\n", rtf->str)); - } - - if (!(hdata = GlobalAlloc (GMEM_MOVEABLE, size))) - { - WIN32_API_FAILED ("GlobalAlloc"); - if (!CloseClipboard ()) - WIN32_API_FAILED ("CloseClipboard"); - if (buf != NULL) - g_free (buf); - if (rtf != NULL) - g_string_free (rtf, TRUE); - return; - } - - ucptr = GlobalLock (hdata); - - switch (method) - { - case SYSTEM_CODEPAGE: - cf = CF_TEXT; - for (i = 0; i < nelements; i++) + else if (G_WIN32_IS_NT_BASED ()) { - if (data[i] == '\n') - *ucptr++ = '\r'; - *ucptr++ = data[i]; + /* On NT, use CF_UNICODETEXT if any non-system codepage + * char present. + */ + method = UNICODE_TEXT; + + wcptr = g_utf8_to_utf16 (data, nelements, NULL, &wclen, NULL); + + wclen++; /* Terminating 0 */ + size = wclen * 2; + GDK_NOTE (DND, g_print ("... as Unicode\n")); } - *ucptr++ = '\0'; - break; - - case UNICODE_TEXT: - cf = CF_UNICODETEXT; - memmove (ucptr, wcptr, size); - g_free (wcptr); - break; - - case SINGLE_LOCALE: - cf = CF_TEXT; - memmove (ucptr, buf, size); - g_free (buf); - - /* Set the CF_LOCALE clipboard data, too */ - if (!(hlcid = GlobalAlloc (GMEM_MOVEABLE, sizeof (LCID)))) - WIN32_API_FAILED ("GlobalAlloc"), ok = FALSE; - if (ok) + else if (find_common_locale (data, nelements, nchars, &lcid, &buf, &size)) { - lcidptr = GlobalLock (hlcid); - *lcidptr = lcid; - GlobalUnlock (hlcid); - if (!SetClipboardData (CF_LOCALE, hlcid)) - WIN32_API_FAILED ("SetClipboardData (CF_LOCALE)"), ok = FALSE; + /* On Win9x, if all chars are in the default code page + * of some installed locale, use CF_TEXT and CF_LOCALE. + */ + method = SINGLE_LOCALE; + GDK_NOTE (DND, g_print ("... as text in locale %#lx %d bytes\n", + (gulong) lcid, size)); } - break; - - case RICH_TEXT: - cf = _cf_rtf; - memmove (ucptr, rtf->str, size); - g_string_free (rtf, TRUE); - - /* Set the UTF8_STRING clipboard data, too, for other - * GTK+ apps to use (won't bother reading RTF). - */ - if (!(hutf8 = GlobalAlloc (GMEM_MOVEABLE, nelements))) - WIN32_API_FAILED ("GlobalAlloc"); else { - guchar *utf8ptr = GlobalLock (hutf8); - memmove (utf8ptr, data, nelements); - GlobalUnlock (hutf8); - if (!SetClipboardData (_cf_utf8_string, hutf8)) - WIN32_API_FAILED ("SetClipboardData (UTF8_STRING)"); + /* On Win9x, otherwise use RTF */ + + const guchar *p = data; + + method = RICH_TEXT; + rtf = g_string_new ("{\\rtf1\\uc0 "); + + while (p < data + nelements) + { + if (*p == '{' || + *p == '\\' || + *p == '}') + { + rtf = g_string_append_c (rtf, '\\'); + rtf = g_string_append_c (rtf, *p); + p++; + } + else if (*p < 0200 && *p >= ' ') + { + rtf = g_string_append_c (rtf, *p); + p++; + } + else + { + guchar *q; + gint n; + + rtf = g_string_append (rtf, "\\uNNNNN "); + rtf->len -= 6; /* five digits and a space */ + q = rtf->str + rtf->len; + n = g_sprintf (q, "%d ", g_utf8_get_char (p)); + g_assert (n <= 6); + rtf->len += n; + + p = g_utf8_next_char (p); + } + } + rtf = g_string_append (rtf, "}"); + size = rtf->len + 1; + GDK_NOTE (DND, g_print ("... as RTF: %.40s\n", rtf->str)); + } + + if (!(hdata = GlobalAlloc (GMEM_MOVEABLE, size))) + { + WIN32_API_FAILED ("GlobalAlloc"); + if (!CloseClipboard ()) + WIN32_API_FAILED ("CloseClipboard"); + if (buf != NULL) + g_free (buf); + if (rtf != NULL) + g_string_free (rtf, TRUE); + return; } - break; - default: - g_assert_not_reached (); - } + ucptr = GlobalLock (hdata); - GlobalUnlock (hdata); - if (ok && !SetClipboardData (cf, hdata)) - WIN32_API_FAILED ("SetClipboardData"), ok = FALSE; + switch (method) + { + case SYSTEM_CODEPAGE: + cf = CF_TEXT; + for (i = 0; i < nelements; i++) + { + if (data[i] == '\n') + *ucptr++ = '\r'; + *ucptr++ = data[i]; + } + *ucptr++ = '\0'; + break; + + case UNICODE_TEXT: + cf = CF_UNICODETEXT; + memmove (ucptr, wcptr, size); + g_free (wcptr); + break; + + case SINGLE_LOCALE: + cf = CF_TEXT; + memmove (ucptr, buf, size); + g_free (buf); + + /* Set the CF_LOCALE clipboard data, too */ + if (!(hlcid = GlobalAlloc (GMEM_MOVEABLE, sizeof (LCID)))) + WIN32_API_FAILED ("GlobalAlloc"), ok = FALSE; + if (ok) + { + lcidptr = GlobalLock (hlcid); + *lcidptr = lcid; + GlobalUnlock (hlcid); + if (!SetClipboardData (CF_LOCALE, hlcid)) + WIN32_API_FAILED ("SetClipboardData (CF_LOCALE)"), ok = FALSE; + } + break; + + case RICH_TEXT: + cf = _cf_rtf; + memmove (ucptr, rtf->str, size); + g_string_free (rtf, TRUE); + + /* Set the UTF8_STRING clipboard data, too, for other + * GTK+ apps to use (won't bother reading RTF). + */ + if (!(hutf8 = GlobalAlloc (GMEM_MOVEABLE, nelements))) + WIN32_API_FAILED ("GlobalAlloc"); + else + { + guchar *utf8ptr = GlobalLock (hutf8); + memmove (utf8ptr, data, nelements); + GlobalUnlock (hutf8); + if (!SetClipboardData (_cf_utf8_string, hutf8)) + WIN32_API_FAILED ("SetClipboardData (UTF8_STRING)"); + } + break; + + default: + g_assert_not_reached (); + } + + GlobalUnlock (hdata); + if (ok && !SetClipboardData (cf, hdata)) + WIN32_API_FAILED ("SetClipboardData"), ok = FALSE; - if (!CloseClipboard ()) - WIN32_API_FAILED ("CloseClipboard"); + if (!CloseClipboard ()) + WIN32_API_FAILED ("CloseClipboard"); + } + else + { + /* Delayed Rendering. We can't assign hdata to the clipboard + * here as type may be "image/png", "image/jpg", etc. In + * this case there's a further conversion afterwards. + */ + _delayed_rendering_data = NULL; + if (!(hdata = GlobalAlloc (GMEM_MOVEABLE, nelements > 0 ? nelements : 1))) + { + WIN32_API_FAILED ("GlobalAlloc"); + return; + } + ucptr = GlobalLock (hdata); + memcpy (ucptr, data, nelements); + GlobalUnlock (hdata); + _delayed_rendering_data = hdata; + } } else g_warning ("gdk_property_change: General case not implemented"); @@ -592,13 +612,15 @@ gdk_screen_get_setting (GdkScreen *screen, ncm.cbSize = sizeof(NONCLIENTMETRICS); if (SystemParametersInfo (SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, FALSE)) { - /* Pango finally uses GetDeviceCaps to scale, we use simple approximation here */ + /* Pango finally uses GetDeviceCaps to scale, we use simple + * approximation here. + */ int nHeight = (0 > ncm.lfMenuFont.lfHeight ? -3*ncm.lfMenuFont.lfHeight/4 : 10); if (OUT_STRING_PRECIS == ncm.lfMenuFont.lfOutPrecision) GDK_NOTE(MISC, g_print("gdk_screen_get_setting(%s) : ignoring bitmap font '%s'\n", name, ncm.lfMenuFont.lfFaceName)); else if (ncm.lfMenuFont.lfFaceName && strlen(ncm.lfMenuFont.lfFaceName) > 0 && - /* avoid issues like those described in bug #135098 */ + /* Avoid issues like those described in bug #135098 */ g_utf8_validate (ncm.lfMenuFont.lfFaceName, -1, NULL)) { char* s = g_strdup_printf ("%s %d", ncm.lfMenuFont.lfFaceName, nHeight); diff --git a/gdk/win32/gdkselection-win32.c b/gdk/win32/gdkselection-win32.c index 31eab06c8b..d5edb2d08b 100644 --- a/gdk/win32/gdkselection-win32.c +++ b/gdk/win32/gdkselection-win32.c @@ -60,6 +60,7 @@ _gdk_win32_selection_init (void) { sel_prop_table = g_hash_table_new (NULL, NULL); sel_owner_table = g_hash_table_new (NULL, NULL); + _format_atom_table = g_hash_table_new (NULL, NULL); } /* The specifications for COMPOUND_TEXT and STRING specify that C0 and @@ -350,52 +351,55 @@ gdk_selection_convert (GdkWindow *requestor, if (selection == GDK_SELECTION_CLIPBOARD && target == _targets) { - /* He wants to know what formats are on the clipboard. If there + gint formats_cnt, i, fmt; + GdkAtom *data; + gboolean has_bmp = FALSE; + + /* He wants to know what formats are on the clipboard. If there * is some kind of text, tell him so. */ if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor)))) return; + formats_cnt = CountClipboardFormats (); + data = g_new (GdkAtom, formats_cnt + 2); + i = 0; + if (IsClipboardFormatAvailable (CF_UNICODETEXT) || IsClipboardFormatAvailable (_cf_utf8_string) || IsClipboardFormatAvailable (CF_TEXT)) { - GdkAtom *data = g_new (GdkAtom, 1); - *data = _utf8_string; - _gdk_selection_property_store (requestor, GDK_SELECTION_TYPE_ATOM, - 32, (guchar *) data, 1 * sizeof (GdkAtom)); + data[i++] = _utf8_string; } - else if (IsClipboardFormatAvailable (CF_BITMAP) || - IsClipboardFormatAvailable (CF_DIB)) - { - GdkAtom *data = g_new (GdkAtom, 1); - GdkAtom atom = gdk_atom_intern ("image/bmp", FALSE); - *data = atom; - _gdk_selection_property_store (requestor, GDK_SELECTION_TYPE_ATOM, - 32, (guchar *) data, 1 * sizeof (GdkAtom)); - } - else if (CountClipboardFormats() > 0) + if (formats_cnt > 0) { - /* if there is anything else in the clipboard, enum it all although we don't - * offer special conversion services + /* If there is anything else in the clipboard, enum it all + * although we don't offer special conversion services. */ - int fmt = 0, i = 0; - GdkAtom *data = g_new (GdkAtom, CountClipboardFormats()); - - for ( ; 0 != (fmt = EnumClipboardFormats (fmt)); ) + for (fmt = 0; 0 != (fmt = EnumClipboardFormats (fmt)); ) { - char sFormat[80]; + gchar sFormat[80]; - if (GetClipboardFormatName (fmt, sFormat, 80) > 0) + if (GetClipboardFormatName (fmt, sFormat, 80) > 0 && + strcmp (sFormat, "UTF8_STRING")) { + if (!has_bmp && + (!strcmp (sFormat, "image/bmp") || + !strcmp (sFormat, "image/x-bmp") || + !strcmp (sFormat, "image/x-MS-bmp"))) + has_bmp = TRUE; GdkAtom atom = gdk_atom_intern (sFormat, FALSE); - data[i] = atom; - i++; + data[i++] = atom; } } + } + if (!has_bmp && (IsClipboardFormatAvailable (CF_BITMAP) || + IsClipboardFormatAvailable (CF_DIB))) + data[i++] = _image_bmp; + + if (i > 0) _gdk_selection_property_store (requestor, GDK_SELECTION_TYPE_ATOM, 32, (guchar *) data, i * sizeof (GdkAtom)); - } else property = GDK_NONE; @@ -542,21 +546,42 @@ gdk_selection_convert (GdkWindow *requestor, API_CALL (CloseClipboard, ()); } else if (selection == GDK_SELECTION_CLIPBOARD && - target == gdk_atom_intern ("image/bmp", TRUE)) + target == _image_bmp) { + guchar *data; + if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor)))) return; - if ((hdata = GetClipboardData (CF_DIB)) != NULL) + if ((hdata = GetClipboardData (RegisterClipboardFormat ("image/bmp"))) != NULL) + { + /* "image/bmp" is the first choice. */ + guchar *ptr; + + if ((ptr = GlobalLock (hdata)) != NULL) + { + gint length = GlobalSize (hdata); + + GDK_NOTE (DND, g_print ("...BITMAP (from \"image/bmp\": %d bytes\n", + length)); + + _gdk_selection_property_store (requestor, target, 8, + g_memdup (ptr, length), length); + GlobalUnlock (hdata); + } + } + else if ((hdata = GetClipboardData (CF_DIB)) != NULL) { + /* If there's CF_DIB but not "image/bmp", the clipboard + * owner is probably a native Win32 application. + */ BITMAPINFOHEADER *ptr; - guchar *data; if ((ptr = GlobalLock (hdata)) != NULL) { - BITMAPFILEHEADER *hdr; /* need to add a file header so gdk-pixbuf can load it */ - gint length = GlobalSize (hdata) + sizeof(BITMAPFILEHEADER); + BITMAPFILEHEADER *hdr; /* Need to add a file header so gdk-pixbuf can load it */ + gint length = GlobalSize (hdata) + sizeof (BITMAPFILEHEADER); - GDK_NOTE (DND, g_print ("... BITMAP: %d bytes\n", length)); + GDK_NOTE (DND, g_print ("... BITMAP (from CF_DIB): %d bytes\n", length)); data = g_try_malloc (length); if (data) @@ -564,16 +589,16 @@ gdk_selection_convert (GdkWindow *requestor, hdr = (BITMAPFILEHEADER *)data; hdr->bfType = 0x4d42; /* 0x42 = "B" 0x4d = "M" */ /* Compute the size of the entire file. */ - hdr->bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + hdr->bfSize = (DWORD) (sizeof (BITMAPFILEHEADER) + ptr->biSize + ptr->biClrUsed - * sizeof(RGBQUAD) + ptr->biSizeImage); + * sizeof (RGBQUAD) + ptr->biSizeImage); hdr->bfReserved1 = 0; hdr->bfReserved2 = 0; /* Compute the offset to the array of color indices. */ - hdr->bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + hdr->bfOffBits = (DWORD) sizeof (BITMAPFILEHEADER) + ptr->biSize + ptr->biClrUsed * sizeof (RGBQUAD); - /* copy the data behind it */ - memcpy (data + sizeof(BITMAPFILEHEADER), ptr, length - sizeof(BITMAPFILEHEADER)); + /* Copy the data behind it */ + memcpy (data + sizeof (BITMAPFILEHEADER), ptr, length - sizeof (BITMAPFILEHEADER)); _gdk_selection_property_store (requestor, target, 8, data, length); } @@ -591,7 +616,12 @@ gdk_selection_convert (GdkWindow *requestor, if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor)))) return; - /* check if its available */ + /* Check if it's available. In fact, we can simply call + * GetClipboardData (RegisterClipboardFormat (targetname)), but + * the global custom format ID space is limited, + * (0xC000~0xFFFF), and we better not waste an format ID if we + * are just a requestor. + */ for ( ; 0 != (fmt = EnumClipboardFormats (fmt)); ) { char sFormat[80]; @@ -601,7 +631,7 @@ gdk_selection_convert (GdkWindow *requestor, { if ((hdata = GetClipboardData (fmt)) != NULL) { - /* simply get it without conversion */ + /* Simply get it without conversion */ guchar *ptr; gint length; @@ -673,7 +703,8 @@ gdk_selection_property_get (GdkWindow *requestor, return 0; } - *data = g_malloc (prop->length); + *data = g_malloc (prop->length + 1); + (*data)[prop->length] = '\0'; if (prop->length > 0) memmove (*data, prop->data, prop->length); @@ -713,7 +744,6 @@ gdk_selection_send_notify_for_display (GdkDisplay *display, GdkAtom property, guint32 time) { - GdkEvent tmp_event; gchar *sel_name, *tgt_name, *prop_name; g_return_if_fail (display == _gdk_display); @@ -968,3 +998,174 @@ gdk_free_compound_text (guchar *ctext) */ g_return_if_fail (ctext == NULL); } + +void +gdk_win32_selection_add_targets (GdkWindow *owner, + GdkAtom selection, + gint n_targets, + GdkAtom *targets) +{ + HWND hwnd; + const gchar *target_name; + guint formatid; + gint i; + GSList *convertable_formats, *format; + gboolean has_set_dib = FALSE, has_real_dib = FALSE; + + if (selection != GDK_SELECTION_CLIPBOARD) + return; + + if (owner != NULL) + { + if (GDK_WINDOW_DESTROYED (owner)) + return; + hwnd = GDK_WINDOW_HWND (owner); + } + + if (!API_CALL (OpenClipboard, (hwnd))) + return; + + convertable_formats = gdk_pixbuf_get_formats (); + for (i = 0; i < n_targets; ++i) + { + if (targets[i] == _utf8_string) + continue; + + target_name = gdk_atom_name (targets[i]); + if (!(formatid = RegisterClipboardFormat (target_name))) { + WIN32_API_FAILED ("RegisterClipboardFormat"); + API_CALL (CloseClipboard, ()); + return; + } + g_hash_table_replace (_format_atom_table, GINT_TO_POINTER (formatid), targets[i]); + SetClipboardData (formatid, NULL); + + /* We should replace the previous image format associated with + * CF_DIB with "image/bmp" if we find "image/bmp", "image/x-bmp" + * or "image/x-MS-bmp" is available. + */ + if (!has_real_dib && + (!strcmp (target_name, "image/bmp") || + !strcmp (target_name, "image/x-bmp") || + !strcmp (target_name, "image/x-MS-bmp"))) + { + g_hash_table_replace (_format_atom_table, + GINT_TO_POINTER (CF_DIB), + targets[i]); + if (!has_set_dib) { + SetClipboardData (CF_DIB, NULL); + has_set_dib = TRUE; + } + has_real_dib = TRUE; + continue; + } + + for (format = convertable_formats; !has_set_dib && format; format = format->next) + { + gchar **mime_types = + gdk_pixbuf_format_get_mime_types ((GdkPixbufFormat *) format->data); + + for (; *mime_types; ++mime_types) + { + if (!strcmp (target_name, *mime_types)) + { + g_hash_table_replace (_format_atom_table, + GINT_TO_POINTER (CF_DIB), + targets[i]); + SetClipboardData (CF_DIB, NULL); + has_set_dib = TRUE; + break; + } + } + } + } + g_slist_free (convertable_formats); + + API_CALL (CloseClipboard, ()); +} + +/* Convert from types such as "image/jpg" or "image/png" to DIB using + * gdk-pixbuf so that image copied from GTK+ apps can be pasted in + * native apps like mspaint.exe + */ +HGLOBAL +_gdk_win32_selection_convert_to_dib (HGLOBAL hdata, + GdkAtom target) +{ + GdkPixbufLoader *loader; + GdkPixbuf *pixbuf; + const gchar *target_name; + guchar *ptr; + gchar *bmp_buf; + gsize size; + gboolean ok; + + if (!(target_name = gdk_atom_name (target))) + { + GlobalFree (hdata); + return NULL; + } + + if (!strcmp (target_name, "image/bmp") || + !strcmp (target_name, "image/x-bmp") || + !strcmp (target_name, "image/x-MS-bmp")) + { + /* No conversion is needed, just strip the BITMAPFILEHEADER */ + HGLOBAL hdatanew; + + size = GlobalSize (hdata) - 1 - sizeof (BITMAPFILEHEADER); + ptr = GlobalLock (hdata); + memmove (ptr, ptr + sizeof (BITMAPFILEHEADER), size); + GlobalUnlock (hdata); + if (!(hdatanew = GlobalReAlloc (hdata, size, 0))) { + WIN32_API_FAILED ("GlobalReAlloc"); + GlobalFree (hdata); /* the old hdata is not freed if error */ + } + return hdatanew; + } + + /* We actually provide image formats -other than- "image/bmp" etc + * and the requestor is either a native Win32 application or a GTK+ + * client that requested "image/bmp". + */ + if (!(loader = gdk_pixbuf_loader_new_with_mime_type (target_name, NULL))) + { + GlobalFree (hdata); + return NULL; + } + + ptr = GlobalLock (hdata); + ok = gdk_pixbuf_loader_write (loader, ptr, GlobalSize (hdata) - 1, NULL) && + gdk_pixbuf_loader_close (loader, NULL); + + GlobalUnlock (hdata); + GlobalFree (hdata); + hdata = NULL; + + if (ok && (pixbuf = gdk_pixbuf_loader_get_pixbuf (loader)) != NULL) + g_object_ref (pixbuf); + + g_object_unref (loader); + + if (ok && gdk_pixbuf_save_to_buffer (pixbuf, &bmp_buf, &size, "bmp", NULL, NULL)) + { + size -= sizeof (BITMAPFILEHEADER); + if (!(hdata = GlobalAlloc (GMEM_MOVEABLE, size))) + { + WIN32_API_FAILED ("GlobalAlloc"); + ok = FALSE; + } + + if (ok) + { + ptr = GlobalLock (hdata); + memcpy (ptr, bmp_buf + sizeof (BITMAPFILEHEADER), size); + GlobalUnlock (hdata); + } + + g_free (bmp_buf); + g_object_unref (pixbuf); + } + + return hdata; +} diff --git a/gdk/win32/gdkwin32.h b/gdk/win32/gdkwin32.h index bae4708adb..14c91baa46 100644 --- a/gdk/win32/gdkwin32.h +++ b/gdk/win32/gdkwin32.h @@ -79,6 +79,11 @@ void gdk_win32_hdc_release (GdkDrawable *drawable, GdkGC *gc, GdkGCValuesMask usage); +void gdk_win32_selection_add_targets (GdkWindow *owner, + GdkAtom selection, + gint n_targets, + GdkAtom *targets); + G_END_DECLS #endif /* __GDK_WIN32_H__ */ diff --git a/gtk/gtkselection.c b/gtk/gtkselection.c index e119df308a..328c5d3901 100644 --- a/gtk/gtkselection.c +++ b/gtk/gtkselection.c @@ -64,6 +64,10 @@ #include "x11/gdkx.h" #endif +#ifdef GDK_WINDOWING_WIN32 +#include "win32/gdkwin32.h" +#endif + #include "gtkalias.h" #undef DEBUG_SELECTION @@ -750,6 +754,9 @@ gtk_selection_add_target (GtkWidget *widget, list = gtk_selection_target_list_get (widget, selection); gtk_target_list_add (list, target, 0, info); +#ifdef GDK_WINDOWING_WIN32 + gdk_win32_selection_add_targets (widget->window, selection, 1, &target); +#endif } /** @@ -776,6 +783,18 @@ gtk_selection_add_targets (GtkWidget *widget, list = gtk_selection_target_list_get (widget, selection); gtk_target_list_add_table (list, targets, ntargets); + +#ifdef GDK_WINDOWING_WIN32 + { + int i; + GdkAtom *atoms = g_new (GdkAtom, ntargets); + + for (i = 0; i < ntargets; ++i) + atoms[i] = gdk_atom_intern (targets[i].target, FALSE); + gdk_win32_selection_add_targets (widget->window, selection, ntargets, atoms); + g_free (atoms); + } +#endif }