| /*=============================================================================# |
| # Copyright (c) 2005, 2019 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.ecommons.text.ui.presentation; |
| |
| import java.beans.PropertyChangeListener; |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.databinding.DataBindingContext; |
| import org.eclipse.core.databinding.beans.BeanProperties; |
| import org.eclipse.core.databinding.observable.IObservable; |
| import org.eclipse.core.databinding.observable.Observables; |
| import org.eclipse.core.databinding.observable.Realm; |
| import org.eclipse.core.databinding.observable.list.IObservableList; |
| import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; |
| import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables; |
| import org.eclipse.core.databinding.observable.value.IObservableValue; |
| import org.eclipse.core.databinding.observable.value.ValueChangeEvent; |
| import org.eclipse.core.filebuffers.IDocumentSetupParticipant; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.jface.databinding.swt.WidgetProperties; |
| import org.eclipse.jface.databinding.viewers.ObservableListContentProvider; |
| import org.eclipse.jface.databinding.viewers.ViewersObservables; |
| import org.eclipse.jface.preference.ColorSelector; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.preference.PreferenceConverter; |
| import org.eclipse.jface.text.Document; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.TextAttribute; |
| import org.eclipse.jface.text.source.SourceViewer; |
| import org.eclipse.jface.viewers.CellLabelProvider; |
| import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; |
| import org.eclipse.jface.viewers.ComboViewer; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.LabelProvider; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.viewers.TreeViewer; |
| import org.eclipse.jface.viewers.ViewerCell; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.RGB; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.ui.editors.text.EditorsUI; |
| import org.eclipse.ui.statushandlers.StatusManager; |
| import org.eclipse.ui.texteditor.ChainedPreferenceStore; |
| |
| import org.eclipse.statet.jcommons.collections.ImCollections; |
| import org.eclipse.statet.jcommons.collections.ImList; |
| |
| import org.eclipse.statet.ecommons.preferences.core.Preference; |
| import org.eclipse.statet.ecommons.preferences.ui.ColorSelectorObservableValue; |
| import org.eclipse.statet.ecommons.preferences.ui.OverlayStoreConfigurationBlock; |
| import org.eclipse.statet.ecommons.preferences.ui.OverlayStorePreference; |
| import org.eclipse.statet.ecommons.preferences.ui.PreferenceStoreBeanWrapper; |
| import org.eclipse.statet.ecommons.preferences.ui.RGBPref; |
| import org.eclipse.statet.ecommons.text.ui.TextViewerEditorColorUpdater; |
| import org.eclipse.statet.ecommons.text.ui.TextViewerJFaceUpdater; |
| import org.eclipse.statet.ecommons.text.ui.presentation.AbstractTextStylesConfigurationBlock.SyntaxNode.UseStyle; |
| import org.eclipse.statet.ecommons.text.ui.settings.TextStyleManager; |
| import org.eclipse.statet.ecommons.ui.ColorManager; |
| import org.eclipse.statet.ecommons.ui.util.LayoutUtils; |
| import org.eclipse.statet.ecommons.ui.util.MessageUtils; |
| import org.eclipse.statet.ecommons.ui.util.PixelConverter; |
| import org.eclipse.statet.ecommons.ui.util.UIAccess; |
| import org.eclipse.statet.ecommons.ui.viewers.ViewerUtils; |
| import org.eclipse.statet.ecommons.ui.viewers.ViewerUtils.Node; |
| |
| import org.eclipse.statet.internal.ecommons.text.ui.Messages; |
| import org.eclipse.statet.internal.ltk.ui.LTKUIPlugin; |
| import org.eclipse.statet.ltk.ui.sourceediting.SourceEditorViewerConfiguration; |
| |
| |
| /** |
| * Common UI to configure the text style of syntax tokens (tree, options, preview). |
| */ |
| public abstract class AbstractTextStylesConfigurationBlock extends OverlayStoreConfigurationBlock { |
| // even in text, it currently depends on ltk SourceEditorViewerConfiguration; |
| |
| |
| /** |
| * Generic node of the tree. |
| * |
| * Note: getter and setters in all nodes for easy DataBinding. |
| */ |
| protected static abstract class SyntaxNode extends Node { |
| |
| |
| public static class UseStyle { |
| |
| private final String label; |
| private final String refRootKey; |
| |
| public UseStyle(final String refRootKey, final String label) { |
| super(); |
| this.refRootKey= refRootKey; |
| this.label= label; |
| } |
| |
| public String getLabel() { |
| return this.label; |
| } |
| |
| public String getRefRootKey() { |
| return this.refRootKey; |
| } |
| |
| } |
| |
| public static UseStyle createUseCustomStyle() { |
| return new UseStyle("", Messages.SyntaxColoring_Use_CustomStyle_label); //$NON-NLS-1$ |
| } |
| |
| public static UseStyle createUseNoExtraStyle(final String parentKey) { |
| return new UseStyle(parentKey, Messages.SyntaxColoring_Use_NoExtraStyle_label); |
| } |
| |
| public static UseStyle createUseOtherStyle(final String otherKey, final String otherLabel) { |
| return new UseStyle(otherKey, NLS.bind(Messages.SyntaxColoring_Use_OtherStyle_label, otherLabel)); |
| } |
| |
| public static UseStyle createUseOtherStyle(final String otherKey, final String cat, final String otherLabel) { |
| return new UseStyle(otherKey, NLS.bind(Messages.SyntaxColoring_Use_OtherStyleOf_label, otherLabel, cat)); |
| } |
| |
| |
| protected static class PrefProperty { |
| |
| public final Preference<?> pref; |
| public final String prop; |
| |
| public PrefProperty(final Preference<?> pref, final String prop) { |
| this.pref= pref; |
| this.prop= prop; |
| } |
| } |
| |
| |
| public static final String PROP_USE= "useStyle"; //$NON-NLS-1$ |
| public static final String PROP_COLOR= "color"; //$NON-NLS-1$ |
| public static final String PROP_BOLD= "bold"; //$NON-NLS-1$ |
| public static final String PROP_ITALIC= "italic"; //$NON-NLS-1$ |
| public static final String PROP_STRIKETHROUGH= "strikethrough"; //$NON-NLS-1$ |
| public static final String PROP_UNDERLINE= "underline"; //$NON-NLS-1$ |
| |
| |
| private final String description; |
| |
| private final ImList<UseStyle> availableStyles; |
| |
| private IPreferenceStore preferenceStore; |
| |
| private ImList<PrefProperty> preferences= ImCollections.emptyList(); |
| |
| private PreferenceStoreBeanWrapper beanSupport; |
| |
| |
| private SyntaxNode(final String name, final String description, |
| final SyntaxNode[] children, |
| final ImList<UseStyle> availableStyles) { |
| super(name, children); |
| assert (availableStyles != null); |
| |
| this.description= description; |
| |
| this.availableStyles= availableStyles; |
| } |
| |
| protected void setPreferences(final List<PrefProperty> prefs) { |
| this.preferences= ImCollections.toList(prefs); |
| } |
| |
| |
| public String getDescription() { |
| return this.description; |
| } |
| |
| |
| protected void gatherPreferenceKeys(final List<OverlayStorePreference> keys) { |
| for (final PrefProperty pref : this.preferences) { |
| keys.add(OverlayStorePreference.create(pref.pref)); |
| } |
| } |
| |
| protected void connectPreferenceStore(final IPreferenceStore store) { |
| this.preferenceStore= store; |
| this.beanSupport= new PreferenceStoreBeanWrapper(store, this); |
| for (final PrefProperty pref : this.preferences) { |
| this.beanSupport.addPreference(pref.prop, pref.pref); |
| } |
| } |
| |
| protected IPreferenceStore getPreferenceStore() { |
| return this.preferenceStore; |
| } |
| |
| |
| /*-- Bean-Support --*/ |
| |
| public void addPropertyChangeListener(final PropertyChangeListener listener) { |
| this.beanSupport.addPropertyChangeListener(listener); |
| } |
| |
| public void addPropertyChangeListener(final String propertyName, |
| final PropertyChangeListener listener) { |
| this.beanSupport.addPropertyChangeListener(propertyName, listener); |
| } |
| |
| public void removePropertyChangeListener(final PropertyChangeListener listener) { |
| this.beanSupport.removePropertyChangeListener(listener); |
| } |
| |
| public void removePropertyChangeListener(final String propertyName, |
| final PropertyChangeListener listener) { |
| this.beanSupport.removePropertyChangeListener(propertyName, listener); |
| } |
| |
| |
| /*-- Property-Access --*/ |
| public ImList<UseStyle> getAvailableUseStyles() { |
| return this.availableStyles; |
| } |
| public UseStyle getUseStyle() { |
| return (!this.availableStyles.isEmpty()) ? this.availableStyles.get(0) : null; |
| } |
| protected UseStyle getUseStyle(final String value) { |
| for (final UseStyle style : this.availableStyles) { |
| if (style.getRefRootKey().equals(value)) { |
| return style; |
| } |
| } |
| return this.availableStyles.get(0); |
| } |
| public void setUseStyle(final UseStyle useStyle) { |
| } |
| |
| public RGB getColor() { |
| return null; |
| } |
| public void setColor(final RGB color) { |
| } |
| |
| public boolean isBold() { |
| return false; |
| } |
| public void setBold(final boolean enabled) { |
| } |
| |
| public boolean isItalic() { |
| return false; |
| } |
| public void setItalic(final boolean enabled) { |
| } |
| |
| public boolean isStrikethrough() { |
| return false; |
| } |
| public void setStrikethrough(final boolean enabled) { |
| } |
| |
| public boolean isUnderline() { |
| return false; |
| } |
| public void setUnderline(final boolean enabled) { |
| } |
| |
| } |
| |
| /** |
| * Category Node without syntax style. |
| */ |
| protected static class CategoryNode extends SyntaxNode { |
| |
| public CategoryNode(final String name, final SyntaxNode[] children) { |
| super(name, null, children, ImCollections.<UseStyle>emptyList()); |
| } |
| |
| |
| } |
| |
| /** |
| * Style Node with syntax style, connected to overlay-preferencestory. |
| */ |
| protected static class StyleNode extends SyntaxNode { |
| |
| public class UseStylePref extends Preference<UseStyle> { |
| UseStylePref(final String qualifier, final String key) { |
| super(qualifier, key); |
| } |
| @Override |
| public Class<UseStyle> getUsageType() { |
| return UseStyle.class; |
| } |
| @Override |
| public UseStyle store2Usage(final String storeValue) { |
| return getUseStyle(storeValue); |
| } |
| @Override |
| public String usage2Store(final UseStyle usageValue) { |
| return usageValue.getRefRootKey(); |
| } |
| } |
| |
| private final String rootKey; |
| |
| |
| public StyleNode(final String name, final String description, final String rootKey, |
| final UseStyle[] availableStyles, final SyntaxNode[] children) { |
| super(name, description, children, ImCollections.newList(availableStyles)); |
| this.rootKey= rootKey; |
| |
| final List<PrefProperty> prefs= new ArrayList<>(); |
| if (getAvailableUseStyles().size() > 1) { |
| prefs.add(new PrefProperty(new UseStylePref(null, getUseKey()), PROP_USE)); |
| } |
| prefs.add(new PrefProperty(new RGBPref(null, getColorKey()), PROP_COLOR)); |
| prefs.add(new PrefProperty(new Preference.BooleanPref(null, getBoldKey()), PROP_BOLD)); |
| prefs.add(new PrefProperty(new Preference.BooleanPref(null, getItalicKey()), PROP_ITALIC)); |
| prefs.add(new PrefProperty(new Preference.BooleanPref(null, getUnderlineKey()), PROP_UNDERLINE)); |
| prefs.add(new PrefProperty(new Preference.BooleanPref(null, getStrikethroughKey()), PROP_STRIKETHROUGH)); |
| setPreferences(prefs); |
| } |
| |
| |
| private String getUseKey() { |
| return this.rootKey + ITextPresentationConstants.TEXTSTYLE_USE_SUFFIX; |
| } |
| private String getColorKey() { |
| return this.rootKey + ITextPresentationConstants.TEXTSTYLE_COLOR_SUFFIX; |
| } |
| private String getBoldKey() { |
| return this.rootKey + ITextPresentationConstants.TEXTSTYLE_BOLD_SUFFIX; |
| } |
| private String getItalicKey() { |
| return this.rootKey + ITextPresentationConstants.TEXTSTYLE_ITALIC_SUFFIX; |
| } |
| private String getUnderlineKey() { |
| return this.rootKey + ITextPresentationConstants.TEXTSTYLE_UNDERLINE_SUFFIX; |
| } |
| private String getStrikethroughKey() { |
| return this.rootKey + ITextPresentationConstants.TEXTSTYLE_STRIKETHROUGH_SUFFIX; |
| } |
| |
| |
| /*-- Property-Access --*/ |
| @Override |
| public void setUseStyle(final UseStyle useStyle) { |
| if (useStyle != null) { |
| getPreferenceStore().setValue(getUseKey(), useStyle.getRefRootKey()); |
| } |
| } |
| @Override |
| public UseStyle getUseStyle() { |
| return getUseStyle(getPreferenceStore().getString(getUseKey())); |
| } |
| |
| @Override |
| public RGB getColor() { |
| return PreferenceConverter.getColor(getPreferenceStore(), getColorKey()); |
| } |
| @Override |
| public void setColor(final RGB color) { |
| PreferenceConverter.setValue(getPreferenceStore(), getColorKey(), color); |
| } |
| |
| @Override |
| public boolean isBold() { |
| return getPreferenceStore().getBoolean(getBoldKey()); |
| } |
| @Override |
| public void setBold(final boolean enabled) { |
| getPreferenceStore().setValue(getBoldKey(), enabled); |
| } |
| |
| @Override |
| public boolean isItalic() { |
| return getPreferenceStore().getBoolean(getItalicKey()); |
| } |
| @Override |
| public void setItalic(final boolean enabled) { |
| getPreferenceStore().setValue(getItalicKey(), enabled); |
| } |
| |
| @Override |
| public boolean isStrikethrough() { |
| return getPreferenceStore().getBoolean(getStrikethroughKey()); |
| } |
| @Override |
| public void setStrikethrough(final boolean enabled) { |
| getPreferenceStore().setValue(getStrikethroughKey(), enabled); |
| } |
| |
| @Override |
| public boolean isUnderline() { |
| return getPreferenceStore().getBoolean(getUnderlineKey()); |
| } |
| @Override |
| public void setUnderline(final boolean enabled) { |
| getPreferenceStore().setValue(getUnderlineKey(), enabled); |
| } |
| |
| } |
| |
| protected static class BackgroundNode extends SyntaxNode { |
| |
| |
| private final String rootKey; |
| |
| |
| public BackgroundNode(final String name, final String description, final String rootKey) { |
| super(name, description, null, ImCollections.newList(createUseCustomStyle())); |
| this.rootKey= rootKey; |
| |
| final List<PrefProperty> prefs= new ArrayList<>(); |
| prefs.add(new PrefProperty(new RGBPref(null, getColorKey()), PROP_COLOR)); |
| setPreferences(prefs); |
| } |
| |
| |
| private String getColorKey() { |
| return this.rootKey + ITextPresentationConstants.TEXTSTYLE_COLOR_SUFFIX; |
| } |
| |
| @Override |
| public RGB getColor() { |
| return PreferenceConverter.getColor(getPreferenceStore(), getColorKey()); |
| } |
| @Override |
| public void setColor(final RGB color) { |
| PreferenceConverter.setValue(getPreferenceStore(), getColorKey(), color); |
| } |
| |
| } |
| |
| |
| private static class SyntaxNodeLabelProvider extends CellLabelProvider { |
| |
| @Override |
| public boolean useNativeToolTip(final Object object) { |
| return true; |
| } |
| @Override |
| public String getToolTipText(final Object element) { |
| return ((SyntaxNode) element).getDescription(); |
| } |
| |
| @Override |
| public void update(final ViewerCell cell) { |
| cell.setText(((Node) cell.getElement()).getName()); |
| } |
| |
| } |
| |
| private static class UseStyleLabelProvider extends LabelProvider { |
| |
| @Override |
| public String getText(final Object element) { |
| final UseStyle style= (UseStyle) element; |
| return style.getLabel(); |
| } |
| |
| } |
| |
| |
| private static void collectKeys(final List<OverlayStorePreference> keys, final Node[] nodes) { |
| for (final Node node : nodes) { |
| if (node instanceof SyntaxNode) { |
| ((SyntaxNode) node).gatherPreferenceKeys(keys); |
| } |
| final Node[] children= node.getChildren(); |
| if (children != null) { |
| collectKeys(keys, children); |
| } |
| } |
| } |
| |
| private static void connectStore(final Node[] nodes, final IPreferenceStore store) { |
| for (final Node node: nodes) { |
| if (node instanceof SyntaxNode) { |
| ((SyntaxNode) node).connectPreferenceStore(store); |
| } |
| final Node[] children= node.getChildren(); |
| if (children != null) { |
| connectStore(children, store); |
| } |
| } |
| } |
| |
| |
| private SyntaxNode[] rootNodes; |
| private DataBindingContext dbc; |
| |
| private TreeViewer selectionViewer; |
| |
| private Set<String> groupIds; |
| |
| private ComboViewer useViewer; |
| private Label colorLabel; |
| private ColorSelector colorEditor; |
| private Button boldCheckbox; |
| private Button italicCheckbox; |
| private Button strikethroughCheckbox; |
| private Button underlineCheckbox; |
| |
| private ColorManager colorManager; |
| private TextStyleManager textStyles; |
| |
| protected SourceViewer previewViewer; |
| private SourceEditorViewerConfiguration configuration; |
| |
| |
| public AbstractTextStylesConfigurationBlock() { |
| } |
| |
| |
| protected abstract SyntaxNode[] createItems(); |
| protected abstract String getSettingsGroup(); |
| |
| |
| @Override |
| protected Set<String> getChangedGroups() { |
| return this.groupIds; |
| } |
| |
| protected String getLinkMessage() { |
| return Messages.SyntaxColoring_link; |
| } |
| |
| /** |
| * If {@link TextAttribute}s (underline, strikethrough) is supported |
| */ |
| protected boolean isTextAttributesSupported() { |
| return true; |
| } |
| |
| @Override |
| protected void createBlockArea(final Composite pageComposite) { |
| // Prepare model |
| this.rootNodes= createItems(); |
| this.groupIds= new HashSet<>(); |
| this.groupIds.add(getSettingsGroup()); |
| final List<OverlayStorePreference> keys= new ArrayList<>(); |
| collectKeys(keys, this.rootNodes); |
| setupOverlayStore(keys.toArray(new OverlayStorePreference[keys.size()])); |
| connectStore(this.rootNodes, this.fOverlayStore); |
| |
| { final String message= getLinkMessage(); |
| if (message != null) { |
| addLinkHeader(pageComposite, Messages.SyntaxColoring_link); |
| } |
| } |
| |
| final Composite composite= new Composite(pageComposite, SWT.NONE); |
| composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| composite.setLayout(LayoutUtils.newCompositeGrid(1)); |
| |
| // Tree / Options |
| { final Label label= new Label(composite, SWT.NONE); |
| label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); |
| label.setText(Messages.SyntaxColoring_List_label); |
| } |
| |
| final Composite configComposite= new Composite(composite, SWT.NONE); |
| configComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); |
| configComposite.setLayout(LayoutUtils.newCompositeGrid(2)); |
| { final Control selectionControl= createTreeViewer(configComposite); |
| final GridData gd= new GridData(SWT.FILL, SWT.FILL, false, true); |
| final Point size= ViewerUtils.calculateTreeSizeHint(this.selectionViewer.getControl(), this.rootNodes, 9); |
| gd.widthHint= size.x; |
| gd.heightHint= size.y; |
| selectionControl.setLayoutData(gd); |
| } |
| { final Control optionControl= createOptionsControl(configComposite); |
| final GridData gd= new GridData(SWT.FILL, SWT.FILL, true, true); |
| gd.horizontalIndent= 5; |
| // gd.horizontalIndent= LayoutUtils.defaultSmallIndent(); |
| optionControl.setLayoutData(gd); |
| } |
| |
| // Previewer |
| { final Label label= new Label(composite, SWT.NONE); |
| label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); |
| label.setText(Messages.SyntaxColoring_Preview); |
| } |
| { final Control previewerControl= createPreviewer(composite); |
| final GridData gd= new GridData(SWT.FILL, SWT.FILL, true, true); |
| gd.horizontalSpan= 2; |
| final PixelConverter conv= new PixelConverter(previewerControl); |
| gd.widthHint= conv.convertWidthInCharsToPixels(20); |
| gd.heightHint= conv.convertHeightInCharsToPixels(5); |
| previewerControl.setLayoutData(gd); |
| } |
| |
| initFields(); |
| initBindings(); |
| |
| UIAccess.getDisplay().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| if (UIAccess.isOkToUse(AbstractTextStylesConfigurationBlock.this.selectionViewer)) { |
| AbstractTextStylesConfigurationBlock.this.selectionViewer.setSelection(new StructuredSelection(AbstractTextStylesConfigurationBlock.this.rootNodes[0])); |
| } |
| } |
| }); |
| } |
| |
| |
| public Control createTreeViewer(final Composite parent) { |
| this.selectionViewer= new TreeViewer(parent, SWT.BORDER | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); |
| |
| this.selectionViewer.setContentProvider(new ViewerUtils.NodeContentProvider()); |
| this.selectionViewer.setLabelProvider(new SyntaxNodeLabelProvider()); |
| ColumnViewerToolTipSupport.enableFor(this.selectionViewer); |
| |
| ViewerUtils.addDoubleClickExpansion(this.selectionViewer); |
| |
| return this.selectionViewer.getControl(); |
| } |
| |
| private Control createOptionsControl(final Composite parent) { |
| final Composite composite= new Composite(parent, SWT.NONE); |
| composite.setLayout(LayoutUtils.newCompositeGrid(2)); |
| { this.useViewer= new ComboViewer(composite, SWT.READ_ONLY | SWT.DROP_DOWN); |
| this.useViewer.setLabelProvider(new UseStyleLabelProvider()); |
| final GridData gd= new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1); |
| gd.widthHint= LayoutUtils.hintWidth(this.useViewer.getCombo(), new String[] { |
| "XXXXXXXXXXXXXXX", //$NON-NLS-1$ |
| Messages.SyntaxColoring_Use_CustomStyle_label, |
| Messages.SyntaxColoring_Use_NoExtraStyle_label }); |
| this.useViewer.getControl().setLayoutData(gd); |
| } |
| final int indent= LayoutUtils.defaultSmallIndent(); |
| { final Label label= new Label(composite, SWT.NONE); |
| final GridData gd= new GridData(SWT.FILL, SWT.CENTER, false, false); |
| gd.horizontalIndent= indent; |
| label.setLayoutData(gd); |
| label.setText(Messages.SyntaxColoring_Color); |
| this.colorLabel= label; |
| |
| this.colorEditor= new ColorSelector(composite); |
| final Button foregroundColorButton= this.colorEditor.getButton(); |
| foregroundColorButton.setLayoutData( |
| new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING) ); |
| } |
| { this.boldCheckbox= new Button(composite, SWT.CHECK); |
| final GridData gd= new GridData(SWT.FILL, SWT.CENTER, false, false); |
| gd.horizontalIndent= indent; |
| this.boldCheckbox.setLayoutData(gd); |
| this.boldCheckbox.setText(Messages.SyntaxColoring_Bold); |
| } |
| { this.italicCheckbox= new Button(composite, SWT.CHECK); |
| final GridData gd= new GridData(SWT.FILL, SWT.CENTER, false, false); |
| gd.horizontalIndent= indent; |
| this.italicCheckbox.setLayoutData(gd); |
| this.italicCheckbox.setText(Messages.SyntaxColoring_Italic); |
| } |
| { this.underlineCheckbox= new Button(composite, SWT.CHECK); |
| final GridData gd= new GridData(SWT.FILL, SWT.CENTER, false, false); |
| gd.horizontalIndent= indent; |
| this.underlineCheckbox.setLayoutData(gd); |
| this.underlineCheckbox.setText(Messages.SyntaxColoring_Underline); |
| } |
| { this.strikethroughCheckbox= new Button(composite, SWT.CHECK); |
| final GridData gd= new GridData(SWT.FILL, SWT.CENTER, false, false); |
| gd.horizontalIndent= indent; |
| this.strikethroughCheckbox.setLayoutData(gd); |
| this.strikethroughCheckbox.setText(Messages.SyntaxColoring_Strikethrough); |
| } |
| |
| return composite; |
| } |
| |
| private Control createPreviewer(final Composite parent) { |
| this.colorManager= new ColorManager(); |
| |
| final IPreferenceStore store= new ChainedPreferenceStore(new IPreferenceStore[] { |
| this.fOverlayStore, EditorsUI.getPreferenceStore() }); |
| this.previewViewer= new SourceViewer(parent, null, null, false, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER); |
| this.previewViewer.setEditable(false); |
| this.configuration= getSourceViewerConfiguration(this.colorManager, store); |
| this.previewViewer.configure(this.configuration); |
| new TextViewerJFaceUpdater(this.previewViewer, store); |
| new TextViewerEditorColorUpdater(this.previewViewer, store); |
| |
| final String content= loadPreviewContentFromFile(getPreviewFileName()); |
| final IDocument document= new Document(content); |
| getDocumentSetupParticipant().setup(document); |
| this.previewViewer.setDocument(document); |
| |
| return this.previewViewer.getControl(); |
| } |
| |
| protected abstract String getPreviewFileName(); |
| |
| protected SourceEditorViewerConfiguration getSourceViewerConfiguration( |
| final ColorManager colorManager, final IPreferenceStore preferenceStore) { |
| this.textStyles= new TextStyleManager(colorManager, preferenceStore, getSettingsGroup()); |
| return getSourceEditorViewerConfiguration(preferenceStore, this.textStyles); |
| } |
| |
| protected abstract SourceEditorViewerConfiguration getSourceEditorViewerConfiguration( |
| IPreferenceStore preferenceStore, TextStyleManager textStyles); |
| |
| protected abstract IDocumentSetupParticipant getDocumentSetupParticipant(); |
| |
| private String loadPreviewContentFromFile(final String filename) { |
| String line; |
| final String separator= "\n"; //$NON-NLS-1$ |
| final StringBuffer buffer= new StringBuffer(512); |
| BufferedReader reader= null; |
| try { |
| reader= new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(filename))); |
| while ((line= reader.readLine()) != null) { |
| buffer.append(line); |
| buffer.append(separator); |
| } |
| } |
| catch (final IOException e) { |
| StatusManager.getManager().handle(new Status(IStatus.ERROR, LTKUIPlugin.BUNDLE_ID, -1, |
| NLS.bind("An error occurred when loading the preview code from ''{0}''.", filename), e)); |
| } |
| finally { |
| if (reader != null) { |
| try { reader.close(); } catch (final IOException e) {} |
| } |
| } |
| return buffer.toString(); |
| } |
| |
| |
| public void initFields() { |
| this.selectionViewer.setInput(this.rootNodes); |
| } |
| |
| private void initBindings() { |
| final Realm realm= Realm.getDefault(); |
| this.dbc= new DataBindingContext(realm); |
| |
| // Observe changes in selection. |
| final IObservableValue<SyntaxNode> selectionValue= ViewersObservables.observeSingleSelection(this.selectionViewer); |
| selectionValue.addValueChangeListener( |
| (final ValueChangeEvent<? extends SyntaxNode> event) -> { |
| final SyntaxNode newNode= event.diff.getNewValue(); |
| if (newNode != null) { |
| updateEnablement(newNode, newNode.getUseStyle()); |
| } |
| } ); |
| // Bind use style selection |
| final IObservableList<UseStyle> list= MasterDetailObservables.detailList( |
| BeanProperties.value("availableUseStyles", List.class).observeDetail(selectionValue), |
| new IObservableFactory() { |
| @Override |
| public IObservable createObservable(final Object target) { |
| return Observables.staticObservableList(realm, (List) target); |
| } |
| }, null); |
| this.useViewer.setContentProvider(new ObservableListContentProvider()); |
| this.useViewer.setInput(list); |
| final IObservableValue<UseStyle> useStyle= BeanProperties.value(SyntaxNode.PROP_USE, UseStyle.class) |
| .observeDetail(selectionValue); |
| useStyle.addValueChangeListener( |
| (final ValueChangeEvent<? extends UseStyle> event) -> { |
| final IStructuredSelection selection= (IStructuredSelection) AbstractTextStylesConfigurationBlock.this |
| .selectionViewer.getSelection(); |
| final UseStyle newUse= event.diff.getNewValue(); |
| updateEnablement((SyntaxNode) selection.getFirstElement(), newUse); |
| } ); |
| this.dbc.bindValue( |
| ViewersObservables.observeSingleSelection(this.useViewer), |
| useStyle ); |
| // Bind option widgets to the properties of the current selection. |
| this.dbc.bindValue( |
| new ColorSelectorObservableValue(this.colorEditor), |
| BeanProperties.value(SyntaxNode.PROP_COLOR, RGB.class) |
| .observeDetail(selectionValue) ); |
| this.dbc.bindValue( |
| WidgetProperties.selection().observe(this.boldCheckbox), |
| BeanProperties.value(SyntaxNode.PROP_BOLD, Boolean.class) |
| .observeDetail(selectionValue) ); |
| this.dbc.bindValue( |
| WidgetProperties.selection().observe(this.italicCheckbox), |
| BeanProperties.value(SyntaxNode.PROP_ITALIC, Boolean.class) |
| .observeDetail(selectionValue) ); |
| if (isTextAttributesSupported()) { |
| this.dbc.bindValue( |
| WidgetProperties.selection().observe(this.strikethroughCheckbox), |
| BeanProperties.value(SyntaxNode.PROP_STRIKETHROUGH, Boolean.class) |
| .observeDetail(selectionValue) ); |
| this.dbc.bindValue( |
| WidgetProperties.selection().observe(this.underlineCheckbox), |
| BeanProperties.value(SyntaxNode.PROP_UNDERLINE, Boolean.class) |
| .observeDetail(selectionValue) ); |
| } |
| else { |
| this.strikethroughCheckbox.setVisible(false); |
| this.underlineCheckbox.setVisible(false); |
| } |
| } |
| |
| private void updateEnablement(final SyntaxNode node, final UseStyle useStyle) { |
| int showOptions; |
| boolean enableOptions; |
| if (node instanceof StyleNode) { |
| this.useViewer.getControl().setEnabled(node.getAvailableUseStyles().size() > 1); |
| showOptions= 2; |
| enableOptions= (useStyle != null && useStyle.getRefRootKey().equals("")); //$NON-NLS-1$ |
| } |
| else if (node instanceof BackgroundNode) { |
| this.useViewer.getControl().setEnabled(false); |
| showOptions= 1; |
| enableOptions= true; |
| } |
| else { |
| this.useViewer.getControl().setEnabled(false); |
| showOptions= 0; |
| enableOptions= false; |
| } |
| |
| this.colorLabel.setVisible(showOptions >= 1); |
| this.colorEditor.getButton().setVisible(showOptions >= 1); |
| this.colorLabel.setEnabled(enableOptions); |
| this.colorEditor.setEnabled(enableOptions); |
| |
| this.boldCheckbox.setVisible(showOptions >= 2); |
| this.boldCheckbox.setEnabled(enableOptions); |
| this.italicCheckbox.setVisible(showOptions >= 2); |
| this.italicCheckbox.setEnabled(enableOptions); |
| if (isTextAttributesSupported()) { |
| this.strikethroughCheckbox.setVisible(showOptions >= 2); |
| this.strikethroughCheckbox.setEnabled(enableOptions); |
| this.underlineCheckbox.setVisible(showOptions >= 2); |
| this.underlineCheckbox.setEnabled(enableOptions); |
| } |
| } |
| |
| @Override |
| protected void handlePropertyChange() { |
| if (UIAccess.isOkToUse(this.previewViewer)) { |
| final Map<String, Object> options= new HashMap<>(); |
| if (this.textStyles != null) { |
| this.textStyles.handleSettingsChanged(this.groupIds, options); |
| } |
| this.configuration.handleSettingsChanged(this.groupIds, options); |
| this.previewViewer.invalidateTextPresentation(); |
| } |
| } |
| |
| @Override |
| public void dispose() { |
| if (this.dbc != null) { |
| this.dbc.dispose(); |
| this.dbc= null; |
| } |
| if (this.colorManager != null) { |
| this.colorManager.dispose(); |
| this.colorManager= null; |
| } |
| super.dispose(); |
| } |
| |
| protected String addListToTooltip(final String tooltip, final String[] listItems) { |
| final StringBuilder description= new StringBuilder(tooltip); |
| final int end= Math.min(20, listItems.length); |
| for (int i= 0; i < end; i++) { |
| description.append("\n "); //$NON-NLS-1$ |
| description.append(listItems[i]); |
| } |
| if (end < listItems.length) { |
| description.append("\n ... (" + listItems.length + ')'); //$NON-NLS-1$ |
| } |
| return MessageUtils.escapeForTooltip(description); |
| } |
| |
| protected String addExtraStyleNoteToTooltip(final String tooltip) { |
| return NLS.bind(tooltip, Messages.SyntaxColoring_MindExtraStyle_tooltip); |
| } |
| |
| } |