Button to allow Cherry Picking of a change to another branch.

Built on the cherry pick REST Api.

Change-Id: I1b0211896eada7de12170ac73de60828bcf19d47
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
index 7ac21db..614be13 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
@@ -27,6 +27,7 @@
   protected boolean allowsAnonymous;
   protected boolean canAbandon;
   protected boolean canEditCommitMessage;
+  protected boolean canCherryPick;
   protected boolean canPublish;
   protected boolean canRebase;
   protected boolean canRestore;
@@ -82,6 +83,14 @@
     canEditCommitMessage = a;
   }
 
+  public boolean canCherryPick() {
+    return canCherryPick;
+  }
+
+  public void setCanCherryPick(final boolean a) {
+    canCherryPick = a;
+  }
+
   public boolean canPublish() {
     return canPublish;
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
index abd94c9..9375e44 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
@@ -82,6 +82,14 @@
   }
 
   /** Submit a specific revision of a change. */
+  public static void cherrypick(int id, String commit, String destination, String message, AsyncCallback<ChangeInfo> cb) {
+    CherryPickInput cherryPickInput = CherryPickInput.create();
+    cherryPickInput.setMessage(message);
+    cherryPickInput.setDestination(destination);
+    call(id, commit, "cherrypick").post(cherryPickInput, cb);
+  }
+
+  /** Submit a specific revision of a change. */
   public static void submit(int id, String commit, AsyncCallback<SubmitInfo> cb) {
     SubmitInput in = SubmitInput.create();
     in.wait_for_merge(true);
@@ -100,6 +108,17 @@
     }
   }
 
+  private static class CherryPickInput extends JavaScriptObject {
+    static CherryPickInput create() {
+      return (CherryPickInput) createObject();
+    }
+    final native void setDestination(String d) /*-{ this.destination = d; }-*/;
+    final native void setMessage(String m) /*-{ this.message = m; }-*/;
+
+    protected CherryPickInput() {
+    }
+  };
+
   private static class SubmitInput extends JavaScriptObject {
     final native void wait_for_merge(boolean b) /*-{ this.wait_for_merge=b; }-*/;
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
index 62d85c4..421cb66 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
@@ -136,6 +136,12 @@
   String editCommitMessageToolTip();
   String titleEditCommitMessage();
 
+  String buttonCherryPickChangeBegin();
+  String buttonCherryPickChangeSend();
+  String headingCherryPickBranch();
+  String cherryPickCommitMessage();
+  String cherryPickTitle();
+
   String buttonAbandonChangeBegin();
   String buttonAbandonChangeSend();
   String headingAbandonMessage();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
index 9388c13..8dbdfa8 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
@@ -121,6 +121,12 @@
 editCommitMessageToolTip = Edit Commit Message
 titleEditCommitMessage = Create New Patch Set
 
+buttonCherryPickChangeBegin = Cherry Pick To
+buttonCherryPickChangeSend = Cherry Pick Change
+headingCherryPickBranch = Cherry Pick to Branch:
+cherryPickCommitMessage = Cherry Pick Commit Message:
+cherryPickTitle = Code Review - Cherry Pick Change to Another Branch
+
 buttonRestoreChangeBegin = Restore Change
 restoreChangeTitle = Code Review - Restore Change
 headingRestoreMessage = Restore Message:
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java
index 098fe07..742454e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java
@@ -24,6 +24,7 @@
 
   String revertChangeDefaultMessage(String commitMsg, String commitId);
 
+  String cherryPickedChangeDefaultMessage(String commitMsg, String commitId);
   String changeScreenTitleId(String changeId);
   String outdatedHeader(int outdated);
   String patchSetHeader(int id);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
index 6c99081..f55c545 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
@@ -6,6 +6,7 @@
 changesAbandonedInProject = Abandoned Changes In {0}
 
 revertChangeDefaultMessage = Revert \"{0}\"\n\nThis reverts commit {1}
+cherryPickedChangeDefaultMessage = {0}\n(cherry picked from commit {1})
 
 changeScreenTitleId = Change {0}
 outdatedHeader = Change depends on {0} outdated change(s) and should be rebased on the latest patch sets.
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
index 2ed094d..679b6bf 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
@@ -25,6 +25,7 @@
 import com.google.gerrit.client.ui.CommentedActionDialog;
 import com.google.gerrit.client.ui.ComplexDisclosurePanel;
 import com.google.gerrit.client.ui.ListenableAccountDiffPreference;
+import com.google.gerrit.client.ui.SmallHeading;
 import com.google.gerrit.common.PageLinks;
 import com.google.gerrit.common.data.ChangeDetail;
 import com.google.gerrit.common.data.PatchSetDetail;
@@ -48,6 +49,7 @@
 import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
 import com.google.gwt.user.client.ui.InlineLabel;
 import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.TextBox;
 import com.google.gwtjsonrpc.common.VoidResult;
 
 import java.util.HashSet;
@@ -379,6 +381,48 @@
       actionsPanel.add(b);
     }
 
