blob: 20bded4d488d45ccb96f589e3ec0982dad39e1dc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2021 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.ui.preferences.formatter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
import java.util.function.Consumer;
import java.util.function.Predicate;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.StatusDialog;
import org.eclipse.jface.layout.PixelConverter;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.Twistie;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.JavaPluginImages;
import org.eclipse.jdt.internal.ui.dialogs.StatusInfo;
import org.eclipse.jdt.internal.ui.preferences.FilteredPreferenceTree;
import org.eclipse.jdt.internal.ui.preferences.FilteredPreferenceTree.PreferenceTreeNode;
import org.eclipse.jdt.internal.ui.preferences.PreferenceHighlight;
import org.eclipse.jdt.internal.ui.preferences.PreferencesMessages;
import org.eclipse.jdt.internal.ui.preferences.formatter.IModifyDialogTabPage.IModificationListener;
import org.eclipse.jdt.internal.ui.preferences.formatter.ProfileManager.CustomProfile;
import org.eclipse.jdt.internal.ui.preferences.formatter.ProfileManager.Profile;
import org.eclipse.jdt.internal.ui.util.ExceptionHandler;
import org.eclipse.jdt.internal.ui.util.SWTUtil;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.LayoutUtil;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.StringDialogField;
public abstract class ModifyDialog extends StatusDialog implements IModificationListener {
protected static class Section extends PreferenceTreeNode<ExpandableComposite> {
protected final Composite fInnerComposite;
private final String fPreviewKey;
private final Control fToggle;
public static Section create(Composite parentComposite, String label, String previewKey) {
final ExpandableComposite excomposite= new FormatterPreferenceSectionComposite(parentComposite, SWT.NONE, ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT);
excomposite.clientVerticalSpacing= 0;
excomposite.setText(label);
excomposite.setExpanded(false);
excomposite.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT));
excomposite.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false, GRID_COLUMNS, 1));
Composite inner= new Composite(excomposite, SWT.NONE);
inner.setFont(parentComposite.getFont());
GridLayout layout= new GridLayout(GRID_COLUMNS, false);
layout.marginWidth= 0;
layout.marginLeft= 5;
inner.setLayout(layout);
excomposite.setClient(inner);
return new Section(label, excomposite, inner, previewKey);
}
private Section(String label, ExpandableComposite control, Composite innerComposite, String previewKey) {
super(label, control, false);
fInnerComposite= innerComposite;
fPreviewKey= previewKey;
Control toggle= null;
for (Control child : control.getChildren()) {
if (child instanceof Twistie)
toggle= child;
}
assert toggle != null;
fToggle= toggle;
// clicking inside section should bring it to focus
final MouseAdapter mouseListener= new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
if (e.getSource() == fControl && e.x < fControl.getClient().getLocation().x)
return;
if (e.getSource() == fControl.getClient() && e.x < getChildren().get(0).getControl().getLocation().x)
return;
fToggle.setFocus();
}
};
control.addMouseListener(mouseListener);
control.getClient().addMouseListener(mouseListener);
}
public String getKey() {
return fPreviewKey;
}
public Control getToggle() {
return fToggle;
}
public Preference<?> findChildPreference(String key) {
for (PreferenceTreeNode<?> child : getChildren()) {
if (child instanceof Preference && key.equals(((Preference<?>) child).getKey()))
return (Preference<?>) child;
}
throw new IllegalArgumentException(key);
}
}
protected abstract static class ModifyAll<T extends Control> {
private final Section fSection;
private final Composite fModifyAllPanel;
protected final T fControl;
public ModifyAll(Section section, Images images) {
fSection= section;
final ExpandableComposite excomposite= section.getControl();
Composite modifyAllParent= new Composite(excomposite, SWT.NONE);
excomposite.setTextClient(modifyAllParent);
RowLayout rowLayout= new RowLayout();
rowLayout.marginTop= rowLayout.marginBottom= rowLayout.marginRight= 0;
rowLayout.center= true;
rowLayout.spacing= 1;
modifyAllParent.setLayout(rowLayout);
fModifyAllPanel= new Composite(modifyAllParent, SWT.NONE);
fModifyAllPanel.setLayout(rowLayout);
fModifyAllPanel.setVisible(false);
fControl= createControl(fModifyAllPanel);
ToolItem item= createToolItem(modifyAllParent, images);
item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
fModifyAllPanel.setVisible(!fModifyAllPanel.isVisible());
if (fModifyAllPanel.isVisible()) {
excomposite.setFocus(); // force preview update
prepareControl();
fControl.requestLayout();
fControl.setFocus();
}
}
});
fControl.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
fModifyAllPanel.setVisible(false);
}
});
}
protected abstract T createControl(Composite parent);
protected abstract void prepareControl();
protected <P extends Preference<T>> List<P> findPreferences(Class<P> prefClass) {
List<P> result= new ArrayList<>();
ArrayDeque<PreferenceTreeNode<?>> queue= new ArrayDeque<>();
queue.add(fSection);
while (!queue.isEmpty()) {
PreferenceTreeNode<?> node= queue.removeFirst();
if (!node.isVisible())
continue;
if (prefClass.isInstance(node))
result.add(prefClass.cast(node));
queue.addAll(node.getChildren());
}
return result;
}
static ToolItem createToolItem(Composite parent, Images images) {
ToolBar toolBar= new ToolBar(parent, SWT.FLAT);
ToolItem item= new ToolItem(toolBar, SWT.PUSH);
item.setToolTipText(FormatterMessages.ModifyDialog_modifyAll_tooltip);
item.setImage(images.get(JavaPluginImages.DESC_ELCL_MODIFYALL));
return item;
}
}
protected abstract static class Preference<T extends Control> extends PreferenceTreeNode<T> {
private Map<String, String> fWorkingValues;
private Predicate<String> fValueValidator;
protected Runnable fValueChangeListener;
private String fKey;
private Map<PreferenceTreeNode<?>, Predicate<String>> fDependants= new HashMap<>();
protected PreferenceHighlight fHighlight;
public Preference(T control, String label, String key, ValueMatcher<T> valueMatcher) {
super(label, control, true, valueMatcher);
assert key != null;
fKey= key;
}
protected void init(Map<String, String> workingValues, Predicate<String> valueValidator, Runnable valueChangeListener) {
fWorkingValues= workingValues;
fValueValidator= valueValidator;
fValueChangeListener= valueChangeListener;
updateWidget();
fControl.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
fValueValidator.test(null);
}
});
}
protected Map<String, String> getPreferences() {
return fWorkingValues;
}
public void setValueValidator(Predicate<String> valueValidator) {
fValueValidator = valueValidator;
}
/**
* Set the key which is used to store the value.
*
* @param key New value
*/
public final void setKey(String key) {
assert key != null;
fKey= key;
updateWidget();
}
/**
* @return Gets the currently used key which is used to store the value.
*/
public final String getKey() {
return fKey;
}
public void addDependant(PreferenceTreeNode<?> dependentChild, Predicate<String> dependencyChecker) {
fDependants.put(dependentChild, dependencyChecker);
if (fWorkingValues != null)
updateDependants();
}
/**
* To be implemented in subclasses. Update the SWT widgets when the state of this object has changed
* (enabled, key, ...).
*/
protected abstract void updateWidget();
protected abstract String getValue();
protected void updateValue() {
String newValue= getValue();
String oldValue= fWorkingValues.put(fKey, newValue);
if (fValueValidator.test(newValue)) {
updateDependants();
fValueChangeListener.run();
} else {
fWorkingValues.put(fKey, oldValue);
}
}
private void updateDependants() {
for (Entry<PreferenceTreeNode<?>, Predicate<String>> entry : fDependants.entrySet()) {
PreferenceTreeNode<?> dependant= entry.getKey();
Predicate<String> dependencyChecker= entry.getValue();
dependant.setEnabled(dependencyChecker.test(fWorkingValues.get(fKey)));
}
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
if (enabled)
updateDependants();
}
protected void addLabel(String label, boolean highlight, int indent) {
if (label == null)
return;
Label labelControl= createLabel(GRID_COLUMNS - 2, fControl.getParent(), label, indent);
labelControl.moveAbove(fControl);
if (highlight)
fHighlight= PreferenceHighlight.addHighlight(labelControl, fControl, false);
addChild(new PreferenceTreeNode<>(label, labelControl, true));
}
}
/**
* Wrapper around a checkbox with a label.
*/
protected static final class CheckboxPreference extends Preference<Button> {
/**
* Constant array for boolean false/true selection.
*/
public static final String[] FALSE_TRUE= { DefaultCodeFormatterConstants.FALSE, DefaultCodeFormatterConstants.TRUE };
/**
* Constant array for boolean true/false selection.
*/
public static final String[] TRUE_FALSE= { DefaultCodeFormatterConstants.TRUE, DefaultCodeFormatterConstants.FALSE };
/**
* Constant array for insert / not_insert.
*/
public static final String[] DO_NOT_INSERT_INSERT= { JavaCore.DO_NOT_INSERT, JavaCore.INSERT };
private final String[] fValues;
/**
* Create a new CheckboxPreference.
*
* @param parentComposite The composite on which the SWT widgets are added.
* @param indent how many levels of indentation to apply.
* @param label The label text for this Preference.
* @param key The key to store the values.
* @param values An array of two elements indicating the values to store when unchecked/checked.
* @return a newly created CheckboxPreference.
*/
public static CheckboxPreference create(Composite parentComposite, int indent, String label, String key, String[] values) {
Button checkbox= new Button(parentComposite, SWT.CHECK);
checkbox.setText(label);
checkbox.setLayoutData(createGridData(GRID_COLUMNS - 1, GridData.FILL_HORIZONTAL, SWT.DEFAULT, indent));
checkbox.setFont(parentComposite.getFont());
return new CheckboxPreference(checkbox, label, key, values);
}
private CheckboxPreference(Button button, String label, String key, String[] values) {
super(button, label, key, FilteredPreferenceTree.CHECK_BOX_MATCHER);
fValues= values;
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
updateValue();
}
});
}
@Override
protected String getValue() {
boolean state= fControl.getSelection();
return state ? fValues[1] : fValues[0];
}
@Override
protected void updateWidget() {
boolean checked= fValues[1].equals(getPreferences().get(getKey()));
fControl.setSelection(checked);
}
public static ModifyAll<Button> addModifyAll(Section section, Images images) {
return new ModifyAll<Button>(section, images) {
@Override
protected Button createControl(Composite parent) {
Button checkBox= new Button(parent, SWT.CHECK);
checkBox.setFont(parent.getFont());
checkBox.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
boolean selected= fControl.getSelection();
for (CheckboxPreference pref : findPreferences(CheckboxPreference.class)) {
pref.getControl().setSelection(selected);
pref.updateValue();
}
prepareControl();
}
});
return checkBox;
}
@Override
protected void prepareControl() {
List<CheckboxPreference> preferences= findPreferences(CheckboxPreference.class);
int count= 0;
for (CheckboxPreference pref : preferences) {
if (pref.getControl().getSelection())
count++;
}
fControl.setSelection(count == preferences.size());
fControl.setText(Messages.format(FormatterMessages.ModifyDialog_modifyAll_checkBox, new Object[] { count, preferences.size() }));
fControl.requestLayout();
}
};
}
}
/**
* Wrapper around a combo box and a label.
*/
protected static final class ComboPreference extends Preference<Combo> {
private final List<String> fValues;
/**
* @param parentComposite The composite on which the SWT widgets are added.
* @param indent how many levels of indentation to apply.
* @param label The label text for this Preference or {@code null} if none.
* @param key The key to store the values.
* @param values An array of n elements indicating the values to store for each selection.
* @param items An array of n elements indicating the text to be written in the combo box.
* @param highlight whether highlight arrow should be added.
* @return a newly created ComboPreference.
*/
public static ComboPreference create(Composite parentComposite, int indent, String label, String key, String[] values, String[] items, boolean highlight) {
Combo combo= new Combo(parentComposite, SWT.SINGLE | SWT.READ_ONLY);
combo.setFont(parentComposite.getFont());
SWTUtil.setDefaultVisibleItemCount(combo);
combo.setItems(items);
combo.setLayoutData(createGridData(1, GridData.HORIZONTAL_ALIGN_FILL, combo.computeSize(SWT.DEFAULT, SWT.DEFAULT).x, 0));
ComboPreference comboPreference= new ComboPreference(combo, label, key, values);
comboPreference.addLabel(label, highlight, indent);
return comboPreference;
}
private ComboPreference(Combo combo, String label, String key, String[] values) {
super(combo, label, key, FilteredPreferenceTree.COMBO_VALUE_MATCHER);
fValues= Arrays.asList(values);
combo.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
updateValue();
}
});
}
@Override
protected void updateWidget() {
int valueIndex= fValues.indexOf(getPreferences().get(getKey()));
if (valueIndex == -1) {
final String message= Messages.format(FormatterMessages.ModifyDialog_ComboPreference_error_invalid_key, new Object[] { getKey(), getPreferences().get(getKey()) });
JavaPlugin.log(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IStatus.OK, message, null));
valueIndex= 0;
}
fControl.select(valueIndex);
}
@Override
protected String getValue() {
return fValues.get(fControl.getSelectionIndex());
}
public static ModifyAll<Combo> addModifyAll(Section section, Images images) {
return new ModifyAll<Combo>(section, images) {
ArrayList<String> fItems= new ArrayList<>();
@Override
protected Combo createControl(Composite parent) {
Combo combo= new Combo(parent, SWT.SINGLE | SWT.READ_ONLY);
combo.setFont(parent.getFont());
SWTUtil.setDefaultVisibleItemCount(combo);
combo.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
String selected= fItems.get(fControl.getSelectionIndex());
for (ComboPreference pref : findPreferences(ComboPreference.class)) {
int index= pref.fControl.indexOf(selected);
if (index >= 0)
pref.fControl.select(index);
pref.updateValue();
}
prepareControl();
}
});
return combo;
}
@Override
protected void prepareControl() {
LinkedHashMap<String, Integer> itemCounts= new LinkedHashMap<>();
List<ComboPreference> preferences= findPreferences(ComboPreference.class);
for (ComboPreference pref : preferences) {
String[] items= pref.getControl().getItems();
for (String item : items) {
if (!itemCounts.containsKey(item))
itemCounts.put(item, 0);
}
String selected= items[pref.getControl().getSelectionIndex()];
itemCounts.put(selected, itemCounts.get(selected) + 1);
}
String[] items= new String[itemCounts.size()];
fItems.clear();
int i= 0;
int maxCount= 0;
int maxCountIndex= 0;
for (Entry<String, Integer> entry : itemCounts.entrySet()) {
String item= entry.getKey();
int count= entry.getValue();
fItems.add(item);
if (count > 0) {
item+= Messages.format(FormatterMessages.ModifyDialog_modifyAll_summary, new Object[] { count, preferences.size() });
}
if (count > maxCount) {
maxCount= count;
maxCountIndex= i;
}
items[i++]= item;
}
fControl.setItems(items);
fControl.select(maxCountIndex);
fControl.requestLayout();
}
};
}
}
/**
* Wrapper around a textfied which requests an integer input of a given range.
*/
protected static final class NumberPreference extends Preference<Spinner> {
/**
* @param parentComposite The composite on which the SWT widgets are added.
* @param indent how many levels of indentation to apply.
* @param label The label text for this Preference or {@code null} if none.
* @param key The key to store the values.
* @param minValue The minimum value which is valid input.
* @param maxValue The maximum value which is valid input.
* @param highlight whether highlight arrow should be added.
* @return a newly created NumberPreference.
*/
public static NumberPreference create(Composite parentComposite, int indent, String label, String key, int minValue, int maxValue, boolean highlight) {
Spinner spinner= createSpinner(parentComposite, minValue, maxValue);
NumberPreference numberPreference= new NumberPreference(spinner, label, key);
numberPreference.addLabel(label, highlight, indent);
return numberPreference;
}
static Spinner createSpinner(Composite parentComposite, int minValue, int maxValue) {
Spinner spinner= new Spinner(parentComposite, SWT.BORDER);
spinner.setFont(parentComposite.getFont());
spinner.setMinimum(minValue);
spinner.setMaximum(maxValue);
spinner.setLayoutData(createGridData(1, GridData.HORIZONTAL_ALIGN_END, SWT.DEFAULT, 0));
return spinner;
}
private NumberPreference(Spinner spinner, String label, String key) {
super(spinner, label, key, FilteredPreferenceTree.SPINNER_VALUE_MATCHER);
spinner.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
updateValue();
}
});
}
@Override
protected void updateWidget() {
try {
String s= getPreferences().get(getKey());
int number= Integer.parseInt(s);
number= Math.max(fControl.getMinimum(), Math.min(fControl.getMaximum(), number));
fControl.setSelection(number);
} catch (NumberFormatException x) {
final String message= Messages.format(FormatterMessages.ModifyDialogTabPage_NumberPreference_error_invalid_key, getKey());
JavaPlugin.log(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IStatus.OK, message, null));
fControl.setSelection(fControl.getMinimum());
}
}
@Override
protected String getValue() {
return Integer.toString(fControl.getSelection());
}
public static ModifyAll<Spinner> addModifyAll(final int minValue, final int maxValue, Section section, Images images) {
return new ModifyAll<Spinner>(section, images) {
private Label fLabel;
@Override
protected Spinner createControl(Composite parent) {
GridLayout layout= new GridLayout(2, false);
layout.marginWidth= layout.marginHeight= 0;
parent.setLayout(layout);
fLabel= createLabel(1, parent, "", 0); //$NON-NLS-1$
Spinner spinner= createSpinner(parent, minValue, maxValue);
spinner.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
int selected= fControl.getSelection();
for (NumberPreference pref : findPreferences(NumberPreference.class)) {
pref.getControl().setSelection(selected);
pref.updateValue();
}
prepareControl();
}
});
return spinner;
}
@Override
protected void prepareControl() {
int modeValue= 0;
int modeCount= -1;
HashMap<Integer, Integer> counts= new HashMap<>();
List<NumberPreference> preferences= findPreferences(NumberPreference.class);
for (NumberPreference pref : preferences) {
int value= pref.getControl().getSelection();
int count= counts.merge(value, 1, Integer::sum);
if (count > modeCount) {
modeValue= value;
modeCount= count;
}
}
fControl.setSelection(modeValue);
fLabel.setText(Messages.format(FormatterMessages.ModifyDialog_modifyAll_summary, new Object[] { modeCount, preferences.size() }));
fLabel.requestLayout();
}
};
}
}
/**
* Wrapper around a text field which requests a string input.
*/
protected static final class StringPreference extends Preference<Text> {
/**
* @param parentComposite The composite on which the SWT widgets are added.
* @param indent how many levels of indentation to apply.
* @param label the label text for this Preference.
* @param key the key to store the values.
* @param highlight whether highlight arrow should be added
* @return a newly created StringPreference
*/
public static StringPreference create(Composite parentComposite, int indent, String label, String key, boolean highlight) {
Text text= new Text(parentComposite, SWT.SINGLE | SWT.BORDER);
text.setFont(parentComposite.getFont());
final int length= 30;
PixelConverter pixelConverter= new PixelConverter(parentComposite);
GridData gridData= createGridData(1, GridData.HORIZONTAL_ALIGN_BEGINNING, pixelConverter.convertWidthInCharsToPixels(length), 0);
gridData.grabExcessHorizontalSpace= true;
text.setLayoutData(gridData);
StringPreference stringPreference= new StringPreference(text, label, key);
stringPreference.addLabel(label, highlight, indent);
return stringPreference;
}
private StringPreference(Text control, String label, String key) {
super(control, label, key, FilteredPreferenceTree.TEXT_VALUE_MATCHER);
fControl.addModifyListener(e -> updateValue());
fControl.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
fControl.setSelection(0, fControl.getCharCount());
}
});
}
@Override
protected void updateWidget() {
String value= getPreferences().get(getKey());
fControl.setText(value);
}
@Override
protected String getValue() {
return fControl.getText();
}
}
@FunctionalInterface
public interface PreferenceBuilder {
Preference<?> buildPreference(Section parent, String label, String key);
}
protected class ProfilePreferenceTree extends FilteredPreferenceTree {
/**
* Helper class for easy, call-chain based building of subtrees that contain only Sections and one
* type of preference.
*
* @param <T> Type of preference or section built
*/
public abstract class SimpleTreeBuilder<T extends PreferenceTreeNode<?>> {
protected final String fLabel, fKey;
protected boolean fGap;
protected Consumer<T> fCustomizer;
protected SimpleTreeBuilder(String label, String key, Consumer<T> customizer) {
fLabel= label;
fKey= key;
fCustomizer= customizer;
}
protected abstract T build(Section parent, PreferenceBuilder prefBuilder);
}
public class SectionBuilder extends SimpleTreeBuilder<Section> {
private ArrayList<SimpleTreeBuilder<?>> fChildren= new ArrayList<>();
SectionBuilder(String label, String key, Consumer<Section> customizer) {
super(label, key, customizer);
}
public SectionBuilder node(SimpleTreeBuilder<?> child) {
fChildren.add(child);
return this;
}
public SectionBuilder pref(String label, String key) {
return pref(label, key, null);
}
public SectionBuilder pref(String label, String key, Consumer<Preference<?>> customizer) {
return node(new PrefBuilder(label, key, customizer));
}
public SectionBuilder gap() {
fChildren.get(fChildren.size() - 1).fGap= true;
return this;
}
@Override
public Section build(Section parent, PreferenceBuilder prefBuilder) {
String key= fKey;
if (key != null) {
Section ancestorWithKey= parent;
while (ancestorWithKey != null && ancestorWithKey.getKey() == null)
ancestorWithKey= (Section) ancestorWithKey.getParent();
if (ancestorWithKey != null)
key= ancestorWithKey.getKey() + key;
}
Section section= addSection(parent, fLabel, key);
for (SimpleTreeBuilder<?> child : fChildren) {
child.build(section, prefBuilder);
if (child.fGap)
addGap(section);
}
if (fCustomizer != null)
fCustomizer.accept(section);
return section;
}
}
private class PrefBuilder extends SimpleTreeBuilder<Preference<?>> {
public PrefBuilder(String label, String key, Consumer<Preference<?>> customizer) {
super(label, key, customizer);
}
@Override
protected Preference<?> build(Section parent, PreferenceBuilder prefBuilder) {
Preference<?> pref= prefBuilder.buildPreference(parent, fLabel, fKey);
if (fCustomizer != null)
fCustomizer.accept(pref);
return pref;
}
}
private boolean fFilterEmpty;
private int fRightMargin;
public ProfilePreferenceTree(Composite parentComposite) {
super(parentComposite, FormatterMessages.ModifyDialog_filter_label, FormatterMessages.ModifyDialog_filter_hint);
// calculate rigth margin width
ToolBar modifyAllToolbar= ModifyAll.createToolItem(parentComposite, fImages).getParent();
fRightMargin= modifyAllToolbar.computeSize(SWT.DEFAULT, SWT.DEFAULT).x - 12;
modifyAllToolbar.dispose();
}
public Section addSection(Section parent, String label, String previewKey) {
Section section= Section.create(getParentComposite(parent), label, previewKey);
ExpandableComposite excomposite= section.getControl();
getScrolledPageContent().adaptChild(excomposite);
Menu expandAllMenu= new Menu(excomposite);
MenuItem expandAllItem= new MenuItem(expandAllMenu, SWT.NONE);
expandAllItem.setText(PreferencesMessages.FilteredPreferencesTree_expandAll_tooltip);
expandAllItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
setAllExpanded(section, true);
}
});
excomposite.setMenu(expandAllMenu);
return addChild(parent, section);
}
public CheckboxPreference addCheckbox(PreferenceTreeNode<?> parent, String label, String key, String[] values) {
CheckboxPreference button= CheckboxPreference.create(getParentComposite(parent), getIndent(parent), label, key, values);
return addChild(parent, button);
}
public ComboPreference addComboPref(PreferenceTreeNode<?> parent, String label, String key, String[] values, String[] items) {
ComboPreference combo= ComboPreference.create(getParentComposite(parent), getIndent(parent), label, key, values, items, true);
return addChild(parent, combo);
}
public NumberPreference addNumberPref(PreferenceTreeNode<?> parent, String label, String key, int minValue, int maxValue) {
NumberPreference number= NumberPreference.create(getParentComposite(parent), getIndent(parent), label, key, minValue, maxValue, true);
return addChild(parent, number);
}
public StringPreference addStringPreference(PreferenceTreeNode<?> parent, String label, String key) {
StringPreference stringPreference= StringPreference.create(getParentComposite(parent), getIndent(parent), label, key, true);
return addChild(parent, stringPreference);
}
@Override
public <T extends PreferenceTreeNode<?>> T addChild(PreferenceTreeNode<?> parent, T node) {
super.addChild(parent, node);
fFocusManager.add(node);
if (node instanceof Preference<?>) {
Predicate<String> validator = v -> {
doValidate();
return true;
};
((Preference<?>) node).init(fWorkingValues, validator, ModifyDialog.this::valuesModified);
}
if (!(node instanceof Section)) {
Label margin= new Label(node.getControl().getParent(), SWT.NONE);
margin.setLayoutData(new GridData(fRightMargin, SWT.DEFAULT));
node.addChild(new PreferenceTreeNode<>("", margin, true)); //$NON-NLS-1$
}
return node;
}
public void addGap(PreferenceTreeNode<?> parent) {
Composite gap= new Composite(getParentComposite(parent), SWT.NONE);
GridData gd= new GridData(0, 4);
gd.horizontalSpan= GRID_COLUMNS;
gap.setLayoutData(gd);
parent.addChild(new PreferenceTreeNode<>("", gap, true)); //$NON-NLS-1$
}
public SectionBuilder builder(String label, String key) {
return builder(label, key, null);
}
public SectionBuilder builder(String label, String key, Consumer<Section> customizer) {
return new SectionBuilder(label, key, customizer);
}
private Composite getParentComposite(PreferenceTreeNode<?> parent) {
while (parent != null) {
if (parent instanceof Section)
return ((Section) parent).fInnerComposite;
parent= parent.getParent();
}
return getScrolledPageContent().getBody();
}
protected int getIndent(PreferenceTreeNode<?> parent) {
int indent= 0;
while (parent != null) {
if (parent instanceof Section)
return indent;
indent++;
parent= parent.getParent();
}
return indent;
}
protected void saveState() {
try (ByteArrayOutputStream out= new ByteArrayOutputStream()) {
writeExpansionState(fRoot, out);
fDialogSettings.put(fKeyPreferenceTreeExpansion, out.toString("UTF-8")); //$NON-NLS-1$
} catch (IOException e) {
throw new AssertionError(e);
}
int scrollPosition= getScrolledPageContent().getOrigin().y;
fDialogSettings.put(fKeyPreferenceScrollPosition, scrollPosition);
}
private void writeExpansionState(PreferenceTreeNode<?> node, OutputStream output) throws IOException {
for (PreferenceTreeNode<?> child : node.getChildren()) {
if (child instanceof Section) {
output.write(((Section) child).getControl().isExpanded() ? '1' : '0');
writeExpansionState(child, output);
}
}
output.write('.');
}
protected void restoreExpansionState() {
String treeState= fDialogSettings.get(fKeyPreferenceTreeExpansion);
if (treeState == null)
return;
try (ByteArrayInputStream in= new ByteArrayInputStream(treeState.getBytes("UTF-8"))) { //$NON-NLS-1$
fScrolledPageContent.setReflow(false);
readExpansionState(fRoot, in);
fScrolledPageContent.setReflow(true);
} catch (IOException e) {
throw new AssertionError(e);
}
}
private void readExpansionState(PreferenceTreeNode<?> node, InputStream in) throws IOException {
for (PreferenceTreeNode<?> child : node.getChildren()) {
if (child instanceof Section) {
int state= in.read();
((Section) child).getControl().setExpanded(state == '1');
readExpansionState(child, in);
if (state == '.' || state == -1)
return; // some nodes missing in stored tree
}
}
int c= in.read();
while (c != '.' && c != -1) { // some extra nodes in stored tree
readExpansionState(new PreferenceTreeNode<>(null, null, false), in);
c= in.read();
}
}
protected void restoreScrollPosition() {
try {
int scrollPosition= fDialogSettings.getInt(fKeyPreferenceScrollPosition);
getScrolledPageContent().setOrigin(0, scrollPosition);
} catch (NumberFormatException e) {
// ignore invalid/undefined value
}
}
@Override
public void doFilter(String filterText) {
fFilterEmpty= filterText.trim().isEmpty();
super.doFilter(filterText);
}
@Override
protected void updateUI(PreferenceTreeNode<?> node) {
super.updateUI(node);
if (node == fRoot && fFilterEmpty)
restoreExpansionState();
}
public void unifySectionTitlesHeights(Section section) {
List<PreferenceTreeNode<?>> children= section == null ? fRoot.getChildren() : section.getChildren();
int maxHeightDiff= 0;
for (PreferenceTreeNode<?> child : children) {
if (child instanceof Section)
maxHeightDiff= Math.max(maxHeightDiff, ((Section) child).getControl().getTextClientHeightDifference());
}
int nextChildIndent= 0;
for (PreferenceTreeNode<?> child : children) {
int indent= nextChildIndent;
nextChildIndent= 0;
if (child instanceof Section) {
Section sectionChild= (Section) child;
int diff= maxHeightDiff - sectionChild.getControl().getTextClientHeightDifference();
nextChildIndent= diff / 2;
indent+= diff - diff / 2;
unifySectionTitlesHeights(sectionChild);
}
GridData gd= (GridData) child.getControl().getLayoutData();
gd.verticalIndent+= indent;
}
}
}
public static class Images {
private Map<ImageDescriptor, Image> imagesMap= new HashMap<>();
protected Images(Composite rootComposite) {
rootComposite.addDisposeListener(e -> {
for (Image image : imagesMap.values())
image.dispose();
imagesMap.clear();
});
}
public Image get(ImageDescriptor descriptor) {
return imagesMap.computeIfAbsent(descriptor, ImageDescriptor::createImage);
}
}
/**
* The default focus manager. It knows all widgets which can have the focus and listens for
* focusGained events, on which it stores the index of the current focus holder. When the dialog
* is restarted, <code>restoreFocus()</code> sets the focus to the last control which had it.
*
* Focus manager also makes sure that proper preview is displayed for currently focused
* preference.
*
* The standard Preference objects are managed by this focus manager if they are created using
* the respective factory methods. Other SWT widgets can be added in subclasses when they are
* created.
*/
protected final class FocusManager implements FocusListener {
private HashMap<Control, PreferenceTreeNode<?>> fControl2node= new HashMap<>();
private final Map<Control, Integer> fItemMap= new HashMap<>();
private final List<Control> fItemList= new ArrayList<>();
private int fIndex= 0;
private PreferenceTreeNode<?> fCurrentlyFocused;
public void add(PreferenceTreeNode<?> node) {
Control control= node.getControl();
if (node instanceof Section) {
// workaround: can't add focus listener directly to ExpadableComposite
control= ((Section) node).getToggle();
}
fControl2node.put(control, node);
add(control);
}
private void add(Control control) {
control.addFocusListener(this);
fItemList.add(fIndex, control);
fItemMap.put(control, fIndex++);
}
@Override
public void focusGained(FocusEvent e) {
PreferenceTreeNode<?> focusNode= fControl2node.get(e.getSource());
if (focusNode != null && focusNode != fCurrentlyFocused) {
highlightCurrent(false);
fCurrentlyFocused= focusNode;
highlightCurrent(true);
updatePreviewCode();
}
fDialogSettings.put(fKeyLastFocusIndex, fItemMap.get(e.widget));
}
private void highlightCurrent(boolean focus) {
if (fCurrentlyFocused instanceof Preference) {
Preference<?> pref= (Preference<?>) fCurrentlyFocused;
if (pref.fHighlight != null) {
pref.fHighlight.setFocus(focus);
} else {
changeFont(pref.getControl(), focus);
}
} else if (fCurrentlyFocused instanceof Section) {
changeFont(((Section) fCurrentlyFocused).getControl(), focus);
}
}
private void changeFont(Control control, boolean italic) {
Display.getCurrent().asyncExec(() -> {
FontData fd= control.getFont().getFontData()[0];
int style= italic ? (fd.getStyle() | SWT.ITALIC) : (fd.getStyle() & ~SWT.ITALIC);
FontData fontData= new FontData(fd.getName(), fd.getHeight(), style);
Font font= new Font(control.getDisplay(), fontData);
control.addDisposeListener(e -> font.dispose());
control.setFont(font);
if (control instanceof Composite)
((Composite) control).layout();
});
}
@Override
public void focusLost(FocusEvent e) {
doValidate();
}
public void restoreFocus() {
try {
int index= fDialogSettings.getInt(fKeyLastFocusIndex);
if (index >= 0 && index < fItemList.size()) {
fItemList.get(index).forceFocus();
}
} catch (NumberFormatException ex) {
// this is the first time
updatePreviewCode();
}
}
}
protected static final int GRID_COLUMNS= 4;
/* The keys to retrieve the preferred area from the dialog settings */
private static final String DS_KEY_PREFERRED_WIDTH= "modify_dialog.preferred_width"; //$NON-NLS-1$
private static final String DS_KEY_PREFERRED_HEIGHT= "modify_dialog.preferred_height"; //$NON-NLS-1$
private static final String DS_KEY_PREFERRED_X= "modify_dialog.preferred_x"; //$NON-NLS-1$
private static final String DS_KEY_PREFERRED_Y= "modify_dialog.preferred_y"; //$NON-NLS-1$
private static final String DS_KEY_SASH_FORM_LEFT_WIDTH= "modify_dialog.sash_form_left_width"; //$NON-NLS-1$
private static final String DS_KEY_SASH_FORM_RIGHT_WIDTH= "modify_dialog.sash_form_rigth_width"; //$NON-NLS-1$
private static final String DS_KEY_PREFERENCE_TREE_EXPANSION= ".preference_tree_expansion"; //$NON-NLS-1$
private static final String DS_KEY_PREFERENCE_SCROLL_POSITION= ".preference_scroll_position"; //$NON-NLS-1$
private static final String DS_KEY_LAST_FOCUS_INDEX= ".last_focus_index"; //$NON-NLS-1$
private static final int APPLAY_BUTTON_ID= IDialogConstants.CLIENT_ID;
private static final int SAVE_BUTTON_ID= IDialogConstants.CLIENT_ID + 1;
protected final Map<String, String> fWorkingValues;
protected final IDialogSettings fDialogSettings;
protected final boolean fNewProfile;
protected final String fKeyPreferredWidth;
protected final String fKeyPreferredHight;
private final String fKeyPreferredX;
private final String fKeyPreferredY;
private final String fKeySashFormLeftWidth;
private final String fKeySashFormRightWidth;
protected final String fKeyPreferenceTreeExpansion;
protected final String fKeyPreferenceScrollPosition;
protected final String fKeyLastFocusIndex;
private final String fLastSaveLoadPathKey;
private final ProfileStore fProfileStore;
private Profile fProfile;
private final ProfileManager fProfileManager;
private Button fApplyButton;
private Button fSaveButton;
private StringDialogField fProfileNameField;
private SashForm fSashForm;
protected JavaPreview fPreview;
protected ProfilePreferenceTree fTree;
protected final Images fImages;
protected final FocusManager fFocusManager= new FocusManager();
private String fPreviewSources= ""; //$NON-NLS-1$
private int fCurrentPreviewType;
public ModifyDialog(Shell parentShell, Profile profile, ProfileManager profileManager, ProfileStore profileStore, boolean newProfile, String dialogPreferencesKey, String lastSavePathKey) {
super(parentShell);
fProfileStore= profileStore;
fLastSaveLoadPathKey= lastSavePathKey;
fKeyPreferredWidth= JavaUI.ID_PLUGIN + dialogPreferencesKey + DS_KEY_PREFERRED_WIDTH;
fKeyPreferredHight= JavaUI.ID_PLUGIN + dialogPreferencesKey + DS_KEY_PREFERRED_HEIGHT;
fKeyPreferredX= JavaUI.ID_PLUGIN + dialogPreferencesKey + DS_KEY_PREFERRED_X;
fKeyPreferredY= JavaUI.ID_PLUGIN + dialogPreferencesKey + DS_KEY_PREFERRED_Y;
fKeySashFormLeftWidth= JavaUI.ID_PLUGIN + dialogPreferencesKey + DS_KEY_SASH_FORM_LEFT_WIDTH;
fKeySashFormRightWidth= JavaUI.ID_PLUGIN + dialogPreferencesKey + DS_KEY_SASH_FORM_RIGHT_WIDTH;
fKeyPreferenceTreeExpansion= JavaUI.ID_PLUGIN + dialogPreferencesKey + DS_KEY_PREFERENCE_TREE_EXPANSION;
fKeyPreferenceScrollPosition= JavaUI.ID_PLUGIN + dialogPreferencesKey + DS_KEY_PREFERENCE_SCROLL_POSITION;
fKeyLastFocusIndex= JavaUI.ID_PLUGIN + dialogPreferencesKey + DS_KEY_LAST_FOCUS_INDEX;
fProfileManager= profileManager;
fNewProfile= newProfile;
fProfile= profile;
setTitle(Messages.format(FormatterMessages.ModifyDialog_dialog_title, profile.getName()));
fWorkingValues= new HashMap<>(fProfile.getSettings());
setStatusLineAboveButtons(false);
fDialogSettings= JavaPlugin.getDefault().getDialogSettings();
fImages= new Images(parentShell);
}
/*
* @see org.eclipse.jface.dialogs.Dialog#isResizable()
* @since 3.4
*/
@Override
protected boolean isResizable() {
return true;
}
@Override
protected Control createDialogArea(Composite parent) {
final Composite composite= (Composite) super.createDialogArea(parent);
createNameArea(composite);
createMainArea(composite);
doValidate();
PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, getHelpContextId());
fFocusManager.restoreFocus();
return composite;
}
protected void createMainArea(Composite parent) {
fSashForm= new SashForm(parent, SWT.HORIZONTAL);
fSashForm.setFont(parent.getFont());
fSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
createPreferenceTree(fSashForm);
fTree.unifySectionTitlesHeights(null);
fTree.restoreExpansionState();
createPreviewPane(fSashForm);
try {
fSashForm.setWeights(new int[] { fDialogSettings.getInt(fKeySashFormLeftWidth), fDialogSettings.getInt(fKeySashFormRightWidth) });
} catch (NumberFormatException e) {
Control[] children= fSashForm.getChildren();
int treeWidth= children[0].computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
int previewWidth= children[1].computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
fSashForm.setWeights(new int[] { treeWidth, previewWidth });
}
}
protected void createPreferenceTree(Composite parent) {
Composite mainComp= new Composite(parent, SWT.NONE);
mainComp.setFont(parent.getFont());
mainComp.setLayout(new GridLayout(2, false));
fTree= new ProfilePreferenceTree(mainComp);
GridLayout layout= new GridLayout();
layout.verticalSpacing= 0;
layout.marginLeft= layout.marginWidth - 1;
layout.marginWidth= 1;
fTree.getScrolledPageContent().getBody().setLayout(layout);
fTree.setConcatAncestorLabels(true);
fTree.setExpectMultiWordValueMatch(true);
// restoring scroll position must wait until layout is complete
Display.getCurrent().asyncExec(fTree::restoreScrollPosition);
}
private void createNameArea(final Composite parent) {
Composite nameComposite= new Composite(parent, SWT.NONE);
nameComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
nameComposite.setLayout(new GridLayout(3, false));
nameComposite.setFont(parent.getFont());
fProfileNameField= new StringDialogField();
fProfileNameField.setLabelText(FormatterMessages.ModifyDialog_ProfileName_Label);
fProfileNameField.setText(fProfile.getName());
fProfileNameField.getLabelControl(nameComposite).setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
fProfileNameField.getTextControl(nameComposite).setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
fProfileNameField.setDialogFieldListener(field -> doValidate());
fSaveButton= createButton(nameComposite, SAVE_BUTTON_ID, FormatterMessages.ModifyDialog_Export_Button, false);
}
/**
* Returns the context ID for the Help system
*
* @return the string used as ID for the Help context
* @since 3.5
*/
protected abstract String getHelpContextId();
@Override
public void updateStatus(IStatus status) {
if (status == null) {
doValidate();
} else {
super.updateStatus(status);
}
}
@Override
protected Point getInitialLocation(Point initialSize) {
try {
return new Point(fDialogSettings.getInt(fKeyPreferredX), fDialogSettings.getInt(fKeyPreferredY));
} catch (NumberFormatException ex) {
return super.getInitialLocation(initialSize);
}
}
@Override
public boolean close() {
final Rectangle shell= getShell().getBounds();
fDialogSettings.put(fKeyPreferredWidth, shell.width);
fDialogSettings.put(fKeyPreferredHight, shell.height);
fDialogSettings.put(fKeyPreferredX, shell.x);
fDialogSettings.put(fKeyPreferredY, shell.y);
if (fSashForm != null) {
Control[] children= fSashForm.getChildren();
fDialogSettings.put(fKeySashFormLeftWidth, children[0].getSize().x);
fDialogSettings.put(fKeySashFormRightWidth, children[1].getSize().x);
}
if (fTree != null)
fTree.saveState();
return super.close();
}
@Override
protected void okPressed() {
applyPressed();
super.okPressed();
}
@Override
protected void buttonPressed(int buttonId) {
if (buttonId == APPLAY_BUTTON_ID) {
applyPressed();
setTitle(Messages.format(FormatterMessages.ModifyDialog_dialog_title, fProfile.getName()));
} else if (buttonId == SAVE_BUTTON_ID) {
saveButtonPressed();
} else {
super.buttonPressed(buttonId);
}
}
private void applyPressed() {
if (!fProfile.getName().equals(fProfileNameField.getText())) {
fProfile= fProfile.rename(fProfileNameField.getText(), fProfileManager);
}
fProfile.setSettings(new HashMap<>(fWorkingValues));
fProfileManager.setSelected(fProfile);
doValidate();
}
private void saveButtonPressed() {
Profile selected= new CustomProfile(fProfileNameField.getText(), new HashMap<>(fWorkingValues), fProfile.getVersion(), fProfileManager.getProfileVersioner().getProfileKind());
final FileDialog dialog= new FileDialog(getShell(), SWT.SAVE | SWT.SHEET);
dialog.setText(FormatterMessages.CodingStyleConfigurationBlock_save_profile_dialog_title);
dialog.setFilterExtensions(new String [] {"*.xml"}); //$NON-NLS-1$
final String lastPath= JavaPlugin.getDefault().getDialogSettings().get(fLastSaveLoadPathKey + ".savepath"); //$NON-NLS-1$
if (lastPath != null) {
dialog.setFilterPath(lastPath);
}
final String path= dialog.open();
if (path == null)
return;
JavaPlugin.getDefault().getDialogSettings().put(fLastSaveLoadPathKey + ".savepath", dialog.getFilterPath()); //$NON-NLS-1$
final File file= new File(path);
if (file.exists() && !MessageDialog.openQuestion(getShell(), FormatterMessages.CodingStyleConfigurationBlock_save_profile_overwrite_title, Messages.format(FormatterMessages.CodingStyleConfigurationBlock_save_profile_overwrite_message, BasicElementLabels.getPathLabel(file)))) {
return;
}
String encoding= ProfileStore.ENCODING;
final IContentType type= Platform.getContentTypeManager().getContentType("org.eclipse.core.runtime.xml"); //$NON-NLS-1$
if (type != null)
encoding= type.getDefaultCharset();
final Collection<Profile> profiles= new ArrayList<>();
profiles.add(selected);
try {
fProfileStore.writeProfilesToFile(profiles, file, encoding);
} catch (CoreException e) {
final String title= FormatterMessages.CodingStyleConfigurationBlock_save_profile_error_title;
final String message= FormatterMessages.CodingStyleConfigurationBlock_save_profile_error_message;
ExceptionHandler.handle(e, getShell(), title, message);
}
}
@Override
protected void createButtonsForButtonBar(Composite parent) {
fApplyButton= createButton(parent, APPLAY_BUTTON_ID, FormatterMessages.ModifyDialog_apply_button, false);
fApplyButton.setEnabled(false);
GridLayout layout= (GridLayout) parent.getLayout();
layout.numColumns++;
layout.makeColumnsEqualWidth= false;
Label label= new Label(parent, SWT.NONE);
GridData data= new GridData();
data.widthHint= layout.horizontalSpacing;
label.setLayoutData(data);
super.createButtonsForButtonBar(parent);
}
@Override
public void valuesModified() {
doValidate();
if (fPreview != null)
fPreview.update();
}
@Override
protected void updateButtonsEnableState(IStatus status) {
super.updateButtonsEnableState(status);
if (fApplyButton != null && !fApplyButton.isDisposed()) {
fApplyButton.setEnabled(hasChanges() && !status.matches(IStatus.ERROR));
}
if (fSaveButton != null && !fSaveButton.isDisposed()) {
fSaveButton.setEnabled(!validateProfileName().matches(IStatus.ERROR));
}
}
private void doValidate() {
String name= fProfileNameField.getText().trim();
if (name.equals(fProfile.getName()) && fProfile.hasEqualSettings(fWorkingValues, fWorkingValues.keySet())) {
updateStatus(StatusInfo.OK_STATUS);
return;
}
IStatus status= validateProfileName();
if (status.matches(IStatus.ERROR)) {
updateStatus(status);
return;
}
if (!name.equals(fProfile.getName()) && fProfileManager.containsName(name)) {
updateStatus(new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, FormatterMessages.ModifyDialog_Duplicate_Status));
return;
}
if (fProfile.isBuiltInProfile() || fProfile.isSharedProfile()) {
updateStatus(new Status(IStatus.INFO, JavaUI.ID_PLUGIN, FormatterMessages.ModifyDialog_NewCreated_Status));
return;
}
updateStatus(StatusInfo.OK_STATUS);
}
private IStatus validateProfileName() {
final String name= fProfileNameField.getText().trim();
if (fProfile.isBuiltInProfile()) {
if (fProfile.getName().equals(name)) {
return new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, FormatterMessages.ModifyDialog_BuiltIn_Status);
}
}
if (fProfile.isSharedProfile()) {
if (fProfile.getName().equals(name)) {
return new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, FormatterMessages.ModifyDialog_Shared_Status);
}
}
if (name.length() == 0) {
return new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, FormatterMessages.ModifyDialog_EmptyName_Status);
}
return StatusInfo.OK_STATUS;
}
private boolean hasChanges() {
if (!fProfileNameField.getText().trim().equals(fProfile.getName()))
return true;
Iterator<Entry<String, String>> iter= fProfile.getSettings().entrySet().iterator();
for (;iter.hasNext();) {
Entry<String, String> curr= iter.next();
if (!fWorkingValues.get(curr.getKey()).equals(curr.getValue())) {
return true;
}
}
return false;
}
protected Composite createPreviewPane(Composite parent) {
final Composite previewPane= new Composite(parent, SWT.NONE);
createGridLayout(previewPane, 1, true);
previewPane.setFont(parent.getFont());
createLabel(1, previewPane, FormatterMessages.ModifyDialogTabPage_preview_label_text, 0);
fPreview= new JavaPreview(fWorkingValues, previewPane);
fPreview.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
return previewPane;
}
protected void updatePreviewCode() {
if (fPreview != null) {
String previewCode= getPreviewCode();
if (previewCode == null || previewCode.trim().isEmpty())
previewCode= "// " + FormatterMessages.ModifyDialog_previewMissing_comment; //$NON-NLS-1$
fPreview.setPreviewText(previewCode, fCurrentPreviewType);
}
}
private String getPreviewCode() {
PreferenceTreeNode<?> currentNode= fFocusManager.fCurrentlyFocused;
if (currentNode == null)
return null;
// try this node
String previewCode= doGetPreviewCode(currentNode);
if (previewCode != null)
return previewCode;
// combine previews from children, if any
StringBuilder sb= new StringBuilder();
List<PreferenceTreeNode<?>> currentLevel= new ArrayList<>(currentNode.getChildren());
List<PreferenceTreeNode<?>> nextLevel= new ArrayList<>();
while (!currentLevel.isEmpty()) {
boolean onlyModuleInfos= true;
for (PreferenceTreeNode<?> node : currentLevel) {
previewCode= doGetPreviewCode(node);
onlyModuleInfos= onlyModuleInfos && fCurrentPreviewType == CodeFormatter.K_MODULE_INFO;
if (previewCode != null && sb.indexOf(previewCode) == -1)
sb.append("\n\n").append(previewCode); //$NON-NLS-1$
nextLevel.addAll(node.getChildren());
}
if (sb.length() > 0) {
fCurrentPreviewType= onlyModuleInfos ? CodeFormatter.K_MODULE_INFO : CodeFormatter.K_UNKNOWN;
return sb.toString();
}
currentLevel= nextLevel;
nextLevel= new ArrayList<>();
}
// try its ancestors
PreferenceTreeNode<?> node= currentNode.getParent();
while (node != null) {
previewCode= doGetPreviewCode(node);
if (previewCode != null)
return previewCode;
node= node.getParent();
}
return null;
}
private String doGetPreviewCode(PreferenceTreeNode<?> node) {
if (!(node instanceof Section) && !(node instanceof Preference))
return null;
String key= node instanceof Section ? ((Section) node).getKey() : ((Preference<?>) node).getKey();
final String START_PREFIX= "//--PREVIEW--START--"; //$NON-NLS-1$
final String END_PREFIX= "//--PREVIEW--END--"; //$NON-NLS-1$
int startIndex= fPreviewSources.indexOf(START_PREFIX + key);
if (startIndex < 0)
return null;
fCurrentPreviewType= CodeFormatter.K_UNKNOWN;
int nextPos= startIndex + START_PREFIX.length() + key.length();
switch (fPreviewSources.charAt(nextPos)) {
case '\n':
break;
case ':':
if (fPreviewSources.indexOf("COMPILATION_UNIT\n", nextPos) == nextPos + 1) { //$NON-NLS-1$
fCurrentPreviewType= CodeFormatter.K_COMPILATION_UNIT;
} else if (fPreviewSources.indexOf("EXPRESSION\n", nextPos) == nextPos + 1) { //$NON-NLS-1$
fCurrentPreviewType= CodeFormatter.K_EXPRESSION;
} else if (fPreviewSources.indexOf("CLASS_BODY_DECLARATIONS\n", nextPos) == nextPos + 1) { //$NON-NLS-1$
fCurrentPreviewType= CodeFormatter.K_CLASS_BODY_DECLARATIONS;
} else if (fPreviewSources.indexOf("STATEMENTS\n", nextPos) == nextPos + 1) { //$NON-NLS-1$
fCurrentPreviewType= CodeFormatter.K_STATEMENTS;
} else if (fPreviewSources.indexOf("MODULE_INFO\n", nextPos) == nextPos + 1) { //$NON-NLS-1$
fCurrentPreviewType= CodeFormatter.K_MODULE_INFO;
}
break;
default:
return null;
}
int endIndex= fPreviewSources.indexOf(END_PREFIX + key + '\n');
String previewCode= fPreviewSources.substring(startIndex, endIndex);
previewCode= previewCode.replaceAll(START_PREFIX + ".*\\R", ""); //$NON-NLS-1$ //$NON-NLS-2$
previewCode= previewCode.replaceAll(END_PREFIX + ".*\\R", ""); //$NON-NLS-1$ //$NON-NLS-2$
return previewCode;
}
protected void loadPreviews(String previewFile) {
String resource= "/preview/" + previewFile; //$NON-NLS-1$
try (Scanner s= new Scanner(getClass().getResourceAsStream(resource), "UTF-8")) { //$NON-NLS-1$
fPreviewSources= s.useDelimiter("\\A").next(); //$NON-NLS-1$
}
}
protected static Composite createGridComposite(Composite parent, int numColumns) {
final Composite grid= new Composite(parent, SWT.NONE);
createGridLayout(parent, numColumns, true);
grid.setFont(parent.getFont());
return grid;
}
/*
* Create a GridLayout with the default margin and spacing settings, as
* well as the specified number of columns.
*/
protected static void createGridLayout(Composite composite, int numColumns, boolean margins) {
final GridLayout layout= new GridLayout(numColumns, false);
PixelConverter pixelConverter= new PixelConverter(composite);
layout.verticalSpacing= pixelConverter.convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
layout.horizontalSpacing= pixelConverter.convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
if (margins) {
layout.marginHeight= pixelConverter.convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
layout.marginWidth= pixelConverter.convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
} else {
layout.marginHeight= 0;
layout.marginWidth= 0;
}
composite.setLayout(layout);
}
/*
* Convenience method to create a GridData.
*/
protected static GridData createGridData(int numColumns, int style, int widthHint, int indent) {
final GridData gd= new GridData(style);
gd.horizontalSpan= numColumns;
gd.widthHint= widthHint;
gd.horizontalIndent= indent * LayoutUtil.getIndent();
return gd;
}
/*
* Convenience method to create a label
*/
protected static Label createLabel(int numColumns, Composite parent, String text, int indent) {
final Label label= new Label(parent, SWT.WRAP);
label.setFont(parent.getFont());
label.setText(text);
GridData gd= new GridData(GridData.BEGINNING, GridData.CENTER, true, false, numColumns, 1);
gd.horizontalIndent= indent * LayoutUtil.getIndent();
label.setLayoutData(gd);
return label;
}
/*
* Helper to be used with Preference.addDependant(), dependency checker that accepts specific values.
*/
protected static Predicate<String> valueAcceptor(String... values) {
final List<String> valuesList= Arrays.asList(values);
return valuesList::contains;
}
}