package com.intellij.javascript.flex.mxml; import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.daemon.LineMarkerSettings; import com.intellij.javascript.flex.css.FlexCssPropertyDescriptor; import com.intellij.javascript.flex.mxml.schema.AnnotationBackedDescriptorImpl; import com.intellij.lang.annotation.Annotation; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.lang.annotation.Annotator; import com.intellij.lang.javascript.JavaScriptSupportLoader; import com.intellij.lang.javascript.flex.FlexBundle; import com.intellij.lang.javascript.psi.JSCommonTypeNames; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.markup.GutterIconRenderer; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.css.impl.util.CssPsiColorUtil; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.ui.ColorChooser; import com.intellij.ui.ColorLineMarkerProvider; import com.intellij.util.ui.EmptyIcon; import com.intellij.util.ui.JBUI; import com.intellij.xml.XmlAttributeDescriptor; import com.intellij.xml.util.ColorIconCache; import com.intellij.xml.util.ColorMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.util.Locale; /** * @author Eugene.Kudelevsky */ public class FlexMxmlColorAnnotator implements Annotator { @Override public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { if (!(element instanceof XmlAttribute) || !JavaScriptSupportLoader.isFlexMxmFile(element.getContainingFile())) { return; } if (!LineMarkerSettings.getSettings().isEnabled(new ColorLineMarkerProvider())) { return; } XmlAttribute attribute = (XmlAttribute)element; XmlAttributeDescriptor descriptor = attribute.getDescriptor(); if (!(descriptor instanceof AnnotationBackedDescriptorImpl)) { return; } AnnotationBackedDescriptorImpl annotationBackedDescriptor = (AnnotationBackedDescriptorImpl)descriptor; String format = annotationBackedDescriptor.getFormat(); if (!FlexCssPropertyDescriptor.COLOR_FORMAT.equals(format)) { return; } final String value = attribute.getValue(); if (value == null || value.length() == 0) { return; } if (!JSCommonTypeNames.ARRAY_CLASS_NAME.equals(annotationBackedDescriptor.getType())) { XmlAttributeValue valueElement = attribute.getValueElement(); if (valueElement != null) { Annotation annotation = holder.createInfoAnnotation(valueElement, null); annotation.setGutterIconRenderer(new MyRenderer(value, attribute)); } } } @Nullable private static Color getColor(@NotNull String colorValue) { try { int num = Integer.parseInt(colorValue); //noinspection UseJBColor return new Color(num); } catch (NumberFormatException ignored) { } String hex = toCanonicalHex(colorValue, false); if (hex == null) { return null; } try { return Color.decode(hex); } catch (NumberFormatException ignored) { } return null; } @Nullable private static String toCanonicalHex(String colorValue, boolean cssStyle) { if (colorValue.startsWith("#")) { if (cssStyle) return colorValue; return "0x" + colorValue.substring(1); } if (colorValue.startsWith("0x")) { if (!cssStyle) return colorValue; return "#" + colorValue.substring(2); } colorValue = colorValue.toLowerCase(Locale.US); if (ColorMap.isStandardColor(colorValue)) { String hex = ColorMap.getHexCodeForColorName(colorValue); if (hex != null) { return toCanonicalHex(hex, cssStyle); } } return null; } public static class MyRenderer extends GutterIconRenderer { private static final int ICON_SIZE = 8; private final String myColorValue; private final XmlAttribute myAttribute; private MyRenderer(String colorValue, XmlAttribute attribute) { myColorValue = colorValue; myAttribute = attribute; } @NotNull @Override public Icon getIcon() { Color color = getColor(myColorValue); if (color != null) { return JBUI.scale(new ColorIconCache.ColorIcon(ICON_SIZE, color)); } return JBUI.scale(EmptyIcon.create(ICON_SIZE)); } @Override public String getTooltipText() { String hex = toCanonicalHex(myColorValue, true); if (hex == null) { return null; } Color color = null; try { color = Color.decode(hex); } catch (NumberFormatException ignored) { } if (color == null) { return null; } StringBuilder builder = new StringBuilder("<html><body><table style=\"padding: 2px 0;\" cellspacing=\"2\">"); String attributeName = myAttribute.getName(); builder.append("<tr><td valign=\"bottom\">").append(attributeName).append(":</td><td style=\"background-color:"); final float[] hsb = new float[3]; Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), hsb); final String textColor = hsb[2] > 0.7f ? "black" : "white"; builder.append(hex); builder.append("; color: ").append(textColor).append("; font-family: monospaced;\" valign=\"bottom\">").append(hex) .append("</td></tr></table></body></html>"); return builder.toString(); } @Override public AnAction getClickAction() { return new AnAction() { public void actionPerformed(final AnActionEvent e) { final Editor editor = CommonDataKeys.EDITOR.getData(e.getDataContext()); if (editor != null) { Color currentColor = getColor(myColorValue); final Color color = ColorChooser .chooseColor(editor.getComponent(), FlexBundle.message("flex.choose.color.dialog.title"), currentColor); if (color != null && !color.equals(currentColor)) { final PsiFile psiFile = myAttribute.getContainingFile(); if (!FileModificationService.getInstance().prepareFileForWrite(psiFile)) return; final String hex = CssPsiColorUtil.toHexColor(color); final String mxmlStyleHex = toCanonicalHex(hex, false); ApplicationManager.getApplication().runWriteAction(() -> myAttribute.setValue(mxmlStyleHex)); } } } }; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MyRenderer that = (MyRenderer)o; if (myAttribute != null ? !myAttribute.equals(that.myAttribute) : that.myAttribute != null) return false; if (myColorValue != null ? !myColorValue.equals(that.myColorValue) : that.myColorValue != null) return false; return true; } @Override public int hashCode() { int result = myColorValue != null ? myColorValue.hashCode() : 0; result = 31 * result + (myAttribute != null ? myAttribute.hashCode() : 0); return result; } } }