Bug 376578, drag feedback is offset when dpi is changed, r+sr=roc
authorenndeakin@sympatico.ca
Tue, 08 Jan 2008 14:03:53 -0800
changeset 10025 cd353cf8b2bc8736192d95d9925b30522d09e865
parent 10024 8653a032c522255eee4ed2545d331ff8919b4fa5
child 10026 76075fc0b51928b26bbb5e520aa23cb7c3ba6716
push idunknown
push userunknown
push dateunknown
bugs376578
milestone1.9b3pre
Bug 376578, drag feedback is offset when dpi is changed, r+sr=roc
widget/src/cocoa/nsDragService.mm
widget/src/gtk2/nsDragService.cpp
widget/src/windows/nsDragService.cpp
widget/src/xpwidgets/nsBaseDragService.cpp
widget/src/xpwidgets/nsBaseDragService.h
--- a/widget/src/cocoa/nsDragService.mm
+++ b/widget/src/cocoa/nsDragService.mm
@@ -159,19 +159,20 @@ nsDragService::ConstructDragImage(nsIDOM
                                   nsIScriptableRegion* aRegion)
 {
   NSPoint screenPoint = [[gLastDragView window] convertBaseToScreen:[gLastDragEvent locationInWindow]];
   // Y coordinates are bottom to top, so reverse this
   if ([[NSScreen screens] count] > 0)
     screenPoint.y = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - screenPoint.y;
 
   nsRefPtr<gfxASurface> surface;
+  nsPresContext* pc;
   nsresult rv = DrawDrag(aDOMNode, aRegion,
                          NSToIntRound(screenPoint.x), NSToIntRound(screenPoint.y),
-                         aDragRect, getter_AddRefs(surface));
+                         aDragRect, getter_AddRefs(surface), &pc);
   if (!aDragRect->width || !aDragRect->height) {
     // just use some suitable defaults
     aDragRect->SetRect(NSToIntRound(screenPoint.x), NSToIntRound(screenPoint.y), 20, 20);
   }
 
   if (NS_FAILED(rv) || !surface)
     return nil;
 
--- a/widget/src/gtk2/nsDragService.cpp
+++ b/widget/src/gtk2/nsDragService.cpp
@@ -54,16 +54,19 @@
 #include "prtime.h"
 #include "prthread.h"
 #include <gtk/gtkinvisible.h>
 #include <gdk/gdkx.h>
 #include "nsCRT.h"
 
 #include "gfxASurface.h"
 #include "nsImageToPixbuf.h"
+#include "nsIPresShell.h"
+#include "nsPresContext.h"
+#include "nsIDocument.h"
 
 static PRLogModuleInfo *sDragLm = NULL;
 
 static const char gMimeListType[] = "application/x-moz-internal-item-list";
 static const char gMozUrlType[] = "_NETSCAPE_URL";
 static const char gTextUriListType[] = "text/uri-list";
 
 static void
@@ -192,30 +195,34 @@ nsDragService::InvokeDragSession(nsIDOMN
         GdkDragContext *context = gtk_drag_begin(mHiddenWidget,
                                                  sourceList,
                                                  action,
                                                  1,
                                                  &event);
 
         GdkPixbuf* dragPixbuf = nsnull;
         nsRect dragRect;
+        nsPresContext* pc;
         if (mHasImage || mSelection) {
           nsRefPtr<gfxASurface> surface;
           DrawDrag(aDOMNode, aRegion, mScreenX, mScreenY,
-                   &dragRect, getter_AddRefs(surface));
+                   &dragRect, getter_AddRefs(surface), &pc);
           if (surface) {
             dragPixbuf =
               nsImageToPixbuf::SurfaceToPixbuf(surface, dragRect.width, dragRect.height);
           }
         }
 
-        if (dragPixbuf)
+        if (dragPixbuf) {
+          PRInt32 sx = mScreenX, sy = mScreenY;
+          ConvertToUnscaledDevPixels(pc, &sx, &sy);
           gtk_drag_set_icon_pixbuf(context, dragPixbuf,
-                                   mScreenX - NSToIntRound(dragRect.x),
-                                   mScreenY - NSToIntRound(dragRect.y));
+                                   sx - NSToIntRound(dragRect.x),
+                                   sy - NSToIntRound(dragRect.y));
+        }
         else
           gtk_drag_set_icon_default(context);
 
         gtk_target_list_unref(sourceList);
     }
 
     return NS_OK;
 }
