package jetbrains.mps.plugin; /*Generated by MPS */ import com.intellij.openapi.wm.StatusBarWidget; import jetbrains.mps.logging.Logger; import org.apache.log4j.LogManager; import com.intellij.openapi.project.Project; import java.util.concurrent.atomic.AtomicReference; import com.intellij.openapi.wm.StatusBar; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import com.intellij.openapi.application.ApplicationManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import com.intellij.util.Consumer; import java.awt.event.MouseEvent; import javax.swing.Icon; import jetbrains.mps.ide.ThreadUtils; import jetbrains.mps.icons.MPSIcons; import javax.swing.Timer; public class PluginStateWidget implements StatusBarWidget, StatusBarWidget.IconPresentation { private static final Logger LOG = Logger.wrap(LogManager.getLogger(PluginStateWidget.class)); private static final int INITIAL_DELAY = 4000; private static final int CRITICAL_DELAY = 16000; private static final double DELAY_MUL = 2.0; private final Project myProject; private final PluginStateWidget.MyTimer myTimer; private AtomicReference<PluginStateWidget.State> myState = new AtomicReference<PluginStateWidget.State>(PluginStateWidget.State.TRYING_TO_CONNECT); private volatile boolean myConnecting = false; private StatusBar myStatusBar; public PluginStateWidget(Project project) { myProject = project; myTimer = new PluginStateWidget.MyTimer(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (myConnecting) { // too complicated to do stuff here // lets not write sophisticated code, it is not worth it :( // another possibility to consider is use the Future that executeOnPooledThread returnes and when the next one wants to start just interrupt the old one // but I'm not sure, what happens when we interrupt a thread in a middle of rmi call // according to the stack trace ordinary io is used // so I'm guessing that just the thread interrupted state is set return; } myConnecting = true; try { ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { @Override public void run() { try { tick(); } finally { myConnecting = false; } } }); } catch (Throwable t) { LOG.error(t); myConnecting = false; } } }); } @Override public void install(@NotNull StatusBar bar) { myStatusBar = bar; myTimer.start(); } @Nullable @Override public Consumer<MouseEvent> getClickConsumer() { return new Consumer<MouseEvent>() { @Override public void consume(MouseEvent event) { if (myState.get() == PluginStateWidget.State.DISCONNECTED) { if (setNewState(PluginStateWidget.State.DISCONNECTED, PluginStateWidget.State.TRYING_TO_CONNECT)) { myTimer.setNewDelay(PluginStateWidget.INITIAL_DELAY); } } } }; } @Nullable @Override public StatusBarWidget.WidgetPresentation getPresentation(@NotNull StatusBarWidget.PlatformType type) { return this; } @Override public void dispose() { if (myTimer.isRunning()) { myTimer.stop(); } } @Nullable @Override public String getTooltipText() { return myState.get().getHelpText(); } @NotNull @Override public Icon getIcon() { return myState.get().getIcon(); } @NotNull @Override public String ID() { return "MpsPluginStateMonitor"; } private void tick() { LOG.assertLog(!(ThreadUtils.isInEDT()), "You should not do this in EDT"); tickImpl(); } private void tickImpl() { PluginStateWidget.State state = myState.get(); if (state == PluginStateWidget.State.CONNECTED) { if (isConnected()) { if (canOperate()) { return; } else { setNewState(state, PluginStateWidget.State.CONNECTED_BAD_PROJECT); } } else { setNewState(state, PluginStateWidget.State.TRYING_TO_CONNECT); } } else if (state == PluginStateWidget.State.CONNECTED_BAD_PROJECT) { if (isConnected()) { if (canOperate()) { setNewState(state, PluginStateWidget.State.CONNECTED); } } else { setNewState(state, PluginStateWidget.State.TRYING_TO_CONNECT); } } else if (state == PluginStateWidget.State.DISCONNECTED) { if (MPSPlugin.getInstance().openConnectionPresent()) { if (isConnected()) { if (canOperate()) { setNewState(state, PluginStateWidget.State.CONNECTED); } else { setNewState(state, PluginStateWidget.State.CONNECTED_BAD_PROJECT); } } } } else if (state == PluginStateWidget.State.TRYING_TO_CONNECT) { if (isConnected()) { if (canOperate()) { setNewState(state, PluginStateWidget.State.CONNECTED); } else { setNewState(state, PluginStateWidget.State.CONNECTED_BAD_PROJECT); } } else { int newDelay = (int) (myTimer.getDelay() * DELAY_MUL); if (newDelay <= CRITICAL_DELAY) { myTimer.setNewDelay(newDelay); } else { setNewState(state, PluginStateWidget.State.DISCONNECTED); } } } } private boolean setNewState(PluginStateWidget.State oldState, PluginStateWidget.State newState) { if (myState.compareAndSet(oldState, newState)) { myTimer.setNewDelay(myState.get().getDefaultDelay()); ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { myStatusBar.updateWidget(ID()); } }); return true; } return false; } private boolean isConnected() { return MPSPlugin.getInstance().isIDEAPresent(); } private boolean canOperate() { return MPSPlugin.getInstance().getProjectHandler(myProject.getBasePath()) != null; } private enum State { DISCONNECTED(MPSIcons.IdeaIntegration.Disconnected, "Not connected to IDEA. Click to reconnect.", PluginStateWidget.INITIAL_DELAY), TRYING_TO_CONNECT(MPSIcons.IdeaIntegration.TryingToConnect, "Connecting to IDEA...", PluginStateWidget.INITIAL_DELAY), CONNECTED_BAD_PROJECT(MPSIcons.IdeaIntegration.ConnectedWithErrors, "Connected to IDEA, Project does not match", PluginStateWidget.CRITICAL_DELAY), CONNECTED(MPSIcons.IdeaIntegration.Connected, "Connected to IDEA", PluginStateWidget.INITIAL_DELAY); private final Icon myIcon; private final String myHelpText; private final int myDefaultDelay; private State(Icon icon, String helpText, int defaultDelay) { myIcon = icon; myHelpText = helpText; myDefaultDelay = defaultDelay; } public Icon getIcon() { return myIcon; } public String getHelpText() { return myHelpText; } public int getDefaultDelay() { return myDefaultDelay; } } private static class MyTimer extends Timer { public MyTimer(ActionListener listener) { super(PluginStateWidget.INITIAL_DELAY, listener); } public void setNewDelay(int delay) { setDelay(delay); setInitialDelay(delay); restart(); } } }