Bug 577878 - [GTK] Adjust coordinate system when rendering Tree/Table drag image

Some SWT.EraseItem/SWT.PaintItem completely ignore `event.getBounds()`
and instead use `event.item.getBounds()`, which returns item rect in
control instead of item rect in current paint GC.

On example of it is Eclipse's
`org.eclipse.jface.viewers.StyledCellLabelProvider#paint`

I would say that this is a bug in Eclipse.

However, macOS has a workaround for it, where it adjusts GC's coordinate
system so that GC's coordinates match Control's coordinates (see
`transform.translateXBy()` in `Table.drawInteriorWithFrame_inView()`).
This patch adds a similar workaround for GTK.

Change-Id: I3fed4aacdc28a389e36453f7ede5e62291a95278
Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
Reviewed-on: https://git.eclipse.org/r/c/platform/eclipse.platform.swt/+/192263
Tested-by: Platform Bot <platform-bot@eclipse.org>
Reviewed-by: Alexander Kurtakov <akurtako@redhat.com>
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java
index 0f41e63..cc67a0e 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java
@@ -2964,6 +2964,7 @@
 
 	GdkRectangle rendererRect = new GdkRectangle ();
 	GdkRectangle columnRect = new GdkRectangle ();
+	int y_offset;
 	{
 		/*
 		 * SWT creates multiple renderers (kind of sub-columns) per column.
@@ -2972,7 +2973,7 @@
 		 * painted renderer. However, for SWT.EraseItem and SWT.PaintItem,
 		 * SWT wants entire column's area along with the event. There's api
 		 * 'gtk_tree_view_get_background_area()' but it calculates item's
-		 * rect in control, which will be have wrong Y if item is rendered
+		 * rect in control, which will have wrong Y if item is rendered
 		 * separately (for example, for drag image).
 		 * The workaround is to take X range from api and Y range from argument.
 		 */
@@ -2982,7 +2983,8 @@
 		GTK.gtk_tree_view_get_background_area (handle, path, columnHandle, columnRect);
 		GTK.gtk_tree_path_free (path);
 
-		columnRect.y = rendererRect.y;
+		y_offset = columnRect.y - rendererRect.y;
+		columnRect.y -= y_offset;
 	}
 
 	if (item != null) {
@@ -3059,14 +3061,27 @@
 					gc.setClipping(rect2.x, rect2.y, rect2.width, rect2.height);
 
 				}
+
+				// SWT.PaintItem/SWT.EraseItem often expect that event.y matches
+				// what 'event.item.getBounds()' returns. The workaround is to
+				// adjust coordinate system temporarily.
 				Event event = new Event ();
-				event.item = item;
-				event.index = columnIndex;
-				event.gc = gc;
-				event.detail = drawState;
-				Rectangle eventRect = new Rectangle (rect.x, rect.y, rect.width, rect.height);
-				event.setBounds (DPIUtil.autoScaleDown (eventRect));
-				sendEvent (SWT.EraseItem, event);
+				try {
+					Rectangle eventRect = new Rectangle (rect.x, rect.y, rect.width, rect.height);
+
+					eventRect.y += y_offset;
+					Cairo.cairo_translate (cr, 0, -y_offset);
+
+					event.item = item;
+					event.index = columnIndex;
+					event.gc = gc;
+					event.detail = drawState;
+					event.setBounds (DPIUtil.autoScaleDown (eventRect));
+					sendEvent (SWT.EraseItem, event);
+				} finally {
+					Cairo.cairo_translate (cr, 0, y_offset);
+				}
+
 				drawForegroundRGBA = null;
 				drawState = event.doit ? event.detail : 0;
 				drawFlags &= ~(GTK.GTK_CELL_RENDERER_FOCUSED | GTK.GTK_CELL_RENDERER_SELECTED);
@@ -3169,14 +3184,26 @@
 				// Caveat: rect2 is necessary because GC#setClipping(Rectangle) got broken by bug 446075
 				gc.setClipping(rect2.x, rect2.y, rect2.width, rect2.height);
 
+				// SWT.PaintItem/SWT.EraseItem often expect that event.y matches
+				// what 'event.item.getBounds()' returns. The workaround is to
+				// adjust coordinate system temporarily.
 				Event event = new Event ();
-				event.item = item;
-				event.index = columnIndex;
-				event.gc = gc;
-				Rectangle eventRect = new Rectangle (rect.x + contentX [0], rect.y, contentWidth [0], rect.height);
-				event.setBounds (DPIUtil.autoScaleDown (eventRect));
-				event.detail = drawState;
-				sendEvent (SWT.PaintItem, event);
+				try {
+					Rectangle eventRect = new Rectangle (rect.x + contentX [0], rect.y, contentWidth [0], rect.height);
+
+					eventRect.y += y_offset;
+					Cairo.cairo_translate (cr, 0, -y_offset);
+
+					event.item = item;
+					event.index = columnIndex;
+					event.gc = gc;
+					event.detail = drawState;
+					event.setBounds (DPIUtil.autoScaleDown (eventRect));
+					sendEvent (SWT.PaintItem, event);
+				} finally {
+					Cairo.cairo_translate (cr, 0, y_offset);
+				}
+
 				gc.dispose();
 			}
 		}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java