--- a/widget/src/windows/nsDragService.cpp
+++ b/widget/src/windows/nsDragService.cpp
@@ -106,19 +106,20 @@ nsDragService::CreateDragImage(nsIDOMNod
 
   memset(psdi, 0, sizeof(SHDRAGIMAGE));
   if (!aDOMNode) 
     return PR_FALSE;
 
   // Prepare the drag image
   nsRect dragRect;
   nsRefPtr<gfxASurface> surface;
+  nsPresContext* pc;
   DrawDrag(aDOMNode, aRegion,
            mScreenX, mScreenY,
-           &dragRect, getter_AddRefs(surface));
+           &dragRect, getter_AddRefs(surface), &pc);
   if (!surface)
     return PR_FALSE;
 
   PRUint32 bmWidth = dragRect.width, bmHeight = dragRect.height;
 
   if (bmWidth == 0 || bmHeight == 0)
     return PR_FALSE;
 
@@ -164,20 +165,20 @@ nsDragService::CreateDragImage(nsIDOMNod
     psdi->sizeDragImage.cx = bmWidth;
     psdi->sizeDragImage.cy = bmHeight;
 
     // Mouse position in center
     if (mScreenX == -1 || mScreenY == -1) {
       psdi->ptOffset.x = (PRUint32)((float)bmWidth/2.0f);
       psdi->ptOffset.y = (PRUint32)((float)bmHeight/2.0f);
     } else {
-      PRInt32 xOffset = mScreenX - dragRect.x;
-      PRInt32 yOffset = mScreenY - dragRect.y;
-      psdi->ptOffset.x = xOffset;
-      psdi->ptOffset.y = yOffset;
+      PRInt32 sx = mScreenX, sy = mScreenY;
+      ConvertToUnscaledDevPixels(pc, &sx, &sy);
+      psdi->ptOffset.x = sx - dragRect.x;
+      psdi->ptOffset.y = sy - dragRect.y;
     }
 
     DeleteDC(hdcSrc);
   }
 
   return psdi->hbmpDragImage != NULL;
 }
 
