/*
* $Id$
*
* Copyright (c) 2004-2005 by the TeXlapse Team.
* 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
*/
package net.sourceforge.texlipse.editor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import net.sourceforge.texlipse.TexlipsePlugin;
import net.sourceforge.texlipse.model.OutlineNode;
import net.sourceforge.texlipse.properties.TexlipseProperties;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
/**
* Updates code folding marks into the given editor.
*
* @author Oskar Ojala
*/
public class TexCodeFolder {
private TexEditor editor;
private ProjectionAnnotationModel model;
// private ArrayList<TexProjectionAnnotation> oldNodes;
private ArrayList oldNodes;
private boolean firstRun;
private HashSet environments;
private boolean preamble;
private boolean part;
private boolean chapter;
private boolean section;
private boolean subs;
private boolean subsubs;
private boolean paragraph;
/**
* Creates a new code folder.
*
* @param editor The editor to which this folder is associated
*/
public TexCodeFolder(TexEditor editor) {
this.editor = editor;
firstRun = true;
}
/**
* Updates the code folds of the editor.
*
* @param outline The document outline data structure containing the document positions
*/
public void update(ArrayList outline) {
model = (ProjectionAnnotationModel)editor.getAdapter(ProjectionAnnotationModel.class);
if (model != null) {
this.addMarks(outline);
}
}
/**
* Adds the folding marks to the editor and removes redundant marks.
*
* @param outline The document outline data structure containing the document positions
*/
private void addMarks(ArrayList outline) {
if (firstRun) {
String[] envs = TexlipsePlugin.getPreferenceArray(TexlipseProperties.CODE_FOLDING_ENVS);
environments = new HashSet(envs.length + 1);
for (int i = 0; i < envs.length; i++)
environments.add(envs[i]);
preamble = TexlipsePlugin.getDefault().getPreferenceStore().getBoolean(TexlipseProperties.CODE_FOLDING_PREAMBLE);
part = TexlipsePlugin.getDefault().getPreferenceStore().getBoolean(TexlipseProperties.CODE_FOLDING_PART);
chapter = TexlipsePlugin.getDefault().getPreferenceStore().getBoolean(TexlipseProperties.CODE_FOLDING_CHAPTER);
section = TexlipsePlugin.getDefault().getPreferenceStore().getBoolean(TexlipseProperties.CODE_FOLDING_SECTION);
subs = TexlipsePlugin.getDefault().getPreferenceStore().getBoolean(TexlipseProperties.CODE_FOLDING_SUBSECTION);
subsubs = TexlipsePlugin.getDefault().getPreferenceStore().getBoolean(TexlipseProperties.CODE_FOLDING_SUBSUBSECTION);
paragraph = TexlipsePlugin.getDefault().getPreferenceStore().getBoolean(TexlipseProperties.CODE_FOLDING_PARAGRAPH);
Map map = new HashMap();
fillAnnotationMap(outline, map);
model.modifyAnnotations(null, map, null);
firstRun = false;
environments = null; // frees up the memory
} else {
// save old nodes
oldNodes = new ArrayList();
for (Iterator iter = model.getAnnotationIterator(); iter.hasNext();) {
oldNodes.add((TexProjectionAnnotation) iter.next());
}
markTreeNodes(outline);
TexProjectionAnnotation[] deletes = new TexProjectionAnnotation[oldNodes.size()];
oldNodes.toArray(deletes);
model.modifyAnnotations(deletes, null, null);
}
}
/**
* Traverses the <code>documentTree</code> and updates each node's
* corresponding marker.
*
* @param documentTree The document outline data structure containing the document positions
*/
private void markTreeNodes(ArrayList documentTree) {
for (ListIterator iter = documentTree.listIterator(); iter.hasNext();) {
OutlineNode on = (OutlineNode) iter.next();
// Here, call the appropriate method on the node
inspectAndAddMark(on);
// ...and recurse over the children...
if (on.getChildren() != null)
markTreeNodes(on.getChildren());
}
}
/**
* Inspects a folding mark and if necessary adds a new mark.
*
* @param node The node to inspect
*/
private void inspectAndAddMark(OutlineNode node) {
Position pos = node.getPosition();
for (ListIterator iter = oldNodes.listIterator(); iter.hasNext();) {
TexProjectionAnnotation cAnnotation = (TexProjectionAnnotation) iter.next();
if (cAnnotation.likelySame(node)) {
oldNodes.remove(cAnnotation);
//model.modifyAnnotationPosition(cAnnotation, pos);
return;
}
}
model.addAnnotation(new TexProjectionAnnotation(node), pos);
}
/**
* Creates an annotation of every node in <code>documentTree</code> and
* puts the annotations into the given map. Annotations of certain types
* defined in the preferences are automatically folded.
*
* @param documentTree The document outline tree
* @param map A <code>Map</code> where to put the annotations
*/
private void fillAnnotationMap(List documentTree, Map map) {
for (ListIterator iter = documentTree.listIterator(); iter.hasNext();) {
OutlineNode node = (OutlineNode) iter.next();
Position pos = node.getPosition();
boolean folding = false;
// strictly speaking object-oriented code should not need switch-statements,
// but this a lot faster than some object-approach
switch (node.getType()) {
case OutlineNode.TYPE_PREAMBLE:
folding = this.preamble;
break;
case OutlineNode.TYPE_PART:
folding = this.part;
break;
case OutlineNode.TYPE_CHAPTER:
folding = this.chapter;
break;
case OutlineNode.TYPE_SECTION:
folding = this.section;
break;
case OutlineNode.TYPE_SUBSECTION:
folding = this.subs;
break;
case OutlineNode.TYPE_SUBSUBSECTION:
folding = this.subsubs;
break;
case OutlineNode.TYPE_PARAGRAPH:
folding = this.paragraph;
break;
case OutlineNode.TYPE_ENVIRONMENT:
if (environments.contains(node.getName()))
folding = true;
break;
default:
break;
}
TexProjectionAnnotation tpa = new TexProjectionAnnotation(node, folding);
map.put(tpa, pos);
if (node.getChildren() != null)
fillAnnotationMap(node.getChildren(), map);
}
}
}