blob: e3d67a7cc3cbf1de52ab631fa155b1ddf6b2beb4 [file] [log] [blame]
package org.eclipse.debug.internal.ui.views;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import java.util.HashMap;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchListener;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IExpression;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.core.model.ISuspendResume;
import org.eclipse.debug.core.model.IThread;
import org.eclipse.debug.core.model.IValue;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
/**
* Handles debug events, updating the launch view and viewer.
*/
public class LaunchViewEventHandler extends AbstractDebugEventHandler implements ILaunchListener{
/**
* Stack frame counts keyed by thread. Used to optimize thread refreshing.
*/
private HashMap fStackFrameCountByThread = new HashMap(5);
/**
* Constructs an event handler for the given launch
* view and viewer.
*
* @param view launch view
* @param viewer launch viewer
*/
public LaunchViewEventHandler(LaunchView view, LaunchViewer viewer) {
super(view, viewer);
DebugPlugin plugin= DebugPlugin.getDefault();
plugin.getLaunchManager().addLaunchListener(this);
}
/**
* @see AbstractDebugEventHandler#doHandleDebugEvent(DebugEvent)
*/
protected void doHandleDebugEvent(DebugEvent event) {
Object element= event.getSource();
if (element instanceof IVariable || element instanceof IValue || element instanceof IExpression) {
// the debug view does not show variables
return;
}
switch (event.getKind()) {
case DebugEvent.CREATE :
insert(element);
break;
case DebugEvent.TERMINATE :
if (element instanceof IThread) {
clearSourceSelection((IThread)element);
fStackFrameCountByThread.remove(element);
remove(element);
} else {
clearSourceSelection(null);
Object parent = ((ITreeContentProvider)getTreeViewer().getContentProvider()).getParent(element);
refresh(parent);
}
break;
case DebugEvent.RESUME :
doHandleResumeEvent(event, element);
break;
case DebugEvent.SUSPEND :
doHandleSuspendEvent(element, event);
break;
case DebugEvent.CHANGE :
refresh(element);
break;
}
}
protected void doHandleResumeEvent(DebugEvent event, Object element) {
if (event.isEvaluation()) {
// do nothing when an evaluation begins
return;
}
if (element instanceof ISuspendResume) {
if (((ISuspendResume)element).isSuspended()) {
IThread thread = getThread(element);
if (thread != null) {
resetStackFrameCount(thread);
}
return;
}
}
clearSourceSelection(null);
if (event.isStepStart()) {
IThread thread = getThread(element);
if (thread != null) {
getLaunchViewer().updateStackFrameIcons(thread);
resetStackFrameCount(thread);
}
} else {
refresh(element);
if (element instanceof IThread) {
selectAndReveal(element);
resetStackFrameCount((IThread)element);
return;
}
}
labelChanged(element);
}
protected void resetStackFrameCount(IThread thread) {
fStackFrameCountByThread.put(thread, new Integer(0));
}
protected void doHandleSuspendEvent(Object element, DebugEvent event) {
if (element instanceof IThread) {
if (!event.isEvaluation()) {
doHandleSuspendThreadEvent((IThread)element);
return;
}
}
refresh(element);
}
// This method exists to provide some optimization for refreshing suspended
// threads. If the number of stack frames has changed from the last suspend,
// we do a full refresh on the thread. Otherwise, we only do a surface-level
// refresh on the thread, then refresh each of the children, which eliminates
// flicker when do quick stepping (e.g., holding down the F6 key) within a method.
protected void doHandleSuspendThreadEvent(IThread thread) {
// if the thread has already resumed, do nothing
if (!thread.isSuspended()) {
return;
}
// Get the current stack frames from the thread
IStackFrame[] stackFrames = null;
try {
stackFrames = thread.getStackFrames();
} catch (DebugException de) {
DebugUIPlugin.logError(de);
return;
}
int currentStackFrameCount = stackFrames.length;
// Retrieve the old and current counts of stack frames for this thread
int oldStackFrameCount = 0;
Integer oldStackFrameCountObject = (Integer)fStackFrameCountByThread.get(thread);
if (oldStackFrameCountObject != null) {
oldStackFrameCount = oldStackFrameCountObject.intValue();
}
// Compare old & current stack frame counts. We need to refresh the thread
// parent if there are fewer stack frame children
boolean refreshNeeded = true;
if (currentStackFrameCount == oldStackFrameCount) {
refreshNeeded = false;
}
// Auto-expand the thread. If we are also refreshing the thread,
// then we don't need to worry about any children, since refreshing
// the parent handles this
getLaunchView().autoExpand(thread, refreshNeeded, refreshNeeded);
if (refreshNeeded) {
// Update the stack frame count for the thread
oldStackFrameCountObject = new Integer(currentStackFrameCount);
fStackFrameCountByThread.put(thread, oldStackFrameCountObject);
return;
} else {
labelChanged(thread);
}
// Auto-expand each stack frame child. This has the effect of adding
// any new stack frames, and updating any existing stack frames
if ((stackFrames != null) && (currentStackFrameCount > 0)) {
for (int i = currentStackFrameCount - 1; i > 0; i--) {
getLaunchView().autoExpand(stackFrames[i], true, false);
}
// Treat the first stack frame differently, since we want to select it
getLaunchView().autoExpand(stackFrames[0], true, true);
}
// Update the stack frame count for the thread
oldStackFrameCountObject = new Integer(currentStackFrameCount);
fStackFrameCountByThread.put(thread, oldStackFrameCountObject);
}
/**
* @see ILaunchListener#launchRemoved(ILaunch)
*/
public void launchRemoved(final ILaunch launch) {
Runnable r= new Runnable() {
public void run() {
remove(launch);
ILaunchManager lm= DebugPlugin.getDefault().getLaunchManager();
IDebugTarget[] targets= lm.getDebugTargets();
if (targets.length > 0) {
IDebugTarget target= targets[targets.length - 1];
try {
IThread[] threads= target.getThreads();
for (int i=0; i < threads.length; i++) {
if (threads[i].isSuspended()) {
getLaunchView().autoExpand(threads[i], false, true);
return;
}
}
} catch (DebugException de) {
DebugUIPlugin.logError(de);
}
getLaunchView().autoExpand(target.getLaunch(), false, true);
}
}
};
getView().asyncExec(r);
}
/**
* @see ILaunchListener#launchChanged(ILaunch)
*/
public void launchChanged(ILaunch launch) {
}
/**
* @see ILaunchListener#launchAdded(ILaunch)
*/
public void launchAdded(final ILaunch newLaunch) {
Runnable r= new Runnable() {
public void run() {
removeTerminatedLaunches(newLaunch);
insert(newLaunch);
getLaunchView().autoExpand(newLaunch, false, true);
}
};
getView().syncExec(r);
}
protected void removeTerminatedLaunches(ILaunch newLaunch) {
if (DebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IDebugUIConstants.PREF_AUTO_REMOVE_OLD_LAUNCHES)) {
ILaunchManager lManager= DebugPlugin.getDefault().getLaunchManager();
Object[] launches= lManager.getLaunches();
for (int i= 0; i < launches.length; i++) {
ILaunch launch= (ILaunch)launches[i];
if (launch != newLaunch && launch.isTerminated()) {
lManager.removeLaunch(launch);
}
}
}
}
/**
* De-registers this event handler from the debug model.
*/
public void dispose() {
super.dispose();
DebugPlugin plugin= DebugPlugin.getDefault();
plugin.getLaunchManager().removeLaunchListener(this);
}
/**
* Clear the selection in the editor - must be called in UI thread
*/
private void clearSourceSelection(IThread thread) {
if (getViewer() != null) {
if (thread != null) {
IStructuredSelection selection= (IStructuredSelection)getLaunchViewer().getSelection();
Object element= selection.getFirstElement();
if (element instanceof IStackFrame) {
IStackFrame stackFrame = (IStackFrame) element;
if (!stackFrame.getThread().equals(thread)) {
//do not clear the source selection
//a thread has terminated that is not the
//parent of the currently selected stack frame
return;
}
}
}
getLaunchView().clearSourceSelection();
}
}
/**
* Returns this event handler's launch viewer
*
* @return launch viewer
*/
protected LaunchViewer getLaunchViewer() {
return (LaunchViewer)getViewer();
}
/**
* Returns this event handler's launch view
*
* @return launch view
*/
protected LaunchView getLaunchView() {
return (LaunchView)getView();
}
private IThread getThread(Object element) {
IThread thread = null;
if (element instanceof IThread) {
thread = (IThread) element;
} else if (element instanceof IStackFrame) {
thread = ((IStackFrame)element).getThread();
}
return thread;
}
}