/*******************************************************************************
* 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
*
*******************************************************************************/
package org.eclipse.jface.text.source.projection;
import java.util.ArrayList;
import java.util.List;
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;
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;
/**
* Taken from ProjectionSupport with comments added
*
* @author Kevin Sawicki (ksawicki@aptana.com)
*/
public class UnifiedProjectionSupport
{
/**
* Key of the projection annotation model inside the visual annotation model. Also internally used as key for the
* projection drawing strategy.
*/
public static final 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);
}
/**
* @see org.eclipse.jface.text.source.AnnotationPainter#findAnnotationModel(org.eclipse.jface.text.source.ISourceViewer)
*/
protected IAnnotationModel findAnnotationModel(ISourceViewer sourceViewer)
{
if (sourceViewer instanceof ProjectionViewer)
{
ProjectionViewer projectionViewer = (ProjectionViewer) sourceViewer;
return projectionViewer.getProjectionAnnotationModel();
}
return null;
}
/**
* @see org.eclipse.jface.text.source.AnnotationPainter#skip(org.eclipse.jface.text.source.Annotation)
*/
protected boolean skip(Annotation annotation)
{
if (annotation instanceof ProjectionAnnotation)
{
return !((ProjectionAnnotation) annotation).isCollapsed();
}
return super.skip(annotation);
}
}
private static class ProjectionDrawingStrategy implements AnnotationPainter.IDrawingStrategy
{
/**
* @param annotation
* @param gc
* @param textWidget
* @param offset
* @param length
* @param color
*/
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
{
/**
* @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionEnabled()
*/
public void projectionEnabled()
{
doEnableProjection();
}
/**
* @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionDisabled()
*/
public void projectionDisabled()
{
doDisableProjection();
}
}
private ProjectionViewer fViewer;
private IAnnotationAccess fAnnotationAccess;
private ISharedTextColors fSharedTextColors;
private List fSummarizableTypes;
private IInformationControlCreator fInformationControlCreator;
private ProjectionListener fProjectionListener;
private ProjectionAnnotationsPainter fPainter;
private UnifiedRulerColumn 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 UnifiedProjectionSupport(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 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 UnifiedRulerColumn(14, 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((String) fSummarizableTypes.get(i));
}
}
return summary;
}
private IAnnotationHover createProjectionAnnotationHover()
{
ProjectionAnnotationHover hover = new ProjectionAnnotationHover();
hover.setHoverControlCreator(fInformationControlCreator);
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>
*/
public Object getAdapter(ISourceViewer viewer, Class required)
{
if (ProjectionAnnotationModel.class.equals(required))
{
if (viewer instanceof ProjectionViewer)
{
ProjectionViewer projectionViewer = (ProjectionViewer) viewer;
return projectionViewer.getProjectionAnnotationModel();
}
}
return null;
}
private RGB getColor()
{
// TODO read out preference settings
Color c = Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY);
return c.getRGB();
}
/**
* Gets the folding column
*
* @return - folding ruler
*/
public UnifiedRulerColumn getRulerColumn()
{
return fColumn;
}
}