/*******************************************************************************
* Copyright (c) 2008, 2015 Angelo Zerr 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:
* Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
* IBM Corporation
* Kai Toedter - added radial gradient support
* Robin Stocker - Bug 420035 - [CSS] Support SWT color constants in gradients
* Stefan Winkler <stefan@winklerweb.net> - Bug 459961
*******************************************************************************/
package org.eclipse.e4.ui.css.swt.helpers;
import static org.eclipse.e4.ui.css.swt.helpers.ThemeElementDefinitionHelper.normalizeId;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import org.eclipse.e4.ui.css.core.css2.CSS2ColorHelper;
import org.eclipse.e4.ui.css.core.css2.CSS2RGBColorImpl;
import org.eclipse.e4.ui.css.core.dom.properties.Gradient;
import org.eclipse.e4.ui.css.core.engine.CSSEngine;
import org.eclipse.e4.ui.internal.css.swt.CSSActivator;
import org.eclipse.e4.ui.internal.css.swt.definition.IColorAndFontProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.RGBA;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.w3c.dom.css.CSSPrimitiveValue;
import org.w3c.dom.css.CSSValue;
import org.w3c.dom.css.CSSValueList;
import org.w3c.dom.css.RGBColor;
public class CSSSWTColorHelper {
public static final String COLOR_DEFINITION_MARKER = "#";
private static final Pattern HEX_COLOR_VALUE_PATTERN = Pattern.compile("#[a-fA-F0-9]{6}");
private static Field[] cachedFields;
/*--------------- SWT Color Helper -----------------*/
public static Color getSWTColor(RGBColor rgbColor, Display display) {
RGBA rgb = getRGBA(rgbColor);
return new Color(display, rgb);
}
public static Color getSWTColor(CSSValue value, Display display) {
if (value.getCssValueType() != CSSValue.CSS_PRIMITIVE_VALUE) {
return null;
}
Color color = display.getSystemColor(SWT.COLOR_BLACK);
RGBA rgba = getRGBA((CSSPrimitiveValue) value, display);
if (rgba != null) {
color = new Color(display, rgba.rgb.red, rgba.rgb.green, rgba.rgb.blue, rgba.alpha);
}
return color;
}
private static RGBA getRGBA(CSSPrimitiveValue value, Display display) {
RGBA rgba = getRGBA(value);
if (rgba == null && display != null) {
String name = value.getStringValue();
if (hasColorDefinitionAsValue(name)) {
rgba = findColorByDefinition(name);
} else if (name.contains("-")) {
name = name.replace('-', '_');
rgba = process(display, name);
}
}
return rgba;
}
public static boolean hasColorDefinitionAsValue(CSSValue value) {
if (value.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
CSSPrimitiveValue primitiveValue = (CSSPrimitiveValue) value;
if (primitiveValue.getPrimitiveType() == CSSPrimitiveValue.CSS_STRING) {
return hasColorDefinitionAsValue(primitiveValue
.getStringValue());
}
}
return false;
}
public static boolean hasColorDefinitionAsValue(String name) {
if (name.startsWith(COLOR_DEFINITION_MARKER)) {
return !HEX_COLOR_VALUE_PATTERN.matcher(name).matches();
}
return false;
}
/**
* Process the given string and return a corresponding RGBA object.
*
* @param value
* the SWT constant <code>String</code>
* @return the value of the SWT constant, or <code>SWT.COLOR_BLACK</code> if
* it could not be determined
*/
private static RGBA process(Display display, String value) {
Field [] fields = getFields();
try {
for (Field field : fields) {
if (field.getName().equals(value)) {
return display.getSystemColor(field.getInt(null)).getRGBA();
}
}
} catch (IllegalArgumentException e) {
// no op - shouldnt happen. We check for static before calling
// getInt(null)
} catch (IllegalAccessException e) {
// no op - shouldnt happen. We check for public before calling
// getInt(null)
}
return display.getSystemColor(SWT.COLOR_BLACK).getRGBA();
}
/**
* Get the SWT constant fields.
*
* @return the fields
* @since 3.3
*/
private static Field[] getFields() {
if (cachedFields == null) {
Class<?> clazz = SWT.class;
Field[] allFields = clazz.getDeclaredFields();
ArrayList<Field> applicableFields = new ArrayList<Field>(
allFields.length);
for (Field field : allFields) {
if (field.getType() == Integer.TYPE
&& Modifier.isStatic(field.getModifiers())
&& Modifier.isPublic(field.getModifiers())
&& Modifier.isFinal(field.getModifiers())
&& field.getName().startsWith("COLOR")) { //$NON-NLS-1$
applicableFields.add(field);
}
}
cachedFields = applicableFields.toArray(new Field [applicableFields.size()]);
}
return cachedFields;
}
public static RGBA getRGBA(String name) {
RGBColor color = CSS2ColorHelper.getRGBColor(name);
if (color != null) {
return getRGBA(color);
}
return null;
}
public static RGBA getRGBA(RGBColor color) {
return new RGBA((int) color.getRed().getFloatValue(
CSSPrimitiveValue.CSS_NUMBER), (int) color.getGreen()
.getFloatValue(CSSPrimitiveValue.CSS_NUMBER), (int) color
.getBlue().getFloatValue(CSSPrimitiveValue.CSS_NUMBER),
// for now, we only support solid RGB colors in CSS - our CSS model
// as of now does not have an element for RGBAColor.
255);
}
public static RGBA getRGBA(CSSValue value) {
if (value.getCssValueType() != CSSValue.CSS_PRIMITIVE_VALUE) {
return null;
}
return getRGBA((CSSPrimitiveValue) value);
}
public static RGBA getRGBA(CSSPrimitiveValue value) {
RGBA rgba = null;
switch (value.getPrimitiveType()) {
case CSSPrimitiveValue.CSS_IDENT:
case CSSPrimitiveValue.CSS_STRING:
String string = value.getStringValue();
rgba = getRGBA(string);
break;
case CSSPrimitiveValue.CSS_RGBCOLOR:
RGBColor rgbColor = value.getRGBColorValue();
rgba = getRGBA(rgbColor);
break;
}
return rgba;
}
public static Integer getPercent(CSSPrimitiveValue value) {
int percent = 0;
switch (value.getPrimitiveType()) {
case CSSPrimitiveValue.CSS_PERCENTAGE:
percent = (int) value
.getFloatValue(CSSPrimitiveValue.CSS_PERCENTAGE);
}
return Integer.valueOf(percent);
}
public static Gradient getGradient(CSSValueList list, Display display) {
Gradient gradient = new Gradient();
for (int i = 0; i < list.getLength(); i++) {
CSSValue value = list.item(i);
if (value.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
short primType = ((CSSPrimitiveValue) value).getPrimitiveType();
if (primType == CSSPrimitiveValue.CSS_IDENT) {
if (value.getCssText().equals("gradient")) {
// Skip the keyword "gradient"
continue;
} else if (value.getCssText().equals("linear")) {
gradient.setLinear(true);
continue;
} else if (value.getCssText().equals("radial")) {
gradient.setLinear(false);
continue;
}
}
switch (primType) {
case CSSPrimitiveValue.CSS_IDENT:
case CSSPrimitiveValue.CSS_STRING:
case CSSPrimitiveValue.CSS_RGBCOLOR:
RGBA rgba = getRGBA((CSSPrimitiveValue) value, display);
if (rgba != null) {
// note that in this call we lose the RGBA alpha
// component - we do currently not support alpha
// gradients
gradient.addRGB(rgba, (CSSPrimitiveValue) value);
} else {
//check for vertical gradient
gradient.setVertical(!value.getCssText().equals("false"));
}
break;
case CSSPrimitiveValue.CSS_PERCENTAGE:
gradient.addPercent(getPercent((CSSPrimitiveValue) value));
break;
}
}
}
return gradient;
}
@SuppressWarnings("rawtypes")
public static Color[] getSWTColors(Gradient grad, Display display,
CSSEngine engine) throws Exception {
List values = grad.getValues();
Color[] colors = new Color[values.size()];
for (int i = 0; i < values.size(); i++) {
CSSPrimitiveValue value = (CSSPrimitiveValue) values.get(i);
//We rely on the fact that when a gradient is created, it's colors are converted and in the registry
//TODO see bug #278077
Color color = (Color) engine.convert(value, Color.class, display);
colors[i] = color;
}
return colors;
}
public static int[] getPercents(Gradient grad) {
// There should be exactly one more RGBs. than percent,
// in which case just return the percents as array
if (grad.getRGBs().size() == grad.getPercents().size() + 1) {
int[] percents = new int[grad.getPercents().size()];
for (int i = 0; i < percents.length; i++) {
int value = (grad.getPercents().get(i)).intValue();
if (value < 0 || value > 100) {
// TODO this should be an exception because bad source
// format
return getDefaultPercents(grad);
}
percents[i] = value;
}
return percents;
} else {
// We can get here if either:
// A: the percents are empty (legal) or
// B: size mismatches (error)
// TODO this should be an exception because bad source format
return getDefaultPercents(grad);
}
}
/*
* Compute and return a default array of percentages based on number of
* colors o If two colors, {100} o if three colors, {50, 100} o if four
* colors, {33, 67, 100}
*/
private static int[] getDefaultPercents(Gradient grad) {
// Needed to avoid /0 in increment calc
if (grad.getRGBs().size() <= 1) {
return new int[0];
}
int[] percents = new int[grad.getRGBs().size() - 1];
float increment = 100f / (grad.getRGBs().size() - 1);
for (int i = 0; i < percents.length; i++) {
percents[i] = Math.round((i + 1) * increment);
}
return percents;
}
public static RGBColor getRGBColor(Color color) {
int red = color.getRed();
int green = color.getGreen();
int blue = color.getBlue();
return new CSS2RGBColorImpl(red, green, blue);
}
public static RGBColor getRGBColor(RGB color) {
int red = color.red;
int green = color.green;
int blue = color.blue;
return new CSS2RGBColorImpl(red, green, blue);
}
private static RGBA findColorByDefinition(String name) {
IColorAndFontProvider provider = CSSActivator.getDefault().getColorAndFontProvider();
if (provider != null) {
RGB rgb = provider.getColor(normalizeId(name.substring(1)));
return new RGBA(rgb.red, rgb.green, rgb.blue, 255);
}
return null;
}
/** Simplify testing for color equality */
public static boolean equals(Color c1, Color c2) {
if (c1 == c2) {
return true;
}
if (c1 == null || c2 == null) {
return false;
}
return c1.equals(c2);
}
/** Helper function to avoid setting colors unnecessarily */
public static void setForeground(Control control, Color newColor) {
if (!equals(control.getForeground(), newColor)) {
control.setForeground(newColor);
}
}
/** Helper function to avoid setting colors unnecessarily */
public static void setBackground(Control control, Color newColor) {
if (!equals(control.getBackground(), newColor)) {
control.setBackground(newColor);
}
}
/** Helper function to avoid setting colors unnecessarily */
public static void setSelectionForeground(CTabFolder folder, Color newColor) {
if (!equals(folder.getSelectionForeground(), newColor)) {
folder.setSelectionForeground(newColor);
}
}
/** Helper function to avoid setting colors unnecessarily */
public static void setSelectionBackground(CTabFolder folder, Color newColor) {
if (!equals(folder.getSelectionBackground(), newColor)) {
folder.setSelectionBackground(newColor);
}
}
}