/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* 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:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cloudfoundry.deployment;
import java.util.Iterator;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextInputListener;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationPainter;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.IAnnotationHover;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.jface.text.source.ISharedTextColors;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.ISourceViewerExtension2;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.StyledTextContent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.RGB;
import org.springframework.ide.eclipse.boot.util.Log;
/**
* Application name annotation support for the {@link ISourceViewer}
*
* @author Alex Boyko
*
*/
public class AppNameAnnotationSupport {
/**
* Unselected application name annotation line color
*/
private static final RGB APP_NAME_COLOR = new RGB(0xBB, 0xBB, 0xBB);
/**
* Selected application name annotation line color
*/
private static final RGB SELECTED_APP_NAME_COLOR = new RGB(0x00, 0x87, 0x00);
/**
* Alpha value for painting application name annotations line highlighting
* (Must be close to 0 to ensure the text behind is visible enough)
*/
private static final int APP_NAME_ANNOTATION_ALPHA = 0x17;
/**
* Width of the application name annotations vertical ruler control
*/
private static final int ANNOTATION_COLUMN_WIDTH = 12;
/**
* The source viewer
*/
private SourceViewer fViewer = null;
/**
* Application name annotations vertical ruler
*/
private AppNameRulerColumn fColumn = null;
/**
* Constant colors cache (passed in)
*/
private ISharedTextColors fColorsCache;
/**
* Listen to viewer input changes to attach the application name annotations model if necessary
*/
private ITextInputListener textInputListener = new ITextInputListener() {
public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
}
@Override
public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
IAnnotationModel annotationModel = fViewer.getVisualAnnotationModel();
if (annotationModel instanceof IAnnotationModelExtension) {
IAnnotationModelExtension extension = (IAnnotationModelExtension) annotationModel;
if (extension.getAnnotationModel(AppNameAnnotationModel.APP_NAME_MODEL_KEY) == null) {
extension.addAnnotationModel(AppNameAnnotationModel.APP_NAME_MODEL_KEY, new AppNameAnnotationModel());
}
}
fColumn.setModel(annotationModel);
}
};
public AppNameAnnotationSupport(SourceViewer viewer, IAnnotationAccess annotationAccess, ISharedTextColors colorsCache) {
super();
fViewer = viewer;
fViewer.addTextInputListener(textInputListener);
fColorsCache = colorsCache;
fColumn = new AppNameRulerColumn(ANNOTATION_COLUMN_WIDTH, annotationAccess);
fColumn.addAnnotationType(AppNameAnnotation.TYPE);
/*
* Setup tooltip for application name annotations
*/
fColumn.setHover(new IAnnotationHover() {
@Override
public String getHoverInfo(ISourceViewer sourceViewer, int lineNumber) {
for (Iterator<?> itr = fColumn.getModel().getAnnotationIterator(); itr.hasNext();) {
Object o = itr.next();
if (o instanceof AppNameAnnotation) {
AppNameAnnotation a = (AppNameAnnotation) o;
Position p = fColumn.getModel().getPosition(a);
try {
if (fViewer.getDocument().getLineOfOffset(p.getOffset()) <= lineNumber && fViewer.getDocument().getLineOfOffset(p.getOffset() + p.getLength() - 1) >= lineNumber) {
String hoverText = "Application '" + a.getText() + "'";
if (a.isSelected()) {
hoverText += " is selected";
}
return hoverText;
}
} catch (BadLocationException e) {
Log.log(e);
}
}
}
return null;
}
});
/*
* Setup application name line highlight painting on the viewer's text widget
*/
AppNameAnnotationsPainter annotationPainter= new AppNameAnnotationsPainter(fViewer, annotationAccess);
annotationPainter.addDrawingStrategy(AppNameAnnotationModel.APP_NAME_MODEL_KEY, new AppNameDrawingStrategy());
annotationPainter.addAnnotationType(AppNameAnnotation.TYPE, AppNameAnnotationModel.APP_NAME_MODEL_KEY);
annotationPainter.setAnnotationTypeColor(AppNameAnnotation.TYPE, fViewer.getControl().getDisplay().getSystemColor(SWT.COLOR_GRAY));
fViewer.addPainter(annotationPainter);
/*
* Attach application annotations ruler to the viewer
*/
fViewer.addVerticalRulerColumn(fColumn);
}
public void dispose() {
fViewer.removeTextInputListener(textInputListener);
}
/**
* Finds and returns viewer's application name annotations model
*
* @param viewer Source viewer
* @return Viewer's application name annotations model
*/
public static AppNameAnnotationModel getAppNameAnnotationModel(ISourceViewer viewer) {
IAnnotationModel model = viewer instanceof ISourceViewerExtension2
? ((ISourceViewerExtension2) viewer).getVisualAnnotationModel() : viewer.getAnnotationModel();
AppNameAnnotationModel appNameModel = null;
if (model instanceof IAnnotationModelExtension) {
appNameModel = (AppNameAnnotationModel) ((IAnnotationModelExtension) model)
.getAnnotationModel(AppNameAnnotationModel.APP_NAME_MODEL_KEY);
} else if (model instanceof AppNameAnnotationModel) {
appNameModel = (AppNameAnnotationModel) model;
}
return appNameModel;
}
private static class AppNameAnnotationsPainter extends AnnotationPainter {
/**
* Creates a new painter indicating the location of collapsed regions.
*
* @param sourceViewer the source viewer for the painter
* @param access the annotation access
*/
public AppNameAnnotationsPainter(ISourceViewer sourceViewer, IAnnotationAccess access) {
super(sourceViewer, access);
}
/*
* @see org.eclipse.jface.text.source.AnnotationPainter#findAnnotationModel(org.eclipse.jface.text.source.ISourceViewer)
*/
protected IAnnotationModel findAnnotationModel(ISourceViewer sourceViewer) {
return getAppNameAnnotationModel(sourceViewer);
}
/*
* @see org.eclipse.jface.text.source.AnnotationPainter#skip(org.eclipse.jface.text.source.Annotation)
*/
protected boolean skip(Annotation annotation) {
return !(annotation instanceof AppNameAnnotation);
}
}
private class AppNameDrawingStrategy implements AnnotationPainter.IDrawingStrategy {
/*
* @see org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy#draw(org.eclipse.swt.graphics.GC, org.eclipse.swt.custom.StyledText, int, int, org.eclipse.swt.graphics.Color)
*/
public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) {
if (annotation instanceof AppNameAnnotation) {
AppNameAnnotation a = (AppNameAnnotation) annotation;
StyledTextContent content = textWidget.getContent();
final int line = content.getLineAtOffset(offset);
if (gc == null) {
/*
* Clear off highlighting case
*/
textWidget.setLineBackground(line, 1, null);
textWidget.redrawRange(offset, length, true);
} else {
/*
* Show highlighting case IMPORTANT: do not modify
* 'textWidget' graphical parameters! It would start
* scheduling async updates indefinitely! Hence other parts
* of the UI won't have a chance to repaint themselves
*/
Position p = fColumn.getModel().getPosition(annotation);
if (p != null && line == content.getLineAtOffset(p.getOffset())) {
/*
* Draw transparent line highlight rectangle. Ensure
* it's transparent such that text behind it is visible
*/
Color lineColor = a.isSelected() ? fColorsCache.getColor(SELECTED_APP_NAME_COLOR)
: fColorsCache.getColor(APP_NAME_COLOR);
Color c = gc.getBackground();
int opacity = gc.getAlpha();
gc.setBackground(lineColor);
gc.setAlpha(APP_NAME_ANNOTATION_ALPHA);
gc.fillRectangle(0, textWidget.getLocationAtOffset(offset).y, textWidget.getClientArea().width,
textWidget.getLineHeight());
gc.setAlpha(opacity);
gc.setBackground(c);
}
}
}
}
}
}