package jetbrains.mps.samples.ActionWithProgress.plugin;
/*Generated by MPS */
import jetbrains.mps.workbench.action.BaseAction;
import javax.swing.Icon;
import com.intellij.openapi.actionSystem.AnActionEvent;
import java.util.Map;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import jetbrains.mps.project.MPSProject;
import jetbrains.mps.ide.actions.MPSCommonDataKeys;
import org.jetbrains.annotations.NotNull;
import com.intellij.openapi.progress.PerformInBackgroundOption;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.progress.ProgressIndicator;
import jetbrains.mps.progress.ProgressMonitorAdapter;
import org.jetbrains.mps.openapi.module.SRepository;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProgressManager;
public class BackgroundableProgressAction_Action extends BaseAction {
private static final Icon ICON = null;
public BackgroundableProgressAction_Action() {
super("BackgroundableProgressAction", "", ICON);
this.setIsAlwaysVisible(false);
this.setExecuteOutsideCommand(false);
}
@Override
public boolean isDumbAware() {
return true;
}
@Override
protected boolean collectActionData(AnActionEvent event, final Map<String, Object> _params) {
if (!(super.collectActionData(event, _params))) {
return false;
}
{
Project p = event.getData(CommonDataKeys.PROJECT);
if (p == null) {
return false;
}
}
{
MPSProject p = event.getData(MPSCommonDataKeys.MPS_PROJECT);
if (p == null) {
return false;
}
}
return true;
}
@Override
public void doExecute(@NotNull final AnActionEvent event, final Map<String, Object> _params) {
// Indicates whether the progress dialog has the'Cancel' option
boolean canBeCanceled = true;
// Will be sent to the background with the flag PerformInBackgroundOption.ALWAYS_BACKGROUND
PerformInBackgroundOption showProgress = PerformInBackgroundOption.DEAF;
// This is a backgroundable task. It can be sent to the background and canceled
// The PerformInBackgroundOption flag specifies if the progress is shown to the user
// or if should be in the background from the start
// ALWAYS_BACKGROUND is probably the best solution for quick background tasks -
// the user will not get a blinking, hardly noticable progress bar visible for only a few fractions of a second
// Important thing - you need to implement the onCacel() method
// Your code needs to frequently check if the process has been canceled (between every calculation steps)
// and handle yourself all steps to revert the action
final Task.Backgroundable backgroundable = new Task.Backgroundable(event.getData(CommonDataKeys.PROJECT), "Backgroundable cancelable task", canBeCanceled, showProgress) {
@Override
public void run(@NotNull final ProgressIndicator indicator) {
final ProgressMonitorAdapter adapter = new ProgressMonitorAdapter(indicator);
SRepository repository = event.getData(MPSCommonDataKeys.MPS_PROJECT).getRepository();
adapter.start("Progress in progress...", 4);
int stepValue = 1;
// a normal step
adapter.step("Do simple work...");
BackgroundableProgressAction_Action.this.doWork(event);
adapter.advance(stepValue);
// Check if the progress is canceled after each step
if (adapter.isCanceled()) {
return;
}
// ReadAction in step is ok
repository.getModelAccess().runReadAction(new Runnable() {
public void run() {
adapter.step("Do some work with Read Lock...");
BackgroundableProgressAction_Action.this.doWork(event);
}
});
adapter.advance(stepValue);
if (adapter.isCanceled()) {
return;
}
// WriteAction in step is ok
repository.getModelAccess().runWriteAction(new Runnable() {
public void run() {
adapter.step("Do some work with Write Lock...");
BackgroundableProgressAction_Action.this.doWork(event);
}
});
adapter.advance(stepValue);
if (adapter.isCanceled()) {
return;
}
adapter.step("Finishing...");
BackgroundableProgressAction_Action.this.doWork(event);
adapter.advance(stepValue);
if (adapter.isCanceled()) {
return;
}
adapter.done();
}
@Override
public void onCancel() {
super.onCancel();
// Needs to handle reverting changes for all the finished steps
// This method does not interrupt the steps - steps must be either short or have such interruption capability
}
};
// The execute() method of actions must be very quick
// so every long calculation must be invoked outside of this method like this:
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
ProgressManager.getInstance().run(backgroundable);
}
});
}
private void doWork(final AnActionEvent event) {
// 42 because it is ultimate answer to everything =)
BackgroundableProgressAction_Action.this.fib(44, event);
}
private int fib(int n, final AnActionEvent event) {
// Very ineffective implementation with exponential time complexity
if (n < 1) {
throw new IllegalArgumentException();
}
if (n == 1) {
return 0;
} else if (n == 2 || n == 3) {
return 1;
}
return BackgroundableProgressAction_Action.this.fib(n - 1, event) + BackgroundableProgressAction_Action.this.fib(n - 2, event);
}
}