package org.python.pydev.debug.views;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.progress.UIJob;
import org.python.pydev.debug.model.PyDebugModelPresentation;
import org.python.pydev.shared_ui.utils.RunInUiThread;
/**
* Base class for some views that are debug-related and have a tree to show contents.
*/
public abstract class BaseDebugView extends ViewPart {
/**
* Note: not using a PyFilteredTree because filtering debug views can get recursive as structures in the debugger
* may be recursive.
*/
protected TreeViewer viewer;
protected ProgressBar progressBar;
protected Composite parent;
protected ITreeContentProvider provider;
private ILaunchAndDebugListener listener;
@Override
public void createPartControl(Composite parent) {
IViewSite viewSite = getViewSite();
if (viewSite != null) {
configureToolBar(viewSite);
}
parent.setLayout(new GridLayout(1, true));
viewer = new TreeViewer(parent);
provider = createContentProvider();
viewer.setContentProvider(provider);
viewer.setLabelProvider(new PyDebugModelPresentation(false));
GridDataFactory.fillDefaults().grab(true, true).applyTo(viewer.getTree());
MenuManager menuManager = new MenuManager();
Menu menu = menuManager.createContextMenu(viewer.getTree());
viewer.getTree().setMenu(menu);
IWorkbenchPartSite site = getSite();
site.registerContextMenu(menuManager, viewer);
site.setSelectionProvider(viewer);
this.parent = parent;
listener = createListener();
if (listener != null) {
DebugPlugin plugin = DebugPlugin.getDefault();
ILaunchManager launchManager = plugin.getLaunchManager();
launchManager.addLaunchListener(listener);
plugin.addDebugEventListener(listener);
}
}
/**
* The content provider to provide contents for our tree.
*/
protected abstract ITreeContentProvider createContentProvider();
/**
* A listener which will be added when the view is created and will be removed when the view is disposed.
*
* Usually this listener will be responsible for cleaning things up when the debug launch is terminated.
*/
protected abstract ILaunchAndDebugListener createListener();
/**
* Should be overridden to add actions to the toolbar/menubar.
*/
protected abstract void configureToolBar(IViewSite viewSite);
@Override
public void dispose() {
super.dispose();
if (listener != null) {
DebugPlugin plugin = DebugPlugin.getDefault();
ILaunchManager launchManager = plugin.getLaunchManager();
launchManager.removeLaunchListener(listener);
plugin.removeDebugEventListener(listener);
}
this.clear();
}
/**
* Set focus to our tree.
*/
@Override
public void setFocus() {
if (viewer == null || viewer.getTree().isDisposed()) {
return;
}
viewer.getTree().setFocus();
}
public void clear() {
updateTreeJob.schedule();
}
protected final Job updateTreeJob = new UIJob("Update PyDev Debug view") {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
if (viewer.getTree().isDisposed()) {
return Status.OK_STATUS;
}
onSetTreeInput();
RunInUiThread.async(new Runnable() {
@Override
public void run() {
if (viewer != null && viewer.getTree() != null && !viewer.getTree().isDisposed()) {
Object input = viewer.getInput();
makeLastVisibleInTree(input);
}
}
});
return Status.OK_STATUS;
}
};
/**
* Subclasses should override to make the last item added in the tree visible. input is the tree input.
*/
protected abstract void makeLastVisibleInTree(Object input);
/**
* Subclasses must override to set the input of the tree.
*/
protected abstract void onSetTreeInput();
// Progress bar handling -------------------------------------------------------------------------------------------
private int inProgress = 0;
private Object progressLock = new Object();
/**
* Finishes showing the progress bar in the view.
*/
protected void endProgress() {
synchronized (progressLock) {
inProgress -= 1;
}
updateProgressBarJob.schedule(); //Dispose ASAP
}
/**
* Starts to show an 'unknown' progress bar in the view.
*/
protected void startProgress() {
synchronized (progressLock) {
inProgress += 1;
}
updateProgressBarJob.schedule(600); //Wait a bit before creating the progress bar
}
Job updateProgressBarJob = new UIJob("Update Referrers view") {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
synchronized (progressLock) {
if (inProgress > 0) {
if (progressBar == null || progressBar.isDisposed()) {
progressBar = new ProgressBar(parent, SWT.INDETERMINATE | SWT.SMOOTH);
GridDataFactory.fillDefaults().grab(true, false).applyTo(progressBar);
parent.layout(true);
final Display display = Display.getCurrent();
display.timerExec(100, new Runnable() {
int i = 0;
@Override
public void run() {
if (progressBar == null || progressBar.isDisposed()) {
return;
}
progressBar.setSelection(i++);
display.timerExec(100, this);
}
});
}
} else {
if (progressBar != null && !progressBar.isDisposed()) {
progressBar.dispose();
progressBar = null;
parent.layout(true);
}
}
}
return Status.OK_STATUS;
}
};
}