package de.ovgu.cide.editor.inlineprojection;
import java.util.Iterator;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.AnnotationRulerColumn;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.jface.text.source.projection.IProjectionPosition;
import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
import org.eclipse.jface.text.source.projection.ProjectionSupport;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
/**
* A ruler column for controlling the behavior of a
* {@link org.eclipse.jface.text.source.projection.ProjectionViewer}.
*
* @since 3.0
*/
class InlineProjectionRulerColumn extends AnnotationRulerColumn {
private ProjectionAnnotation fCurrentAnnotation;
/**
* Creates a new projection ruler column.
*
* @param model the column's annotation model
* @param width the width in pixels
* @param annotationAccess the annotation access
*/
public InlineProjectionRulerColumn(IAnnotationModel model, int width, IAnnotationAccess annotationAccess) {
super(model, width, annotationAccess);
}
/**
* Creates a new projection ruler column.
*
* @param width the width in pixels
* @param annotationAccess the annotation access
*/
public InlineProjectionRulerColumn(int width, IAnnotationAccess annotationAccess) {
super(width, annotationAccess);
}
/*
* @see org.eclipse.jface.text.source.AnnotationRulerColumn#mouseClicked(int)
*/
protected void mouseClicked(int line) {
clearCurrentAnnotation();
ProjectionAnnotation annotation= findAnnotation(line, true);
if (annotation != null) {
ProjectionAnnotationModel model= (ProjectionAnnotationModel) getModel();
model.toggleExpansionState(annotation);
}
}
/**
* Returns the projection annotation of the column's annotation
* model that contains the given line.
*
* @param line the line
* @param exact <code>true</code> if the annotation range must match exactly
* @return the projection annotation containing the given line
*/
private ProjectionAnnotation findAnnotation(int line, boolean exact) {
ProjectionAnnotation previousAnnotation= null;
IAnnotationModel model= getModel();
if (model != null) {
IDocument document= getCachedTextViewer().getDocument();
int previousDistance= Integer.MAX_VALUE;
Iterator e= model.getAnnotationIterator();
while (e.hasNext()) {
Object next= e.next();
if (next instanceof ProjectionAnnotation) {
ProjectionAnnotation annotation= (ProjectionAnnotation) next;
Position p= model.getPosition(annotation);
if (p == null)
continue;
int distance= getDistance(annotation, p, document, line);
if (distance == -1)
continue;
if (!exact) {
if (distance < previousDistance) {
previousAnnotation= annotation;
previousDistance= distance;
}
} else if (distance == 0) {
previousAnnotation= annotation;
}
}
}
}
return previousAnnotation;
}
/**
* Returns the distance of the given line to the start line of the given position in the given document. The distance is
* <code>-1</code> when the line is not included in the given position.
*
* @param annotation the annotation
* @param position the position
* @param document the document
* @param line the line
* @return <code>-1</code> if line is not contained, a position number otherwise
*/
private int getDistance(ProjectionAnnotation annotation, Position position, IDocument document, int line) {
if (position.getOffset() > -1 && position.getLength() > -1) {
try {
int startLine= document.getLineOfOffset(position.getOffset());
int endLine= document.getLineOfOffset(position.getOffset() + position.getLength());
if (startLine <= line && line < endLine) {
if (annotation.isCollapsed()) {
int captionOffset;
if (position instanceof IProjectionPosition)
captionOffset= ((IProjectionPosition) position).computeCaptionOffset(document);
else
captionOffset= 0;
int captionLine= document.getLineOfOffset(position.getOffset() + captionOffset);
if (startLine <= captionLine && captionLine < endLine)
return Math.abs(line - captionLine);
}
return line - startLine;
}
} catch (BadLocationException x) {
}
}
return -1;
}
private boolean clearCurrentAnnotation() {
if (fCurrentAnnotation != null) {
fCurrentAnnotation.setRangeIndication(false);
fCurrentAnnotation= null;
return true;
}
return false;
}
/*
* @see org.eclipse.jface.text.source.IVerticalRulerColumn#createControl(org.eclipse.jface.text.source.CompositeRuler, org.eclipse.swt.widgets.Composite)
*/
public Control createControl(CompositeRuler parentRuler, Composite parentControl) {
Control control= super.createControl(parentRuler, parentControl);
// set background
Display display= parentControl.getDisplay();
Color background= display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
control.setBackground(background);
// install hover listener
control.addMouseTrackListener(new MouseTrackAdapter() {
public void mouseExit(MouseEvent e) {
if (clearCurrentAnnotation())
redraw();
}
});
// install mouse move listener
control.addMouseMoveListener(new MouseMoveListener() {
public void mouseMove(MouseEvent e) {
boolean redraw= false;
ProjectionAnnotation annotation= findAnnotation(toDocumentLineNumber(e.y), false);
if (annotation != fCurrentAnnotation) {
if (fCurrentAnnotation != null) {
fCurrentAnnotation.setRangeIndication(false);
redraw= true;
}
fCurrentAnnotation= annotation;
if (fCurrentAnnotation != null && !fCurrentAnnotation.isCollapsed()) {
fCurrentAnnotation.setRangeIndication(true);
redraw= true;
}
}
if (redraw)
redraw();
}
});
return control;
}
/*
* @see org.eclipse.jface.text.source.AnnotationRulerColumn#setModel(org.eclipse.jface.text.source.IAnnotationModel)
*/
public void setModel(IAnnotationModel model) {
if (model instanceof IAnnotationModelExtension) {
IAnnotationModelExtension extension= (IAnnotationModelExtension) model;
model= extension.getAnnotationModel(ProjectionSupport.PROJECTION);
}
super.setModel(model);
}
/*
* @see org.eclipse.jface.text.source.AnnotationRulerColumn#isPropagatingMouseListener()
*/
protected boolean isPropagatingMouseListener() {
return false;
}
/*
* @see org.eclipse.jface.text.source.AnnotationRulerColumn#hasAnnotation(int)
*/
protected boolean hasAnnotation(int lineNumber) {
return findAnnotation(lineNumber, true) != null;
}
}