/* ********************************************************************** **
** Copyright notice **
** **
** (c) 2005-2009 RSSOwl Development Team **
** http://www.rssowl.org/ **
** **
** 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.rssowl.org/legal/epl-v10.html **
** **
** A copy is found in the file epl-v10.html and important notices to the **
** license from the team is found in the textfile LICENSE.txt distributed **
** in this package. **
** **
** This copyright notice MUST APPEAR in all copies of the file! **
** **
** Contributors: **
** RSSOwl Development Team - initial API and implementation **
** **
** ********************************************************************** */
package org.rssowl.ui.internal.dialogs;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.osgi.service.datalocation.Location;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
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 org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.update.core.ICategory;
import org.eclipse.update.core.IFeature;
import org.eclipse.update.core.IURLEntry;
import org.eclipse.update.operations.IInstallFeatureOperation;
import org.rssowl.core.Owl;
import org.rssowl.core.internal.persist.pref.DefaultPreferences;
import org.rssowl.core.persist.pref.IPreferenceScope;
import org.rssowl.core.util.LoggingSafeRunnable;
import org.rssowl.core.util.StringUtils;
import org.rssowl.ui.internal.OwlUI;
import org.rssowl.ui.internal.util.BrowserUtils;
import org.rssowl.ui.internal.util.LayoutUtils;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* The <code>UpdateDialog</code> shows when updates are available and will show
* a description of the update.
*
* @author bpasero
*/
public class UpdateDialog extends TitleAreaDialog {
private static final String PLUGINS = "plugins"; //$NON-NLS-1$
private static final String FEATURES = "features"; //$NON-NLS-1$
private LocalResourceManager fResources;
private final IInstallFeatureOperation[] fUpdates;
private StyledText fUpdateInfoTextRight;
private StyledText fUpdateInfoTextLeft;
private StyledText fUpdateInfoTextBottom;
private Button fUpdateOnStartupCheck;
private IPreferenceScope fPreferences;
private final boolean fCanUpdate;
/**
* @param parentShell
* @param updates
*/
public UpdateDialog(Shell parentShell, IInstallFeatureOperation[] updates) {
super(parentShell);
fResources = new LocalResourceManager(JFaceResources.getResources());
fPreferences = Owl.getPreferenceService().getGlobalScope();
fUpdates = updates;
fCanUpdate = canUpdate();
}
/*
* @see org.eclipse.jface.dialogs.TitleAreaDialog#close()
*/
@Override
public boolean close() {
if (!fUpdateOnStartupCheck.isDisposed())
fPreferences.putBoolean(DefaultPreferences.UPDATE_ON_STARTUP, fUpdateOnStartupCheck.getSelection());
fResources.dispose();
return super.close();
}
/*
* @see org.eclipse.jface.dialogs.Dialog#okPressed()
*/
@Override
protected void okPressed() {
/* Show an Extra Warning if Update will Fail due to missing write Permissions */
if (!fCanUpdate) {
MessageDialog dialog = new MessageDialog(getShell(), Messages.UpdateDialog_WARNING, null, Messages.UpdateDialog_MISSING_PERMISSIONS_WARNING, MessageDialog.WARNING, new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL }, 0);
if (dialog.open() != 0)
return;
}
super.okPressed();
}
/*
* @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
*/
@Override
protected void configureShell(Shell shell) {
super.configureShell(shell);
shell.setText(Messages.UpdateDialog_UPDATE_AVAILABLE);
}
/*
* @see org.eclipse.jface.dialogs.TitleAreaDialog#createDialogArea(org.eclipse.swt.widgets.Composite)
*/
@Override
protected Control createDialogArea(Composite parent) {
/* Title */
setTitle(Messages.UpdateDialog_UPDATE_AVAILABLE);
/* Title Image */
setTitleImage(OwlUI.getImage(fResources, "icons/wizban/update_wiz.png")); //$NON-NLS-1$
/* Title Message */
setMessage(Messages.UpdateDialog_NEW_VERSION_MSG);
/* Separator */
new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL).setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
/* Composite to hold all components */
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(LayoutUtils.createGridLayout(1, 5, 5));
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
Composite updateInfoContainer = new Composite(composite, SWT.BORDER);
updateInfoContainer.setLayout(LayoutUtils.createGridLayout(2, 5, 5, 0, 5, false));
updateInfoContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
updateInfoContainer.setBackground(composite.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
fUpdateInfoTextLeft = new StyledText(updateInfoContainer, SWT.READ_ONLY | SWT.MULTI | SWT.WRAP);
fUpdateInfoTextLeft.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
fUpdateInfoTextLeft.setLineSpacing(5);
fUpdateInfoTextLeft.setEnabled(false);
fUpdateInfoTextLeft.setBackground(composite.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
fUpdateInfoTextRight = new StyledText(updateInfoContainer, SWT.READ_ONLY | SWT.MULTI | SWT.WRAP);
fUpdateInfoTextRight.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
fUpdateInfoTextRight.setLineSpacing(5);
fUpdateInfoTextRight.setEnabled(false);
fUpdateInfoTextRight.setBackground(composite.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
fUpdateInfoTextBottom = new StyledText(updateInfoContainer, SWT.READ_ONLY | SWT.MULTI | SWT.WRAP);
fUpdateInfoTextBottom.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
((GridData) fUpdateInfoTextBottom.getLayoutData()).widthHint = 300;
fUpdateInfoTextBottom.setLineSpacing(5);
fUpdateInfoTextBottom.setEnabled(false);
fUpdateInfoTextBottom.setBackground(composite.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
/* Fill in Update Details */
showUpdateDescription();
/* Link to open RSSOwl.org */
Link link = new Link(updateInfoContainer, SWT.NONE);
link.setBackground(updateInfoContainer.getBackground());
link.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
link.setText(Messages.UpdateDialog_VISIT_HP);
link.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
BrowserUtils.openLinkExternal("http://www.rssowl.org"); //$NON-NLS-1$
}
});
/* Add Option to Control Checking for Updates Automatically */
fUpdateOnStartupCheck = new Button(composite, SWT.CHECK);
fUpdateOnStartupCheck.setText(Messages.UpdateDialog_UPDATE_ON_STARTUP);
fUpdateOnStartupCheck.setSelection(fPreferences.getBoolean(DefaultPreferences.UPDATE_ON_STARTUP));
/* Show Error if Update will Fail due to missing Write Permissions */
if (!fCanUpdate)
setErrorMessage(Messages.UpdateDialog_MISSING_PERMISSIONS_ERROR);
/* Separator */
new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL).setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
applyDialogFont(composite);
return composite;
}
/* Create a temporary file in the install location to check if write permissions are qualified */
private boolean canUpdate() {
final AtomicBoolean canUpdate = new AtomicBoolean(true);
SafeRunnable.run(new LoggingSafeRunnable() {
public void run() throws Exception {
Location installLocation = Platform.getInstallLocation();
if (installLocation != null && installLocation.getURL() != null) {
File installDirectory = toFile(installLocation.getURL());
if (!installDirectory.isFile()) {
File pluginsDir = new File(installDirectory, PLUGINS);
File featuresDir = new File(installDirectory, FEATURES);
if (pluginsDir.isDirectory() && featuresDir.isDirectory()) {
try {
/* Check Write Permission in Plugins Directory */
writeTempFile(canUpdate, pluginsDir);
/* Check Write Permission in Features Directory */
if (canUpdate.get())
writeTempFile(canUpdate, featuresDir);
} catch (IOException e) {
canUpdate.set(false);
} catch (SecurityException e) {
canUpdate.set(false);
}
}
}
}
}
private void writeTempFile(AtomicBoolean canUpdate, File dir) throws IOException {
File tmpFile = File.createTempFile("permcheck", ".tmp", dir); //$NON-NLS-1$ //$NON-NLS-2$
tmpFile.deleteOnExit();
canUpdate.set(tmpFile.exists() && tmpFile.canWrite());
tmpFile.delete();
}
});
return canUpdate.get();
}
private static File toFile(URL url) {
try {
return new File(url.toURI());
} catch (URISyntaxException e) {
return new File(url.getPath());
}
}
@SuppressWarnings("deprecation")
private void showUpdateDescription() {
String oldVer = null;
String newVer = null;
long dlSize = 0;
String provider = null;
String description = null;
boolean isQualifierUpdate = false;
if (fUpdates != null && fUpdates.length > 0) {
IFeature oldFeature = fUpdates[0].getOldFeature();
IFeature newFeature = fUpdates[0].getFeature();
/* Versions */
org.eclipse.core.runtime.PluginVersionIdentifier oldVersion = (oldFeature != null) ? oldFeature.getVersionedIdentifier().getVersion() : null;
org.eclipse.core.runtime.PluginVersionIdentifier newVersion = newFeature.getVersionedIdentifier().getVersion();
if (oldVersion != null)
oldVer = oldVersion.getMajorComponent() + "." + oldVersion.getMinorComponent() + "." + oldVersion.getServiceComponent(); //$NON-NLS-1$ //$NON-NLS-2$
newVer = newVersion.getMajorComponent() + "." + newVersion.getMinorComponent() + "." + newVersion.getServiceComponent(); //$NON-NLS-1$ //$NON-NLS-2$
/* Special Treat Case of a Qualifier Update */
if (newVer.equals(oldVer) && oldVersion != null) {
isQualifierUpdate = true;
String newQualifierComponent = newVersion.getQualifierComponent();
String oldQualifierComponent = oldVersion.getQualifierComponent();
if (newQualifierComponent.length() == 12 && oldQualifierComponent.length() == 12) {
newVer += " (" + formatQualifier(newQualifierComponent) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
oldVer += " (" + formatQualifier(oldQualifierComponent) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
} else {
newVer += "." + newQualifierComponent; //$NON-NLS-1$
oldVer += "." + oldQualifierComponent; //$NON-NLS-1$
}
}
/* Other */
dlSize = newFeature.getDownloadSize() * 1000;
provider = newFeature.getProvider();
/* Description */
if (!isQualifierUpdate) {
ICategory[] categories = newFeature.getSite().getCategories();
if (categories.length > 0) {
IURLEntry descriptionEntry = categories[0].getDescription();
if (descriptionEntry != null)
description = descriptionEntry.getAnnotation();
}
} else
description = Messages.UpdateDialog_QUALIFIER_UPDATE_DESCRIPTION;
showUpdateDescription(oldVer, newVer, dlSize, provider, description);
}
}
private String formatQualifier(String qualifier) {
if (qualifier.length() == 12)
return qualifier.substring(0, 4) + "-" + qualifier.substring(4, 6) + "-" + qualifier.substring(6, 8); //$NON-NLS-1$ //$NON-NLS-2$
return qualifier;
}
private void showUpdateDescription(String oldVer, String newVer, long dlSize, String provider, String description) {
/* Old Version */
if (StringUtils.isSet(oldVer)) {
int offset = fUpdateInfoTextLeft.getText().length();
fUpdateInfoTextLeft.append(Messages.UpdateDialog_CURRENT_VERSION);
fUpdateInfoTextLeft.setStyleRange(new StyleRange(offset, fUpdateInfoTextLeft.getText().length() - offset, null, null, SWT.BOLD));
fUpdateInfoTextRight.append(oldVer);
fUpdateInfoTextLeft.append("\n"); //$NON-NLS-1$
fUpdateInfoTextRight.append("\n"); //$NON-NLS-1$
}
/* New Version */
if (StringUtils.isSet(newVer)) {
int offset = fUpdateInfoTextLeft.getText().length();
fUpdateInfoTextLeft.append(Messages.UpdateDialog_NEW_VERSION);
fUpdateInfoTextLeft.setStyleRange(new StyleRange(offset, fUpdateInfoTextLeft.getText().length() - offset, null, null, SWT.BOLD));
fUpdateInfoTextRight.append(newVer);
fUpdateInfoTextLeft.append("\n"); //$NON-NLS-1$
fUpdateInfoTextRight.append("\n"); //$NON-NLS-1$
}
/* Provider */
if (StringUtils.isSet(provider)) {
int offset = fUpdateInfoTextLeft.getText().length();
fUpdateInfoTextLeft.append(Messages.UpdateDialog_UPDATE_PROVIDER);
fUpdateInfoTextLeft.setStyleRange(new StyleRange(offset, fUpdateInfoTextLeft.getText().length() - offset, null, null, SWT.BOLD));
fUpdateInfoTextRight.append(provider);
fUpdateInfoTextLeft.append("\n"); //$NON-NLS-1$
fUpdateInfoTextRight.append("\n"); //$NON-NLS-1$
}
/* Size */
if (dlSize > 0) {
int offset = fUpdateInfoTextLeft.getText().length();
fUpdateInfoTextLeft.append(Messages.UpdateDialog_DL_SIZE);
fUpdateInfoTextLeft.setStyleRange(new StyleRange(offset, fUpdateInfoTextLeft.getText().length() - offset, null, null, SWT.BOLD));
fUpdateInfoTextRight.append(OwlUI.getSize((int) dlSize));
fUpdateInfoTextLeft.append("\n"); //$NON-NLS-1$
fUpdateInfoTextRight.append("\n"); //$NON-NLS-1$
}
/* Description */
if (StringUtils.isSet(description)) {
int offset = fUpdateInfoTextBottom.getText().length();
fUpdateInfoTextBottom.append(Messages.UpdateDialog_DESCRIPTION);
fUpdateInfoTextBottom.append("\n"); //$NON-NLS-1$
fUpdateInfoTextBottom.setStyleRange(new StyleRange(offset, fUpdateInfoTextBottom.getText().length() - offset, null, null, SWT.BOLD));
fUpdateInfoTextBottom.append(description);
}
}
/*
* @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
*/
@Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.OK_ID, Messages.UpdateDialog_DOWNLOAD_INSTALL, true).setFocus();
createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
}
/*
* @see org.eclipse.jface.window.Window#getShellStyle()
*/
@Override
protected int getShellStyle() {
int style = SWT.TITLE | SWT.BORDER | SWT.APPLICATION_MODAL | SWT.RESIZE | SWT.CLOSE | getDefaultOrientation();
return style;
}
/*
* @see org.eclipse.jface.dialogs.Dialog#initializeBounds()
*/
@Override
protected void initializeBounds() {
super.initializeBounds();
Shell shell = getShell();
/* Minimum Size */
int minWidth = convertHorizontalDLUsToPixels(OwlUI.MIN_DIALOG_WIDTH_DLU);
int minHeight = shell.computeSize(minWidth, SWT.DEFAULT).y;
/* Required Size */
Point requiredSize = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
shell.setSize(Math.max(minWidth, requiredSize.x), Math.max(minHeight, requiredSize.y));
LayoutUtils.positionShell(shell);
}
}