/*******************************************************************************
* Copyright (c) 2000, 2010 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Wind River Systems - Adapter to use with DSF
*******************************************************************************/
package org.eclipse.cdt.dsf.debug.ui.sourcelookup;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.cdt.dsf.concurrent.ThreadSafe;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.debug.service.IRunControl;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
import org.eclipse.cdt.dsf.debug.service.IStack;
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationPresentation;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;
/**
* This class tracks instruction pointer contexts for a single DSF session.
*/
@ThreadSafe
class InstructionPointerManager {
/**
* Current instruction pointer annotation type.
*/
private static final String ID_CURRENT_IP= IDebugUIConstants.ANNOTATION_TYPE_INSTRUCTION_POINTER_CURRENT;
/**
* Secondary instruction pointer annotation type.
*/
private static final String ID_SECONDARY_IP= IDebugUIConstants.ANNOTATION_TYPE_INSTRUCTION_POINTER_SECONDARY;
/**
* Editor annotation object for instruction pointers.
*/
static class IPAnnotation extends Annotation implements IAnnotationPresentation {
/** The image for this annotation. */
private Image fImage;
/** Frame DMC that this IP is for **/
private IStack.IFrameDMContext fFrame;
/**
* Constructs an instruction pointer image.
*
* @param frame stack frame the instruction pointer is associated with
* @param annotationType the type of annotation to display (annotation identifier)
* @param text the message to display with the annotation as hover help
* @param image the image used to display the annotation
*/
IPAnnotation(IStack.IFrameDMContext frame, String annotationType, String text, Image image) {
super(annotationType, false, text);
fFrame = frame;
fImage = image;
}
/**
* Returns this annotation's image.
*
* @return image
*/
protected Image getImage() {
return fImage;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object other) {
if (other instanceof IPAnnotation) {
return fFrame.equals(((IPAnnotation)other).fFrame);
}
return false;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return fFrame.hashCode();
}
/*
* @see org.eclipse.jface.text.source.IAnnotationPresentation#paint(org.eclipse.swt.graphics.GC, org.eclipse.swt.widgets.Canvas, org.eclipse.swt.graphics.Rectangle)
*/
public void paint(GC gc, Canvas canvas, Rectangle bounds) {
Rectangle imageBounds = fImage.getBounds();
gc.drawImage(fImage, bounds.x + (bounds.width - imageBounds.width) / 2 , bounds.y + (bounds.height - imageBounds.height) / 2);
}
/*
* @see org.eclipse.jface.text.source.IAnnotationPresentation#getLayer()
*/
public int getLayer() {
return 5;
}
}
/**
* Represents the context for a single instruction pointer. This is a convenience class
* used to store the three objects that comprise an instruction pointer 'context' so it
* can be stored in collections.
*/
static class AnnotationWrapper {
/** The text editor for this context. */
private ITextEditor fTextEditor;
/** Stack frame that this annotation is for */
private IStack.IFrameDMContext fFrameDmc;
/** The vertical ruler annotation for this context. */
private Annotation fAnnotation;
AnnotationWrapper(ITextEditor textEditor, Annotation annotation, IStack.IFrameDMContext frameDmc) {
fTextEditor = textEditor;
fAnnotation = annotation;
fFrameDmc = frameDmc;
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object other) {
if (other instanceof AnnotationWrapper) {
AnnotationWrapper otherContext = (AnnotationWrapper) other;
return getAnnotation().equals(otherContext.getAnnotation());
}
return false;
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return getAnnotation().hashCode();
}
ITextEditor getTextEditor() { return fTextEditor; }
Annotation getAnnotation() { return fAnnotation; }
IStack.IFrameDMContext getFrameDMC() { return fFrameDmc; }
}
/**
* Mapping of IDebugTarget objects to (mappings of IThread objects to lists of instruction
* pointer contexts).
*/
private final List<AnnotationWrapper> fAnnotationWrappers;
/**
* For customized instruction pointer presentation.
*/
private final IInstructionPointerPresentation fPresentation;
/**
* Clients must not instantiate this class.
*/
public InstructionPointerManager() {
this(null);
}
/**
* Clients must not instantiate this class.
*
* @param presentation
* the custom instruction pointer presentation or
* <code>null</code> to use the default presentation
*/
public InstructionPointerManager(IInstructionPointerPresentation presentation) {
fPresentation = presentation;
fAnnotationWrappers = new LinkedList<AnnotationWrapper>();
}
/**
* Add an instruction pointer annotation in the specified editor for the
* specified stack frame.
*/
public void addAnnotation(ITextEditor textEditor, IStack.IFrameDMContext frame, Position position, boolean isTopFrame) {
IDocumentProvider docProvider = textEditor.getDocumentProvider();
IEditorInput editorInput = textEditor.getEditorInput();
// If there is no annotation model, there's nothing more to do
IAnnotationModel annModel = docProvider.getAnnotationModel(editorInput);
if (annModel == null) {
return;
}
if (isTopFrame) {
// remove other top-frame IP annotation(s) for this execution-context
removeAnnotations(DMContexts.getAncestorOfType(frame.getParents()[0], IExecutionDMContext.class));
}
Annotation annotation = createAnnotation(textEditor, frame);
// Add the annotation at the position to the editor's annotation model.
annModel.removeAnnotation(annotation);
annModel.addAnnotation(annotation, position);
// Add to list of existing wrappers
synchronized (fAnnotationWrappers) {
fAnnotationWrappers.add(new AnnotationWrapper(textEditor, annotation, frame));
}
}
private Annotation createAnnotation(ITextEditor editorPart, IStack.IFrameDMContext frame) {
String id = null;
String text = null;
Image image = null;
if (fPresentation != null) {
Annotation annotation = fPresentation.getInstructionPointerAnnotation(editorPart, frame);
if (annotation != null) {
return annotation;
}
id = fPresentation.getInstructionPointerAnnotationType(editorPart, frame);
if (id == null) {
image = fPresentation.getInstructionPointerImage(editorPart, frame);
}
text = fPresentation.getInstructionPointerText(editorPart, frame);
}
if (id == null) {
if (frame.getLevel() == 0) {
id = ID_CURRENT_IP;
if (text == null) {
text = Messages.IPAnnotation_primary;
}
if (image == null) {
image = DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_INSTRUCTION_POINTER_TOP);
}
} else {
id = ID_SECONDARY_IP;
if (text == null) {
text = Messages.IPAnnotation_secondary;
}
if (image == null) {
image = DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_INSTRUCTION_POINTER);
}
}
return new IPAnnotation(frame, id, text, image);
}
return new Annotation(id, false, text);
}
/**
* Remove all annotations associated with the specified debug target that this class
* is tracking.
*/
public void removeAnnotations(IRunControl.IExecutionDMContext execDmc) {
// Retrieve the mapping of threads to context lists
synchronized(fAnnotationWrappers) {
for (Iterator<AnnotationWrapper> wrapperItr = fAnnotationWrappers.iterator(); wrapperItr.hasNext();) {
AnnotationWrapper wrapper = wrapperItr.next();
if (DMContexts.isAncestorOf(wrapper.getFrameDMC(), execDmc)) {
removeAnnotation(wrapper.getTextEditor(), wrapper.getAnnotation());
wrapperItr.remove();
}
}
}
}
/**
* Remove all top-frame annotations associated with the specified debug target that this class
* is tracking.
*/
public void removeTopFrameAnnotations(IRunControl.IExecutionDMContext execDmc) {
// Retrieve the mapping of threads to context lists
synchronized(fAnnotationWrappers) {
for (Iterator<AnnotationWrapper> wrapperItr = fAnnotationWrappers.iterator(); wrapperItr.hasNext();) {
AnnotationWrapper wrapper = wrapperItr.next();
final IFrameDMContext frameDmc= wrapper.getFrameDMC();
if (DMContexts.isAncestorOf(frameDmc, execDmc) && frameDmc.getLevel() == 0) {
removeAnnotation(wrapper.getTextEditor(), wrapper.getAnnotation());
wrapperItr.remove();
}
}
}
}
/** Removes all annotations tracked by this manager */
public void removeAllAnnotations() {
synchronized(fAnnotationWrappers) {
for (AnnotationWrapper wrapper : fAnnotationWrappers) {
removeAnnotation(wrapper.getTextEditor(), wrapper.getAnnotation());
}
fAnnotationWrappers.clear();
}
}
/**
* Remove the specified annotation from the specified text editor.
*/
private void removeAnnotation(ITextEditor textEditor, Annotation annotation) {
IDocumentProvider docProvider = textEditor.getDocumentProvider();
if (docProvider != null) {
IAnnotationModel annotationModel = docProvider.getAnnotationModel(textEditor.getEditorInput());
if (annotationModel != null) {
annotationModel.removeAnnotation(annotation);
}
}
}
}