/*=============================================================================# # 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.docmlet.tex.internal.ui.config; import java.util.HashMap; import java.util.Map; import org.eclipse.core.databinding.UpdateValueStrategy; import org.eclipse.core.databinding.beans.BeanProperties; import org.eclipse.core.databinding.observable.set.WritableSet; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.Status; import org.eclipse.jface.databinding.swt.WidgetProperties; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.EditingSupport; import org.eclipse.jface.viewers.ICellEditorListener; import org.eclipse.jface.viewers.ICellEditorValidator; import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; import org.eclipse.swt.widgets.Text; import de.walware.ecommons.IStatusChangeListener; import de.walware.ecommons.databinding.IntegerValidator; import de.walware.ecommons.databinding.jface.DataBindingSupport; import de.walware.ecommons.preferences.core.Preference; import de.walware.ecommons.preferences.ui.ManagedConfigurationBlock; import de.walware.ecommons.text.ui.settings.IndentSettingsUI; import de.walware.ecommons.ui.CombineStatusChangeListener; import de.walware.ecommons.ui.components.EditableTextList; import de.walware.ecommons.ui.util.LayoutUtil; import de.walware.docmlet.tex.core.TexCodeStyleSettings; import de.walware.docmlet.tex.ui.TexUI; /** * A PreferenceBlock for TexCodeStyleSettings (code formatting preferences). */ public class TexCodeStylePreferenceBlock extends ManagedConfigurationBlock { // in future supporting multiple profiles? // -> we bind to bean not to preferences private class LabelEditing extends EditingSupport { private final TextCellEditor cellEditor; private final EditableTextList list; private Object last; private IStatusChangeListener listener; public LabelEditing(final EditableTextList list) { super(list.getViewer()); this.list = list; this.cellEditor = new TextCellEditor(list.getViewer().getTable()); this.cellEditor.addListener(new ICellEditorListener() { @Override public void editorValueChanged(final boolean oldValidState, final boolean newValidState) { if (LabelEditing.this.listener == null) { LabelEditing.this.listener = TexCodeStylePreferenceBlock.this.statusListener.newListener(); } if (!newValidState) { LabelEditing.this.listener.statusChanged(new Status(Status.ERROR, TexUI.PLUGIN_ID, LabelEditing.this.cellEditor.getErrorMessage() )); } else { LabelEditing.this.listener.statusChanged(Status.OK_STATUS); } } @Override public void applyEditorValue() { LabelEditing.this.last = null; if (LabelEditing.this.listener != null) { TexCodeStylePreferenceBlock.this.statusListener.removeListener(LabelEditing.this.listener); LabelEditing.this.listener = null; } } @Override public void cancelEditor() { if (LabelEditing.this.last == "") { //$NON-NLS-1$ LabelEditing.this.list.applyChange("", null); //$NON-NLS-1$ } if (LabelEditing.this.listener != null) { TexCodeStylePreferenceBlock.this.statusListener.removeListener(LabelEditing.this.listener); LabelEditing.this.listener = null; } } }); this.cellEditor.setValidator(new ICellEditorValidator() { @Override public String isValid(final Object value) { final String s = (String) value; for (int i = 0; i < s.length(); i++) { final char c = s.charAt(i); if (!((c >= 0x41 && c <= 0x5A) || (c >= 0x61 && c <= 0x7A) || (c == '*' && i == s.length()-1) )) { return "Invalid environment name"; } } return null; } }); } @Override protected boolean canEdit(final Object element) { return true; } @Override protected CellEditor getCellEditor(final Object element) { return this.cellEditor; } @Override protected Object getValue(final Object element) { this.last = element; return element; } @Override protected void setValue(final Object element, final Object value) { if (value != null) { this.list.applyChange(element, (value != "") ? value : null); //$NON-NLS-1$ } } } private TexCodeStyleSettings model; private IndentSettingsUI stdIndentSettings; private Text indentBlockDepthControl; private Text indentEnvDepthControl; private EditableTextList indentEnvLabelsControl; private final CombineStatusChangeListener statusListener; public TexCodeStylePreferenceBlock(final IProject project, final IStatusChangeListener statusListener) { super(project); this.statusListener = new CombineStatusChangeListener(statusListener); setStatusListener(this.statusListener); } @Override protected void createBlockArea(final Composite pageComposite) { final Map<Preference<?>, String> prefs = new HashMap<>(); prefs.put(TexCodeStyleSettings.TAB_SIZE_PREF, TexCodeStyleSettings.INDENT_GROUP_ID); prefs.put(TexCodeStyleSettings.INDENT_DEFAULT_TYPE_PREF, TexCodeStyleSettings.INDENT_GROUP_ID); prefs.put(TexCodeStyleSettings.INDENT_SPACES_COUNT_PREF, TexCodeStyleSettings.INDENT_GROUP_ID); prefs.put(TexCodeStyleSettings.REPLACE_CONVERSATIVE_PREF, TexCodeStyleSettings.INDENT_GROUP_ID); prefs.put(TexCodeStyleSettings.REPLACE_TABS_WITH_SPACES_PREF, TexCodeStyleSettings.INDENT_GROUP_ID); prefs.put(TexCodeStyleSettings.INDENT_BLOCK_DEPTH_PREF, TexCodeStyleSettings.INDENT_GROUP_ID); prefs.put(TexCodeStyleSettings.INDENT_ENV_DEPTH_PREF, TexCodeStyleSettings.INDENT_GROUP_ID); prefs.put(TexCodeStyleSettings.INDENT_ENV_LABELS_PREF, TexCodeStyleSettings.INDENT_GROUP_ID); setupPreferenceManager(prefs); this.model = new TexCodeStyleSettings(0); this.stdIndentSettings = new IndentSettingsUI(); final Composite mainComposite = new Composite(pageComposite, SWT.NONE); mainComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); mainComposite.setLayout((LayoutUtil.createCompositeGrid(2))); final TabFolder folder = new TabFolder(mainComposite, SWT.NONE); folder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); { final TabItem item = new TabItem(folder, SWT.NONE); item.setText(this.stdIndentSettings.getGroupLabel()); item.setControl(createIndentControls(folder)); } { final TabItem item = new TabItem(folder, SWT.NONE); item.setText("&Line Wrapping"); item.setControl(createLineControls(folder)); } initBindings(); updateControls(); } private Control createIndentControls(final Composite parent) { final Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(LayoutUtil.createTabGrid(2)); this.stdIndentSettings.createControls(composite); LayoutUtil.addSmallFiller(composite, false); final Composite depthComposite = new Composite(composite, SWT.NONE); depthComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); depthComposite.setLayout(LayoutUtil.createCompositeGrid(4)); this.indentBlockDepthControl = createIndentDepthLine(depthComposite, Messages.CodeStyle_Indent_IndentInBlocks_label); this.indentEnvDepthControl = createIndentDepthLine(depthComposite, Messages.CodeStyle_Indent_IndentInEnvs_label); { final Label label = new Label(depthComposite, SWT.NONE); final GridData gd = new GridData(SWT.FILL, SWT.CENTER, true, false, 4, 1); gd.horizontalIndent = LayoutUtil.defaultIndent(); label.setLayoutData(gd); label.setText("Environments to be indented:"); } this.indentEnvLabelsControl = new EditableTextList(); { final Control control = this.indentEnvLabelsControl.create(depthComposite, new ViewerComparator()); final GridData gd = new GridData(SWT.FILL, SWT.FILL, false, true, 3, 1); gd.horizontalIndent = LayoutUtil.defaultIndent(); control.setLayoutData(gd); LayoutUtil.addGDDummy(depthComposite, true); } this.indentEnvLabelsControl.getColumn().setEditingSupport(new LabelEditing(this.indentEnvLabelsControl)); LayoutUtil.addSmallFiller(depthComposite, false); LayoutUtil.addSmallFiller(composite, false); return composite; } private Text createIndentDepthLine(final Composite composite, final String label) { final Label labelControl = new Label(composite, SWT.LEFT); labelControl.setText(label); labelControl.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); final Text textControl = new Text(composite, SWT.RIGHT | SWT.SINGLE | SWT.BORDER); final GridData gd = new GridData(SWT.LEFT, SWT.CENTER, false, false); gd.widthHint = LayoutUtil.hintWidth(textControl, 2); textControl.setLayoutData(gd); final Label typeControl = new Label(composite, SWT.LEFT); typeControl.setText(this.stdIndentSettings.getLevelUnitLabel()); typeControl.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); LayoutUtil.addGDDummy(composite); return textControl; } private Control createLineControls(final Composite parent) { final Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(LayoutUtil.createTabGrid(2)); this.stdIndentSettings.addLineWidth(composite); return composite; } @Override protected void addBindings(final DataBindingSupport db) { this.stdIndentSettings.addBindings(db, this.model); db.getContext().bindValue( WidgetProperties.text(SWT.Modify).observe(this.indentBlockDepthControl), BeanProperties.value(TexCodeStyleSettings.INDENT_BLOCK_DEPTH_PROP) .observe(db.getRealm(), this.model), new UpdateValueStrategy().setAfterGetValidator(new IntegerValidator(1, 10, Messages.CodeStyle_Indent_IndentInBlocks_error_message)), null ); db.getContext().bindValue( WidgetProperties.text(SWT.Modify).observe(this.indentEnvDepthControl), BeanProperties.value(TexCodeStyleSettings.INDENT_ENV_DEPTH_PROP) .observe(db.getRealm(), this.model), new UpdateValueStrategy().setAfterGetValidator(new IntegerValidator(1, 10, Messages.CodeStyle_Indent_IndentInEnvs_error_message)), null ); final WritableSet labels = new WritableSet(db.getRealm()); this.indentEnvLabelsControl.setInput(labels); db.getContext().bindSet( labels, BeanProperties.set(TexCodeStyleSettings.INDENT_ENV_LABELS_PROP, String.class) .observe(db.getRealm(), this.model) ); } @Override protected void updateControls() { this.model.load(this); this.model.resetDirty(); getDataBinding().getContext().updateTargets(); // required for invalid target values this.indentEnvLabelsControl.refresh(); } @Override protected void updatePreferences() { if (this.model.isDirty()) { this.model.resetDirty(); setPrefValues(this.model.toPreferencesMap()); } } }