/*******************************************************************************
* Copyright (c) 2016 Ericsson 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
*******************************************************************************/
package org.eclipse.cdt.debug.internal.ui.views.debuggerconsole;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.eclipse.cdt.debug.ui.CDebugUIPlugin;
import org.eclipse.cdt.debug.ui.debuggerconsole.IDebuggerConsole;
import org.eclipse.cdt.debug.ui.debuggerconsole.IDebuggerConsoleManager;
import org.eclipse.cdt.debug.ui.debuggerconsole.IDebuggerConsoleView;
import org.eclipse.jface.action.IToolBarManager;
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.widgets.Composite;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleListener;
import org.eclipse.ui.console.IConsoleView;
import org.eclipse.ui.console.IOConsole;
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;
/**
* The Debugger console view shows different {@link IDebuggerConsole}.
*
* This class extends {@link IConsoleView} to allow it to easily display
* consoles of type {@link IOConsole}.
*
* @see {@link IDebuggerConsoleManager}
*/
public class DebuggerConsoleView extends PageBookView
implements IConsoleView, IDebuggerConsoleView, IConsoleListener, IPropertyChangeListener {
public static final String DEBUGGER_CONSOLE_VIEW_ID = "org.eclipse.cdt.debug.ui.debuggerConsoleView"; //$NON-NLS-1$
public static final String DROP_DOWN_ACTION_ID = DEBUGGER_CONSOLE_VIEW_ID + ".DebuggerConsoleDropDownAction"; //$NON-NLS-1$
/** The console being displayed, or <code>null</code> if none */
private IDebuggerConsole fActiveConsole;
/** Map of consoles to dummy console parts (used to close pages) */
private Map<IDebuggerConsole, DebuggerConsoleWorkbenchPart> fConsoleToPart = new HashMap<>();
/** Map of parts to consoles */
private Map<DebuggerConsoleWorkbenchPart, IDebuggerConsole> fPartToConsole = new HashMap<>();
private DebuggerConsoleDropDownAction fDisplayConsoleAction;
@Override
public void createPartControl(Composite parent) {
super.createPartControl(parent);
createActions();
configureToolBar(getViewSite().getActionBars().getToolBarManager());
// create pages for existing consoles
IConsole[] consoles = getConsoleManager().getConsoles();
consolesAdded(consoles);
// add as a listener for new consoles
getConsoleManager().addConsoleListener(this);
getViewSite().getActionBars().updateActionBars();
initPageSwitcher();
}
@Override
protected PageRec doCreatePage(IWorkbenchPart dummyPart) {
DebuggerConsoleWorkbenchPart part = (DebuggerConsoleWorkbenchPart)dummyPart;
IDebuggerConsole console = fPartToConsole.get(part);
IPageBookViewPage page = console.createDebuggerPage(this);
initPage(page);
page.createControl(getPageBook());
console.addPropertyChangeListener(this);
return new PageRec(dummyPart, page);
}
protected void createActions() {
fDisplayConsoleAction = new DebuggerConsoleDropDownAction(this);
}
protected void configureToolBar(IToolBarManager mgr) {
mgr.add(fDisplayConsoleAction);
}
@Override
public void dispose() {
super.dispose();
getConsoleManager().removeConsoleListener(this);
if (fDisplayConsoleAction != null) {
fDisplayConsoleAction.dispose();
fDisplayConsoleAction = null;
}
}
@Override
public void propertyChange(PropertyChangeEvent event) {
// This is important to update the title of a console when it terminates
Object source = event.getSource();
if (source instanceof IConsole && event.getProperty().equals(IBasicPropertyConstants.P_TEXT)) {
if (source.equals(getCurrentConsole())) {
updateTitle();
}
}
}
private boolean isAvailable() {
return getPageBook() != null && !getPageBook().isDisposed();
}
/**
* Returns the currently displayed console.
*/
@Override
public IDebuggerConsole getCurrentConsole() {
return fActiveConsole;
}
@Override
protected void showPageRec(PageRec pageRec) {
IDebuggerConsole recConsole = fPartToConsole.get(pageRec.part);
if (recConsole != null && recConsole.equals(getCurrentConsole())) {
return;
}
super.showPageRec(pageRec);
fActiveConsole = recConsole;
updateTitle();
}
/**
* Returns a set of consoles known by the view.
*/
protected Set<IDebuggerConsole> getConsoles() {
return fConsoleToPart.keySet();
}
/**
* Updates the view title based on the active console
*/
protected void updateTitle() {
IConsole console = getCurrentConsole();
if (console == null) {
setContentDescription(ConsoleMessages.ConsoleMessages_no_console);
} else {
String newName = console.getName();
String oldName = getContentDescription();
if (newName != null && !newName.equals(oldName)) {
setContentDescription(newName);
}
}
}
@Override
protected void doDestroyPage(IWorkbenchPart part, PageRec pageRecord) {
pageRecord.page.dispose();
pageRecord.dispose();
IConsole console = fPartToConsole.remove(part);
fConsoleToPart.remove(console);
console.removePropertyChangeListener(this);
if (fPartToConsole.isEmpty()) {
fActiveConsole = null;
}
}
@Override
protected boolean isImportant(IWorkbenchPart part) {
return part instanceof DebuggerConsoleWorkbenchPart;
}
private IDebuggerConsoleManager getConsoleManager() {
return CDebugUIPlugin.getDebuggerConsoleManager();
}
@Override
protected IPage createDefaultPage(PageBook book) {
MessagePage page = new MessagePage();
page.createControl(getPageBook());
initPage(page);
return page;
}
@Override
public void consolesAdded(IConsole[] consoles) {
if (isAvailable()) {
asyncExec(() -> {
for (IConsole console : consoles) {
if (isAvailable()) {
// Ensure console is still registered since this is done asynchronously
IDebuggerConsole[] allConsoles = getConsoleManager().getConsoles();
for (IDebuggerConsole registered : allConsoles) {
if (registered.equals(console)) {
DebuggerConsoleWorkbenchPart part = new DebuggerConsoleWorkbenchPart(registered, getSite());
fConsoleToPart.put(registered, part);
fPartToConsole.put(part, registered);
// Must call partActivated() to create the page
// However, this will also show the page, which is not
// what we want. Therefore, let's force the previous
// page to be shown again right after.
IDebuggerConsole previouslyShown = getCurrentConsole();
partActivated(part);
if (previouslyShown != null) {
display(previouslyShown);
}
break;
}
}
}
}
});
}
}
@Override
public void consolesRemoved(IConsole[] consoles) {
if (isAvailable()) {
asyncExec(() -> {
for (IConsole console : consoles) {
if (isAvailable()) {
DebuggerConsoleWorkbenchPart part = fConsoleToPart.get(console);
if (part != null) {
// partClosed() will also cleanup our maps
partClosed(part);
}
if (getCurrentConsole() == null) {
// When a part is closed, the page that is shown becomes
// the default page, which does not have a console.
// We want to select a page with a console instead.
IDebuggerConsole[] available = getConsoleManager().getConsoles();
if (available.length > 0) {
display(available[available.length - 1]);
}
}
}
}
});
}
}
@Override
public void display(IDebuggerConsole console) {
if (console.equals(getCurrentConsole())) {
// Already displayed
return;
}
DebuggerConsoleWorkbenchPart part = fConsoleToPart.get(console);
if (part != null) {
partActivated(part);
// let the console know it's being activated
fActiveConsole.consoleSelected();
}
}
@Override
protected IWorkbenchPart getBootstrapPart() {
return null;
}
/**
* Registers the given runnable with the display associated with this view's
* control, if any.
*
* @param r the runnable
* @see org.eclipse.swt.widgets.Display#asyncExec(java.lang.Runnable)
*/
private void asyncExec(Runnable r) {
if (isAvailable()) {
getPageBook().getDisplay().asyncExec(r);
}
}
/**
* Initialize the PageSwitcher.
* The page switcher is triggered using a keyboard shortcut
* configured in the user's eclipse and allows to switch
* pages using a popup.
*/
private void initPageSwitcher() {
new PageSwitcher(this) {
@Override
public void activatePage(Object page) {
display((IDebuggerConsole)page);
}
@Override
public ImageDescriptor getImageDescriptor(Object page) {
return ((IDebuggerConsole)page).getImageDescriptor();
}
@Override
public String getName(Object page) {
return ((IDebuggerConsole)page).getName();
}
@Override
public Object[] getPages() {
return getConsoleManager().getConsoles();
}
@Override
public int getCurrentPageIndex() {
IConsole currentConsole = getCurrentConsole();
IConsole[] consoles = getConsoleManager().getConsoles();
for (int i = 0; i < consoles.length; i++) {
if (consoles[i].equals(currentConsole)) {
return i;
}
}
return super.getCurrentPageIndex();
}
};
}
@Override
public void setAutoScrollLock(boolean scrollLock) {
}
@Override
public boolean getAutoScrollLock() {
return false;
}
@Override
public void display(IConsole console) {
if (console instanceof IDebuggerConsole) {
display((IDebuggerConsole)console);
}
}
@Override
public void setPinned(boolean pin) {
}
@Override
public void pin(IConsole console) {
}
@Override
public boolean isPinned() {
return false;
}
@Override
public IConsole getConsole() {
return getCurrentConsole();
}
@Override
public void warnOfContentChange(IConsole console) {
assert false;
}
@Override
public void setScrollLock(boolean scrollLock) {
}
@Override
public boolean getScrollLock() {
return false;
}
@Override
public void setWordWrap(boolean wordWrap) {
}
@Override
public boolean getWordWrap() {
return false;
}
}