org.rstudio.studio.client.workbench.views.output.lint.DiagnosticsBackgroundPopup.java Source code

Java tutorial

Introduction

Here is the source code for org.rstudio.studio.client.workbench.views.output.lint.DiagnosticsBackgroundPopup.java

Source

/*
 * DiagnosticsBackgroundPopup.java
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */
package org.rstudio.studio.client.workbench.views.output.lint;

import org.rstudio.core.client.Rectangle;
import org.rstudio.studio.client.workbench.views.output.lint.model.AceAnnotation;
import org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor;
import org.rstudio.studio.client.workbench.views.source.editors.text.DocDisplay;
import org.rstudio.studio.client.workbench.views.source.editors.text.ace.Marker;
import org.rstudio.studio.client.workbench.views.source.editors.text.ace.Markers;
import org.rstudio.studio.client.workbench.views.source.editors.text.ace.Position;
import org.rstudio.studio.client.workbench.views.source.editors.text.ace.Range;
import org.rstudio.studio.client.workbench.views.source.editors.text.ace.Renderer.ScreenCoordinates;
import org.rstudio.studio.client.workbench.views.source.editors.text.events.CursorChangedEvent;
import org.rstudio.studio.client.workbench.views.source.editors.text.events.CursorChangedHandler;

import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;

public class DiagnosticsBackgroundPopup {
    public DiagnosticsBackgroundPopup(DocDisplay docDisplay) {
        docDisplay_ = docDisplay;
        editor_ = (AceEditor) docDisplay_;

        docDisplay_.addFocusHandler(new FocusHandler() {
            @Override
            public void onFocus(FocusEvent event) {
                if (editor_ != null && !isRunning_)
                    start();
            }
        });

        docDisplay_.addBlurHandler(new BlurHandler() {
            @Override
            public void onBlur(BlurEvent event) {
                hidePopup();
                stopMonitoring();
                if (handler_ != null) {
                    handler_.removeHandler();
                    handler_ = null;
                }
            }
        });

        stopRequested_ = false;
        popup_ = null;
        start();
    }

    public void start() {
        isRunning_ = true;
        stopRequested_ = false;

        handler_ = Event.addNativePreviewHandler(new NativePreviewHandler() {
            @Override
            public void onPreviewNativeEvent(NativePreviewEvent event) {
                if (event.getTypeInt() == Event.ONMOUSEMOVE) {
                    movedMouseMostRecently_ = true;

                    Element target = Element.as(event.getNativeEvent().getEventTarget());
                    if (target.hasClassName("ace_gutter-cell")) {
                        lastMouseCoords_ = null;
                        hidePopup();
                        return;
                    }

                    lastMouseCoords_ = ScreenCoordinates.create(event.getNativeEvent().getClientX(),
                            event.getNativeEvent().getClientY());

                    if (activeMarker_ != null && !activeMarker_.getRange()
                            .containsRightExclusive(editor_.toDocumentPosition(lastMouseCoords_))) {
                        hidePopup();
                    }
                } else {
                    movedMouseMostRecently_ = false;
                }
            }
        });

        Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
            @Override
            public boolean execute() {
                if (stopRequested_)
                    return stopExecution();

                // If the document is not focused, stop execution (can be
                // restarted later)
                if (!docDisplay_.isFocused())
                    return stopExecution();

                long currentTime = System.currentTimeMillis();
                long lastModifiedTime = docDisplay_.getLastModifiedTime();
                long lastCursorChangedTime = docDisplay_.getLastCursorChangedTime();

                // If the document was modified recently, or the
                // cursor was moved recently, then bail
                if ((currentTime - lastModifiedTime) < 500)
                    return completeExecution();

                if ((currentTime - lastCursorChangedTime) < 500)
                    return completeExecution();

                Markers markers = editor_.getSession().getMarkers(true);
                Position currentPos;
                if (movedMouseMostRecently_) {
                    if (lastMouseCoords_ == null)
                        return completeExecution();

                    currentPos = editor_.toDocumentPosition(lastMouseCoords_);
                } else {
                    currentPos = docDisplay_.getCursorPosition();
                }

                int keys[] = markers.getIds();
                for (int i = 0; i < keys.length; i++) {
                    Marker marker = markers.get(keys[i]);
                    if (marker.getRange().containsRightExclusive(currentPos)) {
                        displayMarkerDiagnostics(marker);
                        return completeExecution();
                    }
                }

                return completeExecution();
            }
        }, 500);
    }

    private void displayMarkerDiagnostics(Marker marker) {
        JsArray<AceAnnotation> annotations = editor_.getAnnotations();
        for (int i = 0; i < annotations.length(); i++) {
            AceAnnotation annotation = annotations.get(i);
            int row = annotation.row();
            if (marker.getRange().getStart().getRow() <= row && marker.getRange().getEnd().getRow() >= row) {
                activeMarker_ = marker;
                showPopup(annotation.text(), marker.getRange());
                return;
            }
        }
    }

    class DiagnosticsPopupPanel extends PopupPanel {
        public DiagnosticsPopupPanel(String text, Range range) {
            super(true, false);
            range_ = range;
            editor_.addCursorChangedHandler(new CursorChangedHandler() {
                @Override
                public void onCursorChanged(CursorChangedEvent event) {
                    if (!range_.containsRightExclusive(event.getPosition()))
                        hide();
                }
            });
            addStyleName(RES.styles().popup());
            setWidget(new Label(text));
        }

        public void hide() {
            super.hide();
        }

        private final Range range_;
    }

    private void showPopup(String text, Range range) {
        if (popup_ != null)
            popup_.hide();

        popup_ = new DiagnosticsPopupPanel(text, range);
        final Rectangle coords = editor_.toScreenCoordinates(range);
        popup_.setTitle("Diagnostics");
        popup_.setPopupPositionAndShow(new PositionCallback() {
            @Override
            public void setPosition(int offsetWidth, int offsetHeight) {
                popup_.setPopupPosition(coords.getRight() + 10, coords.getBottom());
            }
        });
    }

    private void hidePopup() {
        activeMarker_ = null;
        if (popup_ != null) {
            popup_.hide();
            popup_ = null;
        }
    }

    private boolean completeExecution() {
        return true;
    }

    private boolean stopExecution() {
        isRunning_ = false;
        stopRequested_ = false;
        activeMarker_ = null;
        return false;
    }

    private void stopMonitoring() {
        stopRequested_ = true;
    }

    public interface Resources extends ClientBundle {
        public static interface Styles extends CssResource {
            String popup();
        }

        @Source("DiagnosticsBackgroundPopup.css")
        Styles styles();

        public static Resources INSTANCE = (Resources) GWT.create(Resources.class);
    }

    private final DocDisplay docDisplay_;
    private final AceEditor editor_;
    private DiagnosticsPopupPanel popup_;
    private boolean isRunning_;
    private boolean stopRequested_;
    private Marker activeMarker_;

    private ScreenCoordinates lastMouseCoords_;
    private HandlerRegistration handler_;
    private static final Resources RES = Resources.INSTANCE;

    private boolean movedMouseMostRecently_;

    static {
        RES.styles().ensureInjected();
    }

}