| /******************************************************************************* |
| * 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; |
| } |
| } |