com.google.gerrit.client.diff.DraftBox.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gerrit.client.diff.DraftBox.java

Source

// 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.FormatUtil;
import com.google.gerrit.client.change.LocalComments;
import com.google.gerrit.client.changes.CommentApi;
import com.google.gerrit.client.changes.CommentInfo;
import com.google.gerrit.client.rpc.CallbackGroup;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.RestApi;
import com.google.gerrit.client.ui.CommentLinkProcessor;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
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.BlurEvent;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DoubleClickEvent;
import com.google.gwt.event.dom.client.DoubleClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
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.Timer;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwtexpui.globalkey.client.NpTextArea;
import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
import net.codemirror.lib.CodeMirror;

/** An HtmlPanel for displaying and editing a draft */
class DraftBox extends CommentBox {
    interface Binder extends UiBinder<HTMLPanel, DraftBox> {
    }

    private static final Binder uiBinder = GWT.create(Binder.class);

    private static final int INITIAL_LINES = 5;
    private static final int MAX_LINES = 30;

    private final CommentLinkProcessor linkProcessor;
    private final PatchSet.Id psId;
    private final boolean expandAll;
    private CommentInfo comment;
    private PublishedBox replyToBox;
    private Timer expandTimer;
    private Timer resizeTimer;
    private int editAreaHeight;
    private boolean autoClosed;
    private CallbackGroup pendingGroup;

    @UiField
    Widget header;
    @UiField
    Element summary;
    @UiField
    Element date;

    @UiField
    Element p_view;
    @UiField
    HTML message;
    @UiField
    Button edit;
    @UiField
    Button discard1;

    @UiField
    Element p_edit;
    @UiField
    NpTextArea editArea;
    @UiField
    Button save;
    @UiField
    Button cancel;
    @UiField
    Button discard2;

