Mercurial > hg > thermostat-tools-eclipse
changeset 108:fde496a78a98
Add UI for editing options
line wrap: on
line diff
--- a/TODO.md Fri Feb 21 15:59:02 2014 -0500 +++ b/TODO.md Thu Feb 27 15:40:52 2014 -0500 @@ -8,7 +8,7 @@ - Fix up stale states -- Implement support for options +- Implement UI for grouping options - Modify the bundle loading dialog to be smarter. Offer auto-completion and automatically identify bundle names from all open projects and dependencies,
--- a/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/Messages.java Fri Feb 21 15:59:02 2014 -0500 +++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/Messages.java Thu Feb 27 15:40:52 2014 -0500 @@ -16,6 +16,7 @@ public static String CommandEditPage_cliCheckTitle; public static String CommandEditPage_commandDetailsSectionTitle; public static String CommandEditPage_descriptionLabel; + public static String CommandEditPage_editOptions; public static String CommandEditPage_environmentsTitle; public static String CommandEditPage_nameLabel; public static String CommandEditPage_removeButton; @@ -43,6 +44,15 @@ public static String NewNameDialog_message; public static String NewNameDialog_nameLabel; public static String NewNameDialog_title; + public static String OptionsWizard_createOptionsPageAddOption; + public static String OptionsWizard_createOptionsPageArgumentLabel; + public static String OptionsWizard_createOptionsPageDescription; + public static String OptionsWizard_createOptionsPageDescriptionLabel; + public static String OptionsWizard_createOptionsPageLongNameLabel; + public static String OptionsWizard_createOptionsPageRemoveOption; + public static String OptionsWizard_createOptionsPageRequiredLabel; + public static String OptionsWizard_createOptionsPageShortNameLabel; + public static String OptionsWizard_createOptionsPageTitle; public static String OverviewPage_bundlesAndExtensionsOverviewForm; public static String OverviewPage_commandsAndExtensionSectionTitle; public static String OverviewPage_formTitle;
--- a/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/editor/CommandEditPage.java Fri Feb 21 15:59:02 2014 -0500 +++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/editor/CommandEditPage.java Thu Feb 27 15:40:52 2014 -0500 @@ -17,9 +17,11 @@ import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.window.Window; +import org.eclipse.jface.wizard.WizardDialog; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; 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; @@ -43,6 +45,7 @@ import com.redhat.thermostat.tools.eclipse.plugin.model.Bundle; import com.redhat.thermostat.tools.eclipse.plugin.model.Command; import com.redhat.thermostat.tools.eclipse.plugin.model.Environments; +import com.redhat.thermostat.tools.eclipse.plugin.model.Options; import com.redhat.thermostat.tools.eclipse.plugin.model.Plugin; /** @@ -210,13 +213,23 @@ argumentsAndOptions.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); argumentsAndOptions.setText(Messages.CommandEditPage_argumentsAndOptionsSectionTitle); - Composite contents = toolkit.createComposite(argumentsAndOptions); + final Composite contents = toolkit.createComposite(argumentsAndOptions); argumentsAndOptions.setClient(contents); contents.setLayout(new GridLayout(2, false)); toolkit.createLabel(contents, Messages.CommandEditPage_argumentsLabel); commandArguments = toolkit.createText(contents, "<arguments>"); //$NON-NLS-1$ commandArguments.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false)); + + Button editOptions = toolkit.createButton(contents, Messages.CommandEditPage_editOptions, SWT.None); + editOptions.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + WizardDialog optionsEditDialog = new WizardDialog(contents.getShell(), new OptionsWizard(commandModel)); + // wizard will update command model as a side-effect if it succeeds + optionsEditDialog.open(); + } + }); } private void createBundlesSection(final Composite sectionContents) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/editor/OptionsWizard.java Thu Feb 27 15:40:52 2014 -0500 @@ -0,0 +1,330 @@ +package com.redhat.thermostat.tools.eclipse.plugin.editor; + +import java.util.Objects; + +import org.eclipse.core.databinding.DataBindingContext; +import org.eclipse.core.databinding.beans.BeanProperties; +import org.eclipse.core.databinding.observable.ChangeEvent; +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.list.IListChangeListener; +import org.eclipse.core.databinding.observable.list.ListChangeEvent; +import org.eclipse.core.databinding.observable.list.WritableList; +import org.eclipse.core.databinding.observable.map.IObservableMap; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.jface.databinding.swt.WidgetProperties; +import org.eclipse.jface.databinding.viewers.IViewerObservableValue; +import org.eclipse.jface.databinding.viewers.ObservableListContentProvider; +import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider; +import org.eclipse.jface.databinding.viewers.ViewerProperties; +import org.eclipse.jface.databinding.wizard.WizardPageSupport; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ListViewer; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.List; +import org.eclipse.swt.widgets.Text; + +import com.redhat.thermostat.tools.eclipse.plugin.Messages; +import com.redhat.thermostat.tools.eclipse.plugin.model.Command; +import com.redhat.thermostat.tools.eclipse.plugin.model.Option; +import com.redhat.thermostat.tools.eclipse.plugin.model.Options; + +/** + * A {@link Wizard} for editing the command line options for a command. + * <p> + * The wizrad allows adding/editing and removing options. + */ +public class OptionsWizard extends Wizard { + + private Command commandModel; + private Options myOptions; + + public OptionsWizard(Command commandModel) { + setNeedsProgressMonitor(true); + + this.commandModel = commandModel; + + if (commandModel.getOptions() != null) { + myOptions = new Options(commandModel.getOptions()); + } else { + myOptions = new Options(); + } + } + + @Override + public void addPages() { + addPage(new CreateOptionsPage()); + } + + @Override + public boolean performFinish() { + commandModel.setOptions(myOptions); + + return true; + } + + class CreateOptionsPage extends WizardPage { + + private DataBindingContext dataBindingContext; + private ListViewer optionsListViewer; + private Text longName; + private Text shortName; + private Text description; + private Text argument; + private Button required; + private WritableList allOptions; + private Button add; + private Button remove; + + public CreateOptionsPage() { + super("Create Options"); //$NON-NLS-1$ + setTitle(Messages.OptionsWizard_createOptionsPageTitle); + setDescription(Messages.OptionsWizard_createOptionsPageDescription); + } + + @Override + public void createControl(Composite parent) { + Composite container = new Composite(parent, SWT.NONE); + container.setLayout(new GridLayout(2, false)); + + createMasterArea(container); + createDetailsArea(container); + + initDataBindings(); + + createWidgetEnableDisableListeners(); + + setControl(container); + } + + private void createMasterArea(Composite parent) { + Composite masterList = new Composite(parent, SWT.NONE); + masterList.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, false, true)); + + masterList.setLayout(new GridLayout(2, false)); + + List optionsList = new List(masterList, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); + GridData listLayoutData = new GridData(SWT.BEGINNING, SWT.FILL, true, true); + listLayoutData.widthHint = 150; + optionsList.setLayoutData(listLayoutData); + + optionsListViewer = new ListViewer(optionsList); + + Composite buttonsComposite = new Composite(masterList, SWT.NONE); + buttonsComposite.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, false, true)); + + buttonsComposite.setLayout(new GridLayout(1, false)); + + add = new Button(buttonsComposite, SWT.PUSH); + add.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + add.setText(Messages.OptionsWizard_createOptionsPageAddOption); + add.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + Option option = new Option(); + allOptions.add(option); + optionsListViewer.setSelection(new StructuredSelection(option), true); + } + }); + + remove = new Button(buttonsComposite, SWT.PUSH); + remove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + remove.setText(Messages.OptionsWizard_createOptionsPageRemoveOption); + remove.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + IStructuredSelection selection = (IStructuredSelection) optionsListViewer.getSelection(); + Option selectedOption = (Option) Objects.requireNonNull(selection.getFirstElement()); + allOptions.remove(selectedOption); + } + }); + } + + private void createDetailsArea(Composite parent) { + Composite details = new Composite(parent, SWT.NONE); + details.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + details.setLayout(new GridLayout(2, false)); + + Label longNameLabel = new Label(details, SWT.NONE); + longNameLabel.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false)); + longNameLabel.setText(Messages.OptionsWizard_createOptionsPageLongNameLabel); + longName = new Text(details, SWT.NONE); + longName.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false)); + + Label shortNameLabel = new Label(details, SWT.NONE); + shortNameLabel.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false)); + shortNameLabel.setText(Messages.OptionsWizard_createOptionsPageShortNameLabel); + shortName = new Text(details, SWT.NONE); + shortName.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false)); + + Label descriptionLabel = new Label(details, SWT.NONE); + descriptionLabel.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false)); + descriptionLabel.setText(Messages.OptionsWizard_createOptionsPageDescriptionLabel); + description = new Text(details, SWT.NONE); + description.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false)); + + Label argumentLabel = new Label(details, SWT.NONE); + argumentLabel.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false)); + argumentLabel.setText(Messages.OptionsWizard_createOptionsPageArgumentLabel); + argument = new Text(details, SWT.NONE); + argument.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false)); + + Label requiredLabel = new Label(details, SWT.NONE); + requiredLabel.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false)); + requiredLabel.setText(Messages.OptionsWizard_createOptionsPageRequiredLabel); + required = new Button(details, SWT.CHECK); + required.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false)); + } + + private void initDataBindings() { + dataBindingContext = new DataBindingContext(); + + ObservableListContentProvider contentProvider = new ObservableListContentProvider(); + optionsListViewer.setContentProvider(contentProvider); + IObservableSet knownElements = contentProvider.getKnownElements(); + IObservableMap longNames = BeanProperties.value(Option.class, "longName", String.class).observeDetail(knownElements); //$NON-NLS-1$ + IObservableMap shortNames = BeanProperties.value(Option.class, "shortName", String.class).observeDetail(knownElements); //$NON-NLS-1$ + + IObservableMap[] attributeMaps = new IObservableMap[] { longNames, shortNames }; + optionsListViewer.setLabelProvider(new OptionLabelProvider(attributeMaps)); + + allOptions = new WritableList(myOptions.getOptions(), Option.class); + optionsListViewer.setInput(allOptions); + + IViewerObservableValue selectedOption = ViewerProperties.singleSelection().observe(optionsListViewer); + + IObservableValue longNameWidgetValue = WidgetProperties.text(SWT.Modify).observe(longName); + IObservableValue longNameModelValue = BeanProperties.value(Option.class, "longName").observeDetail(selectedOption); //$NON-NLS-1$ + dataBindingContext.bindValue(longNameWidgetValue, longNameModelValue); + + IObservableValue shortNameWidgetValue = WidgetProperties.text(SWT.Modify).observe(shortName); + IObservableValue shortNameModelValue = BeanProperties.value(Option.class, "shortName").observeDetail(selectedOption); //$NON-NLS-1$ + dataBindingContext.bindValue(shortNameWidgetValue, shortNameModelValue); + + IObservableValue descriptionWidgetValue = WidgetProperties.text(SWT.Modify).observe(description); + IObservableValue descriptionModelValue = BeanProperties.value(Option.class, "description").observeDetail(selectedOption); //$NON-NLS-1$ + dataBindingContext.bindValue(descriptionWidgetValue, descriptionModelValue); + + IObservableValue argumentWidgetValue = WidgetProperties.text(SWT.Modify).observe(argument); + IObservableValue argumentModelValue = BeanProperties.value(Option.class, "argument").observeDetail(selectedOption); //$NON-NLS-1$ + dataBindingContext.bindValue(argumentWidgetValue, argumentModelValue); + + IObservableValue requiredWidgetValue = WidgetProperties.selection().observe(required); + IObservableValue requiredModelValue = BeanProperties.value(Option.class, "required").observeDetail(selectedOption); //$NON-NLS-1$ + dataBindingContext.bindValue(requiredWidgetValue, requiredModelValue); + + // FIXME enable this after identifying how to deal with null? master data + // WizardPageSupport.create(this, dataBindingContext); + } + + @Override + public void dispose() { + super.dispose(); + + dataBindingContext.dispose(); + } + + private void createWidgetEnableDisableListeners() { + IListChangeListener enableDisableRemoveButton = new IListChangeListener() { + @Override + public void handleListChange(ListChangeEvent event) { + int size = event.getObservableList().size(); + if (size == 0) { + remove.setEnabled(false); + } else { + remove.setEnabled(true); + } + } + }; + allOptions.addListChangeListener(enableDisableRemoveButton); + enableDisableRemoveButton.handleListChange(new ListChangeEvent(allOptions, null)); + + IListChangeListener enableDisableDataWidgets = new IListChangeListener() { + @Override + public void handleListChange(ListChangeEvent event) { + int size = event.getObservableList().size(); + boolean toEnable = true; + if (size == 0) { + toEnable = false; + } else { + toEnable = true; + } + longName.setEnabled(toEnable); + shortName.setEnabled(toEnable); + description.setEnabled(toEnable); + argument.setEnabled(toEnable); + required.setEnabled(toEnable); + + } + }; + allOptions.addListChangeListener(enableDisableDataWidgets); + enableDisableDataWidgets.handleListChange(new ListChangeEvent(allOptions, null)); + + } + } + + // TODO implement a page to organize options into (exclusive) option groups + class GroupOptionsPage extends WizardPage { + + public GroupOptionsPage() { + super("Group Options Page"); //$NON-NLS-1$ + } + + @Override + public void createControl(Composite parent) { + // TODO Auto-generated method stub + } + + } + + private static class OptionLabelProvider extends ObservableMapLabelProvider{ + public OptionLabelProvider(IObservableMap[] attributeMaps) { + super(attributeMaps); + } + + @Override + public String getText(Object element) { + Objects.requireNonNull(element); + Option option = (Option) element; + + String longOption = null; + String longName = option.getLongName(); + if (longName != null && !longName.isEmpty()) { + longOption = "--" + longName; //$NON-NLS-N$ + } + + String shortOption = null; + String shortName = option.getShortName(); + if (shortName != null && !shortName.isEmpty()) { + shortOption = "-" + shortName; //$NON-NLS-N$ + } + + String result = null; + + // FIXME does this need to be internationalized? + if (longOption != null && shortOption != null) { + result = longOption + ", " + shortOption; + } else if (longOption != null) { + result = longOption; + } else if (shortOption != null) { + result = shortOption; + } else { + result = "[unknown]"; + } + + return result; + } + } +}
--- a/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/messages.properties Fri Feb 21 15:59:02 2014 -0500 +++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/messages.properties Thu Feb 27 15:40:52 2014 -0500 @@ -8,6 +8,7 @@ CommandEditPage_cliCheckTitle=CLI CommandEditPage_commandDetailsSectionTitle=Command details CommandEditPage_descriptionLabel=Description: +CommandEditPage_editOptions=Edit Options CommandEditPage_environmentsTitle=Environments CommandEditPage_nameLabel=Name: CommandEditPage_removeButton=Remove @@ -35,6 +36,15 @@ NewNameDialog_message=This is the command users will use to trigger your plugin NewNameDialog_nameLabel=Name: NewNameDialog_title=Enter the name for the new command +OptionsWizard_createOptionsPageAddOption=Add +OptionsWizard_createOptionsPageArgumentLabel=Argument: +OptionsWizard_createOptionsPageDescription=Create and edit options +OptionsWizard_createOptionsPageDescriptionLabel=Description: +OptionsWizard_createOptionsPageLongNameLabel=Long Name: +OptionsWizard_createOptionsPageRemoveOption=Remove +OptionsWizard_createOptionsPageRequiredLabel=Required: +OptionsWizard_createOptionsPageShortNameLabel=Short Name: +OptionsWizard_createOptionsPageTitle=Create Options OverviewPage_bundlesAndExtensionsOverviewForm = <form><p>Plugins provide new commands or extend existing ones.</p><li><a href=\"{0}\">Commands</a>: declares new commands provided by this plugin</li><li><a href=\"{1}\">Extensions</a>: declares which commands provided by other plugins or thermostat itself should be extended</li></form> OverviewPage_commandsAndExtensionSectionTitle=Commands and Extensions OverviewPage_formTitle=Overview: {0}
--- a/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/Group.java Fri Feb 21 15:59:02 2014 -0500 +++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/Group.java Thu Feb 27 15:40:52 2014 -0500 @@ -8,6 +8,18 @@ private boolean required; private List<Option> options = new ArrayList<>(); + public Group() { + // empty default constructor + } + + public Group(Group other) { + this.required = other.required; + this.options = new ArrayList<>(other.options.size()); + for (Option opt : other.options) { + this.options.add(new Option(opt)); + } + } + public boolean getRequired() { return required; }
--- a/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/ModelObject.java Fri Feb 21 15:59:02 2014 -0500 +++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/ModelObject.java Thu Feb 27 15:40:52 2014 -0500 @@ -3,7 +3,7 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; -public class ModelObject { +public abstract class ModelObject { private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
--- a/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/Option.java Fri Feb 21 15:59:02 2014 -0500 +++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/Option.java Thu Feb 27 15:40:52 2014 -0500 @@ -12,7 +12,7 @@ "argument", "required", "description"}) -public class Option { +public class Option extends ModelObject { private String longName; private String shortName; @@ -20,13 +20,25 @@ private boolean required; private String description; + public Option() { + // empty no-arg constructor + } + + public Option(Option other) { + this.longName = other.longName; + this.shortName = other.shortName; + this.argument = other.argument; + this.required = other.required; + this.description = other.description; + } + @XmlElement(name="long") public String getLongName() { return longName; } - public void setLongName(String name) { - this.longName = name; + public void setLongName(String longName) { + firePropertyChange("longName", this.longName, this.longName = longName); } @XmlElement(name="short") @@ -35,7 +47,7 @@ } public void setShortName(String shortName) { - this.shortName = shortName; + firePropertyChange("shortName", this.shortName, this.shortName = shortName); } @XmlElement(name="argument") @@ -44,7 +56,7 @@ } public void setArgument(String argument) { - this.argument = argument; + firePropertyChange("argument", this.argument, this.argument = argument); } @XmlElement(name="required") @@ -53,7 +65,7 @@ } public void setRequired(boolean required) { - this.required = required; + firePropertyChange("required", this.required, this.required = required); } @XmlElement(name="description") @@ -62,7 +74,6 @@ } public void setDescription(String description) { - this.description = description; + firePropertyChange("description", this.description, this.description = description); } - }
--- a/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/Options.java Fri Feb 21 15:59:02 2014 -0500 +++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/Options.java Thu Feb 27 15:40:52 2014 -0500 @@ -10,6 +10,23 @@ private Group group; private List<Option> options = new ArrayList<>(); + public Options() { + // empty no-arg constructor + } + + public Options(Options other) { + if (other.group != null) { + this.group = new Group(other.group); + } + + if (other.options != null) { + this.options = new ArrayList<>(other.options.size()); + for (Option option : other.options) { + this.options.add(new Option(option)); + } + } + } + public Group getGroup() { return group; }