diff --git a/widget/src/gtk2/nsDragService.cpp b/widget/src/gtk2/nsDragService.cpp index 8140a8f4dcd..c09809dea8f 100644 --- a/widget/src/gtk2/nsDragService.cpp +++ b/widget/src/gtk2/nsDragService.cpp @@ -58,12 +58,17 @@ #include "nsCRT.h" #include "gfxASurface.h" +#include "gfxXlibSurface.h" +#include "gfxContext.h" #include "nsImageToPixbuf.h" #include "nsIPresShell.h" #include "nsPresContext.h" #include "nsIDocument.h" #include "nsISelection.h" +// This sets how opaque the drag image is +#define DRAG_IMAGE_ALPHA_LEVEL 0.5 + static PRLogModuleInfo *sDragLm = NULL; static const char gMimeListType[] = "application/x-moz-internal-item-list"; @@ -202,27 +207,34 @@ nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode, 1, &event); - GdkPixbuf* dragPixbuf = nsnull; + PRBool needsFallbackIcon = PR_FALSE; nsRect dragRect; nsPresContext* pc; + nsRefPtr surface; if (mHasImage || mSelection) { - nsRefPtr surface; DrawDrag(aDOMNode, aRegion, mScreenX, mScreenY, &dragRect, getter_AddRefs(surface), &pc); - if (surface) { - dragPixbuf = - nsImageToPixbuf::SurfaceToPixbuf(surface, dragRect.width, dragRect.height); - } } - if (dragPixbuf) { + if (surface) { PRInt32 sx = mScreenX, sy = mScreenY; ConvertToUnscaledDevPixels(pc, &sx, &sy); - gtk_drag_set_icon_pixbuf(context, dragPixbuf, - sx - NSToIntRound(dragRect.x), - sy - NSToIntRound(dragRect.y)); + + PRInt32 offsetX = sx - NSToIntRound(dragRect.x); + PRInt32 offsetY = sy - NSToIntRound(dragRect.y); + if (!SetAlphaPixmap(surface, context, offsetX, offsetY, dragRect)) { + GdkPixbuf* dragPixbuf = + nsImageToPixbuf::SurfaceToPixbuf(surface, dragRect.width, dragRect.height); + if (dragPixbuf) + gtk_drag_set_icon_pixbuf(context, dragPixbuf, offsetX, offsetY); + else + needsFallbackIcon = PR_TRUE; + } + } else { + needsFallbackIcon = PR_TRUE; } - else + + if (needsFallbackIcon) gtk_drag_set_icon_default(context); gtk_target_list_unref(sourceList); @@ -231,6 +243,56 @@ nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode, return NS_OK; } +PRBool +nsDragService::SetAlphaPixmap(gfxASurface *aSurface, + GdkDragContext *aContext, + PRInt32 aXOffset, + PRInt32 aYOffset, + const nsRect& dragRect) +{ + GdkScreen* screen = gtk_widget_get_screen(mHiddenWidget); + + // Transparent drag icons need, like a lot of transparency-related things, + // a compositing X window manager + if (!gdk_screen_is_composited(screen)) + return PR_FALSE; + + GdkColormap* alphaColormap = gdk_screen_get_rgba_colormap(screen); + if (!alphaColormap) + return PR_FALSE; + + GdkPixmap* pixmap = gdk_pixmap_new(NULL, dragRect.width, dragRect.height, + gdk_colormap_get_visual(alphaColormap)->depth); + if (!pixmap) + return PR_FALSE; + + gdk_drawable_set_colormap(GDK_DRAWABLE(pixmap), alphaColormap); + + // Make a gfxXlibSurface wrapped around the pixmap to render on + nsRefPtr xPixmapSurface = + nsWindow::GetSurfaceForGdkDrawable(GDK_DRAWABLE(pixmap), + dragRect.Size()); + if (!xPixmapSurface) + return PR_FALSE; + + nsRefPtr xPixmapCtx = new gfxContext(xPixmapSurface); + + // Clear it... + xPixmapCtx->SetOperator(gfxContext::OPERATOR_CLEAR); + xPixmapCtx->Paint(); + + // ...and paint the drag image with translucency + xPixmapCtx->SetOperator(gfxContext::OPERATOR_SOURCE); + xPixmapCtx->SetSource(aSurface); + xPixmapCtx->Paint(DRAG_IMAGE_ALPHA_LEVEL); + + // The drag transaction addrefs the pixmap, so we can just unref it from us here + gtk_drag_set_icon_pixmap(aContext, alphaColormap, pixmap, NULL, + aXOffset, aYOffset); + gdk_pixmap_unref(pixmap); + return PR_TRUE; +} + NS_IMETHODIMP nsDragService::StartDragSession() { diff --git a/widget/src/gtk2/nsDragService.h b/widget/src/gtk2/nsDragService.h index efbcde7fded..f20c67184ab 100644 --- a/widget/src/gtk2/nsDragService.h +++ b/widget/src/gtk2/nsDragService.h @@ -142,6 +142,14 @@ private: // get a list of the sources in gtk's format GtkTargetList *GetSourceList(void); + // attempts to create a semi-transparent drag image. Returns TRUE if + // successful, FALSE if not + PRBool SetAlphaPixmap(gfxASurface *aPixbuf, + GdkDragContext *aContext, + PRInt32 aXOffset, + PRInt32 aYOffset, + const nsRect& dragRect); + }; #endif // nsDragService_h__ diff --git a/widget/src/gtk2/nsWindow.cpp b/widget/src/gtk2/nsWindow.cpp index 9e0d18739f3..162e3347115 100644 --- a/widget/src/gtk2/nsWindow.cpp +++ b/widget/src/gtk2/nsWindow.cpp @@ -1677,7 +1677,7 @@ nsWindow::OnExposeEvent(GtkWidget *aWidget, GdkEventExpose *aEvent) GetHasTransparentBackground(translucent); nsIntRect boundsRect; GdkPixmap* bufferPixmap = nsnull; - nsRefPtr bufferPixmapSurface; + nsRefPtr bufferPixmapSurface; updateRegion->GetBoundingBox(&boundsRect.x, &boundsRect.y, &boundsRect.width, &boundsRect.height); @@ -1715,13 +1715,8 @@ nsWindow::OnExposeEvent(GtkWidget *aWidget, GdkEventExpose *aEvent) gint depth = gdk_drawable_get_depth(d); bufferPixmap = gdk_pixmap_new(d, boundsRect.width, boundsRect.height, depth); if (bufferPixmap) { - GdkVisual* visual = gdk_drawable_get_visual(GDK_DRAWABLE(bufferPixmap)); - Visual* XVisual = gdk_x11_visual_get_xvisual(visual); - Display* display = gdk_x11_drawable_get_xdisplay(GDK_DRAWABLE(bufferPixmap)); - Drawable drawable = gdk_x11_drawable_get_xid(GDK_DRAWABLE(bufferPixmap)); - bufferPixmapSurface = - new gfxXlibSurface(display, drawable, XVisual, - gfxIntSize(boundsRect.width, boundsRect.height)); + bufferPixmapSurface = GetSurfaceForGdkDrawable(GDK_DRAWABLE(bufferPixmap), + boundsRect.Size()); if (bufferPixmapSurface) { bufferPixmapSurface->SetDeviceOffset(gfxPoint(-boundsRect.x, -boundsRect.y)); nsCOMPtr newRC; @@ -6180,6 +6175,19 @@ IM_get_input_context(nsWindow *aWindow) #endif +/* static */ already_AddRefed +nsWindow::GetSurfaceForGdkDrawable(GdkDrawable* aDrawable, + const nsSize& aSize) +{ + GdkVisual* visual = gdk_drawable_get_visual(aDrawable); + Visual* xVisual = gdk_x11_visual_get_xvisual(visual); + Display* xDisplay = gdk_x11_drawable_get_xdisplay(aDrawable); + Drawable xDrawable = gdk_x11_drawable_get_xid(aDrawable); + + return new gfxXlibSurface(xDisplay, xDrawable, xVisual, + gfxIntSize(aSize.width, aSize.height)); +} + // return the gfxASurface for rendering to this widget gfxASurface* nsWindow::GetThebesSurface() diff --git a/widget/src/gtk2/nsWindow.h b/widget/src/gtk2/nsWindow.h index 0414e6628f2..0631535408e 100644 --- a/widget/src/gtk2/nsWindow.h +++ b/widget/src/gtk2/nsWindow.h @@ -52,6 +52,8 @@ #include "nsITimer.h" #include "nsWidgetAtoms.h" +#include "gfxASurface.h" + #include #include @@ -364,6 +366,9 @@ public: gfxASurface *GetThebesSurface(); + static already_AddRefed GetSurfaceForGdkDrawable(GdkDrawable* aDrawable, + const nsSize& aSize); + #ifdef ACCESSIBILITY static PRBool sAccessibilityEnabled; #endif