blob: 09b1be4f9c821225cc9962ff2e992266eed8b6f6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jface.text.source.projection;
import java.util.ArrayList;
import java.util.List;
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.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
import org.eclipse.jface.text.IInformationControlCreator;
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.ISharedTextColors;
import org.eclipse.jface.text.source.ISourceViewer;
/**
* Supports the configuration of projection capabilities a {@link org.eclipse.jface.text.source.projection.ProjectionViewer}.
* <p>
* This class is not intended to be subclassed. Clients are supposed to configure and use it as is.</p>
*
* @since 3.0
* @noextend This class is not intended to be subclassed by clients.
*/
public class ProjectionSupport {
/**
* Key of the projection annotation model inside the visual annotation
* model. Also internally used as key for the projection drawing strategy.
*/
public final static Object PROJECTION= new Object();
private static class ProjectionAnnotationsPainter 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 ProjectionAnnotationsPainter(ISourceViewer sourceViewer, IAnnotationAccess access) {
super(sourceViewer, access);
}
@Override
protected IAnnotationModel findAnnotationModel(ISourceViewer sourceViewer) {
if (sourceViewer instanceof ProjectionViewer) {
ProjectionViewer projectionViewer= (ProjectionViewer) sourceViewer;
return projectionViewer.getProjectionAnnotationModel();
}
return null;
}
@Override
protected boolean skip(Annotation annotation) {
if (annotation instanceof ProjectionAnnotation)
return !((ProjectionAnnotation) annotation).isCollapsed();
return super.skip(annotation);
}
}
private static class ProjectionDrawingStrategy implements AnnotationPainter.IDrawingStrategy {
@Override
public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) {
if (annotation instanceof ProjectionAnnotation) {
ProjectionAnnotation projectionAnnotation= (ProjectionAnnotation) annotation;
if (projectionAnnotation.isCollapsed()) {
if (gc != null) {
StyledTextContent content= textWidget.getContent();
int line= content.getLineAtOffset(offset);
int lineStart= content.getOffsetAtLine(line);
String text= content.getLine(line);
int lineLength= text == null ? 0 : text.length();
int lineEnd= lineStart + lineLength;
Point p= textWidget.getLocationAtOffset(lineEnd);
Color c= gc.getForeground();
gc.setForeground(color);
FontMetrics metrics= gc.getFontMetrics();
// baseline: where the dots are drawn
int baseline= textWidget.getBaseline(offset);
// descent: number of pixels that the box extends over baseline
int descent= Math.min(2, textWidget.getLineHeight(offset) - baseline);
// ascent: so much does the box stand up from baseline
int ascent= metrics.getAscent();
// leading: free space from line top to box upper line
int leading= baseline - ascent;
// height: height of the box
int height= ascent + descent;
int width= metrics.getAverageCharWidth();
gc.drawRectangle(p.x, p.y + leading, width, height);
int third= width/3;
int dotsVertical= p.y + baseline - 1;
gc.drawPoint(p.x + third, dotsVertical);
gc.drawPoint(p.x + width - third, dotsVertical);
gc.setForeground(c);
} else {
textWidget.redrawRange(offset, length, true);
}
}
}
}
}
private class ProjectionListener implements IProjectionListener {
@Override
public void projectionEnabled() {
doEnableProjection();
}
@Override
public void projectionDisabled() {
doDisableProjection();
}
}
private ProjectionViewer fViewer;
private IAnnotationAccess fAnnotationAccess;
private ISharedTextColors fSharedTextColors;
private List<String> fSummarizableTypes;
private IInformationControlCreator fInformationControlCreator;
private IInformationControlCreator fInformationPresenterControlCreator;
private ProjectionListener fProjectionListener;
private ProjectionAnnotationsPainter fPainter;
private ProjectionRulerColumn fColumn;
/**
* @since 3.1
*/
private AnnotationPainter.IDrawingStrategy fDrawingStrategy;
/**
* Creates new projection support for the given projection viewer. Initially,
* no annotation types are summarized. A default hover control creator and a
* default drawing strategy are used.
*
* @param viewer the projection viewer
* @param annotationAccess the annotation access
* @param sharedTextColors the shared text colors to use
*/
public ProjectionSupport(ProjectionViewer viewer, IAnnotationAccess annotationAccess, ISharedTextColors sharedTextColors) {
fViewer= viewer;
fAnnotationAccess= annotationAccess;
fSharedTextColors= sharedTextColors;
}
/**
* Marks the given annotation type to be considered when creating summaries for
* collapsed regions of the projection viewer.
* <p>
* A summary is an annotation that gets created out of all annotations with a
* type that has been registered through this method and that are inside the
* folded region.
* </p>
*
* @param annotationType the annotation type to consider
*/
public void addSummarizableAnnotationType(String annotationType) {
if (fSummarizableTypes == null) {
fSummarizableTypes= new ArrayList<>();
fSummarizableTypes.add(annotationType);
} else if (!fSummarizableTypes.contains(annotationType))
fSummarizableTypes.add(annotationType);
}
/**
* Marks the given annotation type to be ignored when creating summaries for
* collapsed regions of the projection viewer. This method has only an effect
* when <code>addSummarizableAnnotationType</code> has been called before for
* the give annotation type.
* <p>
* A summary is an annotation that gets created out of all annotations with a
* type that has been registered through this method and that are inside the
* folded region.
* </p>
*
* @param annotationType the annotation type to remove
*/
public void removeSummarizableAnnotationType(String annotationType) {
if (fSummarizableTypes != null) {
fSummarizableTypes.remove(annotationType);
if (fSummarizableTypes.size() == 0)
fSummarizableTypes= null;
}
}
/**
* Sets the hover control creator that is used for the annotation hovers
* that are shown in the projection viewer's projection ruler column.
*
* @param creator the hover control creator
*/
public void setHoverControlCreator(IInformationControlCreator creator) {
fInformationControlCreator= creator;
}
/**
* Sets the information presenter control creator that is used for the annotation
* hovers that are shown in the projection viewer's projection ruler column.
*
* @param creator the information presenter control creator
* @since 3.3
*/
public void setInformationPresenterControlCreator(IInformationControlCreator creator) {
fInformationPresenterControlCreator= creator;
}
/**
* Sets the drawing strategy that the projection support's annotation
* painter uses to draw the indication of collapsed regions onto the
* projection viewer's text widget. When <code>null</code> is passed in,
* the drawing strategy is reset to the default. In order to avoid any
* representation use {@link org.eclipse.jface.text.source.AnnotationPainter.NullStrategy}.
*
* @param strategy the drawing strategy or <code>null</code> to reset the
* strategy to the default
* @since 3.1
*/
public void setAnnotationPainterDrawingStrategy(AnnotationPainter.IDrawingStrategy strategy) {
fDrawingStrategy= strategy;
}
/**
* Returns the drawing strategy to be used by the support's annotation painter.
*
* @return the drawing strategy to be used by the support's annotation painter
* @since 3.1
*/
private AnnotationPainter.IDrawingStrategy getDrawingStrategy() {
if (fDrawingStrategy == null)
fDrawingStrategy= new ProjectionDrawingStrategy();
return fDrawingStrategy;
}
/**
* Installs this projection support on its viewer.
*/
public void install() {
fViewer.setProjectionSummary(createProjectionSummary());
fProjectionListener= new ProjectionListener();
fViewer.addProjectionListener(fProjectionListener);
}
/**
* Disposes this projection support.
*/
public void dispose() {
if (fProjectionListener != null) {
fViewer.removeProjectionListener(fProjectionListener);
fProjectionListener= null;
}
}
/**
* Enables projection mode. If not yet done, installs the projection ruler
* column in the viewer's vertical ruler and installs a painter that
* indicate the locations of collapsed regions.
*
*/
protected void doEnableProjection() {
if (fPainter == null) {
fPainter= new ProjectionAnnotationsPainter(fViewer, fAnnotationAccess);
fPainter.addDrawingStrategy(PROJECTION, getDrawingStrategy());
fPainter.addAnnotationType(ProjectionAnnotation.TYPE, PROJECTION);
fPainter.setAnnotationTypeColor(ProjectionAnnotation.TYPE, fSharedTextColors.getColor(getColor()));
fViewer.addPainter(fPainter);
}
if (fColumn == null) {
fColumn= new ProjectionRulerColumn(9, fAnnotationAccess);
fColumn.addAnnotationType(ProjectionAnnotation.TYPE);
fColumn.setHover(createProjectionAnnotationHover());
fViewer.addVerticalRulerColumn(fColumn);
}
fColumn.setModel(fViewer.getVisualAnnotationModel());
}
/**
* Removes the projection ruler column and the painter from the projection
* viewer.
*/
protected void doDisableProjection() {
if (fPainter != null) {
fViewer.removePainter(fPainter);
fPainter.dispose();
fPainter= null;
}
if (fColumn != null) {
fViewer.removeVerticalRulerColumn(fColumn);
fColumn= null;
}
}
private ProjectionSummary createProjectionSummary() {
ProjectionSummary summary= new ProjectionSummary(fViewer, fAnnotationAccess);
if (fSummarizableTypes != null) {
int size= fSummarizableTypes.size();
for (int i= 0; i < size; i++)
summary.addAnnotationType(fSummarizableTypes.get(i));
}
return summary;
}
private IAnnotationHover createProjectionAnnotationHover() {
ProjectionAnnotationHover hover= new ProjectionAnnotationHover();
hover.setHoverControlCreator(fInformationControlCreator);
hover.setInformationPresenterControlCreator(fInformationPresenterControlCreator);
return hover;
}
/**
* Implements the contract of {@link org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)}
* by forwarding the adapter requests to the given viewer.
*
* @param viewer the viewer
* @param required the required class of the adapter
* @return the adapter or <code>null</code>
*
*/
@SuppressWarnings("unchecked")
public <T> T getAdapter(ISourceViewer viewer, Class<T> required) {
if (ProjectionAnnotationModel.class.equals(required)) {
if (viewer instanceof ProjectionViewer) {
ProjectionViewer projectionViewer= (ProjectionViewer) viewer;
return (T) projectionViewer.getProjectionAnnotationModel();
}
}
return null;
}
private RGB getColor() {
// TODO read out preference settings
Color c= Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY);
return c.getRGB();
}
}