blob: c8eb13a6c07f3b0cbd4ba10b91159dd486098d48 [file] [log] [blame]
// 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.change;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.ChangeApi;
import com.google.gerrit.client.changes.ChangeInfo.ApprovalInfo;
import com.google.gerrit.client.changes.ChangeInfo.LabelInfo;
import com.google.gerrit.client.changes.ReviewInput;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.data.LabelValue;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwtexpui.globalkey.client.NpTextArea;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
class ReplyBox extends Composite {
interface Binder extends UiBinder<HTMLPanel, ReplyBox> {}
private static final Binder uiBinder = GWT.create(Binder.class);
interface Styles extends CssResource {
String label_name();
String label_value();
}
private final PatchSet.Id psId;
private final String revision;
private ReviewInput in = ReviewInput.create();
private List<Runnable> lgtm;
@UiField Styles style;
@UiField NpTextArea message;
@UiField Element labelsParent;
@UiField Grid labelsTable;
@UiField Button send;
@UiField CheckBox email;
@UiField Button cancel;
ReplyBox(
PatchSet.Id psId,
String revision,
NativeMap<LabelInfo> all,
NativeMap<JsArrayString> permitted) {
this.psId = psId;
this.revision = revision;
initWidget(uiBinder.createAndBindUi(this));
List<String> names = new ArrayList<String>(permitted.keySet());
if (names.isEmpty()) {
UIObject.setVisible(labelsParent, false);
} else {
Collections.sort(names);
lgtm = new ArrayList<Runnable>(names.size());
renderLabels(names, all, permitted);
}
}
@Override
protected void onLoad() {
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
message.setFocus(true);
}});
}
@UiHandler("message")
void onMessageKey(KeyPressEvent event) {
if ((event.getCharCode() == '\n' || event.getCharCode() == KeyCodes.KEY_ENTER)
&& event.isControlKeyDown()) {
event.preventDefault();
event.stopPropagation();
onSend(null);
} else if (lgtm != null
&& event.getCharCode() == 'M'
&& message.getValue().equals("LGT")) {
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
if (message.getValue().startsWith("LGTM")) {
for (Runnable r : lgtm) {
r.run();
}
}
}
});
}
}
@UiHandler("email")
void onEmail(ValueChangeEvent<Boolean> e) {
if (e.getValue()) {
in.notify(ReviewInput.NotifyHandling.ALL);
} else {
in.notify(ReviewInput.NotifyHandling.NONE);
}
}
@UiHandler("send")
void onSend(ClickEvent e) {
in.message(message.getText().trim());
ChangeApi.revision(psId.getParentKey().get(), revision)
.view("review")
.post(in, new GerritCallback<ReviewInput>() {
@Override
public void onSuccess(ReviewInput result) {
Gerrit.display(PageLinks.toChange(
psId.getParentKey(),
String.valueOf(psId.get())));
}
});
hide();
}
@UiHandler("cancel")
void onCancel(ClickEvent e) {
hide();
}
private void hide() {
for (Widget w = getParent(); w != null; w = w.getParent()) {
if (w instanceof PopupPanel) {
((PopupPanel) w).hide();
break;
}
}
}
private void renderLabels(
List<String> names,
NativeMap<LabelInfo> all,
NativeMap<JsArrayString> permitted) {
TreeSet<Short> values = new TreeSet<Short>();
for (String id : names) {
JsArrayString p = permitted.get(id);
if (p != null) {
for (int i = 0; i < p.length(); i++) {
values.add(LabelInfo.parseValue(p.get(i)));
}
}
}
List<Short> columns = new ArrayList<Short>(values);
labelsTable.resize(1 + permitted.size(), 1 + values.size());
for (int c = 0; c < columns.size(); c++) {
labelsTable.setText(0, 1 + c, LabelValue.formatValue(columns.get(c)));
labelsTable.getCellFormatter().setStyleName(0, 1 + c, style.label_value());
}
List<String> checkboxes = new ArrayList<String>(permitted.size());
int row = 1;
for (String id : names) {
Set<Short> vals = all.get(id).value_set();
if (isCheckBox(vals)) {
checkboxes.add(id);
} else {
renderRadio(row++, id, columns, vals, all.get(id));
}
}
for (String id : checkboxes) {
renderCheckBox(row++, id, all.get(id));
}
}
private void renderRadio(int row, final String id,
List<Short> columns,
Set<Short> values,
LabelInfo info) {
labelsTable.setText(row, 0, id);
labelsTable.getCellFormatter().setStyleName(row, 0, style.label_name());
ApprovalInfo self = Gerrit.isSignedIn()
? info.for_user(Gerrit.getUserAccount().getId().get())
: null;
final List<RadioButton> group = new ArrayList<RadioButton>(values.size());
for (int i = 0; i < columns.size(); i++) {
final Short v = columns.get(i);
if (values.contains(v)) {
RadioButton b = new RadioButton(id);
b.setTitle(info.value_text(LabelValue.formatValue(v)));
if ((self != null && v == self.value()) || (self == null && v == 0)) {
b.setValue(true);
}
b.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
if (event.getValue()) {
in.label(id, v);
}
}
});
group.add(b);
labelsTable.setWidget(row, 1 + i, b);
}
}
if (!group.isEmpty()) {
lgtm.add(new Runnable() {
@Override
public void run() {
for (int i = 0; i < group.size() - 1; i++) {
group.get(i).setValue(false, false);
}
group.get(group.size() - 1).setValue(true, true);
}
});
}
}
private void renderCheckBox(int row, final String id, LabelInfo info) {
ApprovalInfo self = Gerrit.isSignedIn()
? info.for_user(Gerrit.getUserAccount().getId().get())
: null;
final CheckBox b = new CheckBox();
b.setText(id);
b.setTitle(info.value_text("+1"));
if (self != null && self.value() == 1) {
b.setValue(true);
}
b.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
in.label(id, event.getValue() ? (short) 1 : (short) 0);
}
});
b.setStyleName(style.label_name());
labelsTable.setWidget(row, 0, b);
lgtm.add(new Runnable() {
@Override
public void run() {
b.setValue(true, true);
}
});
}
private static boolean isCheckBox(Set<Short> values) {
return values.size() == 2
&& values.contains((short) 0)
&& values.contains((short) 1);
}
}