/*******************************************************************************
* Copyright (c) 2014 Mentor Graphics 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:
* Mentor Graphics - initial API and implementation
*******************************************************************************/
package com.codesourcery.internal.installer.ui.pages;
import java.net.URI;
import java.text.MessageFormat;
import java.util.ArrayList;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.equinox.p2.metadata.IVersionedId;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
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.Label;
import com.codesourcery.installer.IInstallComponent;
import com.codesourcery.installer.IInstallConsoleProvider;
import com.codesourcery.installer.IInstallConstraint;
import com.codesourcery.installer.IInstallData;
import com.codesourcery.installer.IInstallDescription;
import com.codesourcery.installer.IInstallMode;
import com.codesourcery.installer.IInstallValues;
import com.codesourcery.installer.Installer;
import com.codesourcery.installer.console.ConsoleListPrompter;
import com.codesourcery.installer.console.ConsoleYesNoPrompter;
import com.codesourcery.installer.ui.FormattedLabel;
import com.codesourcery.installer.ui.IInstallSummaryProvider;
import com.codesourcery.installer.ui.InstallWizardPage;
import com.codesourcery.internal.installer.IInstallPlan;
import com.codesourcery.internal.installer.IInstallRepositoryListener;
import com.codesourcery.internal.installer.IInstallerImages;
import com.codesourcery.internal.installer.InstallManager;
import com.codesourcery.internal.installer.InstallMessages;
import com.codesourcery.internal.installer.RepositoryManager;
import com.codesourcery.internal.installer.ui.DetailTree;
import com.codesourcery.internal.installer.ui.DetailTree.ImageType;
import com.codesourcery.internal.installer.ui.DetailTreeItem;
import com.codesourcery.internal.installer.ui.SpinnerProgress;
import com.codesourcery.internal.installer.ui.UIUtils;
/**
* Page to show components for the install. Optional components can be selected.
* This page supports console.
*/
public class ComponentsPage extends InstallWizardPage implements IInstallSummaryProvider, IInstallConsoleProvider, IInstallRepositoryListener, ICheckStateListener {
/** Component name column */
private static final int COLUMN_NAME = 0;
/** Component version column */
private static final int COLUMN_VERSION = 1;
/** Message label */
protected Label messageLabel;
/** Console list */
protected ConsoleListPrompter<IInstallComponent> consoleList;
/** Description font */
protected Font descriptionFont;
/** Button area */
protected Composite buttonArea;
/** Select all button */
protected Button selectAllButton;
/** Deselect all button */
protected Button deselectAllButton;
/** Status area containing the install size label and progress bar */
protected StatusPanel statusArea;
/** Last calculated install size */
//protected long installSize;
/** Install description */
private IInstallDescription installDescription;
/** Job to compute install plan */
private InstallPlanJob installPlanJob = new InstallPlanJob();
/** Components tree */
private DetailTree tree;
/** <code>true</code> if update is required */
private boolean needsUpdate = true;
/** Status for selection problems */
private IStatus selectionStatus;
/** Install plan */
private IInstallPlan installPlan;
/** Label provider */
private ComponentLabelProvider labelProvider;
/** Warning console prompter */
private ConsoleYesNoPrompter warningConsolePrompter;
/** Last computed available space */
private long lastAvailableSpace = -1;
/**
* Constructor
*
* @param pageName Page name
* @param title Page title
* @param installDescription Install description
*/
@SuppressWarnings("synthetic-access")
public ComponentsPage(String pageName, String title, IInstallDescription installDescription) {
super(pageName, title);
this.installDescription = installDescription;
// Label provider
labelProvider = new ComponentLabelProvider();
}
/**
* Returns the install description.
*
* @return Install description
*/
private IInstallDescription getInstallDescription() {
return installDescription;
}
/**
* Returns if the size status area should be shown.
*
* @return <code>true</code> if size status area should be shown
*/
private boolean getShowSizeStatus() {
return (getInstallDescription().getInstallSizeFormat() != null);
}
/**
* Returns the components label provider.
*
* @return Label provider
*/
private ComponentLabelProvider getLabelProvider() {
return labelProvider;
}
@Override
public void dispose() {
RepositoryManager.getDefault().removeRepositoryListener(this);
if (descriptionFont != null) {
descriptionFont.dispose();
}
super.dispose();
}
@SuppressWarnings("synthetic-access")
@Override
public Control createContents(Composite parent) {
Composite area = new Composite(parent, SWT.NONE);
GridLayout areaLayout = new GridLayout();
areaLayout.verticalSpacing = 0;
areaLayout.marginWidth = 0;
areaLayout.marginHeight = 0;
area.setLayout(areaLayout);
area.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
// Create bold font
FontData[] fd = getFont().getFontData();
fd[0].setHeight(fd[0].getHeight() - 1);
descriptionFont = new Font(getShell().getDisplay(), fd[0]);
// Message label
messageLabel = new Label(area, SWT.WRAP);
messageLabel.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
// Components tree
tree = new DetailTree(area, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.WRAP | SWT.DOUBLE_BUFFERED);
tree.setFont(getFont());
tree.setDescriptionFont(descriptionFont);
tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
// Set images
tree.setImage(ImageType.COLLAPSED, Installer.getDefault().getImageRegistry().get(IInstallerImages.TREE_COLLAPSE));
tree.setImage(ImageType.EXPANDED, Installer.getDefault().getImageRegistry().get(IInstallerImages.TREE_EXPAND));
tree.setImage(ImageType.UNCHECKED, Installer.getDefault().getImageRegistry().get(IInstallerImages.TREE_UNCHECKED));
tree.setImage(ImageType.CHECKED, Installer.getDefault().getImageRegistry().get(IInstallerImages.TREE_CHECKED));
tree.setImage(ImageType.NOCHECK, Installer.getDefault().getImageRegistry().get(IInstallerImages.TREE_NOCHECK));
// Button area
buttonArea = new Composite(area, SWT.NONE);
buttonArea.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
buttonArea.setLayout(new GridLayout(2, false));
// Select all optional button
selectAllButton = new Button(buttonArea, SWT.PUSH);
selectAllButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));
selectAllButton.setText(InstallMessages.ComponentsPage_SelectAllOptional);
selectAllButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
selectAllOptional(true);
}
});
// Deselect all optional button
deselectAllButton = new Button(buttonArea, SWT.PUSH);
deselectAllButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));
deselectAllButton.setText(InstallMessages.ComponentsPage_DeselectAllOptional);
deselectAllButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
selectAllOptional(false);
}
});
// Status area
if (getShowSizeStatus()) {
statusArea = new StatusPanel(area, SWT.NONE);
statusArea.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
}
// Listen to component changes
RepositoryManager.getDefault().addRepositoryListener(this);
// Update page
updatePage();
return area;
}
/**
* Returns the components tree.
*
* @return Tree
*/
private DetailTree getTree() {
return tree;
}
/**
* Selects/deselects all optional components.
*
* @param select <code>true</code> to select all, <code>false</code> to
* deselect all
*/
private void selectAllOptional(boolean select) {
getTree().setAllChecked(select);
updateButtons();
updateInstallPlan();
validateSelection(null);
}
/**
* Returns the components title.
*
* @return Title text
*/
private String getComponentsTitle() {
return Installer.getDefault().getInstallManager().getInstallMode().isUpdate() ?
InstallMessages.ComponentsPage_ComponentsLabelUpdate:
InstallMessages.ComponentsPage_ComponentsLabelInstall;
}
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
// Update page in case component attributes changed
if (visible) {
updatePage();
// Update install plan for new components
updateInstallPlan();
}
}
/**
* Adds a new component.
*
* @param component Component to add
* @param parent Parent item or <code>null</code>
*/
private void addComponent(IInstallComponent component, DetailTreeItem parent) {
// If component is included
if (component.isIncluded()) {
// Create item, optional components get a check-box
DetailTreeItem item;
// Member of other item
if (parent == null) {
item = new DetailTreeItem(getTree(), component.isOptional() ? SWT.CHECK : SWT.NONE);
}
// Root item
else {
item = new DetailTreeItem(parent, component.isOptional() ? SWT.CHECK : SWT.NONE);
}
item.setData(component);
item.setText(getLabelProvider().getText(component));
String description = component.getDescription();
if (description != null) {
item.setDescription(component.getDescription());
}
// Set component installed
item.setChecked(component.getInstall());
// Component group
if (component.hasMembers()) {
IInstallComponent[] groupComponents = component.getMembers();
for (IInstallComponent groupComponent : groupComponents) {
addComponent(groupComponent, item);
}
}
}
}
/**
* Sets the page to update the next time it is displayed.
*
* @param needsUpdate <code>true</code> to update page
*/
private void setUpdatePage(boolean needsUpdate) {
this.needsUpdate = needsUpdate;
}
/**
* Returns if the page should update the next time it is displayed.
*
* @return <code>true</code> if the page will be updated
*/
private boolean getUpdatePage() {
return needsUpdate;
}
/**
* Adds components to the page.
*/
private void addComponents() {
// Remove existing components
getTree().removeAll();
// First set of components to show
ArrayList<IInstallComponent> first = new ArrayList<IInstallComponent>();
// Second set of components to show
ArrayList<IInstallComponent> second = new ArrayList<IInstallComponent>();
// Add components. By default, order the required components first
IInstallComponent[] components = RepositoryManager.getDefault().getInstallComponents(true);
for (IInstallComponent component : components) {
if (component.isOptional()) {
second.add(component);
}
else {
first.add(component);
}
}
// Option to order optional components firs is set
if (getInstallDescription().getShowOptionalComponentsFirst()) {
ArrayList<IInstallComponent> swap = first;
first = second;
second = swap;
}
// Add first set of components
for (IInstallComponent component : first) {
addComponent(component, null);
}
// Add second set of components
for (IInstallComponent component : second) {
addComponent(component, null);
}
}
/**
* Updates the page.
*/
private void updatePage() {
// No update required
if (!getUpdatePage())
return;
setUpdatePage(false);
setPageComplete(true);
messageLabel.setText(getComponentsTitle());
// Stop listening for checked events
getTree().removeCheckStateListener(this);
// Add components
addComponents();
// Set expanded components
IVersionedId[] expandedRoots = getInstallDescription().getWizardExpandedRoots();
if ((expandedRoots != null) && (expandedRoots.length != 0)) {
DetailTreeItem[] items = getTree().getAllItems();
for (DetailTreeItem item : items) {
if (item.hasChildren()) {
IInstallComponent component = (IInstallComponent)item.getData();
for (IVersionedId expandedRoot : expandedRoots) {
if (component.getInstallUnit().getId().equals(expandedRoot.getId())) {
item.expandAll();
break;
}
}
}
}
}
// Listen for checked events
getTree().addCheckStateListener(this);
// Update buttons
updateButtons();
}
@Override
protected void autoUpdate() {
if (installPlan != null) {
long availableSpace = installPlan.getAvailableSpace();
// If available space changed
if (availableSpace != lastAvailableSpace) {
lastAvailableSpace = availableSpace;
// Update to clear any warning for insufficient installation space
updateStatus();
// Update size status to reflect changes in available space
updateSizeStatus();
}
}
}
/**
* Updates the install plan.
*/
private void updateInstallPlan() {
// Cancel current job if running
if (installPlanJob.getState() != Job.NONE) {
installPlanJob.cancel();
}
// Schedule plan job
installPlanJob.schedule();
}
/**
* Updates the enabled/disable state of buttons.
*/
private void updateButtons() {
boolean allSelected = true;
boolean oneSelected = false;
DetailTreeItem[] items = getTree().getAllItems();
for (DetailTreeItem item : items) {
IInstallComponent component = (IInstallComponent)item.getData();
if (component.isOptional()) {
if (item.isChecked()) {
oneSelected = true;
}
else {
allSelected = false;
}
}
}
// No items
if (items.length == 0) {
selectAllButton.setEnabled(false);
deselectAllButton.setEnabled(false);
}
// Select all is enabled if all are not selected
// Deselect all is enabled if at least one is selected
else {
selectAllButton.setEnabled(!allSelected);
deselectAllButton.setEnabled(oneSelected);
}
}
@Override
public String getInstallSummary() {
StringBuffer output = new StringBuffer();
output.append(InstallMessages.ComponentsPage_Summary_Installed);
output.append('\n');
consoleList = getConsolePrompter();
consoleList.setCheckedString(
"[" + InstallMessages.Yes + "]",
"[" + InstallMessages.No +" ]");
output.append(consoleList.toString(false, false, false));
output.append('\n');
return output.toString();
}
/**
* Sets the component install states from the checked items.
*/
private void saveInstallState() {
if (!isConsoleMode()) {
DetailTreeItem[] items = getTree().getAllItems();
for (DetailTreeItem item : items) {
IInstallComponent component = (IInstallComponent)item.getData();
component.setInstall(item.isChecked());
}
}
}
/**
* Sets the checked items according to component install states.
*/
private void updateInstallState() {
if (!isConsoleMode()) {
DetailTreeItem[] items = getTree().getAllItems();
for (DetailTreeItem item : items) {
IInstallComponent component = (IInstallComponent)item.getData();
item.setChecked(component.getInstall());
}
}
}
@Override
public void saveInstallData(IInstallData data) throws CoreException {
// Save install states
saveInstallState();
// Save install size
if (installPlan != null) {
data.setProperty(IInstallValues.INSTALL_SIZE, Long.toString(installPlan.getSize()));
}
}
/**
* Traverses install components and member components and adds them to a list.
*
* @param items List for items
* @param component Component to add
*/
private void addConsoleItem(ArrayList<IInstallComponent> items, IInstallComponent component) {
// Do not add excluded components
if (!component.isIncluded())
return;
items.add(component);
if (component.hasMembers()) {
for (IInstallComponent member : component.getMembers()) {
addConsoleItem(items, member);
}
}
}
/**
* Returns the console name for an install component.
*
* @param component Install component
* @return Console name
*/
private String getConsoleName(IInstallComponent component) {
StringBuffer name = new StringBuffer();
// Indent for parents
IInstallComponent parent = component.getParent();
while (parent != null) {
name.append(" ");
parent = parent.getParent();
}
// Append component information
name.append(getLabelProvider().getColumnText(component, COLUMN_NAME));
return name.toString();
}
/**
* Returns the console prompter.
*
* @return Console prompter
*/
private ConsoleListPrompter<IInstallComponent> getConsolePrompter() {
ConsoleListPrompter<IInstallComponent> prompter = new ConsoleListPrompter<IInstallComponent>(getComponentsTitle());
// Add console items
ArrayList<IInstallComponent> items = new ArrayList<IInstallComponent>();
IInstallComponent[] components = RepositoryManager.getDefault().getInstallComponents(true);
for (IInstallComponent component : components) {
addConsoleItem(items, component);
}
// Add items
for (IInstallComponent item : items) {
// Item is member of other item
if (item.getParent() != null) {
int parentIndex = prompter.getItemIndex(item.getParent());
prompter.addItem(parentIndex, getConsoleName(item), item, item.getInstall(),
item.isOptional());
}
// Root item
else {
prompter.addItem(getConsoleName(item), item, item.getInstall(),
item.isOptional());
}
}
return prompter;
}
@Override
public String getConsoleResponse(String input)
throws IllegalArgumentException {
String response = null;
// Warning prompter active
if (warningConsolePrompter != null) {
response = warningConsolePrompter.getConsoleResponse(input);
// Continue
if (warningConsolePrompter.getResult()) {
return null;
}
// Do not continue
else {
warningConsolePrompter = null;
input = null;
}
}
// Initial response
if (input == null) {
// Create console items list
consoleList = getConsolePrompter();
}
// Get response
response = consoleList.getConsoleResponse(input);
// Save install selections
ArrayList<IInstallComponent> selectedComponents = new ArrayList<IInstallComponent>();
consoleList.getSelectedData(selectedComponents);
//Traverse through all install components and mark only selected components for installation.
IInstallComponent[] components = RepositoryManager.getDefault().getInstallComponents(false);
for (IInstallComponent component : components) {
if (component.isOptional()) {
component.setInstall(false);
if (selectedComponents.contains(component))
component.setInstall(true);
}
}
// Report any selection error
String status = validateConstraints();
if (status != null) {
System.out.println(status);
}
// Validate result
if (response == null) {
// Compute plan
final IInstallPlan plan = RepositoryManager.getDefault().computeInstallPlan(null);
if (plan != null) {
// Error in plan
if (plan.getStatus().getSeverity() == IStatus.ERROR) {
response = MessageFormat.format("ERROR: {0}\n{1}", plan.getErrorMessage(), consoleList.toString());
}
// Show warnings and/or information and prompt to continue
else if (plan.getStatus().getSeverity() == IStatus.WARNING) {
String message = plan.getErrorMessage();
if (message != null) {
warningConsolePrompter = new ConsoleYesNoPrompter(plan.getErrorMessage(),
InstallMessages.Continue, true);
response = warningConsolePrompter.getConsoleResponse(null);
}
}
}
}
// Validate selections
if (response == null) {
status = validateConstraints();
// Selection error
if (status != null) {
response = MessageFormat.format("ERROR: {0}\n{1}", //$NON-NLS-1$
status,
consoleList.toString());
}
}
return response;
}
/**
* Updates the page status.
*/
private void updateStatus() {
ArrayList<IStatus> status = new ArrayList<IStatus>();
// Selection status
if (selectionStatus != null) {
status.add(selectionStatus);
}
// Provision status
if (installPlan != null) {
IStatus installStatus = installPlan.getStatus();
if ((installStatus != null) && !installStatus.isOK()) {
status.add(new Status(
installStatus.getSeverity(),
installStatus.getPlugin(),
installPlan.getErrorMessage(),
installStatus.getException()));
}
}
// Checker status
InstallManager manager = (InstallManager)Installer.getDefault().getInstallManager();
IStatus[] checkerStatus = manager.verifyInstallComponentSelection(getCheckedComponents());
for (IStatus s : checkerStatus) {
status.add(s);
}
IStatus[] totalStatus = status.toArray(new IStatus[status.size()]);
// Set status
setStatus((totalStatus.length > 0) ? totalStatus : null);
if (!isConsoleMode()) {
if (totalStatus.length > 0) {
showStatus(totalStatus);
}
else {
hideStatus();
}
}
// Set complete if no errors
setPageComplete(!hasStatusError());
}
/**
* Formats a comma delimited components list.
*
* @param components Components
* @return Components list
*/
private String formatComponentList(IInstallComponent[] components) {
StringBuffer names = new StringBuffer();
for (IInstallComponent component : components) {
if (names.length() > 0) {
names.append(", ");
}
names.append(getLabelProvider().getText(component));
}
return names.toString();
}
/**
* Returns the currently checked install components.
*
* @return Install components
*/
private IInstallComponent[] getCheckedComponents() {
DetailTreeItem[] items = getTree().getCheckedItems();
IInstallComponent[] components = new IInstallComponent[items.length];
for (int index = 0; index < components.length; index ++) {
components[index] = (IInstallComponent)items[index].getData();
}
return components;
}
/**
* Returns the install components.
*
* @return Install components
*/
private IInstallComponent[] getInstallComponents() {
ArrayList<IInstallComponent> toInstall = new ArrayList<IInstallComponent>();
IInstallComponent[] components = RepositoryManager.getDefault().getInstallComponents(false);
for (IInstallComponent component : components) {
if (component.getInstall() && component.isIncluded()) {
toInstall.add(component);
}
}
return toInstall.toArray(new IInstallComponent[toInstall.size()]);
}
/**
* Validates component constraints.
*
* @return Constraint problem message or <code>null</code> if all constraints
* are met.
*/
public String validateConstraints() {
saveInstallState();
String error = null;
// Get constraints
IInstallConstraint[] constraints = getInstallDescription().getInstallConstraints();
if (constraints != null) {
// Get selected components
IInstallComponent[] checkedComponents = getInstallComponents();
// Verify constraints
for (IInstallConstraint constraint : constraints) {
if (!constraint.validate(checkedComponents)) {
error = getConstraintError(constraint);
break;
}
}
}
return error;
}
/**
* Sets the installed state of components.
*
* @param components Components to set
* @param install <code>true</code> to install
*/
public void setInstallState(IInstallComponent[] components, boolean install) {
for (IInstallComponent component : components) {
component.setInstall(install);
if (component.hasMembers()) {
setInstallState(component.getMembers(), install);
}
}
updateInstallState();
}
/**
* Attempts to handle constraint problems when a component state changes
* by selecting or de-selecting components.
*
* @param item Component item that changed state
*/
public void handleConstraints(DetailTreeItem item) {
// Selected components
IInstallComponent[] checkedComponents = getCheckedComponents();
// Install constraints
IInstallConstraint[] constraints = getInstallDescription().getInstallConstraints();
if (constraints != null) {
// Check each constraint
for (IInstallConstraint constraint : constraints) {
// Constraint failed
if (!constraint.validate(checkedComponents)) {
switch(constraint.getConstraint()) {
// One of a set of components must be selected
case ONE_OF:
// Nothing can be done
break;
// One component requires others
case REQUIRES:
IInstallComponent[] targets = constraint.getTargets();
IInstallComponent component = (IInstallComponent)item.getData();
// If component is set to install but requires other components.
// Set the other components to install.
if (component.equals(constraint.getSource()) || component.isMemberOf(constraint.getSource())) {
setInstallState(targets, true);
}
// Component is not set to be installed, but is required by other
// components. Set other components to not install.
else {
setInstallState(new IInstallComponent[] { constraint.getSource() }, false);
}
break;
// Only of of a set of components can be selected
case ONLY_ONE:
// Set other components to not install
ArrayList<IInstallComponent> notInstall = new ArrayList<IInstallComponent>();
IInstallComponent source = (IInstallComponent)item.getData();
for (IInstallComponent target : constraint.getTargets()) {
if (!source.equals(target) && !source.isMemberOf(target)) {
notInstall.add(target);
}
}
setInstallState(notInstall.toArray(new IInstallComponent[notInstall.size()]), false);
break;
}
}
}
}
}
/**
* Returns an error message for a failed constraint.
*
* @param constraint Constraint
* @return Error message
*/
private String getConstraintError(IInstallConstraint constraint) {
String error = null;
if (constraint != null) {
switch (constraint.getConstraint()) {
// One of a set of components must be selected
case ONE_OF:
error = NLS.bind(InstallMessages.Error_ConstraintOneRequired0,
formatComponentList(constraint.getTargets()));
break;
// One component requires others
case REQUIRES:
String sourceName = getLabelProvider().getText(constraint.getSource());
error = NLS.bind(InstallMessages.Error_ConstraintRequires1, sourceName, formatComponentList(constraint.getTargets()));
break;
// Only one of a set of components can be selected
case ONLY_ONE:
error = NLS.bind(InstallMessages.Error_Constraint0, formatComponentList(constraint.getTargets()));
break;
}
}
return error;
}
/**
* Validates the current selection and shows a status.
*
* @param item Tree item or <code>null</code>
*/
private void validateSelection(DetailTreeItem item) {
boolean statusChanged = false;
String error = validateConstraints();
if (error != null) {
// If constraints are not satisfied, attempt to correct the selection
if (item != null) {
handleConstraints(item);
}
// Check constraints again
String recheckError = validateConstraints();
// Constraints still have errors
if (recheckError != null) {
selectionStatus = new Status(IStatus.ERROR, Installer.ID, recheckError);
statusChanged = true;
}
// Constraints were correct, show information that action was taken
else {
selectionStatus = new Status(IStatus.INFO, Installer.ID, error);
statusChanged = true;
}
}
// Clear any error status, but leave any information status to inform
// user that some action was taken to correct the constraint.
else if ((selectionStatus != null) && !(selectionStatus.getSeverity() == IStatus.INFO)) {
selectionStatus = null;
statusChanged = true;
}
// Update status
if (statusChanged) {
updateStatus();
}
}
@Override
public void checkStateChanged(CheckStateChangedEvent event) {
DetailTreeItem item = (DetailTreeItem)event.getElement();
IInstallComponent component = (IInstallComponent)item.getData();
if (component.isOptional()) {
// Updated install state
component.setInstall(item.isChecked());
// Update install space for new component selection
updateInstallPlan();
// Update button state
updateButtons();
// Validate selection
validateSelection(item);
}
}
@Override
public boolean validate() {
// Check constraint errors
String constraintError = validateConstraints();
if (constraintError != null) {
selectionStatus = new Status(IStatus.ERROR, Installer.ID, constraintError);
}
else {
selectionStatus = null;
}
// Update status
updateStatus();
return (selectionStatus == null);
}
@Override
public void repositoryStatus(final RepositoryStatus status) {
getShell().getDisplay().syncExec(new Runnable() {
@Override
public void run() {
// Repositories loading
if (status == IInstallRepositoryListener.RepositoryStatus.loadingStarted) {
showBusy(InstallMessages.ComponentsPage_LoadingInstallInformation);
setPageComplete(false);
}
// Repositories loaded
else if (status == IInstallRepositoryListener.RepositoryStatus.loadingCompleted) {
hideBusy();
setUpdatePage(true);
updatePage();
validate();
}
}
});
}
@Override
public void installComponentsChanged() {
// Mark the page to be updated when it is displayed
setUpdatePage(true);
}
@Override
public void repositoryError(URI location, String errorMessage) {
}
@Override
public void installComponentChanged(IInstallComponent component) {
// Mark the page to be updated when it is displayed
setUpdatePage(true);
}
@Override
public boolean isSupported() {
IInstallMode mode = Installer.getDefault().getInstallManager().getInstallMode();
return mode.isInstall();
}
/**
* Component label provider
*/
private class ComponentLabelProvider extends LabelProvider implements ITableLabelProvider {
@Override
public Image getImage(Object element) {
return getColumnImage(element, 0);
}
@Override
public String getText(Object element) {
return getColumnText(element, 0);
}
@Override
public Image getColumnImage(Object element, int columnIndex) {
Image image = null;
if (element instanceof IInstallComponent) {
IInstallComponent component = (IInstallComponent)element;
if (columnIndex == 0) {
// Optional component
if (component.isOptional()) {
image = null;
}
// Required component
else {
image = null;
}
}
}
return image;
}
@Override
public String getColumnText(Object element, int columnIndex) {
String text = null;
if (element instanceof IInstallComponent) {
IInstallComponent component = (IInstallComponent)element;
// Name column
if (columnIndex == COLUMN_NAME) {
text = component.getName();
if (getInstallDescription().getShowComponentVersions()) {
text += " (" + component.getInstallUnit().getVersion().toString() + ")";
}
}
// Version column
else if (columnIndex == COLUMN_VERSION) {
text = component.getInstallUnit().getVersion().toString();
}
}
return text;
}
}
/**
* Updates the size status text.
*/
private void updateSizeStatus() {
if (getShowSizeStatus()) {
if (installPlan != null) {
String msg;
if (installPlan.getSize() == -1) {
msg = MessageFormat.format(InstallMessages.RequiredSizeFormat0,
UIUtils.formatBytes(installPlan.getRequiredSize()));
}
else {
msg = MessageFormat.format(getInstallDescription().getInstallSizeFormat(),
UIUtils.formatBytes(installPlan.getSize()),
UIUtils.formatBytes(installPlan.getRequiredSize()),
UIUtils.formatBytes(installPlan.getAvailableSpace()));
}
statusArea.setText(msg);
}
}
// Start automatically updating so the available space is re-computed as the file system is changed
startAutoUpdate();
}
/**
* A job to compute the install plan.
*/
private class InstallPlanJob extends Job {
/**
* Constructor
*/
public InstallPlanJob() {
super("InstallPlanJob");
setSystem(true);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
// Show progress
getShell().getDisplay().syncExec(new Runnable() {
@Override
public void run() {
if (getShowSizeStatus()) {
statusArea.setProgressVisible(true);
}
}
});
// Compute plan
if (Installer.getDefault().getInstallManager().getInstallMode().isMirror()) {
installPlan = RepositoryManager.getDefault().computeCacheSize(monitor);
}
else {
installPlan = RepositoryManager.getDefault().computeInstallPlan(monitor);
}
// Hide progress and update install plan size and status
if ((installPlan != null) && !(getShell() == null) && !getShell().isDisposed()) {
getShell().getDisplay().syncExec(new Runnable() {
@Override
public void run() {
statusArea.setProgressVisible(false);
// Update size information
updateSizeStatus();
updateStatus();
}
});
}
return Status.OK_STATUS;
}
}
/**
* Panel to show size information.
*/
private class StatusPanel extends Composite {
/** Size progress */
private SpinnerProgress progressBar;
/** Size label */
private FormattedLabel installSizeLabel;
/**
* Constructor
*
* @param parent Parent for panel
* @param style Style flags
*/
public StatusPanel(Composite parent, int style) {
super(parent, style);
GridLayout layout = new GridLayout(1, true);
layout.marginTop = 8;
layout.marginWidth = 2;
layout.marginBottom = layout.horizontalSpacing = layout.verticalSpacing = 0;
setLayout(layout);
progressBar = new SpinnerProgress(this, SWT.NONE);
progressBar.setText(InstallMessages.ComponentsPage_ComputingSize);
progressBar.setLayoutData(new GridData(SWT.END, SWT.CENTER, true, false, 1, 1));
installSizeLabel = new FormattedLabel(this, SWT.NONE);
installSizeLabel.setLayoutData(new GridData(SWT.END, SWT.CENTER, true, false, 1, 1));
setProgressVisible(false);
}
/**
* Sets the visibility of the progress bar.
*
* @param visible <code>true</code> to set progress bar visible
*/
public void setProgressVisible(final boolean visible) {
progressBar.setVisible(visible);
progressBar.setProgress(visible);
int dimensionsProgress = (visible) ? SWT.DEFAULT : 0;
int dimensionsLabel = (visible) ? 0 : SWT.DEFAULT;
((GridData)progressBar.getLayoutData()).widthHint = dimensionsProgress;
((GridData)progressBar.getLayoutData()).heightHint = dimensionsProgress;
installSizeLabel.setVisible(!visible);
((GridData)installSizeLabel.getLayoutData()).widthHint = dimensionsLabel;
((GridData)installSizeLabel.getLayoutData()).heightHint = dimensionsLabel;
layout(true);
}
/**
* Sets the install size label.
* @param text
*/
public void setText(final String text) {
installSizeLabel.setText(text);
layout(true);
}
}
}