blob: cd082e845b53922cafede8ddac41fdc9af735d7b [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.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
import org.eclipse.jface.text.source.projection.ProjectionViewer;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.wst.html.ui.internal.Logger;
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 {
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 need the styledtext line height
int lineHeight = fAdapterFactory.getProjectionViewer().getTextWidget().getLineHeight();
// do not draw annotations that only span one line and mark
// them as not visible
if ((rectangle.height / lineHeight) <= 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
private final static boolean debugProjectionPerf = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.html.ui/projectionperf")); //$NON-NLS-1$ //$NON-NLS-2$
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) {
try {
// // region-based
// // extra line when collapsed, but no region increase when add newline
// pos = new Position(start, end - start);
// // 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.getProjectionViewer() == null) {
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) {
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]);
}
ProjectionViewer viewer = fAdapterFactory.getProjectionViewer();
ProjectionAnnotationModel annotationModel = viewer.getProjectionAnnotationModel();
annotationModel.modifyAnnotations(oldList, additions, modifyList);
}
// save new list of annotations
fTagAnnotations = additions;
long end = System.currentTimeMillis();
if (debugProjectionPerf) {
String nodeName = node != null ? node.getNodeName() : "null"; //$NON-NLS-1$
System.out.println("ProjectionModelNodeAdapterHTML.updateAdapter (" + nodeName + "):" + (end - start)); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}