blob: 275015c59a00bfba97be057cc3e9d802894baff0 [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.xdebugger.impl.frame;
import com.intellij.ide.CommonActionsManager;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.CaptionPanel;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.border.CustomLineBorder;
import com.intellij.ui.components.panels.Wrapper;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.frame.XExecutionStack;
import com.intellij.xdebugger.frame.XStackFrame;
import com.intellij.xdebugger.frame.XSuspendContext;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.*;
import java.util.List;
/**
* @author nik
*/
public class XFramesView implements XDebugView {
private final JPanel myMainPanel;
private final XDebuggerFramesList myFramesList;
private final JComboBox myThreadComboBox;
private final Set<XExecutionStack> myExecutionStacks = ContainerUtil.newHashSet();
@NotNull private final XDebugSession mySession;
private XExecutionStack mySelectedStack;
private boolean myListenersEnabled;
private final Map<XExecutionStack, StackFramesListBuilder> myBuilders = new HashMap<XExecutionStack, StackFramesListBuilder>();
private final ActionToolbarImpl myToolbar;
private final Wrapper myThreadsPanel;
public XFramesView(@NotNull final XDebugSession session) {
mySession = session;
myMainPanel = new JPanel(new BorderLayout());
myFramesList = new XDebuggerFramesList(session.getProject());
myFramesList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(final ListSelectionEvent e) {
if (e.getValueIsAdjusting()) return;
processFrameSelection();
}
});
myFramesList.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(final MouseEvent e) {
int i = myFramesList.locationToIndex(e.getPoint());
if (i != -1 && myFramesList.isSelectedIndex(i)) {
processFrameSelection();
}
}
});
myMainPanel.add(ScrollPaneFactory.createScrollPane(myFramesList), BorderLayout.CENTER);
myThreadComboBox = new JComboBox();
//noinspection unchecked
myThreadComboBox.setRenderer(new ThreadComboBoxRenderer(myThreadComboBox));
myThreadComboBox.addItemListener(new MyItemListener());
myToolbar = createToolbar();
myThreadsPanel = new Wrapper();
CustomLineBorder border = new CustomLineBorder(CaptionPanel.CNT_ACTIVE_BORDER_COLOR, 0, 0, 1, 0);
myThreadsPanel.setBorder(border);
myThreadsPanel.add(myToolbar.getComponent(), BorderLayout.EAST);
myMainPanel.add(myThreadsPanel, BorderLayout.NORTH);
processSessionEvent(SessionEvent.RESUMED);
}
private ActionToolbarImpl createToolbar() {
final DefaultActionGroup framesGroup = new DefaultActionGroup();
CommonActionsManager actionsManager = CommonActionsManager.getInstance();
framesGroup.add(actionsManager.createPrevOccurenceAction(getFramesList()));
framesGroup.add(actionsManager.createNextOccurenceAction(getFramesList()));
final ActionToolbarImpl toolbar =
(ActionToolbarImpl)ActionManager.getInstance().createActionToolbar(ActionPlaces.DEBUGGER_TOOLBAR, framesGroup, true);
toolbar.setReservePlaceAutoPopupIcon(false);
toolbar.setAddSeparatorFirst(true);
toolbar.getComponent().setBorder(new EmptyBorder(1, 0, 0, 0));
return toolbar;
}
private StackFramesListBuilder getOrCreateBuilder(XExecutionStack executionStack) {
StackFramesListBuilder builder = myBuilders.get(executionStack);
if (builder == null) {
builder = new StackFramesListBuilder(executionStack);
myBuilders.put(executionStack, builder);
}
return builder;
}
@Override
public void processSessionEvent(@NotNull final SessionEvent event) {
if (event == SessionEvent.BEFORE_RESUME) return;
if (event == SessionEvent.FRAME_CHANGED) {
XStackFrame currentStackFrame = mySession.getCurrentStackFrame();
if (currentStackFrame != null) {
myFramesList.setSelectedValue(currentStackFrame, true);
}
return;
}
myListenersEnabled = false;
for (StackFramesListBuilder builder : myBuilders.values()) {
builder.dispose();
}
myBuilders.clear();
mySelectedStack = null;
XSuspendContext suspendContext = mySession.getSuspendContext();
if (suspendContext == null) {
myThreadComboBox.removeAllItems();
myFramesList.clear();
myExecutionStacks.clear();
return;
}
XExecutionStack[] executionStacks = suspendContext.getExecutionStacks();
for (XExecutionStack executionStack : executionStacks) {
if (!myExecutionStacks.contains(executionStack)) {
//noinspection unchecked
myThreadComboBox.addItem(executionStack);
myExecutionStacks.add(executionStack);
}
}
XExecutionStack activeExecutionStack = suspendContext.getActiveExecutionStack();
myThreadComboBox.setSelectedItem(activeExecutionStack);
myThreadsPanel.removeAll();
myThreadsPanel.add(myToolbar.getComponent(), BorderLayout.EAST);
final boolean invisible = executionStacks.length == 1 && StringUtil.isEmpty(executionStacks[0].getDisplayName());
if (!invisible) {
myThreadsPanel.add(myThreadComboBox, BorderLayout.CENTER);
}
myToolbar.setAddSeparatorFirst(!invisible);
updateFrames(activeExecutionStack);
myListenersEnabled = true;
}
private void updateFrames(final XExecutionStack executionStack) {
if (mySelectedStack == executionStack) {
return;
}
if (mySelectedStack != null) {
getOrCreateBuilder(mySelectedStack).stop();
}
mySelectedStack = executionStack;
if (executionStack != null) {
StackFramesListBuilder builder = getOrCreateBuilder(executionStack);
builder.initModel(myFramesList.getModel());
builder.start();
XStackFrame topFrame = executionStack.getTopFrame();
if (topFrame != null) {
myFramesList.setSelectedValue(topFrame, true);
onFrameSelected(executionStack, topFrame);
}
}
}
@Override
public void dispose() {
}
public XDebuggerFramesList getFramesList() {
return myFramesList;
}
private void onFrameSelected(XExecutionStack executionStack, final @NotNull XStackFrame stackFrame) {
mySession.setCurrentStackFrame(executionStack, stackFrame);
}
public JPanel getMainPanel() {
return myMainPanel;
}
private void processFrameSelection() {
if (!myListenersEnabled) return;
Object selected = myFramesList.getSelectedValue();
if (selected instanceof XStackFrame) {
onFrameSelected(mySelectedStack, (XStackFrame)selected);
}
}
private class MyItemListener implements ItemListener {
@Override
public void itemStateChanged(final ItemEvent e) {
if (!myListenersEnabled) return;
if (e.getStateChange() == ItemEvent.SELECTED) {
Object item = e.getItem();
if (item instanceof XExecutionStack) {
updateFrames((XExecutionStack)item);
}
}
}
}
private class StackFramesListBuilder implements XExecutionStack.XStackFrameContainer {
private XExecutionStack myExecutionStack;
private final List<XStackFrame> myStackFrames;
private String myErrorMessage;
private int myNextFrameIndex;
private boolean myRunning;
private boolean myAllFramesLoaded;
private StackFramesListBuilder(final XExecutionStack executionStack) {
myExecutionStack = executionStack;
myStackFrames = new ArrayList<XStackFrame>();
XStackFrame topFrame = executionStack.getTopFrame();
if (topFrame != null) {
myStackFrames.add(topFrame);
}
myNextFrameIndex = 1;
}
@Override
public void addStackFrames(@NotNull final List<? extends XStackFrame> stackFrames, final boolean last) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
myStackFrames.addAll(stackFrames);
addFrameListElements(stackFrames, last);
myNextFrameIndex += stackFrames.size();
myAllFramesLoaded = last;
if (last) {
myRunning = false;
}
}
});
}
@Override
public void errorOccurred(@NotNull final String errorMessage) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
if (myErrorMessage == null) {
myErrorMessage = errorMessage;
addFrameListElements(Collections.singletonList(errorMessage), true);
myRunning = false;
}
}
});
}
private void addFrameListElements(final List<?> values, final boolean last) {
if (myExecutionStack != null && myExecutionStack == mySelectedStack) {
DefaultListModel model = myFramesList.getModel();
if (model.getElementAt(model.getSize() - 1) == null) {
model.removeElementAt(model.getSize() - 1);
}
for (Object value : values) {
//noinspection unchecked
model.addElement(value);
}
if (!last) {
//noinspection unchecked
model.addElement(null);
}
myFramesList.repaint();
}
}
@Override
public boolean isObsolete() {
return !myRunning;
}
public void dispose() {
myRunning = false;
myExecutionStack = null;
}
public void start() {
if (myExecutionStack == null || myErrorMessage != null) {
return;
}
myRunning = true;
myExecutionStack.computeStackFrames(myNextFrameIndex, this);
}
public void stop() {
myRunning = false;
}
@SuppressWarnings("unchecked")
public void initModel(final DefaultListModel model) {
model.removeAllElements();
for (XStackFrame stackFrame : myStackFrames) {
model.addElement(stackFrame);
}
if (myErrorMessage != null) {
model.addElement(myErrorMessage);
}
else if (!myAllFramesLoaded) {
model.addElement(null);
}
}
}
}