SideBySide: Make cursor blink rate customizable

Now, that we migrated diff user preferences to Git backend, we can
easily introduce one of the most wanted customization option: blinking
cursor.

Set default to 0: no blinking cursor, to be backwards compatible.

Change-Id: I40a3a43ea7636508f45badc4946d2f2455adb192
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 6920c4c..329c4cd 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -1259,6 +1259,7 @@
     "ignore_whitespace": "IGNORE_ALL",
     "intraline_difference": true,
     "line_length": 100,
+    "cursor_blink_rate": 500,
     "show_tabs": true,
     "show_whitespace_errors": true,
     "syntax_highlighting": true,
@@ -1288,6 +1289,7 @@
     "ignore_whitespace": "IGNORE_ALL",
     "intraline_difference": true,
     "line_length": 100,
+    "cursor_blink_rate": 500,
     "show_line_endings": true,
     "show_tabs": true,
     "show_whitespace_errors": true,
@@ -1672,6 +1674,9 @@
 Whether intraline differences should be highlighted.
 |`line_length`                 ||
 Number of characters that should be displayed in one line.
+|`cursor_blink_rate`           ||
+Half-period in milliseconds used for cursor blinking.
+Setting it to 0 disables cursor blinking.
 |`manual_review`               |not set if `false`|
 Whether the 'Reviewed' flag should not be set automatically on a patch
 when it is viewed.
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/DiffPreferencesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/DiffPreferencesIT.java
index 0dea558..38687a4 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/DiffPreferencesIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/DiffPreferencesIT.java
@@ -47,6 +47,7 @@
     assertThat(o.context).isEqualTo(d.context);
     assertThat(o.tabSize).isEqualTo(d.tabSize);
     assertThat(o.lineLength).isEqualTo(d.lineLength);
+    assertThat(o.cursorBlinkRate).isEqualTo(d.cursorBlinkRate);
     assertThat(o.expandAllComments).isNull();
     assertThat(o.intralineDifference).isEqualTo(d.intralineDifference);
     assertThat(o.manualReview).isNull();
@@ -74,6 +75,7 @@
     i.context *= -1;
     i.tabSize *= -1;
     i.lineLength *= -1;
+    i.cursorBlinkRate = 500;
     i.theme = Theme.MIDNIGHT;
     i.ignoreWhitespace = Whitespace.IGNORE_ALL;
     i.expandAllComments ^= true;
@@ -101,6 +103,7 @@
     assertThat(o.context).isEqualTo(i.context);
     assertThat(o.tabSize).isEqualTo(i.tabSize);
     assertThat(o.lineLength).isEqualTo(i.lineLength);
+    assertThat(o.cursorBlinkRate).isEqualTo(i.cursorBlinkRate);
     assertThat(o.expandAllComments).isEqualTo(i.expandAllComments);
     assertThat(o.intralineDifference).isNull();
     assertThat(o.manualReview).isEqualTo(i.manualReview);
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java
index c488bd7..b52a439 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/client/DiffPreferencesInfo.java
@@ -42,6 +42,7 @@
   public Integer context;
   public Integer tabSize;
   public Integer lineLength;
+  public Integer cursorBlinkRate;
   public Boolean expandAllComments;
   public Boolean intralineDifference;
   public Boolean manualReview;
@@ -65,6 +66,7 @@
     i.context = DEFAULT_CONTEXT;
     i.tabSize = DEFAULT_TAB_SIZE;
     i.lineLength = DEFAULT_LINE_LENGTH;
+    i.cursorBlinkRate = 0;
     i.ignoreWhitespace = Whitespace.IGNORE_NONE;
     i.theme = Theme.DEFAULT;
     i.expandAllComments = false;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java
index 9a8e90c..8db8d21 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/DiffPreferences.java
@@ -28,6 +28,7 @@
     p.ignoreWhitespace(in.ignoreWhitespace);
     p.tabSize(in.tabSize);
     p.lineLength(in.lineLength);
+    p.cursorBlinkRate(in.cursorBlinkRate);
     p.context(in.context);
     p.intralineDifference(in.intralineDifference);
     p.showLineEndings(in.showLineEndings);
