diff --git a/ChangeLog b/ChangeLog index 2979cca025..11418e6667 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,100 @@ +2009-01-29 Tor Lillqvist + + Bug 559408 - Transparency lost when images are copied between GTK+ + and the clipboard on Windows + + The code in gdkselection-win32.c is simplified quite a lot. When + an image is stored on the clipboard from GTK+, only the "PNG" and + CF_DIB formats are advertised. PNG is our preferred format because + it can losslessly represent any image that gdk-pixbuf can handle, + even with alpha, unambiguously. CF_DIB is also advertised because + of its ubiquitous support in Windows software. + + Delayed rendering is used as before, so for other data than text + nothing is actually stored on the clipboard until some app asks + for it. + + It's pointless to advertise images formats like "image/png" or + "image/x-MS-bmp" that no other Windows application is going to + look for anyway. Likewise it's pointless to store text under + formats like "text/plain" or "UTF8_STRING". Just store + CF_UNICODETEXT. CF_TEXT will be available as a synthesized format + anyways. + + Office 2007 offers and asks for images in PNG format using the + registered clipboard format name "PNG", so we do likewise. If the + "PNG" format is available that is preferred when copying to GTK+ + from the clipboard. + + Unfortunately Word 2007 still uses CF_DIB without handling + alpha. But PowerPoint 2007 uses PNG if available and handles alpha + correctly. Both Word and Powerpoint offer PNG with alpha nicely. + + Firefox and IE7 offer images that contain alpha as 32-bit version + 3 BI_RGB DIBs with the undocumented "extra" bytes actually being + alpha. Also, alpha is premultiplied into the RGB bytes, presumably + because that is how AlphaBlend() wants such DIBs. That is also + taken care of. At least for Firefox it easy to be sure that a + CF_DIB on the clipboard is from Firefox. + + Also some general stylistic cleanup, comment improvements, and + improvements of debugging printout especially in the clipboard + handling. Those are not detailled below. + + * gdk/win32/gdkprivate-win32.h + * gdk/win32/gdkglobals-win32.c + * gdk/win32/gdkgmain-win32.c: Move some globals that were used + only in gdkselection-win32.c to be local to that file. + + * gdk/win32/gdkproperty-win32.c (gdk_property_change): Don't + bother checking if text to be placed on the clipboard consists of + only ASCII. + + * gdk/win32/gdkselection-win32.c: Add static variables for a list + of GdkPixbuf-supported formats, well-known registered clipboard + formats for images, and for GdkAtoms for well-known image and text + formats. + + (_gdk_win32_selection_init): Initialize above static variables. + + (selection_property_store) (gdk_selection_property_get) + (_gdk_selection_property_delete): Don't use a FIFO of GdkSelProps + for a window after all, it breaks testtext. See bug #163844. + + (gdk_selection_convert): When converting to the TARGETS format, + i.e. when the caller wants to know what clipboard formats are + available, if PNG is available we report just that and skip + CF_DIB, JPEG and GIF even if advertised. + + If CF_UNICODETEXT is available, report only UTF8_STRING. + + When converting to the UTF8_STRING format, i.e. when the caller + wants text from the clipboard, try just CF_UNICODETEXT. There is + no point in trying CF_TEXT as Windows will synthesize + CF_UNICODETEXT from CF_TEXT anyway, if some app has stored just + CF_TEXT. + + When converting to the image/bmp format, i.e. when the caller + wants an CF_DIB image from the clipboard, we check if the DIB is a + version 3 32-bit BI_RGB one that is likely to actually contain + alpha in the "extra" bytes. Such a DIB is likely to have + premultiplied alpha even, at least in the case of Firefox 3 and + IE7. We then edit the DIB in-place into a version 5 one in + BI_BITFIELDS format and undo the alpha premultiplication. + + In any case, prepend a BMP file header before letting go of the + data which will be fed to the gdk-pixbuf bmp loader by upper + levels. + + (gdk_win32_selection_add_targets): If some kind of pixmap image + format is being added, actually advertise just PNG and + CF_DIB. Note that alpha won't be stored on the clipboard through + CF_DIB. This is because gdk-pixbuf's bmp loader doesn't save + alpha. Furthermore, few if any non-GTK+ Windows apps seem to + understand a version 5 DIB with proper alpha anyway. + + (_gdk_win32_selection_convert_to_dib): Simplified muchly. + 2009-01-29 Tor Lillqvist Bug 145058 - Inputting "^^" requires four keystrokes on Win32, diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c index 07e68b4597..599ff8da2e 100644 --- a/gdk/win32/gdkevents-win32.c +++ b/gdk/win32/gdkevents-win32.c @@ -694,12 +694,12 @@ gdk_keyboard_grab (GdkWindow *window, g_return_val_if_fail (window != NULL, 0); g_return_val_if_fail (GDK_IS_WINDOW (window), 0); - GDK_NOTE (EVENTS, g_print ("gdk_keyboard_grab %p\n", - GDK_WINDOW_HWND (window))); + GDK_NOTE (EVENTS, g_print ("gdk_keyboard_grab %p%s\n", + GDK_WINDOW_HWND (window), owner_events ? " OWNER_EVENTS" : "")); if (!GDK_WINDOW_DESTROYED (window)) { - k_grab_owner_events = owner_events != 0; + k_grab_owner_events = owner_events; return_val = GDK_GRAB_SUCCESS; } else @@ -746,7 +746,7 @@ gdk_display_keyboard_ungrab (GdkDisplay *display, g_return_if_fail (display == _gdk_display); - GDK_NOTE (EVENTS, g_print ("gdk_keyboard_ungrab\n")); + GDK_NOTE (EVENTS, g_print ("gdk_display_keyboard_ungrab\n")); if (k_grab_window && !k_grab_owner_events) { @@ -955,6 +955,7 @@ static void print_event (GdkEvent *event) { gchar *escaped, *kvname; + gchar *selection_name, *target_name, *property_name; g_print ("%s%*s===> ", (debug_indent > 0 ? "\n" : ""), debug_indent, ""); switch (event->any.type) @@ -1065,6 +1066,18 @@ print_event (GdkEvent *event) case GDK_FOCUS_CHANGE: g_print ("%s", (event->focus_change.in ? "IN" : "OUT")); break; + case GDK_SELECTION_REQUEST: + case GDK_SELECTION_NOTIFY: + case GDK_SELECTION_CLEAR: + selection_name = gdk_atom_name (event->selection.selection); + target_name = gdk_atom_name (event->selection.target); + property_name = gdk_atom_name (event->selection.property); + g_print ("sel:%s tgt:%s prop:%s", + selection_name, target_name, property_name); + g_free (selection_name); + g_free (target_name); + g_free (property_name); + break; case GDK_CONFIGURE: g_print ("x:%d y:%d w:%d h:%d", event->configure.x, event->configure.y, @@ -1682,7 +1695,8 @@ translate_mouse_coords (GdkWindow *window1, /* The check_extended flag controls whether to check if the windows want * events from extended input devices and if the message should be skipped - * because an extended input device is active */ + * because an extended input device is active + */ static gboolean propagate (GdkWindow **window, MSG *msg, @@ -1693,14 +1707,13 @@ propagate (GdkWindow **window, MSG *msg), gboolean check_extended) { - gboolean in_propagation = FALSE; - if (grab_window != NULL && !grab_owner_events) { /* Event source is grabbed with owner_events FALSE */ - /* See if the event should be ignored because an extended input device - * is used */ + /* See if the event should be ignored because an extended input + * device is used + */ if (check_extended && ((GdkWindowObject *) grab_window)->extension_events != 0 && _gdk_input_ignore_core) @@ -1720,10 +1733,12 @@ propagate (GdkWindow **window, return TRUE; } } + + /* If we come here, we know that if grab_window != NULL then + * grab_owner_events is TRUE + */ while (TRUE) { - /* See if the event should be ignored because an extended input device - * is used */ if (check_extended && ((GdkWindowObject *) *window)->extension_events != 0 && _gdk_input_ignore_core) @@ -1742,8 +1757,6 @@ propagate (GdkWindow **window, { /* Event source is grabbed with owner_events TRUE */ - /* See if the event should be ignored because an extended - * input device is used */ if (check_extended && ((GdkWindowObject *) grab_window)->extension_events != 0 && _gdk_input_ignore_core) @@ -1774,7 +1787,6 @@ propagate (GdkWindow **window, else { assign_object (window, parent); - in_propagation = TRUE; /* The only branch where we actually continue the loop */ } } @@ -2867,7 +2879,7 @@ gdk_event_translate (MSG *msg, break; case WM_MOUSEWHEEL: - GDK_NOTE (EVENTS, g_print (" %d", HIWORD (msg->wParam))); + GDK_NOTE (EVENTS, g_print (" %d", (short) HIWORD (msg->wParam))); /* WM_MOUSEWHEEL is delivered to the focus window. Work around * that. Also, the position is in screen coordinates, not client @@ -2917,6 +2929,42 @@ gdk_event_translate (MSG *msg, return_val = TRUE; break; + case WM_HSCROLL: + GDK_NOTE (EVENTS, + (g_print (" %s", + (LOWORD (msg->wParam) == SB_ENDSCROLL ? "ENDSCROLL" : + (LOWORD (msg->wParam) == SB_LEFT ? "LEFT" : + (LOWORD (msg->wParam) == SB_RIGHT ? "RIGHT" : + (LOWORD (msg->wParam) == SB_LINELEFT ? "LINELEFT" : + (LOWORD (msg->wParam) == SB_LINERIGHT ? "LINERIGHT" : + (LOWORD (msg->wParam) == SB_PAGELEFT ? "PAGELEFT" : + (LOWORD (msg->wParam) == SB_PAGERIGHT ? "PAGERIGHT" : + (LOWORD (msg->wParam) == SB_THUMBPOSITION ? "THUMBPOSITION" : + (LOWORD (msg->wParam) == SB_THUMBTRACK ? "THUMBTRACK" : + "???")))))))))), + (LOWORD (msg->wParam) == SB_THUMBPOSITION || + LOWORD (msg->wParam) == SB_THUMBTRACK) ? + g_print (" %d", HIWORD (msg->wParam)) : 0)); + break; + + case WM_VSCROLL: + GDK_NOTE (EVENTS, + (g_print (" %s", + (LOWORD (msg->wParam) == SB_ENDSCROLL ? "ENDSCROLL" : + (LOWORD (msg->wParam) == SB_BOTTOM ? "BOTTOM" : + (LOWORD (msg->wParam) == SB_TOP ? "TOP" : + (LOWORD (msg->wParam) == SB_LINEDOWN ? "LINDOWN" : + (LOWORD (msg->wParam) == SB_LINEUP ? "LINEIP" : + (LOWORD (msg->wParam) == SB_PAGEDOWN ? "PAGEDOWN" : + (LOWORD (msg->wParam) == SB_PAGEUP ? "PAGEUP" : + (LOWORD (msg->wParam) == SB_THUMBPOSITION ? "THUMBPOSITION" : + (LOWORD (msg->wParam) == SB_THUMBTRACK ? "THUMBTRACK" : + "???")))))))))), + (LOWORD (msg->wParam) == SB_THUMBPOSITION || + LOWORD (msg->wParam) == SB_THUMBTRACK) ? + g_print (" %d", HIWORD (msg->wParam)) : 0)); + break; + case WM_QUERYNEWPALETTE: if (gdk_visual_get_system ()->type == GDK_VISUAL_PSEUDO_COLOR) { @@ -3608,7 +3656,7 @@ gdk_event_translate (MSG *msg, event->selection.send_event = FALSE; event->selection.selection = GDK_SELECTION_CLIPBOARD; event->selection.target = target; - event->selection.property = _gdk_selection_property; + event->selection.property = _gdk_selection; event->selection.requestor = msg->hwnd; event->selection.time = msg->time; @@ -3640,6 +3688,11 @@ gdk_event_translate (MSG *msg, /* The requestor is holding the clipboard, no * OpenClipboard() is required/possible */ + GDK_NOTE (DND, + g_print (" SetClipboardData(%s,%p)", + _gdk_win32_cf_to_string (msg->wParam), + _delayed_rendering_data)); + API_CALL (SetClipboardData, (msg->wParam, _delayed_rendering_data)); _delayed_rendering_data = NULL; } @@ -3679,7 +3732,6 @@ gdk_event_translate (MSG *msg, GDK_NOTE (EVENTS, g_print (" %s thread: %I64d", msg->wParam ? "YES" : "NO", (gint64) msg->lParam)); - if (msg->wParam && GDK_WINDOW_IS_MAPPED (window)) ensure_stacking_on_activate_app (msg, window); break; diff --git a/gdk/win32/gdkglobals-win32.c b/gdk/win32/gdkglobals-win32.c index 3f0f749415..d30ad32e5b 100644 --- a/gdk/win32/gdkglobals-win32.c +++ b/gdk/win32/gdkglobals-win32.c @@ -46,10 +46,7 @@ HKL _gdk_input_locale; gboolean _gdk_input_locale_is_ime; UINT _gdk_input_codepage; -WORD _cf_utf8_string; -WORD _cf_image_bmp; - -GdkAtom _gdk_selection_property; +GdkAtom _gdk_selection; GdkAtom _wm_transient_for; GdkAtom _targets; GdkAtom _save_targets; @@ -57,7 +54,6 @@ GdkAtom _utf8_string; GdkAtom _text; GdkAtom _compound_text; GdkAtom _text_uri_list; -GdkAtom _image_bmp; GdkAtom _local_dnd; GdkAtom _gdk_win32_dropfiles; diff --git a/gdk/win32/gdkmain-win32.c b/gdk/win32/gdkmain-win32.c index 498a709261..5be01631ac 100644 --- a/gdk/win32/gdkmain-win32.c +++ b/gdk/win32/gdkmain-win32.c @@ -100,10 +100,7 @@ _gdk_windowing_init (void) CoInitialize (NULL); - _cf_utf8_string = RegisterClipboardFormat ("UTF8_STRING"); - _cf_image_bmp = RegisterClipboardFormat ("image/bmp"); - - _gdk_selection_property = gdk_atom_intern ("GDK_SELECTION", FALSE); + _gdk_selection = gdk_atom_intern ("GDK_SELECTION", FALSE); _wm_transient_for = gdk_atom_intern ("WM_TRANSIENT_FOR", FALSE); _targets = gdk_atom_intern ("TARGETS", FALSE); _save_targets = gdk_atom_intern ("SAVE_TARGETS", FALSE); @@ -111,7 +108,6 @@ _gdk_windowing_init (void) _text = gdk_atom_intern ("TEXT", FALSE); _compound_text = gdk_atom_intern ("COMPOUND_TEXT", 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); @@ -1039,9 +1035,7 @@ _gdk_win32_cf_to_string (UINT format) #define CASE(x) case CF_##x: return "CF_" #x CASE (BITMAP); CASE (DIB); -#ifdef CF_DIBV5 CASE (DIBV5); -#endif CASE (DIF); CASE (DSPBITMAP); CASE (DSPENHMETAFILE); diff --git a/gdk/win32/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h index 0d3fe99b98..bc54e00dd3 100644 --- a/gdk/win32/gdkprivate-win32.h +++ b/gdk/win32/gdkprivate-win32.h @@ -93,6 +93,11 @@ #define WM_APPCOMMAND 0x319 #endif +#ifndef CF_DIBV5 +#define CF_DIBV5 17 +#endif + + /* Define some combinations of GdkDebugFlags */ #define GDK_DEBUG_EVENTS_OR_COLORMAP (GDK_DEBUG_EVENTS|GDK_DEBUG_COLORMAP) #define GDK_DEBUG_EVENTS_OR_INPUT (GDK_DEBUG_EVENTS|GDK_DEBUG_INPUT) @@ -400,12 +405,8 @@ extern guint _gdk_keymap_serial; extern gboolean _gdk_keyboard_has_altgr; extern guint _scancode_rshift; -/* Registered clipboard formats */ -extern WORD _cf_utf8_string; -extern WORD _cf_image_bmp; - /* GdkAtoms: properties, targets and types */ -extern GdkAtom _gdk_selection_property; +extern GdkAtom _gdk_selection; extern GdkAtom _wm_transient_for; extern GdkAtom _targets; extern GdkAtom _save_targets; @@ -413,7 +414,6 @@ extern GdkAtom _utf8_string; extern GdkAtom _text; extern GdkAtom _compound_text; extern GdkAtom _text_uri_list; -extern GdkAtom _image_bmp; /* DND selections */ extern GdkAtom _local_dnd; diff --git a/gdk/win32/gdkproperty-win32.c b/gdk/win32/gdkproperty-win32.c index 0215c47e3d..927fd6476b 100644 --- a/gdk/win32/gdkproperty-win32.c +++ b/gdk/win32/gdkproperty-win32.c @@ -99,7 +99,8 @@ gdk_atom_name (GdkAtom atom) ATOM win32_atom; gchar name[256]; - if (GDK_SELECTION_PRIMARY == atom) return g_strdup ("PRIMARY"); + if (GDK_NONE == atom) return g_strdup (""); + else if (GDK_SELECTION_PRIMARY == atom) return g_strdup ("PRIMARY"); else if (GDK_SELECTION_SECONDARY == atom) return g_strdup ("SECONDARY"); else if (GDK_SELECTION_CLIPBOARD == atom) return g_strdup ("CLIPBOARD"); else if (GDK_SELECTION_TYPE_ATOM == atom) return g_strdup ("ATOM"); @@ -153,14 +154,10 @@ gdk_property_change (GdkWindow *window, gint nelements) { HGLOBAL hdata; - UINT cf = 0; - gint i, size, nchars; - gchar *prop_name, *type_name; + gint i, size; guchar *ucptr, *buf = NULL; - wchar_t *wcptr; + wchar_t *wcptr, *p; glong wclen; - enum { SYSTEM_CODEPAGE, UNICODE_TEXT } method; - gboolean ok = TRUE; g_return_if_fail (window != NULL); g_return_if_fail (GDK_IS_WINDOW (window)); @@ -168,21 +165,23 @@ gdk_property_change (GdkWindow *window, if (GDK_WINDOW_DESTROYED (window)) return; - GDK_NOTE (DND, - (prop_name = gdk_atom_name (property), - type_name = gdk_atom_name (type), - g_print ("gdk_property_change: %p %p (%s) %p (%s) %s %d*%d bytes: %s\n", - GDK_WINDOW_HWND (window), - property, prop_name, - type, type_name, - (mode == GDK_PROP_MODE_REPLACE ? "REPLACE" : - (mode == GDK_PROP_MODE_PREPEND ? "PREPEND" : - (mode == GDK_PROP_MODE_APPEND ? "APPEND" : - "???"))), - format, nelements, - _gdk_win32_data_to_string (data, MIN (10, format*nelements/8))), - g_free (prop_name), - g_free (type_name))); + GDK_NOTE (DND, { + gchar *prop_name = gdk_atom_name (property); + gchar *type_name = gdk_atom_name (type); + + g_print ("gdk_property_change: %p %s %s %s %d*%d bits: %s\n", + GDK_WINDOW_HWND (window), + prop_name, + type_name, + (mode == GDK_PROP_MODE_REPLACE ? "REPLACE" : + (mode == GDK_PROP_MODE_PREPEND ? "PREPEND" : + (mode == GDK_PROP_MODE_APPEND ? "APPEND" : + "???"))), + format, nelements, + _gdk_win32_data_to_string (data, MIN (10, format*nelements/8))); + g_free (prop_name); + g_free (type_name); + }); /* We should never come here for these types */ g_return_if_fail (type != GDK_TARGET_STRING); @@ -190,9 +189,9 @@ gdk_property_change (GdkWindow *window, g_return_if_fail (type != _compound_text); g_return_if_fail (type != _save_targets); - if (property == _gdk_selection_property - && format == 8 - && mode == GDK_PROP_MODE_REPLACE) + if (property == _gdk_selection && + format == 8 && + mode == GDK_PROP_MODE_REPLACE) { if (type == _utf8_string) { @@ -202,38 +201,13 @@ gdk_property_change (GdkWindow *window, return; } - nchars = g_utf8_strlen ((char*) data, nelements); + wcptr = g_utf8_to_utf16 ((char *) data, nelements, NULL, &wclen, NULL); - /* Check if only ASCII */ - for (i = 0; i < nelements; i++) - if (data[i] >= 0200) - break; - - if (i == nelements) - { - /* If only ASCII, 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++; - GDK_NOTE (DND, g_print ("... as text: %.40s\n", data)); - } - else - { - /* Use CF_UNICODETEXT */ - method = UNICODE_TEXT; - - wcptr = g_utf8_to_utf16 ((char *) data, nelements, NULL, &wclen, NULL); - - wclen++; /* Terminating 0 */ - size = wclen * 2; - for (i = 0; i < wclen; i++) - if (wcptr[i] == '\n') - size += 2; - GDK_NOTE (DND, g_print ("... as Unicode\n")); - } + wclen++; /* Terminating 0 */ + size = wclen * 2; + for (i = 0; i < wclen; i++) + if (wcptr[i] == '\n') + size += 2; if (!(hdata = GlobalAlloc (GMEM_MOVEABLE, size))) { @@ -246,53 +220,32 @@ gdk_property_change (GdkWindow *window, ucptr = GlobalLock (hdata); - switch (method) + p = (wchar_t *) ucptr; + for (i = 0; i < wclen; i++) { - 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: - { - wchar_t *p = (wchar_t *) ucptr; - cf = CF_UNICODETEXT; - for (i = 0; i < wclen; i++) - { - if (wcptr[i] == '\n') - *p++ = '\r'; - *p++ = wcptr[i]; - } - g_free (wcptr); - } - break; - - default: - g_assert_not_reached (); + if (wcptr[i] == '\n') + *p++ = '\r'; + *p++ = wcptr[i]; } + g_free (wcptr); GlobalUnlock (hdata); - GDK_NOTE (DND, g_print ("... SetClipboardData(%s,%p)\n", - _gdk_win32_cf_to_string (cf), hdata)); - if (ok && !SetClipboardData (cf, hdata)) - WIN32_API_FAILED ("SetClipboardData"), ok = FALSE; + GDK_NOTE (DND, g_print ("... SetClipboardData(CF_UNICODETEXT,%p)\n", + hdata)); + if (!SetClipboardData (CF_UNICODETEXT, hdata)) + WIN32_API_FAILED ("SetClipboardData"); if (!CloseClipboard ()) WIN32_API_FAILED ("CloseClipboard"); } else { - GDK_NOTE (DND, g_print ("... delayed rendering\n")); - /* 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. + /* We use delayed rendering for everything else than + * text. 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. */ + GDK_NOTE (DND, g_print ("... delayed rendering\n")); _delayed_rendering_data = NULL; if (!(hdata = GlobalAlloc (GMEM_MOVEABLE, nelements > 0 ? nelements : 1))) { @@ -318,14 +271,16 @@ gdk_property_delete (GdkWindow *window, g_return_if_fail (window != NULL); g_return_if_fail (GDK_IS_WINDOW (window)); - GDK_NOTE (DND, - (prop_name = gdk_atom_name (property), - g_print ("gdk_property_delete: %p %p (%s)\n", - GDK_WINDOW_HWND (window), - property, prop_name), - g_free (prop_name))); + GDK_NOTE (DND, { + prop_name = gdk_atom_name (property); - if (property == _gdk_selection_property) + g_print ("gdk_property_delete: %p %s\n", + GDK_WINDOW_HWND (window), + prop_name); + g_free (prop_name); + }); + + if (property == _gdk_selection) _gdk_selection_property_delete (window); else if (property == _wm_transient_for) gdk_window_set_transient_for (window, _gdk_root); @@ -339,37 +294,56 @@ gdk_property_delete (GdkWindow *window, } /* - for reference copied from gdk/x11/gdkevents-x11.c + For reference, from gdk/x11/gdksettings.c: - { "Net/DoubleClickTime", "gtk-double-click-time" }, - { "Net/DoubleClickDistance", "gtk-double-click-distance" }, - { "Net/DndDragThreshold", "gtk-dnd-drag-threshold" }, - { "Gtk/CanChangeAccels", "gtk-can-change-accels" }, - { "Gtk/ColorPalette", "gtk-color-palette" }, - { "Gtk/FontName", "gtk-font-name" }, - { "Gtk/IconSizes", "gtk-icon-sizes" }, - { "Gtk/KeyThemeName", "gtk-key-theme-name" }, - { "Gtk/ToolbarStyle", "gtk-toolbar-style" }, - { "Gtk/ToolbarIconSize", "gtk-toolbar-icon-size" }, - { "Gtk/IMPreeditStyle", "gtk-im-preedit-style" }, - { "Gtk/IMStatusStyle", "gtk-im-status-style" }, - { "Gtk/IMModule", "gtk-im-module" }, - { "Net/CursorBlink", "gtk-cursor-blink" }, - { "Net/CursorBlinkTime", "gtk-cursor-blink-time" }, - { "Net/ThemeName", "gtk-theme-name" }, - { "Net/IconThemeName", "gtk-icon-theme-name" }, - { "Gtk/ButtonImages", "gtk-button-images" }, - { "Gtk/MenuImages", "gtk-menu-images" }, - { "Xft/Antialias", "gtk-xft-antialias" }, - { "Xft/Hinting", "gtk-xft-hinting" }, - { "Xft/HintStyle", "gtk-xft-hintstyle" }, - { "Xft/RGBA", "gtk-xft-rgba" }, - { "Xft/DPI", "gtk-xft-dpi" }, + "Net/DoubleClickTime\0" "gtk-double-click-time\0" + "Net/DoubleClickDistance\0" "gtk-double-click-distance\0" + "Net/DndDragThreshold\0" "gtk-dnd-drag-threshold\0" + "Net/CursorBlink\0" "gtk-cursor-blink\0" + "Net/CursorBlinkTime\0" "gtk-cursor-blink-time\0" + "Net/ThemeName\0" "gtk-theme-name\0" + "Net/IconThemeName\0" "gtk-icon-theme-name\0" + "Gtk/CanChangeAccels\0" "gtk-can-change-accels\0" + "Gtk/ColorPalette\0" "gtk-color-palette\0" + "Gtk/FontName\0" "gtk-font-name\0" + "Gtk/IconSizes\0" "gtk-icon-sizes\0" + "Gtk/KeyThemeName\0" "gtk-key-theme-name\0" + "Gtk/ToolbarStyle\0" "gtk-toolbar-style\0" + "Gtk/ToolbarIconSize\0" "gtk-toolbar-icon-size\0" + "Gtk/IMPreeditStyle\0" "gtk-im-preedit-style\0" + "Gtk/IMStatusStyle\0" "gtk-im-status-style\0" + "Gtk/Modules\0" "gtk-modules\0" + "Gtk/FileChooserBackend\0" "gtk-file-chooser-backend\0" + "Gtk/ButtonImages\0" "gtk-button-images\0" + "Gtk/MenuImages\0" "gtk-menu-images\0" + "Gtk/MenuBarAccel\0" "gtk-menu-bar-accel\0" + "Gtk/CursorThemeName\0" "gtk-cursor-theme-name\0" + "Gtk/CursorThemeSize\0" "gtk-cursor-theme-size\0" + "Gtk/ShowInputMethodMenu\0" "gtk-show-input-method-menu\0" + "Gtk/ShowUnicodeMenu\0" "gtk-show-unicode-menu\0" + "Gtk/TimeoutInitial\0" "gtk-timeout-initial\0" + "Gtk/TimeoutRepeat\0" "gtk-timeout-repeat\0" + "Gtk/ColorScheme\0" "gtk-color-scheme\0" + "Gtk/EnableAnimations\0" "gtk-enable-animations\0" + "Xft/Antialias\0" "gtk-xft-antialias\0" + "Xft/Hinting\0" "gtk-xft-hinting\0" + "Xft/HintStyle\0" "gtk-xft-hintstyle\0" + "Xft/RGBA\0" "gtk-xft-rgba\0" + "Xft/DPI\0" "gtk-xft-dpi\0" + "Net/FallbackIconTheme\0" "gtk-fallback-icon-theme\0" + "Gtk/TouchscreenMode\0" "gtk-touchscreen-mode\0" + "Gtk/EnableAccels\0" "gtk-enable-accels\0" + "Gtk/EnableMnemonics\0" "gtk-enable-mnemonics\0" + "Gtk/ScrolledWindowPlacement\0" "gtk-scrolled-window-placement\0" + "Gtk/IMModule\0" "gtk-im-module\0" + "Fontconfig/Timestamp\0" "gtk-fontconfig-timestamp\0" + "Net/SoundThemeName\0" "gtk-sound-theme-name\0" + "Net/EnableInputFeedbackSounds\0" "gtk-enable-input-feedback-sounds\0" + "Net/EnableEventSounds\0" "gtk-enable-event-sounds\0"; + + More, from various places in gtk sources: - // more spread in gtk sources gtk-entry-select-on-focus - gtk-cursor-blink - gtk-cursor-blink-time gtk-split-cursor */ @@ -463,6 +437,5 @@ gdk_screen_get_setting (GdkScreen *screen, } #endif - GDK_NOTE(MISC, g_print("gdk_screen_get_setting(%s) not handled\n", name)); return FALSE; } diff --git a/gdk/win32/gdkselection-win32.c b/gdk/win32/gdkselection-win32.c index 5ae4425893..73588b8d90 100644 --- a/gdk/win32/gdkselection-win32.c +++ b/gdk/win32/gdkselection-win32.c @@ -35,7 +35,7 @@ #include "gdkprivate-win32.h" /* We emulate the GDK_SELECTION window properties of windows (as used - * in the X11 backend) by using a hash table from GdkWindows to + * in the X11 backend) by using a hash table from window handles to * GdkSelProp structs. */ @@ -55,12 +55,89 @@ static GdkSelProp *dropfiles_prop = NULL; */ static GHashTable *sel_owner_table = NULL; +/* Well-known registered clipboard image formats */ +static UINT cf_png; +static UINT cf_jfif; +static UINT cf_gif; + +/* GdkAtoms for well-known image formats */ +static GdkAtom *known_pixbuf_formats; +static int n_known_pixbuf_formats; +static GdkAtom image_png; +static GdkAtom image_jpeg; +static GdkAtom image_bmp; +static GdkAtom image_gif; + +/* GdkAtoms for well-known text formats */ +static GdkAtom text_plain; +static GdkAtom text_plain_charset_utf_8; +static GdkAtom text_plain_charset_CP1252; + void _gdk_win32_selection_init (void) { + GSList *pixbuf_formats; + GSList *rover; + 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); + + /* MS Office 2007, at least, offers images in common file formats + * using clipboard format names like "PNG" and "JFIF". So we follow + * the lead and map the GDK target name "image/png" to the clipboard + * format name "PNG" etc. + */ + cf_png = RegisterClipboardFormat ("PNG"); + cf_jfif = RegisterClipboardFormat ("JFIF"); + cf_gif = RegisterClipboardFormat ("GIF"); + + pixbuf_formats = gdk_pixbuf_get_formats (); + + n_known_pixbuf_formats = 0; + for (rover = pixbuf_formats; rover != NULL; rover = rover->next) + { + gchar **mime_types = + gdk_pixbuf_format_get_mime_types ((GdkPixbufFormat *) rover->data); + + gchar **mime_type; + + for (mime_type = mime_types; *mime_type != NULL; mime_type++) + n_known_pixbuf_formats++; + } + + known_pixbuf_formats = g_new (GdkAtom, n_known_pixbuf_formats); + + n_known_pixbuf_formats = 0; + for (rover = pixbuf_formats; rover != NULL; rover = rover->next) + { + gchar **mime_types = + gdk_pixbuf_format_get_mime_types ((GdkPixbufFormat *) rover->data); + + gchar **mime_type; + + for (mime_type = mime_types; *mime_type != NULL; mime_type++) + known_pixbuf_formats[n_known_pixbuf_formats++] = gdk_atom_intern (*mime_type, FALSE); + } + + g_slist_free (pixbuf_formats); + + image_png = gdk_atom_intern ("image/png", FALSE); + image_jpeg = gdk_atom_intern ("image/jpeg", FALSE); + image_bmp = gdk_atom_intern ("image/bmp", FALSE); + image_gif = gdk_atom_intern ("image/gif", FALSE); + + text_plain = gdk_atom_intern ("text/plain", FALSE); + text_plain_charset_utf_8= gdk_atom_intern ("text/plain;charset=utf-8", FALSE); + text_plain_charset_CP1252 = gdk_atom_intern ("text/plain;charset=CP1252", FALSE); + + g_hash_table_replace (_format_atom_table, + GINT_TO_POINTER (cf_png), + image_png); + + g_hash_table_replace (_format_atom_table, + GINT_TO_POINTER (CF_DIB), + image_bmp); } /* The specifications for COMPOUND_TEXT and STRING specify that C0 and @@ -130,42 +207,33 @@ _gdk_utf8_to_string_target_internal (const gchar *str, } static void -_gdk_selection_property_store (GdkWindow *owner, - GdkAtom type, - gint format, - guchar *data, - gint length) +selection_property_store (GdkWindow *owner, + GdkAtom type, + gint format, + guchar *data, + gint length) { GdkSelProp *prop; - GSList *prop_list; + + g_return_if_fail (type != GDK_TARGET_STRING); + + prop = g_hash_table_lookup (sel_prop_table, GDK_WINDOW_HWND (owner)); + + if (prop != NULL) + { + g_free (prop->data); + g_free (prop); + g_hash_table_remove (sel_prop_table, GDK_WINDOW_HWND (owner)); + } prop = g_new (GdkSelProp, 1); - if (type == GDK_TARGET_STRING) - { - /* We know that data is UTF-8 */ - prop->data = (guchar *) _gdk_utf8_to_string_target_internal ((char*) data, length); - g_free (data); - - if (!prop->data) - { - g_free (prop); - - return; - } - else - prop->length = strlen ((char*) prop->data) + 1; - } - else - { - prop->data = data; - prop->length = length; - } + prop->data = data; + prop->length = length; prop->format = format; prop->type = type; - prop_list = g_hash_table_lookup (sel_prop_table, GDK_WINDOW_HWND (owner)); - prop_list = g_slist_append (prop_list, prop); - g_hash_table_insert (sel_prop_table, GDK_WINDOW_HWND (owner), prop_list); + + g_hash_table_insert (sel_prop_table, GDK_WINDOW_HWND (owner), prop); } void @@ -192,6 +260,21 @@ _gdk_dropfiles_store (gchar *data) } } +static gchar * +get_mapped_gdk_atom_name (GdkAtom gdk_target) +{ + if (gdk_target == image_png) + return g_strdup ("PNG"); + + if (gdk_target == image_jpeg) + return g_strdup ("JFIF"); + + if (gdk_target == image_gif) + return g_strdup ("GIF"); + + return gdk_atom_name (gdk_target); +} + gboolean gdk_selection_owner_set_for_display (GdkDisplay *display, GdkWindow *owner, @@ -205,18 +288,14 @@ gdk_selection_owner_set_for_display (GdkDisplay *display, g_return_val_if_fail (display == _gdk_display, FALSE); g_return_val_if_fail (selection != GDK_NONE, FALSE); -#ifdef G_ENABLE_DEBUG - { - gchar *sel_name; + GDK_NOTE (DND, { + gchar *sel_name = gdk_atom_name (selection); - GDK_NOTE (DND, - (sel_name = gdk_atom_name (selection), - g_print ("gdk_selection_owner_set_for_display: %p %p (%s)\n", - (owner ? GDK_WINDOW_HWND (owner) : NULL), - selection, sel_name), - g_free (sel_name))); - } -#endif + g_print ("gdk_selection_owner_set_for_display: %p %s\n", + (owner ? GDK_WINDOW_HWND (owner) : NULL), + sel_name); + g_free (sel_name); + }); if (selection != GDK_SELECTION_CLIPBOARD) { @@ -266,7 +345,7 @@ gdk_selection_owner_set_for_display (GdkDisplay *display, tmp_event.selection.send_event = FALSE; tmp_event.selection.selection = selection; tmp_event.selection.target = _utf8_string; - tmp_event.selection.property = _gdk_selection_property; + tmp_event.selection.property = _gdk_selection; tmp_event.selection.requestor = hwnd; tmp_event.selection.time = time; @@ -297,18 +376,14 @@ gdk_selection_owner_get_for_display (GdkDisplay *display, window = gdk_window_lookup ((GdkNativeWindow) g_hash_table_lookup (sel_owner_table, selection)); -#ifdef G_ENABLE_DEBUG - { - gchar *sel_name; - - GDK_NOTE (DND, - (sel_name = gdk_atom_name (selection), - g_print ("gdk_selection_owner_get: %p (%s) = %p\n", - selection, sel_name, - (window ? GDK_WINDOW_HWND (window) : NULL)), - g_free (sel_name))); - } -#endif + GDK_NOTE (DND, { + gchar *sel_name = gdk_atom_name (selection); + + g_print ("gdk_selection_owner_get: %s = %p\n", + sel_name, + (window ? GDK_WINDOW_HWND (window) : NULL)); + g_free (sel_name); + }); return window; } @@ -341,7 +416,7 @@ gdk_selection_convert (GdkWindow *requestor, guint32 time) { HGLOBAL hdata; - GdkAtom property = _gdk_selection_property; + GdkAtom property = _gdk_selection; g_return_if_fail (selection != GDK_NONE); g_return_if_fail (requestor != NULL); @@ -349,76 +424,119 @@ gdk_selection_convert (GdkWindow *requestor, if (GDK_WINDOW_DESTROYED (requestor)) return; -#ifdef G_ENABLE_DEBUG - { - gchar *sel_name, *tgt_name; - - GDK_NOTE (DND, - (sel_name = gdk_atom_name (selection), - tgt_name = gdk_atom_name (target), - g_print ("gdk_selection_convert: %p %p (%s) %p (%s)\n", - GDK_WINDOW_HWND (requestor), - selection, sel_name, - target, tgt_name), - g_free (sel_name), - g_free (tgt_name))); - } -#endif + GDK_NOTE (DND, { + gchar *sel_name = gdk_atom_name (selection); + gchar *tgt_name = gdk_atom_name (target); + + g_print ("gdk_selection_convert: %p %s %s\n", + GDK_WINDOW_HWND (requestor), + sel_name, tgt_name); + g_free (sel_name); + g_free (tgt_name); + }); if (selection == GDK_SELECTION_CLIPBOARD && target == _targets) { - gint formats_cnt, i, fmt; - GdkAtom *data; + gint ntargets, fmt; + GdkAtom *targets; + gboolean has_text = FALSE; + gboolean has_png = FALSE; 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; + targets = g_new (GdkAtom, CountClipboardFormats ()); + ntargets = 0; - if (IsClipboardFormatAvailable (CF_UNICODETEXT) || - IsClipboardFormatAvailable (_cf_utf8_string) || - IsClipboardFormatAvailable (CF_TEXT)) + for (fmt = 0; 0 != (fmt = EnumClipboardFormats (fmt)); ) { - data[i++] = _utf8_string; + if (fmt == cf_png) + { + targets[ntargets++] = image_png; + has_png = TRUE; + } } - if (formats_cnt > 0) - { - /* If there is anything else in the clipboard, enum it all - * although we don't offer special conversion services. - */ - for (fmt = 0; 0 != (fmt = EnumClipboardFormats (fmt)); ) - { - gchar sFormat[80]; - if (GetClipboardFormatName (fmt, sFormat, 80) > 0 && - strcmp (sFormat, "UTF8_STRING")) - { - GdkAtom atom; + for (fmt = 0; 0 != (fmt = EnumClipboardFormats (fmt)); ) + { + gchar sFormat[80]; - if (!has_bmp && - (!strcmp (sFormat, "image/bmp") || - !strcmp (sFormat, "image/x-bmp") || - !strcmp (sFormat, "image/x-MS-bmp"))) - has_bmp = TRUE; - atom = gdk_atom_intern (sFormat, FALSE); - data[i++] = atom; - } + if (fmt == CF_UNICODETEXT || fmt == CF_TEXT) + { + /* Advertise text to GDK always as UTF8_STRING */ + if (!has_text) + targets[ntargets++] = _utf8_string; + has_text = TRUE; + } + else if (fmt == cf_png) + { + /* Already handled above */ + } + else if (fmt == CF_DIB || + fmt == CF_DIBV5) + { + /* Don't bother telling that a bitmap is present if there is + * also PNG, which is much more reliable in transferring + * transparency. + */ + if (!has_bmp && !has_png) + targets[ntargets++] = image_bmp; + has_bmp = TRUE; + } + else if (fmt == cf_jfif) + { + /* Ditto for JPEG */ + if (!has_png) + targets[ntargets++] = image_jpeg; + } + else if (fmt == cf_gif) + { + /* Ditto for GIF. + */ + if (!has_png) + targets[ntargets++] = image_gif; + } + else if (GetClipboardFormatName (fmt, sFormat, 80) > 0) + { + if (strcmp (sFormat, "image/bmp") == 0 || + strcmp (sFormat, "image/x-bmp") == 0 || + strcmp (sFormat, "image/x-MS-bmp") == 0 || + strcmp (sFormat, "image/x-icon") == 0 || + strcmp (sFormat, "image/x-ico") == 0 || + strcmp (sFormat, "image/x-win-bitmap") == 0) + { + /* Ignore these (from older GTK+ versions + * presumably), as the same image in the CF_DIB + * format will also be on the clipboard anyway. + */ + } + else + targets[ntargets++] = gdk_atom_intern (sFormat, FALSE); } } - 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 + GDK_NOTE (DND, { + int i; + + g_print ("... "); + for (i = 0; i < ntargets; i++) + { + gchar *atom_name = gdk_atom_name (targets[i]); + + g_print ("%s", atom_name); + g_free (atom_name); + if (i < ntargets - 1) + g_print (", "); + } + g_print ("\n"); + }); + + if (ntargets > 0) + selection_property_store (requestor, GDK_SELECTION_TYPE_ATOM, + 32, (guchar *) targets, + ntargets * sizeof (GdkAtom)); + else property = GDK_NONE; API_CALL (CloseClipboard, ()); @@ -432,7 +550,6 @@ gdk_selection_convert (GdkWindow *requestor, if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor)))) return; - /* Try various formats. First the simplest, CF_UNICODETEXT. */ if ((hdata = GetClipboardData (CF_UNICODETEXT)) != NULL) { wchar_t *ptr, *wcs, *p, *q; @@ -465,88 +582,8 @@ gdk_selection_convert (GdkWindow *requestor, g_free (wcs); if (data) - _gdk_selection_property_store (requestor, target, 8, - data, strlen (data) + 1); - GlobalUnlock (hdata); - } - } - else if ((hdata = GetClipboardData (_cf_utf8_string)) != NULL) - { - /* UTF8_STRING is a format we store ourselves when necessary */ - guchar *ptr; - gint length; - - if ((ptr = GlobalLock (hdata)) != NULL) - { - length = GlobalSize (hdata); - - GDK_NOTE (DND, g_print ("... UTF8_STRING: %d bytes: %.10s\n", - length, ptr)); - - _gdk_selection_property_store (requestor, target, 8, - g_memdup (ptr, length), length); - GlobalUnlock (hdata); - } - } - else if ((hdata = GetClipboardData (CF_TEXT)) != NULL) - { - /* We must always assume the data can contain non-ASCII - * in either the current code page, or if there is CF_LOCALE - * data, in that locale's default code page. - */ - HGLOBAL hlcid; - UINT cp = CP_ACP; - wchar_t *wcs, *wcs2, *p, *q; - guchar *ptr, *data; - glong length, wclen, wclen2; - - if ((ptr = GlobalLock (hdata)) != NULL) - { - length = GlobalSize (hdata); - - GDK_NOTE (DND, g_print ("... CF_TEXT: %ld bytes: %.10s\n", - length, ptr)); - - if ((hlcid = GetClipboardData (CF_LOCALE)) != NULL) - { - gchar buf[10]; - LCID *lcidptr = GlobalLock (hlcid); - if (GetLocaleInfo (*lcidptr, LOCALE_IDEFAULTANSICODEPAGE, - buf, sizeof (buf))) - { - cp = atoi (buf); - GDK_NOTE (DND, g_print ("... CF_LOCALE: %#lx cp:%d\n", - *lcidptr, cp)); - } - GlobalUnlock (hlcid); - } - - wcs = g_new (wchar_t, length + 1); - wclen = MultiByteToWideChar (cp, 0, ptr, length, - wcs, length + 1); - - /* Strip out \r */ - wcs2 = g_new (wchar_t, wclen); - p = wcs; - q = wcs2; - wclen2 = 0; - while (p < wcs + wclen) - { - if (*p != '\r') - { - *q++ = *p; - wclen2++; - } - p++; - } - g_free (wcs); - - data = g_utf16_to_utf8 (wcs2, wclen2, NULL, &length, NULL); - g_free (wcs2); - - if (data) - _gdk_selection_property_store (requestor, target, 8, - data, length + 1); + selection_property_store (requestor, _utf8_string, 8, + data, strlen (data) + 1); GlobalUnlock (hdata); } } @@ -555,80 +592,180 @@ gdk_selection_convert (GdkWindow *requestor, API_CALL (CloseClipboard, ()); } - else if (selection == GDK_SELECTION_CLIPBOARD && target == _image_bmp) + else if (selection == GDK_SELECTION_CLIPBOARD && target == image_bmp) { - guchar *data; - if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor)))) return; - if ((hdata = GetClipboardData (_cf_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 ((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; + BITMAPINFOHEADER *bi; - if ((ptr = GlobalLock (hdata)) != NULL) + if ((bi = GlobalLock (hdata)) != NULL) { - BITMAPFILEHEADER *hdr; /* Need to add a file header so gdk-pixbuf can load it */ - gint length = GlobalSize (hdata) + sizeof (BITMAPFILEHEADER); + /* Need to add a BMP file header so gdk-pixbuf can load + * it. + * + * If the data is from Mozilla Firefox or IE7, and + * starts with an "oldfashioned" BITMAPINFOHEADER, + * i.e. with biSize==40, and biCompression == BI_RGB and + * biBitCount==32, we assume that the "extra" byte in + * each pixel in fact is alpha. + * + * The gdk-pixbuf bmp loader doesn't trust 32-bit BI_RGB + * nitmaps to in fact have alpha, so we have to convince + * it by changint the bitmap header to a version 5 + * BI_BITFIELDS one with explicit alpha mask indicated. + * + * The RGB bytes that are in bitmaps on the clipboard + * originating from Firefox or IE7 seem to be + * premultiplied with alpha. The gdk-pixbuf bmp loader + * of course doesn't expect that, so we have to undo the + * premultiplication before feeding the bitmap to the + * bmp loader. + * + * Note that for some reason the bmp loader used to want + * the alpha bytes in its input to actually be + * 255-alpha, but here we assume that this has been + * fixed before this is committed. + */ + BITMAPFILEHEADER *bf; + gpointer data; + gint data_length = GlobalSize (hdata); + gint new_length; + gboolean make_dibv5 = FALSE; + + GDK_NOTE (DND, g_print ("... CF_DIB: %d bytes\n", data_length)); + + if (bi->biSize == sizeof (BITMAPINFOHEADER) && + bi->biPlanes == 1 && + bi->biBitCount == 32 && + bi->biCompression == BI_RGB && +#if 0 + /* Maybe check explicitly for Mozilla or IE7? + * + * If the clipboard format + * application/x-moz-nativeimage is present, that is + * a reliable indicator that the data is offered by + * Mozilla one would think. For IE7, + * UniformResourceLocatorW is presumably not that + * uniqie, so probably need to do some + * GetClipboardOwner(), GetWindowThreadProcessId(), + * OpenProcess(), GetModuleFileNameEx() dance to + * check? + */ + (IsClipboardFormatAvailable + (RegisterClipboardFormat ("application/x-moz-nativeimage")) || + IsClipboardFormatAvailable + (RegisterClipboardFormat ("UniformResourceLocatorW"))) && +#endif + TRUE) + { + /* We turn the BITMAPINFOHEADER into a + * BITMAPV5HEADER before feeding it to gdk-pixbuf. + */ + new_length = (data_length + + sizeof (BITMAPFILEHEADER) + + (sizeof (BITMAPV5HEADER) - sizeof (BITMAPINFOHEADER))); + make_dibv5 = TRUE; + } + else + { + new_length = data_length + sizeof (BITMAPFILEHEADER); + } - GDK_NOTE (DND, g_print ("... BITMAP (from CF_DIB): %d bytes\n", length)); - - data = g_try_malloc (length); + data = g_try_malloc (new_length); + if (data) { - hdr = (BITMAPFILEHEADER *)data; - hdr->bfType = 0x4d42; /* 0x42 = "B" 0x4d = "M" */ - /* Compute the size of the entire file. */ - hdr->bfSize = (DWORD) (sizeof (BITMAPFILEHEADER) - + ptr->biSize + ptr->biClrUsed - * sizeof (RGBQUAD) + ptr->biSizeImage); - hdr->bfReserved1 = 0; - hdr->bfReserved2 = 0; - /* Compute the offset to the array of color indices. */ - hdr->bfOffBits = (DWORD) sizeof (BITMAPFILEHEADER) - + ptr->biSize + ptr->biClrUsed * sizeof (RGBQUAD); - /* Copy the data behind it */ - memcpy (data + sizeof (BITMAPFILEHEADER), ptr, length - sizeof (BITMAPFILEHEADER)); - _gdk_selection_property_store (requestor, target, 8, - data, length); + bf = (BITMAPFILEHEADER *)data; + bf->bfType = 0x4d42; /* "BM" */ + bf->bfSize = new_length; + bf->bfReserved1 = 0; + bf->bfReserved2 = 0; + + if (make_dibv5) + { + BITMAPV5HEADER *bV5 = data + sizeof (BITMAPFILEHEADER); + guchar *p; + int i; + + bV5->bV5Size = sizeof (BITMAPV5HEADER); + bV5->bV5Width = bi->biWidth; + bV5->bV5Height = bi->biHeight; + bV5->bV5Planes = 1; + bV5->bV5BitCount = 32; + bV5->bV5Compression = BI_BITFIELDS; + bV5->bV5SizeImage = 4 * bV5->bV5Width * ABS (bV5->bV5Height); + bV5->bV5XPelsPerMeter = bi->biXPelsPerMeter; + bV5->bV5YPelsPerMeter = bi->biYPelsPerMeter; + bV5->bV5ClrUsed = 0; + bV5->bV5ClrImportant = 0; + /* Now the added mask fields */ + bV5->bV5RedMask = 0x00ff0000; + bV5->bV5GreenMask = 0x0000ff00; + bV5->bV5BlueMask = 0x000000ff; + bV5->bV5AlphaMask = 0xff000000; + ((char *) &bV5->bV5CSType)[3] = 's'; + ((char *) &bV5->bV5CSType)[2] = 'R'; + ((char *) &bV5->bV5CSType)[1] = 'G'; + ((char *) &bV5->bV5CSType)[0] = 'B'; + /* Ignore colorspace and profile fields */ + bV5->bV5Intent = LCS_GM_GRAPHICS; + bV5->bV5Reserved = 0; + + bf->bfOffBits = (sizeof (BITMAPFILEHEADER) + + bV5->bV5Size); + + p = ((guchar *) data) + sizeof (BITMAPFILEHEADER) + sizeof (BITMAPV5HEADER); + memcpy (p, ((char *) bi) + bi->biSize, + data_length - sizeof (BITMAPINFOHEADER)); + + for (i = 0; i < bV5->bV5SizeImage/4; i++) + { + if (p[3] != 0) + { + gdouble inverse_alpha = 255./p[3]; + + p[0] = p[0] * inverse_alpha + 0.5; + p[1] = p[1] * inverse_alpha + 0.5; + p[2] = p[2] * inverse_alpha + 0.5; + } + + p += 4; + } + } + else + { + bf->bfOffBits = (sizeof (BITMAPFILEHEADER) + + bi->biSize + + bi->biClrUsed * sizeof (RGBQUAD)); + + memcpy (data + sizeof (BITMAPFILEHEADER), + bi, + data_length); + } + + selection_property_store (requestor, image_bmp, 8, + data, new_length); } GlobalUnlock (hdata); } - } API_CALL (CloseClipboard, ()); } else if (selection == GDK_SELECTION_CLIPBOARD) { - char *target_name; + gchar *mapped_target_name; UINT fmt = 0; if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor)))) return; - target_name = gdk_atom_name (target); + mapped_target_name = get_mapped_gdk_atom_name (target); - /* Check if it's available. In fact, we can simply call + /* Check if it's available. We could 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 @@ -639,7 +776,7 @@ gdk_selection_convert (GdkWindow *requestor, char sFormat[80]; if (GetClipboardFormatName (fmt, sFormat, 80) > 0 && - strcmp (sFormat, target_name) == 0) + strcmp (sFormat, mapped_target_name) == 0) { if ((hdata = GetClipboardData (fmt)) != NULL) { @@ -651,17 +788,17 @@ gdk_selection_convert (GdkWindow *requestor, { length = GlobalSize (hdata); - GDK_NOTE (DND, g_print ("... %s: %d bytes\n", target_name, length)); + GDK_NOTE (DND, g_print ("... %s: %d bytes\n", mapped_target_name, length)); - _gdk_selection_property_store (requestor, target, 8, - g_memdup (ptr, length), length); + selection_property_store (requestor, target, 8, + g_memdup (ptr, length), length); GlobalUnlock (hdata); break; } } } } - g_free (target_name); + g_free (mapped_target_name); API_CALL (CloseClipboard, ()); } else if (selection == _gdk_win32_dropfiles) @@ -672,7 +809,7 @@ gdk_selection_convert (GdkWindow *requestor, */ if (dropfiles_prop != NULL) { - _gdk_selection_property_store + selection_property_store (requestor, selection, dropfiles_prop->format, dropfiles_prop->data, dropfiles_prop->length); g_free (dropfiles_prop); @@ -682,9 +819,9 @@ gdk_selection_convert (GdkWindow *requestor, else property = GDK_NONE; - /* Generate a selection notify message so that we actually fetch - * the data (if property == _gdk_selection_property) or indicating failure - * (if property == GDK_NONE). + /* Generate a selection notify message so that we actually fetch the + * data (if property == _gdk_selection) or indicating failure (if + * property == GDK_NONE). */ generate_selection_notify (requestor, selection, target, property, time); } @@ -696,7 +833,6 @@ gdk_selection_property_get (GdkWindow *requestor, gint *ret_format) { GdkSelProp *prop; - GSList *prop_list; g_return_val_if_fail (requestor != NULL, 0); g_return_val_if_fail (GDK_IS_WINDOW (requestor), 0); @@ -704,15 +840,16 @@ gdk_selection_property_get (GdkWindow *requestor, if (GDK_WINDOW_DESTROYED (requestor)) return 0; - GDK_NOTE (DND, g_print ("gdk_selection_property_get: %p\n", + GDK_NOTE (DND, g_print ("gdk_selection_property_get: %p", GDK_WINDOW_HWND (requestor))); - prop_list = g_hash_table_lookup (sel_prop_table, GDK_WINDOW_HWND (requestor)); - prop = prop_list ? (GdkSelProp *) prop_list->data : NULL; + prop = g_hash_table_lookup (sel_prop_table, GDK_WINDOW_HWND (requestor)); if (prop == NULL) { + GDK_NOTE (DND, g_print (" (nothing)\n")); *data = NULL; + return 0; } @@ -721,6 +858,13 @@ gdk_selection_property_get (GdkWindow *requestor, if (prop->length > 0) memmove (*data, prop->data, prop->length); + GDK_NOTE (DND, { + gchar *type_name = gdk_atom_name (prop->type); + + g_print (" %s format:%d length:%d\n", type_name, prop->format, prop->length); + g_free (type_name); + }); + if (ret_type) *ret_type = prop->type; @@ -734,19 +878,19 @@ void _gdk_selection_property_delete (GdkWindow *window) { GdkSelProp *prop; - GSList *prop_list; - GDK_NOTE (DND, g_print ("_gdk_selection_property_delete: %p\n", + GDK_NOTE (DND, g_print ("_gdk_selection_property_delete: %p (no-op)\n", GDK_WINDOW_HWND (window))); - prop_list = g_hash_table_lookup (sel_prop_table, GDK_WINDOW_HWND (window)); - if (prop_list && (prop = (GdkSelProp *) prop_list->data) != NULL) +#if 0 + prop = g_hash_table_lookup (sel_prop_table, GDK_WINDOW_HWND (window)); + if (prop != NULL) { g_free (prop->data); - prop_list = g_slist_remove (prop_list, prop); g_free (prop); - g_hash_table_insert (sel_prop_table, GDK_WINDOW_HWND (window), prop_list); + g_hash_table_remove (sel_prop_table, GDK_WINDOW_HWND (window)); } +#endif } void @@ -759,24 +903,17 @@ gdk_selection_send_notify_for_display (GdkDisplay *display, { g_return_if_fail (display == _gdk_display); -#ifdef G_ENABLE_DEBUG - { - gchar *sel_name, *tgt_name, *prop_name; - - GDK_NOTE (DND, - (sel_name = gdk_atom_name (selection), - tgt_name = gdk_atom_name (target), - prop_name = gdk_atom_name (property), - g_print ("gdk_selection_send_notify_for_display: %p %p (%s) %p (%s) %p (%s)\n", - requestor, - selection, sel_name, - target, tgt_name, - property, prop_name), - g_free (sel_name), - g_free (tgt_name), - g_free (prop_name))); - } -#endif + GDK_NOTE (DND, { + gchar *sel_name = gdk_atom_name (selection); + gchar *tgt_name = gdk_atom_name (target); + gchar *prop_name = gdk_atom_name (property); + + g_print ("gdk_selection_send_notify_for_display: %p %s %s %s (no-op)\n", + requestor, sel_name, tgt_name, prop_name); + g_free (sel_name); + g_free (tgt_name); + g_free (prop_name); + }); } /* It's hard to say whether implementing this actually is of any use @@ -797,16 +934,13 @@ gdk_text_property_to_text_list_for_display (GdkDisplay *display, g_return_val_if_fail (display == _gdk_display, 0); -#ifdef G_ENABLE_DEBUG - { - gchar *enc_name; - - GDK_NOTE (DND, (enc_name = gdk_atom_name (encoding), - g_print ("gdk_text_property_to_text_list_for_display: %s %d %.20s %d\n", - enc_name, format, text, length), - g_free (enc_name))); - } -#endif + GDK_NOTE (DND, { + gchar *enc_name = gdk_atom_name (encoding); + + g_print ("gdk_text_property_to_text_list_for_display: %s %d %.20s %d\n", + enc_name, format, text, length); + g_free (enc_name); + }); if (!list) return 0; @@ -1022,6 +1156,13 @@ gdk_free_compound_text (guchar *ctext) g_return_if_fail (ctext == NULL); } +/* This function is called from gtk_selection_add_target() and + * gtk_selection_add_targets() in gtkselection.c. It is this function + * that takes care of setting those clipboard formats for which we use + * delayed rendering. Formats copied directly to the clipboard are + * handled in gdk_property_change() in gdkproperty-win32.c. + */ + void gdk_win32_selection_add_targets (GdkWindow *owner, GdkAtom selection, @@ -1029,14 +1170,10 @@ gdk_win32_selection_add_targets (GdkWindow *owner, GdkAtom *targets) { HWND hwnd = NULL; - guint formatid; + gboolean has_image = FALSE; gint i; - GSList *convertable_formats, *format; - gboolean has_set_dib = FALSE, has_real_dib = FALSE; -#ifdef G_ENABLE_DEBUG - if (_gdk_debug_flags & GDK_DEBUG_DND) - { + GDK_NOTE (DND, { gchar *sel_name = gdk_atom_name (selection); g_print ("gdk_win32_selection_add_targets: %p: %s: ", @@ -1048,12 +1185,13 @@ gdk_win32_selection_add_targets (GdkWindow *owner, { gchar *tgt_name = gdk_atom_name (targets[i]); - g_print ("%s ", tgt_name); + g_print ("%s", tgt_name); g_free (tgt_name); + if (i < n_targets - 1) + g_print (", "); } g_print ("\n"); - } -#endif + }); if (selection != GDK_SELECTION_CLIPBOARD) return; @@ -1068,80 +1206,77 @@ gdk_win32_selection_add_targets (GdkWindow *owner, if (!API_CALL (OpenClipboard, (hwnd))) return; - convertable_formats = gdk_pixbuf_get_formats (); - for (i = 0; i < n_targets; ++i) + /* We have a very simple strategy: If some kind of pixmap image + * format is being added, actually advertise just PNG and DIB. PNG + * is our preferred format because it can losslessly represent any + * image that gdk-pixbuf formats in general can, even with alpha, + * unambiguously. CF_DIB is also advertised because of the general + * support for it in Windows software, but note that alpha won't be + * handled. + */ + for (i = 0; !has_image && i < n_targets; ++i) { + UINT cf; gchar *target_name; + int j; + + for (j = 0; j < n_known_pixbuf_formats; j++) + if (targets[i] == known_pixbuf_formats[j]) + { + if (!has_image) + { + GDK_NOTE (DND, g_print ("... SetClipboardData(PNG,NULL)\n")); + SetClipboardData (cf_png, NULL); - if (targets[i] == _utf8_string || + GDK_NOTE (DND, g_print ("... SetClipboardData(CF_DIB,NULL)\n")); + SetClipboardData (CF_DIB, NULL); + + has_image = TRUE; + } + break; + } + + /* If it is one of the pixmap formats, already handled or not + * needed. + */ + if (j < n_known_pixbuf_formats) + continue; + + /* We don't bother registering and advertising clipboard formats + * that are X11 specific or no non-GTK+ apps will have ever + * heard of, and when there are equivalent clipboard formats + * that are commonly used. + */ + if (targets[i] == _save_targets || + targets[i] == _utf8_string || targets[i] == GDK_TARGET_STRING || - targets[i] == _text || targets[i] == _compound_text || - targets[i] == _save_targets) + targets[i] == _text || + targets[i] == text_plain_charset_utf_8 || + targets[i] == text_plain_charset_CP1252 || + targets[i] == text_plain) continue; target_name = gdk_atom_name (targets[i]); - if (!(formatid = RegisterClipboardFormat (target_name))) - { - WIN32_API_FAILED ("RegisterClipboardFormat"); - API_CALL (CloseClipboard, ()); - g_free (target_name); - return; - } - g_hash_table_replace (_format_atom_table, GINT_TO_POINTER (formatid), targets[i]); - GDK_NOTE (DND, g_print ("... SetClipboardData(%s,NULL)\n", - _gdk_win32_cf_to_string (formatid))); - 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"))) + if (g_str_has_prefix (target_name, "text/plain;charset=")) { - g_hash_table_replace (_format_atom_table, - GINT_TO_POINTER (CF_DIB), - targets[i]); - if (!has_set_dib) - { - GDK_NOTE (DND, g_print ("... SetClipboardData(CF_DIB,NULL)\n")); - SetClipboardData (CF_DIB, NULL); - has_set_dib = TRUE; - } - has_real_dib = TRUE; g_free (target_name); continue; } - for (format = convertable_formats; !has_set_dib && format; format = format->next) - { - gchar **mime_types = - gdk_pixbuf_format_get_mime_types ((GdkPixbufFormat *) format->data); - gchar **mime_type; + cf = RegisterClipboardFormat (target_name); + + g_hash_table_replace (_format_atom_table, + GINT_TO_POINTER (cf), + targets[i]); + + GDK_NOTE (DND, g_print ("... SetClipboardData(%s,NULL)\n", + _gdk_win32_cf_to_string (cf))); + SetClipboardData (cf, NULL); - for (mime_type = mime_types; *mime_type; ++mime_type) - { - if (!strcmp (target_name, *mime_type)) - { - g_hash_table_replace (_format_atom_table, - GINT_TO_POINTER (CF_DIB), - targets[i]); - GDK_NOTE (DND, g_print ("... SetClipboardData(CF_DIB,NULL)\n")); - SetClipboardData (CF_DIB, NULL); - has_set_dib = TRUE; - break; - } - } - g_strfreev(mime_types); - } g_free (target_name); } - g_slist_free (convertable_formats); - API_CALL (CloseClipboard, ()); } @@ -1153,84 +1288,33 @@ HGLOBAL _gdk_win32_selection_convert_to_dib (HGLOBAL hdata, GdkAtom target) { - GdkPixbufLoader *loader; - GdkPixbuf *pixbuf; - gchar *target_name; - guchar *ptr; - gchar *bmp_buf; - gsize size; - gboolean ok; + GDK_NOTE (DND, { + gchar *target_name = gdk_atom_name (target); - if (!(target_name = gdk_atom_name (target))) - { - GlobalFree (hdata); - return NULL; - } + g_print ("_gdk_win32_selection_convert_to_dib: %p %s\n", + hdata, target_name); + g_free (target_name); + }); - if (!strcmp (target_name, "image/bmp") || - !strcmp (target_name, "image/x-bmp") || - !strcmp (target_name, "image/x-MS-bmp")) + if (target == image_bmp) { /* No conversion is needed, just strip the BITMAPFILEHEADER */ HGLOBAL hdatanew; + SIZE_T size = GlobalSize (hdata) - sizeof (BITMAPFILEHEADER); + guchar *ptr = GlobalLock (hdata); - g_free (target_name); - size = GlobalSize (hdata) - sizeof (BITMAPFILEHEADER); - ptr = GlobalLock (hdata); memmove (ptr, ptr + sizeof (BITMAPFILEHEADER), size); GlobalUnlock (hdata); - if (!(hdatanew = GlobalReAlloc (hdata, size, 0))) + + if ((hdatanew = GlobalReAlloc (hdata, size, GMEM_MOVEABLE)) == NULL) { WIN32_API_FAILED ("GlobalReAlloc"); - GlobalFree (hdata); /* the old hdata is not freed if error */ + 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); - g_free (target_name); - return NULL; - } - g_free (target_name); + g_warning ("Should not happen: We provide some image format but not CF_DIB and CF_DIB is requested."); - 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; + return NULL; }