| /*=============================================================================# |
| # Copyright (c) 2007, 2020 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.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 org.eclipse.statet.ecommons.text.IIndentSettings; |
| import org.eclipse.statet.ecommons.text.core.sections.DocContentSections; |
| import org.eclipse.statet.ecommons.text.ui.presentation.ITextPresentationConstants; |
| import org.eclipse.statet.ecommons.text.ui.settings.DecorationPreferences; |
| import org.eclipse.statet.ecommons.ui.ISettingsChangedHandler; |
| |
| |
| /** |
| * Controls the configuration of an {@link ISourceEditor}. |
| */ |
| public abstract class SourceEditorViewerConfigurator implements ISettingsChangedHandler, |
| PropertyChangeListener { |
| |
| |
| private final SourceEditorViewerConfiguration configuration; |
| |
| private ISourceEditor sourceEditor; |
| |
| private final List<ISourceEditorAddon> addons= new ArrayList<>(); |
| private List<ISourceEditorAddon> configurationAddons; |
| |
| protected boolean isConfigured; |
| |
| protected boolean updateCompleteConfig; |
| protected boolean updateTextPresentation; |
| protected boolean updateTabSize; |
| protected boolean updateIndent; |
| protected boolean updateInfoHovers; |
| |
| |
| protected SourceEditorViewerConfigurator(final SourceEditorViewerConfiguration config) { |
| if (config == null) { |
| throw new NullPointerException("config"); |
| } |
| this.configuration= 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 DocContentSections getDocumentContentInfo() { |
| return this.configuration.getDocumentContentInfo(); |
| } |
| |
| protected abstract Set<String> getResetGroupIds(); |
| |
| public SourceEditorViewerConfiguration getSourceViewerConfiguration() { |
| return this.configuration; |
| } |
| |
| public void configureSourceViewerDecorationSupport(final SourceViewerDecorationSupport support) { |
| final ICharacterPairMatcher pairMatcher= this.configuration.getPairMatcher(); |
| final DecorationPreferences preferences= this.configuration.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); |
| this.sourceEditor= sourceEditor; |
| if (!(this.sourceEditor instanceof AbstractDecoratedTextEditor)) { |
| this.sourceEditor.getViewer().getControl().addDisposeListener(new DisposeListener() { |
| @Override |
| public void widgetDisposed(final DisposeEvent e) { |
| if (SourceEditorViewerConfigurator.this.isConfigured) { |
| uninstallCurrentAddons(); |
| } |
| } |
| }); |
| configureTarget(); |
| } |
| else { |
| this.isConfigured= true; |
| installCurrentAddons(); |
| } |
| handleSettingsChanged(null, null); |
| } |
| |
| protected ISourceViewer getSourceViewer() { |
| if (this.sourceEditor != null) { |
| return this.sourceEditor.getViewer(); |
| } |
| return null; |
| } |
| |
| public final void unconfigureTarget() { |
| if (this.sourceEditor != null) { |
| this.isConfigured= false; |
| uninstallCurrentAddons(); |
| this.sourceEditor.getViewer().unconfigure(); |
| } |
| } |
| |
| public final void configureTarget() { |
| if (this.sourceEditor != null) { |
| this.isConfigured= true; |
| this.sourceEditor.getViewer().configure(this.configuration); |
| installCurrentAddons(); |
| } |
| } |
| |
| private void installCurrentAddons() { |
| this.configurationAddons= getSourceViewerConfiguration().getAddOns(); |
| for (final ISourceEditorAddon addon : this.configurationAddons) { |
| addon.install(this.sourceEditor); |
| } |
| for (final ISourceEditorAddon addon : this.addons) { |
| addon.install(this.sourceEditor); |
| } |
| } |
| |
| private void uninstallCurrentAddons() { |
| for (final ISourceEditorAddon addon : this.addons) { |
| addon.uninstall(); |
| } |
| if (this.configurationAddons != null) { |
| for (final ISourceEditorAddon addon : this.configurationAddons) { |
| addon.uninstall(); |
| } |
| this.configurationAddons= null; |
| } |
| } |
| |
| public final void installAddon(final ISourceEditorAddon installable) { |
| this.addons.add(installable); |
| if (this.isConfigured) { |
| installable.install(this.sourceEditor); |
| } |
| } |
| |
| @Override |
| public void propertyChange(final PropertyChangeEvent event) { |
| final String name= event.getPropertyName(); |
| if (name.equals(IIndentSettings.TAB_SIZE_PROP)) { |
| this.updateTabSize= true; |
| this.updateIndent= true; |
| return; |
| } |
| if (name.equals(IIndentSettings.INDENT_SPACES_COUNT_PROP) |
| || name.equals(IIndentSettings.INDENT_DEFAULT_TYPE_PROP)) { |
| this.updateIndent= true; |
| return; |
| } |
| } |
| |
| @Override |
| public void handleSettingsChanged(Set<String> groupIds, Map<String, Object> options) { |
| final SourceViewer viewer; |
| if (this.sourceEditor == null || (viewer= this.sourceEditor.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)) { |
| this.updateTextPresentation= true; |
| } |
| |
| updateSourceViewer(viewer); |
| viewer.setSelectedRange(selectedRange.x, selectedRange.y); |
| } |
| |
| protected void checkSettingsChanges(final Set<String> groupIds, final Map<String, Object> options) { |
| this.configuration.handleSettingsChanged(groupIds, options); |
| } |
| |
| |
| protected void updateSourceViewer(final ISourceViewer viewer) { |
| if (!this.isConfigured) { |
| return; |
| } |
| if (this.updateCompleteConfig) { |
| if (this.sourceEditor instanceof ITextEditor) { |
| SpellingProblem.removeAllInActiveEditor((ITextEditor) this.sourceEditor, null); |
| } |
| reconfigureSourceViewer(); |
| } |
| else { |
| if (this.updateTabSize) { |
| viewer.getTextWidget().setTabs(getSourceViewerConfiguration().getTabWidth(viewer)); |
| } |
| if (this.updateTextPresentation) { |
| viewer.invalidateTextPresentation(); |
| } |
| if (this.updateIndent && this.sourceEditor instanceof SourceEditor1) { |
| ((SourceEditor1) this.sourceEditor).updateIndentSettings(); |
| } |
| if (this.updateInfoHovers) { |
| updateConfiguredInfoHovers(); |
| } |
| } |
| |
| this.updateCompleteConfig= false; |
| this.updateTextPresentation= false; |
| this.updateTabSize= false; |
| this.updateIndent= false; |
| } |
| |
| private final void reconfigureSourceViewer() { |
| if (this.isConfigured) { |
| this.isConfigured= false; |
| this.sourceEditor.getViewer().unconfigure(); |
| this.isConfigured= true; |
| this.sourceEditor.getViewer().configure(this.configuration); |
| } |
| } |
| |
| private void updateConfiguredInfoHovers() { |
| final SourceViewer viewer= this.sourceEditor.getViewer(); |
| final String[] contentTypes= this.configuration.getConfiguredContentTypes(viewer); |
| for (final String contentType : contentTypes) { |
| ((ITextViewerExtension2)viewer).removeTextHovers(contentType); |
| final int[] stateMasks= this.configuration.getConfiguredTextHoverStateMasks(viewer, contentType); |
| if (stateMasks != null) { |
| for (int j= 0; j < stateMasks.length; j++) { |
| final int stateMask= stateMasks[j]; |
| final ITextHover textHover= this.configuration.getTextHover(viewer, contentType, stateMask); |
| if (textHover != null) { |
| viewer.setTextHover(textHover, contentType, stateMask); |
| } |
| } |
| } |
| else { |
| final ITextHover textHover= this.configuration.getTextHover(viewer, contentType); |
| if (textHover != null) { |
| viewer.setTextHover(textHover, contentType, ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK); |
| } |
| } |
| } |
| } |
| |
| } |