blob: 0427ec0c0ed59f89447808d7a6547aab369b733b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 The Eclipse Foundation 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:
* Yatta Solutions - initial API and implementation, public API (bug 432803)
*******************************************************************************/
package org.eclipse.epp.internal.mpc.ui.wizards;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.epp.internal.mpc.core.model.News;
import org.eclipse.epp.internal.mpc.ui.MarketplaceClientUiPlugin;
import org.eclipse.epp.mpc.core.model.INews;
import org.eclipse.epp.mpc.ui.CatalogDescriptor;
import org.eclipse.equinox.internal.p2.ui.discovery.util.WorkbenchUtil;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.wizard.IWizardContainer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.ProgressEvent;
import org.eclipse.swt.browser.ProgressListener;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
import org.eclipse.ui.internal.browser.WorkbenchBrowserSupport;
/**
* @author Carsten Reckord
*/
public class NewsViewer {
private Control browser;
private Control control;
private final MarketplaceWizard wizard;
public NewsViewer(MarketplaceWizard marketplaceWizard) {
this.wizard = marketplaceWizard;
}
public Control createControl(Composite parent) {
control = createBrowser(parent);
if (control == null) {
control = createNoBrowserPart(parent);
}
return control;
}
protected Control createNoBrowserPart(Composite parent) {
Composite container = new Composite(parent, SWT.NONE);
GridLayoutFactory.fillDefaults().numColumns(1).equalWidth(false).margins(20, 0).applyTo(container);
Label noEmbedBrowserLabel = new Label(container, SWT.WRAP);
GridDataFactory.fillDefaults()
.align(SWT.BEGINNING, SWT.END)
.grab(true, true)
.hint(450, SWT.DEFAULT)
.applyTo(noEmbedBrowserLabel);
noEmbedBrowserLabel.setText(Messages.NewsViewer_No_embeddable_browser);
final Link link = new Link(container, SWT.NONE);
GridDataFactory.fillDefaults()
.align(SWT.BEGINNING, SWT.BEGINNING)
.indent(IDialogConstants.SMALL_INDENT, 0)
.grab(true, true)
.hint(450 - IDialogConstants.SMALL_INDENT, SWT.DEFAULT)
.applyTo(link);
link.setText(Messages.NewsViewer_No_news);
link.setEnabled(false);
link.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
WorkbenchUtil.openUrl(e.text, IWorkbenchBrowserSupport.AS_EXTERNAL);
}
public void widgetDefaultSelected(SelectionEvent e) {
widgetSelected(e);
}
});
Menu popup = new Menu(parent.getShell(), SWT.POP_UP);
MenuItem copyMenuItem = new MenuItem(popup, SWT.PUSH);
copyMenuItem.setText(Messages.NewsViewer_Copy_Link_Address);
copyMenuItem.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
Clipboard clipboard = new Clipboard(link.getDisplay());
String data = (String) link.getData("href"); //$NON-NLS-1$
clipboard.setContents(new Object[] { data }, new Transfer[] { TextTransfer.getInstance() });
clipboard.dispose();
}
public void widgetDefaultSelected(SelectionEvent e) {
widgetSelected(e);
}
});
link.setMenu(popup);
browser = link;
return container;
}
protected Control createBrowser(Composite parent) {
final Browser browser;
try {
if (!WorkbenchBrowserSupport.getInstance().isInternalWebBrowserAvailable()) {
return null;
}
browser = new Browser(parent, SWT.NONE);
} catch (Throwable t) {
// embedded browser not available
return null;
}
browser.addProgressListener(new ProgressRunnable());
browser.addLocationListener(new NewsUrlHandler(this));
this.browser = browser;
return browser;
}
public Control getControl() {
return control;
}
public MarketplaceWizard getWizard() {
return this.wizard;
}
public Browser getBrowser() {
if (browser instanceof Browser) {
return (Browser) browser;
} else {
return null;
}
}
public void showNews(INews news) {
final String url = news.getUrl();
if (url != null && url.length() > 0) {
showUrl(url);
String key = computeNewsPreferenceKey();
MarketplaceClientUiPlugin.getInstance().getPreferenceStore().putValue(key, computeNewsStamp(news));
}
}
protected void showUrl(String url) {
if (browser instanceof Browser) {
((Browser) browser).setUrl(url);
} else {
Link link = (Link) browser;
link.setData("href", url); //$NON-NLS-1$
link.setText(NLS.bind("<a href=\"{0}\">{1}</a>", url, url)); //$NON-NLS-1$
link.setEnabled(true);
link.getParent().layout();
}
}
public void refresh() {
if (browser instanceof Browser) {
((Browser) browser).refresh();
}
}
public void stop() {
if (browser instanceof Browser) {
((Browser) browser).stop();
}
}
public void dispose() {
if (browser != null && browser.getMenu() != null) {
browser.getMenu().dispose();
}
if (control != null) {
control.dispose();
}
}
public boolean isUpdated(INews news) {
if (news == null) {
return false;
}
String url = news.getUrl();
if (url == null || url.length() == 0) {
return false;
}
String key = computeNewsPreferenceKey();
String previous = MarketplaceClientUiPlugin.getInstance().getPreferenceStore().getString(key);
if (previous != null && previous.length() > 0) {
String current = computeNewsStamp(news);
return !previous.equals(current);
}
return true;
}
private String computeNewsStamp(INews news) {
return NLS.bind("[{0}]{1}", news.getTimestamp(), news.getUrl()); //$NON-NLS-1$
}
private String computeNewsPreferenceKey() {
CatalogDescriptor catalogDescriptor = wizard.getConfiguration().getCatalogDescriptor();
URL catalogUrl = catalogDescriptor.getUrl();
URI catalogUri;
try {
catalogUri = catalogUrl.toURI();
} catch (URISyntaxException e) {
// should never happen
throw new IllegalStateException(e);
}
String marketplaceId = catalogUri.toString().replaceAll("[^a-zA-Z0-9_-]", "_"); //$NON-NLS-1$ //$NON-NLS-2$
return News.class.getSimpleName() + "/" + marketplaceId; //$NON-NLS-1$
}
private class ProgressRunnable implements IRunnableWithProgress, ProgressListener {
private int current;
private int total;
private int lastCurrent;
private int lastTotal;
private boolean done;
private boolean running = false;
public void completed(ProgressEvent event) {
if (running) {
running = false;
done();
}
}
public void changed(ProgressEvent event) {
if (event.total == 0) {
return;
}
if (!running) {
running = true;
current = event.current;
total = event.total;
Display.getCurrent().asyncExec(new Runnable() {
public void run() {
try {
IWizardContainer container = wizard.getContainer();
if (container != null) {
wizard.getContainer().run(true, true, ProgressRunnable.this);
}
} catch (InvocationTargetException e) {
} catch (InterruptedException e) {
// cancelled by the user
} finally {
completed(null);
Browser browser = getBrowser();
if (!browser.isDisposed()) {
browser.stop();
browser.removeProgressListener(ProgressRunnable.this);
}
}
}
});
} else {
progress(event.current, event.total);
}
}
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
final SubMonitor progress = SubMonitor.convert(monitor, Messages.NewsViewer_Loading, total);
if (current > 0) {
progress.worked(current);
}
long lastUpdate = System.currentTimeMillis();
while (!monitor.isCanceled() && !done) {
int newCurrent, newTotal, newRemaining, worked;
synchronized (this) {
wait(200);
newCurrent = current;
newTotal = total;
int oldRemaining = lastTotal - lastCurrent;
newRemaining = newTotal - newCurrent;
long now = System.currentTimeMillis();
long timeSinceLastUpdate = now - lastUpdate;
if (newCurrent != lastCurrent || newTotal != lastTotal) {
lastUpdate = now;
} else if ((newRemaining == 0 && oldRemaining == 0 && timeSinceLastUpdate >= 900)
|| timeSinceLastUpdate > 10000) {
//FIXME hack because completed event is not always coming
break;
}
worked = oldRemaining - newRemaining;
if (worked <= 0) {
worked = 1;
}
lastTotal = newTotal;
lastCurrent = newCurrent;
}
progress.setWorkRemaining(newRemaining + worked);
progress.worked(worked);
}
if (monitor.isCanceled()) {
getBrowser().stop();
return;
}
}
public synchronized void progress(int current, int total) {
this.current = current;
this.total = total;
notify();
}
public synchronized void done() {
done = true;
notify();
}
}
}