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

Java tutorial

Introduction

Here is the source code for com.google.gerrit.client.diff.OverviewBar.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.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseUpEvent;
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.DOM;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;

import net.codemirror.lib.CodeMirror;
import net.codemirror.lib.LineCharacter;
import net.codemirror.lib.ScrollInfo;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/** Displays overview of all edits and comments in this file. */
class OverviewBar extends Composite implements ClickHandler {
    interface Binder extends UiBinder<HTMLPanel, OverviewBar> {
    }

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

    interface Style extends CssResource {
        String gutter();

        String halfGutter();

        String comment();

        String draft();

        String insert();

        String delete();

        String viewportDrag();
    }

    enum MarkType {
        COMMENT, DRAFT, INSERT, DELETE, EDIT
    }

    @UiField
    Style style;
    @UiField
    Label viewport;

    private final List<MarkHandle> diff;
    private final Set<MarkHandle> comments;
    private CodeMirror cmB;

    private boolean dragging;
    private int startY;
    private double ratio;

    OverviewBar() {
        initWidget(uiBinder.createAndBindUi(this));
        diff = new ArrayList<>();
        comments = new HashSet<>();
        addDomHandler(this, ClickEvent.getType());
    }

    void init(CodeMirror cmB) {
        this.cmB = cmB;
    }

    void refresh() {
        update(cmB.getScrollInfo());
    }

    void update(ScrollInfo si) {
        double viewHeight = si.getClientHeight();
        double r = ratio(si);

        com.google.gwt.dom.client.Style style = viewport.getElement().getStyle();
        style.setTop(si.getTop() * r, Unit.PX);
        style.setHeight(Math.max(10, viewHeight * r), Unit.PX);
        getElement().getStyle().setHeight(viewHeight, Unit.PX);
        for (MarkHandle info : diff) {
            info.position(r);
        }
        for (MarkHandle info : comments) {
            info.position(r);
        }
    }

    @Override
    protected void onUnload() {
        super.onUnload();
        if (dragging) {
            DOM.releaseCapture(viewport.getElement());
        }
    }

    @Override
    public void onClick(ClickEvent e) {
        if (e.getY() < viewport.getElement().getOffsetTop()) {
            CodeMirror.handleVimKey(cmB, "<PageUp>");
        } else {
            CodeMirror.handleVimKey(cmB, "<PageDown>");
        }
        cmB.focus();
    }

    @UiHandler("viewport")
    void onMouseDown(MouseDownEvent e) {
        if (cmB != null) {
            dragging = true;
            ratio = ratio(cmB.getScrollInfo());
            startY = e.getY();
            viewport.addStyleName(style.viewportDrag());
            DOM.setCapture(viewport.getElement());
            e.preventDefault();
            e.stopPropagation();
        }
    }

    @UiHandler("viewport")
    void onMouseMove(MouseMoveEvent e) {
        if (dragging) {
            int y = e.getRelativeY(getElement()) - startY;
            cmB.scrollToY(Math.max(0, y / ratio));
            e.preventDefault();
            e.stopPropagation();
        }
    }

    @UiHandler("viewport")
    void onMouseUp(MouseUpEvent e) {
        if (dragging) {
            dragging = false;
            DOM.releaseCapture(viewport.getElement());
            viewport.removeStyleName(style.viewportDrag());
            e.preventDefault();
            e.stopPropagation();
        }
    }

    private double ratio(ScrollInfo si) {
        double barHeight = si.getClientHeight();
        double contentHeight = si.getHeight();
        return barHeight / contentHeight;
    }

    MarkHandle add(CodeMirror cm, int line, int height, MarkType type) {
        MarkHandle mark = new MarkHandle(cm, line, height);
        switch (type) {
        case COMMENT:
            mark.addStyleName(style.comment());
            comments.add(mark);
            break;
        case DRAFT:
            mark.addStyleName(style.draft());
            mark.getElement().setInnerText("*");
            comments.add(mark);
            break;
        case INSERT:
            mark.addStyleName(style.insert());
            diff.add(mark);
            break;
        case DELETE:
            mark.addStyleName(style.delete());
            diff.add(mark);
            break;
        case EDIT:
            mark.edit = DOM.createDiv();
            mark.edit.setClassName(style.halfGutter());
            mark.getElement().appendChild(mark.edit);
            mark.addStyleName(style.insert());
            diff.add(mark);
            break;
        }
        if (cmB != null) {
            mark.position(ratio(cmB.getScrollInfo()));
        }
        ((HTMLPanel) getWidget()).add(mark);
        return mark;
    }

    void clearDiffMarkers() {
        for (MarkHandle mark : diff) {
            mark.removeFromParent();
        }
        diff.clear();
    }

    class MarkHandle extends Widget implements ClickHandler {
        private static final int MIN_HEIGHT = 3;

        private final CodeMirror cm;
        private final int line;
        private final int height;
        private Element edit;

        MarkHandle(CodeMirror cm, int line, int height) {
            this.cm = cm;
            this.line = line;
            this.height = height;

            setElement((Element) (DOM.createDiv()));
            setStyleName(style.gutter());
            addDomHandler(this, ClickEvent.getType());
        }

        void position(double ratio) {
            double y = cm.heightAtLine(line, "local");
            getElement().getStyle().setTop(y * ratio, Unit.PX);
            if (height > 1) {
                double e = cm.heightAtLine(line + height, "local");
                double h = Math.max(MIN_HEIGHT, (e - y) * ratio);
                getElement().getStyle().setHeight(h, Unit.PX);
                if (edit != null) {
                    edit.getStyle().setHeight(h, Unit.PX);
                }
            }
        }

        @Override
        public void onClick(ClickEvent e) {
            if (height == 1 || !visible()) {
                e.stopPropagation();

                double y = cm.heightAtLine(line, "local");
                double viewport = cm.getScrollInfo().getClientHeight();
                cm.setCursor(LineCharacter.create(line));
                cm.scrollToY(y - 0.5 * viewport);
                cm.focus();
            }
        }

        private boolean visible() {
            int markT = getElement().getOffsetTop();
            int markE = markT + getElement().getOffsetHeight();

            int viewT = viewport.getElement().getOffsetTop();
            int viewE = viewT + viewport.getElement().getOffsetHeight();

            return (viewT <= markT && markT < viewE) // mark top within viewport
                    || (viewT <= markE && markE < viewE) // mark end within viewport
                    || (markT <= viewT && viewE <= markE); // mark contains viewport
        }

        void remove() {
            removeFromParent();
            comments.remove(this);
        }
    }
}