| /*******************************************************************************
|
| * Copyright (c) 2011, 2013 IBM Corporation and others.
|
| * All rights reserved. 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.wst.css.ui.internal.text.hover;
|
|
|
| import org.eclipse.jface.action.Action;
|
| import org.eclipse.jface.action.ToolBarManager;
|
| import org.eclipse.jface.resource.ImageDescriptor;
|
| 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.SWT;
|
| import org.eclipse.swt.graphics.Color;
|
| import org.eclipse.swt.graphics.Point;
|
| import org.eclipse.swt.graphics.RGB;
|
| import org.eclipse.swt.widgets.ColorDialog;
|
| 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.css.ui.internal.CSSUIMessages;
|
| import org.eclipse.wst.css.ui.internal.editor.CSSEditorPluginImages;
|
| import org.eclipse.wst.css.ui.internal.image.CSSImageHelper;
|
| 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 ColorRegion.create((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 static 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 static 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 static 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 static 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 static 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 static class ColorRegion {
|
| ICSSPrimitiveValue value;
|
| RGB rgb;
|
|
|
| public static ColorRegion create(ICSSPrimitiveValue value) {
|
| final RGB rgb = getColorValue(value);
|
| ColorRegion region = null;
|
| if (rgb != null) {
|
| region = new ColorRegion();
|
| region.value = value;
|
| region.rgb = rgb;
|
| }
|
| return region;
|
| }
|
| }
|
|
|
| private class CSSColorInformationControlCreator extends AbstractReusableInformationControlCreator {
|
|
|
| protected IInformationControl doCreateInformationControl(final Shell parent) {
|
| ToolBarManager manager = new ToolBarManager(SWT.FLAT);
|
| final CSSColorInformationControl control = new CSSColorInformationControl(parent, manager);
|
| manager.add(new Action() {
|
| public String getText() {
|
| return CSSUIMessages.CSSHover_ColorAction;
|
| }
|
| public ImageDescriptor getImageDescriptor() {
|
| return CSSImageHelper.getInstance().getImageDescriptor(CSSEditorPluginImages.IMG_OBJ_COLOR_PALETTE);
|
| }
|
| public void run() {
|
| ColorDialog dialog = new ColorDialog(parent);
|
| if (control.region != null && control.region.rgb != null) {
|
| dialog.setRGB(control.region.rgb);
|
| }
|
| final RGB rgb = dialog.open();
|
| if (rgb != null) {
|
| control.setColorValue(rgb);
|
| }
|
| }
|
| });
|
| manager.update(true);
|
|
|
| return control;
|
| }
|
|
|
| }
|
|
|
| /**
|
| * 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;
|
| private ColorRegion region;
|
|
|
| CSSColorInformationControl(Shell shell, ToolBarManager manager) {
|
| super(shell, manager);
|
| 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 ColorRegion) {
|
| region = (ColorRegion) input;
|
| if (region.rgb != null && (fColor == null || !region.rgb.equals(fColor.getRGB()))) {
|
| if (fColor != null) { // Cleanup any old color
|
| fColor.dispose();
|
| }
|
| fColor = new Color(getShell().getDisplay(), region.rgb);
|
| setBackgroundColor(fColor);
|
| }
|
| }
|
| else {
|
| region = null;
|
| }
|
| }
|
|
|
| public IInformationControlCreator getInformationPresenterControlCreator() {
|
| return new CSSColorInformationControlCreator();
|
| }
|
|
|
| public void setColorValue(RGB rgb) {
|
| if (region != null && region.value != null) {
|
| region.value.setStringValue(ICSSPrimitiveValue.CSS_HASH, "#" + getHexString(rgb.red) + getHexString(rgb.green) + getHexString(rgb.blue)); //$NON-NLS-1$
|
| }
|
| }
|
|
|
| /**
|
| * Creates a string of the integer using base 16. For values less than 16, the string is padded with a 0
|
| * @param value the integer
|
| * @return a 0-padded string of the integer using base 16
|
| */
|
| private String getHexString(int value) {
|
| return Integer.toHexString(value | 0x100).substring(1);
|
| }
|
| }
|
| }
|