/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.api.editor.annotation;
import static org.eclipse.che.ide.api.editor.gutter.Gutters.ANNOTATION_GUTTER;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.eclipse.che.ide.api.editor.text.Position;
import org.eclipse.che.ide.api.editor.text.annotation.Annotation;
import org.eclipse.che.ide.api.editor.document.Document;
import org.eclipse.che.ide.api.editor.gutter.Gutter;
import org.eclipse.che.ide.api.editor.text.TextPosition;
import org.eclipse.che.ide.ui.Tooltip;
import org.eclipse.che.ide.ui.menu.PositionController;
import org.eclipse.che.ide.util.loging.Log;
import elemental.dom.Element;
import elemental.events.Event;
import elemental.events.EventListener;
/**
* Renderer for annotation marks in gutter (on the left margin of the text).
*/
public class GutterAnnotationRenderer implements AnnotationModelHandler, ClearAnnotationModelHandler {
/** The logger. */
private static Logger LOG = Logger.getLogger(GutterAnnotationRenderer.class.getName());
/** The component responsible for gutter handling. */
private Gutter hasGutter;
/** The document. */
private Document document;
/**
* Sets the component responsible for gutter handling.
* @param hasGutter the component
*/
public void setHasGutter(final Gutter hasGutter) {
this.hasGutter = hasGutter;
}
@Override
public void onAnnotationModel(final AnnotationModelEvent event) {
// remove removed and changed annotations
for (final Annotation annotation : event.getRemovedAnnotations()) {
LOG.fine("Remove annotation: " + annotation);
removeAnnotationItem(event, annotation);
}
for (final Annotation annotation : event.getChangedAnnotations()) {
LOG.fine("Remove changed annotation: " + annotation);
removeAnnotationItem(event, annotation);
}
// add new and changed (new version) annotation
for (final Annotation annotation : event.getAddedAnnotations()) {
LOG.fine("Add annotation: " + annotation);
addAnnotationItem(event.getAnnotationModel(), annotation);
}
for (final Annotation annotation : event.getChangedAnnotations()) {
LOG.fine("Add back changed annotation: " + annotation);
addAnnotationItem(event.getAnnotationModel(), annotation);
}
}
private void removeAnnotationItem(final AnnotationModelEvent event, final Annotation annotation) {
final Position position = event.getPositionOfRemovedAnnotation(annotation);
final TextPosition textPosition = this.document.getPositionFromIndex(position.getOffset());
final Element annotationItem = this.hasGutter.getGutterItem(textPosition.getLine(), ANNOTATION_GUTTER);
if (AnnotationGroupImpl.isAnnotation(annotationItem)) {
final AnnotationGroup group = AnnotationGroupImpl.create(annotationItem);
group.removeAnnotation(annotation, position.getOffset());
if (group.getAnnotationCount() != 0) {
return;
}
}
// else
this.hasGutter.removeGutterItem(textPosition.getLine(), ANNOTATION_GUTTER);
}
private void addAnnotationItem(final AnnotationModel model, final Annotation annotation) {
final Position position = model.getPosition(annotation);
if (position == null) {
Log.warn(GutterAnnotationRenderer.class, "No position for annotation " + annotation);
return;
}
final TextPosition textPosition = this.document.getPositionFromIndex(position.getOffset());
final Element annotationItem = this.hasGutter.getGutterItem(textPosition.getLine(),
ANNOTATION_GUTTER);
AnnotationGroup annotationGroup;
if (!AnnotationGroupImpl.isAnnotation(annotationItem)) {
LOG.fine("Create new annotation group for line " + textPosition.getLine());
final AnnotationGroup newGroup = AnnotationGroupImpl.create();
newGroup.getElement().addEventListener(Event.MOUSEOVER, new EventListener() {
@Override
public void handleEvent(final Event evt) {
showToolTip(newGroup, textPosition.getLine());
}
}, false);
this.hasGutter.addGutterItem(textPosition.getLine(), ANNOTATION_GUTTER,
newGroup.getElement());
annotationGroup = newGroup;
} else {
LOG.fine("Reuse annotation group for line " + textPosition.getLine());
annotationGroup = AnnotationGroupImpl.create(annotationItem);
}
annotationGroup.addAnnotation(annotation, position.getOffset());
}
private void showToolTip(AnnotationGroup item, int line) {
final List<String> messages = new ArrayList<>();
for (final String message : item.getMessages()) {
messages.add(message);
}
Tooltip tooltip = null;
if (messages.size() == 1) {
tooltip = Tooltip.create(item.getElement(),
PositionController.VerticalAlign.MIDDLE,
PositionController.HorizontalAlign.RIGHT,
messages.get(0));
} else if (messages.size() > 1) {
String[] messagesArray = new String[messages.size()];
messagesArray = messages.toArray(messagesArray);
tooltip = ListTooltipFactory.create(item.getElement(),
"Multiple markers on this line:",
PositionController.VerticalAlign.MIDDLE,
PositionController.HorizontalAlign.RIGHT,
messagesArray);
}
if (tooltip != null) {
tooltip.show();
}
}
public void setDocument(final Document document) {
this.document = document;
}
@Override
public void onClearModel(final ClearAnnotationModelEvent event) {
this.hasGutter.clearGutter(ANNOTATION_GUTTER);
}
}