Bug 517487:  [GTK3][Regression] Caret disappears when moving/editing

Backport the original fix into 4.7.1.

Change-Id: I25614b7f33a01cf7c45d59fdd3adc8d6d70c02f0
Signed-off-by: Eric Williams <ericwill@redhat.com>
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Canvas.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Canvas.java
index 2e3a671..c483118 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Canvas.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Canvas.java
@@ -42,7 +42,7 @@
 public class Canvas extends Composite {
 	Caret caret;
 	IME ime;
-	private boolean drawFlag;
+	boolean blink, drawFlag;
 
 Canvas () {}
 
@@ -176,8 +176,14 @@
 		if (isFocus) caret.setFocus ();
 	} else {
 		result = super.gtk_draw (widget, cairo);
-		if (caret != null) {
+		/*
+		 *  blink is needed to be checked as gtk_draw() signals sent from other parts of the canvas
+		 *  can interfere with the blinking state. This will ensure that we are only draw/redrawing the
+		 *  caret when it is intended to. See Bug 517487.
+		 */
+		if (caret != null && blink == true) {
 			drawCaret(widget,cairo);
+			blink = false;
 		}
 	}
 	return result;
@@ -186,7 +192,7 @@
 private void drawCaret (long /*int*/ widget, long /*int*/ cairo) {
 	if(this.isDisposed()) return;
 	if (cairo == 0) error(SWT.ERROR_NO_HANDLES);
-	if (!drawFlag) {
+	if (drawFlag) {
 		Cairo.cairo_save(cairo);
 		if (caret.image != null && !caret.image.isDisposed() && caret.image.mask == 0) {
 			Cairo.cairo_set_source_rgb(cairo, 1, 1, 1);
@@ -217,9 +223,9 @@
 			}
 		Cairo.cairo_fill(cairo);
 		Cairo.cairo_restore(cairo);
-		drawFlag = true;
-	} else {
 		drawFlag = false;
+	} else {
+		drawFlag = true;
 		}
 	return;
 }
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Caret.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Caret.java
index 0e4a13a..c193c89 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Caret.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Caret.java
@@ -584,6 +584,10 @@
  */
 public void setVisible (boolean visible) {
 	checkWidget();
+	Canvas canvas = getParent();
+	canvas.blink = true;
+	canvas.drawFlag = visible;
+	display.resetCaretTiming();
 	if (visible == isVisible) return;
 	isVisible = visible;
 	if (!isFocusCaret ()) return;
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java
index 43cffdc..c2c1974 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java
@@ -5187,6 +5187,11 @@
 	if (currentCaret.blinkCaret()) {
 		int blinkRate = currentCaret.blinkRate;
 		if (blinkRate == 0) return 0;
+		/*
+		 * blink is set to true so that when gtk_draw() is called, we know it is the correct
+		 * state to draw/redraw the caret. See Bug 517487.
+		 */
+		currentCaret.getParent().blink = true;
 		caretId = OS.g_timeout_add (blinkRate, caretProc, 0);
 	} else {
 		currentCaret = null;
@@ -5194,6 +5199,17 @@
 	return 0;
 }
 
+/*
+ * Causes the caretProc method timing interval to be reset. This is useful as the blink rate
+ * intervals should always reset when the caret is moved. See Bug 517487.
+ */
+void resetCaretTiming() {
+	if (caretId != 0) {
+		OS.g_source_remove(caretId);
+		caretId = OS.g_timeout_add(currentCaret.blinkRate, caretProc, 0);
+	}
+}
+
 long /*int*/ sizeAllocateProc (long /*int*/ handle, long /*int*/ arg0, long /*int*/ user_data) {
 	Widget widget = getWidget (user_data);
 	if (widget == null) return 0;