blob: b008117f4c4822896bb47e94b0dc079240d6a1a7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2014 Code 9 Corporation and others.
* 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:
* Code 9 Corporation - initial API and implementation
* Bartosz Michalik <bartosz.michalik@gmail.com> - bug 240737
* Benjamin Cabe <benjamin.cabe@anyware-tech.com> - bug 265931
*******************************************************************************/
package org.eclipse.pde.internal.ui.editor.product;
import java.util.ArrayList;
import java.util.StringTokenizer;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.util.Util;
import org.eclipse.jface.viewers.*;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.pde.core.IModelChangedEvent;
import org.eclipse.pde.core.IWritable;
import org.eclipse.pde.core.plugin.IFragmentModel;
import org.eclipse.pde.core.plugin.PluginRegistry;
import org.eclipse.pde.internal.core.FeatureModelManager;
import org.eclipse.pde.internal.core.PDECore;
import org.eclipse.pde.internal.core.ifeature.IFeatureModel;
import org.eclipse.pde.internal.core.ifeature.IFeaturePlugin;
import org.eclipse.pde.internal.core.iproduct.*;
import org.eclipse.pde.internal.ui.*;
import org.eclipse.pde.internal.ui.editor.PDEFormPage;
import org.eclipse.pde.internal.ui.editor.TableSection;
import org.eclipse.pde.internal.ui.parts.TablePart;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.*;
import org.eclipse.ui.dialogs.ElementListSelectionDialog;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
public class PluginConfigurationSection extends TableSection {
private class ContentProvider implements IStructuredContentProvider {
private IProduct fProduct;
ContentProvider() {
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
*/
public Object[] getElements(Object inputElement) {
return fProduct.getPluginConfigurations();
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IContentProvider#dispose()
*/
public void dispose() {
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
*/
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
if (oldInput == newInput)
return;
fProduct = (IProduct) newInput;
//TODO refresh
}
}
private class LabelProvider extends PDELabelProvider {
public Image getColumnImage(Object obj, int index) {
if (index == 0)
return super.getColumnImage(PluginRegistry.findModel(((IPluginConfiguration) obj).getId()), index);
return null;
}
public String getColumnText(Object obj, int index) {
IPluginConfiguration configuration = (IPluginConfiguration) obj;
switch (index) {
case 0 :
return configuration.getId();
//return super.getColumnText(PluginRegistry.findModel(configuration.getId()), index);
case 1 :
return (configuration.getStartLevel() == 0) ? "default" : Integer.toString(configuration.getStartLevel()); //$NON-NLS-1$
case 2 :
return Boolean.toString(configuration.isAutoStart());
}
return null;
}
}
private TableViewer fConfigurationsTable;
private TableEditor fLevelColumnEditor;
private TableEditor fAutoColumnEditor;
//private IStructuredSelection fLastSelection = null;
/**
* @param page
* @param parent
*/
public PluginConfigurationSection(PDEFormPage page, Composite parent) {
super(page, parent, Section.DESCRIPTION, getButtonLabels());
}
private static String[] getButtonLabels() {
String[] labels = new String[4];
labels[0] = PDEUIMessages.Product_PluginSection_add;
labels[1] = PDEUIMessages.Product_PluginSection_recommended;
labels[2] = PDEUIMessages.PluginSection_remove;
labels[3] = PDEUIMessages.Product_PluginSection_removeAll;
return labels;
}
/*
* Return a comma-separated list of bundles that typically require auto-start and optionally
* require a special startlevel. Each entry is of the form <bundleID>[@ [<startlevel>] [":start"]]
* If the startlevel is omitted then the framework will use the default start level for the bundle.
* The "start" tag indicates that the bundle is autostarted.
*
* This list loosely based on TargetPlatform.getBundleList and more specifically on
* TargetPlatformHelper.getDefaultBundleList(). Both of these implementations are
* problematic because they are out of date, and also leave out commonly used bundles.
*
* This list attempts to describe a typical set up on the assumption that an advanced user can
* further modify it. The list is hard-coded rather than walking the plugin requirements of
* the product and all required products. The reason for this is that there are some bundles,
* such as org.eclipse.equinox.ds, that are typically needed but users do not remember to
* add them, and they are not required by any bundle. The idea of this list is to suggest
* these commonly used bundles and start levels that clients typically do not remember.
* See https://bugs.eclipse.org/bugs/show_bug.cgi?id=426529
*
* We use the same String format described in TargetPlatform so that in the future, we could
* obtain this list from another source that uses the same format.
*/
private static String getBundlesWithStartLevels() {
StringBuffer buffer = new StringBuffer();
buffer.append("org.eclipse.core.runtime@start,"); //$NON-NLS-1$
buffer.append("org.eclipse.equinox.common@2:start,"); //$NON-NLS-1$
buffer.append("org.eclipse.equinox.ds@2:start,"); //$NON-NLS-1$
buffer.append("org.eclipse.equinox.event@2:start,"); //$NON-NLS-1$
buffer.append("org.eclipse.equinox.simpleconfigurator@1:start,"); //$NON-NLS-1$
buffer.append("org.eclipse.equinox.p2.reconciler.dropins@start"); //$NON-NLS-1$
return buffer.toString();
}
private static final char VERSION_SEPARATOR = '*';
/* (non-Javadoc)
* @see org.eclipse.pde.internal.ui.editor.PDESection#createClient(org.eclipse.ui.forms.widgets.Section, org.eclipse.ui.forms.widgets.FormToolkit)
*/
protected void createClient(Section section, FormToolkit toolkit) {
section.setText(PDEUIMessages.ConfigurationPageMock_sectionTitle);
section.setDescription(PDEUIMessages.ConfigurationPageMock_sectionDesc);
GridData sectionData = new GridData(SWT.FILL, SWT.FILL, true, true);
sectionData.horizontalSpan = 2;
section.setLayoutData(sectionData);
Composite container = createClientContainer(section, 3, toolkit);
createViewerPartControl(container, SWT.SINGLE | SWT.FULL_SELECTION, 3, toolkit);
fConfigurationsTable = getTablePart().getTableViewer();
final Table table = fConfigurationsTable.getTable();
final TableColumn column1 = new TableColumn(table, SWT.LEFT);
column1.setText(PDEUIMessages.PluginConfigurationSection_tablePluginTitle);
column1.setWidth(300);
final TableColumn levelColumnEditor = new TableColumn(table, SWT.LEFT);
levelColumnEditor.setText(PDEUIMessages.EquinoxPluginBlock_levelColumn);
final TableColumn autoColumnEditor = new TableColumn(table, SWT.LEFT);
autoColumnEditor.setText(PDEUIMessages.EquinoxPluginBlock_autoColumn);
table.addControlListener(new ControlListener() {
public void controlMoved(ControlEvent e) {
}
public void controlResized(ControlEvent e) {
int size = table.getSize().x;
column1.setWidth(size / 7 * 4);
levelColumnEditor.setWidth(size / 7 * 2);
autoColumnEditor.setWidth(size / 7 * 1);
}
});
table.setHeaderVisible(true);
toolkit.paintBordersFor(container);
fConfigurationsTable.setLabelProvider(getLabelProvider());
fConfigurationsTable.setContentProvider(new ContentProvider());
fConfigurationsTable.setInput(getProduct());
createEditors();
section.setClient(container);
getModel().addModelChangedListener(this);
getTablePart().setButtonEnabled(0, isEditable());
updateRemoveButtons(true, true);
}
protected void buttonSelected(int index) {
switch (index) {
case 0 :
handleAdd();
break;
case 1 :
handleAddDefaults();
break;
case 2 :
handleRemove();
break;
case 3 :
handleRemoveAll();
break;
}
}
private void handleAdd() {
ElementListSelectionDialog dialog = new ElementListSelectionDialog(PDEPlugin.getActiveWorkbenchShell(), PDEPlugin.getDefault().getLabelProvider());
ArrayList<IWritable> plugins = new ArrayList<IWritable>();
// TODO there must be a better way to do this!
if (getProduct().useFeatures()) {
IProductFeature[] features = getProduct().getFeatures();
for (int i = 0; i < features.length; i++) {
IProductFeature feature = features[i];
FeatureModelManager manager = PDECore.getDefault().getFeatureModelManager();
IFeatureModel fModel = manager.findFeatureModelRelaxed(feature.getId(), feature.getVersion());
if (fModel == null)
fModel = manager.findFeatureModel(feature.getId());
if (fModel == null)
continue;
IFeaturePlugin[] fPlugins = fModel.getFeature().getPlugins();
for (int j = 0; j < fPlugins.length; j++) {
IFeaturePlugin fPlugin = fPlugins[j];
if (!fPlugin.isFragment())
plugins.add(fPlugin);
}
}
dialog.setElements(plugins.toArray(new IFeaturePlugin[plugins.size()]));
} else {
IProductPlugin[] allPlugins = getProduct().getPlugins();
IPluginConfiguration[] configs = getProduct().getPluginConfigurations();
for (int i = 0; i < allPlugins.length; ++i) {
boolean match = false;
for (int j = 0; j < configs.length; ++j) {
String id = allPlugins[i].getId();
if (id.equals(configs[j].getId())) {
match = true;
break;
}
}
if (!match) {
// ensure we don't add fragments
if (!(PluginRegistry.findModel(allPlugins[i].getId()) instanceof IFragmentModel))
plugins.add(allPlugins[i]);
}
}
dialog.setElements(plugins.toArray(new IProductPlugin[plugins.size()]));
}
dialog.setTitle(PDEUIMessages.PluginSelectionDialog_title);
dialog.setMessage(PDEUIMessages.PluginSelectionDialog_message);
dialog.setMultipleSelection(true);
if (dialog.open() == Window.OK) {
Object[] results = dialog.getResult();
for (int i = 0; i < results.length; i++) {
Object result = results[i];
if (result instanceof IProductPlugin) {
addPlugin(((IProductPlugin) result).getId());
} else if (result instanceof IFeaturePlugin) {
addPlugin(((IFeaturePlugin) result).getId());
}
}
}
}
private void handleRemove() {
IStructuredSelection ssel = (IStructuredSelection) fConfigurationsTable.getSelection();
if (ssel.size() > 0) {
Object[] objects = ssel.toArray();
IPluginConfiguration[] configurations = new IPluginConfiguration[objects.length];
System.arraycopy(objects, 0, configurations, 0, objects.length);
getProduct().removePluginConfigurations(configurations);
}
clearEditors();
}
private void handleRemoveAll() {
IProduct product = getProduct();
product.removePluginConfigurations(product.getPluginConfigurations());
clearEditors();
}
private void handleAddDefaults() {
StringTokenizer tok = new StringTokenizer(getBundlesWithStartLevels(), ","); //$NON-NLS-1$
ArrayList<String[]> plugins = new ArrayList<String[]>();
IProduct product = getProduct();
while (tok.hasMoreTokens()) {
String token = tok.nextToken();
int index = token.indexOf('@');
if (index >= 0) { // there is a start level and/or autostart information
String idVersion = token.substring(0, index);
int versionIndex = idVersion.indexOf(VERSION_SEPARATOR);
String id = (versionIndex > 0) ? idVersion.substring(0, versionIndex) : idVersion;
int endStartLevelIndex = token.indexOf(':', index);
String startLevel = ""; //$NON-NLS-1$
String autostart;
if (endStartLevelIndex > 0) { // there is a start level
startLevel = token.substring(index + 1, endStartLevelIndex);
autostart = token.substring(endStartLevelIndex + 1);
} else {
autostart = token.substring(index + 1);
}
// If the product list does not already have this plugin, build an array of the parsed strings
// so that we can show the user what we propose to add. We don't yet create plugin configuration
// objects because that will change the model and we want confirmation.
if (product.findPluginConfiguration(id) == null) {
plugins.add(new String[] {id, startLevel, autostart});
}
}
}
if (plugins.size() > 0) {
// Build a user-presentable description of the plugins and start levels.
StringBuffer bundlesList = new StringBuffer();
bundlesList.append('\n');
bundlesList.append('\n');
for (int i = 0; i < plugins.size(); i++) {
String[] config = plugins.get(i);
bundlesList.append('\t');
bundlesList.append(config[0]);
bundlesList.append(", "); //$NON-NLS-1$ // Not translated. This is bundle syntax, not a sentence
String startLevel = config[1];
if (startLevel.length() > 0) {
bundlesList.append(PDEUIMessages.EquinoxPluginBlock_levelColumn);
bundlesList.append(' ');
bundlesList.append(startLevel);
} else {
String defaultLevelColumn = NLS.bind(PDEUIMessages.EquinoxPluginBlock_defaultLevelColumn, "Default"); //$NON-NLS-1$
bundlesList.append(defaultLevelColumn);
}
if ("start".equals(config[2])) { //$NON-NLS-1$
bundlesList.append(", "); //$NON-NLS-1$
bundlesList.append(PDEUIMessages.EquinoxPluginBlock_autoColumn);
}
bundlesList.append('\n');
}
bundlesList.append('\n');
// Confirm with user
if (MessageDialog.openConfirm(PDEPlugin.getActiveWorkbenchShell(), PDEUIMessages.Product_PluginSection_RecommendedBundles_title, NLS.bind(PDEUIMessages.Product_PluginSection_RecommendedBundles_message, bundlesList.toString()))) {
IPluginConfiguration[] pluginConfigs = new IPluginConfiguration[plugins.size()];
// Build the model objects for the plugins and add to the product model.
for (int i = 0; i < plugins.size(); i++) {
IProductModelFactory factory = product.getModel().getFactory();
IPluginConfiguration configuration = factory.createPluginConfiguration();
configuration.setId(plugins.get(i)[0]);
String startString = plugins.get(i)[1];
if (startString.length() > 0) {
configuration.setStartLevel(Integer.parseInt(startString));
}
configuration.setAutoStart("start".equals(plugins.get(i)[2])); //$NON-NLS-1$
pluginConfigs[i] = configuration;
}
product.addPluginConfigurations(pluginConfigs);
showControls();
}
} else {
// The user already had all the recommended bundles
MessageDialog.openInformation(PDEPlugin.getActiveWorkbenchShell(), PDEUIMessages.Product_PluginSection_RecommendedBundles_title, PDEUIMessages.Product_PluginSection_NoRecommendedBundles_message);
}
}
protected void selectionChanged(IStructuredSelection selection) {
updateRemoveButtons(true, false);
}
private void addPlugin(String id) {
IProduct product = getProduct();
IProductModelFactory factory = product.getModel().getFactory();
IPluginConfiguration configuration = factory.createPluginConfiguration();
configuration.setId(id);
product.addPluginConfigurations(new IPluginConfiguration[] {configuration});
fConfigurationsTable.setSelection(new StructuredSelection(configuration));
showControls();
}
private ILabelProvider getLabelProvider() {
return new LabelProvider();
}
private void clearEditors() {
Control oldEditor = fLevelColumnEditor.getEditor();
if (oldEditor != null && !oldEditor.isDisposed())
oldEditor.dispose();
oldEditor = fAutoColumnEditor.getEditor();
if (oldEditor != null && !oldEditor.isDisposed())
oldEditor.dispose();
}
private void createEditors() {
final Table table = fConfigurationsTable.getTable();
fLevelColumnEditor = new TableEditor(table);
fLevelColumnEditor.horizontalAlignment = SWT.CENTER;
fLevelColumnEditor.minimumWidth = 40;
fLevelColumnEditor.grabHorizontal = true;
if (Util.isMac())
fLevelColumnEditor.minimumHeight = 27;
fAutoColumnEditor = new TableEditor(table);
fAutoColumnEditor.horizontalAlignment = SWT.CENTER;
fAutoColumnEditor.grabHorizontal = true;
fAutoColumnEditor.minimumWidth = 50;
table.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
showControls();
}
});
}
private void showControls() {
// Clean up any previous editor control
clearEditors();
// Identify the selected row
Table table = fConfigurationsTable.getTable();
IStructuredSelection selection = (IStructuredSelection) fConfigurationsTable.getSelection();
if (selection.isEmpty())
return;
final TableItem item = table.getSelection()[0];
if (item != null && !isEditable())
return;
if (item != null) {
final IPluginConfiguration ppc = (IPluginConfiguration) selection.getFirstElement();
final Spinner spinner = new Spinner(table, SWT.BORDER);
spinner.setMinimum(0);
String level = item.getText(1);
int defaultLevel = level.length() == 0 || "default".equals(level) ? 0 : Integer.parseInt(level); //$NON-NLS-1$
spinner.setSelection(defaultLevel);
spinner.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
int selection = spinner.getSelection();
item.setText(1, selection == 0 ? "default" //$NON-NLS-1$
: Integer.toString(selection));
ppc.setStartLevel(selection);
}
});
fLevelColumnEditor.setEditor(spinner, item, 1);
final CCombo combo = new CCombo(table, SWT.BORDER | SWT.READ_ONLY);
//TODO is there need for the default options ??
combo.setItems(new String[] {Boolean.toString(true), Boolean.toString(false)});
combo.setText(item.getText(2));
combo.pack();
combo.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
item.setText(2, combo.getText());
ppc.setAutoStart(Boolean.valueOf(combo.getText()).booleanValue());
}
});
fAutoColumnEditor.setEditor(combo, item, 2);
}
}
private IProduct getProduct() {
return getModel().getProduct();
}
private IProductModel getModel() {
return (IProductModel) getPage().getPDEEditor().getAggregateModel();
}
/* (non-Javadoc)
* @see org.eclipse.pde.internal.core.IPluginModelListener#modelsChanged(org.eclipse.pde.internal.core.PluginModelDelta)
*/
public void modelChanged(IModelChangedEvent e) {
//TODO update modelChanged method
// No need to call super, handling world changed event here
if (e.getChangeType() == IModelChangedEvent.WORLD_CHANGED) {
handleGlobalRefresh();
return;
}
Table table = fConfigurationsTable.getTable();
int count = table.getItemCount();
Object[] objects = e.getChangedObjects();
boolean refreshRemove = false;
boolean refreshRemoveAll = false;
if (e.getChangeType() == IModelChangedEvent.INSERT) {
if (count == 0) {
refreshRemoveAll = true;
}
for (int i = 0; i < objects.length; i++) {
if (objects[i] instanceof IPluginConfiguration)
fConfigurationsTable.add(objects[i]);
}
} else if (e.getChangeType() == IModelChangedEvent.REMOVE) {
refreshRemove = refreshRemoveAll = true;
int index = table.getSelectionIndex();
boolean global = false;
for (int i = 0; i < objects.length; i++) {
if (objects[i] instanceof IPluginConfiguration)
fConfigurationsTable.remove(objects[i]);
else if (objects[i] instanceof IProductPlugin) {
global = true;
break;
}
}
if (global)
handleGlobalRefresh();
// Update Selection
if (count == 0) {
table.deselectAll();
clearEditors();
} else if (index < count) {
table.setSelection(index);
} else {
table.setSelection(count - 1);
}
}
getTablePart().setButtonEnabled(0, isEditable());
updateRemoveButtons(refreshRemove, refreshRemoveAll);
}
private void handleGlobalRefresh() {
fConfigurationsTable.setInput(getProduct());
fConfigurationsTable.refresh();
}
private void updateRemoveButtons(boolean updateRemove, boolean updateRemoveAll) {
TablePart tablePart = getTablePart();
if (updateRemove) {
ISelection selection = getViewerSelection();
tablePart.setButtonEnabled(2, isEditable() && !selection.isEmpty());
}
int count = fConfigurationsTable.getTable().getItemCount();
if (updateRemoveAll)
tablePart.setButtonEnabled(3, isEditable() && count > 0);
}
}