| /******************************************************************************* |
| * Copyright (c) 2000, 2017 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 |
| * |
| *******************************************************************************/ |
| package org.eclipse.dltk.ui.text; |
| |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.dltk.ui.ColorPreferenceConverter; |
| import org.eclipse.dltk.ui.PreferenceConstants; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.preference.PreferenceConverter; |
| import org.eclipse.jface.text.TextAttribute; |
| import org.eclipse.jface.text.rules.BufferedRuleBasedScanner; |
| import org.eclipse.jface.text.rules.IRule; |
| import org.eclipse.jface.text.rules.IToken; |
| import org.eclipse.jface.text.rules.Token; |
| import org.eclipse.jface.util.PropertyChangeEvent; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.RGB; |
| import org.eclipse.swt.widgets.Display; |
| |
| /** |
| * Initialized with a color manager and a preference store, its subclasses are |
| * only responsible for providing a list of preference keys for based on which |
| * tokens are generated and to use this tokens to define the rules controlling |
| * this scanner. |
| * <p> |
| * This scanner stores the color defined by the color preference key into the |
| * color manager under the same key. |
| * </p> |
| * <p> |
| * Preference color key + {@link PreferenceConstants#EDITOR_BOLD_SUFFIX} are |
| * used to retrieve whether the token is rendered in bold. |
| * </p> |
| * <p> |
| * Preference color key + {@link PreferenceConstants#EDITOR_ITALIC_SUFFIX} are |
| * used to retrieve whether the token is rendered in italic. |
| * </p> |
| * <p> |
| * Preference color key + |
| * {@link PreferenceConstants#EDITOR_STRIKETHROUGH_SUFFIX} are used to retrieve |
| * whether the token is rendered in strikethrough. |
| * </p> |
| * <p> |
| * Preference color key + {@link PreferenceConstants#EDITOR_UNDERLINE_SUFFIX} |
| * are used to retrieve whether the token is rendered in underline. |
| * </p> |
| */ |
| public abstract class AbstractScriptScanner extends BufferedRuleBasedScanner |
| implements ITokenFactory { |
| |
| private IColorManager fColorManager; |
| private IPreferenceStore fPreferenceStore; |
| |
| private Map<String, Token> fTokenMap = new HashMap<>(); |
| private String[] fPropertyNamesColor; |
| /** |
| * Preference keys for boolean preferences which are <code>true</code>, iff |
| * the corresponding token should be rendered bold. |
| */ |
| private String[] fPropertyNamesBold; |
| /** |
| * Preference keys for boolean preferences which are <code>true</code>, iff |
| * the corresponding token should be rendered italic. |
| * |
| * |
| */ |
| private String[] fPropertyNamesItalic; |
| /** |
| * Preference keys for boolean preferences which are <code>true</code>, iff |
| * the corresponding token should be rendered strikethrough. |
| * |
| * |
| */ |
| private String[] fPropertyNamesStrikethrough; |
| /** |
| * Preference keys for boolean preferences which are <code>true</code>, iff |
| * the corresponding token should be rendered underline. |
| * |
| * |
| */ |
| private String[] fPropertyNamesUnderline; |
| |
| private boolean fNeedsLazyColorLoading; |
| |
| /** |
| * Returns an array of preference keys which define the tokens used in the |
| * rules of this scanner. |
| * <p> |
| * The preference key is used access the color in the preference store and |
| * in the color manager. |
| * </p> |
| * <p> |
| * Preference key + {@link PreferenceConstants#EDITOR_BOLD_SUFFIX} is used |
| * to retrieve whether the token is rendered in bold. |
| * </p> |
| * <p> |
| * Preference key + {@link PreferenceConstants#EDITOR_ITALIC_SUFFIX} is used |
| * to retrieve whether the token is rendered in italic. |
| * </p> |
| * <p> |
| * Preference key + {@link PreferenceConstants#EDITOR_UNDERLINE_SUFFIX} is |
| * used to retrieve whether the token is rendered underlined. |
| * </p> |
| * <p> |
| * Preference key + {@link PreferenceConstants#EDITOR_STRIKETHROUGH_SUFFIX} |
| * is used to retrieve whether the token is rendered stricken out. |
| * </p> |
| */ |
| abstract protected String[] getTokenProperties(); |
| |
| /** |
| * Creates the list of rules controlling this scanner. |
| */ |
| abstract protected List<IRule> createRules(); |
| |
| /** |
| * Creates an abstract script scanner. |
| */ |
| public AbstractScriptScanner(IColorManager manager, |
| IPreferenceStore store) { |
| super(); |
| fColorManager = manager; |
| fPreferenceStore = store; |
| } |
| |
| /** |
| * Must be called after the constructor has been called. |
| */ |
| public final void initialize() { |
| |
| fPropertyNamesColor = getTokenProperties(); |
| int length = fPropertyNamesColor.length; |
| fPropertyNamesBold = new String[length]; |
| fPropertyNamesItalic = new String[length]; |
| fPropertyNamesStrikethrough = new String[length]; |
| fPropertyNamesUnderline = new String[length]; |
| |
| for (int i = 0; i < length; i++) { |
| fPropertyNamesBold[i] = getBoldKey(fPropertyNamesColor[i]); |
| fPropertyNamesItalic[i] = getItalicKey(fPropertyNamesColor[i]); |
| fPropertyNamesStrikethrough[i] = getStrikethroughKey( |
| fPropertyNamesColor[i]); |
| fPropertyNamesUnderline[i] = getUnderlineKey( |
| fPropertyNamesColor[i]); |
| } |
| |
| fNeedsLazyColorLoading = Display.getCurrent() == null; |
| for (int i = 0; i < length; i++) { |
| createToken(i); |
| } |
| |
| initializeRules(); |
| } |
| |
| protected void createToken(int index) { |
| if (fNeedsLazyColorLoading) |
| addTokenWithProxyAttribute(fPropertyNamesColor[index], |
| fPropertyNamesBold[index], fPropertyNamesItalic[index], |
| fPropertyNamesStrikethrough[index], |
| fPropertyNamesUnderline[index]); |
| else |
| addToken(fPropertyNamesColor[index], fPropertyNamesBold[index], |
| fPropertyNamesItalic[index], |
| fPropertyNamesStrikethrough[index], |
| fPropertyNamesUnderline[index]); |
| } |
| |
| protected String getBoldKey(String colorKey) { |
| return colorKey + PreferenceConstants.EDITOR_BOLD_SUFFIX; |
| } |
| |
| protected String getItalicKey(String colorKey) { |
| return colorKey + PreferenceConstants.EDITOR_ITALIC_SUFFIX; |
| } |
| |
| protected String getStrikethroughKey(String colorKey) { |
| return colorKey + PreferenceConstants.EDITOR_STRIKETHROUGH_SUFFIX; |
| } |
| |
| protected String getUnderlineKey(String colorKey) { |
| return colorKey + PreferenceConstants.EDITOR_UNDERLINE_SUFFIX; |
| } |
| |
| @Override |
| public IToken nextToken() { |
| doResolveProxyAttributes(); |
| return super.nextToken(); |
| } |
| |
| /** |
| * @since 3.0 |
| */ |
| protected final void doResolveProxyAttributes() { |
| if (fNeedsLazyColorLoading) |
| resolveProxyAttributes(); |
| } |
| |
| private void resolveProxyAttributes() { |
| if (fNeedsLazyColorLoading && Display.getCurrent() != null) { |
| for (int i = 0; i < fPropertyNamesColor.length; i++) { |
| addToken(fPropertyNamesColor[i], fPropertyNamesBold[i], |
| fPropertyNamesItalic[i], fPropertyNamesStrikethrough[i], |
| fPropertyNamesUnderline[i]); |
| } |
| fNeedsLazyColorLoading = false; |
| } |
| } |
| |
| private void addTokenWithProxyAttribute(String colorKey, String boldKey, |
| String italicKey, String strikethroughKey, String underlineKey) { |
| fTokenMap.put(colorKey, new Token(createTextAttribute(null, boldKey, |
| italicKey, strikethroughKey, underlineKey))); |
| } |
| |
| private void addToken(String colorKey, String boldKey, String italicKey, |
| String strikethroughKey, String underlineKey) { |
| if (fColorManager != null && colorKey != null) { |
| RGB rgb = PreferenceConverter.getColor(fPreferenceStore, colorKey); |
| if (fColorManager instanceof IColorManagerExtension) { |
| IColorManagerExtension ext = (IColorManagerExtension) fColorManager; |
| ext.unbindColor(colorKey); |
| ext.bindColor(colorKey, rgb); |
| } |
| } |
| |
| if (!fNeedsLazyColorLoading) |
| fTokenMap.put(colorKey, new Token(createTextAttribute(colorKey, |
| boldKey, italicKey, strikethroughKey, underlineKey))); |
| else { |
| Token token = fTokenMap.get(colorKey); |
| if (token != null) |
| token.setData(createTextAttribute(colorKey, boldKey, italicKey, |
| strikethroughKey, underlineKey)); |
| } |
| } |
| |
| /** |
| * Create a text attribute based on the given color, bold, italic, |
| * strikethrough and underline preference keys. |
| * |
| * @param colorKey |
| * the color preference key |
| * @param boldKey |
| * the bold preference key |
| * @param italicKey |
| * the italic preference key |
| * @param strikethroughKey |
| * the strikethrough preference key |
| * @param underlineKey |
| * the italic preference key |
| * @return the created text attribute |
| * |
| */ |
| private TextAttribute createTextAttribute(String colorKey, String boldKey, |
| String italicKey, String strikethroughKey, String underlineKey) { |
| Color color = null; |
| if (colorKey != null) |
| color = fColorManager.getColor(colorKey); |
| |
| int style = fPreferenceStore.getBoolean(boldKey) ? SWT.BOLD |
| : SWT.NORMAL; |
| if (fPreferenceStore.getBoolean(italicKey)) |
| style |= SWT.ITALIC; |
| |
| if (fPreferenceStore.getBoolean(strikethroughKey)) |
| style |= TextAttribute.STRIKETHROUGH; |
| |
| if (fPreferenceStore.getBoolean(underlineKey)) |
| style |= TextAttribute.UNDERLINE; |
| |
| return new TextAttribute(color, null, style); |
| } |
| |
| @Override |
| public Token getToken(String key) { |
| doResolveProxyAttributes(); |
| Token token = fTokenMap.get(key); |
| if (token == null) { |
| final int index = fPropertyNamesColor.length; |
| fPropertyNamesColor = resize(fPropertyNamesColor, index, key); |
| fPropertyNamesBold = resize(fPropertyNamesBold, index, |
| getBoldKey(key)); |
| fPropertyNamesItalic = resize(fPropertyNamesItalic, index, |
| getItalicKey(key)); |
| fPropertyNamesStrikethrough = resize(fPropertyNamesStrikethrough, |
| index, getStrikethroughKey(key)); |
| fPropertyNamesUnderline = resize(fPropertyNamesUnderline, index, |
| getUnderlineKey(key)); |
| createToken(index); |
| token = fTokenMap.get(key); |
| Assert.isNotNull(token); |
| } |
| return token; |
| } |
| |
| private static String[] resize(String[] old, int index, String value) { |
| final String[] result = new String[index + 1]; |
| System.arraycopy(old, 0, result, 0, old.length); |
| result[index] = value; |
| return result; |
| } |
| |
| private void initializeRules() { |
| List<IRule> rules = createRules(); |
| if (rules != null) { |
| setRules(rules.toArray(new IRule[rules.size()])); |
| } |
| } |
| |
| private int indexOf(String property) { |
| if (property != null) { |
| int length = fPropertyNamesColor.length; |
| for (int i = 0; i < length; i++) { |
| if (property.equals(fPropertyNamesColor[i]) |
| || property.equals(fPropertyNamesBold[i]) |
| || property.equals(fPropertyNamesItalic[i]) |
| || property.equals(fPropertyNamesStrikethrough[i]) |
| || property.equals(fPropertyNamesUnderline[i])) |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| public boolean affectsBehavior(PropertyChangeEvent event) { |
| return indexOf(event.getProperty()) >= 0; |
| } |
| |
| public void adaptToPreferenceChange(PropertyChangeEvent event) { |
| String p = event.getProperty(); |
| int index = indexOf(p); |
| if (index < 0) |
| return; |
| Token token = getToken(fPropertyNamesColor[index]); |
| if (fPropertyNamesColor[index].equals(p)) |
| adaptToColorChange(token, event); |
| else if (fPropertyNamesBold[index].equals(p)) |
| adaptToStyleChange(token, event, SWT.BOLD); |
| else if (fPropertyNamesItalic[index].equals(p)) |
| adaptToStyleChange(token, event, SWT.ITALIC); |
| else if (fPropertyNamesStrikethrough[index].equals(p)) |
| adaptToStyleChange(token, event, TextAttribute.STRIKETHROUGH); |
| else if (fPropertyNamesUnderline[index].equals(p)) |
| adaptToStyleChange(token, event, TextAttribute.UNDERLINE); |
| } |
| |
| private void adaptToColorChange(Token token, PropertyChangeEvent event) { |
| RGB rgb = ColorPreferenceConverter.asRGB(event.getNewValue()); |
| if (rgb != null) { |
| |
| String property = event.getProperty(); |
| Color color = fColorManager.getColor(property); |
| |
| if ((color == null || !rgb.equals(color.getRGB())) |
| && fColorManager instanceof IColorManagerExtension) { |
| IColorManagerExtension ext = (IColorManagerExtension) fColorManager; |
| |
| ext.unbindColor(property); |
| ext.bindColor(property, rgb); |
| |
| color = fColorManager.getColor(property); |
| } |
| |
| Object data = token.getData(); |
| if (data instanceof TextAttribute) { |
| TextAttribute oldAttr = (TextAttribute) data; |
| token.setData(new TextAttribute(color, oldAttr.getBackground(), |
| oldAttr.getStyle())); |
| } |
| } |
| } |
| |
| private void adaptToStyleChange(Token token, PropertyChangeEvent event, |
| int styleAttribute) { |
| boolean eventValue = false; |
| Object value = event.getNewValue(); |
| if (value instanceof Boolean) |
| eventValue = ((Boolean) value).booleanValue(); |
| else if (IPreferenceStore.TRUE.equals(value)) |
| eventValue = true; |
| |
| Object data = token.getData(); |
| if (data instanceof TextAttribute) { |
| TextAttribute oldAttr = (TextAttribute) data; |
| boolean activeValue = (oldAttr.getStyle() |
| & styleAttribute) == styleAttribute; |
| if (activeValue != eventValue) |
| token.setData(new TextAttribute(oldAttr.getForeground(), |
| oldAttr.getBackground(), |
| eventValue ? oldAttr.getStyle() | styleAttribute |
| : oldAttr.getStyle() & ~styleAttribute)); |
| } |
| } |
| |
| /** |
| * Returns the preference store. |
| * |
| * @return the preference store. |
| * |
| * |
| */ |
| protected IPreferenceStore getPreferenceStore() { |
| return fPreferenceStore; |
| } |
| } |