Merge "Add ",codemirror" view of a file"
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
index c8b1e48..56e6f13 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
@@ -69,6 +69,7 @@
 import com.google.gerrit.client.changes.QueryScreen;
 import com.google.gerrit.client.dashboards.DashboardInfo;
 import com.google.gerrit.client.dashboards.DashboardList;
+import com.google.gerrit.client.diff.CodeMirrorDemo;
 import com.google.gerrit.client.groups.GroupApi;
 import com.google.gerrit.client.groups.GroupInfo;
 import com.google.gerrit.client.patches.PatchScreen;
@@ -567,6 +568,8 @@
                 top, //
                 baseId //
             );
+          } else if ("codemirror".equals(panel)) {
+            return new CodeMirrorDemo(baseId, id.getParentKey(), id.get());
           }
         }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CodeMirrorDemo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CodeMirrorDemo.java
new file mode 100644
index 0000000..d0398daa
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CodeMirrorDemo.java
@@ -0,0 +1,111 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.client.diff;
+
+import com.google.gerrit.client.rpc.ScreenLoadCallback;
+import com.google.gerrit.client.ui.Screen;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gwt.event.logical.shared.ResizeEvent;
+import com.google.gwt.event.logical.shared.ResizeHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.FlowPanel;
+
+import net.codemirror.lib.CodeMirror;
+import net.codemirror.lib.Configuration;
+
+public class CodeMirrorDemo extends Screen {
+  private static final int HEADER_FOOTER = 60 + 15 * 2 + 38;
+  private final PatchSet.Id base;
+  private final PatchSet.Id revision;
+  private final String path;
+
+  private FlowPanel editorContainer;
+  private CodeMirror cm;
+  private HandlerRegistration resizeHandler;
+
+  public CodeMirrorDemo(
+      PatchSet.Id base,
+      PatchSet.Id revision,
+      String path) {
+    this.base = base;
+    this.revision = revision;
+    this.path = path;
+  }
+
+  @Override
+  protected void onInitUI() {
+    super.onInitUI();
+    add(editorContainer = new FlowPanel());
+  }
+
+  @Override
+  protected void onLoad() {
+    super.onLoad();
+
+    DiffApi.diff(revision, path)
+      .base(base)
+      .wholeFile()
+      .ignoreWhitespace(DiffApi.IgnoreWhitespace.NONE)
+      .get(new ScreenLoadCallback<DiffInfo>(this) {
+        @Override
+        protected void preDisplay(DiffInfo diff) {
+          display(diff);
+        }
+      });
+  }
+
+  @Override
+  public void onShowView() {
+    super.onShowView();
+    cm.refresh();
+  }
+
+  @Override
+  protected void onUnload() {
+    super.onUnload();
+
+    if (resizeHandler != null) {
+      resizeHandler.removeHandler();
+      resizeHandler = null;
+    }
+    if (cm != null) {
+      cm.getWrapperElement().removeFromParent();
+      cm = null;
+    }
+  }
+
+  private void display(DiffInfo diff) {
+    Configuration cfg = Configuration.create()
+      .set("readOnly", true)
+      .set("lineNumbers", true)
+      .set("tabSize", 2)
+      .set("value", diff.text_b());
+    if (diff.meta_b() != null && diff.meta_b().content_type() != null) {
+      cfg.set("mode", diff.meta_b().content_type());
+    }
+
+    cm = CodeMirror.create(editorContainer.getElement(), cfg);
+    cm.setWidth("100%");
+    cm.setHeight(Window.getClientHeight() - HEADER_FOOTER);
+    resizeHandler = Window.addResizeHandler(new ResizeHandler() {
+      @Override
+      public void onResize(ResizeEvent event) {
+        cm.setHeight(event.getHeight() - HEADER_FOOTER);
+        cm.refresh();
+      }
+    });
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffApi.java
index b82ddee..ee556c5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffApi.java
@@ -37,7 +37,9 @@
   }
 
   public DiffApi base(PatchSet.Id id) {
-    call.addParameter("base", id.get());
+    if (id != null) {
+      call.addParameter("base", id.get());
+    }
     return this;
   }
 
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
index 0f08943..e365595 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
@@ -14,7 +14,9 @@
 
 package net.codemirror.lib;
 
+import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.ScriptElement;
 import com.google.gwt.dom.client.StyleInjector;
 import com.google.gwt.resources.client.ExternalTextResource;
@@ -26,7 +28,26 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-public class CodeMirror {
+/**
+ * Glue to connect CodeMirror to be callable from GWT.
+ *
+ * @link http://codemirror.net/doc/manual.html#api
+ */
+public class CodeMirror extends JavaScriptObject {
+  public static native CodeMirror create(Element parent, Configuration cfg) /*-{
+    return $wnd.CodeMirror(parent, cfg);
+  }-*/;
+
+  public final native void setValue(String v) /*-{ this.setValue(v); }-*/;
+
+  public final native void setWidth(int w) /*-{ this.setSize(w, null); }-*/;
+  public final native void setWidth(String w) /*-{ this.setSize(w, null); }-*/;
+  public final native void setHeight(int h) /*-{ this.setSize(null, h); }-*/;
+  public final native void setHeight(String h) /*-{ this.setSize(null, h); }-*/;
+
+  public final native void refresh() /*-{ this.refresh(); }-*/;
+  public final native Element getWrapperElement() /*-{ return this.getWrapperElement(); }-*/;
+
   public static void install() {
     asyncInjectCss(Lib.I.css());
     asyncInjectScript(Lib.I.js().getSafeUri());
@@ -63,6 +84,6 @@
     log.log(Level.SEVERE, "Cannot fetch CSS", e);
   }
 
-  private CodeMirror() {
+  protected CodeMirror() {
   }
 }
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/Configuration.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/Configuration.java
new file mode 100644
index 0000000..a121697
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/Configuration.java
@@ -0,0 +1,41 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package net.codemirror.lib;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * Simple map-like structure to pass configuration to CodeMirror.
+ *
+ * @link http://codemirror.net/doc/manual.html#config
+ * @see CodeMirror#create(com.google.gwt.dom.client.Element, Configuration)
+ */
+public class Configuration extends JavaScriptObject {
+  public static Configuration create() {
+    return createObject().cast();
+  }
+
+  public final native Configuration set(String name, String val)
+  /*-{ this[name] = val; return this; }-*/;
+
+  public final native Configuration set(String name, int val)
+  /*-{ this[name] = val; return this; }-*/;
+
+  public final native Configuration set(String name, boolean val)
+  /*-{ this[name] = val; return this; }-*/;
+
+  protected Configuration() {
+  }
+}