blob: 499dd70b4c3b8ab38e6a85e98a7cba07a47b1f95 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 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
* Semion Chichelnitsky (semion@il.ibm.com) - bug 278064
* Denis Zygann <d.zygann@web.de> - Bug 330453
* Lars Vogel <Lars.Vogel@vogella.com> - Bug 472654
*******************************************************************************/
package org.eclipse.ui.internal.dialogs;
import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.PlainMessageDialog;
import org.eclipse.jface.dialogs.PlainMessageDialog.Builder;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
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.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.IPerspectiveDescriptor;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.IPreferenceConstants;
import org.eclipse.ui.internal.IWorkbenchHelpContextIds;
import org.eclipse.ui.internal.Workbench;
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.internal.registry.PerspectiveDescriptor;
import org.eclipse.ui.internal.registry.PerspectiveRegistry;
import org.eclipse.ui.internal.util.Descriptors;
import org.eclipse.ui.internal.util.PrefUtil;
/**
* The Workbench / Perspectives preference page.
*/
public class PerspectivesPreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
private IWorkbench workbench;
private PerspectiveRegistry perspectiveRegistry;
// List of perspectives to populate preference page
private ArrayList<IPerspectiveDescriptor> perspectives;
private String defaultPerspectiveId;
private ArrayList<IPerspectiveDescriptor> perspToDelete = new ArrayList<>();
private ArrayList<IPerspectiveDescriptor> perspToRevert = new ArrayList<>();
private Table perspectivesTable;
private Button revertButton;
private Button deleteButton;
private Button setDefaultButton;
// widgets for open perspective mode;
private Button openSameWindowButton;
private Button openNewWindowButton;
private int openPerspMode;
// labels
private final String OPM_TITLE = WorkbenchMessages.OpenPerspectiveMode_optionsTitle;
private final String OPM_SAME_WINDOW = WorkbenchMessages.OpenPerspectiveMode_sameWindow;
private final String OPM_NEW_WINDOW = WorkbenchMessages.OpenPerspectiveMode_newWindow;
/**
* <code>Comparator</code> to compare two perspective descriptors
*/
private Comparator<IPerspectiveDescriptor> comparator = new Comparator<>() {
private Collator collator = Collator.getInstance();
@Override
public int compare(IPerspectiveDescriptor d1, IPerspectiveDescriptor d2) {
return collator.compare(d1.getLabel(), d2.getLabel());
}
};
/**
* Creates the page's UI content.
*/
@Override
protected Control createContents(Composite parent) {
// @issue if the product subclasses this page, then it should provide
// the help content
PlatformUI.getWorkbench().getHelpSystem().setHelp(parent,
IWorkbenchHelpContextIds.PERSPECTIVES_PREFERENCE_PAGE);
Composite composite = createComposite(parent);
createOpenPerspButtonGroup(composite);
createCustomizePerspective(composite);
return composite;
}
/**
* Creates the composite which will contain all the preference controls for this
* page.
*
* @param parent the parent composite
* @return the composite for this page
*/
protected Composite createComposite(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
GridData data = new GridData(GridData.FILL_BOTH);
composite.setLayoutData(data);
composite.setFont(parent.getFont());
GridLayout layout = new GridLayout();
layout.marginWidth = 0;
layout.marginHeight = 0;
layout.verticalSpacing = 10;
composite.setLayout(layout);
return composite;
}
/**
* Create a composite that contains buttons for selecting the open perspective
* mode.
*
* @param composite the parent composite
*/
protected void createOpenPerspButtonGroup(Composite composite) {
Font font = composite.getFont();
Group buttonComposite = new Group(composite, SWT.LEFT);
buttonComposite.setText(OPM_TITLE);
buttonComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
buttonComposite.setFont(composite.getFont());
GridLayout layout = new GridLayout();
layout.numColumns = 2;
buttonComposite.setLayout(layout);
openSameWindowButton = new Button(buttonComposite, SWT.RADIO);
openSameWindowButton.setText(OPM_SAME_WINDOW);
openSameWindowButton.setSelection(IPreferenceConstants.OPM_ACTIVE_PAGE == openPerspMode);
openSameWindowButton.setFont(font);
openSameWindowButton
.addSelectionListener(widgetSelectedAdapter(e -> openPerspMode = IPreferenceConstants.OPM_ACTIVE_PAGE));
openNewWindowButton = new Button(buttonComposite, SWT.RADIO);
openNewWindowButton.setText(OPM_NEW_WINDOW);
openNewWindowButton.setSelection(IPreferenceConstants.OPM_NEW_WINDOW == openPerspMode);
openNewWindowButton.setFont(font);
openNewWindowButton
.addSelectionListener(widgetSelectedAdapter(e -> openPerspMode = IPreferenceConstants.OPM_NEW_WINDOW));
}
/**
* Create a table of 3 buttons to enable the user to manage customized
* perspectives.
*
* @param parent the parent for the button parent
* @return Composite that the buttons are created in.
*/
protected Composite createCustomizePerspective(Composite parent) {
Font font = parent.getFont();
// define container & its gridding
Composite perspectivesComponent = new Composite(parent, SWT.NONE);
perspectivesComponent.setLayoutData(new GridData(GridData.FILL_BOTH));
perspectivesComponent.setFont(parent.getFont());
GridLayout layout = new GridLayout();
layout.numColumns = 2;
layout.marginWidth = 0;
layout.marginHeight = 0;
perspectivesComponent.setLayout(layout);
// Add the label
Label label = new Label(perspectivesComponent, SWT.LEFT);
label.setText(WorkbenchMessages.PerspectivesPreference_available);
GridData data = new GridData();
data.horizontalSpan = 2;
label.setLayoutData(data);
label.setFont(font);
// Add perspectivesTable.
perspectivesTable = new Table(perspectivesComponent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
perspectivesTable.addSelectionListener(widgetSelectedAdapter(e -> updateButtons()));
perspectivesTable.setFont(font);
data = new GridData(GridData.FILL_BOTH);
data.grabExcessHorizontalSpace = true;
data.grabExcessVerticalSpace = true;
perspectivesTable.setLayoutData(data);
// Populate the perspectivesTable
IPerspectiveDescriptor[] persps = perspectiveRegistry.getPerspectives();
perspectives = new ArrayList<>(persps.length);
for (int i = 0; i < persps.length; i++) {
perspectives.add(i, persps[i]);
}
perspectives.sort(comparator);
defaultPerspectiveId = perspectiveRegistry.getDefaultPerspective();
updatePerspectivesTable();
// Create vertical button bar.
Composite buttonBar = (Composite) createVerticalButtonBar(perspectivesComponent);
data = new GridData(GridData.FILL_VERTICAL);
buttonBar.setLayoutData(data);
// Add note label
String NOTE_LABEL = WorkbenchMessages.Preference_note;
String REVERT_NOTE = WorkbenchMessages.RevertPerspective_note;
Composite noteComposite = createNoteComposite(font, parent, NOTE_LABEL, REVERT_NOTE);
GridData noteData = new GridData();
noteData.horizontalSpan = 2;
noteComposite.setLayoutData(noteData);
return perspectivesComponent;
}
/**
* Creates a new vertical button with the given id.
* <p>
* The default implementation of this framework method creates a standard push
* button, registers for selection events including button presses and help
* requests, and registers default buttons with its shell. The button id is
* stored as the buttons client data.
* </p>
*
* @param parent the parent composite
* @param label the label from the button
* @param defaultButton <code>true</code> if the button is to be the default
* button, and <code>false</code> otherwise
* @return Button The created button.
*/
protected Button createVerticalButton(Composite parent, String label, boolean defaultButton) {
Button button = new Button(parent, SWT.PUSH);
button.setText(label);
GridData data = setButtonLayoutData(button);
data.horizontalAlignment = GridData.FILL;
button.addSelectionListener(widgetSelectedAdapter(event -> verticalButtonPressed(event.widget)));
button.setToolTipText(label);
if (defaultButton) {
Shell shell = parent.getShell();
if (shell != null) {
shell.setDefaultButton(button);
}
}
button.setFont(parent.getFont());
return button;
}
/**
* Creates and returns the vertical button bar.
*
* @param parent the parent composite to contain the button bar
* @return the button bar control
*/
protected Control createVerticalButtonBar(Composite parent) {
// Create composite.
Composite composite = new Composite(parent, SWT.NULL);
// create a layout with spacing and margins appropriate for the font
// size.
GridLayout layout = new GridLayout();
layout.numColumns = 1;
layout.marginWidth = 5;
layout.marginHeight = 0;
layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
composite.setLayout(layout);
composite.setFont(parent.getFont());
// Add the buttons to the button bar.
setDefaultButton = createVerticalButton(composite, WorkbenchMessages.PerspectivesPreference_MakeDefault, false);
setDefaultButton.setToolTipText(WorkbenchMessages.PerspectivesPreference_MakeDefaultTip);
revertButton = createVerticalButton(composite, WorkbenchMessages.PerspectivesPreference_Reset, false);
revertButton.setToolTipText(WorkbenchMessages.PerspectivesPreference_ResetTip);
deleteButton = createVerticalButton(composite, WorkbenchMessages.PerspectivesPreference_Delete, false);
deleteButton.setToolTipText(WorkbenchMessages.PerspectivesPreference_DeleteTip);
updateButtons();
return composite;
}
/**
* @see IWorkbenchPreferencePage
*/
@Override
public void init(IWorkbench aWorkbench) {
this.workbench = aWorkbench;
this.perspectiveRegistry = (PerspectiveRegistry) workbench.getPerspectiveRegistry();
IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore();
setPreferenceStore(store);
openPerspMode = store.getInt(IPreferenceConstants.OPEN_PERSP_MODE);
}
/**
* The default button has been pressed.
*/
@Override
protected void performDefaults() {
// Project perspective preferences
IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore();
openPerspMode = store.getDefaultInt(IPreferenceConstants.OPEN_PERSP_MODE);
openSameWindowButton.setSelection(IPreferenceConstants.OPM_ACTIVE_PAGE == openPerspMode);
openNewWindowButton.setSelection(IPreferenceConstants.OPM_NEW_WINDOW == openPerspMode);
String currentDefault = perspectiveRegistry.getDefaultPerspective();
int index = indexOf(currentDefault);
if (index >= 0) {
defaultPerspectiveId = currentDefault;
updatePerspectivesTable();
perspectivesTable.setSelection(index);
}
String newDefault = PrefUtil.getAPIPreferenceStore()
.getDefaultString(IWorkbenchPreferenceConstants.DEFAULT_PERSPECTIVE_ID);
IPerspectiveDescriptor desc = null;
if (newDefault != null) {
desc = workbench.getPerspectiveRegistry().findPerspectiveWithId(newDefault);
}
if (desc == null) {
newDefault = workbench.getPerspectiveRegistry().getDefaultPerspective();
}
defaultPerspectiveId = newDefault;
updatePerspectivesTable();
}
/**
* Look up the index of the perpective with the given if.
*
* @param perspectiveId or <code>null</code>
* @return int -1 if it cannot be found
*/
private int indexOf(String perspectiveId) {
if (perspectiveId == null) {
return -1;
}
PerspectiveDescriptor[] descriptors = new PerspectiveDescriptor[perspectives.size()];
perspectives.toArray(descriptors);
for (int i = 0; i < descriptors.length; i++) {
PerspectiveDescriptor descriptor = descriptors[i];
if (descriptor.getId().equals(perspectiveId)) {
return i;
}
}
return -1;
}
/**
* Return true if there are no open instances of the perspective. If there are
* open instances of the perspective prompt the user and return true if the user
* answers "yes" to the delete prompt.
*
* @return boolean <code>true</code> if it is OK to delete the perspective
* either because there are no open instances or the user has confirmed
* the deletion.
*/
private boolean canDeletePerspective(IPerspectiveDescriptor desc) {
MApplication application = ((Workbench) workbench).getApplication();
EModelService modelService = application.getContext().get(EModelService.class);
if (modelService.findElements(application, desc.getId(), MPerspective.class).isEmpty())
return true;
Builder builder = PlainMessageDialog.getBuilder(getShell(),
WorkbenchMessages.PerspectivesPreference_perspectiveopen_title);
builder.message(NLS.bind(WorkbenchMessages.PerspectivesPreference_perspectiveopen_message, desc.getLabel()));
builder.image(getShell().getDisplay().getSystemImage(SWT.ICON_QUESTION));
builder.buttonLabels(Arrays.asList(IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL));
return builder.build().open() == 0;
}
/**
* Apply the user's changes if any
*/
@Override
public boolean performOk() {
// Set the default perspective
if (!Objects.equals(defaultPerspectiveId, perspectiveRegistry.getDefaultPerspective())) {
perspectiveRegistry.setDefaultPerspective(defaultPerspectiveId);
}
// Don't bother figuring out which window a perspective may be open in,
// the number of windows will be small.
for (IPerspectiveDescriptor perspective : perspToDelete) {
IWorkbenchWindow[] windows = workbench.getWorkbenchWindows();
for (IWorkbenchWindow window : windows) {
IWorkbenchPage[] pages = window.getPages();
for (IWorkbenchPage page : pages) {
page.closePerspective(perspective, true, false);
}
}
perspectiveRegistry.deletePerspectives(perspToDelete);
}
// Revert perspectives
for (IPerspectiveDescriptor perspective : perspToRevert) {
perspectiveRegistry.revertPerspective(perspective);
}
IPreferenceStore store = getPreferenceStore();
// store the open perspective mode setting
store.setValue(IPreferenceConstants.OPEN_PERSP_MODE, openPerspMode);
// save both the API prefs and the internal prefs
// the API prefs are modified by
// PerspectiveRegistry.setDefaultPerspective
PrefUtil.savePrefs();
return true;
}
/**
* Update the button enablement state.
*/
protected void updateButtons() {
// Get selection.
int index = perspectivesTable.getSelectionIndex();
// Map it to the perspective descriptor
PerspectiveDescriptor desc = null;
if (index > -1) {
desc = (PerspectiveDescriptor) perspectives.get(index);
}
// Do enable.
if (desc != null) {
revertButton.setEnabled(desc.isPredefined() && desc.hasCustomDefinition() && !perspToRevert.contains(desc));
deleteButton.setEnabled(!desc.isPredefined());
setDefaultButton.setEnabled(true);
} else {
revertButton.setEnabled(false);
deleteButton.setEnabled(false);
setDefaultButton.setEnabled(false);
}
}
/**
* Update the perspectivesTable.
*/
protected void updatePerspectivesTable() {
// Populate the table with the items
perspectivesTable.removeAll();
for (int i = 0; i < perspectives.size(); i++) {
PerspectiveDescriptor persp = (PerspectiveDescriptor) perspectives.get(i);
newPerspectivesTableItem(persp, i, false);
}
}
/**
* Create a new tableItem using given perspective, and set image for the new
* item.
*/
protected TableItem newPerspectivesTableItem(IPerspectiveDescriptor persp, int index, boolean selected) {
ImageDescriptor image = persp.getImageDescriptor();
TableItem item = new TableItem(perspectivesTable, SWT.NULL, index);
if (image != null) {
Descriptors.setImage(item, image);
}
String label = persp.getLabel();
if (persp.getId().equals(defaultPerspectiveId)) {
label = NLS.bind(WorkbenchMessages.PerspectivesPreference_defaultLabel, label);
}
item.setText(label);
item.setData(persp);
if (selected) {
perspectivesTable.setSelection(index);
}
return item;
}
/**
* Notifies that this page's button with the given id has been pressed.
*
* @param button the button that was pressed
*/
protected void verticalButtonPressed(Widget button) {
// Get selection.
int index = perspectivesTable.getSelectionIndex();
// Map it to the perspective descriptor
PerspectiveDescriptor desc = null;
if (index > -1) {
desc = (PerspectiveDescriptor) perspectives.get(index);
} else {
return;
}
// Take action.
if (button == revertButton) {
if (!perspToRevert.contains(desc)) {
perspToRevert.add(desc);
}
} else if (button == deleteButton) {
if (!perspToDelete.contains(desc)) {
if (canDeletePerspective(desc)) {
perspToDelete.add(desc);
perspToRevert.remove(desc);
perspectives.remove(desc);
updatePerspectivesTable();
}
}
} else if (button == setDefaultButton) {
defaultPerspectiveId = desc.getId();
updatePerspectivesTable();
perspectivesTable.setSelection(index);
}
updateButtons();
}
}