/*=============================================================================#
# Copyright (c) 2007-2016 Stephan Wahlbrink (WalWare.de) 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:
# Stephan Wahlbrink - initial API and implementation
#=============================================================================*/
package de.walware.ecommons.ltk.ui.sourceediting;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.filebuffers.IDocumentSetupParticipant;
import org.eclipse.jface.text.ITextHover;
import org.eclipse.jface.text.ITextViewerExtension2;
import org.eclipse.jface.text.source.ICharacterPairMatcher;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
import org.eclipse.ui.texteditor.spelling.SpellingProblem;
import de.walware.ecommons.text.IIndentSettings;
import de.walware.ecommons.text.core.sections.IDocContentSections;
import de.walware.ecommons.text.ui.presentation.ITextPresentationConstants;
import de.walware.ecommons.text.ui.settings.DecorationPreferences;
import de.walware.ecommons.ui.ISettingsChangedHandler;
/**
* Controls the configuration of an {@link ISourceEditor}.
*/
public abstract class SourceEditorViewerConfigurator implements ISettingsChangedHandler,
PropertyChangeListener {
private final SourceEditorViewerConfiguration fConfiguration;
private ISourceEditor fSourceEditor;
private final List<ISourceEditorAddon> fAddons = new ArrayList<>();
private List<ISourceEditorAddon> fConfigurationAddons;
protected boolean fIsConfigured;
protected boolean fUpdateCompleteConfig;
protected boolean fUpdateTextPresentation;
protected boolean fUpdateTabSize;
protected boolean fUpdateIndent;
protected boolean fUpdateInfoHovers;
protected SourceEditorViewerConfigurator(final SourceEditorViewerConfiguration config) {
if (config == null) {
throw new NullPointerException("config");
}
fConfiguration = config;
}
/**
* A setup participant for the document of the editor.
*
* @return a document setup participant or <code>null</code>.
*/
public abstract IDocumentSetupParticipant getDocumentSetupParticipant();
public final IDocContentSections getDocumentContentInfo() {
return fConfiguration.getDocumentContentInfo();
}
protected abstract Set<String> getResetGroupIds();
public SourceEditorViewerConfiguration getSourceViewerConfiguration() {
return fConfiguration;
}
public void configureSourceViewerDecorationSupport(final SourceViewerDecorationSupport support) {
final ICharacterPairMatcher pairMatcher = fConfiguration.getPairMatcher();
final DecorationPreferences preferences = fConfiguration.getDecorationPreferences();
if (pairMatcher != null && preferences != null) {
support.setCharacterPairMatcher(pairMatcher);
support.setMatchingCharacterPainterPreferenceKeys(
preferences.getMatchingBracketsEnabled().getKey(),
preferences.getMatchingBracketsColor().getKey() );
}
}
public void setTarget(final ISourceEditor sourceEditor) {
assert (sourceEditor != null);
fSourceEditor = sourceEditor;
if (!(fSourceEditor instanceof AbstractDecoratedTextEditor)) {
fSourceEditor.getViewer().getControl().addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(final DisposeEvent e) {
if (fIsConfigured) {
uninstallCurrentAddons();
}
}
});
configureTarget();
}
else {
fIsConfigured = true;
installCurrentAddons();
}
handleSettingsChanged(null, null);
}
protected ISourceViewer getSourceViewer() {
if (fSourceEditor != null) {
return fSourceEditor.getViewer();
}
return null;
}
public final void unconfigureTarget() {
if (fSourceEditor != null) {
fIsConfigured = false;
uninstallCurrentAddons();
fSourceEditor.getViewer().unconfigure();
}
}
public final void configureTarget() {
if (fSourceEditor != null) {
fIsConfigured = true;
fSourceEditor.getViewer().configure(fConfiguration);
installCurrentAddons();
}
}
private void installCurrentAddons() {
fConfigurationAddons = getSourceViewerConfiguration().getAddOns();
for (final ISourceEditorAddon addon : fConfigurationAddons) {
addon.install(fSourceEditor);
}
for (final ISourceEditorAddon addon : fAddons) {
addon.install(fSourceEditor);
}
}
private void uninstallCurrentAddons() {
for (final ISourceEditorAddon addon : fAddons) {
addon.uninstall();
}
if (fConfigurationAddons != null) {
for (final ISourceEditorAddon addon : fConfigurationAddons) {
addon.uninstall();
}
fConfigurationAddons = null;
}
}
public final void installAddon(final ISourceEditorAddon installable) {
fAddons.add(installable);
if (fIsConfigured) {
installable.install(fSourceEditor);
}
}
@Override
public void propertyChange(final PropertyChangeEvent event) {
final String name = event.getPropertyName();
if (name.equals(IIndentSettings.TAB_SIZE_PROP)) {
fUpdateTabSize = true;
fUpdateIndent = true;
return;
}
if (name.equals(IIndentSettings.INDENT_SPACES_COUNT_PROP)
|| name.equals(IIndentSettings.INDENT_DEFAULT_TYPE_PROP)) {
fUpdateIndent = true;
return;
}
}
@Override
public void handleSettingsChanged(Set<String> groupIds, Map<String, Object> options) {
final SourceViewer viewer;
if (fSourceEditor == null || (viewer = fSourceEditor.getViewer()) == null) {
return;
}
final Point selectedRange = viewer.getSelectedRange();
if (groupIds == null) {
groupIds = getResetGroupIds();
}
if (options == null) {
options = new HashMap<>();
}
options.put(ISettingsChangedHandler.VIEWER_KEY, viewer);
checkSettingsChanges(groupIds, options);
if (options.containsKey(ITextPresentationConstants.SETTINGSCHANGE_AFFECTSPRESENTATION_KEY)) {
fUpdateTextPresentation = true;
}
updateSourceViewer(viewer);
viewer.setSelectedRange(selectedRange.x, selectedRange.y);
}
protected void checkSettingsChanges(final Set<String> groupIds, final Map<String, Object> options) {
fConfiguration.handleSettingsChanged(groupIds, options);
}
protected void updateSourceViewer(final ISourceViewer viewer) {
if (!fIsConfigured) {
return;
}
if (fUpdateCompleteConfig) {
if (fSourceEditor instanceof ITextEditor) {
SpellingProblem.removeAllInActiveEditor((ITextEditor) fSourceEditor, null);
}
reconfigureSourceViewer();
}
else {
if (fUpdateTabSize) {
viewer.getTextWidget().setTabs(getSourceViewerConfiguration().getTabWidth(viewer));
}
if (fUpdateTextPresentation) {
viewer.invalidateTextPresentation();
}
if (fUpdateIndent && fSourceEditor instanceof SourceEditor1) {
((SourceEditor1) fSourceEditor).updateIndentSettings();
}
if (fUpdateInfoHovers) {
updateConfiguredInfoHovers();
}
}
fUpdateCompleteConfig = false;
fUpdateTextPresentation = false;
fUpdateTabSize = false;
fUpdateIndent = false;
}
private final void reconfigureSourceViewer() {
if (fIsConfigured) {
fIsConfigured = false;
fSourceEditor.getViewer().unconfigure();
fIsConfigured = true;
fSourceEditor.getViewer().configure(fConfiguration);
}
}
private void updateConfiguredInfoHovers() {
final SourceViewer viewer = fSourceEditor.getViewer();
final String[] contentTypes = fConfiguration.getConfiguredContentTypes(viewer);
for (final String contentType : contentTypes) {
((ITextViewerExtension2)viewer).removeTextHovers(contentType);
final int[] stateMasks = fConfiguration.getConfiguredTextHoverStateMasks(viewer, contentType);
if (stateMasks != null) {
for (int j = 0; j < stateMasks.length; j++) {
final int stateMask = stateMasks[j];
final ITextHover textHover = fConfiguration.getTextHover(viewer, contentType, stateMask);
if (textHover != null) {
viewer.setTextHover(textHover, contentType, stateMask);
}
}
}
else {
final ITextHover textHover = fConfiguration.getTextHover(viewer, contentType);
if (textHover != null) {
viewer.setTextHover(textHover, contentType, ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK);
}
}
}
}
}