@@ -52,6 +53,7 @@
     p.context = context();
     p.tabSize = tabSize();
     p.lineLength = lineLength();
+    p.cursorBlinkRate = cursorBlinkRate();
     p.expandAllComments = expandAllComments();
     p.intralineDifference = intralineDifference();
     p.manualReview = manualReview();
@@ -105,6 +107,10 @@
     return get("line_length", 100);
   }
 
+  public final int cursorBlinkRate() {
+    return get("cursor_blink_rate", 0);
+  }
+
   public final boolean showLineNumbers() {
     return !hideLineNumbers();
   }
@@ -116,6 +122,7 @@
   public final native void tabSize(int t) /*-{ this.tab_size = t }-*/;
   public final native void lineLength(int c) /*-{ this.line_length = c }-*/;
   public final native void context(int c) /*-{ this.context = c }-*/;
+  public final native void cursorBlinkRate(int r) /*-{ this.cursor_blink_rate = r }-*/;
   public final native void intralineDifference(boolean i) /*-{ this.intraline_difference = i }-*/;
   public final native void showLineEndings(boolean s) /*-{ this.show_line_endings = s }-*/;
   public final native void showTabs(boolean s) /*-{ this.show_tabs = s }-*/;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.java
index 36d6514..f2f468e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.java
@@ -86,6 +86,7 @@
   @UiField NpIntTextBox tabWidth;
   @UiField NpIntTextBox lineLength;
   @UiField NpIntTextBox context;
+  @UiField NpIntTextBox cursorBlinkRate;
   @UiField CheckBox contextEntireFile;
   @UiField ToggleButton intralineDifference;
   @UiField ToggleButton syntaxHighlighting;
@@ -173,6 +174,7 @@
       lineLength.setEnabled(true);
       lineLength.setIntValue(prefs.lineLength());
     }
+    cursorBlinkRate.setIntValue(prefs.cursorBlinkRate());
     syntaxHighlighting.setValue(prefs.syntaxHighlighting());
     whitespaceErrors.setValue(prefs.showWhitespaceErrors());
     showTabs.setValue(prefs.showTabs());
@@ -343,6 +345,20 @@
     }
   }
 
+  @UiHandler("cursorBlinkRate")
+  void onCursoBlinkRate(ValueChangeEvent<String> e) {
+    String v = e.getValue();
+    if (v != null && v.length() > 0) {
+      // A negative value hides the cursor entirely:
+      // don't let user shoot himself in the foot.
+      prefs.cursorBlinkRate(Math.max(0, Integer.parseInt(v)));
+      view.getCmFromSide(DisplaySide.A).setOption("cursorBlinkRate",
+          prefs.cursorBlinkRate());
+      view.getCmFromSide(DisplaySide.B).setOption("cursorBlinkRate",
+          prefs.cursorBlinkRate());
+    }
+  }
+
   @UiHandler("showTabs")
   void onShowTabs(ValueChangeEvent<Boolean> e) {
     prefs.showTabs(e.getValue());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.ui.xml
index da744c4..057d20d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/PreferencesBox.ui.xml
@@ -196,6 +196,12 @@
           or <g:CheckBox ui:field='contextEntireFile'>entire file</g:CheckBox></ui:msg></td>
       </tr>
       <tr>
+        <th><ui:msg>Cursor Blink Rate</ui:msg></th>
+        <td><x:NpIntTextBox ui:field='cursorBlinkRate'
+            visibleLength='4'
+            alignment='RIGHT'/></td>
+      </tr>
+      <tr>
         <th><ui:msg>Intraline Difference</ui:msg></th>
         <td><g:ToggleButton ui:field='intralineDifference'>
           <g:upFace><ui:msg>Hide</ui:msg></g:upFace>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
index eb902ad..8b47835 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/SideBySide.java
@@ -636,7 +636,7 @@
       Element parent) {
     return CodeMirror.create(parent, Configuration.create()
       .set("readOnly", true)
-      .set("cursorBlinkRate", 0)
+      .set("cursorBlinkRate", prefs.cursorBlinkRate())
       .set("cursorHeight", 0.85)
       .set("lineNumbers", prefs.showLineNumbers())
       .set("tabSize", prefs.tabSize())