blob: 9194a577b804100dcc5f8afcc2c4671d98d3e4ad [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2004 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
*
* Contributors:
* IBM Corporation - initial API and implementation
* Jens Lukowski/Innoopract - initial renaming/restructuring
*
*******************************************************************************/
package org.eclipse.wst.sse.ui.internal.provisional.style;
import java.util.Collection;
import java.util.HashMap;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.TextAttribute;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionCollection;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
import org.eclipse.wst.sse.core.internal.util.Debug;
import org.eclipse.wst.sse.ui.internal.preferences.ui.ColorHelper;
import org.eclipse.wst.sse.ui.internal.util.EditorUtility;
public abstract class AbstractLineStyleProvider {
private class PropertyChangeListener implements IPropertyChangeListener {
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent event) {
// have to do it this way so others can override the method
handlePropertyChange(event);
}
}
// protected IStructuredDocumentRegion currentStructuredDocumentRegion;
// Note: the var=x.class contructs were put into this method
// as a workaround for slow VAJava class lookups. They compiler
// assigns them to a variable in the JDK, but Class.forName("x")
// in VAJava. It is workaround specific for VAJava environment, so could
// be simplified in future.
static Class LineStyleProviderClass = LineStyleProvider.class;
private IStructuredDocument fDocument;
private Highlighter fHighlighter;
private boolean fInitialized;
private PropertyChangeListener fPreferenceListener = new PropertyChangeListener();
/** Contains all text attributes pretaining to this line style provider */
private HashMap fTextAttributes = null;
// we keep track of LogMessage to avoid writing hundreds of messages,
// but still give a hint that something is wrong with attributeProviders
// and/or regions.
// It's only written in the case of a program error, but there's no use
// adding
// salt to the wound.
// private boolean wroteOneLogMessage;
/**
*/
protected AbstractLineStyleProvider() {
}
/**
* Looks up the colorKey in the preference store and adds the style
* information to list of TextAttributes
*
* @param colorKey
*/
protected void addTextAttribute(String colorKey) {
if (getColorPreferences() != null) {
String prefString = getColorPreferences().getString(colorKey);
String[] stylePrefs = ColorHelper.unpackStylePreferences(prefString);
if (stylePrefs != null) {
RGB foreground = ColorHelper.toRGB(stylePrefs[0]);
RGB background = ColorHelper.toRGB(stylePrefs[1]);
boolean bold = Boolean.valueOf(stylePrefs[2]).booleanValue();
getTextAttributes().put(colorKey, createTextAttribute(foreground, background, bold));
}
}
}
protected void commonInit(IStructuredDocument document, Highlighter highlighter) {
fDocument = document;
fHighlighter = highlighter;
}
/**
* this version does "trim" regions to match request
*/
private StyleRange createStyleRange(ITextRegionCollection flatNode, ITextRegion region, TextAttribute attr, int startOffset, int length) {
int start = flatNode.getStartOffset(region);
if (start < startOffset)
start = startOffset;
int maxOffset = startOffset + length;
int end = flatNode.getEndOffset(region); // use get length directly
// instead of end-start?
if (end > maxOffset)
end = maxOffset;
StyleRange result = new StyleRange(start, end - start, attr.getForeground(), attr.getBackground(), attr.getStyle());
return result;
}
protected TextAttribute createTextAttribute(RGB foreground, RGB background, boolean bold) {
return new TextAttribute((foreground != null) ? EditorUtility.getColor(foreground) : null, (background != null) ? EditorUtility.getColor(background) : null, bold ? SWT.BOLD : SWT.NORMAL);
}
abstract protected TextAttribute getAttributeFor(ITextRegion region);
abstract protected IPreferenceStore getColorPreferences();
protected IStructuredDocument getDocument() {
return fDocument;
}
/**
*/
protected Highlighter getHighlighter() {
return fHighlighter;
}
/**
* Returns the hashtable containing all the text attributes for this line
* style provider. Lazily creates a hashtable if one has not already been
* created.
*
* @return
*/
protected HashMap getTextAttributes() {
if (fTextAttributes == null) {
fTextAttributes = new HashMap();
loadColors();
}
return fTextAttributes;
}
protected void handlePropertyChange(PropertyChangeEvent event) {
// force a full update of the text viewer
fHighlighter.refreshDisplay();
}
public void init(IStructuredDocument structuredDocument, Highlighter highlighter) {
commonInit(structuredDocument, highlighter);
if (isInitialized())
return;
registerPreferenceManager();
setInitialized(true);
}
/**
* Allowing the INodeAdapter to compare itself against the type allows it
* to return true in more than one case.
*/
public boolean isAdapterForType(java.lang.Object type) {
return type == LineStyleProviderClass;
}
/**
* Returns the initialized.
*
* @return boolean
*/
public boolean isInitialized() {
return fInitialized;
}
abstract protected void loadColors();
public boolean prepareRegions(ITypedRegion typedRegion, int lineRequestStart, int lineRequestLength, Collection holdResults) {
final int partitionStartOffset = typedRegion.getOffset();
final int partitionLength = typedRegion.getLength();
IStructuredDocumentRegion structuredDocumentRegion = getDocument().getRegionAtCharacterOffset(partitionStartOffset);
boolean handled = false;
handled = prepareTextRegions(structuredDocumentRegion, partitionStartOffset, partitionLength, holdResults);
return handled;
}
/**
* @param region
* @param start
* @param length
* @param holdResults
* @return
*/
private boolean prepareTextRegion(ITextRegionCollection blockedRegion, int partitionStartOffset, int partitionLength, Collection holdResults) {
boolean handled = false;
final int partitionEndOffset = partitionStartOffset + partitionLength - 1;
ITextRegion region = null;
ITextRegionList regions = blockedRegion.getRegions();
int nRegions = regions.size();
StyleRange styleRange = null;
for (int i = 0; i < nRegions; i++) {
region = regions.get(i);
TextAttribute attr = null;
TextAttribute previousAttr = null;
if (blockedRegion.getStartOffset(region) > partitionEndOffset)
break;
if (blockedRegion.getEndOffset(region) <= partitionStartOffset)
continue;
if (region instanceof ITextRegionCollection) {
handled = prepareTextRegion((ITextRegionCollection) region, partitionStartOffset, partitionLength, holdResults);
} else {
attr = getAttributeFor(region);
if (attr != null) {
handled = true;
// if this region's attr is the same as previous one, then
// just adjust the previous style range
// instead of creating a new instance of one
// note: to use 'equals' in this case is important, since
// sometimes
// different instances of attributes are associated with a
// region, even the
// the attribute has the same values.
// TODO: this needs to be improved to handle readonly
// regions correctly
if ((styleRange != null) && (previousAttr != null) && (previousAttr.equals(attr))) {
styleRange.length += region.getLength();
} else {
styleRange = createStyleRange(blockedRegion, region, attr, partitionStartOffset, partitionLength);
holdResults.add(styleRange);
// technically speaking, we don't need to update
// previousAttr
// in the other case, because the other case is when
// it hasn't changed
previousAttr = attr;
}
} else {
previousAttr = null;
}
}
}
return handled;
}
private boolean prepareTextRegions(IStructuredDocumentRegion structuredDocumentRegion, int partitionStartOffset, int partitionLength, Collection holdResults) {
boolean handled = false;
final int partitionEndOffset = partitionStartOffset + partitionLength - 1;
while (structuredDocumentRegion != null && structuredDocumentRegion.getStartOffset() <= partitionEndOffset) {
ITextRegion region = null;
ITextRegionList regions = structuredDocumentRegion.getRegions();
int nRegions = regions.size();
StyleRange styleRange = null;
for (int i = 0; i < nRegions; i++) {
region = regions.get(i);
TextAttribute attr = null;
TextAttribute previousAttr = null;
if (structuredDocumentRegion.getStartOffset(region) > partitionEndOffset)
break;
if (structuredDocumentRegion.getEndOffset(region) <= partitionStartOffset)
continue;
if (region instanceof ITextRegionCollection) {
handled = prepareTextRegion((ITextRegionCollection) region, partitionStartOffset, partitionLength, holdResults);
} else {
attr = getAttributeFor(region);
if (attr != null) {
handled = true;
// if this region's attr is the same as previous one,
// then just adjust the previous style range
// instead of creating a new instance of one
// note: to use 'equals' in this case is important,
// since sometimes
// different instances of attributes are associated
// with a region, even the
// the attribute has the same values.
// TODO: this needs to be improved to handle readonly
// regions correctly
if ((styleRange != null) && (previousAttr != null) && (previousAttr.equals(attr))) {
styleRange.length += region.getLength();
} else {
styleRange = createStyleRange(structuredDocumentRegion, region, attr, partitionStartOffset, partitionLength);
holdResults.add(styleRange);
// technically speaking, we don't need to update
// previousAttr
// in the other case, because the other case is
// when it hasn't changed
previousAttr = attr;
}
} else {
previousAttr = null;
}
}
if (Debug.syntaxHighlighting && !handled) {
System.out.println("not handled in prepareRegions"); //$NON-NLS-1$
}
}
structuredDocumentRegion = structuredDocumentRegion.getNext();
}
return handled;
}
private void registerPreferenceManager() {
IPreferenceStore pref = getColorPreferences();
if (pref != null) {
pref.addPropertyChangeListener(fPreferenceListener);
}
}
public void release() {
unRegisterPreferenceManager();
if (fTextAttributes != null) {
fTextAttributes.clear();
fTextAttributes = null;
}
}
/**
* Sets the initialized.
*
* @param initialized
* The initialized to set
*/
private void setInitialized(boolean initialized) {
this.fInitialized = initialized;
}
private void unRegisterPreferenceManager() {
IPreferenceStore pref = getColorPreferences();
if (pref != null) {
pref.removePropertyChangeListener(fPreferenceListener);
}
}
}