/*******************************************************************************
* Copyright (c) 2010, 2011 Obeo.
* 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.docs.intent.client.ui.editor.annotation.image;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.mylyn.docs.intent.client.ui.editor.IntentEditor;
import org.eclipse.mylyn.docs.intent.client.ui.editor.configuration.IntentFontConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
/**
* An {@link IDrawingStrategy} used to display images as described in {@link AbstractIntentImageAnnotation}s.
*
* @author <a href="mailto:alex.lagarde@obeo.fr">Alex Lagarde</a>
*/
public class IntentImageAnnotationDrawingStrategy implements IDrawingStrategy {
/**
* The {@link IntentEditor} holding the {@link AbstractIntentImageAnnotation}s to paint.
*/
private IntentEditor editor;
/**
* The {@link ISourceViewer} holding the {@link AbstractIntentImageAnnotation}s to paint.
*/
private final ISourceViewer viewer;
/**
* Default constructor.
*
* @param editor
* the {@link IntentEditor} holding the {@link AbstractIntentImageAnnotation}s to paint
* @param viewer
* the {@link ISourceViewer} holding the {@link AbstractIntentImageAnnotation}s to paint
*/
public IntentImageAnnotationDrawingStrategy(IntentEditor editor, ISourceViewer viewer) {
this.editor = editor;
this.viewer = viewer;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy#draw(org.eclipse.jface.text.source.Annotation,
* 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 eventOffset, int length,
Color color) {
// If the Intent editor has not yet initiated its folding structure, we do not paint images (only
// paint annotations that will actually be displayed to the end-user)
if (!(annotation instanceof AbstractIntentImageAnnotation)
|| !editor.isInitialFoldingStructureComplete()) {
return;
}
try {
int offset = eventOffset;
if (gc != null) {
Position position = viewer.getAnnotationModel().getPosition(annotation);
if (position != null) {
// Step 1: get the image to paint
AbstractIntentImageAnnotation imageAnnotation = (AbstractIntentImageAnnotation)annotation;
Image image = imageAnnotation.getImage();
// If image is not available, get the default SWT ICON_QUESTION
if (image != null && !image.isDisposed()) {
// Step 2: get position
offset = position.offset;
Point imagePosition = computeImagePosition(textWidget, offset, length, position);
// Step 3: paint the image background as rectangle
Color foreground = gc.getForeground();
Color background = gc.getBackground();
gc.setForeground(textWidget.getForeground());
gc.setBackground(textWidget.getBackground());
Rectangle bounds = image.getBounds();
gc.fillRectangle(new Rectangle(imagePosition.x, imagePosition.y, bounds.width,
bounds.height));
// Step 4: update style range so that the font size is equals to the image height
updateStyleRange(textWidget, offset, bounds.height, gc);
// Step 5: draw image
gc.setForeground(foreground);
gc.setBackground(background);
gc.drawImage(image, imagePosition.x, imagePosition.y);
}
} else {
updateStyleRange(textWidget, offset, 1, gc);
}
} else {
// Calling redraw on the text widget
textWidget.redrawRange(offset, 1, true);
}
} catch (IllegalArgumentException e) {
// Silent catch
}
}
/**
* Computes the position of the image to draw.
*
* @param textWidget
* the {@link StyledText} on which the image will be painted
* @param offset
* the offset
* @param length
* the length
* @param position
* the position
* @return the position of the image to draw
*/
private Point computeImagePosition(StyledText textWidget, int offset, int length, Position position) {
Point imagePosition = textWidget.getLocationAtOffset(offset);
if (position == null && length > 0) {
Point right = textWidget.getLocationAtOffset(offset + length);
if (imagePosition.x > right.x) {
imagePosition.x = 0;
imagePosition.y = right.y;
}
}
return imagePosition;
}
/**
* Updates the style range to use a font with sufficient height to cover the whole drawn image.
*
* @param textWidget
* the text widget
* @param offset
* the draw image offset
* @param expectedHeight
* the draw image heigh (in px)
* @param gc
* the current gc
*/
private void updateStyleRange(StyledText textWidget, int offset, int expectedHeight, GC gc) {
// Step 1: determine if we have already overridden style range
Iterable<IntentImageStyleRange> newArrayList = Iterables.filter(
Lists.newArrayList(textWidget.getStyleRangeAtOffset(offset)), IntentImageStyleRange.class);
// Step 2: create a new (or get existing) style range at the image offset
int length = 1;
StyleRange styleRange = null;
if (!newArrayList.iterator().hasNext()) {
styleRange = new IntentImageStyleRange(offset, length, gc.getForeground(), gc.getBackground(),
SWT.NONE);
} else {
styleRange = newArrayList.iterator().next();
}
// Step 3: calculate the font size in points required to cover the whole image
Font oldFont = gc.getFont();
Font referenceFont = IntentFontConstants.getImageReferenceFont();
gc.setFont(referenceFont);
float referenceFontHeightInPX = gc.getFontMetrics().getAscent();
float expectedFontSizeFloat = expectedHeight / referenceFontHeightInPX;
expectedFontSizeFloat += 0.5;
int expectedFontSizeInPoints = Math.round(expectedFontSizeFloat);
gc.setFont(oldFont);
// Step 4: update style ranges if needed
if (styleRange.font == null
|| styleRange.font.getFontData()[0].getHeight() != expectedFontSizeInPoints) {
Font coverringImageFont = new Font(referenceFont.getDevice(),
referenceFont.getFontData()[0].getName(), expectedFontSizeInPoints
* referenceFont.getFontData()[0].getHeight(), SWT.NONE);
styleRange.font = coverringImageFont;
// Step 4: replace style range
StyleRange[] ranges = new StyleRange[1];
ranges[0] = styleRange;
textWidget.replaceStyleRanges(offset, length, ranges);
}
}
}