| /******************************************************************************* |
| * 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.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.text.IStructuredDocument; |
| import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; |
| import org.eclipse.wst.sse.core.text.ITextRegion; |
| import org.eclipse.wst.sse.core.text.ITextRegionCollection; |
| import org.eclipse.wst.sse.core.text.ITextRegionList; |
| import org.eclipse.wst.sse.core.util.Debug; |
| import org.eclipse.wst.sse.ui.preferences.ui.ColorHelper; |
| import org.eclipse.wst.sse.ui.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; |
| |
| // had to make this not final, or got in to infinite recursion during |
| // class loading in VAJava! |
| // (must be a VAJava thing) |
| private LineStyleProvider fDefaultAttributeProvider = null; |
| 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() { |
| } |
| |
| protected void addEmptyRange(int start, int length, Collection holdResults) { |
| StyleRange result = new StyleRange(); |
| result.start = start; |
| result.length = length; |
| holdResults.add(result); |
| } |
| |
| /** |
| * 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(getPreferenceKey(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 |
| */ |
| protected 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); |
| } |
| |
| protected TextAttribute getAttributeFor(ITextRegion region) { |
| // should be "abstract" method |
| return null; |
| } |
| |
| // this should actually be an abstract method to replace getColorManager |
| // but returning null to avoid API changes |
| protected IPreferenceStore getColorPreferences() { |
| return null; |
| } |
| |
| /** |
| * See also Highligher::getTextAttributeProvider |
| */ |
| protected LineStyleProvider getDefaultLineStyleProvider() { |
| if (fDefaultAttributeProvider == null) { |
| fDefaultAttributeProvider = new LineStyleProviderForNoOp(); |
| } |
| return fDefaultAttributeProvider; |
| } |
| |
| protected IStructuredDocument getDocument() { |
| return fDocument; |
| } |
| |
| /** |
| */ |
| protected Highlighter getHighlighter() { |
| return fHighlighter; |
| } |
| |
| /** |
| * Transforms the key in any way to use in preference store (ex: |
| * PreferenceKeyGenerator) |
| * |
| * @deprecated PrefernceKeyGenerator should no longer be needed |
| * @param key |
| * @return |
| */ |
| protected String getPreferenceKey(String key) { |
| return key; |
| } |
| |
| /** |
| * 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(); |
| } |
| 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; |
| } |
| |
| 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; |
| } |
| |
| protected void registerPreferenceManager() { |
| IPreferenceStore pref = getColorPreferences(); |
| if (pref != null) { |
| pref.addPropertyChangeListener(fPreferenceListener); |
| } |
| } |
| |
| public void release() { |
| unRegisterPreferenceManager(); |
| getTextAttributes().clear(); |
| } |
| |
| /** |
| * Sets the initialized. |
| * |
| * @param initialized |
| * The initialized to set |
| */ |
| public void setInitialized(boolean initialized) { |
| this.fInitialized = initialized; |
| } |
| |
| protected void unRegisterPreferenceManager() { |
| IPreferenceStore pref = getColorPreferences(); |
| if (pref != null) { |
| pref.removePropertyChangeListener(fPreferenceListener); |
| } |
| } |
| } |