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.Task; import com.intellij.openapi.progress.ProgressIndicator; import jetbrains.mps.progress.ProgressMonitorAdapter; import org.jetbrains.mps.openapi.module.SRepository; import com.intellij.util.WaitForProgressToShow; import com.intellij.openapi.application.ModalityState; import java.util.concurrent.CyclicBarrier; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.progress.ProgressManager; import java.util.concurrent.BrokenBarrierException; public class ModalProgressAction_Action extends BaseAction { private static final Icon ICON = null; public ModalProgressAction_Action() { super("ModalProgressAction", "", 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; // This is a common modal task. It can't be sent to the background, but can be canceled // 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.Modal modalTask = new Task.Modal(event.getData(CommonDataKeys.PROJECT), "Modal cancelable task", canBeCanceled) { @Override public void run(@NotNull final ProgressIndicator indicator) { final ProgressMonitorAdapter adapter = new ProgressMonitorAdapter(indicator); final SRepository repository = event.getData(MPSCommonDataKeys.MPS_PROJECT).getRepository(); adapter.start("Progress in progress...", 9); int stepValue = 1; // a normal step adapter.step("Do simple work..."); ModalProgressAction_Action.this.doWork(event); adapter.advance(stepValue); // Check if progress is canceled 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..."); ModalProgressAction_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..."); ModalProgressAction_Action.this.doWork(event); } }); adapter.advance(stepValue); if (adapter.isCanceled()) { return; } adapter.step("Now will try to do some work with Locks in EDT..."); ModalProgressAction_Action.this.doWork(event); adapter.advance(stepValue); if (adapter.isCanceled()) { return; } // The correct way to call command with progress is as follows // The dialog might not show up if the method for the usual read & write locks are used adapter.step("Do some work in command in EDT..."); WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(new Runnable() { public void run() { repository.getModelAccess().executeCommand(new Runnable() { public void run() { ModalProgressAction_Action.this.doWork(event); } }); } }, ModalityState.defaultModalityState()); adapter.advance(stepValue); if (adapter.isCanceled()) { return; } adapter.step("Do some work with Read Lock in EDT using IDEA API..."); WaitForProgressToShow.runOrInvokeAndWaitAboveProgress(new Runnable() { public void run() { repository.getModelAccess().runReadAction(new Runnable() { public void run() { ModalProgressAction_Action.this.doWork(event); } }); } }); adapter.advance(stepValue); if (adapter.isCanceled()) { return; } final CyclicBarrier barrier = new CyclicBarrier(2); adapter.step("Do some work with Read Lock in EDT using jdk..."); repository.getModelAccess().runReadInEDT(new Runnable() { public void run() { ModalProgressAction_Action.this.doWork(event); ModalProgressAction_Action.this.block(barrier, event); } }); ModalProgressAction_Action.this.block(barrier, event); adapter.advance(stepValue); if (adapter.isCanceled()) { return; } // Any EDT access lock brokes normal progress behaviour adapter.step("Do some work with Write Lock in EDT using jdk..."); repository.getModelAccess().runWriteInEDT(new Runnable() { public void run() { ModalProgressAction_Action.this.doWork(event); ModalProgressAction_Action.this.block(barrier, event); } }); ModalProgressAction_Action.this.block(barrier, event); adapter.advance(stepValue); if (adapter.isCanceled()) { return; } // Any EDT access lock brokes normal progress behaviour ModalProgressAction_Action.this.doWork(event); adapter.step("Finishing..."); ModalProgressAction_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(modalTask); } }); } private void block(CyclicBarrier barrier, final AnActionEvent event) { try { barrier.await(); } catch (BrokenBarrierException e) { } catch (InterruptedException e) { } } private void doWork(final AnActionEvent event) { // 42 because it is ultimate answer to everything =) ModalProgressAction_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 ModalProgressAction_Action.this.fib(n - 1, event) + ModalProgressAction_Action.this.fib(n - 2, event); } }