/*******************************************************************************
* Copyright (c) 2005, 2012 eBay Inc.
* 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.vjet.eclipse.internal.ui.editor.semantic.highlighting;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.dltk.mod.compiler.env.CompilerSourceCode;
import org.eclipse.dltk.mod.internal.ui.editor.ScriptEditor;
import org.eclipse.dltk.mod.internal.ui.editor.ScriptSourceViewer;
import org.eclipse.dltk.mod.internal.ui.editor.semantic.highlighting.SemanticHighlightingPresenter;
import org.eclipse.dltk.mod.ui.editor.highlighting.HighlightedPosition;
import org.eclipse.dltk.mod.ui.editor.highlighting.HighlightingStyle;
import org.eclipse.dltk.mod.ui.editor.highlighting.SemanticHighlighting;
import org.eclipse.dltk.mod.ui.text.IColorManager;
import org.eclipse.dltk.mod.ui.text.IColorManagerExtension;
import org.eclipse.dltk.mod.ui.text.ScriptPresentationReconciler;
import org.eclipse.dltk.mod.ui.text.ScriptSourceViewerConfiguration;
import org.eclipse.dltk.mod.ui.text.ScriptTextTools;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.StringConverter;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextAttribute;
import org.eclipse.jface.text.presentation.IPresentationReconciler;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
public class VjoSemanticHighlightingManager implements IPropertyChangeListener {
/**
* Highlighted ranges.
*/
public static class HighlightedRange extends Region {
/**
* The highlighting key as returned by
* {@link SemanticHighlighting#getPreferenceKey()}.
*/
private String fKey;
/**
* Initialize with the given offset, length and highlighting key.
*
* @param offset
* @param length
* @param key
* the highlighting key as returned by
* {@link SemanticHighlighting#getPreferenceKey()}
*/
public HighlightedRange(int offset, int length, String key) {
super(offset, length);
fKey = key;
}
/**
* @return the highlighting key as returned by
* {@link SemanticHighlighting#getPreferenceKey()}
*/
public String getKey() {
return fKey;
}
/*
* @see org.eclipse.jface.text.Region#equals(java.lang.Object)
*/
public boolean equals(Object o) {
return super.equals(o) && o instanceof HighlightedRange && fKey.equals(((HighlightedRange) o).getKey());
}
/*
* @see org.eclipse.jface.text.Region#hashCode()
*/
public int hashCode() {
return super.hashCode() | fKey.hashCode();
}
}
private static class SourceCode extends CompilerSourceCode {
public SourceCode(String source) {
super(source);
}
}
/** Semantic highlighting presenter */
private SemanticHighlightingPresenter fPresenter;
/** Semantic highlighting reconciler */
private VjoSemanticHighlightingReconciler fReconciler;
/** Semantic highlightings */
private VjoSemanticHighlighting[] fSemanticHighlightings;
/** Highlightings */
private HighlightingStyle[] fHighlightings;
/** The editor */
private ScriptEditor fEditor;
/** The source viewer */
private ScriptSourceViewer fSourceViewer;
/** The color manager */
private IColorManager fColorManager;
/** The preference store */
private IPreferenceStore fPreferenceStore;
/** The source viewer configuration */
private ScriptSourceViewerConfiguration fConfiguration;
/** The presentation reconciler */
private ScriptPresentationReconciler fPresentationReconciler;
/** The hard-coded ranges */
private HighlightedRange[][] fHardcodedRanges;
/**
* Install the semantic highlighting on the given editor infrastructure
*
* @param editor
* The Script editor
* @param sourceViewer
* The source viewer
* @param colorManager
* The color manager
* @param preferenceStore
* The preference store
*/
public void install(ScriptEditor editor, ScriptSourceViewer sourceViewer, IColorManager colorManager, IPreferenceStore preferenceStore) {
fEditor = editor;
fSourceViewer = sourceViewer;
fColorManager = colorManager;
fPreferenceStore = preferenceStore;
final ScriptTextTools textTools = getTextTools();
if (textTools != null) {
fSemanticHighlightings = (VjoSemanticHighlighting[]) textTools.getSemanticHighlightings();
fConfiguration = textTools.createSourceViewerConfiguraton(preferenceStore, editor);
}
if (fEditor != null) {
Assert.isNotNull(fConfiguration);
IPresentationReconciler presReconciler = fConfiguration.getPresentationReconciler(sourceViewer);
if (presReconciler instanceof ScriptPresentationReconciler) {
fPresentationReconciler = (ScriptPresentationReconciler) presReconciler;
} else {
fPresentationReconciler = null;
}
} else {
fConfiguration = null;
fPresentationReconciler = null;
}
fPreferenceStore.addPropertyChangeListener(this);
if (isEnabled())
enable();
}
/**
* Install the semantic highlighting on the given source viewer
* infrastructure. No reconciliation will be performed.
*
* @param sourceViewer
* the source viewer
* @param colorManager
* the color manager
* @param preferenceStore
* the preference store
*/
public void install(ScriptSourceViewer sourceViewer, IColorManager colorManager, IPreferenceStore preferenceStore) {
install(null, sourceViewer, colorManager, preferenceStore);
}
/**
* Install the semantic highlighting on the given source viewer
* infrastructure. No reconciliation will be performed.
*
* @param sourceViewer
* the source viewer
* @param colorManager
* the color manager
* @param preferenceStore
* the preference store
* @param hardcodedRanges
* the hard-coded ranges to be highlighted
*
*/
public void install(ScriptSourceViewer sourceViewer, IColorManager colorManager, IPreferenceStore preferenceStore, HighlightedRange[][] hardcodedRanges) {
fHardcodedRanges= hardcodedRanges;
install(null, sourceViewer, colorManager, preferenceStore);
}
/**
* Enable semantic highlighting.
*/
private void enable() {
initializeHighlightings();
fPresenter = new SemanticHighlightingPresenter();
fPresenter.install(fSourceViewer, fPresentationReconciler);
if (fEditor != null) {
fReconciler = new VjoSemanticHighlightingReconciler();
fReconciler.install(fEditor, fSourceViewer, fPresenter, fSemanticHighlightings, fHighlightings);
} else {
// final ScriptTextTools textTools = getTextTools();
// if (textTools != null) {
// final ISemanticHighlighter updater = textTools.getSemanticPositionUpdater();
// if (updater != null) {
// updater.initialize(fPresenter, fHighlightings);
// final ISourceModule code = new SourceCode(fSourceViewer.getDocument().get());
// UpdateResult result = updater.reconcile(code, Collections.EMPTY_LIST);
// fPresenter.updatePresentation(null, result.addedPositions, HighlightedPosition.NO_POSITIONS);
// }
// }
fPresenter.updatePresentation(null, createHardcodedPositions(), new HighlightedPosition[0]);
}
}
public HighlightingStyle getHighlighting(int pos) {
return fHighlightings[pos];
}
/**
* Uninstall the semantic highlighting
*/
public void uninstall() {
disable();
if (fPreferenceStore != null) {
fPreferenceStore.removePropertyChangeListener(this);
fPreferenceStore = null;
}
fSemanticHighlightings = null;
fEditor = null;
fSourceViewer = null;
fColorManager = null;
fConfiguration = null;
fPresentationReconciler = null;
fHardcodedRanges= null;
}
/**
* Disable semantic highlighting.
*/
private void disable() {
if (fReconciler != null) {
fReconciler.uninstall();
fReconciler = null;
}
if (fPresenter != null) {
fPresenter.uninstall();
fPresenter = null;
}
if (fHighlightings != null)
disposeHighlightings();
}
/**
* @return <code>true</code> iff semantic highlighting is enabled in the
* preferences
*/
private boolean isEnabled() {
// if (fSemanticHighlightings == null) {
// return false;
// }
// for (int i = 0; i < fSemanticHighlightings.length; ++i) {
// final VjoSemanticHighlighting sh = fSemanticHighlightings[i];
// if (!sh.isSemanticOnly()) {
// return true;
// }
// if (fPreferenceStore.getBoolean(sh.getEnabledPreferenceKey())) {
// return true;
// }
// }
// return false;
return VjoSemanticHighlightings.isEnabled(fPreferenceStore);
}
// private TextAttribute createTextAttribute(IColorManager manager, IPreferenceStore ps, String colorKey, String boldKey, String italicKey,
// String strikethroughKey, String underlineKey, String bgKey) {
//
// addColor(colorKey);
// if (bgKey != null) {
// addColor(bgKey);
// }
// Color color = null;
// Color bgcolor = null;
// if (colorKey != null)
// color = manager.getColor(colorKey);
// if (bgKey != null)
// bgcolor = manager.getColor(bgKey);
//
// int style = ps.getBoolean(boldKey) ? SWT.BOLD : SWT.NORMAL;
// if (ps.getBoolean(italicKey))
// style |= SWT.ITALIC;
//
// if (ps.getBoolean(strikethroughKey))
// style |= TextAttribute.STRIKETHROUGH;
//
// if (ps.getBoolean(underlineKey))
// style |= TextAttribute.UNDERLINE;
//
// return new TextAttribute(color, bgcolor, style);
// }
// private TextAttribute createTextAttribute(IColorManager manager, IPreferenceStore ps, String colorKey, String bgKey) {
// return createTextAttribute(manager, ps, colorKey, colorKey + PreferenceConstants.EDITOR_BOLD_SUFFIX, colorKey
// + PreferenceConstants.EDITOR_ITALIC_SUFFIX, colorKey + PreferenceConstants.EDITOR_STRIKETHROUGH_SUFFIX, colorKey
// + PreferenceConstants.EDITOR_UNDERLINE_SUFFIX, bgKey);
// }
/**
* Initialize semantic highlightings.
*/
private void initializeHighlightings() {
// final ScriptTextTools textTools = getTextTools();
// if (textTools != null) {
// Assert.isNotNull(fSemanticHighlightings);
// fHighlightings = new HighlightingStyle[fSemanticHighlightings.length];
// for (int a = 0; a < fSemanticHighlightings.length; a++) {
// final VjoSemanticHighlighting sh = fSemanticHighlightings[a];
// final TextAttribute ta = createTextAttribute(fColorManager, fPreferenceStore, sh.getPreferenceKey(), sh.getBackgroundPreferenceKey());
// final boolean isEnabled = !sh.isSemanticOnly() || fPreferenceStore.getBoolean(sh.getEnabledPreferenceKey());
// fHighlightings[a] = new HighlightingStyle(ta, isEnabled, sh);
// }
// }
fSemanticHighlightings= VjoSemanticHighlightings.getSemanticHighlightings();
fHighlightings= new HighlightingStyle[fSemanticHighlightings.length];
for (int i= 0, n= fSemanticHighlightings.length; i < n; i++) {
VjoSemanticHighlighting semanticHighlighting= fSemanticHighlightings[i];
String colorKey= VjoSemanticHighlightings.getColorPreferenceKey(semanticHighlighting);
addColor(colorKey);
String boldKey= VjoSemanticHighlightings.getBoldPreferenceKey(semanticHighlighting);
int style= fPreferenceStore.getBoolean(boldKey) ? SWT.BOLD : SWT.NORMAL;
String italicKey= VjoSemanticHighlightings.getItalicPreferenceKey(semanticHighlighting);
if (fPreferenceStore.getBoolean(italicKey))
style |= SWT.ITALIC;
String strikethroughKey= VjoSemanticHighlightings.getStrikethroughPreferenceKey(semanticHighlighting);
if (fPreferenceStore.getBoolean(strikethroughKey))
style |= TextAttribute.STRIKETHROUGH;
String underlineKey= VjoSemanticHighlightings.getUnderlinePreferenceKey(semanticHighlighting);
if (fPreferenceStore.getBoolean(underlineKey))
style |= TextAttribute.UNDERLINE;
boolean isEnabled= fPreferenceStore.getBoolean(VjoSemanticHighlightings.getEnabledPreferenceKey(semanticHighlighting));
fHighlightings[i]= new HighlightingStyle(new TextAttribute(fColorManager.getColor(PreferenceConverter.getColor(fPreferenceStore, colorKey)), null, style), isEnabled,
semanticHighlighting);
}
}
protected ScriptTextTools getTextTools() {
return fEditor != null ? fEditor.getTextTools() : null;
}
/**
* Dispose the semantic highlightings.
*/
private void disposeHighlightings() {
for (int i= 0, n= fSemanticHighlightings.length; i < n; i++)
removeColor(VjoSemanticHighlightings.getColorPreferenceKey(fSemanticHighlightings[i]));
fSemanticHighlightings= null;
fHighlightings= null;
}
/*
* @see IPropertyChangeListener#propertyChange(PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent event) {
handlePropertyChangeEvent(event);
}
/**
* Computes the hard-coded positions from the hard-coded ranges
*
* @return the hard-coded positions
*/
private HighlightedPosition[] createHardcodedPositions() {
List positions= new ArrayList();
for (int i= 0; i < fHardcodedRanges.length; i++) {
HighlightedRange range= null;
HighlightingStyle hl= null;
for (int j= 0; j < fHardcodedRanges[i].length; j++ ) {
hl= getHighlighting(fHardcodedRanges[i][j].getKey());
if (hl.isEnabled()) {
range= fHardcodedRanges[i][j];
break;
}
}
if (range != null)
positions.add(fPresenter.createHighlightedPosition(range.getOffset(), range.getLength(), hl));
}
return (HighlightedPosition[]) positions.toArray(new HighlightedPosition[positions.size()]);
}
/**
* Returns the highlighting corresponding to the given key.
*
* @param key the highlighting key as returned by {@link SemanticHighlighting#getPreferenceKey()}
* @return the corresponding highlighting
*/
private HighlightingStyle getHighlighting(String key) {
for (int i= 0; i < fSemanticHighlightings.length; i++) {
SemanticHighlighting semanticHighlighting= fSemanticHighlightings[i];
if (key.equals(semanticHighlighting.getPreferenceKey()))
return fHighlightings[i];
}
return null;
}
/**
* Handle the given property change event
*
* @param event
* The event
*/
private void handlePropertyChangeEvent(PropertyChangeEvent event) {
if (fPreferenceStore == null)
return; // Uninstalled during event notification
if (fConfiguration != null)
fConfiguration.handlePropertyChangeEvent(event);
if (VjoSemanticHighlightings.affectsEnablement(fPreferenceStore, event)) {
if (isEnabled())
enable();
else
disable();
}
if (!isEnabled())
return;
boolean refreshNeeded = false;
for (int i = 0, n = fSemanticHighlightings.length; i < n; i++) {
VjoSemanticHighlighting semanticHighlighting = fSemanticHighlightings[i];
// String preferenceKey = semanticHighlighting.getPreferenceKey();
// if (preferenceKey != null)
// if (preferenceKey.equals(event.getProperty())) {
// adaptToTextForegroundChange(fHighlightings[i], event);
// fPresenter.highlightingStyleChanged(fHighlightings[i]);
// refreshNeeded = true;
// continue;
// }
// String bpreferenceKey = semanticHighlighting.getBackgroundPreferenceKey();
// if (bpreferenceKey != null)
// if (bpreferenceKey.equals(event.getProperty())) {
// adaptToTextBackgroundChange(fHighlightings[i], event);
// fPresenter.highlightingStyleChanged(fHighlightings[i]);
// refreshNeeded = true;
// continue;
// }
String colorKey= VjoSemanticHighlightings.getColorPreferenceKey(semanticHighlighting);
if (colorKey.equals(event.getProperty())) {
adaptToTextForegroundChange(fHighlightings[i], event);
fPresenter.highlightingStyleChanged(fHighlightings[i]);
refreshNeeded= true;
continue;
}
String boldKey= VjoSemanticHighlightings.getBoldPreferenceKey(semanticHighlighting);
if (boldKey.equals(event.getProperty())) {
adaptToTextStyleChange(fHighlightings[i], event, SWT.BOLD);
fPresenter.highlightingStyleChanged(fHighlightings[i]);
refreshNeeded= true;
continue;
}
String italicKey= VjoSemanticHighlightings.getItalicPreferenceKey(semanticHighlighting);
if (italicKey.equals(event.getProperty())) {
adaptToTextStyleChange(fHighlightings[i], event, SWT.ITALIC);
fPresenter.highlightingStyleChanged(fHighlightings[i]);
refreshNeeded= true;
continue;
}
String strikethroughKey= VjoSemanticHighlightings.getStrikethroughPreferenceKey(semanticHighlighting);
if (strikethroughKey.equals(event.getProperty())) {
adaptToTextStyleChange(fHighlightings[i], event, TextAttribute.STRIKETHROUGH);
fPresenter.highlightingStyleChanged(fHighlightings[i]);
refreshNeeded= true;
continue;
}
String underlineKey= VjoSemanticHighlightings.getUnderlinePreferenceKey(semanticHighlighting);
if (underlineKey.equals(event.getProperty())) {
adaptToTextStyleChange(fHighlightings[i], event, TextAttribute.UNDERLINE);
fPresenter.highlightingStyleChanged(fHighlightings[i]);
refreshNeeded= true;
continue;
}
String enabledKey= VjoSemanticHighlightings.getEnabledPreferenceKey(semanticHighlighting);
if (enabledKey.equals(event.getProperty())) {
adaptToEnablementChange(fHighlightings[i], event);
fPresenter.highlightingStyleChanged(fHighlightings[i]);
refreshNeeded= true;
continue;
}
// String boldKey = preferenceKey + PreferenceConstants.EDITOR_BOLD_SUFFIX;
// if (boldKey.equals(event.getProperty())) {
// adaptToTextStyleChange(fHighlightings[i], event, SWT.BOLD);
// fPresenter.highlightingStyleChanged(fHighlightings[i]);
// refreshNeeded = true;
// continue;
// }
//
// String italicKey = preferenceKey + PreferenceConstants.EDITOR_ITALIC_SUFFIX;
// if (italicKey.equals(event.getProperty())) {
// adaptToTextStyleChange(fHighlightings[i], event, SWT.ITALIC);
// fPresenter.highlightingStyleChanged(fHighlightings[i]);
// refreshNeeded = true;
// continue;
// }
//
// String strikethroughKey = preferenceKey + PreferenceConstants.EDITOR_STRIKETHROUGH_SUFFIX;
// if (strikethroughKey.equals(event.getProperty())) {
// adaptToTextStyleChange(fHighlightings[i], event, TextAttribute.STRIKETHROUGH);
// fPresenter.highlightingStyleChanged(fHighlightings[i]);
// refreshNeeded = true;
// continue;
// }
//
// String underlineKey = preferenceKey + PreferenceConstants.EDITOR_UNDERLINE_SUFFIX;
// if (underlineKey.equals(event.getProperty())) {
// adaptToTextStyleChange(fHighlightings[i], event, TextAttribute.UNDERLINE);
// fPresenter.highlightingStyleChanged(fHighlightings[i]);
// refreshNeeded = true;
// continue;
// }
// if (semanticHighlighting.isSemanticOnly()) {
// if (semanticHighlighting.getEnabledPreferenceKey().equals(event.getProperty())) {
// adaptToEnablementChange(fHighlightings[i], event);
// fPresenter.highlightingStyleChanged(fHighlightings[i]);
// refreshNeeded = true;
// continue;
// }
// }
}
if (refreshNeeded && fReconciler != null)
fReconciler.refresh();
}
/**
* Tests whether <code>event</code> affects the enablement of semantic
* highlighting.
*
* @param event
* the property change under examination
* @return <code>true</code> if <code>event</code> changed semantic
* highlighting enablement, <code>false</code> if it did not
*/
// private boolean affectsEnablement(PropertyChangeEvent event) {
// if (fSemanticHighlightings == null) {
// return false;
// }
// String relevantKey = null;
// for (int i = 0; i < fSemanticHighlightings.length; i++) {
// if (event.getProperty().equals(fSemanticHighlightings[i].getEnabledPreferenceKey())) {
// relevantKey = event.getProperty();
// break;
// }
// }
// if (relevantKey == null)
// return false;
//
// for (int i = 0; i < fSemanticHighlightings.length; i++) {
// String key = fSemanticHighlightings[i].getEnabledPreferenceKey();
// if (key.equals(relevantKey))
// continue;
// if (fPreferenceStore.getBoolean(key))
// return false; // another is still enabled or was enabled
// // before
// }
// // all others are disabled, so toggling relevantKey affects the
// // enablement
// return true;
// }
// private void adaptToTextBackgroundChange(HighlightingStyle highlighting, PropertyChangeEvent event) {
// RGB rgb = null;
//
// Object value = event.getNewValue();
// if (value instanceof RGB)
// rgb = (RGB) value;
// else if (value instanceof String)
// rgb = StringConverter.asRGB((String) value);
//
// 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);
// }
//
// TextAttribute oldAttr = highlighting.getTextAttribute();
// highlighting.setTextAttribute(new TextAttribute(oldAttr.getForeground(), color, oldAttr.getStyle()));
// }
// }
private void adaptToEnablementChange(HighlightingStyle highlighting, PropertyChangeEvent event) {
Object value = event.getNewValue();
boolean eventValue;
if (value instanceof Boolean)
eventValue = ((Boolean) value).booleanValue();
else if (IPreferenceStore.TRUE.equals(value))
eventValue = true;
else
eventValue = false;
highlighting.setEnabled(eventValue);
}
private void adaptToTextForegroundChange(HighlightingStyle highlighting, PropertyChangeEvent event) {
RGB rgb = null;
Object value = event.getNewValue();
if (value instanceof RGB)
rgb = (RGB) value;
else if (value instanceof String)
rgb = StringConverter.asRGB((String) value);
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);
}
TextAttribute oldAttr = highlighting.getTextAttribute();
highlighting.setTextAttribute(new TextAttribute(color, oldAttr.getBackground(), oldAttr.getStyle()));
}
}
private void adaptToTextStyleChange(HighlightingStyle highlighting, 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;
TextAttribute oldAttr = highlighting.getTextAttribute();
boolean activeValue = (oldAttr.getStyle() & styleAttribute) == styleAttribute;
if (activeValue != eventValue)
highlighting.setTextAttribute(new TextAttribute(oldAttr.getForeground(), oldAttr.getBackground(), eventValue ? oldAttr.getStyle() | styleAttribute
: oldAttr.getStyle() & ~styleAttribute));
}
private void addColor(String colorKey) {
if (fColorManager != null && colorKey != null && fColorManager.getColor(colorKey) == null) {
RGB rgb = PreferenceConverter.getColor(fPreferenceStore, colorKey);
if (fColorManager instanceof IColorManagerExtension) {
IColorManagerExtension ext = (IColorManagerExtension) fColorManager;
ext.unbindColor(colorKey);
ext.bindColor(colorKey, rgb);
}
}
}
private void removeColor(String colorKey) {
if (fColorManager instanceof IColorManagerExtension)
((IColorManagerExtension) fColorManager).unbindColor(colorKey);
}
/**
* Returns this hightlighter's reconciler.
*
* @return the semantic highlighter reconciler or <code>null</code> if
* none
* @since 3.3
*/
public VjoSemanticHighlightingReconciler getReconciler() {
return fReconciler;
}
}