Bug 577878 - [GTK] Table/Tree with SWT.PaintItem don't show rows when dragging them

Rendering drag images for SWT.PaintItem case was "temporarily" disabled
for a very long time, since commit
    5a8028d0 by Steve Northover at 2008-04-29 07:56:43
    disable drag images in table and tree for custom draw

It didn't work well, because instead of using `background_area`
callback argument, it requested the value via
`gtk_tree_view_get_background_area()`, which returns screen coordinates
in Tree/Table. However, in case of rendering a drag image, an isolated
row is rendered, and coordinates are different.

Change-Id: I816fcd32ad6a7fee92f93ac2ff600f7cac444989
Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
Reviewed-on: https://git.eclipse.org/r/c/platform/eclipse.platform.swt/+/188983
Tested-by: Platform Bot <platform-bot@eclipse.org>
Reviewed-by: Alexander Kurtakov <akurtako@redhat.com>
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/TableDragSourceEffect.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/TableDragSourceEffect.java
index af900f9..ac4df82 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/TableDragSourceEffect.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/TableDragSourceEffect.java
@@ -86,8 +86,6 @@
 		dragSourceImage = null;
 
 		Table table = (Table) control;
-		//TEMPORARY CODE
-		if (table.isListening(SWT.EraseItem) || table.isListening (SWT.PaintItem)) return null;
 
 		/*
 		* Bug in GTK.  gtk_tree_selection_get_selected_rows() segmentation faults
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/TreeDragSourceEffect.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/TreeDragSourceEffect.java
index d48a177..9163528 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/TreeDragSourceEffect.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/TreeDragSourceEffect.java
@@ -85,8 +85,6 @@
 		dragSourceImage = null;
 
 		Tree tree = (Tree) control;
-		//TEMPORARY CODE
-		if (tree.isListening(SWT.EraseItem) || tree.isListening (SWT.PaintItem)) return null;
 
 		/*
 		 * Bug in GTK.  gtk_tree_selection_get_selected_rows() segmentation faults
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GdkRectangle.java b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GdkRectangle.java
index 4e547d0..7623e32 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GdkRectangle.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GdkRectangle.java
@@ -14,6 +14,7 @@
  *******************************************************************************/
 package org.eclipse.swt.internal.gtk;
 
+import org.eclipse.swt.graphics.Rectangle;
 
 public class GdkRectangle {
 	/** @field cast=(gint) */
@@ -36,4 +37,8 @@
 	public String toString() {
 		return "GdkRectangle {" + x + ", " + y + ", " + width + ", " + height + "}"; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
 	}
+
+	public Rectangle toRectangle() {
+		return new Rectangle(x, y, width, height);
+	}
 }
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 1e0ec9e..35a1834 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
@@ -2961,6 +2961,10 @@
 			}
 		}
 	}
