blob: 56c28a1097da5b73bf85c2dc5de6127cb2d1c02c [file] [log] [blame]
// Copyright (C) 2008 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.patches;
import com.google.gerrit.client.reviewdb.PatchLineComment;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.ui.TextSaveButtonListener;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Focusable;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwtexpui.globalkey.client.NpTextArea;
import com.google.gwtjsonrpc.client.VoidResult;
import java.sql.Timestamp;
class CommentEditorPanel extends Composite implements ClickHandler {
private static final int INITIAL_COLS = 60;
private static final int INITIAL_LINES = 5;
private static final int MAX_LINES = 30;
private PatchLineComment comment;
private final LineCommentPanel renderedPanel;
private final NpTextArea text;
private final Button edit;
private final Button save;
private final Button cancel;
private final Button discard;
private final Label savedAt;
private final Timer expandTimer;
CommentEditorPanel(final PatchLineComment plc) {
comment = plc;
final FlowPanel body = new FlowPanel();
initWidget(body);
setStyleName("gerrit-CommentEditor");
renderedPanel = new LineCommentPanel(comment) {
{
sinkEvents(Event.ONDBLCLICK);
}
@Override
public void onBrowserEvent(final Event event) {
switch (DOM.eventGetType(event)) {
case Event.ONDBLCLICK:
edit();
break;
}
super.onBrowserEvent(event);
}
};
body.add(renderedPanel);
expandTimer = new Timer() {
@Override
public void run() {
expandText();
}
};
text = new NpTextArea();
text.setText(comment.getMessage());
text.setCharacterWidth(INITIAL_COLS);
text.setVisibleLines(INITIAL_LINES);
DOM.setElementPropertyBoolean(text.getElement(), "spellcheck", true);
text.addKeyPressHandler(new KeyPressHandler() {
@Override
public void onKeyPress(final KeyPressEvent event) {
if (event.getCharCode() == KeyCodes.KEY_ESCAPE
&& !event.isAnyModifierKeyDown()) {
event.preventDefault();
if (isNew()) {
onDiscard();
} else {
render();
}
return;
}
if ((event.isControlKeyDown() || event.isMetaKeyDown())
&& !event.isAltKeyDown() && !event.isShiftKeyDown()) {
switch (event.getCharCode()) {
case 's':
event.preventDefault();
onSave();
return;
case 'd':
event.preventDefault();
if (isNew()) {
onDiscard();
} else if (Window.confirm(PatchUtil.C.confirmDiscard())) {
onDiscard();
} else {
text.setFocus(true);
}
return;
}
}
expandTimer.schedule(250);
}
});
body.add(text);
final FlowPanel buttons = new FlowPanel();
buttons.setStyleName("gerrit-CommentEditor-Buttons");
body.add(buttons);
edit = new Button();
edit.setText(PatchUtil.C.buttonEdit());
edit.addClickHandler(this);
buttons.add(edit);
save = new Button();
save.setText(PatchUtil.C.buttonSave());
save.addClickHandler(this);
new TextSaveButtonListener(text, save);
save.setEnabled(false);
buttons.add(save);
cancel = new Button();
cancel.setText(PatchUtil.C.buttonCancel());
cancel.addClickHandler(this);
buttons.add(cancel);
discard = new Button();
discard.setText(PatchUtil.C.buttonDiscard());
discard.addClickHandler(this);
buttons.add(discard);
savedAt = new InlineLabel();
savedAt.setStyleName("gerrit-CommentEditor-SavedDraft");
buttons.add(savedAt);
if (isNew()) {
edit();
} else {
render();
}
}
private void expandText() {
final double cols = text.getCharacterWidth();
int rows = 2;
for (final String line : text.getText().split("\n")) {
rows += Math.ceil((1.0 + line.length()) / cols);
}
rows = Math.max(INITIAL_LINES, Math.min(rows, MAX_LINES));
if (text.getVisibleLines() != rows) {
text.setVisibleLines(rows);
}
}
private void edit() {
text.setText(comment.getMessage());
expandText();
stateEdit(true);
text.setFocus(true);
}
private void render() {
final Timestamp on = comment.getWrittenOn();
savedAt.setText(PatchUtil.M.draftSaved(new java.util.Date(on.getTime())));
renderedPanel.update(comment);
stateEdit(false);
}
private void stateEdit(final boolean inEdit) {
expandTimer.cancel();
renderedPanel.setVisible(!inEdit);
edit.setVisible(!inEdit);
text.setVisible(inEdit);
save.setVisible(inEdit);
cancel.setVisible(inEdit && !isNew());
discard.setVisible(inEdit);
}
void setFocus(final boolean take) {
if (text.isVisible()) {
text.setFocus(take);
} else if (take) {
edit();
}
}
boolean isNew() {
return comment.getKey().get() == null;
}
@Override
public void onClick(final ClickEvent event) {
final Widget sender = (Widget) event.getSource();
if (sender == edit) {
edit();
} else if (sender == save) {
onSave();
} else if (sender == cancel) {
render();
} else if (sender == discard) {
onDiscard();
}
}
private void onSave() {
expandTimer.cancel();
final String txt = text.getText().trim();
if ("".equals(txt)) {
return;
}
comment.setMessage(txt);
text.setReadOnly(true);
save.setEnabled(false);
cancel.setEnabled(false);
discard.setEnabled(false);
PatchUtil.DETAIL_SVC.saveDraft(comment,
new GerritCallback<PatchLineComment>() {
public void onSuccess(final PatchLineComment result) {
if (isNew()) {
notifyDraftDelta(1);
}
comment = result;
text.setReadOnly(false);
cancel.setEnabled(true);
discard.setEnabled(true);
render();
}
@Override
public void onFailure(final Throwable caught) {
text.setReadOnly(false);
save.setEnabled(true);
cancel.setEnabled(true);
discard.setEnabled(true);
super.onFailure(caught);
}
});
}
private void notifyDraftDelta(final int delta) {
Widget p = getParent();
if (p != null) {
p = p.getParent();
if (p != null) {
((AbstractPatchContentTable) p).notifyDraftDelta(delta);
}
}
}
private void onDiscard() {
expandTimer.cancel();
if (isNew()) {
removeUI();
return;
}
final boolean saveOn = save.isEnabled();
text.setReadOnly(true);
save.setEnabled(false);
cancel.setEnabled(false);
discard.setEnabled(false);
PatchUtil.DETAIL_SVC.deleteDraft(comment.getKey(),
new GerritCallback<VoidResult>() {
public void onSuccess(final VoidResult result) {
notifyDraftDelta(-1);
removeUI();
}
@Override
public void onFailure(final Throwable caught) {
text.setReadOnly(false);
save.setEnabled(saveOn);
cancel.setEnabled(true);
discard.setEnabled(true);
super.onFailure(caught);
}
});
}
private void removeUI() {
final FlexTable table = (FlexTable) getParent();
final int nRows = table.getRowCount();
for (int row = 0; row < nRows; row++) {
final int nCells = table.getCellCount(row);
for (int cell = 0; cell < nCells; cell++) {
if (table.getWidget(row, cell) == this) {
AbstractPatchContentTable.destroyEditor(table, row, cell);
Widget p = table;
while (p != null) {
if (p instanceof Focusable) {
((Focusable) p).setFocus(true);
break;
}
p = p.getParent();
}
return;
}
}
}
}
}