package jetbrains.mps.debugger.core.breakpoints;
/*Generated by MPS */
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import jetbrains.mps.nodeEditor.LeftMarginMouseListener;
import com.intellij.util.messages.MessageBusConnection;
import jetbrains.mps.util.containers.MultiMap;
import jetbrains.mps.nodeEditor.highlighter.EditorComponentCreateListener;
import jetbrains.mps.RuntimeFlags;
import jetbrains.mps.nodeEditor.EditorComponent;
import jetbrains.mps.ide.editor.util.EditorComponentUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
import java.util.List;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.openapi.editor.cells.EditorCell;
import jetbrains.mps.textgen.trace.TraceInfo;
import org.jetbrains.mps.util.Condition;
import org.jetbrains.annotations.Nullable;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import com.intellij.openapi.application.ApplicationManager;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.awt.event.MouseEvent;
import jetbrains.mps.smodel.ModelAccess;
/**
*
*
* @param <B> breakpoint type
* @param <L> location breakpoint type
*/
public abstract class BreakpointsUiComponentEx<B, L extends B> {
protected final FileEditorManager myFileEditorManager;
protected final Project myProject;
private final LeftMarginMouseListener myMouseListener = new BreakpointsUiComponentEx.MyLeftMarginMouseListener();
private final BreakpointsUiComponentEx.MyEditorComponentCreateListener myEditorComponentCreationHandler = new BreakpointsUiComponentEx.MyEditorComponentCreateListener();
private MessageBusConnection myMessageBusConnection;
private final MultiMap<L, BreakpointIconRenderrerEx<L>> myBreakpointRender = new MultiMap<L, BreakpointIconRenderrerEx<L>>();
public BreakpointsUiComponentEx(Project project, FileEditorManager manager) {
myProject = project;
myFileEditorManager = manager;
}
public void init() {
myMessageBusConnection = myProject.getMessageBus().connect();
myMessageBusConnection.subscribe(EditorComponentCreateListener.EDITOR_COMPONENT_CREATION, myEditorComponentCreationHandler);
if (RuntimeFlags.isTestMode()) {
return;
}
for (EditorComponent editor : EditorComponentUtil.getAllEditorComponents(myFileEditorManager, true)) {
editorComponentCreated(editor);
}
}
public void dispose() {
myMessageBusConnection.disconnect();
myBreakpointRender.clear();
}
@NotNull
protected abstract Set<L> getBreakpointsForComponent(@NotNull EditorComponent component);
@NotNull
protected abstract List<EditorComponent> getComponentsForBreakpoint(@NotNull L breakpoint);
protected abstract BreakpointPainterEx<L> createPainter(L breakpoint);
protected abstract BreakpointIconRenderrerEx<L> createRenderrer(L breakpoint, EditorComponent component);
protected abstract void toggleBreakpoint(SNode node);
protected abstract EditorCell findDebuggableOrTraceableCell(EditorCell cell);
public void toggleBreakpoint(EditorCell cell) {
EditorCell debuggableCell = findDebuggableOrTraceableCell(cell);
if (debuggableCell != null) {
toggleBreakpoint(debuggableCell.getSNode());
}
}
public boolean isDebuggable(EditorCell cell) {
return findDebuggableOrTraceableCell(cell) != null;
}
protected EditorCell findTraceableCell(EditorCell foundCell) {
EditorCell cell = foundCell;
while (cell != null) {
SNode node = cell.getSNode();
if (TraceInfo.hasTrace(node)) {
break;
}
cell = cell.getParent();
}
return cell;
}
private SNode findDebuggableNode(final EditorComponent editorComponent, int x, final int y) {
EditorCell foundCell = editorComponent.getRootCell().findNearestLeafOnLine(x, y, new Condition<EditorCell>() {
@Override
public boolean met(EditorCell object) {
EditorCell debuggableOrTraceableCell = findDebuggableOrTraceableCell(object);
if (debuggableOrTraceableCell == null) {
return false;
}
// todo do we need to know about ui?
EditorCell iconAnchorCell = BreakpointIconRenderrerEx.getBreakpointIconAnchorCell(editorComponent.findNodeCell(debuggableOrTraceableCell.getSNode()));
// ignoring mouse clicks to any other rows except one containing "BreakpointIconAnchorCell"
// (this cell will be marked with breakpoint icon in LeftEditorHighlighter)
return iconAnchorCell != null && (y >= iconAnchorCell.getY() && iconAnchorCell.getBaseline() >= y);
}
});
if (foundCell == null) {
return null;
}
EditorCell cell = findDebuggableOrTraceableCell(foundCell);
if (cell == null) {
return null;
}
return cell.getSNode();
}
protected void editorComponentCreated(@Nullable EditorComponent editorComponent) {
if (editorComponent == null) {
return;
}
if (!(editorComponent.getLeftMarginPressListeners().contains(myMouseListener))) {
editorComponent.addLeftMarginPressListener(myMouseListener);
}
Set<L> breakpointsForRoot = getBreakpointsForComponent(editorComponent);
for (L breakpoint : SetSequence.fromSet(breakpointsForRoot)) {
editorComponent.addAdditionalPainter(createPainter(breakpoint));
BreakpointIconRenderrerEx<L> r = createRenderrer(breakpoint, editorComponent);
myBreakpointRender.putValue(breakpoint, r);
editorComponent.getLeftEditorHighlighter().addIconRenderer(r);
}
editorComponent.repaintExternalComponent();
}
protected void editorComponentDisposed(@Nullable EditorComponent editorComponent) {
if (editorComponent == null) {
return;
}
editorComponent.removeLeftMarginPressListener(myMouseListener);
Set<L> breakpointsForRoot = getBreakpointsForComponent(editorComponent);
for (L breakpoint : SetSequence.fromSet(breakpointsForRoot)) {
editorComponent.removeAdditionalPainterByItem(breakpoint);
editorComponent.getLeftEditorHighlighter().removeAllIconRenderers(myBreakpointRender.get(breakpoint));
}
// stale renderers may persist in myBreakpointRenderer, I have no means to identify EditorComponents to use them as additional key along with breakpoint itself
// I don't care as these stale renders are only for equals() match in removeAllIconsRenderers().
}
protected void addLocationBreakpoint(L breakpoint) {
for (EditorComponent editorComponent : getComponentsForBreakpoint(breakpoint)) {
editorComponent.addAdditionalPainter(createPainter(breakpoint));
BreakpointIconRenderrerEx<L> r = createRenderrer(breakpoint, editorComponent);
myBreakpointRender.putValue(breakpoint, r);
editorComponent.getLeftEditorHighlighter().addIconRenderer(r);
editorComponent.repaintExternalComponent();
}
}
protected void removeLocationBreakpoint(L breakpoint) {
for (EditorComponent editorComponent : getComponentsForBreakpoint(breakpoint)) {
editorComponent.removeAdditionalPainterByItem(breakpoint);
editorComponent.getLeftEditorHighlighter().removeAllIconRenderers(myBreakpointRender.get(breakpoint));
editorComponent.repaintExternalComponent();
}
myBreakpointRender.remove(breakpoint);
}
public void repaintBreakpoints() {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
List<EditorComponent> allEditorComponents = EditorComponentUtil.getAllEditorComponents(myFileEditorManager, true);
for (EditorComponent component : ListSequence.fromList(allEditorComponents)) {
component.repaintExternalComponent();
}
}
});
}
private class MyLeftMarginMouseListener implements LeftMarginMouseListener {
private MyLeftMarginMouseListener() {
}
@Override
public void mousePressed(MouseEvent e, EditorComponent editorComponent) {
}
@Override
public void mouseReleased(MouseEvent e, EditorComponent editorComponent) {
}
@Override
public void mouseClicked(final MouseEvent e, final EditorComponent editorComponent) {
if (e.getButton() == MouseEvent.BUTTON1) {
ModelAccess.instance().runReadAction(new Runnable() {
@Override
public void run() {
SNode node = findDebuggableNode(editorComponent, e.getX(), e.getY());
if (node != null) {
toggleBreakpoint(node);
}
}
});
}
}
}
private class MyEditorComponentCreateListener implements EditorComponentCreateListener {
private MyEditorComponentCreateListener() {
}
@Override
public void editorComponentCreated(@NotNull EditorComponent editorComponent) {
BreakpointsUiComponentEx.this.editorComponentCreated(editorComponent);
}
@Override
public void editorComponentDisposed(@NotNull EditorComponent editorComponent) {
BreakpointsUiComponentEx.this.editorComponentDisposed(editorComponent);
}
}
}