/*******************************************************************************
* Copyright (c) 2013, 2016 Red Hat, Inc.
* 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:
* Red Hat Inc. - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.thym.ui.internal.engine;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.layout.LayoutConstants;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.operation.ModalContext;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.wizard.ProgressMonitorPart;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
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.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.thym.core.HybridCore;
import org.eclipse.thym.core.engine.HybridMobileEngine;
import org.eclipse.thym.core.engine.internal.cordova.CordovaEngineProvider;
import org.eclipse.thym.core.engine.internal.cordova.DownloadableCordovaEngine;
import org.eclipse.thym.core.extensions.PlatformSupport;
import org.eclipse.thym.ui.HybridUI;
import org.eclipse.thym.ui.PlatformImage;
import org.eclipse.thym.ui.internal.status.StatusManager;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.services.IEvaluationService;
import com.github.zafarkhaja.semver.Version;
public class EngineDownloadDialog extends TitleAreaDialog{
private static final int TREE_WIDTH = 350;
private static final int TREE_HEIGHT = 175;
private CheckboxTreeViewer platformList;
private CordovaEngineProvider engineProvider;
private List<HybridMobileEngine> engines;
private ProgressMonitorPart progressMonitorPart;
private Button nightlyBuilds;
private class DownloadableEngineLabelProvider extends LabelProvider implements ITableLabelProvider{
@Override
public Image getImage(Object element) {
return getColumnImage(element, 0);
}
@Override
public Image getColumnImage(Object element, int columnIndex) {
if(element instanceof PlatformSupport){
PlatformSupport platform = (PlatformSupport)element;
return PlatformImage.getImageFor(PlatformImage.ATTR_PLATFORM_SUPPORT, platform.getID());
}
return null;
}
@Override
public String getColumnText(Object element, int columnIndex) {
if(element instanceof PlatformSupport){
return ((PlatformSupport)element).getPlatform();
}
if(element instanceof DownloadableCordovaEngine){
return ((DownloadableCordovaEngine) element).getVersion();
}
return null;
}
@Override
public String getText(Object element) {
return getColumnText(element, 0);
}
}
private class DownloadableEngineComparator extends ViewerComparator implements Comparator<DownloadableCordovaEngine>{
@Override
public int compare(Viewer viewer, Object e1, Object e2) {
if(e1 instanceof DownloadableCordovaEngine && e2 instanceof DownloadableCordovaEngine){
DownloadableCordovaEngine o1 = (DownloadableCordovaEngine) e1;
DownloadableCordovaEngine o2 = (DownloadableCordovaEngine) e2;
return compare(o1, o2);
}
return 1;
}
@Override
public int compare(DownloadableCordovaEngine o1, DownloadableCordovaEngine o2) {
Version v1 = Version.valueOf(o1.getVersion());
Version v2 = Version.valueOf(o2.getVersion());
//Make it descending switch v1 to v2
return v2.compareTo(v1);
}
}
private class InstalledVersionsFilter extends ViewerFilter{
@Override
public boolean select(Viewer viewer, Object parentElement,
Object element) {
if(element instanceof DownloadableCordovaEngine ){
DownloadableCordovaEngine e = (DownloadableCordovaEngine)element;
return !isInstalled(e.getVersion(), e.getPlatformId());
}
return true;
}
}
private class DownloadableVersionsContentProvider implements ITreeContentProvider{
private DownloadableCordovaEngine[] downloadables;
private PlatformSupport[] platforms;
private boolean nightlyBuilds = false;
@Override
public void dispose() {
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
if(newInput != null && newInput instanceof List){
@SuppressWarnings("unchecked")
List<DownloadableCordovaEngine> list = (List<DownloadableCordovaEngine>)newInput;
downloadables = list.toArray(new DownloadableCordovaEngine[list.size()]);
}else{
downloadables = null;
}
}
@Override
public Object[] getElements(Object inputElement) {
if(platforms == null ){
List<PlatformSupport> allPlatforms = HybridCore.getPlatformSupports();
ArrayList<PlatformSupport> elements = new ArrayList<PlatformSupport>();
IEvaluationService service = (IEvaluationService)PlatformUI.getWorkbench().getService(IEvaluationService.class);
for (PlatformSupport generator : allPlatforms) {
try {
if(generator.isEnabled(service.getCurrentState())){
elements.add(generator);
}
} catch (CoreException e) {
HybridUI.log(IStatus.ERROR, "Error filtering objects", e);
}
}
platforms = elements.toArray(new PlatformSupport[elements.size()]);
}
return platforms;
}
@Override
public Object[] getChildren(Object parentElement) {
if(downloadables == null || !hasChildren(parentElement)){
return null;
}
PlatformSupport platform = (PlatformSupport) parentElement;
ArrayList<DownloadableCordovaEngine> platformDownloadables = new ArrayList<DownloadableCordovaEngine>();
for (DownloadableCordovaEngine engine : downloadables) {
if(engine.getPlatformId().equals(platform.getPlatformId())){
if(nightlyBuilds) {
platformDownloadables.add(engine);
} else if (!isNightlyBuild(engine)){
platformDownloadables.add(engine);
}
}
}
return platformDownloadables.toArray(new DownloadableCordovaEngine[platformDownloadables.size()]);
}
@Override
public Object getParent(Object element) {
if(platforms != null && element instanceof DownloadableCordovaEngine){
DownloadableCordovaEngine engine = (DownloadableCordovaEngine) element;
for (PlatformSupport platformSupport : platforms) {
if(engine.getPlatformId().equals(platformSupport.getPlatformId())){
return platformSupport;
}
}
}
return null;
}
@Override
public boolean hasChildren(Object element) {
return element instanceof PlatformSupport;
}
public void showNightlyBuilds(boolean nightlyBuilds){
this.nightlyBuilds = nightlyBuilds;
}
private boolean isNightlyBuild(DownloadableCordovaEngine engine){
return engine.getVersion() != null && engine.getVersion().contains("nightly");
}
}
public EngineDownloadDialog(Shell parent) {
super(parent);
setShellStyle(getShellStyle()| SWT.SHEET);
}
@Override
protected Control createContents(Composite parent) {
Control contents = super.createContents(parent);
toggleOKButton(false);
return contents;
}
@Override
protected Control createDialogArea(Composite parent) {
setTitle("Download Hybrid Mobile Engine");
setMessage("Download a new engine version");
parent.getShell().setText("Download Hybrid Mobile Engine");
final Composite composite = new Composite(parent, SWT.NONE);
GridLayoutFactory.fillDefaults().margins(LayoutConstants.getMargins()).spacing(LayoutConstants.getSpacing()).numColumns(1).applyTo(composite);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(composite);
Tree tree = new Tree(composite, SWT.CHECK | SWT.BORDER | SWT.FULL_SELECTION);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).minSize(new Point(TREE_WIDTH, TREE_HEIGHT)).applyTo(tree);
tree.setHeaderVisible(false);
tree.setLinesVisible(true);
platformList = new CheckboxTreeViewer(tree);
final DownloadableVersionsContentProvider provider = new DownloadableVersionsContentProvider();
platformList.setContentProvider(provider);
platformList.setLabelProvider(new DownloadableEngineLabelProvider());
platformList.setComparator(new DownloadableEngineComparator());
platformList.addFilter(new InstalledVersionsFilter());
platformList.addCheckStateListener(new ICheckStateListener() {
@Override
public void checkStateChanged(CheckStateChangedEvent event) {
if(event.getElement() instanceof PlatformSupport ){
platformList.setChecked(event.getElement(), false);
}
validate();
}
});
nightlyBuilds = new Button(composite, SWT.CHECK);
nightlyBuilds.setText("Show nightly builds");
nightlyBuilds.setSelection(false);
nightlyBuilds.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
provider.showNightlyBuilds(nightlyBuilds.getSelection());
platformList.refresh();
validate();
}
});
createProgressMonitorPart(composite);
engineProvider = new CordovaEngineProvider();
try {
platformList.setInput(engineProvider.getDownloadableVersions());
} catch (CoreException e) {
platformList.setInput(new Object());//Set the input to allow platforms to be displayed
setErrorMessage(e.getMessage());
HybridUI.log(IStatus.ERROR, "Unable to retrieve the downloadable versions list", e);
}
return composite;
}
private void createProgressMonitorPart(final Composite composite) {
progressMonitorPart = new ProgressMonitorPart(composite, new GridLayout());
progressMonitorPart.setVisible(true);
GridDataFactory.fillDefaults().applyTo(progressMonitorPart);
}
private boolean isInstalled(String version, String platformId){
if(version == null || platformId == null ) return false;
if(engines == null ){
engines = engineProvider.getAvailableEngines();
}
for (HybridMobileEngine engine : engines) {
if(engine.getVersion().equals(version)
&& engine.getId().equals(platformId)){
return true;
}
}
return false;
}
private void validate() {
Object[] checked = platformList.getCheckedElements();
if(checked == null || checked.length == 0 ){
toggleOKButton(false);
return;
}
setErrorMessage(null);
toggleOKButton(true);
}
private void toggleOKButton(boolean state) {
Button ok = getButton(IDialogConstants.OK_ID);
if(ok != null && !ok.isDisposed()){
ok.setEnabled(state);
}
}
@Override
protected void okPressed() {
final DownloadableCordovaEngine[] downloads = getDownloadsList();
IRunnableWithProgress runnable = new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException,
InterruptedException {
getShell().getDisplay().syncExec(new Runnable() {
@Override
public void run() {
toggleOKButton(false);
}
});
engineProvider.downloadEngine(downloads, monitor);
}
};
Button cancelBtn = getButton(IDialogConstants.CANCEL_ID);
progressMonitorPart.attachToCancelComponent(cancelBtn);
try {
ModalContext.run(runnable, true, progressMonitorPart, getShell().getDisplay());
} catch (InvocationTargetException e) {
if (e.getTargetException() != null) {
if(e.getTargetException() instanceof CoreException ){
StatusManager.handle((CoreException) e.getTargetException());
}else{
ErrorDialog.openError(getShell(), "Error downloading engine",null,
new Status(IStatus.ERROR, HybridUI.PLUGIN_ID, "Error downloading the engine", e.getTargetException() ));
}
}
} catch (InterruptedException e) {
throw new OperationCanceledException();
}
finally{
if(getShell() != null && !getShell().isDisposed()){
progressMonitorPart.removeFromCancelComponent(cancelBtn);
}
super.okPressed();
}
}
private DownloadableCordovaEngine[] getDownloadsList() {
final Object[] checked = platformList.getCheckedElements();
final DownloadableCordovaEngine[] downloads = new DownloadableCordovaEngine[checked.length];
for (int i = 0; i < checked.length; i++) {
DownloadableCordovaEngine dce = (DownloadableCordovaEngine) checked[i];
downloads[i] = dce;
}
return downloads;
}
}