Java tutorial
/******************************************************************************* * Copyright (c) 2006, 2011 Institute of Theoretical and Computational Physics (ITPCP), * Graz University of Technology. * 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: * Georg Huhs, Winfried Kernbichler, David Camhy (ITPCP) - * initial API and implementation * Last changed: * 2008-01-22 *******************************************************************************/ package com.matlab.eclipse.meditor; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Preferences; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.DialogPage; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.preference.PreferencePage; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; 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.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.editors.text.ITextEditorHelpContextIds; import com.matlab.eclipse.meditor.dialogs.FileDialogListener; public abstract class AbstractMatlabenginePrefsPage extends PreferencePage implements IWorkbenchPreferencePage { protected OverlayPreferenceStore fOverlayStore; private Map<Button, String> fCheckBoxes = new HashMap<Button, String>(); private SelectionListener fCheckBoxListener = new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { } public void widgetSelected(SelectionEvent e) { Button button = (Button) e.widget; fOverlayStore.setValue((String) fCheckBoxes.get(button), button.getSelection()); } }; private Map<Text, String> fTextFields = new HashMap<Text, String>(); private ModifyListener fTextFieldListener = new ModifyListener() { public void modifyText(ModifyEvent e) { Text text = (Text) e.widget; fOverlayStore.setValue((String) fTextFields.get(text), text.getText()); } }; private Map<Combo, String> fComboFields = new HashMap<Combo, String>(); private ModifyListener fComboFieldListener = new ModifyListener() { public void modifyText(ModifyEvent e) { Combo combo = (Combo) e.widget; fOverlayStore.setValue((String) fComboFields.get(combo), combo.getText()); } }; private ArrayList<Text> fNumberFields = new ArrayList<Text>(); private ModifyListener fNumberFieldListener = new ModifyListener() { public void modifyText(ModifyEvent e) { numberFieldChanged((Text) e.widget); } }; /** * Tells whether the fields are initialized. * @since 3.0 */ private boolean fFieldsInitialized = false; /** * List of master/slave listeners when there's a dependency. * * @see #createDependency(Button, String, Control) */ private ArrayList<SelectionListener> fMasterSlaveListeners = new ArrayList<SelectionListener>(); public AbstractMatlabenginePrefsPage(String description) { setDescription(description); setPreferenceStore(Activator.getDefault().getPreferenceStore()); fOverlayStore = createOverlayStore(); } protected abstract Control createAppearancePage(Composite parent); protected abstract OverlayPreferenceStore createOverlayStore(); /* * @see IWorkbenchPreferencePage#init() */ public void init(IWorkbench workbench) { } /* * @see PreferencePage#createControl(Composite) */ public void createControl(Composite parent) { super.createControl(parent); PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), ITextEditorHelpContextIds.TEXT_EDITOR_PREFERENCE_PAGE); } /* * @see PreferencePage#createContents(Composite) */ protected Control createContents(Composite parent) { fOverlayStore.load(); fOverlayStore.start(); Control control = createAppearancePage(parent); initialize(); Dialog.applyDialogFont(control); return control; } protected void initialize() { initializeFields(); } private void initializeFields() { Iterator<Button> checkBoxIterator = fCheckBoxes.keySet().iterator(); while (checkBoxIterator.hasNext()) { Button checkBox = checkBoxIterator.next(); String key = fCheckBoxes.get(checkBox); checkBox.setSelection(fOverlayStore.getBoolean(key)); } Iterator<Text> textFieldIterator = fTextFields.keySet().iterator(); while (textFieldIterator.hasNext()) { Text textField = textFieldIterator.next(); String key = fTextFields.get(textField); textField.setText(fOverlayStore.getString(key)); } Iterator<Combo> comboBoxIterator = fComboFields.keySet().iterator(); while (comboBoxIterator.hasNext()) { Combo comboBox = comboBoxIterator.next(); String key = fComboFields.get(comboBox); comboBox.setText(fOverlayStore.getString(key)); } fFieldsInitialized = true; updateStatus(validatePositiveNumber("0")); // Update slaves Iterator<SelectionListener> selectionListenerIterator = fMasterSlaveListeners.iterator(); while (selectionListenerIterator.hasNext()) { SelectionListener listener = selectionListenerIterator.next(); listener.widgetSelected(null); } } /* * @see PreferencePage#performOk() */ public boolean performOk() { fOverlayStore.propagate(); Activator.getDefault().savePluginPreferences(); return true; } /* * @see PreferencePage#performDefaults() */ protected void performDefaults() { fOverlayStore.loadDefaults(); initializeFields(); super.performDefaults(); } /* * @see DialogPage#dispose() */ public void dispose() { if (fOverlayStore != null) { fOverlayStore.stop(); fOverlayStore = null; } super.dispose(); } /** * Creates a checkbox and adds it to the parent. * @param parent Composite to add the checkbox into. * @param label Label for this textfield. * @param key Key of the corresponding preference. * @param indentation Horizontal indentation of the checkbox. * @return The created checkbox */ protected Button addCheckBox(Composite parent, String label, String key, int indentation) { Button checkBox = new Button(parent, SWT.CHECK); checkBox.setText(label); GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); gd.horizontalIndent = indentation; gd.horizontalSpan = 2; checkBox.setLayoutData(gd); checkBox.addSelectionListener(fCheckBoxListener); fCheckBoxes.put(checkBox, key); return checkBox; } /** * Creates a labeled textfield and adds it to the parent. (Label on the left site.) * @param parent Composite to add the textfield into. * @param label Label for this textfield. * @param key Key of the corresponding preference. * @param textLimit Maximum number of characters in the textfield. * @param indentation Horizontal indentation of the label. * @param isNumber Specifies if this is a textfield for numbers. * @return the created textfield. */ protected Control addTextField(Composite parent, String label, String key, int textLimit, int indentation, boolean isNumber) { Label labelControl = new Label(parent, SWT.NONE); labelControl.setText(label); GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); gd.horizontalIndent = indentation; labelControl.setLayoutData(gd); Text textControl = new Text(parent, SWT.BORDER | SWT.SINGLE); if (textLimit == Text.LIMIT) { gd = new GridData(SWT.FILL, SWT.CENTER, true, false); } else { gd = new GridData(SWT.LEFT, SWT.CENTER, false, false); gd.widthHint = convertWidthInCharsToPixels(textLimit + 1); } textControl.setLayoutData(gd); textControl.setTextLimit(textLimit); fTextFields.put(textControl, key); if (isNumber) { fNumberFields.add(textControl); textControl.addModifyListener(fNumberFieldListener); } else { textControl.addModifyListener(fTextFieldListener); } return textControl; } /** * Creates and adds three Elements to the parent: A label, a textfield * and a "Browse..." button that opens a FileDialog where a file or directory, * whose path will be written into the textfield, can be specified. * The last two elements are encapsulated by a Composite. * @param parent Composite to add the elements into. * @param label Label of this line. * @param key Key of the corresponding preference. * @param indentation Horizontal indent of the label. * @param isDir specifies if a directory is searched. * @param filterExtensions See org.eclipse.swt.widgets.FileDialog.setFilterExtensions(...) * @param filterNames See org.eclipse.swt.widgets.FileDialog.setFilterNames(...) * @return the created textfield. */ protected Control addFileLine(Composite parent, String label, String key, int indentation, boolean isDir, String[] filterExtensions, String[] filterNames) { Label labelControl = new Label(parent, SWT.NONE); labelControl.setText(label); GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); gd.horizontalIndent = indentation; labelControl.setLayoutData(gd); Composite rightContainer = parent; if (((GridLayout) parent.getLayout()).numColumns == 2) { rightContainer = new Composite(parent, SWT.NONE); GridLayout layoutRContainer = new GridLayout(); layoutRContainer.numColumns = 2; rightContainer.setLayout(layoutRContainer); gd = new GridData(SWT.FILL, SWT.CENTER, true, false); rightContainer.setLayoutData(gd); } return addFileLine(rightContainer, key, 0, isDir, filterExtensions, filterNames); } /** * Creates and adds two Elements to the parent: A textfield and a "Browse..." button * that opens a FileDialog where a file or directory, whose path will be written * into the textfield, can be specified. * @param parent Composite to add the elements into. * @param key Key of the corresponding preference. * @param indentation Horizontal indent of the textfield. * @param isDir specifies if a directory is searched. * @param filterExtensions See org.eclipse.swt.widgets.FileDialog.setFilterExtensions(...) * @param filterNames See org.eclipse.swt.widgets.FileDialog.setFilterNames(...) * @return the created textfield. */ protected Control addFileLine(Composite parent, String key, int indentation, boolean isDir, String[] filterExtensions, String[] filterNames) { Text textControl = new Text(parent, SWT.BORDER | SWT.SINGLE); GridData gd = new GridData(SWT.FILL, SWT.CENTER, true, false); gd.horizontalIndent = indentation; textControl.setLayoutData(gd); textControl.setTextLimit(Text.LIMIT); fTextFields.put(textControl, key); textControl.addModifyListener(fTextFieldListener); Button browseButton = new Button(parent, SWT.PUSH); browseButton.setText("Browse..."); FileDialogListener listener = new FileDialogListener(textControl, isDir, getShell()); if (filterExtensions != null && filterNames != null) { listener.setFilter(filterExtensions, filterNames); } browseButton.addSelectionListener(listener); return textControl; } /** * Creates a Combo box and adds it to the parent Composite. * @param parent Composite to add this Combo box into * @param label The Combo's label. * @param key Key of the corresponding preference * @param entries Array of Strings which represent the entries of the Combo. * @param indentation Horizontal indent of the Combo * @return the created Combo. */ protected Control addComboField(Composite parent, String label, String key, String[] entries, int indentation) { Label labelControl = new Label(parent, SWT.NONE); labelControl.setText(label); GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); gd.horizontalIndent = indentation; labelControl.setLayoutData(gd); Combo comboControl = new Combo(parent, SWT.DROP_DOWN); for (String entry : entries) { comboControl.add(entry); } gd = new GridData(SWT.LEFT, SWT.CENTER, false, false); comboControl.setLayoutData(gd); fComboFields.put(comboControl, key); comboControl.addModifyListener(fComboFieldListener); return comboControl; } /** * Adds a group that can be used to add elements with addXYZ(...) into. * @param parent Composite to add this Group into * @param title Title of this Group, may be null if no title should be displayed * @return generated Group */ protected Group addGroup(Composite parent, String title, int verticalIndent, int hSpan, int numInternalCols) { Group group = new Group(parent, SWT.NONE); if (title != null) { group.setText(title); } GridData gd = new GridData(SWT.FILL, SWT.CENTER, true, false); gd.horizontalSpan = hSpan; gd.verticalIndent = verticalIndent; group.setLayoutData(gd); GridLayout layoutGroup = new GridLayout(); layoutGroup.numColumns = numInternalCols; group.setLayout(layoutGroup); return group; } /** * Creates an exclusive radio button field and adds it to the parent Composite. * All components of this field are enclosed by a Container. * @param parent Composite to add the whole field into. * @param title A title for this field which occures as a Label, may be null if no title * should be used * @param buttonInfo String matrix which contains the information for all radio buttons. * One button is described by one line which contains in it's first row the button's text * and in the second row the corresponding preference key * @param verticalIndent Vertical indent for the container * @param hSpan Horizontal span for the container * @param radioButtonIndent Indent of the buttons with respect to the field's container. * @return the Container that keeps all generated elements. */ protected Composite addRadioButtonField(Composite parent, String title, String[][] buttonInfo, int verticalIndent, int hSpan, int radioButtonIndent) { Composite mainContainer = new Composite(parent, SWT.NULL); GridLayout mainLayout = new GridLayout(); mainLayout.numColumns = 1; mainContainer.setLayout(mainLayout); if (title != null) { Label label = new Label(mainContainer, SWT.NONE); label.setText(title); } Composite buttonContainer = new Composite(mainContainer, SWT.NULL); RowLayout buttonLayout = new RowLayout(); buttonLayout.type = SWT.VERTICAL; buttonContainer.setLayout(buttonLayout); for (int i = 0; i < buttonInfo.length; i++) { String buttonText = buttonInfo[i][0]; String buttonKey = buttonInfo[i][1]; Button button = new Button(buttonContainer, SWT.RADIO); button.setText(buttonText); fCheckBoxes.put(button, buttonKey); button.addSelectionListener(fCheckBoxListener); } GridData mainGd = new GridData(SWT.FILL, SWT.CENTER, true, false); mainGd.horizontalSpan = hSpan; mainGd.verticalIndent = verticalIndent; mainContainer.setLayoutData(mainGd); GridData buttonGd = new GridData(SWT.FILL, SWT.CENTER, true, false); buttonGd.horizontalIndent = radioButtonIndent; buttonContainer.setLayoutData(buttonGd); return mainContainer; } /** * Creates a a Label and adds it to the parent Composite. * @param parent Composite to add the label into. * @param text The Label's text. * @param verticalIndent Vertical indent of the Label. * @param hSpan Horizontal span of the Label. * @return the created Label */ protected Label addLabel(Composite parent, String text, int verticalIndent, int hSpan) { Label label = new Label(parent, SWT.NONE); label.setText(text); GridData gd = new GridData(SWT.FILL, SWT.CENTER, true, false); gd.horizontalSpan = hSpan; gd.verticalIndent = verticalIndent; label.setLayoutData(gd); return label; } /** * Creates a dependency between a master and a slave element. * This means that the slave will be disabled until the master gets selected. * If inverse behavior is chosen, the slave is enabled if the master is not selected. * @param master A Button that controls the slave. * @param masterKey The master's corresponding property key. * @param slave A Control that will be enabled/disabled depending on the master's state. * @param isInverse Defines the character of the relationship. * if false: slave is enabled if master is selected * if true: slave is enabled if master is not selected */ protected void createDependency(final Button master, String masterKey, final Control slave, final boolean isInverse) { boolean masterState = fOverlayStore.getBoolean(masterKey); slave.setEnabled(masterState ^ isInverse); SelectionListener listener = new SelectionListener() { public void widgetSelected(SelectionEvent e) { slave.setEnabled(master.getSelection() ^ isInverse); } public void widgetDefaultSelected(SelectionEvent e) { } }; master.addSelectionListener(listener); fMasterSlaveListeners.add(listener); } /** * Creates a dependency between several masters and a slave element. * This means that the slave will be disabled until all masters' * selection states equal a defined set of states. Inverse behavior is selectable * for the slave and for each single master, thus any relationship can be defined. * If all invert-flags are false, the slave will be enabled only if all masters * are selected. If only the global behavior is inverted, the slave will be * disabled only if all masters are selected. If all masters' behavior is inverted, * the slave will be enabled only if all masters are not selected. And so on. * @param masters Array of MasterInfo. Each entry contains a button, * its preference key and a flag for inverted use of this master. * @param slave A Control that will be enabled/disabled depending of the masters' states. * @param invertAll Global invert flag. */ protected void createMultiDependency(final MasterInfo[] masters, final Control slave, final boolean invertAll) { boolean mastersState = true; for (MasterInfo master : masters) { mastersState = mastersState && fOverlayStore.getBoolean(master.key) ^ master.inverse; } slave.setEnabled(mastersState ^ invertAll); SelectionListener listener = new SelectionListener() { public void widgetSelected(SelectionEvent e) { boolean mastersSelected = true; for (MasterInfo master : masters) { mastersSelected = mastersSelected && master.button.getSelection() ^ master.inverse; } slave.setEnabled(mastersSelected ^ invertAll); } public void widgetDefaultSelected(SelectionEvent e) { } }; for (MasterInfo master : masters) { master.button.addSelectionListener(listener); } fMasterSlaveListeners.add(listener); } /** * Indents the specified Control by a certain amount. * @param control Control to indent. */ protected static void indent(Control control) { GridData gridData; gridData = (GridData) control.getLayoutData(); if (gridData == null) { gridData = new GridData(); } gridData.horizontalIndent = 20; control.setLayoutData(gridData); } private void numberFieldChanged(Text textControl) { String number = textControl.getText(); IStatus status = validatePositiveNumber(number); if (!status.matches(IStatus.ERROR)) fOverlayStore.setValue((String) fTextFields.get(textControl), number); updateStatus(status); } private IStatus validatePositiveNumber(String number) { StatusInfo status = new StatusInfo(); if (number.length() == 0) { status.setError("empty_input??"); } else { try { int value = Integer.parseInt(number); if (value < 0) status.setError("invalid_input??"); } catch (NumberFormatException e) { status.setError("invalid_input??"); } } return status; } private void updateStatus(IStatus status) { if (!fFieldsInitialized) return; if (!status.matches(IStatus.ERROR)) { for (int i = 0; i < fNumberFields.size(); i++) { Text text = (Text) fNumberFields.get(i); IStatus s = validatePositiveNumber(text.getText()); status = s.getSeverity() > status.getSeverity() ? s : status; } } setValid(!status.matches(IStatus.ERROR)); applyToStatusLine(this, status); } /** * Applies the status to the status line of a dialog page. * @param page the dialog page * @param status the status */ public void applyToStatusLine(DialogPage page, IStatus status) { String message = status.getMessage(); switch (status.getSeverity()) { case IStatus.OK: page.setMessage(message, IMessageProvider.NONE); page.setErrorMessage(null); break; case IStatus.WARNING: page.setMessage(message, IMessageProvider.WARNING); page.setErrorMessage(null); break; case IStatus.INFO: page.setMessage(message, IMessageProvider.INFORMATION); page.setErrorMessage(null); break; default: if (message.length() == 0) { message = null; } page.setMessage(null); page.setErrorMessage(message); break; } } /** * Sets default preference values */ protected static void initializeDefaultPreferences(Preferences prefs) { } static public Preferences getPreferences() { return Activator.getDefault().getPluginPreferences(); } protected class MasterInfo { public Button button; public String key; public boolean inverse; public MasterInfo(Button button, String key, boolean inverse) { this.button = button; this.key = key; this.inverse = inverse; } } }