blob: 6544f7aeed8345773704d120dd58a0e14508f8e7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 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
*
*******************************************************************************/
package org.eclipse.wst.html.ui.internal.projection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
import org.eclipse.jface.text.source.projection.ProjectionViewer;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.wst.sse.core.internal.provisional.INodeAdapter;
import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier;
import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
import org.w3c.dom.Node;
/**
* Updates projection annotation model with projection annotations for this
* adapter node's children
*/
public class ProjectionModelNodeAdapterHTML implements INodeAdapter {
// copies of this class located in:
// org.eclipse.wst.html.ui.internal.projection
// org.eclipse.jst.jsp.ui.internal.projection
private final static boolean debugProjectionPerf = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.html.ui/projectionperf")); //$NON-NLS-1$ //$NON-NLS-2$
private class TagProjectionAnnotation extends ProjectionAnnotation {
private boolean fIsVisible = false; /* workaround for BUG85874 */
private Node fNode;
public TagProjectionAnnotation(Node node, boolean isCollapsed) {
super(isCollapsed);
fNode = node;
}
public Node getNode() {
return fNode;
}
public void setNode(Node node) {
fNode = node;
}
/**
* Does not paint hidden annotations. Annotations are hidden when they
* only span one line.
*
* @see ProjectionAnnotation#paint(org.eclipse.swt.graphics.GC,
* org.eclipse.swt.widgets.Canvas,
* org.eclipse.swt.graphics.Rectangle)
*/
public void paint(GC gc, Canvas canvas, Rectangle rectangle) {
/* workaround for BUG85874 */
/*
* only need to check annotations that are expanded because hidden
* annotations should never have been given the chance to
* collapse.
*/
if (!isCollapsed()) {
// working with rectangle, so line height
FontMetrics metrics = gc.getFontMetrics();
if (metrics != null) {
// do not draw annotations that only span one line and
// mark them as not visible
if ((rectangle.height / metrics.getHeight()) <= 1) {
fIsVisible = false;
return;
}
}
}
fIsVisible = true;
super.paint(gc, canvas, rectangle);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.text.source.projection.ProjectionAnnotation#markCollapsed()
*/
public void markCollapsed() {
/* workaround for BUG85874 */
// do not mark collapsed if annotation is not visible
if (fIsVisible)
super.markCollapsed();
}
}
// copies of this class located in:
// org.eclipse.wst.html.ui.internal.projection
// org.eclipse.jst.jsp.ui.internal.projection
ProjectionModelNodeAdapterFactoryHTML fAdapterFactory;
private Map fTagAnnotations = new HashMap();
public ProjectionModelNodeAdapterHTML(ProjectionModelNodeAdapterFactoryHTML factory) {
fAdapterFactory = factory;
}
/**
* Create a projection position from the given node. Able to get
* projection position if node isNodeProjectable.
*
* @param node
* @return null if no projection position possible, a Position otherwise
*/
private Position createProjectionPosition(Node node) {
Position pos = null;
if (fAdapterFactory.isNodeProjectable(node) && node instanceof IndexedRegion) {
// IDocument document =
// fAdapterFactory.getProjectionViewer().getDocument();
// if (document != null) {
IndexedRegion inode = (IndexedRegion) node;
int start = inode.getStartOffset();
int end = inode.getEndOffset();
if (start >= 0 && start < end) {
// region-based
// extra line when collapsed, but no region
// increase when add newline
pos = new Position(start, end - start);
// try {
// // line-based
// // extra line when collapsed, but no region
// // increase when add newline
// IRegion startLineRegion =
// document.getLineInformationOfOffset(start);
// IRegion endLineRegion =
// document.getLineInformationOfOffset(end);
// int startOffset = startLineRegion.getOffset();
// int endOffset = endLineRegion.getOffset() +
// endLineRegion.getLength();
// if (endOffset > startOffset) {
// pos = new Position(startOffset, endOffset -
// startOffset);
// }
//
// // line-based
// // no extra line when collapsed, but region increase
// // when add newline
// int startLine = document.getLineOfOffset(start);
// int endLine = document.getLineOfOffset(end);
// if (endLine + 1 < document.getNumberOfLines()) {
// int offset = document.getLineOffset(startLine);
// int endOffset = document.getLineOffset(endLine + 1);
// pos = new Position(offset, endOffset - offset);
// }
// }
// catch (BadLocationException x) {
// Logger.log(Logger.WARNING_DEBUG, null, x);
// }
}
}
// }
return pos;
}
/**
* Find TagProjectionAnnotation for node in the current list of projection
* annotations for this adapter
*
* @param node
* @return TagProjectionAnnotation
*/
private TagProjectionAnnotation getExistingAnnotation(Node node) {
TagProjectionAnnotation anno = null;
if ((node != null) && (!fTagAnnotations.isEmpty())) {
Iterator it = fTagAnnotations.keySet().iterator();
while (it.hasNext() && anno == null) {
TagProjectionAnnotation a = (TagProjectionAnnotation) it.next();
Node n = a.getNode();
if (node.equals(n)) {
anno = a;
}
}
}
return anno;
}
public boolean isAdapterForType(Object type) {
return type == ProjectionModelNodeAdapterHTML.class;
}
public void notifyChanged(INodeNotifier notifier, int eventType, Object changedFeature, Object oldValue, Object newValue, int pos) {
// check if folding is even enabled, if not, just ignore notifyChanged
// events
if (!fAdapterFactory.isActive()) {
return;
}
if ((eventType == INodeNotifier.STRUCTURE_CHANGED) && (notifier instanceof Node)) {
updateAdapter((Node) notifier);
}
}
/**
* Update the projection annotation of all the nodes that are children of
* node
*
* @param node
*/
void updateAdapter(Node node) {
updateAdapter(node, null);
}
/**
* Update the projection annotation of all the nodes that are children of
* node and adds all projection annotations to viewer (for newly added
* viewers)
*
* @param node
* @param viewer
*/
void updateAdapter(Node node, ProjectionViewer viewer) {
long start = System.currentTimeMillis();
Map additions = new HashMap();
Map projectionAnnotations = new HashMap();
// go through immediate child nodes and figure out projection
// model annotations
if (node != null) {
Node childNode = node.getFirstChild();
while (childNode != null) {
Position newPos = createProjectionPosition(childNode);
if (newPos != null) {
TagProjectionAnnotation newAnnotation = new TagProjectionAnnotation(childNode, false);
TagProjectionAnnotation existing = getExistingAnnotation(childNode);
if (existing == null) {
// add to map containing all annotations for this
// adapter
projectionAnnotations.put(newAnnotation, newPos);
// add to map containing annotations to add
additions.put(newAnnotation, newPos);
}
else {
// add to map containing all annotations for this
// adapter
projectionAnnotations.put(existing, newPos);
// remove from map containing annotations to delete
fTagAnnotations.remove(existing);
}
}
childNode = childNode.getNextSibling();
}
// in the end, want to delete anything leftover in old list, add
// everything in additions, and update everything in
// projectionAnnotations
ProjectionAnnotation[] oldList = null;
if (!fTagAnnotations.isEmpty()) {
oldList = (ProjectionAnnotation[]) fTagAnnotations.keySet().toArray(new ProjectionAnnotation[0]);
}
ProjectionAnnotation[] modifyList = null;
if (!projectionAnnotations.isEmpty()) {
modifyList = (ProjectionAnnotation[]) projectionAnnotations.keySet().toArray(new ProjectionAnnotation[0]);
}
// specifically add all annotations to viewer
if (viewer != null && !projectionAnnotations.isEmpty()) {
fAdapterFactory.queueAnnotationModelChanges(node, null, projectionAnnotations, null, viewer);
}
// only update when there is something to update
if ((oldList != null && oldList.length > 0) || (!additions.isEmpty()) || (modifyList != null && modifyList.length > 0))
fAdapterFactory.queueAnnotationModelChanges(node, oldList, additions, modifyList);
}
// save new list of annotations
fTagAnnotations = projectionAnnotations;
if (debugProjectionPerf) {
long end = System.currentTimeMillis();
String nodeName = node != null ? node.getNodeName() : "null"; //$NON-NLS-1$
System.out.println("ProjectionModelNodeAdapterHTML.updateAdapter (" + nodeName + "):" + (end - start)); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}