+    if (changeDetail.canCherryPick()) {
+      final Button b = new Button(Util.C.buttonCherryPickChangeBegin());
+      b.addClickHandler(new ClickHandler() {
+        @Override
+        public void onClick(final ClickEvent event) {
+          b.setEnabled(false);
+          new CherryPickDialog(b) {
+            {
+              sendButton.setText(Util.C.buttonCherryPickChangeSend());
+              message.setText(Util.M.cherryPickedChangeDefaultMessage(
+                  detail.getInfo().getMessage().trim(),
+                  detail.getPatchSet().getRevision().get()));
+            }
+
+            @Override
+            public void onSend() {
+              ChangeApi.cherrypick(changeDetail.getChange().getChangeId(),
+                  patchSet.getRevision().get(),
+                  getDestinationBranch(),
+                  getMessageText(),
+                  new GerritCallback<ChangeInfo>() {
+                    @Override
+                    public void onSuccess(ChangeInfo result) {
+                      sent = true;
+                      Gerrit.display(PageLinks.toChange(new Change.Id(result
+                          ._number())));
+                      hide();
+                    }
+
+                    @Override
+                    public void onFailure(Throwable caught) {
+                      enableButtons(true);
+                      super.onFailure(caught);
+                    }
+                  });
+            }
+          }.center();
+        }
+      });
+      actionsPanel.add(b);
+    }
+
     if (changeDetail.canAbandon()) {
       final Button b = new Button(Util.C.buttonAbandonChangeBegin());
       b.addClickHandler(new ClickHandler() {
@@ -650,4 +694,23 @@
         });
     }
   }
+
+  private abstract class CherryPickDialog extends ActionDialog {
+    private TextBox newBranch;
+
+    CherryPickDialog(final FocusWidget enableOnFailure) {
+      super(enableOnFailure, true, Util.C.cherryPickTitle(), Util.C.cherryPickCommitMessage());
+
+      newBranch = new TextBox();
+      newBranch.setVisibleLength(65);
+      setFocusOn(newBranch);
+      message.setCharacterWidth(70);
+      panel.insert(newBranch, 0);
+      panel.insert(new SmallHeading(Util.C.headingCherryPickBranch()), 0);
+    }
+
+    public String getDestinationBranch() {
+      return newBranch.getText();
+    }
+  }
 }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
index 08bcf88..aeaa3bb 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.httpd.rpc.changedetail;
 
+import com.google.gerrit.common.data.Capable;
 import com.google.gerrit.common.data.ChangeDetail;
 import com.google.gerrit.common.data.ChangeInfo;
 import com.google.gerrit.common.data.SubmitRecord;
@@ -136,6 +137,8 @@
 
     detail.setCanRevert(change.getStatus() == Change.Status.MERGED && control.canAddPatchSet());
 
+    detail.setCanCherryPick(control.getProjectControl().canPushToAtLeastOneRef() == Capable.OK);
+
     detail.setCanEdit(control.getRefControl().canWrite());
     detail.setCanEditCommitMessage(change.getStatus().isOpen() && control.canAddPatchSet());
     detail.setCanEditTopicName(control.canEditTopicName());