blob: 21b12225015744c9a07d8ef5b72619c201777e3e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011 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.wst.css.ui.internal.text.hover;
import org.eclipse.jface.text.AbstractInformationControl;
import org.eclipse.jface.text.AbstractReusableInformationControlCreator;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IInformationControlExtension2;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextHover;
import org.eclipse.jface.text.ITextHoverExtension;
import org.eclipse.jface.text.ITextHoverExtension2;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Region;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.wst.css.core.internal.provisional.document.ICSSPrimitiveValue;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.w3c.dom.css.CSSPrimitiveValue;
import org.w3c.dom.css.RGBColor;
public class CSSColorHover implements ITextHover, ITextHoverExtension, ITextHoverExtension2 {
private IInformationControlCreator fInformationControlCreator;
public CSSColorHover() {
}
public Object getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) {
final IDocument document = textViewer.getDocument();
if (document instanceof IStructuredDocument) {
IStructuredModel model = StructuredModelManager.getModelManager().getModelForRead((IStructuredDocument) document);
try {
final IndexedRegion region = model.getIndexedRegion(hoverRegion.getOffset());
if (region instanceof ICSSPrimitiveValue) {
return getColorValue((ICSSPrimitiveValue) region);
}
}
finally {
if (model != null)
model.releaseFromRead();
}
}
return null;
}
public IInformationControlCreator getHoverControlCreator() {
if (fInformationControlCreator == null) {
fInformationControlCreator = new CSSColorInformationControlCreator();
}
return fInformationControlCreator;
}
public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) {
Object result = getHoverInfo2(textViewer, hoverRegion);
return result != null ? result.toString() : null;
}
/**
* Gets an {@link RGB} color value from the primitive value
*
* @param value the primitive CSS value
* @return an RGB value if it can be extracted from the primitive
*/
private RGB getColorValue(ICSSPrimitiveValue value) {
if (value.getParentNode() instanceof RGBColor) {
value = (ICSSPrimitiveValue) value.getParentNode();
}
RGB rgb = null;
switch (value.getPrimitiveType()) {
case CSSPrimitiveValue.CSS_RGBCOLOR:
rgb = getRGB((RGBColor) value);
break;
case ICSSPrimitiveValue.CSS_HASH:
rgb = getRGBFromHex(value.getStringValue());
break;
case CSSPrimitiveValue.CSS_IDENT:
rgb = CSSColorNames.getInstance().getRGB(value.getStringValue());
break;
}
return rgb;
}
/**
* Converts a hex string (either 3-digit or 6-digit notation) into an {@link RGB}. Any
* invalid hex color will result in a null RGB value.
* @param hex rgb value in hexadecimal notation (3- or 6-digit)
* @return an {@link RGB} based on the hexadecimal value. <code>null</code> if the
* rgb value is invalid
*/
private RGB getRGBFromHex(String hex) {
try {
final int length = hex.length();
if (length == 4) { // convert 3-digit notation
final int r = Integer.parseInt(hex.substring(1, 2), 16);
final int g = Integer.parseInt(hex.substring(2, 3), 16);
final int b = Integer.parseInt(hex.substring(3, 4), 16);
return new RGB((r << 4) | r, (g << 4) | g, (b << 4) | b);
}
else if (length == 7) { // convert 6-digit notation
return new RGB(Integer.parseInt(hex.substring(1, 3), 16), Integer.parseInt(hex.substring(3, 5), 16), Integer.parseInt(hex.substring(5, 7), 16));
}
}
catch (NumberFormatException e) {} // Invalid hexcode used
return null;
}
/**
* Provides an {@link RGB} based on an {@link RGBColor}
* @param color The {@link RGBColor} to extra RGB information from
* @return an {@link RGB} based on an {@link RGBColor}
*/
private RGB getRGB(RGBColor color) {
final int red = getColorInt(color.getRed());
final int green = getColorInt(color.getGreen());
final int blue = getColorInt(color.getBlue());
if (red >= 0 && green >= 0 && blue >= 0)
return new RGB(red, green, blue);
return null;
}
private int getColorInt(CSSPrimitiveValue value) {
if (value.getPrimitiveType() == CSSPrimitiveValue.CSS_PERCENTAGE) { // Percentage of 255
final float percentage = value.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
return capIntValue((int) (percentage / 100 * 255));
}
else if (value.getPrimitiveType() == ICSSPrimitiveValue.CSS_INTEGER) {
return capIntValue((int) value.getFloatValue(CSSPrimitiveValue.CSS_NUMBER));
}
return -1;
}
/**
* Caps the integer value between the ranges of 0 and 255, inclusive.
* @param value integer value to cap
* @return a value between 0 and 255, inclusive
*/
private int capIntValue(int value) {
if (value > 255) value = 255;
else if (value < 0) value = 0;
return value;
}
public IRegion getHoverRegion(ITextViewer textViewer, int offset) {
final IDocument document = textViewer.getDocument();
if (document instanceof IStructuredDocument) {
IStructuredModel model = StructuredModelManager.getModelManager().getModelForRead((IStructuredDocument) document);
try {
IndexedRegion region = model.getIndexedRegion(offset);
if (region instanceof ICSSPrimitiveValue) {
// Shift the hover region to the rgb() function instead of the individual numbers
if (((ICSSPrimitiveValue) region).getParentNode() instanceof RGBColor) {
region = (IndexedRegion) ((ICSSPrimitiveValue) region).getParentNode();
}
}
if (region != null) {
return new Region(region.getStartOffset(), region.getLength());
}
}
finally {
if (model != null)
model.releaseFromRead();
}
}
return null;
}
private class CSSColorInformationControlCreator extends AbstractReusableInformationControlCreator {
protected IInformationControl doCreateInformationControl(Shell parent) {
return new CSSColorInformationControl(parent);
}
}
/**
* An information control that simply displays color information.
* The control takes an {@link RGB} input value and sets its background
* to the corresponding color.
*
*/
private class CSSColorInformationControl extends AbstractInformationControl implements IInformationControlExtension2 {
private Color fColor;
CSSColorInformationControl(Shell shell) {
super(shell, false);
create();
}
public Point computeSizeConstraints(int widthInChars, int heightInChars) {
return new Point(50, 50);
}
public boolean hasContents() {
return fColor != null;
}
protected void createContent(Composite parent) {
}
public void setInformation(String information) {
}
public void dispose() {
if (fColor != null) {
fColor.dispose();
fColor = null;
}
super.dispose();
}
public void setInput(Object input) {
if (input instanceof RGB) {
RGB rgb = (RGB) input;
if (fColor == null || !rgb.equals(fColor.getRGB())) {
if (fColor != null) { // Cleanup any old color
fColor.dispose();
}
fColor = new Color(getShell().getDisplay(), rgb);
setBackgroundColor(fColor);
}
}
}
public IInformationControlCreator getInformationPresenterControlCreator() {
return new CSSColorInformationControlCreator();
}
}
}