/*******************************************************************************
* Copyright (c) 2012 Pivotal Software, Inc.
* 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:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springsource.ide.eclipse.commons.frameworks.ui.internal.wizard;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.dialogs.SearchPattern;
import org.springsource.ide.eclipse.commons.frameworks.core.internal.commands.ICommandParameterDescriptor;
import org.springsource.ide.eclipse.commons.frameworks.core.internal.commands.IFrameworkCommandDescriptor;
import org.springsource.ide.eclipse.commons.frameworks.ui.internal.swt.ViewerSearchPart;
/**
* Page that displays commands to configure and execute. Commands may be added
* even after the page opens and the table is initially populated. Only one
* command may be executed at a given time. Some commands with no parameters or
* with just optional parameters can be completed from this page without going
* to the next page.
* @author Nieraj Singh
* @author Christian Dupuis
*/
public class GenericWizardCommandListPage extends AbstractGenericWizardPage {
private TableViewer commandTableViewer;
private Text descriptionControl;
private IFrameworkCommandDescriptor selectedCommandDescriptor;
private static final String DESCRIPTION_INDENT = " ";
private Set<IFrameworkCommandDescriptor> commandDescriptors;
public static final String PAGE_MESSAGE = "Select a command from the list of commands below. Only one command can be configured for execution at a time";
// Use the 'S' letter as the keyboard shortcut
public static final String SELECT_COMMAND_LABEL = "&Select a command to configure and execute:";
public GenericWizardCommandListPage(String pageName) {
super(pageName);
commandDescriptors = new HashSet<IFrameworkCommandDescriptor>();
setTitle("Select Command");
}
protected int getViewerConfiguration() {
return SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION | SWT.H_SCROLL
| SWT.V_SCROLL;
}
protected Composite createPageArea(Composite parent) {
Composite listSection = new Composite(parent, SWT.NONE);
GridLayoutFactory.fillDefaults().numColumns(1).applyTo(listSection);
GridDataFactory.fillDefaults().grab(true, true).applyTo(listSection);
createCommandTableArea(listSection);
createDescriptionArea(listSection);
refreshDescriptionArea();
return listSection;
}
public synchronized boolean containsCommandDescriptor(
IFrameworkCommandDescriptor command) {
if (commandDescriptors != null) {
return commandDescriptors.contains(command);
}
return false;
}
/**
* Returns the command descriptor if it is successfully added. Returns null
* if the command was not added (either it is null or it already exists)
*
* @param command
* descriptor to add
* @return returns command if successfully added, null otherwise
*/
public synchronized IFrameworkCommandDescriptor addCommandDescriptor(
IFrameworkCommandDescriptor command) {
if (command == null) {
return null;
}
if (!commandDescriptors.contains(command)) {
commandDescriptors.add(command);
// If the table is already created and not yet disposed
// it means it is open so also add it to the table
if (commandTableViewer != null
&& !commandTableViewer.getTable().isDisposed()) {
commandTableViewer.add(command);
}
return command;
}
return null;
}
/**
* Set a command in the table viewer, if the command is listed in the table.
* True is returned if the command is found in the current table input.
* <p/>
* If it is not already listed in the viewer, no selection will occur, and
* false will be returned
* <p/>
* If the command is null, all selections in the table are cleared, true is
* returned
* <p/>
*
* @param command
* to select. True if selected or selection cleared, false if no
* selection
*/
public synchronized boolean selectCommandInViewer(
IFrameworkCommandDescriptor command) {
return selectCommand(command);
}
/**
* Set a command in the table viewer, if the command is listed in the table.
* True is returned if the command is found in the current table input.
* <p/>
* If it is not already listed in the viewer, no selection will occur, and
* false will be returned
* <p/>
* If the command is null, all selections in the table are cleared, true is
* returned
* <p/>
* Note that the table only supports one INSTANCE of a given command, and
* they selection is based on the command name, not any other value or
* property of the command
*
* @param command
* to select. True if selected or selection cleared, false if no
* selection
*/
private boolean selectCommand(IFrameworkCommandDescriptor command) {
// dont do anything if not contained in the list
if (command != null && !commandDescriptors.contains(command)) {
return false;
}
selectedCommandDescriptor = command;
if (commandTableViewer != null) {
// If null clear everything
if (command == null) {
commandTableViewer.getTable().deselectAll();
// Clear the selections in the wizard as well
GenericWizardCommandListPage.this.selectionChanged(null);
} else {
commandTableViewer.setSelection(
new StructuredSelection(command), true);
}
return true;
}
return false;
}
protected ViewerSearchPart createSearchControl(Composite parent) {
ViewerSearchPart part = new ViewerSearchPart(parent) {
protected boolean matches(Object element, Object parentElement,
String pattern) {
if (element instanceof IFrameworkCommandDescriptor) {
String commandName = ((IFrameworkCommandDescriptor) element)
.getName();
String lowerCasePattern = pattern.toLowerCase();
if (commandName != null) {
commandName = commandName.toLowerCase();
SearchPattern filter = new SearchPattern();
filter.setPattern(lowerCasePattern);
return filter.matches(commandName);
}
}
return false;
}
};
return part;
}
protected synchronized void createCommandTableArea(Composite parent) {
Composite tableArea = new Composite(parent, SWT.NONE);
GridLayoutFactory.fillDefaults().numColumns(1).applyTo(tableArea);
GridDataFactory.fillDefaults().grab(true, true).applyTo(tableArea);
Label tableLabel = new Label(tableArea, SWT.LEFT);
GridDataFactory.fillDefaults().grab(true, false).applyTo(tableLabel);
tableLabel.setText(SELECT_COMMAND_LABEL);
final ViewerSearchPart part = createSearchControl(tableArea);
Table table = new Table(tableArea, getViewerConfiguration());
commandTableViewer = new TableViewer(table);
part.connectViewer(commandTableViewer);
setTableColumnAndLayout(commandTableViewer);
commandTableViewer.setContentProvider(new IStructuredContentProvider() {
public Object[] getElements(Object inputElement) {
if (inputElement instanceof Collection<?>) {
Collection<?> topLevel = (Collection<?>) inputElement;
return topLevel.toArray();
}
return null;
}
public void inputChanged(Viewer viewer, Object oldInput,
Object newInput) {
// Nothing
}
public void dispose() {
// Nothing
}
});
commandTableViewer.setLabelProvider(new ColumnLabelProvider() {
public String getText(Object element) {
if (element instanceof IFrameworkCommandDescriptor) {
return ((IFrameworkCommandDescriptor) element).getName();
}
return null;
}
});
commandTableViewer
.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
IStructuredSelection selection = (IStructuredSelection) commandTableViewer
.getSelection();
if (selection != null) {
Object selObj = selection.getFirstElement();
if (selObj instanceof IFrameworkCommandDescriptor) {
GenericWizardCommandListPage.this
.selectionChanged((IFrameworkCommandDescriptor) selObj);
}
}
checkPageComplete();
}
});
commandTableViewer.getTable().addKeyListener(new KeyListener() {
public void keyReleased(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
// STS-1250: Add support to navigate to search control when up
// arrow pressed
if (e.keyCode == SWT.ARROW_UP) {
Object topSelection = commandTableViewer.getElementAt(0);
IFrameworkCommandDescriptor currentSelection = getSelectedCommandDescriptor();
if (topSelection == currentSelection) {
part.getTextControl().setFocus();
}
}
}
});
commandTableViewer.setInput(commandDescriptors);
Text searchText = part.getTextControl();
searchText.addKeyListener(new KeyListener() {
public void keyReleased(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
// STS-1250: Add support to navigate to table viewer when
// down arrow is pressed
if (e.keyCode == SWT.ARROW_DOWN) {
commandTableViewer.getTable().setFocus();
Object selection = commandTableViewer.getElementAt(0);
if (selection instanceof IFrameworkCommandDescriptor) {
selectCommandInViewer((IFrameworkCommandDescriptor) selection);
}
}
}
});
// STS 1251: Add filter listener to automatically select commands based
// on the pattern that is typed
searchText.addKeyListener(new KeyListener() {
public void keyReleased(KeyEvent event) {
// STS 1251: If one element is left in the table after search is
// performed, select it to enable
// the appropriate wizard buttons
int itemCount = commandTableViewer.getTable().getItemCount();
if (itemCount == 1) {
IFrameworkCommandDescriptor remainingDescriptor = (IFrameworkCommandDescriptor) commandTableViewer
.getElementAt(0);
if (remainingDescriptor != null) {
GenericWizardCommandListPage.this
.selectCommandInViewer(remainingDescriptor);
}
} else {
// else clear the selection nothing is found by search or
// multiple matches are found
GenericWizardCommandListPage.this
.selectCommandInViewer(null);
}
}
public void keyPressed(KeyEvent event) {
}
});
searchText.setFocus();
checkPageComplete();
}
/**
* Uses can override to modify the behaviour of the wizard when a selection
* changes. Note that can intercept a selection change and prevent the
* default behaviour of assigning the selection to the wizard. This allows
* users to perform additional checks before delegating to the super
* selectChange implementation
*
* @param selectedDescriptor
*/
protected void selectionChanged(
IFrameworkCommandDescriptor selectedDescriptor) {
selectedCommandDescriptor = selectedDescriptor;
getGenericCommandWizard().setCommandInstance(selectedCommandDescriptor);
refreshDescriptionArea();
checkPageComplete();
}
protected int getMinTableHeight() {
return 200;
}
protected int getMinCommandColumnWeight() {
return 400;
}
/**
* Sets the main column in the viewer that lists all the commands and adds a
* sorter.
*
* @param tableviewer
*/
protected void setTableColumnAndLayout(final TableViewer tableviewer) {
tableviewer.setSorter(new ViewerSorter() {
public int compare(Viewer viewer, Object command1, Object command2) {
if (viewer instanceof TableViewer) {
Table table = ((TableViewer) viewer).getTable();
if (command1 instanceof IFrameworkCommandDescriptor
&& command2 instanceof IFrameworkCommandDescriptor) {
int sortDirection = table.getSortDirection();
String commandName1 = ((IFrameworkCommandDescriptor) command1)
.getName();
String commandName2 = ((IFrameworkCommandDescriptor) command2)
.getName();
return sortDirection == SWT.UP ? commandName1
.compareToIgnoreCase(commandName2)
: commandName2
.compareToIgnoreCase(commandName1);
}
}
return super.compare(viewer, command1, command2);
}
});
final Table table = tableviewer.getTable();
GridDataFactory.fillDefaults().grab(true, true)
.hint(SWT.DEFAULT, getMinTableHeight()).applyTo(table);
TableLayout tableLayout = new TableLayout();
tableLayout.addColumnData(new ColumnWeightData(
getMinCommandColumnWeight(), true));
TableColumn commandColumn = new TableColumn(table, SWT.NONE);
table.setLayout(tableLayout);
// the command column cannot be null, as the table viewer must have at
// least one column
commandColumn.setText("Commands");
commandColumn.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
int dir = table.getSortDirection();
dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
table.setSortDirection(dir);
tableviewer.refresh();
}
});
table.setSortColumn(commandColumn);
table.setSortDirection(SWT.UP);
// Hide the header for now. If sorting direction change is required,
// simply set to true:
table.setHeaderVisible(false);
table.layout(true);
tableviewer.refresh();
}
protected void checkPageComplete() {
IFrameworkCommandDescriptor command = getSelectedCommandDescriptor();
if (command == null || hasRequiredParameters(command)) {
setPageComplete(false);
} else {
setPageComplete(true);
}
}
public boolean isPageComplete() {
// Dont block termination of wizard if
// it is not the current page
if (!isCurrentPage()) {
return true;
}
return super.isPageComplete();
}
public boolean canFlipToNextPage() {
IFrameworkCommandDescriptor command = getSelectedCommandDescriptor();
if (command == null) {
setMessage(PAGE_MESSAGE);
return false;
} else if (hasRequiredParameters(command)) {
setMessage("This command has required parameters with values to set. Click next to set the values.");
return true;
} else if (command.getParameters().length == 0) {
setMessage("This command has no parameters to set. Click finish to execute the command.");
return false;
} else {
setMessage("Click next to set optional parameter values, or click finish to execute the command.");
return true;
}
}
protected boolean hasRequiredParameters(
IFrameworkCommandDescriptor commandDescriptor) {
ICommandParameterDescriptor[] parameters = commandDescriptor
.getParameters();
for (ICommandParameterDescriptor parameter : parameters) {
if (parameter.isMandatory()) {
return true;
}
}
return false;
}
protected void createDescriptionArea(Composite parent) {
Composite descriptionArea = new Composite(parent, SWT.NONE);
GridLayoutFactory.fillDefaults().numColumns(1).applyTo(descriptionArea);
GridDataFactory.fillDefaults().grab(true, true)
.applyTo(descriptionArea);
Label descriptionLabel = new Label(descriptionArea, SWT.LEFT);
GridDataFactory.fillDefaults().grab(true, false)
.applyTo(descriptionLabel);
descriptionLabel.setText("Command Description:");
descriptionControl = new Text(descriptionArea, SWT.V_SCROLL
| SWT.BORDER | SWT.READ_ONLY | SWT.WRAP);
GridDataFactory.fillDefaults().grab(true, true)
.hint(getDescriptionWidthHint(), getDescriptionHeightHint())
.applyTo(descriptionControl);
}
protected int getDescriptionWidthHint() {
return SWT.DEFAULT;
}
protected int getDescriptionHeightHint() {
return 180;
}
public synchronized IFrameworkCommandDescriptor getSelectedCommandDescriptor() {
return selectedCommandDescriptor;
}
protected void refreshDescriptionArea() {
if (descriptionControl != null && !descriptionControl.isDisposed()) {
DescriptionSetter buffer = new DescriptionSetter(
getSelectedCommandDescriptor(), descriptionControl);
buffer.setDescription();
}
}
protected static class DescriptionSetter {
private int lines = 0;
private IFrameworkCommandDescriptor command;
private Text descriptionText;
private StringBuffer buffer;
public DescriptionSetter(IFrameworkCommandDescriptor command,
Text descriptionText) {
this.command = command;
this.descriptionText = descriptionText;
}
public String setDescription() {
buffer = new StringBuffer();
if (command == null) {
appendLine("n/a");
} else {
String description = command.getDescription();
if (description != null && description.length() > 0) {
appendLine(description);
} else {
appendLine("Description not available");
}
ICommandParameterDescriptor[] parameters = command
.getParameters();
appendLine("");
appendLine("Parameters:");
if (parameters == null || parameters.length == 0) {
appendLine(DESCRIPTION_INDENT
+ "This command has no parameters");
} else {
for (ICommandParameterDescriptor param : parameters) {
StringBuffer params = new StringBuffer();
params.append(DESCRIPTION_INDENT);
params.append(param.getName());
params.append(" -- ");
params.append(param.isMandatory() ? " is required"
: " optional");
appendLine(params.toString());
}
}
}
String text = buffer.toString();
descriptionText.setText(text);
return text;
}
public int getLines() {
return lines;
}
protected void appendLine(String text) {
buffer.append(text);
buffer.append(System.getProperty("line.separator"));
lines++;
}
}
}