blob: b1ae6eea47a3a47ba9453a99eb9f455658ac037e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.debug.ui;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.debug.internal.ui.SWTFactory;
import org.eclipse.debug.internal.ui.model.elements.ElementContentProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener;
import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer;
import org.eclipse.debug.internal.ui.views.variables.details.DefaultDetailPane;
import org.eclipse.debug.internal.ui.views.variables.details.DetailPaneProxy;
import org.eclipse.debug.internal.ui.views.variables.details.IDetailPaneContainer;
import org.eclipse.debug.ui.AbstractDebugView;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.preference.JFacePreferences;
import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.jface.resource.JFaceColors;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.AbstractInformationControl;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IInformationControlExtension2;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPartSite;
/**
* Creates an information control to display an expression in a hover control.
*
* @noextend This class is not intended to be subclassed by clients.
*
* @since 3.3
*/
public class ExpressionInformationControlCreator implements IInformationControlCreator {
class ExpressionInformationControl extends AbstractInformationControl implements IInformationControlExtension2 {
/**
* Dialog setting key for height
*/
private static final String HEIGHT = "HEIGHT"; //$NON-NLS-1$
/**
* Dialog setting key for width.
*/
private static final String WIDTH = "WIDTH"; //$NON-NLS-1$
/**
* Dialog setting key for tree sash weight
*/
private static final String SASH_WEIGHT_TREE = "SashWeightTree"; //$NON-NLS-1$
/**
* Dialog setting key for details sash weight
*/
private static final String SASH_WEIGHT_DETAILS = "SashWeightDetails"; //$NON-NLS-1$
/**
* Variable to display.
*/
private IVariable fVariable;
private IPresentationContext fContext;
private TreeModelViewer fViewer;
private SashForm fSashForm;
private Composite fDetailPaneComposite;
private DetailPaneProxy fDetailPane;
private Tree fTree;
/**
* Creates the content for the root element of the tree viewer in the hover
*/
private class TreeRoot extends ElementContentProvider {
/* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.viewers.model.provisional.elements.ElementContentProvider#getChildCount(java.lang.Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext)
*/
@Override
protected int getChildCount(Object element, IPresentationContext context, IViewerUpdate monitor) throws CoreException {
return 1;
}
/* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.viewers.model.provisional.elements.ElementContentProvider#getChildren(java.lang.Object, int, int, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext)
*/
@Override
protected Object[] getChildren(Object parent, int index, int length, IPresentationContext context, IViewerUpdate monitor) throws CoreException {
return new Object[] { fVariable };
}
/* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.viewers.model.provisional.elements.ElementContentProvider#supportsContextId(java.lang.String)
*/
@Override
protected boolean supportsContextId(String id) {
return true;
}
}
/**
* Inner class implementing IDetailPaneContainer methods. Handles changes to detail
* pane and provides limited access to the detail pane proxy.
*/
private class DetailPaneContainer implements IDetailPaneContainer{
/* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.views.variables.details.IDetailPaneContainer#getCurrentPaneID()
*/
@Override
public String getCurrentPaneID() {
return fDetailPane.getCurrentPaneID();
}
/* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.views.variables.details.IDetailPaneContainer#getCurrentSelection()
*/
@Override
public IStructuredSelection getCurrentSelection() {
return (IStructuredSelection)fViewer.getSelection();
}
/* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.views.variables.details.IDetailPaneContainer#refreshDetailPaneContents()
*/
@Override
public void refreshDetailPaneContents() {
fDetailPane.display(getCurrentSelection());
}
/* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.views.variables.details.IDetailPaneContainer#getParentComposite()
*/
@Override
public Composite getParentComposite() {
return fDetailPaneComposite;
}
/* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.views.variables.details.IDetailPaneContainer#getWorkbenchPartSite()
*/
@Override
public IWorkbenchPartSite getWorkbenchPartSite() {
return null;
}
/* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.views.variables.details.IDetailPaneContainer#paneChanged(java.lang.String)
*/
@Override
public void paneChanged(String newPaneID) {
if (newPaneID.equals(DefaultDetailPane.ID)){
fDetailPane.getCurrentControl().setForeground(getSystemForegroundColor());
fDetailPane.getCurrentControl().setBackground(getSystemBackgroundColor());
}
}
}
/**
* Constructs a new control in the given shell.
*
* @param parentShell shell
* @param resize whether resize is supported
*/
ExpressionInformationControl(Shell parentShell, boolean resize) {
super(parentShell, resize);
create();
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.AbstractInformationControl#computeSizeHint()
*/
@Override
public Point computeSizeHint() {
IDialogSettings settings = getDialogSettings(false);
if (settings != null) {
int x = getIntSetting(settings, WIDTH);
if (x > 0) {
int y = getIntSetting(settings, HEIGHT);
if (y > 0) {
return new Point(x,y);
}
}
}
return super.computeSizeHint();
}
/**
* Returns the dialog settings for this hover or <code>null</code> if none
*
* @param create whether to create the settings
*/
private IDialogSettings getDialogSettings(boolean create) {
IDialogSettings settings = JDIDebugUIPlugin.getDefault().getDialogSettings();
IDialogSettings section = settings.getSection(this.getClass().getName());
if (section == null & create) {
section = settings.addNewSection(this.getClass().getName());
}
return section;
}
/**
* Returns an integer value in the given dialog settings or -1 if none.
*
* @param settings dialog settings
* @param key key
* @return value or -1 if not present
*/
private int getIntSetting(IDialogSettings settings, String key) {
try {
return settings.getInt(key);
} catch (NumberFormatException e) {
return -1;
}
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.AbstractInformationControl#dispose()
*/
@Override
public void dispose() {
persistSettings(getShell());
fContext.dispose();
super.dispose();
}
/**
* Persists dialog settings.
*
* @param shell
*/
private void persistSettings(Shell shell) {
if (shell != null && !shell.isDisposed()) {
if (isResizable()) {
IDialogSettings settings = getDialogSettings(true);
Point size = shell.getSize();
settings.put(WIDTH, size.x);
settings.put(HEIGHT, size.y);
int[] weights = fSashForm.getWeights();
settings.put(SASH_WEIGHT_TREE, weights[0]);
settings.put(SASH_WEIGHT_DETAILS, weights[1]);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.AbstractInformationControl#setVisible(boolean)
*/
@Override
public void setVisible(boolean visible) {
if (!visible) {
persistSettings(getShell());
}
super.setVisible(visible);
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.AbstractInformationControl#createContent(org.eclipse.swt.widgets.Composite)
*/
@Override
protected void createContent(Composite parent) {
fSashForm = new SashForm(parent, parent.getStyle());
fSashForm.setOrientation(SWT.VERTICAL);
// update presentation context
AbstractDebugView view = getViewToEmulate();
fContext = new PresentationContext(IDebugUIConstants.ID_VARIABLE_VIEW);
if (view != null) {
// copy over properties
IPresentationContext copy = ((TreeModelViewer)view.getViewer()).getPresentationContext();
String[] properties = copy.getProperties();
for (int i = 0; i < properties.length; i++) {
String key = properties[i];
fContext.setProperty(key, copy.getProperty(key));
}
}
fViewer = new TreeModelViewer(fSashForm, SWT.NO_TRIM | SWT.MULTI | SWT.VIRTUAL, fContext);
fViewer.setAutoExpandLevel(1);
if (view != null) {
// copy over filters
StructuredViewer structuredViewer = (StructuredViewer) view.getViewer();
if (structuredViewer != null) {
ViewerFilter[] filters = structuredViewer.getFilters();
for (int i = 0; i < filters.length; i++) {
fViewer.addFilter(filters[i]);
}
}
}
fDetailPaneComposite = SWTFactory.createComposite(fSashForm, 1, 1, GridData.FILL_BOTH);
Layout layout = fDetailPaneComposite.getLayout();
if (layout instanceof GridLayout) {
GridLayout gl = (GridLayout) layout;
gl.marginHeight = 0;
gl.marginWidth = 0;
}
fDetailPane = new DetailPaneProxy(new DetailPaneContainer());
fDetailPane.display(null); // Bring up the default pane so the user doesn't see an empty composite
fTree = fViewer.getTree();
fTree.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
fDetailPane.display((IStructuredSelection)fViewer.getSelection());
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {}
});
initSashWeights();
// add update listener to auto-select and display details of root expression
fViewer.addViewerUpdateListener(new IViewerUpdateListener() {
@Override
public void viewerUpdatesComplete() {
}
@Override
public void viewerUpdatesBegin() {
}
@Override
public void updateStarted(IViewerUpdate update) {
}
@Override
public void updateComplete(IViewerUpdate update) {
if (update instanceof IChildrenUpdate) {
TreeSelection selection = new TreeSelection(new TreePath(new Object[]{fVariable}));
fViewer.setSelection(selection);
fDetailPane.display(selection);
fViewer.removeViewerUpdateListener(this);
}
}
});
setForegroundColor(getSystemForegroundColor());
setBackgroundColor(getSystemBackgroundColor());
}
/**
* Attempts to find an appropriate view to emulate, this will either be the
* variables view or the expressions view.
* @return a view to emulate or <code>null</code>
*/
private AbstractDebugView getViewToEmulate() {
IWorkbenchPage page = JDIDebugUIPlugin.getActiveWorkbenchWindow().getActivePage();
AbstractDebugView expressionsView = (AbstractDebugView) page.findView(IDebugUIConstants.ID_EXPRESSION_VIEW);
if (expressionsView != null && expressionsView.isVisible()) {
return expressionsView;
}
AbstractDebugView variablesView = (AbstractDebugView) page.findView(IDebugUIConstants.ID_VARIABLE_VIEW);
if (variablesView != null && variablesView.isVisible()) {
return variablesView;
}
if (expressionsView != null) {
return expressionsView;
}
return variablesView;
}
/**
* Initializes the sash form weights from the preference store (using default values if
* no sash weights were stored previously).
*/
protected void initSashWeights(){
IDialogSettings settings = getDialogSettings(false);
if (settings != null) {
int tree = getIntSetting(settings, SASH_WEIGHT_TREE);
if (tree > 0) {
int details = getIntSetting(settings, SASH_WEIGHT_DETAILS);
if (details > 0) {
fSashForm.setWeights(new int[]{tree, details});
}
}
}
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.AbstractInformationControl#setForegroundColor(org.eclipse.swt.graphics.Color)
*/
@Override
public void setForegroundColor(Color foreground) {
super.setForegroundColor(foreground);
fDetailPaneComposite.setForeground(foreground);
fTree.setForeground(foreground);
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.AbstractInformationControl#setBackgroundColor(org.eclipse.swt.graphics.Color)
*/
@Override
public void setBackgroundColor(Color background) {
super.setBackgroundColor(background);
fDetailPaneComposite.setBackground(background);
fTree.setBackground(background);
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.AbstractInformationControl#setFocus()
*/
@Override
public void setFocus() {
super.setFocus();
fTree.setFocus();
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.IInformationControlExtension#hasContents()
*/
@Override
public boolean hasContents() {
return fVariable != null;
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.IInformationControlExtension2#setInput(java.lang.Object)
*/
@Override
public void setInput(Object input) {
if (input instanceof IVariable) {
fVariable = (IVariable) input;
fViewer.setInput(new TreeRoot());
}
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.AbstractInformationControl#getInformationPresenterControlCreator()
*/
@Override
public IInformationControlCreator getInformationPresenterControlCreator() {
return new ExpressionInformationControlCreator() {
/* (non-Javadoc)
* @see org.eclipse.jdt.internal.debug.ui.ExpressionInformationControlCreator#createInformationControl(org.eclipse.swt.widgets.Shell)
*/
@Override
public IInformationControl createInformationControl(Shell shell) {
return new ExpressionInformationControl(shell, true);
}
};
}
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.IInformationControlCreator#createInformationControl(org.eclipse.swt.widgets.Shell)
*/
@Override
public IInformationControl createInformationControl(Shell parent) {
return new ExpressionInformationControl(parent, false);
}
private static Color getSystemForegroundColor() {
ColorRegistry colorRegistry = JFaceResources.getColorRegistry();
Color foreground = colorRegistry.get(JFacePreferences.INFORMATION_FOREGROUND_COLOR);
if (foreground == null) {
return JFaceColors.getInformationViewerForegroundColor(Display.getDefault());
}
return foreground;
}
public static Color getSystemBackgroundColor() {
ColorRegistry colorRegistry = JFaceResources.getColorRegistry();
Color background = colorRegistry.get(JFacePreferences.INFORMATION_BACKGROUND_COLOR);
if (background == null) {
return JFaceColors.getInformationViewerBackgroundColor(Display.getDefault());
}
return background;
}
}