From 7640754a617d2a64db39f8261affb55b6b41df1c Mon Sep 17 00:00:00 2001 From: Kai Maetzel Date: Wed, 7 Apr 2004 19:57:45 +0000 Subject: projection summaries --- .../text/source/projection/AnnotationBag.java | 50 ++++++ .../projection/ProjectionAnnotationModel.java | 102 ++++++++++- .../text/source/projection/ProjectionSummary.java | 189 +++++++++++++++++++++ .../text/source/projection/ProjectionSupport.java | 47 ++++- 4 files changed, 380 insertions(+), 8 deletions(-) create mode 100644 org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/AnnotationBag.java create mode 100644 org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionSummary.java diff --git a/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/AnnotationBag.java b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/AnnotationBag.java new file mode 100644 index 000000000..830c9fed4 --- /dev/null +++ b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/AnnotationBag.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2000, 2003 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jface.text.source.projection; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.jface.text.source.Annotation; + +/** + * A bag of annotations. + */ +public class AnnotationBag extends Annotation { + + private Set fAnnotations; + + /** Creates a new annotation bag. + * + * @param type the annotation type + */ + public AnnotationBag(String type) { + super(type, false, null); + } + + public void add(Annotation annotation) { + if (fAnnotations == null) + fAnnotations= new HashSet(2); + fAnnotations.add(annotation); + } + + public void remove(Annotation annotation) { + if (fAnnotations != null) { + fAnnotations.remove(annotation); + if (fAnnotations.isEmpty()) + fAnnotations= null; + } + } + + public boolean isEmpty() { + return fAnnotations == null; + } +} diff --git a/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionAnnotationModel.java b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionAnnotationModel.java index aac5b2736..72c9779eb 100644 --- a/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionAnnotationModel.java +++ b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionAnnotationModel.java @@ -11,7 +11,11 @@ package org.eclipse.jface.text.source.projection; import java.util.Iterator; +import java.util.Map; +import org.eclipse.core.runtime.NullProgressMonitor; + +import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.AnnotationModel; @@ -25,6 +29,61 @@ import org.eclipse.jface.text.source.AnnotationModel; * @since 3.0 */ public class ProjectionAnnotationModel extends AnnotationModel { + + + private class Summarizer extends Thread { + + public Summarizer() { + setDaemon(true); + start(); + synchronized (fLock) { + fHasStarted= false; + } + } + + /* + * @see java.lang.Thread#run() + */ + public void run() { + + synchronized (fLock) { + fHasStarted= true; + } + + fProjectionSummary.updateSummaries(new NullProgressMonitor()); + + synchronized (fLock) { + if (fSummarizer == this) + fSummarizer= null; + } + } + } + + + private ProjectionSummary fProjectionSummary; + + private Object fLock= new Object(); + private volatile Summarizer fSummarizer; + private volatile boolean fHasStarted= false; + + + public ProjectionAnnotationModel(ProjectionSummary summary) { + fProjectionSummary= summary; + } + + public void setProjectionSummary(ProjectionSummary summary) { + fProjectionSummary= summary; + } + + private void updateSummaries() { + if (fProjectionSummary == null) + return; + + synchronized (fLock) { + if (fSummarizer == null || fHasStarted) + fSummarizer= new Summarizer(); + } + } /** * Changes the state of the given annotation to collapsed. An appropriate @@ -38,6 +97,7 @@ public class ProjectionAnnotationModel extends AnnotationModel { if (!projection.isCollapsed()) { projection.markCollapsed(); modifyAnnotation(projection, true); + updateSummaries(); } } } @@ -54,6 +114,7 @@ public class ProjectionAnnotationModel extends AnnotationModel { if (projection.isCollapsed()) { projection.markExpanded(); modifyAnnotation(projection, true); + updateSummaries(); } } } @@ -74,6 +135,7 @@ public class ProjectionAnnotationModel extends AnnotationModel { projection.markCollapsed(); modifyAnnotation(projection, true); + updateSummaries(); } } @@ -85,6 +147,20 @@ public class ProjectionAnnotationModel extends AnnotationModel { * @return true if any annotation has been expanded, false otherwise */ public boolean expandAll(int offset, int length) { + return expandAll(offset, length, true); + } + + /** + * Expands all annotations that overlap with the given range and are collapsed. Fires a model change event if + * requested. + * + * @param offset the offset of the range + * @param length the length of the range + * @param fireModelChanged true if a model change event + * should be fired, false otherwise + * @return true if any annotation has been expanded, false otherwise + */ + protected boolean expandAll(int offset, int length, boolean fireModelChanged) { boolean expanding= false; @@ -101,9 +177,31 @@ public class ProjectionAnnotationModel extends AnnotationModel { } } - if (expanding) - fireModelChanged(); + if (expanding) { + if (fireModelChanged) + fireModelChanged(); + updateSummaries(); + } return expanding; } + + /** + * Modifies the annotation model. + * + * @param deletions the list of deleted annotations + * @param additions the set of annotations to add together with their associated position + * @param modifications the list of modified annotations + */ + public void modifyAnnotations(Annotation[] deletions, Map additions, Annotation[] modifications) { + try { + replaceAnnotations(deletions, additions, false); + if (modifications != null) { + for (int i= 0; i < modifications.length; i++) + modifyAnnotation(modifications[i], false); + } + } catch (BadLocationException x) { + } + fireModelChanged(); + } } \ No newline at end of file diff --git a/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionSummary.java b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionSummary.java new file mode 100644 index 000000000..814325b73 --- /dev/null +++ b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionSummary.java @@ -0,0 +1,189 @@ +/******************************************************************************* + * Copyright (c) 2000, 2003 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jface.text.source.projection; + +import org.eclipse.core.runtime.IProgressMonitor; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.IAnnotationAccess; +import org.eclipse.jface.text.source.IAnnotationAccessExtension; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.text.source.IAnnotationModelExtension; + +/** + * Strategy for managing annotation summaries for collapsed ranges. + * + * @since 3.0 + */ +public class ProjectionSummary { + + private ProjectionViewer fProjectionViewer; + private IAnnotationModel fAnnotationModel; + private IAnnotationAccess fAnnotationAccess; + private List fConfiguredAnnotationTypes; + + public ProjectionSummary(ProjectionViewer projectionViewer, IAnnotationAccess annotationAccess) { + super(); + fProjectionViewer= projectionViewer; + fAnnotationAccess= annotationAccess; + } + + public void addAnnotationType(String annotationType) { + if (fConfiguredAnnotationTypes == null) { + fConfiguredAnnotationTypes= new ArrayList(); + fConfiguredAnnotationTypes.add(annotationType); + } else if (!fConfiguredAnnotationTypes.contains(annotationType)) + fConfiguredAnnotationTypes.add(annotationType); + } + + public void removeAnnotationType(String annotationType) { + if (fConfiguredAnnotationTypes != null) { + fConfiguredAnnotationTypes.remove(annotationType); + if (fConfiguredAnnotationTypes.size() == 0) + fConfiguredAnnotationTypes= null; + } + } + + public void updateSummaries(IProgressMonitor monitor) { + fAnnotationModel= fProjectionViewer.getVisualAnnotationModel(); + removeSummaries(monitor); + createSummaries(monitor); + } + + private boolean isCanceled(IProgressMonitor monitor) { + return monitor != null && monitor.isCanceled(); + } + + public void removeSummaries(IProgressMonitor monitor) { + IAnnotationModelExtension extension= null; + List bags= null; + + if (fAnnotationModel instanceof IAnnotationModelExtension) { + extension= (IAnnotationModelExtension) fAnnotationModel; + bags= new ArrayList(); + } + + Iterator e= fAnnotationModel.getAnnotationIterator(); + while (e.hasNext()) { + Annotation annotation= (Annotation) e.next(); + if (annotation instanceof AnnotationBag) { + if (bags == null) + fAnnotationModel.removeAnnotation(annotation); + else + bags.add(annotation); + } + + if (isCanceled(monitor)) + return; + } + + if (bags != null && bags.size() > 0) { + Annotation[] deletions= new Annotation[bags.size()]; + bags.toArray(deletions); + if (!isCanceled(monitor)) + extension.replaceAnnotations(deletions, null); + } + } + + public void createSummaries(IProgressMonitor monitor) { + Map additions= new HashMap(); + + ProjectionAnnotationModel model= fProjectionViewer.getProjectionAnnotationModel(); + Iterator e= model.getAnnotationIterator(); + while (e.hasNext()) { + ProjectionAnnotation projection= (ProjectionAnnotation) e.next(); + if (projection.isCollapsed()) { + Position position= model.getPosition(projection); + IRegion summaryRegion= fProjectionViewer.computeCollapsedRegion(position); + if (summaryRegion != null) { + Position summaryAnchor= fProjectionViewer.computeCollapsedRegionAnchor(position); + if (summaryAnchor != null) + createSummary(additions, summaryRegion, summaryAnchor); + } + } + + if (isCanceled(monitor)) + return; + } + + if (additions.size() > 0) { + if (fAnnotationModel instanceof IAnnotationModelExtension) { + IAnnotationModelExtension extension= (IAnnotationModelExtension) fAnnotationModel; + if (!isCanceled(monitor)) + extension.replaceAnnotations(null, additions); + } else { + Iterator e1= additions.keySet().iterator(); + while (e1.hasNext()) { + AnnotationBag bag= (AnnotationBag) e1.next(); + Position position= (Position) additions.get(bag); + if (isCanceled(monitor)) + return; + fAnnotationModel.addAnnotation(bag, position); + } + } + } + } + + private void createSummary(Map additions, IRegion summaryRange, Position summaryAnchor) { + Map map= new HashMap(); + + int size= fConfiguredAnnotationTypes.size(); + for (int i= 0; i < size; i++) { + String type= (String) fConfiguredAnnotationTypes.get(i); + map.put(type, new AnnotationBag(type)); + } + + IAnnotationModel model= fProjectionViewer.getAnnotationModel(); + Iterator e= model.getAnnotationIterator(); + while (e.hasNext()) { + Annotation annotation= (Annotation) e.next(); + AnnotationBag bag= findBagForType(map, annotation.getType()); + if (bag != null) { + Position position= model.getPosition(annotation); + if (includes(summaryRange, position)) + bag.add(annotation); + } + } + + for (int i= 0; i < size; i++) { + AnnotationBag bag= (AnnotationBag) map.get(fConfiguredAnnotationTypes.get(i)); + if (!bag.isEmpty()) + additions.put(bag, new Position(summaryAnchor.getOffset(), summaryAnchor.getLength())); + } + } + + private AnnotationBag findBagForType(Map bagMap, String annotationType) { + if (fAnnotationAccess instanceof IAnnotationAccessExtension) { + IAnnotationAccessExtension extension= (IAnnotationAccessExtension) fAnnotationAccess; + Object[] superTypes= extension.getSupertypes(annotationType); + for (int i= 0; i < superTypes.length; i++) { + AnnotationBag bag= (AnnotationBag) bagMap.get(superTypes[i]); + if (bag != null) + return bag; + } + } + return null; + } + + private boolean includes(IRegion range, Position position) { + if (!position.isDeleted()) + return range.getOffset() <= position.getOffset() && position.getOffset() + position.getLength() <= range.getOffset() + range.getLength(); + return false; + } +} diff --git a/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionSupport.java b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionSupport.java index d86034995..13a5ef92f 100644 --- a/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionSupport.java +++ b/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionSupport.java @@ -10,6 +10,9 @@ *******************************************************************************/ package org.eclipse.jface.text.source.projection; +import java.util.ArrayList; +import java.util.List; + import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.custom.StyledTextContent; @@ -99,13 +102,26 @@ public class ProjectionSupport { } private final static Object PROJECTION= new Object(); + private List fSummarizableTypes; - private static RGB getColor() { - // TODO read out preference settings - Color c= Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY); - return c.getRGB(); + public ProjectionSupport() { } + public void addSummarizableAnnotationType(String annotationType) { + if (fSummarizableTypes == null) { + fSummarizableTypes= new ArrayList(); + fSummarizableTypes.add(annotationType); + } else if (!fSummarizableTypes.contains(annotationType)) + fSummarizableTypes.add(annotationType); + } + + public void removeSummarizableAnnotationType(String annotationType) { + if (fSummarizableTypes != null) + fSummarizableTypes.remove(annotationType); + if (fSummarizableTypes.size() == 0) + fSummarizableTypes= null; + } + /** * Enable projection for the given viewer * @@ -113,11 +129,13 @@ public class ProjectionSupport { * @param annotationAccess the annotation access * @param sharedTextColors the shared text colors */ - public static void enableProjection(ISourceViewer viewer, IAnnotationAccess annotationAccess, ISharedTextColors sharedTextColors) { + public void enableProjection(ISourceViewer viewer, IAnnotationAccess annotationAccess, ISharedTextColors sharedTextColors) { if (viewer instanceof ProjectionViewer) { ProjectionViewer projectionViewer= (ProjectionViewer) viewer; + projectionViewer.setProjectionSummary(createProjectionSummary(projectionViewer, annotationAccess)); + AnnotationPainter painter= new ProjectionAnnotationsPainter(projectionViewer, annotationAccess); painter.addDrawingStrategy(PROJECTION, new ProjectionDrawingStrategy()); painter.addAnnotationType(ProjectionAnnotation.TYPE, PROJECTION); @@ -126,15 +144,26 @@ public class ProjectionSupport { ProjectionRulerColumn column= new ProjectionRulerColumn(projectionViewer.getProjectionAnnotationModel(), 9, annotationAccess); column.addAnnotationType(ProjectionAnnotation.TYPE); + // TODO make hover configurable from outside column.setHover(new ProjectionAnnotationHover()); projectionViewer.addVerticalRulerColumn(column); } } + + private ProjectionSummary createProjectionSummary(ProjectionViewer projectionViewer, IAnnotationAccess annotationAccess) { + ProjectionSummary summary= new ProjectionSummary(projectionViewer, annotationAccess); + if (fSummarizableTypes != null) { + int size= fSummarizableTypes.size(); + for (int i= 0; i < size; i++) + summary.addAnnotationType((String) fSummarizableTypes.get(i)); + } + return summary; + } /** * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) */ - public static Object getAdapter(ISourceViewer viewer, Class required) { + public Object getAdapter(ISourceViewer viewer, Class required) { if (ProjectionAnnotationModel.class.equals(required)) { if (viewer instanceof ProjectionViewer) { ProjectionViewer projectionViewer= (ProjectionViewer) viewer; @@ -143,4 +172,10 @@ public class ProjectionSupport { } return null; } + + private RGB getColor() { + // TODO read out preference settings + Color c= Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY); + return c.getRGB(); + } } -- cgit v1.2.3