index 215f2ee..cab5bf8 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java
@@ -3171,6 +3171,7 @@
 
 	GdkRectangle rendererRect = new GdkRectangle ();
 	GdkRectangle columnRect = new GdkRectangle ();
+	int y_offset;
 	{
 		/*
 		 * SWT creates multiple renderers (kind of sub-columns) per column.
@@ -3179,7 +3180,7 @@
 		 * painted renderer. However, for SWT.EraseItem and SWT.PaintItem,
 		 * SWT wants entire column's area along with the event. There's api
 		 * 'gtk_tree_view_get_background_area()' but it calculates item's
-		 * rect in control, which will be have wrong Y if item is rendered
+		 * rect in control, which will have wrong Y if item is rendered
 		 * separately (for example, for drag image).
 		 * The workaround is to take X range from api and Y range from argument.
 		 */
@@ -3189,7 +3190,8 @@
 		GTK.gtk_tree_view_get_background_area (handle, path, columnHandle, columnRect);
 		GTK.gtk_tree_path_free (path);
 
-		columnRect.y = rendererRect.y;
+		y_offset = columnRect.y - rendererRect.y;
+		columnRect.y -= y_offset;
 	}
 
 	if (item != null) {
@@ -3271,14 +3273,27 @@
 					// Caveat: rect2 is necessary because GC#setClipping(Rectangle) got broken by bug 446075
 					gc.setClipping(rect2.x, rect2.y, rect2.width, rect2.height);
 				}
+
+				// SWT.PaintItem/SWT.EraseItem often expect that event.y matches
+				// what 'event.item.getBounds()' returns. The workaround is to
+				// adjust coordinate system temporarily.
 				Event event = new Event ();
-				event.item = item;
-				event.index = columnIndex;
-				event.gc = gc;
-				Rectangle eventRect = new Rectangle (rect.x, rect.y, rect.width, rect.height);
-				event.setBounds (DPIUtil.autoScaleDown (eventRect));
-				event.detail = drawState;
-				sendEvent (SWT.EraseItem, event);
+				try {
+					Rectangle eventRect = new Rectangle (rect.x, rect.y, rect.width, rect.height);
+
+					eventRect.y += y_offset;
+					Cairo.cairo_translate (cr, 0, -y_offset);
+
+					event.item = item;
+					event.index = columnIndex;
+					event.gc = gc;
+					event.detail = drawState;
+					event.setBounds (DPIUtil.autoScaleDown (eventRect));
+					sendEvent (SWT.EraseItem, event);
+				} finally {
+					Cairo.cairo_translate (cr, 0, y_offset);
+				}
+
 				drawForegroundRGBA = null;
 				drawState = event.doit ? event.detail : 0;
 				drawFlags &= ~(GTK.GTK_CELL_RENDERER_FOCUSED | GTK.GTK_CELL_RENDERER_SELECTED);
@@ -3381,14 +3396,26 @@
 				// Caveat: rect2 is necessary because GC#setClipping(Rectangle) got broken by bug 446075
 				gc.setClipping(rect2.x, rect2.y, rect2.width, rect2.height);
 
+				// SWT.PaintItem/SWT.EraseItem often expect that event.y matches
+				// what 'event.item.getBounds()' returns. The workaround is to
+				// adjust coordinate system temporarily.
 				Event event = new Event ();
-				event.item = item;
-				event.index = columnIndex;
-				event.gc = gc;
-				Rectangle eventRect = new Rectangle (rect.x + contentX [0], rect.y, contentWidth [0], rect.height);
-				event.setBounds (DPIUtil.autoScaleDown (eventRect));
-				event.detail = drawState;
-				sendEvent(SWT.PaintItem, event);
+				try {
+					Rectangle eventRect = new Rectangle (rect.x + contentX [0], rect.y, contentWidth [0], rect.height);
+
+					eventRect.y += y_offset;
+					Cairo.cairo_translate (cr, 0, -y_offset);
+
+					event.item = item;
+					event.index = columnIndex;
+					event.gc = gc;
+					event.detail = drawState;
+					event.setBounds (DPIUtil.autoScaleDown (eventRect));
+					sendEvent (SWT.PaintItem, event);
+				} finally {
+					Cairo.cairo_translate (cr, 0, y_offset);
+				}
+
 				gc.dispose();
 			}
 		}