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();
}
}
}