/*******************************************************************************
* Copyright (c) 2008, 2014 Wind River Systems, 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:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.internal.debug.ui.model;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.debug.ui.IDebugView;
import org.eclipse.debug.ui.IDetailPane;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.resource.FontDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextPresentationListener;
import org.eclipse.jface.text.TextPresentation;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.tcf.protocol.Protocol;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchPartSite;
/**
* This detail pane uses a source viewer to display detailed information about the current
* selection.
*/
public class TCFDetailPane implements IDetailPane, IPropertyChangeListener {
public static final String ID = "org.eclipse.tcf.debug.DetailPaneFactory";
public static final String NAME = "TCF Detail Pane";
public static final String DESC = "TCF Detail Pane";
private static final String DETAIL_COPY_ACTION = IDebugView.COPY_ACTION + ".DetailPane"; //$NON-NLS-1$
private static final String DETAIL_SELECT_ALL_ACTION = IDebugView.SELECT_ALL_ACTION + ".DetailPane"; //$NON-NLS-1$
private SourceViewer source_viewer;
private Display display;
private int generation;
private IWorkbenchPartSite part_site;
private final Document document = new Document();
private final ArrayList<StyleRange> style_ranges = new ArrayList<StyleRange>();
private final HashMap<RGB,Color> colors = new HashMap<RGB,Color>();
private final Map<String,IAction> action_map = new HashMap<String,IAction>();
private final List<String> selection_actions = new ArrayList<String>();
private StyledStringBuffer doc_buffer;
private Font mono_font;
private final ITextPresentationListener presentation_listener = new ITextPresentationListener() {
public void applyTextPresentation(TextPresentation presentation) {
for (StyleRange r : style_ranges) presentation.addStyleRange(r);
}
};
private class DetailPaneAction extends Action {
final int op_code;
DetailPaneAction(String text, int op_code) {
super(text);
this.op_code = op_code;
}
void update() {
setEnabled(source_viewer != null && source_viewer.canDoOperation(op_code));
}
@Override
public void run() {
if (!isEnabled()) return;
source_viewer.doOperation(op_code);
}
}
private void createActions() {
DetailPaneAction action = null;
action = new DetailPaneAction("Select &All", ITextOperationTarget.SELECT_ALL);
action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_SELECT_ALL);
action_map.put(DETAIL_SELECT_ALL_ACTION, action);
action = new DetailPaneAction("&Copy", ITextOperationTarget.COPY);
action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY);
action_map.put(DETAIL_COPY_ACTION, action);
selection_actions.add(DETAIL_COPY_ACTION);
updateSelectionDependentActions();
}
private IAction getAction(String id) {
return action_map.get(id);
}
private void setGlobalAction(String id, IAction action) {
if (part_site instanceof IViewSite) {
((IViewSite)part_site).getActionBars().setGlobalActionHandler(id, action);
}
}
private void createDetailContextMenu(Control menuControl) {
if (part_site == null) return;
MenuManager manager = new MenuManager();
manager.setRemoveAllWhenShown(true);
manager.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager mgr) {
fillDetailContextMenu(mgr);
}
});
Menu menu = manager.createContextMenu(menuControl);
menuControl.setMenu(menu);
part_site.registerContextMenu(ID, manager, source_viewer.getSelectionProvider());
}
private void fillDetailContextMenu(IMenuManager menu) {
//menu.add(new Separator(MODULES_GROUP));
//menu.add(new Separator());
menu.add(getAction(DETAIL_COPY_ACTION));
menu.add(getAction(DETAIL_SELECT_ALL_ACTION));
menu.add(new Separator());
menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
private void updateAction(String id) {
IAction action = getAction(id);
if (action instanceof DetailPaneAction) ((DetailPaneAction)action).update();
}
private void updateSelectionDependentActions() {
for (String id : selection_actions) updateAction(id);
}
public Control createControl(Composite parent) {
assert source_viewer == null;
source_viewer = new SourceViewer(parent, null, SWT.V_SCROLL | SWT.H_SCROLL);
source_viewer.configure(new SourceViewerConfiguration());
source_viewer.setDocument(document);
source_viewer.setEditable(false);
source_viewer.addTextPresentationListener(presentation_listener);
Control control = source_viewer.getControl();
GridData gd = new GridData(GridData.FILL_BOTH);
control.setLayoutData(gd);
display = control.getDisplay();
createActions();
// Add the selection listener so selection dependent actions get updated.
document.addDocumentListener(new IDocumentListener() {
public void documentAboutToBeChanged(DocumentEvent event) {}
public void documentChanged(DocumentEvent event) {
updateSelectionDependentActions();
}
});
// Add the selection listener so selection dependent actions get updated.
source_viewer.getSelectionProvider().addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
updateSelectionDependentActions();
}
});
// Add a focus listener to update actions when details area gains focus
source_viewer.getControl().addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent e) {
if (part_site != null) part_site.setSelectionProvider(source_viewer.getSelectionProvider());
setGlobalAction(IDebugView.SELECT_ALL_ACTION, getAction(DETAIL_SELECT_ALL_ACTION));
setGlobalAction(IDebugView.COPY_ACTION, getAction(DETAIL_COPY_ACTION));
if (part_site instanceof IViewSite) ((IViewSite)part_site).getActionBars().updateActionBars();
}
public void focusLost(FocusEvent e) {
if (part_site != null) part_site.setSelectionProvider(null);
setGlobalAction(IDebugView.SELECT_ALL_ACTION, null);
setGlobalAction(IDebugView.COPY_ACTION, null);
if (part_site instanceof IViewSite) ((IViewSite)part_site).getActionBars().updateActionBars();
}
});
// Add a context menu to the detail area
createDetailContextMenu(source_viewer.getTextWidget());
// Add font registry listener
JFaceResources.getFontRegistry().addListener(this);
return control;
}
public void display(IStructuredSelection selection) {
if (source_viewer == null) return;
generation++;
final int g = generation;
final ArrayList<TCFNode> nodes = new ArrayList<TCFNode>();
if (selection != null) {
Iterator<?> iterator = selection.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
if (next instanceof TCFNode) nodes.add((TCFNode)next);
}
}
Protocol.invokeLater(new Runnable() {
public void run() {
if (g != generation) return;
final StyledStringBuffer s = getDetailText(nodes, this);
if (s == null) return;
display.asyncExec(new Runnable() {
public void run() {
if (g != generation) return;
doc_buffer = s;
document.set(getStyleRanges(s));
}
});
}
});
}
private StyledStringBuffer getDetailText(ArrayList<TCFNode> nodes, Runnable done) {
StyledStringBuffer bf = new StyledStringBuffer();
if (nodes.size() > 0) {
bf.enableFullErrorReports(nodes.get(0).model.getShowFullErrorReports());
for (TCFNode n : nodes) {
if (n instanceof IDetailsProvider) {
if (!((IDetailsProvider)n).getDetailText(bf, done)) return null;
}
}
}
return bf;
}
private String getStyleRanges(StyledStringBuffer s) {
style_ranges.clear();
for (StyledStringBuffer.Style x : s.getStyle()) {
StyleRange r = new StyleRange(x.pos, x.len, getColor(x.fg), getColor(x.bg), x.font);
if ((x.font & StyledStringBuffer.MONOSPACED) != 0) {
r.fontStyle &= ~StyledStringBuffer.MONOSPACED;
r.font = getMonospacedFont();
}
style_ranges.add(r);
}
return s.toString();
}
private Color getColor(RGB rgb) {
if (rgb == null) return null;
Color c = colors.get(rgb);
if (c == null) colors.put(rgb, c = new Color(display, rgb));
return c;
}
private Font getMonospacedFont() {
if (mono_font == null) {
FontData[] fd = source_viewer.getControl().getFont().getFontData();
FontDescriptor d = JFaceResources.getFontDescriptor(IDebugUIConstants.PREF_DETAIL_PANE_FONT);
if (fd != null && fd.length > 0) d = d.setHeight(fd[0].getHeight());
mono_font = d.createFont(display);
}
return mono_font;
}
public void propertyChange(PropertyChangeEvent event) {
for (Color c : colors.values()) c.dispose();
colors.clear();
if (mono_font != null) {
mono_font.dispose();
mono_font = null;
if (doc_buffer != null) {
document.set(getStyleRanges(doc_buffer));
}
}
}
public void dispose() {
JFaceResources.getFontRegistry().removeListener(this);
if (mono_font != null) {
mono_font.dispose();
mono_font = null;
}
for (Color c : colors.values()) c.dispose();
colors.clear();
if (source_viewer == null) return;
generation++;
if (source_viewer.getControl() != null) {
source_viewer.getControl().dispose();
}
source_viewer = null;
selection_actions.clear();
action_map.clear();
}
public String getDescription() {
return DESC;
}
public String getID() {
return ID;
}
public String getName() {
return NAME;
}
public void init(IWorkbenchPartSite part_site) {
this.part_site = part_site;
}
public boolean setFocus() {
if (source_viewer == null) return false;
source_viewer.getTextWidget().setFocus();
return true;
}
}