+
+	GdkRectangle backround_area_rect = new GdkRectangle ();
+	OS.memmove (backround_area_rect, background_area, GdkRectangle.sizeof);
+
 	if (item != null) {
 		if (GTK.GTK_IS_CELL_RENDERER_TOGGLE (cell) || (columnIndex != 0 || (style & SWT.CHECK) == 0)) {
 			drawFlags = (int)flags;
@@ -2980,10 +2984,7 @@
 				if ((flags & GTK.GTK_CELL_RENDERER_FOCUSED) != 0) drawState |= SWT.FOCUSED;
 			}
 
-			GdkRectangle rect = new GdkRectangle ();
-			long path = GTK.gtk_tree_model_get_path (modelHandle, iter);
-			GTK.gtk_tree_view_get_background_area (handle, path, columnHandle, rect);
-			GTK.gtk_tree_path_free (path);
+			Rectangle rect = backround_area_rect.toRectangle ();
 			if ((drawState & SWT.SELECTED) == 0) {
 				if ((state & PARENT_BACKGROUND) != 0 || backgroundImage != null) {
 					Control control = findBackgroundControl ();
@@ -3029,11 +3030,11 @@
 				if (cr != 0) {
 					GdkRectangle r = new GdkRectangle();
 					GDK.gdk_cairo_get_clip_rectangle(cr, r);
-					Rectangle rect2 = DPIUtil.autoScaleDown(new Rectangle(rect.x, r.y, r.width, r.height));
+					Rectangle rect2 = DPIUtil.autoScaleDown(rect);
 					// Caveat: rect2 is necessary because GC#setClipping(Rectangle) got broken by bug 446075
 					gc.setClipping(rect2.x, rect2.y, rect2.width, rect2.height);
 				} else {
-					Rectangle rect2 = DPIUtil.autoScaleDown(new Rectangle(rect.x, rect.y, rect.width, rect.height));
+					Rectangle rect2 = DPIUtil.autoScaleDown(rect);
 					// Caveat: rect2 is necessary because GC#setClipping(Rectangle) got broken by bug 446075
 					gc.setClipping(rect2.x, rect2.y, rect2.width, rect2.height);
 
@@ -3074,9 +3075,7 @@
 	if ((drawState & SWT.BACKGROUND) != 0 && (drawState & SWT.SELECTED) == 0) {
 		GC gc = getGC(cr);
 		gc.setBackground (item.getBackground (columnIndex));
-		GdkRectangle rect = new GdkRectangle ();
-		OS.memmove (rect, background_area, GdkRectangle.sizeof);
-		gc.fillRectangle(DPIUtil.autoScaleDown(new Rectangle(rect.x, rect.y, rect.width, rect.height)));
+		gc.fillRectangle (DPIUtil.autoScaleDown (backround_area_rect.toRectangle ()));
 		gc.dispose ();
 	}
 	if ((drawState & SWT.FOREGROUND) != 0 || GTK.GTK_IS_CELL_RENDERER_TOGGLE (cell)) {
@@ -3102,10 +3101,7 @@
 		if (GTK.GTK_IS_CELL_RENDERER_TEXT (cell)) {
 			if (hooks (SWT.PaintItem)) {
 				if (wasSelected) drawState |= SWT.SELECTED;
-				GdkRectangle rect = new GdkRectangle ();
-				long path = GTK.gtk_tree_model_get_path (modelHandle, iter);
-				GTK.gtk_tree_view_get_background_area (handle, path, columnHandle, rect);
-				GTK.gtk_tree_path_free (path);
+				Rectangle rect = backround_area_rect.toRectangle ();
 				ignoreSize = true;
 				int [] contentX = new int [1], contentWidth = new int [1];
 				gtk_cell_renderer_get_preferred_size (cell, handle, contentWidth, null);
@@ -3149,7 +3145,7 @@
 				gc.setFont (item.getFont (columnIndex));
 				if ((style & SWT.MIRRORED) != 0) rect.x = getClientWidth () - rect.width - rect.x;
 
-				Rectangle rect2 = DPIUtil.autoScaleDown(new Rectangle(rect.x, rect.y, rect.width, rect.height));
+				Rectangle rect2 = DPIUtil.autoScaleDown(rect);
 				// Caveat: rect2 is necessary because GC#setClipping(Rectangle) got broken by bug 446075
 				gc.setClipping(rect2.x, rect2.y, rect2.width, rect2.height);
 
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 081a474..a5fda90 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
@@ -3168,6 +3168,10 @@
 			}
 		}
 	}
+
+	GdkRectangle backround_area_rect = new GdkRectangle ();
+	OS.memmove (backround_area_rect, background_area, GdkRectangle.sizeof);
+
 	if (item != null) {
 		if (GTK.GTK_IS_CELL_RENDERER_TOGGLE (cell) || ( columnIndex != 0 || (style & SWT.CHECK) == 0)) {
 			drawFlags = (int)flags;
@@ -3187,10 +3191,7 @@
 				if ((flags & GTK.GTK_CELL_RENDERER_FOCUSED) != 0) drawState |= SWT.FOCUSED;
 			}
 
-			GdkRectangle rect = new GdkRectangle ();
-			long path = GTK.gtk_tree_model_get_path (modelHandle, iter);
-			GTK.gtk_tree_view_get_background_area (handle, path, columnHandle, rect);
-			GTK.gtk_tree_path_free (path);
+			Rectangle rect = backround_area_rect.toRectangle ();
 			// Use the x and width information from the Cairo context. See bug 535124.
 			if (cr != 0) {
 				GdkRectangle r2 = new GdkRectangle ();
@@ -3243,10 +3244,10 @@
 				if (cr != 0) {
 					// Use the original rectangle, not the Cairo clipping for the y, width, and height values.
 					// See bug 535124.
-					Rectangle rect2 = DPIUtil.autoScaleDown(new Rectangle(rect.x, rect.y, rect.width, rect.height));
+					Rectangle rect2 = DPIUtil.autoScaleDown(rect);
 					gc.setClipping(rect2.x, rect2.y, rect2.width, rect2.height);
 				} else {
-					Rectangle rect2 = DPIUtil.autoScaleDown(new Rectangle(rect.x, rect.y, rect.width, rect.height));
+					Rectangle rect2 = DPIUtil.autoScaleDown(rect);
 					// Caveat: rect2 is necessary because GC#setClipping(Rectangle) got broken by bug 446075
 					gc.setClipping(rect2.x, rect2.y, rect2.width, rect2.height);
 				}
@@ -3275,12 +3276,9 @@
 		}
 	}
 	if ((drawState & SWT.BACKGROUND) != 0 && (drawState & SWT.SELECTED) == 0) {
-
 		GC gc = getGC(cr);
 		gc.setBackground (item.getBackground (columnIndex));
-		GdkRectangle rect = new GdkRectangle ();
-		OS.memmove (rect, background_area, GdkRectangle.sizeof);
-		gc.fillRectangle(DPIUtil.autoScaleDown(new Rectangle(rect.x, rect.y, rect.width, rect.height)));
+		gc.fillRectangle (DPIUtil.autoScaleDown (backround_area_rect.toRectangle ()));
 		gc.dispose ();
 	}
 	if ((drawState & SWT.FOREGROUND) != 0 || GTK.GTK_IS_CELL_RENDERER_TOGGLE (cell)) {
@@ -3306,10 +3304,7 @@
 		if (GTK.GTK_IS_CELL_RENDERER_TEXT (cell)) {
 			if (hooks (SWT.PaintItem)) {
 				if (wasSelected) drawState |= SWT.SELECTED;
-				GdkRectangle rect = new GdkRectangle ();
-				long path = GTK.gtk_tree_model_get_path (modelHandle, iter);
-				GTK.gtk_tree_view_get_background_area (handle, path, columnHandle, rect);
-				GTK.gtk_tree_path_free (path);
+				Rectangle rect = backround_area_rect.toRectangle ();
 				ignoreSize = true;
 				int [] contentX = new int [1], contentWidth = new int [1];
 				gtk_cell_renderer_get_preferred_size (cell, handle, contentWidth, null);
@@ -3339,7 +3334,7 @@
 					/* indent */
 					GdkRectangle rect3 = new GdkRectangle ();
 					GTK.gtk_widget_realize (handle);
-					path = GTK.gtk_tree_model_get_path (modelHandle, iter);
+					long path = GTK.gtk_tree_model_get_path (modelHandle, iter);
 					GTK.gtk_tree_view_get_cell_area (handle, path, columnHandle, rect3);
 					GTK.gtk_tree_path_free (path);
 					contentX[0] += rect3.x;
@@ -3362,7 +3357,7 @@
 					rect.x = getClientWidth () - rect.width - rect.x;
 				}
 
-				Rectangle rect2 = DPIUtil.autoScaleDown(new Rectangle(rect.x, rect.y, rect.width, rect.height));
+				Rectangle rect2 = DPIUtil.autoScaleDown(rect);
 				// Caveat: rect2 is necessary because GC#setClipping(Rectangle) got broken by bug 446075
 				gc.setClipping(rect2.x, rect2.y, rect2.width, rect2.height);
 
diff --git a/tests/org.eclipse.swt.tests/ManualTests/org/eclipse/swt/tests/manual/Bug577878_GTK_TreeTable_NoDragImageWithPaintItem.java b/tests/org.eclipse.swt.tests/ManualTests/org/eclipse/swt/tests/manual/Bug577878_GTK_TreeTable_NoDragImageWithPaintItem.java
new file mode 100644
index 0000000..2d65286
--- /dev/null
+++ b/tests/org.eclipse.swt.tests/ManualTests/org/eclipse/swt/tests/manual/Bug577878_GTK_TreeTable_NoDragImageWithPaintItem.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * Copyright (c) 2021, 2022 Syntevo and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Syntevo - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.swt.tests.manual;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.dnd.*;
+import org.eclipse.swt.layout.*;
+import org.eclipse.swt.widgets.*;
+
+public final class Bug577878_GTK_TreeTable_NoDragImageWithPaintItem {
+	static final int NUM_ROWS = 100;
+	static final int NUM_COLS = 3;
+
+	static String makeItemText(int iRow, int iCol) {
+		return "Item#" + iRow + ":" + iCol;
+	}
+
+	static void createTable(Composite parent, boolean isCustomPaint) {
+		Table control = new Table (parent, SWT.BORDER | SWT.V_SCROLL);
+		control.setLayoutData (new GridData (SWT.FILL, SWT.FILL, true, true));
+
+		control.setHeaderVisible (true);
+		for (int iColumn = 0; iColumn < 3; iColumn++) {
+			TableColumn column = new TableColumn (control, 0);
+			column.setText ("Col#" + iColumn);
+			column.setWidth (120);
+		}
+
+		control.setItemCount (NUM_ROWS);
+
+		if (isCustomPaint) {
+			control.addListener (SWT.MeasureItem, e -> {
+				e.width = 120;
+				e.height = 20;
+			});
+
+			control.addListener (SWT.PaintItem, e -> {
+				TableItem item = (TableItem) e.item;
+				int iRow = item.getParent ().indexOf (item);
+				String text = makeItemText(iRow, e.index);
+				e.gc.drawString (text, e.x, e.y, true);
+			});
+		} else {
+			for (int iRow = 0; iRow < NUM_ROWS; iRow++) {
+				for (int iCol = 0; iCol < NUM_COLS; iCol++) {
+					control.getItem (iRow).setText (iCol, makeItemText(iRow, iCol));
+				}
+			}
+		}
+
+		DragSource dragSource = new DragSource (control, DND.DROP_MOVE | DND.DROP_COPY);
+		dragSource.setTransfer (TextTransfer.getInstance ());
+		dragSource.addDragListener (new DragSourceAdapter ());
+	}
+
+	static void createTree(Composite parent, boolean isCustomPaint) {
+		Tree control = new Tree (parent, SWT.BORDER | SWT.V_SCROLL);
+		control.setLayoutData (new GridData (SWT.FILL, SWT.FILL, true, true));
+
+		control.setHeaderVisible (true);
+		for (int iColumn = 0; iColumn < 3; iColumn++) {
+			TreeColumn column = new TreeColumn (control, 0);
+			column.setText ("Col#" + iColumn);
+			column.setWidth (120);
+		}
+
+		control.setItemCount (NUM_ROWS);
+
+		if (isCustomPaint) {
+			control.addListener (SWT.MeasureItem, e -> {
+				e.width = 120;
+				e.height = 20;
+			});
+
+			control.addListener (SWT.PaintItem, e -> {
+				TreeItem item = (TreeItem) e.item;
+				TreeItem parentItem = item.getParentItem ();
+				int iRow = (parentItem != null) ? parentItem.indexOf (item) : item.getParent ().indexOf (item);
+				String text = makeItemText(iRow, e.index);
+				e.gc.drawString (text, e.x, e.y, true);
+			});
+		} else {
+			for (int iRow = 0; iRow < NUM_ROWS; iRow++) {
+				for (int iCol = 0; iCol < NUM_COLS; iCol++) {
+					control.getItem (iRow).setText (iCol, makeItemText(iRow, iCol));
+				}
+			}
+		}
+
+		TreeItem child = new TreeItem (control.getItem(1), 0);
+		if (!isCustomPaint) {
+			for (int iCol = 0; iCol < NUM_COLS; iCol++) {
+				child.setText (iCol, makeItemText(0, iCol));
+			}
+		}
+
+		DragSource dragSource = new DragSource (control, DND.DROP_MOVE | DND.DROP_COPY);
+		dragSource.setTransfer (TextTransfer.getInstance ());
+		dragSource.addDragListener (new DragSourceAdapter ());
+	}
+
+	public static void main(String[] args) {
+		final Display display = new Display();
+		final Shell shell = new Shell(display);
+		shell.setLayout (new GridLayout (1, true));
+
+		Label hint = new Label(shell, 0);
+		hint.setText (
+			"1) Run on GTK\n" +
+			"2) Drag rows from regular Table/Tree - there will be a drag image\n" +
+			"3) Bug 577878: Table/Tree with SWT.PaintItem do not have drag image\n"
+		);
+
+		Composite composite = new Composite (shell, 0);
+		composite.setLayoutData (new GridData (SWT.FILL, SWT.FILL, true, true));
+		composite.setLayout (new GridLayout (2, true));
+
+		new Label(composite, 0).setText ("Table");
+		new Label(composite, 0).setText ("Table+PaintItem");
+		createTable(composite, false);
+		createTable(composite, true);
+
+		new Label(composite, 0).setText ("Tree");
+		new Label(composite, 0).setText ("Tree+PaintItem");
+		createTree(composite, false);
+		createTree(composite, true);
+
+		shell.setSize (800, 800);
+		shell.open();
+
+		while (!shell.isDisposed()) {
+			if (!display.readAndDispatch()) {
+				display.sleep();
+			}
+		}
+
+		display.dispose();
+	}
+}