blob: 1f3e869cf38a89e9162aa9339adf7d77c3ec76b2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2017 xored software, Inc. and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* xored software, Inc. - initial API and Implementation (Alex Panchenko)
*******************************************************************************/
package org.eclipse.dltk.debug.ui.display;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.dltk.debug.ui.DLTKDebugUIPlugin;
import org.eclipse.dltk.debug.ui.display.internal.ConsoleDropDownAction;
import org.eclipse.dltk.debug.ui.display.internal.ConsoleWorkbenchPart;
import org.eclipse.dltk.debug.ui.display.internal.EvaluateConsoleFactoryManager;
import org.eclipse.dltk.debug.ui.display.internal.OpenConsoleAction;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.IBasicPropertyConstants;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.console.AbstractConsole;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleConstants;
import org.eclipse.ui.console.IConsolePageParticipant;
import org.eclipse.ui.console.IConsoleView;
import org.eclipse.ui.contexts.IContextActivation;
import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.internal.console.ConsoleMessages;
import org.eclipse.ui.internal.console.PinConsoleAction;
import org.eclipse.ui.internal.console.ShowConsoleAction;
import org.eclipse.ui.part.IPage;
import org.eclipse.ui.part.IPageBookViewPage;
import org.eclipse.ui.part.MessagePage;
import org.eclipse.ui.part.PageBook;
import org.eclipse.ui.part.PageBookView;
import org.eclipse.ui.part.PageSwitcher;
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
public class ScriptDisplayView extends PageBookView
implements IConsoleView, IPropertyChangeListener, IPartListener2 {
/**
* Whether this console is pinned.
*/
private boolean fPinned = false;
/**
* Stack of consoles in MRU order
*/
private List<IConsole> fStack = new ArrayList<>();
/**
* The console being displayed, or <code>null</code> if none
*/
private IConsole fActiveConsole = null;
/**
* Map of consoles to dummy console parts (used to close pages)
*/
private Map<IConsole, ConsoleWorkbenchPart> fConsoleToPart;
/**
* Map of consoles to array of page participants
*/
private Map<IConsole, ListenerList<IConsolePageParticipant>> fConsoleToPageParticipants;
/**
* Map of parts to consoles
*/
private Map<ConsoleWorkbenchPart, IConsole> fPartToConsole;
/**
* Whether this view is active
*/
private boolean fActive = false;
/**
* 'In Console View' context
*/
private IContextActivation fActivatedContext;
// actions
private PinConsoleAction fPinAction = null;
private ConsoleDropDownAction fDisplayConsoleAction = null;
private OpenConsoleAction fOpenConsoleAction = null;
private boolean fScrollLock;
private boolean isAvailable() {
return getPageBook() != null && !getPageBook().isDisposed();
}
@Override
public boolean getWordWrap() {
return false;
}
@Override
public void setWordWrap(boolean wordWrap) {
}
@Override
public void propertyChange(PropertyChangeEvent event) {
Object source = event.getSource();
if (source instanceof IConsole
&& event.getProperty().equals(IBasicPropertyConstants.P_TEXT)) {
if (source.equals(getConsole())) {
updateTitle();
}
}
}
@Override
public void partClosed(IWorkbenchPart part) {
super.partClosed(part);
fPinAction.update();
}
@Override
public IConsole getConsole() {
return fActiveConsole;
}
@Override
protected void showPageRec(PageRec pageRec) {
// don't show the page when pinned, unless this is the first console to
// be added
// or its the default page
if (fActiveConsole != null && pageRec.page != getDefaultPage()
&& fPinned && fConsoleToPart.size() > 1) {
IConsole console = fPartToConsole.get(pageRec.part);
if (!fStack.contains(console)) {
fStack.add(console);
}
return;
}
IConsole recConsole = fPartToConsole.get(pageRec.part);
if (recConsole != null && recConsole.equals(fActiveConsole)) {
return;
}
super.showPageRec(pageRec);
fActiveConsole = recConsole;
IConsole tos = null;
if (!fStack.isEmpty()) {
tos = fStack.get(0);
}
if (tos != null && !tos.equals(fActiveConsole)) {
deactivateParticipants(tos);
}
if (fActiveConsole != null && !fActiveConsole.equals(tos)) {
fStack.remove(fActiveConsole);
fStack.add(0, fActiveConsole);
activateParticipants(fActiveConsole);
}
updateTitle();
updateHelp();
// update console actions
if (fPinAction != null) {
fPinAction.update();
}
IPage page = getCurrentPage();
// if (page instanceof IOConsolePage) {
// ((IOConsolePage) page).setAutoScroll(!fScrollLock);
// }
}
/**
* Activates the participants for the given console, if any.
*
* @param console
*/
private void activateParticipants(IConsole console) {
// activate
if (console != null && fActive) {
final ListenerList<IConsolePageParticipant> listeners = getParticipants(
console);
if (listeners != null) {
for (final IConsolePageParticipant participant : listeners) {
SafeRunner.run(new ISafeRunnable() {
@Override
public void run() throws Exception {
participant.activated();
}
@Override
public void handleException(Throwable exception) {
DLTKDebugUIPlugin.log(exception);
listeners.remove(participant);
}
});
}
}
}
}
/**
* Returns a stack of consoles in the view in MRU order.
*
* @return a stack of consoles in the view in MRU order
*/
public List<IConsole> getConsoleStack() {
return fStack;
}
/**
* Updates the view title based on the active console
*/
protected void updateTitle() {
IConsole console = getConsole();
if (console == null) {
setContentDescription(ConsoleMessages.ConsoleView_0);
} else {
String newName = console.getName();
String oldName = getContentDescription();
if (newName != null && !(newName.equals(oldName))) {
setContentDescription(console.getName());
}
}
}
protected void updateHelp() {
IConsole console = getConsole();
String helpContextId = null;
if (console instanceof AbstractConsole) {
AbstractConsole abs = (AbstractConsole) console;
helpContextId = abs.getHelpContextId();
}
if (helpContextId == null) {
// helpContextId = IConsoleHelpContextIds.CONSOLE_VIEW;
}
// PlatformUI.getWorkbench().getHelpSystem()
// .setHelp(getPageBook().getParent(), helpContextId);
}
@Override
protected void doDestroyPage(IWorkbenchPart part, PageRec pageRecord) {
IConsole console = fPartToConsole.get(part);
// dispose page participants
ListenerList<IConsolePageParticipant> listeners = fConsoleToPageParticipants
.remove(console);
if (listeners != null) {
for (final IConsolePageParticipant participant : listeners) {
SafeRunner.run(new ISafeRunnable() {
@Override
public void run() throws Exception {
participant.dispose();
}
@Override
public void handleException(Throwable exception) {
DLTKDebugUIPlugin.log(exception);
}
});
}
}
IPage page = pageRecord.page;
page.dispose();
pageRecord.dispose();
console.removePropertyChangeListener(this);
if (console instanceof AbstractConsole)
((AbstractConsole) console).destroy();
// empty cross-reference cache
fPartToConsole.remove(part);
fConsoleToPart.remove(console);
if (fPartToConsole.isEmpty()) {
fActiveConsole = null;
}
// update console actions
fPinAction.update();
}
/**
* Returns the page participants registered for the given console, or
* <code>null</code>
*
* @param console
* @return registered page participants or <code>null</code>
*/
private ListenerList<IConsolePageParticipant> getParticipants(
IConsole console) {
return fConsoleToPageParticipants.get(console);
}
@Override
protected PageRec doCreatePage(IWorkbenchPart dummyPart) {
ConsoleWorkbenchPart part = (ConsoleWorkbenchPart) dummyPart;
final IConsole console = part.getConsole();
final IPageBookViewPage page = console.createPage(this);
initPage(page);
page.createControl(getPageBook());
console.addPropertyChangeListener(this);
// initialize page participants
final ListenerList<IConsolePageParticipant> participants = new ListenerList<>();
IConsolePageParticipant[] consoleParticipants = getPageParticipants(
console);
for (int i = 0; i < consoleParticipants.length; i++) {
participants.add(consoleParticipants[i]);
}
fConsoleToPageParticipants.put(console, participants);
for (final IConsolePageParticipant participant : participants) {
SafeRunner.run(new ISafeRunnable() {
@Override
public void run() throws Exception {
participant.init(page, console);
}
@Override
public void handleException(Throwable exception) {
DLTKDebugUIPlugin.log(exception);
participants.remove(participant);
}
});
}
PageRec rec = new PageRec(dummyPart, page);
return rec;
}
private IConsolePageParticipant[] getPageParticipants(IConsole console) {
// TODO Auto-generated method stub
return new IConsolePageParticipant[0];
}
@Override
protected boolean isImportant(IWorkbenchPart part) {
return part instanceof ConsoleWorkbenchPart;
}
@Override
public void dispose() {
IViewSite site = getViewSite();
if (site != null) {
site.getPage().removePartListener((IPartListener2) this);
}
super.dispose();
}
@Override
protected IPage createDefaultPage(PageBook book) {
MessagePage page = new MessagePage();
page.createControl(getPageBook());
initPage(page);
return page;
}
public void addConsole(final IEvaluateConsole console) {
if (isAvailable()) {
ConsoleWorkbenchPart part = new ConsoleWorkbenchPart(console,
getSite());
fConsoleToPart.put(console, part);
fPartToConsole.put(part, console);
partActivated(part);
for (IEvaluateConsoleListener listener : consoleListeners) {
listener.consoleAdded(console);
}
}
}
public void consolesRemoved(final IConsole[] consoles) {
if (isAvailable()) {
Runnable r = () -> {
for (int i = 0; i < consoles.length; i++) {
if (isAvailable()) {
IConsole console = consoles[i];
fStack.remove(console);
ConsoleWorkbenchPart part = fConsoleToPart.get(console);
if (part != null) {
partClosed(part);
}
if (getConsole() == null) {
IConsole[] available = {}; // FIXME
// getConsoleManager().getConsoles();
if (available.length > 0) {
display(available[available.length - 1]);
}
}
}
}
};
asyncExec(r);
}
}
/**
* Constructs a console view
*/
public ScriptDisplayView() {
super();
fConsoleToPart = new LinkedHashMap<>();
fPartToConsole = new HashMap<>();
fConsoleToPageParticipants = new HashMap<>();
}
protected void createActions() {
fPinAction = new PinConsoleAction(this);
fDisplayConsoleAction = new ConsoleDropDownAction(this);
IEvaluateConsoleFactory[] extensions = EvaluateConsoleFactoryManager
.getManager().getInstances();
if (extensions.length > 0) {
fOpenConsoleAction = new OpenConsoleAction(this, extensions);
}
}
protected void configureToolBar(IToolBarManager mgr) {
mgr.add(new Separator(IConsoleConstants.LAUNCH_GROUP));
mgr.add(new Separator(IConsoleConstants.OUTPUT_GROUP));
mgr.add(new Separator("fixedGroup")); //$NON-NLS-1$
mgr.add(fPinAction);
mgr.add(fDisplayConsoleAction);
if (fOpenConsoleAction != null) {
mgr.add(fOpenConsoleAction);
if (mgr instanceof ToolBarManager) {
ToolBarManager tbm = (ToolBarManager) mgr;
final ToolBar tb = tbm.getControl();
tb.addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
ToolItem ti = tb.getItem(new Point(e.x, e.y));
if (ti != null) {
if (ti.getData() instanceof ActionContributionItem) {
ActionContributionItem actionContributionItem = (ActionContributionItem) ti
.getData();
IAction action = actionContributionItem
.getAction();
if (action == fOpenConsoleAction) {
Event event = new Event();
event.widget = ti;
event.x = e.x;
event.y = e.y;
action.runWithEvent(event);
}
}
}
}
});
}
}
}
@Override
public void display(IConsole console) {
if (fPinned && fActiveConsole != null) {
return;
}
if (console.equals(fActiveConsole)) {
return;
}
ConsoleWorkbenchPart part = fConsoleToPart.get(console);
if (part != null) {
partActivated(part);
}
}
@Override
public void setPinned(boolean pin) {
fPinned = pin;
if (fPinAction != null) {
fPinAction.update();
}
}
@Override
public boolean isPinned() {
return fPinned;
}
@Override
protected IWorkbenchPart getBootstrapPart() {
return null;
}
/**
* Registers the given runnable with the display associated with this view's
* control, if any.
*
* @see org.eclipse.swt.widgets.Display#asyncExec(java.lang.Runnable)
*/
public void asyncExec(Runnable r) {
if (isAvailable()) {
getPageBook().getDisplay().asyncExec(r);
}
}
/**
* Creates this view's underlying viewer and actions. Hooks a pop-up menu to
* the underlying viewer's control, as well as a key listener. When the
* delete key is pressed, the <code>REMOVE_ACTION</code> is invoked. Hooks
* help to this view. Subclasses must implement the following methods which
* are called in the following order when a view is created:
* <ul>
* <li><code>createViewer(Composite)</code> - the context menu is hooked to
* the viewer's control.</li>
* <li><code>createActions()</code></li>
* <li><code>configureToolBar(IToolBarManager)</code></li>
* <li><code>getHelpContextId()</code></li>
* </ul>
*
* @see IWorkbenchPart#createPartControl(Composite)
*/
@Override
public void createPartControl(Composite parent) {
super.createPartControl(parent);
createActions();
IToolBarManager tbm = getViewSite().getActionBars().getToolBarManager();
configureToolBar(tbm);
updateForExistingConsoles();
getViewSite().getActionBars().updateActionBars();
// PlatformUI.getWorkbench().getHelpSystem()
// .setHelp(parent, IConsoleHelpContextIds.CONSOLE_VIEW);
getViewSite().getPage().addPartListener((IPartListener2) this);
initPageSwitcher();
createDebugConsole();
}
private void createDebugConsole() {
addConsole(new DebugConsole(Messages.ScriptDisplayView_consoleName,
DebugConsole.class.getName(),
new DebugScriptInterpreter(this)));
}
/**
* Initialize the PageSwitcher.
*/
private void initPageSwitcher() {
new PageSwitcher(this) {
@Override
public void activatePage(Object page) {
ShowConsoleAction.showConsole((IConsole) page,
ScriptDisplayView.this);
}
@Override
public ImageDescriptor getImageDescriptor(Object page) {
return ((IConsole) page).getImageDescriptor();
}
@Override
public String getName(Object page) {
return ((IConsole) page).getName();
}
@Override
public Object[] getPages() {
return getConsoles();
}
@Override
public int getCurrentPageIndex() {
IConsole currentConsole = getConsole();
IConsole[] consoles = getConsoles();
for (int i = 0; i < consoles.length; i++) {
if (consoles[i].equals(currentConsole))
return i;
}
return super.getCurrentPageIndex();
}
};
}
public IConsole[] getConsoles() {
return fConsoleToPart.keySet()
.toArray(new IConsole[fConsoleToPart.size()]);
}
/**
* Initialize for existing consoles
*/
private void updateForExistingConsoles() {
// IConsoleManager manager = getConsoleManager();
// // create pages for consoles
// IConsole[] consoles = manager.getConsoles();
// consolesAdded(consoles);
// // add as a listener
// manager.addConsoleListener(this);
}
@Override
public void warnOfContentChange(IConsole console) {
IWorkbenchPart part = fConsoleToPart.get(console);
if (part != null) {
IWorkbenchSiteProgressService service = part.getSite()
.getAdapter(IWorkbenchSiteProgressService.class);
if (service != null) {
service.warnOfContentChange();
}
}
}
@Override
public <T> T getAdapter(Class<T> key) {
T adpater = super.getAdapter(key);
if (adpater == null) {
IConsole console = getConsole();
if (console != null) {
ListenerList<IConsolePageParticipant> listeners = getParticipants(
console);
// an adapter can be asked for before the console participants
// are created
if (listeners != null) {
for (IConsolePageParticipant participant : listeners) {
adpater = participant.getAdapter(key);
if (adpater != null) {
return adpater;
}
}
}
}
}
return adpater;
}
@Override
public void partActivated(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
fActive = true;
IContextService contextService = getSite()
.getService(IContextService.class);
if (contextService != null) {
fActivatedContext = contextService
.activateContext(IConsoleConstants.ID_CONSOLE_VIEW);
activateParticipants(fActiveConsole);
}
}
}
@Override
public void partBroughtToTop(IWorkbenchPartReference partRef) {
}
@Override
public void partClosed(IWorkbenchPartReference partRef) {
}
@Override
public void partDeactivated(IWorkbenchPartReference partRef) {
if (isThisPart(partRef)) {
fActive = false;
IContextService contextService = getSite()
.getService(IContextService.class);
if (contextService != null) {
contextService.deactivateContext(fActivatedContext);
deactivateParticipants(fActiveConsole);
}
}
}
/**
* Returns if the specified part reference is to this view part (if the part
* reference is the console view or not)
*
* @param partRef
* @return true if the specified part reference is the console view
*/
protected boolean isThisPart(IWorkbenchPartReference partRef) {
if (partRef instanceof IViewReference) {
IViewReference viewRef = (IViewReference) partRef;
if (getViewSite() != null
&& viewRef.getId().equals(getViewSite().getId())) {
String secId = viewRef.getSecondaryId();
String mySec = null;
if (getSite() instanceof IViewSite) {
mySec = ((IViewSite) getSite()).getSecondaryId();
}
if (mySec == null) {
return secId == null;
}
return mySec.equals(secId);
}
}
return false;
}
/**
* Deactivates participants for the given console, if any.
*
* @param console
* console to deactivate
*/
private void deactivateParticipants(IConsole console) {
// deactivate
if (console != null) {
final ListenerList<IConsolePageParticipant> listeners = getParticipants(
console);
if (listeners != null) {
for (final IConsolePageParticipant participant : listeners) {
SafeRunner.run(new ISafeRunnable() {
@Override
public void run() throws Exception {
participant.deactivated();
}
@Override
public void handleException(Throwable exception) {
DLTKDebugUIPlugin.log(exception);
listeners.remove(participant);
}
});
}
}
}
}
@Override
public void partOpened(IWorkbenchPartReference partRef) {
}
@Override
public void partHidden(IWorkbenchPartReference partRef) {
}
@Override
public void partVisible(IWorkbenchPartReference partRef) {
}
@Override
public void partInputChanged(IWorkbenchPartReference partRef) {
}
@Override
public void setScrollLock(boolean scrollLock) {
fScrollLock = scrollLock;
IPage page = getCurrentPage();
// if (page instanceof IOConsolePage) {
// ((IOConsolePage) page).setAutoScroll(!scrollLock);
// }
}
@Override
public boolean getScrollLock() {
return fScrollLock;
}
@Override
public void pin(IConsole console) {
if (console == null) {
setPinned(false);
} else {
if (isPinned()) {
setPinned(false);
}
display(console);
setPinned(true);
}
}
private final ListenerList<IEvaluateConsoleListener> consoleListeners = new ListenerList<>();
public void addConsoleListener(IEvaluateConsoleListener listener) {
consoleListeners.add(listener);
}
public void removeConsoleListener(IEvaluateConsoleListener listener) {
consoleListeners.remove(listener);
}
@Override
public void setAutoScrollLock(boolean scrollLock) {
// Ignore as not implemented
}
@Override
public boolean getAutoScrollLock() {
return false;
}
}