--- a/widget/src/xpwidgets/nsBaseDragService.cpp
+++ b/widget/src/xpwidgets/nsBaseDragService.cpp
@@ -370,19 +370,21 @@ GetPresShellForContent(nsIDOMNode* aDOMN
   return nsnull;
 }
 
 nsresult
 nsBaseDragService::DrawDrag(nsIDOMNode* aDOMNode,
                             nsIScriptableRegion* aRegion,
                             PRInt32 aScreenX, PRInt32 aScreenY,
                             nsRect* aScreenDragRect,
-                            gfxASurface** aSurface)
+                            gfxASurface** aSurface,
+                            nsPresContext** aPresContext)
 {
   *aSurface = nsnull;
+  *aPresContext = nsnull;
 
   // use a default size, in case of an error.
   aScreenDragRect->x = aScreenX - mImageX;
   aScreenDragRect->y = aScreenY - mImageY;
   aScreenDragRect->width = 20;
   aScreenDragRect->height = 20;
 
   // if a drag image was specified, use that, otherwise, use the source node
@@ -391,35 +393,36 @@ nsBaseDragService::DrawDrag(nsIDOMNode* 
   // get the presshell for the node being dragged. If the drag image is not in
   // a document or has no frame, get the presshell from the source drag node
   nsIPresShell* presShell = GetPresShellForContent(dragNode);
   if (!presShell && mImage)
     presShell = GetPresShellForContent(aDOMNode);
   if (!presShell)
     return NS_ERROR_FAILURE;
 
+  *aPresContext = presShell->GetPresContext();
+
   // check if drag images are disabled
   PRBool enableDragImages = PR_TRUE;
   nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
   if (prefs)
     prefs->GetBoolPref(DRAGIMAGES_PREF, &enableDragImages);
 
   // didn't want an image, so just set the screen rectangle to the frame size
   if (!enableDragImages || !mHasImage) {
     // if a region was specified, set the screen rectangle to the area that
     // the region occupies
     if (aRegion) {
       // the region's coordinates are relative to the root frame
-      nsPresContext* pc = presShell->GetPresContext();
       nsIFrame* rootFrame = presShell->GetRootFrame();
-      if (rootFrame && pc) {
+      if (rootFrame && *aPresContext) {
         nsRect dragRect;
         aRegion->GetBoundingBox(&dragRect.x, &dragRect.y, &dragRect.width, &dragRect.height);
         dragRect.ScaleRoundOut(nsPresContext::AppUnitsPerCSSPixel());
-        dragRect.ScaleRoundOut(1.0 / pc->AppUnitsPerDevPixel());
+        dragRect.ScaleRoundOut(1.0 / (*aPresContext)->AppUnitsPerDevPixel());
 
         nsIntRect screenRect = rootFrame->GetScreenRectExternal();
         aScreenDragRect->SetRect(screenRect.x + dragRect.x, screenRect.y + dragRect.y,
                                  dragRect.width, dragRect.height);
       }
     }
     else {
       // otherwise, there was no region so just set the rectangle to
@@ -447,21 +450,17 @@ nsBaseDragService::DrawDrag(nsIDOMNode* 
 
   // if an custom image was specified, check if it is an image node and draw
   // using the source rather than the displayed image. But if mImage isn't
   // an image, fall through to RenderNode below.
   if (mImage) {
     nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(dragNode);
     // for image nodes, create the drag image from the actual image data
     if (imageLoader) {
-      nsPresContext* pc = presShell->GetPresContext();
-      if (!pc)
-        return NS_ERROR_FAILURE;
-
-      return DrawDragForImage(pc, imageLoader, aScreenX, aScreenY,
+      return DrawDragForImage(*aPresContext, imageLoader, aScreenX, aScreenY,
                               aScreenDragRect, aSurface);
     }
   }
 
   // otherwise, just draw the node
   nsCOMPtr<nsIRegion> clipRegion;
   if (aRegion)
     aRegion->GetRegion(getter_AddRefs(clipRegion));
@@ -564,8 +563,18 @@ nsBaseDragService::DrawDragForImage(nsPr
   context.SetOperator(gfxContext::OPERATOR_CLEAR);
   context.Rectangle(gfxRect(0, 0, destRect.width, destRect.height));
   context.Fill();
 
   gfxRect inRect = gfxRect(srcRect.x, srcRect.y, srcRect.width, srcRect.height);
   gfxRect outRect = gfxRect(destRect.x, destRect.y, destRect.width, destRect.height);
   return img->Draw(*rc, inRect, outRect);
 }
+
+void
+nsBaseDragService::ConvertToUnscaledDevPixels(nsPresContext* aPresContext,
+                                              PRInt32* aScreenX, PRInt32* aScreenY)
+{
+  PRInt32 adj = aPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel();
+  *aScreenX = nsPresContext::CSSPixelsToAppUnits(*aScreenX) / adj;
+  *aScreenY = nsPresContext::CSSPixelsToAppUnits(*aScreenY) / adj;
+}
+
--- a/widget/src/xpwidgets/nsBaseDragService.h
+++ b/widget/src/xpwidgets/nsBaseDragService.h
@@ -89,32 +89,43 @@ protected:
    * for the drag.
    *
    * On return, aScreenDragRect will contain the screen coordinates of the
    * area being dragged. This is used by the platform-specific part of the
    * drag service to determine the drag feedback.
    *
    * If there is no drag image, the returned surface will be null, but
    * aScreenDragRect will still be set to the drag area.
+   *
+   * aPresContext will be set to the nsPresContext used determined from
+   * whichever of mImage or aDOMNode is used.
    */
   nsresult DrawDrag(nsIDOMNode* aDOMNode,
                     nsIScriptableRegion* aRegion,
                     PRInt32 aScreenX, PRInt32 aScreenY,
                     nsRect* aScreenDragRect,
-                    gfxASurface** aSurface);
+                    gfxASurface** aSurface,
+                    nsPresContext **aPresContext);
 
   /**
    * Draw a drag image for an image node. This is called by DrawDrag.
    */
   nsresult DrawDragForImage(nsPresContext* aPresContext,
                             nsIImageLoadingContent* aImageLoader,
                             PRInt32 aScreenX, PRInt32 aScreenY,
                             nsRect* aScreenDragRect,
                             gfxASurface** aSurface);
 
+  /**
+   * Convert aScreenX and aScreenY from CSS pixels into unscaled device pixels.
+   */
+  void
+  ConvertToUnscaledDevPixels(nsPresContext* aPresContext,
+                             PRInt32* aScreenX, PRInt32* aScreenY);
+
   PRPackedBool mCanDrop;
   PRPackedBool mDoingDrag;
   // true if mImage should be used to set a drag image
   PRPackedBool mHasImage;
 
   PRUint32 mDragAction;
   nsSize mTargetSize;
   nsCOMPtr<nsIDOMNode> mSourceNode;