/*=============================================================================#
# Copyright (c) 2010-2016 Stephan Wahlbrink (WalWare.de) 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:
# Stephan Wahlbrink - initial API and implementation
#=============================================================================*/
package de.walware.statet.r.console.ui.tools;
import static de.walware.statet.r.launching.RRunDebugPreferenceConstants.ASK;
import static de.walware.statet.r.launching.RRunDebugPreferenceConstants.AUTO;
import static de.walware.statet.r.launching.RRunDebugPreferenceConstants.DISABLED;
import static de.walware.statet.r.launching.RRunDebugPreferenceConstants.PREF_RENV_CHECK_UPDATE;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
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.preferences.InstanceScope;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
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.ui.statushandlers.StatusManager;
import de.walware.ecommons.preferences.PreferencesUtil;
import de.walware.ecommons.ts.ISystemRunnable;
import de.walware.ecommons.ts.ITool;
import de.walware.ecommons.ts.IToolService;
import de.walware.ecommons.ui.util.LayoutUtil;
import de.walware.ecommons.ui.util.UIAccess;
import de.walware.statet.nico.core.runtime.ToolProcess;
import de.walware.statet.nico.ui.util.ToolMessageDialog;
import de.walware.statet.r.console.core.AbstractRDataRunnable;
import de.walware.statet.r.console.core.IRDataAdapter;
import de.walware.statet.r.console.core.RConsoleTool;
import de.walware.statet.r.console.core.RProcess;
import de.walware.statet.r.core.RCore;
import de.walware.statet.r.core.pkgmanager.IRPkgChangeSet;
import de.walware.statet.r.core.pkgmanager.IRPkgManager;
import de.walware.statet.r.core.pkgmanager.IRPkgManager.Event;
import de.walware.statet.r.core.renv.IREnvConfiguration;
import de.walware.statet.r.core.rhelp.rj.RJREnvIndexChecker;
import de.walware.statet.r.core.rhelp.rj.RJREnvIndexUpdater;
import de.walware.statet.r.internal.console.ui.Messages;
import de.walware.statet.r.internal.console.ui.RConsoleUIPlugin;
import de.walware.statet.r.nico.impl.RjsController;
public class REnvIndexAutoUpdater {
public static void connect(final RProcess process, final IRPkgManager manager) {
final IREnvConfiguration rEnvConfig = (IREnvConfiguration) process
.getAdapter(IREnvConfiguration.class);
if (rEnvConfig != null) {
final CheckRunnable checker = new CheckRunnable(process, manager,
new RJREnvIndexChecker(rEnvConfig) );
process.getQueue().addOnIdle(checker, 1100);
return;
}
}
public static final class UpdateRunnable extends AbstractRDataRunnable {
private final boolean fCompletely;
public UpdateRunnable(final boolean completely) {
super("r/index/update", Messages.REnvIndex_Update_task); //$NON-NLS-1$
fCompletely = completely;
}
@Override
public boolean changed(final int event, final ITool tool) {
if (event == MOVING_FROM) {
return false;
}
return true;
}
@Override
protected void run(final IRDataAdapter r,
final IProgressMonitor monitor) throws CoreException {
IREnvConfiguration rEnvConfig = (IREnvConfiguration) r.getTool().getAdapter(IREnvConfiguration.class);
if (rEnvConfig != null) {
rEnvConfig = rEnvConfig.getReference().getConfig();
if (rEnvConfig != null) {
final String remoteAddress = r.getWorkspaceData().getRemoteAddress();
final Map<String, String> properties = new HashMap<>();
if (remoteAddress != null) {
properties.put("renv.hostname", remoteAddress); //$NON-NLS-1$
}
final IRPkgManager rPkgManager= RCore.getRPkgManager(rEnvConfig.getReference());
rPkgManager.check(IRPkgManager.NONE, r, monitor);
r.handleStatus(new Status(IStatus.INFO, RConsoleUIPlugin.PLUGIN_ID, -1,
Messages.REnvIndex_Update_Started_message, null ), monitor);
final RJREnvIndexUpdater updater = new RJREnvIndexUpdater(rEnvConfig);
final IStatus status = updater.update(r, fCompletely, properties, monitor);
r.handleStatus(status, monitor);
}
}
}
}
private static class AskDialog extends ToolMessageDialog {
private Button fRememberSessionControl;
private Button fRememberGloballyControl;
private boolean fRememberSession;
private boolean fRememberGlobally;
public AskDialog(final ToolProcess tool, final String message) {
super(tool, null, Messages.REnvIndex_CheckDialog_title, null, message, QUESTION,
new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL }, 0);
}
@Override
protected Control createMessageArea(final Composite parent) {
super.createMessageArea(parent);
LayoutUtil.addGDDummy(parent);
final Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(LayoutUtil.createCompositeGrid(1));
{ final Label label = new Label(composite, SWT.NONE);
label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
label.setText(Messages.REnvIndex_CheckDialog_Remember_label);
}
{ final Button button = new Button(composite, SWT.CHECK);
final GridData gd = new GridData(SWT.FILL, SWT.FILL, true, false);
gd.horizontalIndent = LayoutUtil.defaultIndent();
button.setLayoutData(gd);
button.setText(Messages.REnvIndex_CheckDialog_RememberSession_label);
fRememberSessionControl = button;
}
{ final Button button = new Button(composite, SWT.CHECK);
final GridData gd = new GridData(SWT.FILL, SWT.FILL, true, false);
gd.horizontalIndent = LayoutUtil.defaultIndent();
button.setLayoutData(gd);
button.setText(Messages.REnvIndex_CheckDialog_RememberGlobally_label);
fRememberGloballyControl = button;
}
fRememberGloballyControl.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(final SelectionEvent e) {
if (fRememberGloballyControl.getSelection()) {
fRememberSessionControl.setSelection(false);
}
}
@Override
public void widgetDefaultSelected(final SelectionEvent e) {
}
});
fRememberSessionControl.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(final SelectionEvent e) {
if (fRememberSessionControl.getSelection()) {
fRememberGloballyControl.setSelection(false);
}
}
@Override
public void widgetDefaultSelected(final SelectionEvent e) {
}
});
return composite;
}
@Override
public boolean close() {
fRememberGlobally = fRememberGloballyControl.getSelection();
fRememberSession = fRememberSessionControl.getSelection();
return super.close();
}
}
private static class CheckRunnable implements ISystemRunnable, IRPkgManager.Listener {
private final RProcess fProcess;
private final RJREnvIndexChecker fChecker;
private final IRPkgManager fManager;
private boolean fChanged;
private String fSessionSetting;
public CheckRunnable(final RProcess process, final IRPkgManager manager, final RJREnvIndexChecker checker) {
fProcess = process;
fManager = manager;
fChecker = checker;
fChanged = true;
fManager.addListener(this);
}
@Override
public String getTypeId() {
return "r/index/check"; //$NON-NLS-1$
}
@Override
public String getLabel() {
return Messages.REnvIndex_Check_task;
}
@Override
public boolean isRunnableIn(final ITool tool) {
return (tool.isProvidingFeatureSet(RConsoleTool.R_DATA_FEATURESET_ID));
}
@Override
public boolean changed(final int event, final ITool tool) {
if (event == MOVING_FROM) {
return false;
}
if (event == BEING_ABANDONED) {
fManager.removeListener(this);
}
return true;
}
@Override
public void handleChange(final Event event) {
if ((event.pkgsChanged() & IRPkgManager.INSTALLED) != 0) {
final IRPkgChangeSet changeSet= event.getInstalledPkgChangeSet();
if (changeSet != null && !changeSet.getNames().isEmpty()) {
fChanged = true;
}
}
}
@Override
public void run(final IToolService service,
final IProgressMonitor monitor) throws CoreException {
final RjsController r = (RjsController) service; // interface?
if (r.isBusy() || !r.isDefaultPrompt() || !fChanged ) {
return;
}
fChanged = false;
try {
final String global = PreferencesUtil.getInstancePrefs().getPreferenceValue(PREF_RENV_CHECK_UPDATE).intern();
if (global == DISABLED
|| (global == ASK && fSessionSetting == DISABLED) ) {
return;
}
final int check = fChecker.check(r, monitor);
final String message;
if (fChecker.wasAlreadyReported()) {
return;
}
switch (check) {
case RJREnvIndexChecker.NOT_AVAILABLE:
case RJREnvIndexChecker.UP_TO_DATE:
return;
case RJREnvIndexChecker.PACKAGES:
message = NLS.bind(((fChecker.getNewPackageCount() + fChecker.getChangedPackageCount()) == 1) ?
Messages.REnvIndex_Check_Changed_singular_message :
Messages.REnvIndex_Check_Changed_plural_message,
fChecker.getNewPackageCount(), fChecker.getChangedPackageCount());
break;
case RJREnvIndexChecker.COMPLETE:
message = Messages.REnvIndex_Check_NoIndex_message;
break;
default:
return;
}
if (global != AUTO && fSessionSetting == null) {
final AtomicBoolean update = new AtomicBoolean();
UIAccess.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
final AskDialog dialog = new AskDialog(fProcess, message);
update.set(dialog.open() == 0);
if (dialog.fRememberGlobally) {
PreferencesUtil.setPrefValue(InstanceScope.INSTANCE,
PREF_RENV_CHECK_UPDATE, update.get() ? AUTO : DISABLED);
}
else if (dialog.fRememberSession) {
fSessionSetting = update.get() ? AUTO : DISABLED;
}
}
});
if (!update.get()) {
return;
}
}
// schedule update
service.getTool().getQueue().add(new UpdateRunnable(false));
}
catch (final CoreException e) {
if (e.getStatus().getSeverity() == IStatus.CANCEL) {
throw e;
}
StatusManager.getManager().handle(new Status(IStatus.ERROR, RConsoleUIPlugin.PLUGIN_ID, -1,
Messages.REnvIndex_Check_error_message, e ));
}
}
}
}