/******************************************************************************* * Copyright (c) 2001, 2008 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.text.source.ISourceViewer; 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 IStructuredDocument fDocument; protected Highlighter fHighlighter; private boolean fInitialized; protected PropertyChangeListener fPreferenceListener = new PropertyChangeListener(); //private ISourceViewer fSourceViewer = null; protected ReconcilerHighlighter fRecHighlighter = null; /** Contains all text attributes pertaining 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(); boolean italic = Boolean.valueOf(stylePrefs[3]).booleanValue(); boolean strikethrough = Boolean.valueOf(stylePrefs[4]).booleanValue(); boolean underline = Boolean.valueOf(stylePrefs[5]).booleanValue(); int style = SWT.NORMAL; if (bold) { style = style | SWT.BOLD; } if (italic) { style = style | SWT.ITALIC; } if (strikethrough) { style = style | TextAttribute.STRIKETHROUGH; } if (underline) { style = style | TextAttribute.UNDERLINE; } TextAttribute createTextAttribute = createTextAttribute(foreground, background, style); getTextAttributes().put(colorKey, createTextAttribute); } } } 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; // Base the text end offset off of the, possibly adjusted, start int textEnd = start + region.getTextLength(); int maxOffset = startOffset + length; int end = flatNode.getEndOffset(region); // Use the end of the text in the region to avoid applying background color to trailing whitespace if(textEnd < end) end = textEnd; // instead of end-start? if (end > maxOffset) end = maxOffset; StyleRange result = new StyleRange(start, end - start, attr.getForeground(), attr.getBackground(), attr.getStyle()); if((attr.getStyle() & TextAttribute.STRIKETHROUGH) != 0) { result.strikeout = true; } if((attr.getStyle() & TextAttribute.UNDERLINE) != 0) { result.underline = true; } 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 createTextAttribute(RGB foreground, RGB background, int style) { return new TextAttribute((foreground != null) ? EditorUtility.getColor(foreground) : null, (background != null) ? EditorUtility.getColor(background) : null, style); } abstract protected TextAttribute getAttributeFor(ITextRegion region); protected TextAttribute getAttributeFor(ITextRegionCollection collection, ITextRegion region) { return getAttributeFor(region); } abstract protected IPreferenceStore getColorPreferences(); protected IStructuredDocument getDocument() { return fDocument; } public void setDocument(IStructuredDocument document) { fDocument = document; } /** */ 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 if(fRecHighlighter != null) fRecHighlighter.refreshDisplay(); } public void init(IStructuredDocument structuredDocument, Highlighter highlighter) { commonInit(structuredDocument, highlighter); if (isInitialized()) return; registerPreferenceManager(); setInitialized(true); } public void init(IStructuredDocument structuredDocument, ISourceViewer sourceViewer) { init(structuredDocument, (Highlighter) null); } public void init(IStructuredDocument structuredDocument, ReconcilerHighlighter highlighter) { fDocument = structuredDocument; fRecHighlighter = highlighter; if(isInitialized()) return; registerPreferenceManager(); setInitialized(true); } /** * @deprecated - left because it's public, but we aren't adapters any more */ public boolean isAdapterForType(java.lang.Object type) { return type == LineStyleProvider.class; } /** * 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(blockedRegion, 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) { boolean handledCollection = (prepareTextRegion((ITextRegionCollection) region, partitionStartOffset, partitionLength, holdResults)); handled = (!handled) ? handledCollection : handled; } else { attr = getAttributeFor(structuredDocumentRegion, 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(); if (fTextAttributes != null) { fTextAttributes.clear(); fTextAttributes = null; } setInitialized(false); } /** * Sets the initialized. * * @param initialized * The initialized to set */ private void setInitialized(boolean initialized) { this.fInitialized = initialized; } protected void unRegisterPreferenceManager() { IPreferenceStore pref = getColorPreferences(); if (pref != null) { pref.removePropertyChangeListener(fPreferenceListener); } } }