| /******************************************************************************* |
| * Copyright (c) 2000, 2018 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * 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; |
| |
| double width= metrics.getAverageCharacterWidth(); |
| gc.drawRectangle(p.x, p.y + leading, (int) width, height); |
| int third= (int) (width / 3); |
| int dotsVertical= p.y + baseline - 1; |
| gc.drawPoint(p.x + third, dotsVertical); |
| gc.drawPoint((int) (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.isEmpty()) |
| 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(); |
| } |
| } |