blob: 92ddef1b4d1a36e244a346f8e902ebd8cae1cb8f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2015 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
* Remy Chi Jian Suen <remy.suen@gmail.com> - bug 201661
* Simon Scholz <simon.scholz@vogella.com> - Bug 483425, 483429, 483435
*******************************************************************************/
package org.eclipse.ui.dialogs;
import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter;
import com.ibm.icu.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.IWizardPage;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.IWorkingSetManager;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.IWorkbenchHelpContextIds;
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.internal.dialogs.SimpleWorkingSetSelectionDialog;
/**
* Instances of this class provide a reusable composite with controls that allow
* the selection of working sets. This class is most useful in
* {@link IWizardPage} instances that wish to create resources and pre-install
* them into particular working sets.
*
* @since 3.4
*
*/
public class WorkingSetConfigurationBlock {
/**
* Filters the given working sets such that the following is true: for each
* IWorkingSet s in result: s.getId() is element of workingSetIds
*
* @param workingSets
* the array to filter
* @param workingSetIds
* the acceptable working set ids
* @return the filtered elements
*/
public static IWorkingSet[] filter(IWorkingSet[] workingSets,
String[] workingSetIds) {
// create a copy so we can sort the array without mucking it up for clients.
String[] workingSetIdsCopy = new String[workingSetIds.length];
System.arraycopy(workingSetIds, 0, workingSetIdsCopy, 0,
workingSetIds.length);
Arrays.sort(workingSetIdsCopy);
List<IWorkingSet> result = new ArrayList<>();
for (IWorkingSet workingSet : workingSets) {
if (Arrays.binarySearch(workingSetIdsCopy, workingSet.getId()) >= 0) {
result.add(workingSet);
}
}
return result.toArray(new IWorkingSet[result.size()]);
}
/**
* Empty working set array constant.
*/
private static final IWorkingSet[] EMPTY_WORKING_SET_ARRAY = new IWorkingSet[0];
private static final String WORKINGSET_SELECTION_HISTORY = "workingset_selection_history"; //$NON-NLS-1$
private static final int MAX_HISTORY_SIZE = 5;
private Label workingSetLabel;
private Combo workingSetCombo;
private Button selectButton;
private Button enableButton;
private Button newButton;
private IWorkingSet[] selectedWorkingSets;
private List<String> selectionHistory;
private final IDialogSettings dialogSettings;
private final String[] workingSetTypeIds;
private final String selectLabel;
private final String comboLabel;
private final String enableButtonLabel;
private final String newButtonLabel;
/**
* Create a new instance of this working set block using default labels.
*
* @param settings
* to store/load the selection history
* @param workingSetIds
* working set ids from which the user can choose
* @since 3.108
*/
public WorkingSetConfigurationBlock(IDialogSettings settings, String... workingSetIds) {
this(settings, null, null, null, null, workingSetIds);
}
/**
* Create a new instance of this working set block using default labels.
*
* <p>
* Note: Consider using the vararg version of this contructor.
* </p>
*
* @param workingSetIds working set ids from which the user can choose
* @param settings to store/load the selection history
*/
public WorkingSetConfigurationBlock(String[] workingSetIds, IDialogSettings settings) {
this(settings, workingSetIds);
}
/**
* Create a new instance of this working set block using custom labels.
*
* <p>
* Note: Consider using the vararg version of this contructor.
* </p>
*
* @param workingSetIds working set ids from which the user can choose
* @param settings to store/load the selection history
* @param enableButtonLabel the label to use for the checkable enablement
* button. May be <code>null</code> to use the default
* value.
* @param comboLabel the label to use for the recent working set combo.
* May be <code>null</code> to use the default value.
* @param selectLabel the label to use for the select button. May be
* <code>null</code> to use the default value.
*/
public WorkingSetConfigurationBlock(String[] workingSetIds, IDialogSettings settings, String enableButtonLabel, String comboLabel, String selectLabel) {
this(settings, enableButtonLabel, null, comboLabel, selectLabel, workingSetIds);
}
/**
* Create a new instance of this working set block using custom labels.
*
* @param settings
* to store/load the selection history
* @param enableButtonLabel
* the label to use for the checkable enablement button. May be
* <code>null</code> to use the default value.
* @param newButtonLabel
* the label to use for the new button. May be <code>null</code>
* to use the default value.
* @param comboLabel
* the label to use for the recent working set combo. May be
* <code>null</code> to use the default value.
* @param selectLabel
* the label to use for the select button. May be
* <code>null</code> to use the default value.
* @param workingSetIds
* working set ids from which the user can choose
* @since 3.108
*/
public WorkingSetConfigurationBlock(IDialogSettings settings, String enableButtonLabel, String newButtonLabel,
String comboLabel, String selectLabel, String... workingSetIds) {
Assert.isNotNull(workingSetIds);
Assert.isNotNull(settings);
workingSetTypeIds = workingSetIds;
Arrays.sort(workingSetIds); // we'll be performing some searches with these later - presort them
selectedWorkingSets = EMPTY_WORKING_SET_ARRAY;
dialogSettings = settings;
selectionHistory = loadSelectionHistory(settings, workingSetIds);
this.enableButtonLabel = enableButtonLabel == null ? WorkbenchMessages.WorkingSetGroup_EnableWorkingSet_button
: enableButtonLabel;
this.newButtonLabel = newButtonLabel == null
? WorkbenchMessages.WorkingSetConfigurationBlock_NewWorkingSet_button : newButtonLabel;
this.comboLabel = comboLabel == null ? WorkbenchMessages.WorkingSetConfigurationBlock_WorkingSetText_name
: comboLabel;
this.selectLabel = selectLabel == null ? WorkbenchMessages.WorkingSetConfigurationBlock_SelectWorkingSet_button
: selectLabel;
}
/**
* Set the current selection in the workbench.
*
* @param selection
* the selection to present in the UI or <b>null</b>
* @deprecated use
* {@link #setWorkingSets(IWorkingSet[])} and {@link #findApplicableWorkingSets(IStructuredSelection)}
* instead.
*/
@Deprecated
public void setSelection(IStructuredSelection selection) {
selectedWorkingSets = findApplicableWorkingSets(selection);
if (workingSetCombo != null) {
updateSelectedWorkingSets();
}
}
/**
* Set the current selection of working sets. This array will be filtered to
* contain only working sets that are applicable to this instance.
*
* @param workingSets
* the working sets
*/
public void setWorkingSets(IWorkingSet... workingSets) {
selectedWorkingSets = filterWorkingSets(Arrays.asList(workingSets));
if (workingSetCombo != null) {
updateSelectedWorkingSets();
}
}
/**
* Retrieves a working set from the given <code>selection</code> or an
* empty array if no working set could be retrieved. This selection is
* filtered based on the criteria used to construct this instance.
*
* @param selection
* the selection to retrieve the working set from
* @return the selected working set or an empty array
*/
public IWorkingSet[] findApplicableWorkingSets(
IStructuredSelection selection) {
if (selection == null) {
return EMPTY_WORKING_SET_ARRAY;
}
return filterWorkingSets(selection.toList());
}
/**
* Prune a list of working sets such that they all match the criteria set
* out by this block.
*
* @param elements
* the elements to filter
* @return the filtered elements
*/
private IWorkingSet[] filterWorkingSets(Collection<?> elements) {
List<IWorkingSet> result = new ArrayList<>();
for (Object element : elements) {
if (element instanceof IWorkingSet
&& verifyWorkingSet((IWorkingSet) element)) {
result.add((IWorkingSet) element);
}
}
return result.toArray(new IWorkingSet[result.size()]);
}
/**
* Verifies that the given working set is suitable for selection in this
* block.
*
* @param workingSetCandidate
* the candidate to test
* @return whether it is suitable
*/
private boolean verifyWorkingSet(IWorkingSet workingSetCandidate) {
return !workingSetCandidate.isAggregateWorkingSet()
&& Arrays.binarySearch(workingSetTypeIds, workingSetCandidate
.getId()) >= 0 ;
}
/**
* Return the currently selected working sets. If the controls representing
* this block are disabled this array will be empty regardless of the
* currently selected values.
*
* @return the selected working sets
*/
public IWorkingSet[] getSelectedWorkingSets() {
if (enableButton.getSelection()) {
return selectedWorkingSets;
}
return EMPTY_WORKING_SET_ARRAY;
}
/**
* Add this block to the <code>parent</code>
*
* @param parent the parent to add the block to
*/
public void createContent(final Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
composite.setLayout(new GridLayout(3, false));
enableButton = new Button(composite, SWT.CHECK);
enableButton
.setText(enableButtonLabel);
GridData enableData = new GridData(SWT.FILL, SWT.CENTER, true, false);
enableData.horizontalSpan = 2;
enableButton.setLayoutData(enableData);
enableButton.setSelection(selectedWorkingSets.length > 0);
newButton = new Button(composite, SWT.PUSH);
newButton.setText(this.newButtonLabel);
setButtonLayoutData(newButton);
newButton.addSelectionListener(widgetSelectedAdapter(e -> createNewWorkingSet(newButton.getShell())));
workingSetLabel = new Label(composite, SWT.NONE);
workingSetLabel
.setText(comboLabel);
workingSetCombo = new Combo(composite, SWT.READ_ONLY | SWT.BORDER);
GridData textData = new GridData(SWT.FILL, SWT.CENTER, true, false);
textData.horizontalIndent = 0;
workingSetCombo.setLayoutData(textData);
selectButton = new Button(composite, SWT.PUSH);
selectButton
.setText(selectLabel);
setButtonLayoutData(selectButton);
selectButton.addSelectionListener(widgetSelectedAdapter(e -> {
SimpleWorkingSetSelectionDialog dialog = new SimpleWorkingSetSelectionDialog(parent.getShell(),
workingSetTypeIds, selectedWorkingSets, false);
dialog.setMessage(WorkbenchMessages.WorkingSetGroup_WorkingSetSelection_message);
if (dialog.open() == Window.OK) {
IWorkingSet[] result = dialog.getSelection();
if (result != null && result.length > 0) {
selectedWorkingSets = result;
PlatformUI.getWorkbench().getWorkingSetManager().addRecentWorkingSet(result[0]);
} else {
selectedWorkingSets = EMPTY_WORKING_SET_ARRAY;
}
updateWorkingSetSelection();
}
}));
enableButton.addSelectionListener(widgetSelectedAdapter(e -> updateEnableState(enableButton.getSelection())));
updateEnableState(enableButton.getSelection());
workingSetCombo.addSelectionListener(widgetSelectedAdapter(e -> updateSelectedWorkingSets()));
workingSetCombo.setItems(getHistoryEntries());
if (selectedWorkingSets.length == 0 && selectionHistory.size() > 0) {
workingSetCombo.select(historyIndex(selectionHistory.get(0)));
updateSelectedWorkingSets();
} else {
updateWorkingSetSelection();
}
}
private void createNewWorkingSet(Shell shell) {
IWorkingSetManager manager = WorkbenchPlugin.getDefault().getWorkingSetManager();
IWorkingSetNewWizard wizard = manager.createWorkingSetNewWizard(workingSetTypeIds);
// the wizard can never be null since we have at least a resource
// working set creation page
WizardDialog dialog = new WizardDialog(shell, wizard);
dialog.create();
PlatformUI.getWorkbench().getHelpSystem().setHelp(dialog.getShell(),
IWorkbenchHelpContextIds.WORKING_SET_NEW_WIZARD);
if (dialog.open() == Window.OK) {
IWorkingSet workingSet = wizard.getSelection();
if (workingSet != null) {
manager.addWorkingSet(workingSet);
selectedWorkingSets = new IWorkingSet[] { workingSet };
PlatformUI.getWorkbench().getWorkingSetManager().addRecentWorkingSet(workingSet);
}
enableButton.setSelection(true);
updateEnableState(true);
updateWorkingSetSelection();
}
}
private void updateEnableState(boolean enabled) {
workingSetLabel.setEnabled(enabled);
workingSetCombo
.setEnabled(enabled
&& (selectedWorkingSets.length > 0 || getHistoryEntries().length > 0));
selectButton.setEnabled(enabled);
}
private void updateWorkingSetSelection() {
if (selectedWorkingSets.length > 0) {
workingSetCombo.setEnabled(true);
StringBuilder buf = new StringBuilder();
buf.append(selectedWorkingSets[0].getLabel());
for (int i = 1; i < selectedWorkingSets.length; i++) {
IWorkingSet ws = selectedWorkingSets[i];
buf.append(',').append(' ');
buf.append(ws.getLabel());
}
String currentSelection = buf.toString();
int index = historyIndex(currentSelection);
historyInsert(currentSelection);
if (index >= 0) {
workingSetCombo.select(index);
} else {
workingSetCombo.setItems(getHistoryEntries());
workingSetCombo.select(historyIndex(currentSelection));
}
} else {
enableButton.setSelection(false);
updateEnableState(false);
}
}
private String[] getHistoryEntries() {
String[] history = selectionHistory
.toArray(new String[selectionHistory.size()]);
Arrays.sort(history, (o1, o2) -> Collator.getInstance().compare(o1, o2));
return history;
}
private void historyInsert(String entry) {
selectionHistory.remove(entry);
selectionHistory.add(0, entry);
storeSelectionHistory(dialogSettings);
}
private int historyIndex(String entry) {
for (int i = 0; i < workingSetCombo.getItemCount(); i++) {
if (workingSetCombo.getItem(i).equals(entry)) {
return i;
}
}
return -1;
}
// copied from org.eclipse.jdt.internal.ui.text.JavaCommentScanner
private String[] split(String value, String delimiters) {
StringTokenizer tokenizer= new StringTokenizer(value, delimiters);
int size= tokenizer.countTokens();
String[] tokens= new String[size];
int i= 0;
while (i < size) {
tokens[i++]= tokenizer.nextToken();
}
return tokens;
}
private void updateSelectedWorkingSets() {
String item = workingSetCombo.getItem(workingSetCombo
.getSelectionIndex());
String[] workingSetNames = split(item, ", "); //$NON-NLS-1$
IWorkingSetManager workingSetManager = PlatformUI.getWorkbench()
.getWorkingSetManager();
selectedWorkingSets = new IWorkingSet[workingSetNames.length];
for (int i = 0; i < workingSetNames.length; i++) {
IWorkingSet set = workingSetManager
.getWorkingSet(workingSetNames[i]);
Assert.isNotNull(set);
selectedWorkingSets[i] = set;
}
}
private void storeSelectionHistory(IDialogSettings settings) {
String[] history;
if (selectionHistory.size() > MAX_HISTORY_SIZE) {
List<String> subList = selectionHistory.subList(0, MAX_HISTORY_SIZE);
history = subList.toArray(new String[subList.size()]);
} else {
history = selectionHistory
.toArray(new String[selectionHistory.size()]);
}
settings.put(WORKINGSET_SELECTION_HISTORY, history);
}
private List<String> loadSelectionHistory(IDialogSettings settings, String... workingSetIds) {
String[] strings = settings.getArray(WORKINGSET_SELECTION_HISTORY);
if (strings == null || strings.length == 0) {
return new ArrayList<>();
}
List<String> result = new ArrayList<>();
Set<String> workingSetIdsSet = new HashSet<>(Arrays.asList(workingSetIds));
IWorkingSetManager workingSetManager = PlatformUI.getWorkbench()
.getWorkingSetManager();
for (String string : strings) {
String[] workingSetNames = split(string, ", "); //$NON-NLS-1$
boolean valid = true;
for (int j = 0; j < workingSetNames.length && valid; j++) {
IWorkingSet workingSet = workingSetManager
.getWorkingSet(workingSetNames[j]);
if (workingSet == null) {
valid = false;
} else if (!workingSetIdsSet.contains(workingSet.getId())) {
valid = false;
}
}
if (valid) {
result.add(string);
}
}
return result;
}
/*
* Copy from DialogPage with changes to accomodate the lack of a Dialog context.
*/
private GridData setButtonLayoutData(Button button) {
button.setFont(JFaceResources.getDialogFont());
GC gc = new GC(button);
gc.setFont(button.getFont());
FontMetrics fontMetrics = gc.getFontMetrics();
gc.dispose();
GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
int widthHint = Dialog.convertHorizontalDLUsToPixels(fontMetrics, IDialogConstants.BUTTON_WIDTH);
Point minSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
data.widthHint = Math.max(widthHint, minSize.x);
button.setLayoutData(data);
return data;
}
}