    DraftBox(CommentGroup group, CommentLinkProcessor clp, PatchSet.Id id, CommentInfo info,
            boolean expandAllComments) {
        super(group, info.range());

        linkProcessor = clp;
        psId = id;
        expandAll = expandAllComments;
        initWidget(uiBinder.createAndBindUi(this));

        expandTimer = new Timer() {
            @Override
            public void run() {
                expandText();
            }
        };
        set(info);

        header.addDomHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                if (!isEdit()) {
                    if (autoClosed && !isOpen()) {
                        setOpen(true);
                        setEdit(true);
                    } else {
                        setOpen(!isOpen());
                    }
                }
            }
        }, ClickEvent.getType());

        addDomHandler(new DoubleClickHandler() {
            @Override
            public void onDoubleClick(DoubleClickEvent event) {
                if (isEdit()) {
                    editArea.setFocus(true);
                } else {
                    setOpen(true);
                    setEdit(true);
                }
            }
        }, DoubleClickEvent.getType());

        initResizeHandler();
    }

    private void set(CommentInfo info) {
        autoClosed = !expandAll && info.message() != null && info.message().length() < 70;
        date.setInnerText(FormatUtil.shortFormatDayTime(info.updated()));
        if (info.message() != null) {
            String msg = info.message().trim();
            summary.setInnerText(msg);
            message.setHTML(linkProcessor.apply(new SafeHtmlBuilder().append(msg).wikify()));
        }
        comment = info;
    }

    @Override
    CommentInfo getCommentInfo() {
        return comment;
    }

    @Override
    boolean isOpen() {
        return UIObject.isVisible(p_view);
    }

    @Override
    void setOpen(boolean open) {
        UIObject.setVisible(summary, !open);
        UIObject.setVisible(p_view, open);
        super.setOpen(open);
    }

    private void expandText() {
        double cols = editArea.getCharacterWidth();
        int rows = 2;
        for (String line : editArea.getValue().split("\n")) {
            rows += Math.ceil((1.0 + line.length()) / cols);
        }
        rows = Math.max(INITIAL_LINES, Math.min(rows, MAX_LINES));
        if (editArea.getVisibleLines() != rows) {
            editArea.setVisibleLines(rows);
        }
        editAreaHeight = editArea.getOffsetHeight();
        getCommentGroup().resize();
    }

    boolean isEdit() {
        return UIObject.isVisible(p_edit);
    }

    void setEdit(boolean edit) {
        UIObject.setVisible(summary, false);
        UIObject.setVisible(p_view, !edit);
        UIObject.setVisible(p_edit, edit);

        setRangeHighlight(edit);
        if (edit) {
            String msg = comment.message() != null ? comment.message() : "";
            editArea.setValue(msg);
            cancel.setVisible(!isNew());
            expandText();
            editAreaHeight = editArea.getOffsetHeight();

            final int len = msg.length();
            Scheduler.get().scheduleDeferred(new ScheduledCommand() {
                @Override
                public void execute() {
                    editArea.setFocus(true);
                    if (len > 0) {
                        editArea.setCursorPos(len);
                    }
                }
            });
        } else {
            expandTimer.cancel();
            resizeTimer.cancel();
        }
        getCommentManager().setUnsaved(this, edit);
        getCommentGroup().resize();
    }

    PublishedBox getReplyToBox() {
        return replyToBox;
    }

    void setReplyToBox(PublishedBox box) {
        replyToBox = box;
    }

    @Override
    protected void onUnload() {
        expandTimer.cancel();
        resizeTimer.cancel();
        super.onUnload();
    }

    private void removeUI() {
        if (replyToBox != null) {
            replyToBox.unregisterReplyBox();
        }

        getCommentManager().setUnsaved(this, false);
        setRangeHighlight(false);
        clearRange();
        getAnnotation().remove();
        getCommentGroup().remove(this);
        getCm().focus();
    }

    private void restoreSelection() {
        if (getFromTo() != null && comment.inReplyTo() == null) {
            getCm().setSelection(getFromTo().from(), getFromTo().to());
        }
    }

    @UiHandler("message")
    void onMessageClick(ClickEvent e) {
        e.stopPropagation();
    }

    @UiHandler("message")
    void onMessageDoubleClick(@SuppressWarnings("unused") DoubleClickEvent e) {
        setEdit(true);
    }

    @UiHandler("edit")
    void onEdit(ClickEvent e) {
        e.stopPropagation();
        setEdit(true);
    }

    @UiHandler("save")
    void onSave(ClickEvent e) {
        e.stopPropagation();
        CallbackGroup group = new CallbackGroup();
        save(group);
        group.done();
    }

    void save(CallbackGroup group) {
        if (pendingGroup != null) {
            pendingGroup.addListener(group);
            return;
        }

        String message = editArea.getValue().trim();
        if (message.length() == 0) {
            return;
        }

        CommentInfo input = CommentInfo.copy(comment);
        input.message(message);
        enableEdit(false);

        pendingGroup = group;
        final LocalComments lc = new LocalComments(psId);
        GerritCallback<CommentInfo> cb = new GerritCallback<CommentInfo>() {
            @Override
            public void onSuccess(CommentInfo result) {
                enableEdit(true);
                pendingGroup = null;
                set(result);
                setEdit(false);
                if (autoClosed) {
                    setOpen(false);
                }
                getCommentManager().setUnsaved(DraftBox.this, false);
            }

            @Override
            public void onFailure(Throwable e) {
                enableEdit(true);
                pendingGroup = null;
                if (RestApi.isNotSignedIn(e)) {
                    CommentInfo saved = CommentInfo.copy(comment);
                    saved.message(editArea.getValue().trim());
                    lc.setInlineComment(saved);
                }
                super.onFailure(e);
            }
        };
        if (input.id() == null) {
            CommentApi.createDraft(psId, input, group.add(cb));
        } else {
            CommentApi.updateDraft(psId, input.id(), input, group.add(cb));
        }
        CodeMirror cm = getCm();
        cm.vim().handleKey("<Esc>");
        cm.focus();
    }

    private void enableEdit(boolean on) {
        editArea.setEnabled(on);
        save.setEnabled(on);
        cancel.setEnabled(on);
        discard2.setEnabled(on);
    }

    @UiHandler("cancel")
    void onCancel(ClickEvent e) {
        e.stopPropagation();
        if (isNew() && !isDirty()) {
            removeUI();
            restoreSelection();
        } else {
            setEdit(false);
            if (autoClosed) {
                setOpen(false);
            }
            getCm().focus();
        }
    }

    @UiHandler({ "discard1", "discard2" })
    void onDiscard(ClickEvent e) {
        e.stopPropagation();
        if (isNew()) {
            removeUI();
            restoreSelection();
        } else {
            setEdit(false);
            pendingGroup = new CallbackGroup();
            CommentApi.deleteDraft(psId, comment.id(),
                    pendingGroup.addFinal(new GerritCallback<JavaScriptObject>() {
                        @Override
                        public void onSuccess(JavaScriptObject result) {
                            pendingGroup = null;
                            removeUI();
                        }
                    }));
        }
    }

    @UiHandler("editArea")
    void onKeyDown(KeyDownEvent e) {
        resizeTimer.cancel();
        if ((e.isControlKeyDown() || e.isMetaKeyDown()) && !e.isAltKeyDown() && !e.isShiftKeyDown()) {
            switch (e.getNativeKeyCode()) {
            case 's':
            case 'S':
                e.preventDefault();
                CallbackGroup group = new CallbackGroup();
                save(group);
                group.done();
                return;
            }
        } else if (e.getNativeKeyCode() == KeyCodes.KEY_ESCAPE && !isDirty()) {
            if (isNew()) {
                removeUI();
                restoreSelection();
                return;
            }
            setEdit(false);
            if (autoClosed) {
                setOpen(false);
            }
            getCm().focus();
            return;
        }
        expandTimer.schedule(250);
    }

    @UiHandler("editArea")
    void onBlur(@SuppressWarnings("unused") BlurEvent e) {
        resizeTimer.cancel();
    }

    private void initResizeHandler() {
        resizeTimer = new Timer() {
            @Override
            public void run() {
                getCommentGroup().resize();
            }
        };

        addDomHandler(new MouseMoveHandler() {
            @Override
            public void onMouseMove(MouseMoveEvent event) {
                int h = editArea.getOffsetHeight();
                if (isEdit() && h != editAreaHeight) {
                    getCommentGroup().resize();
                    resizeTimer.scheduleRepeating(50);
                    editAreaHeight = h;
                }
            }
        }, MouseMoveEvent.getType());

        addDomHandler(new MouseUpHandler() {
            @Override
            public void onMouseUp(MouseUpEvent event) {
                resizeTimer.cancel();
                getCommentGroup().resize();
            }
        }, MouseUpEvent.getType());
    }

    private boolean isNew() {
        return comment.id() == null;
    }

    private boolean isDirty() {
        String msg = editArea.getValue().trim();
        if (isNew()) {
            return msg.length() > 0;
        }
        return !msg.equals(comment.message() != null ? comment.message().trim() : "");
    }
}