blob: 5dae8b4d1fb24a0a259aba95ca51ee074e7b2212 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2012, 2020 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.internal.r.ui.pkgmanager;
import org.eclipse.core.databinding.observable.ChangeEvent;
import org.eclipse.core.databinding.observable.IChangeListener;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.ui.services.IServiceLocator;
import org.eclipse.statet.jcommons.status.Status;
import org.eclipse.statet.ecommons.databinding.jface.DataBindingSupport;
import org.eclipse.statet.ecommons.runtime.core.util.StatusUtils;
import org.eclipse.statet.ecommons.ui.components.StatusInfo;
import org.eclipse.statet.ecommons.ui.dialogs.DialogUtils;
import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
import org.eclipse.statet.ecommons.ui.util.NestedServices;
import org.eclipse.statet.ecommons.ui.util.UIAccess;
import org.eclipse.statet.internal.r.ui.RUIPlugin;
import org.eclipse.statet.nico.ui.util.ToolDialog;
import org.eclipse.statet.nico.ui.util.ToolInfoGroup;
import org.eclipse.statet.r.console.core.RProcess;
import org.eclipse.statet.r.core.pkgmanager.IRPkgManager;
import org.eclipse.statet.r.core.pkgmanager.IRPkgSet;
import org.eclipse.statet.r.core.pkgmanager.ISelectedRepos;
import org.eclipse.statet.r.core.renv.IREnvConfiguration;
import org.eclipse.statet.r.ui.pkgmanager.StartAction;
import org.eclipse.statet.rj.renv.runtime.RPkgManager;
public class RPkgManagerDialog extends ToolDialog implements IChangeListener, IRPkgManager.Listener {
static final int APPLY_ID= 10;
static final int INSTFILE_ID= 12;
static final Object[] NO_INPUT= new Object[0];
private final IRPkgManager.Ext rPkgManager;
private Display display;
private IStatus status;
private TabFolder tabFolder;
private PkgTab pkgTab;
private RepoTab repoTab;
private OptionsTab optionsTab;
private Button applyButton;
private final Object updateLock= new Object();
private boolean updateRepos;
private boolean updatePkgs;
private boolean updateState;
private int updatePage;
private StartAction startAction;
private NestedServices uiServices;
public RPkgManagerDialog(final IRPkgManager.Ext rPkgManager, final RProcess rProcess,
final Shell parentShell) {
super(rProcess, parentShell, null, "R Package Manager", ToolInfoGroup.WIDE);
this.rPkgManager= rPkgManager;
}
@Override
protected IDialogSettings getDialogBoundsSettings() {
return DialogUtils.getDialogSettings(RUIPlugin.getInstance(), "pkgmanager/MainDialog"); //$NON-NLS-1$
}
@Override
protected RProcess getTool() {
return (RProcess) super.getTool();
}
@Override
protected void setShellStyle(final int newShellStyle) {
super.setShellStyle((newShellStyle & ~SWT.APPLICATION_MODAL) | SWT.MIN | SWT.MAX);
}
@Override
public void create() {
super.create();
updateStatus();
}
@Override
protected Control createContents(final Composite parent) {
final Control control= super.createContents(parent);
setTitle(NLS.bind("R Package Manager for ''{0}''", this.rPkgManager.getREnv().getName()));
setTabFocus();
if (this.rPkgManager.getReposStatus(null).getSeverity() != Status.OK) {
activateTab(this.repoTab.getTab());
}
else {
onTabSelected(this.pkgTab.getTab());
}
this.display= parent.getDisplay();
this.rPkgManager.addListener(this);
parent.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(final DisposeEvent e) {
RPkgManagerDialog.this.rPkgManager.removeListener(RPkgManagerDialog.this);
}
});
this.uiServices= new NestedServices.Dialog(getShell());
this.pkgTab.createActions();
return control;
}
protected final IServiceLocator getServiceLocator() {
return this.uiServices.getLocator();
}
protected final PkgTab getPkgTab() {
return this.pkgTab;
}
protected final RepoTab getRepoTab() {
return this.repoTab;
}
protected final OptionsTab getOptionsTab() {
return this.optionsTab;
}
@Override
protected Control createDialogContent(final Composite parent) {
final Composite composite= new Composite(parent, SWT.NONE);
composite.setLayout(LayoutUtils.newCompositeGrid(1));
this.tabFolder= new TabFolder(composite, SWT.TOP);
this.tabFolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
{ final TabItem tabItem= new TabItem(this.tabFolder, SWT.NONE);
tabItem.setText("&Packages");
this.pkgTab= new PkgTab(this, tabItem, this.tabFolder, this.rPkgManager);
tabItem.setControl(this.pkgTab);
}
{ final TabItem tabItem= new TabItem(this.tabFolder, SWT.NONE);
tabItem.setText("&Repositories");
this.repoTab= new RepoTab(this, tabItem, this.tabFolder);
tabItem.setControl(this.repoTab);
}
{ final TabItem tabItem= new TabItem(this.tabFolder, SWT.NONE);
tabItem.setText("&Options");
this.optionsTab= new OptionsTab(this, tabItem, this.tabFolder);
tabItem.setControl(this.optionsTab);
}
applyDialogFont(composite);
final DataBindingSupport databinding= new DataBindingSupport(parent);
addBindings(databinding);
this.updateRepos= this.updatePkgs= this.updateState= true;
update();
this.repoTab.init();
this.tabFolder.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
onTabSelected((TabItem) e.item);
}
});
return composite;
}
public void activateTab(final TabItem tab) {
this.tabFolder.setSelection(tab);
onTabSelected(tab);
}
private void onTabSelected(final TabItem tab) {
this.applyButton.setVisible(this.repoTab.getTab() == tab);
checkAction();
}
@Override
protected void createButtonsForButtonBar(final Composite parent) {
createButton(parent, INSTFILE_ID, "Install from file...", false);
createButton(parent, 99, "", false).setVisible(false); //$NON-NLS-1$
this.applyButton= createButton(parent, APPLY_ID, "&Apply", false);
// super.createButtonsForButtonBar(parent);
createButton(parent, IDialogConstants.OK_ID, IDialogConstants.CLOSE_LABEL, true);
}
private void setTabFocus() {
final Display display= Display.getCurrent();
display.asyncExec(new Runnable() {
@Override
public void run() {
if (display.getFocusControl() == RPkgManagerDialog.this.tabFolder) {
final int idx= RPkgManagerDialog.this.tabFolder.getSelectionIndex();
if (idx >= 0) {
RPkgManagerDialog.this.tabFolder.getItem(idx).getControl().setFocus();
}
}
}
});
}
protected void initBindings(final Control control) {
// databinding.installStatusListener(new StatusUpdater());
}
int hintWidthInChars(final int chars) {
return convertWidthInCharsToPixels(chars);
}
void openPrefPage(final String pageId) {
final Control content= getContents();
final PreferenceDialog dialog= org.eclipse.ui.dialogs.PreferencesUtil
.createPreferenceDialogOn((content != null) ? content.getShell() : null,
pageId, null, null);
dialog.open();
if (content != null) {
content.setFocus();
}
}
protected void addBindings(final DataBindingSupport db) {
this.repoTab.addBindings(db);
this.pkgTab.addBinding(db);
}
private void update() {
boolean updateRepos;
boolean updatePkgs;
boolean updateState;
synchronized (this.updateLock) {
updateRepos= this.updateRepos;
this.updateRepos= false;
updatePkgs= this.updatePkgs;
this.updatePkgs= false;
updateState= this.updateState;
this.updateState= false;
if (!updateRepos && updatePkgs && !updateState) {
return;
}
}
this.rPkgManager.getReadLock().lock();
try {
if (updateRepos) {
this.repoTab.updateSettings(this.rPkgManager);
}
if (updatePkgs) {
this.pkgTab.updateSettings(this.rPkgManager);
checkAction();
}
updateStatus();
}
finally {
this.rPkgManager.getReadLock().unlock();
}
}
@Override
// data binding
public void handleChange(final ChangeEvent event) {
updateStatus();
}
@Override
// core pkg manager
public void handleChange(final IRPkgManager.Event event) {
synchronized (this.updateLock) {
this.updateRepos |= (event.reposChanged() > 0);
this.updatePkgs |= (event.pkgsChanged() > 0);
this.updatePkgs |= (event.viewsChanged() > 0);
this.updateState |= true;
}
if (!this.display.isDisposed()) {
this.display.asyncExec(new Runnable() {
@Override
public void run() {
if (UIAccess.isOkToUse(getContents())) {
update();
}
}
});
}
}
private void updateStatus() {
if (!UIAccess.isOkToUse(getContents())) {
return;
}
final ISelectedRepos repoSettings= this.repoTab.createRepoSettings();
final Status status= this.rPkgManager.getReposStatus(repoSettings);
if (status.getSeverity() > Status.OK) {
setStatus(StatusUtils.convert(status), this.repoTab.getTab());
return;
}
setStatus(new StatusInfo(IStatus.OK, "Install and Update R Packages"), this.pkgTab.getTab());
}
private void setStatus(final IStatus status, final TabItem tab) {
this.status= status;
if (!UIAccess.isOkToUse(getButtonBar())) {
return;
}
StatusInfo.applyToStatusLine(this, status);
final boolean apply= (status.getSeverity() != IStatus.ERROR);
getButton(IDialogConstants.OK_ID).setEnabled(apply);
getButton(APPLY_ID).setEnabled(apply);
this.pkgTab.updateStatus(status);
if (tab != null && this.updatePage >= 0 && this.updatePage == this.tabFolder.getSelectionIndex()
&& this.tabFolder.getItem(this.updatePage) != tab) {
activateTab(tab);
}
this.updatePage= -1;
}
protected final IStatus getStatus() {
return this.status;
}
@Override
protected void buttonPressed(final int buttonId) {
switch (buttonId) {
case APPLY_ID:
doApply(true);
break;
case INSTFILE_ID:
doInstFile();
break;
}
super.buttonPressed(buttonId);
}
void doApply(final boolean forceRefresh) {
this.rPkgManager.getWriteLock().lock();
try {
final ISelectedRepos repoSettings= this.repoTab.createRepoSettings();
this.rPkgManager.setSelectedRepos(repoSettings);
if (forceRefresh) {
this.rPkgManager.request(RPkgManager.REFRESH_AVAILABLE_PKGS | RPkgManager.REFRESH_INSTALLED_PKGS);
}
final int page= this.tabFolder.getSelectionIndex();
this.display.asyncExec(new Runnable() {
@Override
public void run() {
RPkgManagerDialog.this.updatePage= page;
}
});
this.rPkgManager.apply(getTool());
}
finally {
this.rPkgManager.getWriteLock().unlock();
}
}
void doInstFile() {
final IREnvConfiguration config= this.rPkgManager.getREnv().get(IREnvConfiguration.class);
if (config == null) {
return;
}
final InstallPkgFileWizard wizard= new InstallPkgFileWizard(getTool(), this.rPkgManager);
final WizardDialog dialog= new WizardDialog(getShell(), wizard);
dialog.setBlockOnOpen(true);
dialog.open();
}
@Override
protected void okPressed() {
doApply(false);
super.okPressed();
}
public void start(final StartAction action) {
this.startAction= action;
if (this.pkgTab != null && this.tabFolder.getSelectionIndex() >= 0) {
checkAction();
}
}
private void checkAction() {
if (this.startAction != null) {
if (this.status.getSeverity() == IStatus.OK
&& this.pkgTab.getPkgSet() != IRPkgSet.DUMMY) {
final StartAction action= this.startAction;
this.startAction= null;
switch (action.getAction()) {
case StartAction.INSTALL:
this.pkgTab.install(action.getPkgNames());
break;
case StartAction.REINSTALL:
this.pkgTab.reinstallAll();
break;
}
}
}
}
}