/*
* Created on Jan 13, 2005
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package org.rubypeople.rdt.internal.ui.rubyeditor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPositionCategoryException;
import org.eclipse.jface.text.DocumentCommand;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IAutoEditStrategy;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IPositionUpdater;
import org.eclipse.jface.text.information.IInformationPresenter;
import org.eclipse.jface.text.link.ILinkedModeListener;
import org.eclipse.jface.text.link.InclusivePositionUpdater;
import org.eclipse.jface.text.link.LinkedModeModel;
import org.eclipse.jface.text.link.LinkedModeUI;
import org.eclipse.jface.text.link.LinkedPosition;
import org.eclipse.jface.text.link.LinkedPositionGroup;
import org.eclipse.jface.text.source.IOverviewRuler;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.jface.text.source.projection.ProjectionViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
import org.eclipse.ui.texteditor.AbstractTextEditor;
import org.rubypeople.rdt.ui.text.RubySourceViewerConfiguration;
public class RubySourceViewer extends ProjectionViewer implements IPropertyChangeListener {
/**
* Text operation code for requesting the outline for the current input.
*/
public static final int SHOW_OUTLINE= 51;
/**
* Text operation code for requesting the outline for the element at the current position.
*/
public static final int OPEN_STRUCTURE= 52;
/**
* Text operation code for requesting the hierarchy for the current input.
*/
public static final int SHOW_HIERARCHY= 53;
private boolean fIgnoreTextConverters = false;
/** The linked position list for code auto edit */
protected final LinkedList fPositionList = new LinkedList();
private IInformationPresenter fOutlinePresenter;
private IInformationPresenter fStructurePresenter;
/**
* This viewer's foreground color.
* @since 0.8.0
*/
private Color fForegroundColor;
/**
* The viewer's background color.
* @since 0.8.0
*/
private Color fBackgroundColor;
/**
* This viewer's selection foreground color.
* @since 0.8.0
*/
private Color fSelectionForegroundColor;
/**
* The viewer's selection background color.
* @since 0.8.0
*/
private Color fSelectionBackgroundColor;
/**
* The preference store.
*
* @since 0.8.0
*/
private IPreferenceStore fPreferenceStore;
/**
* Is this source viewer configured?
*
* @since 0.8.0
*/
private boolean fIsConfigured;
/** The auto edit category */
protected static final String CATEGORY_AUTO_EDIT = "org.rubypeople.rdt.ui.RubyEditor"
+ ".auto.edit." + System.currentTimeMillis(); //$NON-NLS-1$
/** The position updater for auto edit */
protected final IPositionUpdater fAutoEditUpdater;
public RubySourceViewer(Composite composite, IVerticalRuler verticalRuler,
IOverviewRuler overviewRuler, boolean overviewRulerVisible, int styles, IPreferenceStore store) {
super(composite, verticalRuler, overviewRuler, overviewRulerVisible, styles);
setPreferenceStore(store);
fAutoEditUpdater = new InclusivePositionUpdater(CATEGORY_AUTO_EDIT);
}
/**
* Sets the preference store on this viewer.
*
* @param store the preference store
*
* @since 0.8.0
*/
public void setPreferenceStore(IPreferenceStore store) {
if (fIsConfigured && fPreferenceStore != null)
fPreferenceStore.removePropertyChangeListener(this);
fPreferenceStore= store;
if (fIsConfigured && fPreferenceStore != null) {
fPreferenceStore.addPropertyChangeListener(this);
initializeViewerColors();
}
}
protected void initializeViewerColors() {
if (fPreferenceStore != null) {
StyledText styledText= getTextWidget();
// ----------- foreground color --------------------
Color color= fPreferenceStore.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT)
? null
: createColor(fPreferenceStore, AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND, styledText.getDisplay());
styledText.setForeground(color);
if (fForegroundColor != null)
fForegroundColor.dispose();
fForegroundColor= color;
// ---------- background color ----------------------
color= fPreferenceStore.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT)
? null
: createColor(fPreferenceStore, AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND, styledText.getDisplay());
styledText.setBackground(color);
if (fBackgroundColor != null)
fBackgroundColor.dispose();
fBackgroundColor= color;
// ----------- selection foreground color --------------------
color= fPreferenceStore.getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_FOREGROUND_DEFAULT_COLOR)
? null
: createColor(fPreferenceStore, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_FOREGROUND_COLOR, styledText.getDisplay());
styledText.setSelectionForeground(color);
if (fSelectionForegroundColor != null)
fSelectionForegroundColor.dispose();
fSelectionForegroundColor= color;
// ---------- selection background color ----------------------
color= fPreferenceStore.getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_BACKGROUND_DEFAULT_COLOR)
? null
: createColor(fPreferenceStore, AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_BACKGROUND_COLOR, styledText.getDisplay());
styledText.setSelectionBackground(color);
if (fSelectionBackgroundColor != null)
fSelectionBackgroundColor.dispose();
fSelectionBackgroundColor= color;
}
}
/*
* @see ISourceViewer#configure(SourceViewerConfiguration)
*/
public void configure(SourceViewerConfiguration configuration) {
/*
* Prevent access to colors disposed in unconfigure(), see:
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=53641
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=86177
*/
StyledText textWidget= getTextWidget();
if (textWidget != null && !textWidget.isDisposed()) {
Color foregroundColor= textWidget.getForeground();
if (foregroundColor != null && foregroundColor.isDisposed())
textWidget.setForeground(null);
Color backgroundColor= textWidget.getBackground();
if (backgroundColor != null && backgroundColor.isDisposed())
textWidget.setBackground(null);
}
super.configure(configuration);
if (configuration instanceof RubySourceViewerConfiguration) {
RubySourceViewerConfiguration javaSVCconfiguration= (RubySourceViewerConfiguration)configuration;
fOutlinePresenter= javaSVCconfiguration.getOutlinePresenter(this, false);
if (fOutlinePresenter != null)
fOutlinePresenter.install(this);
fStructurePresenter= javaSVCconfiguration.getOutlinePresenter(this, true);
if (fStructurePresenter != null)
fStructurePresenter.install(this);
// fHierarchyPresenter= javaSVCconfiguration.getHierarchyPresenter(this, true);
// if (fHierarchyPresenter != null)
// fHierarchyPresenter.install(this);
}
if (fPreferenceStore != null) {
fPreferenceStore.addPropertyChangeListener(this);
initializeViewerColors();
}
fIsConfigured= true;
}
/*
* @see org.eclipse.jface.text.source.ISourceViewerExtension2#unconfigure()
* @since 0.8.0
*/
public void unconfigure() {
if (fOutlinePresenter != null) {
fOutlinePresenter.uninstall();
fOutlinePresenter= null;
}
if (fStructurePresenter != null) {
fStructurePresenter.uninstall();
fStructurePresenter= null;
}
// if (fHierarchyPresenter != null) {
// fHierarchyPresenter.uninstall();
// fHierarchyPresenter= null;
// }
if (fForegroundColor != null) {
fForegroundColor.dispose();
fForegroundColor= null;
}
if (fBackgroundColor != null) {
fBackgroundColor.dispose();
fBackgroundColor= null;
}
if (fPreferenceStore != null)
fPreferenceStore.removePropertyChangeListener(this);
super.unconfigure();
fIsConfigured= false;
}
/*
* @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent event) {
String property = event.getProperty();
if (AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND.equals(property)
|| AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT.equals(property)
|| AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND.equals(property)
|| AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT.equals(property)
|| AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_FOREGROUND_COLOR.equals(property)
|| AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_FOREGROUND_DEFAULT_COLOR.equals(property)
|| AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_BACKGROUND_COLOR.equals(property)
|| AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SELECTION_BACKGROUND_DEFAULT_COLOR.equals(property))
{
initializeViewerColors();
}
}
/**
* Creates a color from the information stored in the given preference store.
* Returns <code>null</code> if there is no such information available.
*
* @param store the store to read from
* @param key the key used for the lookup in the preference store
* @param display the display used create the color
* @return the created color according to the specification in the preference store
* @since 3.0
*/
private Color createColor(IPreferenceStore store, String key, Display display) {
RGB rgb= null;
if (store.contains(key)) {
if (store.isDefault(key))
rgb= PreferenceConverter.getDefaultColor(store, key);
else
rgb= PreferenceConverter.getColor(store, key);
if (rgb != null)
return new Color(display, rgb);
}
return null;
}
/*
* @see ITextOperationTarget#canDoOperation(int)
*/
public boolean canDoOperation(int operation) {
if (operation == SHOW_OUTLINE)
return fOutlinePresenter != null;
if (operation == OPEN_STRUCTURE)
return fStructurePresenter != null;
// if (operation == SHOW_HIERARCHY)
// return fHierarchyPresenter != null;
return super.canDoOperation(operation);
}
public void doOperation(int operation) {
if (getTextWidget() == null)
return;
switch (operation) {
case SHOW_OUTLINE:
if (fOutlinePresenter != null)
fOutlinePresenter.showInformation();
return;
case OPEN_STRUCTURE:
if (fStructurePresenter != null)
fStructurePresenter.showInformation();
return;
// case SHOW_HIERARCHY:
// if (fHierarchyPresenter != null)
// fHierarchyPresenter.showInformation();
// return;
}
super.doOperation(operation);
}
/*
* @see org.eclipse.jface.text.TextViewer#handleVisibleDocumentChanged(org.eclipse.jface.text.DocumentEvent)
*/
protected final void handleVisibleDocumentChanged(final DocumentEvent event) {
super.handleVisibleDocumentChanged(event);
if (!fPositionList.isEmpty()) {
try {
final IDocument document = event.getDocument();
final LinkedModeModel model = new LinkedModeModel();
final String category = CATEGORY_AUTO_EDIT;
if (!document.containsPositionCategory(category)) {
document.addPositionCategory(category);
document.addPositionUpdater(fAutoEditUpdater);
model.addLinkingListener(new ILinkedModeListener() {
public final void left(final LinkedModeModel dummy,
final int flags) {
if (document.containsPositionCategory(category)) {
try {
document.removePositionCategory(category);
} catch (BadPositionCategoryException exception) {
// Do nothing
}
document
.removePositionUpdater(fAutoEditUpdater);
}
}
public final void resume(final LinkedModeModel dummy,
final int flags) {
}
public final void suspend(final LinkedModeModel dummy) {
}
});
}
LinkedPosition position = null;
LinkedPositionGroup group = null;
for (final Iterator iterator = fPositionList.iterator(); iterator
.hasNext();) {
position = (LinkedPosition) iterator.next();
group = new LinkedPositionGroup();
group.addPosition(position);
model.addGroup(group);
}
model.forceInstall();
final LinkedModeUI handler = new LinkedModeUI(model, this);
final LinkedPosition exit = (LinkedPosition) fPositionList
.getFirst();
final LinkedPosition entry = (LinkedPosition) fPositionList
.getLast();
addSelectionChangedListener(new ISelectionChangedListener() {
private boolean fHandled = false;
public final void selectionChanged(
final SelectionChangedEvent dummy) {
if (!fHandled) {
fHandled = true;
removeSelectionChangedListener(this);
}
}
});
addPostSelectionChangedListener(new ISelectionChangedListener() {
private boolean fHandled = false;
public final void selectionChanged(
final SelectionChangedEvent dummy) {
if (!fHandled) {
setSelectedRange(entry.offset, entry.length);
invalidateTextPresentation(entry.offset, 0);
fHandled = true;
removePostSelectionChangedListener(this);
}
}
});
handler
.setExitPosition(this, exit.offset, 0,
LinkedPositionGroup.NO_STOP);
handler.enter();
} catch (BadLocationException exception) {
// Do nothing
} finally {
fPositionList.clear();
}
}
}
protected void customizeDocumentCommand(DocumentCommand command) {
if (isIgnoringAutoEditStrategies())
return;
List strategies = (List) selectContentTypePlugin(command.offset,
fAutoIndentStrategies);
if (strategies == null)
return;
IDocument document = getDocument();
if (!strategies.isEmpty()) {
fPositionList.clear();
String originalCommandText = command.text;
LinkedPosition[] result = null;
IAutoEditStrategy strategy = null;
for (final Iterator iterator = new ArrayList(strategies).iterator(); iterator
.hasNext();) {
strategy = (IAutoEditStrategy) iterator.next();
strategy.customizeDocumentCommand(document, command);
if ((strategy instanceof ILinkedModeEditStrategy) && originalCommandText.equals("\t")) {
result = ((ILinkedModeEditStrategy) strategy)
.getLinkedPositions();
if (result != null && result.length > 0)
fPositionList.addAll(Arrays.asList(result));
}
}
}
}
}