Reimplementation of DefaultOutlineProvider

The OutlineProvider now supports display of inline elements and element
content.

Change-Id: I479ac9556b6a1335f435493bab2133166802b33a
Signed-off-by: Carsten Hiesserich <carsten.hie@gmail.com>
diff --git a/org.eclipse.vex.ui.tests/.classpath b/org.eclipse.vex.ui.tests/.classpath
index a955886..288a780 100644
--- a/org.eclipse.vex.ui.tests/.classpath
+++ b/org.eclipse.vex.ui.tests/.classpath
@@ -1,7 +1,7 @@
-<?xml version="1.0" encoding="UTF-8"?>

-<classpath>

-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>

-	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>

-	<classpathentry kind="src" path="src/"/>

-	<classpathentry kind="output" path="target/classes"/>

-</classpath>

+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/org.eclipse.vex.ui.tests/META-INF/MANIFEST.MF b/org.eclipse.vex.ui.tests/META-INF/MANIFEST.MF
index 838b08a..afe1639 100644
--- a/org.eclipse.vex.ui.tests/META-INF/MANIFEST.MF
+++ b/org.eclipse.vex.ui.tests/META-INF/MANIFEST.MF
@@ -11,7 +11,8 @@
  org.eclipse.vex.core.tests;bundle-version="[1.0.0,2.0.0)",
  org.eclipse.vex.ui;bundle-version="[1.0.0,2.0.0)",
  org.junit;bundle-version="3.8.1",
- org.eclipse.jface.text;bundle-version="3.0.0"
+ org.eclipse.jface.text;bundle-version="3.0.0",
+ org.w3c.css.sac;bundle-version="[1.3.0,2.0.0)"
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
 Export-Package: org.eclipse.vex.ui.internal.config.tests;x-internal:=true,
  org.eclipse.vex.ui.internal.editor.tests;x-internal:=true,
diff --git a/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/internal/outline/tests/OutlineProviderTest.java b/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/internal/outline/tests/OutlineProviderTest.java
new file mode 100644
index 0000000..9b759b4
--- /dev/null
+++ b/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/internal/outline/tests/OutlineProviderTest.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Carsten Hiesserich 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:
+ *     Carsten Hiesserich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.ui.internal.outline.tests;
+
+import static org.junit.Assert.assertEquals;
+
+import java.net.URL;
+
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.vex.core.internal.css.StyleSheet;
+import org.eclipse.vex.core.internal.css.StyleSheetReader;
+import org.eclipse.vex.core.internal.dom.Document;
+import org.eclipse.vex.core.internal.io.XMLFragment;
+import org.eclipse.vex.core.provisional.dom.IDocument;
+import org.eclipse.vex.core.provisional.dom.IDocumentFragment;
+import org.eclipse.vex.core.provisional.dom.IElement;
+import org.eclipse.vex.ui.internal.outline.DefaultOutlineProvider;
+import org.junit.Before;
+import org.junit.Test;
+
+@SuppressWarnings("restriction")
+public class OutlineProviderTest {
+
+	private DefaultOutlineProvider outlineProvider;
+
+	@Before
+	public void setUp() throws Exception {
+		final URL url = this.getClass().getResource("/tests/resources/outlineTest.css");
+		final StyleSheet styleSheet = new StyleSheetReader().read(url);
+		outlineProvider = new DefaultOutlineProvider();
+		outlineProvider.init(styleSheet);
+	}
+
+	@Test
+	public void testContentProvider() throws Exception {
+		final IDocument doc = new Document(new QualifiedName(null, "root"));
+		final IElement parent = doc.insertElement(2, new QualifiedName(null, "parent"));
+		doc.insertElement(parent.getEndOffset(), new QualifiedName(null, "child"));
+		doc.insertElement(parent.getEndOffset(), new QualifiedName(null, "child"));
+
+		final Object[] outlineElements = outlineProvider.getContentProvider().getElements(doc);
+		assertEquals("Count of root elements", 1, outlineElements.length);
+		assertEquals(parent, outlineElements[0]);
+		final Object[] childElements = outlineProvider.getContentProvider().getChildren(parent);
+		assertEquals("Count of child elements", 2, childElements.length);
+	}
+
+	@Test
+	public void testLabelProviderWithoutContent() throws Exception {
+		final IDocument doc = new Document(new QualifiedName(null, "root"));
+		final IDocumentFragment fragment = new XMLFragment("<parent><title>titleText</title></parent>").getDocumentFragment();
+		doc.insertFragment(2, fragment);
+		final Object[] outlineElements = outlineProvider.getContentProvider().getElements(doc);
+		assertEquals("Count of root elements", 1, outlineElements.length);
+
+		outlineProvider.setState(DefaultOutlineProvider.SHOW_ELEMENT_CONTENT, false);
+
+		final IElement parent = (IElement) outlineElements[0];
+		assertEquals("Should return local name", "parent", outlineProvider.getOutlineLabel(parent).getString());
+
+		final IElement title = (IElement) outlineProvider.getContentProvider().getChildren(outlineElements[0])[0];
+		assertEquals("Should return local name", "title", outlineProvider.getOutlineLabel(title).getString());
+	}
+
+	@Test
+	public void testLabelProviderWithContent() throws Exception {
+		final IDocument doc = new Document(new QualifiedName(null, "root"));
+		final IDocumentFragment fragment = new XMLFragment("<parent><title>titleText</title></parent>").getDocumentFragment();
+		doc.insertFragment(2, fragment);
+		final Object[] outlineElements = outlineProvider.getContentProvider().getElements(doc);
+		assertEquals("Count of root elements", 1, outlineElements.length);
+
+		outlineProvider.setState(DefaultOutlineProvider.SHOW_ELEMENT_CONTENT, true);
+
+		final IElement parent = (IElement) outlineElements[0];
+		assertEquals("Should return local name:title content", "parent : titleText", outlineProvider.getOutlineLabel(parent).getString());
+
+		final IElement title = (IElement) outlineProvider.getContentProvider().getChildren(outlineElements[0])[0];
+		assertEquals("Should return local name:content", "title : titleText", outlineProvider.getOutlineLabel(title).getString());
+	}
+}
diff --git a/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/tests/VexUiTestSuite.java b/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/tests/VexUiTestSuite.java
index 571cb54..97aa94c 100644
--- a/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/tests/VexUiTestSuite.java
+++ b/org.eclipse.vex.ui.tests/src/org/eclipse/vex/ui/tests/VexUiTestSuite.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2009 Holger Voormann and others.
+ * Copyright (c) 2009, 2013 Holger Voormann 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
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *     Holger Voormann - initial API and implementation
+ *     Carsten Hiesserich - added OutlineProvider tests
  *******************************************************************************/
 package org.eclipse.vex.ui.tests;
 
@@ -14,10 +15,11 @@
 import org.eclipse.vex.ui.internal.config.tests.ConfigurationRegistryTest;
 import org.eclipse.vex.ui.internal.editor.tests.FindReplaceTargetTest;
 import org.eclipse.vex.ui.internal.namespace.tests.EditNamespacesControllerTest;
+import org.eclipse.vex.ui.internal.outline.tests.OutlineProviderTest;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
 
 @RunWith(Suite.class)
-@Suite.SuiteClasses({ ConfigLoaderJobTest.class, ConfigurationRegistryTest.class, EditNamespacesControllerTest.class, FindReplaceTargetTest.class })
+@Suite.SuiteClasses({ ConfigLoaderJobTest.class, ConfigurationRegistryTest.class, EditNamespacesControllerTest.class, FindReplaceTargetTest.class, OutlineProviderTest.class })
 public class VexUiTestSuite {
 }
diff --git a/org.eclipse.vex.ui.tests/src/tests/resources/outlineTest.css b/org.eclipse.vex.ui.tests/src/tests/resources/outlineTest.css
new file mode 100644
index 0000000..66b7460
--- /dev/null
+++ b/org.eclipse.vex.ui.tests/src/tests/resources/outlineTest.css
@@ -0,0 +1,13 @@
+/* Styles for OutlineProvider tests */
+parent {
+  display: block;
+  -vex-outline-content: title;
+}
+
+title {
+  display: inline;
+}
+
+child {
+  display: block;
+}
\ No newline at end of file
diff --git a/org.eclipse.vex.ui/icons/attribute_obj.gif b/org.eclipse.vex.ui/icons/attribute_obj.gif
new file mode 100644
index 0000000..79d49d0
--- /dev/null
+++ b/org.eclipse.vex.ui/icons/attribute_obj.gif
Binary files differ
diff --git a/org.eclipse.vex.ui/icons/comment_obj.gif b/org.eclipse.vex.ui/icons/comment_obj.gif
new file mode 100644
index 0000000..28c2ccb
--- /dev/null
+++ b/org.eclipse.vex.ui/icons/comment_obj.gif
Binary files differ
diff --git a/org.eclipse.vex.ui/icons/hide_inline.gif b/org.eclipse.vex.ui/icons/hide_inline.gif
new file mode 100644
index 0000000..fa3fe0d
--- /dev/null
+++ b/org.eclipse.vex.ui/icons/hide_inline.gif
Binary files differ
diff --git a/org.eclipse.vex.ui/icons/show_content.gif b/org.eclipse.vex.ui/icons/show_content.gif
new file mode 100644
index 0000000..028a2e8
--- /dev/null
+++ b/org.eclipse.vex.ui/icons/show_content.gif
Binary files differ
diff --git a/org.eclipse.vex.ui/icons/unknown_obj.gif b/org.eclipse.vex.ui/icons/unknown_obj.gif
new file mode 100644
index 0000000..c11fbc9
--- /dev/null
+++ b/org.eclipse.vex.ui/icons/unknown_obj.gif
Binary files differ
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/PluginImages.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/PluginImages.java
new file mode 100644
index 0000000..6b7b96b
--- /dev/null
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/PluginImages.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Carsten Hiesserich 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:
+ *     Carsten Hiesserich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.ui.internal;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+public class PluginImages {
+
+	private static ImageRegistry IMAGE_REGISTRY;
+
+	// Outline toolbar actions
+	public static final ImageDescriptor DESC_HIDE_INLINE_ELEMENTS = createImageDescriptor("icons/hide_inline.gif"); //$NON-NLS-1$
+	public static final ImageDescriptor DESC_SHOW_ELEMENT_CONTENT = createImageDescriptor("icons/show_content.gif"); //$NON-NLS-1$
+
+	// Element Icons
+	/** XML element */
+	public static final ImageDescriptor DESC_XML_ELEMENT = createImageDescriptor("icons/element_obj.gif"); //$NON-NLS-1$
+	/** XML comment. */
+	public static final ImageDescriptor DESC_XML_COMMENT = createImageDescriptor("icons/comment_obj.gif"); //$NON-NLS-1$
+	/** XML attribute. */
+	public static final ImageDescriptor DESC_XML_ATTRIBUTE = createImageDescriptor("icons/attribute_obj.gif"); //$NON-NLS-1$
+	/** XML unknown object. */
+	public static final ImageDescriptor DESC_XML_UNKNOWN = createImageDescriptor("icons/unknown_obj.gif"); //$NON-NLS-1$
+
+	private static ImageDescriptor createImageDescriptor(final String filePath) {
+		return AbstractUIPlugin.imageDescriptorFromPlugin(VexPlugin.ID, filePath);
+	}
+
+	// ImageRegistry for frequently used images
+	private static final String NAME_PREFIX = VexPlugin.ID + "."; //$NON-NLS-1$
+	public static final String IMG_XML_ELEMENT = NAME_PREFIX + "img.xml_element"; //$NON-NLS-1$
+	public static final String IMG_XML_COMMENT = NAME_PREFIX + "img.xml_comment"; //$NON-NLS-1$
+	public static final String IMG_XML_ATTRIBUTE = NAME_PREFIX + "img.xml_attribute"; //$NON-NLS-1$
+	public static final String IMG_XML_UNKNOWN = NAME_PREFIX + "img.xml_unknown"; //$NON-NLS-1$
+
+	public static Image get(final String key) {
+		if (IMAGE_REGISTRY == null) {
+			initializeImageRegistry();
+		}
+		return IMAGE_REGISTRY.get(key);
+	}
+
+	private static final void initializeImageRegistry() {
+		IMAGE_REGISTRY = new ImageRegistry();
+		register(IMG_XML_ELEMENT, DESC_XML_ELEMENT);
+		register(IMG_XML_COMMENT, DESC_XML_COMMENT);
+		register(IMG_XML_ATTRIBUTE, DESC_XML_ATTRIBUTE);
+		register(IMG_XML_UNKNOWN, DESC_XML_UNKNOWN);
+	}
+
+	public static void register(final String key, final ImageDescriptor desc) {
+		final Image image = desc.createImage();
+		IMAGE_REGISTRY.put(key, image);
+	}
+}
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/editor/VexEditor.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/editor/VexEditor.java
index fe87ccf..1d88f1e 100644
--- a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/editor/VexEditor.java
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/editor/VexEditor.java
@@ -817,7 +817,7 @@
 	@Override
 	public Object getAdapter(@SuppressWarnings("rawtypes") final Class adapter) {
 		if (adapter == IContentOutlinePage.class) {
-			return new DocumentOutlinePage();
+			return new DocumentOutlinePage(this);
 		} else if (adapter == IPropertySheetPage.class) {
 			final PropertySheetPage page = new PropertySheetPage();
 			page.setPropertySourceProvider(new IPropertySourceProvider() {
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/editor/messages.properties b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/editor/messages.properties
index 74dbe90..c7d72e2 100644
--- a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/editor/messages.properties
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/editor/messages.properties
@@ -1,5 +1,5 @@
 ###############################################################################
-# Copyright (c) 2004, 2010 John Krasnay and others.
+# Copyright (c) 2004, 2013 John Krasnay 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
@@ -8,6 +8,7 @@
 # Contributors:
 #     John Krasnay - initial API and implementation
 #     Igor Jacy Lino Campista - bug 283976 - changed URL in error to WTP incubator bugzilla
+#     Added DocumentOutlinePage.action properties
 ###############################################################################
 #
 # Language-specific strings in the vex-editor plugin
@@ -26,6 +27,10 @@
 DocumentOutlinePage.loadingError=Exception while loading {0} from bundle {1}: {2}
 DocumentOutlinePage.loading=Loading...
 DocumentOutlinePage.reloading=Reloading...
+DocumentOutlinePage.action.hideInlineElements.title=Hide Inline Elements
+DocumentOutlinePage.action.hideInlineElements.tooltip=Hide Inline Elements
+DocumentOutlinePage.action.showElementContent.title=Show Element Content
+DocumentOutlinePage.action.showElementContent.tooltip=Show Element Content
 
 Messages.cantFindResource=Can''t find resource string {0}
 
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/DefaultOutlineProvider.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/DefaultOutlineProvider.java
index 6af2965..9ceec3f 100644
--- a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/DefaultOutlineProvider.java
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/DefaultOutlineProvider.java
@@ -1,48 +1,75 @@
 /*******************************************************************************
- * Copyright (c) 2004, 2008 John Krasnay and others.
+ * Copyright (c) 2004, 2013 John Krasnay 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:
  *     John Krasnay - initial API and implementation
  *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
+ *     Carsten Hiesserich - Added support for view states and content display
  *******************************************************************************/
 package org.eclipse.vex.ui.internal.outline;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 
+import org.eclipse.core.runtime.preferences.InstanceScope;
 import org.eclipse.jface.viewers.IBaseLabelProvider;
 import org.eclipse.jface.viewers.ITreeContentProvider;
-import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.StyledCellLabelProvider;
+import org.eclipse.jface.viewers.StyledString;
 import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.vex.core.internal.css.IWhitespacePolicy;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.vex.core.XML;
+import org.eclipse.vex.core.internal.css.StyleSheet;
+import org.eclipse.vex.core.provisional.dom.BaseNodeVisitorWithResult;
+import org.eclipse.vex.core.provisional.dom.IAttribute;
 import org.eclipse.vex.core.provisional.dom.IDocument;
 import org.eclipse.vex.core.provisional.dom.IElement;
+import org.eclipse.vex.core.provisional.dom.INode;
+import org.eclipse.vex.core.provisional.dom.INodeVisitorWithResult;
+import org.eclipse.vex.core.provisional.dom.IParent;
+import org.eclipse.vex.core.provisional.dom.IText;
+import org.eclipse.vex.ui.internal.PluginImages;
+import org.eclipse.vex.ui.internal.VexPlugin;
+import org.eclipse.vex.ui.internal.editor.EditorEventAdapter;
+import org.eclipse.vex.ui.internal.editor.IVexEditorListener;
 import org.eclipse.vex.ui.internal.editor.VexEditor;
+import org.eclipse.vex.ui.internal.editor.VexEditorEvent;
+import org.osgi.service.prefs.Preferences;
 
 /**
- * Default implementation of IOutlineProvider. Simply displays all block-level elements.
+ * Default implementation of IOutlineProvider.
  */
-public class DefaultOutlineProvider implements IOutlineProvider {
+public class DefaultOutlineProvider implements IOutlineProvider, IToolBarContributor {
+
+	/* Display the element content */
+	public static final String SHOW_ELEMENT_CONTENT = "showElementContent";
+
+	private ITreeContentProvider contentProvider;
+	private IBaseLabelProvider labelProvider;
+
+	/* The 'Show Element Content' command state */
+	private boolean showElementContent = false;
+
+	/* The maximum length of the shown element content */
+	private static final int MAX_CONTENT_LENGTH = 30;
 
 	public void init(final VexEditor editor) {
-		whitespacePolicy = editor.getVexWidget().getWhitespacePolicy();
-		contentProvider = new ContentProvider();
-		labelProvider = new LabelProvider() {
-			@Override
-			public String getText(final Object o) {
-				final IElement e = (IElement) o;
-				String s = e.getText();
-				if (s.length() > 30) {
-					s = s.substring(0, 30) + "..."; //$NON-NLS-1$
-				}
-				return e.getPrefixedName() + ": " + s; //$NON-NLS-1$
-			}
-		};
+		contentProvider = new OutlineContentProvider();
+		labelProvider = new OutlineLabelProvider(editor);
+		initStates();
+	}
 
+	public void init(final StyleSheet styleSheet) {
+		contentProvider = new OutlineContentProvider();
+		labelProvider = new OutlineLabelProvider(styleSheet);
+		initStates();
 	}
 
 	public ITreeContentProvider getContentProvider() {
@@ -54,73 +81,181 @@
 	}
 
 	public IElement getOutlineElement(final IElement child) {
-		IElement element = child;
-		while (element != null) {
-
-			// block element?
-			if (whitespacePolicy.isBlock(element)) {
-				return element;
-			}
-
-			// root?
-			final IElement parent = element.getParentElement();
-			if (parent == null) {
-				return element;
-			}
-			element = parent;
-		}
-		return element;
+		return child;
 	}
 
-	// ====================================================== PRIVATE
+	public void setState(final String commandId, final boolean state) {
+		if (commandId.equals(SHOW_ELEMENT_CONTENT)) {
+			showElementContent = state;
+		}
+	}
 
-	private IWhitespacePolicy whitespacePolicy;
-	private ITreeContentProvider contentProvider;
-	private IBaseLabelProvider labelProvider;
+	public void registerToolBarActions(final DocumentOutlinePage page, final IActionBars actionBars) {
+		actionBars.getToolBarManager().add(new ToolBarToggleAction(page, SHOW_ELEMENT_CONTENT, PluginImages.DESC_SHOW_ELEMENT_CONTENT));
+	}
 
-	private class ContentProvider implements ITreeContentProvider {
-
-		public Object[] getChildren(final Object parentElement) {
-			final List<IElement> blockChildren = new ArrayList<IElement>();
-			for (final IElement child : ((IElement) parentElement).childElements()) {
-				if (whitespacePolicy.isBlock(child)) {
-					blockChildren.add(child);
-				}
-			}
-			return blockChildren.toArray();
+	public boolean isStateSupported(final String commandId) {
+		if (commandId.equals(SHOW_ELEMENT_CONTENT)) {
+			return true;
 		}
 
-		public Object getParent(final Object element) {
-			return ((IElement) element).getParent();
-		}
+		return false;
+	}
 
-		public boolean hasChildren(final Object o) {
-			return hasBlockChild((IElement) o);
-		}
+	public StyledString getOutlineLabel(final IElement element) {
+		return ((OutlineLabelProvider) labelProvider).getElementLabel(element);
+	}
 
-		public Object[] getElements(final Object inputElement) {
-			return new Object[] { ((IDocument) inputElement).getRootElement() };
-			// return this.getChildren(inputElement);
+	public Image getOutlineImage(final IElement element) {
+		return ((OutlineLabelProvider) labelProvider).getElementImage(element);
+	}
+
+	private void initStates() {
+		final Preferences preferences = InstanceScope.INSTANCE.getNode(VexPlugin.ID);
+		if (preferences != null) {
+			showElementContent = preferences.getBoolean(SHOW_ELEMENT_CONTENT, true);
 		}
+	}
+
+	private class OutlineContentProvider implements ITreeContentProvider {
 
 		public void dispose() {
 		}
 
 		public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) {
-			// TODO Auto-generated method stub
-
 		}
 
-		// ====================================================== PRIVATE
+		public Object[] getChildren(final Object parentElement) {
+			return getOutlineChildren((IElement) parentElement);
+		}
 
-		private boolean hasBlockChild(final IElement element) {
+		public Object getParent(final Object element) {
+			final IElement parent = ((IElement) element).getParentElement();
+			if (parent == null) {
+				//return element;
+				return null;
+			} else {
+				return getOutlineElement(parent);
+			}
+		}
+
+		public boolean hasChildren(final Object element) {
+			return getOutlineChildren((IElement) element).length > 0;
+		}
+
+		public Object[] getElements(final Object inputElement) {
+			final IDocument document = (IDocument) inputElement;
+			final IElement root = document.getRootElement();
+			if (root.hasChildren()) {
+				return document.getRootElement().children().asList().toArray();
+			} else {
+				// Only show the root element if there are no childs
+				return new Object[] { root };
+			}
+		}
+
+		private IElement[] getOutlineChildren(final IElement element) {
+
+			final List<IElement> children = new ArrayList<IElement>();
 			for (final IElement child : element.childElements()) {
-				if (whitespacePolicy.isBlock(child)) {
-					return true;
+				children.add(child);
+			}
+			return children.toArray(new IElement[children.size()]);
+		}
+	}
+
+	private class OutlineLabelProvider extends StyledCellLabelProvider {
+
+		private VexEditor editor = null;
+		private IVexEditorListener listener = null;
+		private StyleSheet styleSheet;
+
+		public OutlineLabelProvider(final VexEditor editor) {
+			this(editor.getStyle().getStyleSheet());
+			this.editor = editor;
+			listener = new EditorEventAdapter() {
+				@Override
+				public void styleChanged(final VexEditorEvent event) {
+					styleSheet = event.getVexEditor().getStyle().getStyleSheet();
+				}
+			};
+			editor.addVexEditorListener(listener);
+		}
+
+		public OutlineLabelProvider(final StyleSheet styleSheet) {
+			this.styleSheet = styleSheet;
+		}
+
+		@Override
+		public void update(final ViewerCell cell) {
+			final IElement e = (IElement) cell.getElement();
+
+			final StyledString label = getElementLabel(e);
+			cell.setText(label.getString());
+			cell.setStyleRanges(label.getStyleRanges());
+
+			cell.setImage(getElementImage(e));
+			super.update(cell);
+		}
+
+		@Override
+		public void dispose() {
+			super.dispose();
+			if (editor != null) {
+				editor.removeVexEditorListener(listener);
+			}
+		}
+
+		public Image getElementImage(final IElement element) {
+			return element.accept(elementImageVisitor);
+		}
+
+		public StyledString getElementLabel(final IElement element) {
+			if (!showElementContent) {
+				return new StyledString(element.getLocalName());
+			}
+
+			final StyledString label = new StyledString(element.getLocalName());
+			String content = null;
+			// getOutlineContent returns either an IAttribute or an INode
+			final Object outlineElement = styleSheet.getStyles(element).getOutlineContent();
+			if (outlineElement != null) {
+				if (outlineElement instanceof IAttribute) {
+					content = ((IAttribute) outlineElement).getValue();
+				} else if (outlineElement instanceof IParent) {
+					content = "";
+					final Iterator<? extends INode> childIter = ((IParent) outlineElement).children().iterator();
+					while (content.length() < MAX_CONTENT_LENGTH && childIter.hasNext()) {
+						content += childIter.next().accept(new BaseNodeVisitorWithResult<String>("") {
+							@Override
+							public String visit(final IElement element) {
+								return element.getText();
+							}
+
+							@Override
+							public String visit(final IText text) {
+								return text.getText();
+							}
+						});
+					}
+					content = XML.compressWhitespace(content, false, false, false);
+					content = content.substring(0, Math.min(MAX_CONTENT_LENGTH, content.length()));
 				}
 			}
-			return false;
+
+			if (content != null && content.length() > 0) {
+				label.append(" : " + content, StyledString.DECORATIONS_STYLER);
+			}
+
+			return label;
 		}
+
+		private final INodeVisitorWithResult<Image> elementImageVisitor = new BaseNodeVisitorWithResult<Image>(PluginImages.get(PluginImages.IMG_XML_UNKNOWN)) {
+			@Override
+			public Image visit(final IElement element) {
+				return PluginImages.get(PluginImages.IMG_XML_ELEMENT);
+			}
+		};
 	}
 
 }
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/DocumentOutlinePage.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/DocumentOutlinePage.java
index c496a53..4e4916e 100644
--- a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/DocumentOutlinePage.java
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/DocumentOutlinePage.java
@@ -10,6 +10,8 @@
  *     Torsten Stolpmann - bug 257946 - fixed outline view to work with multipage editor.
  *     Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
  *     Carsten Hiesserich - Use EditorEventAdapter instead of IVexEditorListener
+ *     Carsten Hiesserich - complete revision
+ *                          Support for ToolBar and actions, performance optimization
  *******************************************************************************/
 package org.eclipse.vex.ui.internal.outline;
 
@@ -17,23 +19,32 @@
 
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.action.IToolBarManager;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.ISelectionChangedListener;
 import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.jface.viewers.StructuredSelection;
 import org.eclipse.jface.viewers.TreeViewer;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.BusyIndicator;
 import org.eclipse.swt.layout.FillLayout;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.TreeItem;
-import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IActionBars;
 import org.eclipse.ui.part.IPageSite;
 import org.eclipse.ui.part.Page;
 import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
 import org.eclipse.vex.core.internal.widget.swt.VexWidget;
+import org.eclipse.vex.core.provisional.dom.AttributeChangeEvent;
+import org.eclipse.vex.core.provisional.dom.ContentChangeEvent;
+import org.eclipse.vex.core.provisional.dom.IAttribute;
+import org.eclipse.vex.core.provisional.dom.IDocument;
+import org.eclipse.vex.core.provisional.dom.IDocumentListener;
 import org.eclipse.vex.core.provisional.dom.IElement;
+import org.eclipse.vex.core.provisional.dom.IParent;
+import org.eclipse.vex.core.provisional.dom.NamespaceDeclarationChangeEvent;
 import org.eclipse.vex.ui.internal.VexPlugin;
 import org.eclipse.vex.ui.internal.config.DocumentType;
 import org.eclipse.vex.ui.internal.editor.EditorEventAdapter;
@@ -50,12 +61,20 @@
  */
 public class DocumentOutlinePage extends Page implements IContentOutlinePage {
 
+	public DocumentOutlinePage(final VexEditor vexEditor) {
+		super();
+		this.vexEditor = vexEditor;
+	}
+
 	@Override
 	public void createControl(final Composite parent) {
 
 		composite = new Composite(parent, SWT.NONE);
 		composite.setLayout(new FillLayout());
 
+		vexEditor.addVexEditorListener(vexEditorListener);
+		vexEditor.getEditorSite().getSelectionProvider().addSelectionChangedListener(selectionListener);
+		registerToolbarActions(getSite().getActionBars());
 		if (vexEditor.isLoaded()) {
 			showTreeViewer();
 		} else {
@@ -68,6 +87,9 @@
 	public void dispose() {
 		vexEditor.removeVexEditorListener(vexEditorListener);
 		vexEditor.getEditorSite().getSelectionProvider().removeSelectionChangedListener(selectionListener);
+		if (filterActionGroup != null) {
+			filterActionGroup.dispose();
+		}
 	}
 
 	@Override
@@ -78,12 +100,6 @@
 	@Override
 	public void init(final IPageSite pageSite) {
 		super.init(pageSite);
-		final IEditorPart editor = pageSite.getPage().getActiveEditor();
-		if (editor instanceof VexEditor) {
-			vexEditor = (VexEditor) editor;
-		}
-		vexEditor.addVexEditorListener(vexEditorListener);
-		vexEditor.getEditorSite().getSelectionProvider().addSelectionChangedListener(selectionListener);
 	}
 
 	@Override
@@ -111,21 +127,42 @@
 
 	public void removeSelectionChangedListener(final ISelectionChangedListener listener) {
 		selectionProvider.removeSelectionChangedListener(listener);
-
 	}
 
 	public void setSelection(final ISelection selection) {
 		selectionProvider.setSelection(selection);
 	}
 
+	/**
+	 * Updates the state of the outline view and refreshes the tree viewer.
+	 * 
+	 * @see IToolBarContributor
+	 */
+	public void setViewState(final String stateId, final boolean newValue) {
+		if (outlineProvider instanceof IToolBarContributor) {
+			((IToolBarContributor) outlineProvider).setState(stateId, newValue);
+		}
+
+		if (treeViewer != null) {
+			treeViewer.getControl().setRedraw(false);
+			BusyIndicator.showWhile(treeViewer.getControl().getDisplay(), new Runnable() {
+				public void run() {
+					treeViewer.refresh();
+				}
+			});
+			treeViewer.getControl().setRedraw(true);
+		}
+	};
+
 	// ===================================================== PRIVATE
 
 	private Composite composite;
 
 	private Label label;
 	private TreeViewer treeViewer;
+	private OutlineFilterActionGroup filterActionGroup;
 
-	private VexEditor vexEditor;
+	private final VexEditor vexEditor;
 
 	private IOutlineProvider outlineProvider;
 
@@ -160,6 +197,7 @@
 		}
 
 		treeViewer = new TreeViewer(composite, SWT.NONE);
+
 		composite.layout();
 
 		final DocumentType doctype = vexEditor.getDocumentType();
@@ -185,16 +223,24 @@
 			outlineProvider = new DefaultOutlineProvider();
 		}
 
+		if (outlineProvider instanceof IToolBarContributor) {
+			((IToolBarContributor) outlineProvider).registerToolBarActions(this, getSite().getActionBars());
+		}
+
 		outlineProvider.init(vexEditor);
 
 		treeViewer.setContentProvider(outlineProvider.getContentProvider());
 		treeViewer.setLabelProvider(outlineProvider.getLabelProvider());
 		treeViewer.setAutoExpandLevel(2);
 
-		treeViewer.setInput(vexEditor.getVexWidget().getDocument());
+		filterActionGroup.setViewer(treeViewer);
+
+		treeViewer.setUseHashlookup(true);
+		final IDocument document = vexEditor.getVexWidget().getDocument();
+		treeViewer.setInput(document);
+		document.addDocumentListener(documentListener);
 
 		treeViewer.addSelectionChangedListener(selectionListener);
-
 	}
 
 	/**
@@ -207,17 +253,49 @@
 	 * - notifications from the TreeViewer are passed on to our SelectionChangedListeners.
 	 */
 	private final ISelectionChangedListener selectionListener = new ISelectionChangedListener() {
+		private Object[] lastExpandedElements = null;
+		private IElement selectedTreeElement;
+
 		public void selectionChanged(final SelectionChangedEvent event) {
 			if (event.getSource() instanceof VexWidget) {
 				final VexWidget vexWidget = (VexWidget) event.getSource();
 				if (vexWidget.isFocusControl() && getTreeViewer() != null) {
 					final IElement element = vexWidget.getCurrentElement();
-					if (element != null) {
-						final IElement outlineElement = outlineProvider.getOutlineElement(element);
-						getTreeViewer().refresh(outlineElement);
-						getTreeViewer().setSelection(new StructuredSelection(outlineElement), true);
+
+					if (element != null && element.equals(selectedTreeElement)) {
+						// If we're still in the same element, there is no need to refresh
+						return;
+					}
+
+					if (element.getDocument().getRootElement() == element) {
+						return;
+					}
+
+					selectedTreeElement = element;
+					while (selectedTreeElement != null && filterActionGroup.isElementFiltered(selectedTreeElement)) {
+						// If the selected element is not visible, try to find a visible parent
+						selectedTreeElement = selectedTreeElement.getParentElement();
+					}
+					if (selectedTreeElement != null) {
+						getTreeViewer().getControl().setRedraw(false);
+						BusyIndicator.showWhile(getTreeViewer().getControl().getDisplay(), new Runnable() {
+							public void run() {
+								// restore the expanded state
+								if (lastExpandedElements != null) {
+									getTreeViewer().setExpandedElements(lastExpandedElements);
+									lastExpandedElements = null;
+								}
+								if (!getTreeViewer().getExpandedState(selectedTreeElement.getParentElement())) {
+									// ELement is not visible - save the tree state
+									lastExpandedElements = getTreeViewer().getExpandedElements();
+								}
+								getTreeViewer().setSelection(new StructuredSelection(selectedTreeElement));
+							}
+						});
+						getTreeViewer().getControl().setRedraw(true);
 					} else {
-						getTreeViewer().setSelection(new StructuredSelection(), true);
+						getTreeViewer().setSelection(new StructuredSelection());
+						selectedTreeElement = null;
 					}
 				}
 			} else {
@@ -226,8 +304,9 @@
 				if (treeViewer.getTree().isFocusControl()) {
 					final TreeItem[] selected = treeViewer.getTree().getSelection();
 					if (selected.length > 0) {
-
+						lastExpandedElements = null;
 						final IElement element = (IElement) selected[0].getData();
+						selectedTreeElement = element;
 						final VexWidget vexWidget = vexEditor.getVexWidget();
 
 						// Moving to the end of the element first is a cheap
@@ -253,5 +332,68 @@
 			showLabel(Messages.getString("DocumentOutlinePage.reloading")); //$NON-NLS-1$
 		}
 
+		@Override
+		public void styleChanged(final VexEditorEvent event) {
+			filterActionGroup.setStyleSheet(event.getVexEditor().getStyle().getStyleSheet());
+		};
 	};
+
+	private final IDocumentListener documentListener = new IDocumentListener() {
+
+		public void attributeChanged(final AttributeChangeEvent event) {
+
+			// This cast is save because this event is only fired due to the attribute changes of elements.
+			final IElement parent = (IElement) event.getParent();
+			final IAttribute attr = parent.getAttribute(event.getAttributeName());
+			if (vexEditor.getStyle().getStyleSheet().getStyles(parent).getOutlineContent() == attr) {
+				// Parent has to be refreshed, since it uses this attribute as outline content
+				getTreeViewer().refresh(outlineProvider.getOutlineElement(parent));
+			}
+		}
+
+		public void namespaceChanged(final NamespaceDeclarationChangeEvent event) {
+		}
+
+		public void beforeContentDeleted(final ContentChangeEvent event) {
+		}
+
+		public void beforeContentInserted(final ContentChangeEvent event) {
+		}
+
+		public void contentDeleted(final ContentChangeEvent event) {
+			final IParent outlineElement = event.getParent();
+			refreshOutlineElement(outlineElement);
+		}
+
+		public void contentInserted(final ContentChangeEvent event) {
+			final IParent outlineElement = event.getParent();
+			refreshOutlineElement(outlineElement);
+		}
+
+		private void refreshOutlineElement(final IParent outlineElement) {
+			if (outlineElement.getDocument().getRootElement().equals(outlineElement)) {
+				getTreeViewer().refresh();
+			} else if (outlineElement instanceof IElement) {
+				// This SHOULD always be the case
+				final IElement parent = ((IElement) outlineElement).getParentElement();
+				if (parent != null && vexEditor.getStyle().getStyleSheet().getStyles(parent).getOutlineContent() == outlineElement) {
+					// Parent has to be refreshed, since it uses this element as content
+					getTreeViewer().refresh(outlineProvider.getOutlineElement(parent));
+				} else {
+					getTreeViewer().refresh(outlineProvider.getOutlineElement((IElement) outlineElement));
+				}
+			}
+		}
+	};
+
+	private void registerToolbarActions(final IActionBars actionBars) {
+
+		filterActionGroup = new OutlineFilterActionGroup(vexEditor.getStyle().getStyleSheet());
+
+		final IToolBarManager toolBarManager = actionBars.getToolBarManager();
+		if (toolBarManager != null) {
+			filterActionGroup.fillActionBars(actionBars);
+			//toolBarManager.add(new ToolBarToggleAction(this, IOutlineViewState.SHOW_ELEMENT_CONTENT, PluginImages.DESC_SHOW_ELEMENT_CONTENT));
+		}
+	}
 }
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/IToolBarContributor.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/IToolBarContributor.java
new file mode 100644
index 0000000..291e6ae
--- /dev/null
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/IToolBarContributor.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Carsten Hiesserich 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:
+ *     Carsten Hiesserich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.ui.internal.outline;
+
+import org.eclipse.ui.IActionBars;
+
+/**
+ * Implemented by outline providers that use ToolBar actions.
+ */
+public interface IToolBarContributor {
+
+	/**
+	 * Handler for toggle states.
+	 * 
+	 * @param commandId
+	 *            The commandId that defines the state
+	 * @param state
+	 *            The states new values
+	 */
+	public void setState(String commandId, boolean state);
+
+	/**
+	 * Register the ToolBarActions supoorted by this OutlineProvider
+	 * 
+	 * @param page
+	 *            The DocumentOutlinePage
+	 * @param actionBars
+	 *            The page sites ActionBars
+	 */
+	public void registerToolBarActions(DocumentOutlinePage page, IActionBars actionBars);
+}
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/OutlineFilter.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/OutlineFilter.java
new file mode 100644
index 0000000..a10904d
--- /dev/null
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/OutlineFilter.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Carsten Hiesserich 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:
+ *     Carsten Hiesserich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.ui.internal.outline;
+
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.vex.core.internal.css.CSS;
+import org.eclipse.vex.core.internal.css.StyleSheet;
+import org.eclipse.vex.core.provisional.dom.IElement;
+import org.eclipse.vex.core.provisional.dom.INode;
+
+public class OutlineFilter extends ViewerFilter {
+
+	// Binary coded - use 2-4-8... for next constants
+	public static final int FILTER_ID_INLINE_ELEMENTS = 1;
+
+	private int activeFilters = 0;
+	private StyleSheet styleSheet;
+
+	public OutlineFilter(final StyleSheet styleSheet) {
+		super();
+		this.styleSheet = styleSheet;
+	}
+
+	public void setStyleSheet(final StyleSheet styleSheet) {
+		this.styleSheet = styleSheet;
+	}
+
+	@Override
+	public boolean select(final Viewer viewer, final Object parentElement, final Object element) {
+		if (!(element instanceof INode)) {
+			return true;
+		}
+
+		final IElement domElement = (IElement) element;
+		if (hasFilter(FILTER_ID_INLINE_ELEMENTS)) {
+			if (styleSheet.getStyles(domElement).getDisplay().equals(CSS.INLINE)) {
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * @param filterId
+	 *            The filter ID to add as active filter.
+	 */
+	public final void addFilter(final int filterId) {
+		activeFilters |= filterId;
+	}
+
+	/**
+	 * @param filterId
+	 *            The filter ID to remove from active filters.
+	 */
+	public final void removeFilter(final int filterId) {
+		activeFilters &= -1 ^ filterId;
+	}
+
+	/**
+	 * Tests if a filter is active
+	 * 
+	 * @param filter
+	 *            The filter id to test.
+	 * @return <code>true</true> if the given filter is active.
+	 */
+	public final boolean hasFilter(final int filter) {
+		return (activeFilters & filter) != 0;
+	}
+
+}
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/OutlineFilterAction.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/OutlineFilterAction.java
new file mode 100644
index 0000000..cc9c217
--- /dev/null
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/OutlineFilterAction.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Carsten Hiesserich 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:
+ *     Carsten Hiesserich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.ui.internal.outline;
+
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.vex.ui.internal.VexPlugin;
+import org.eclipse.vex.ui.internal.editor.Messages;
+import org.osgi.service.prefs.Preferences;
+
+/**
+ * The Action used by all outline toggle buttons.
+ */
+public class OutlineFilterAction extends Action {
+
+	private final OutlineFilterActionGroup actionGroup;
+	private final int filterId;
+	private final String messageKey;
+
+	/**
+	 * @param outlinePage
+	 *            The ActionGroup that contians this action.
+	 * @param filter
+	 *            The binary ID of this filter. See {@link OutlineFilter}.
+	 * @param messageKey
+	 *            The key used to get message texts and preference values.
+	 * @param contextHelpId
+	 *            The id to be registered with the help system. May be null.
+	 */
+	public OutlineFilterAction(final OutlineFilterActionGroup actionGroup, final int filterId, final String messageKey, final String contextHelpId) {
+		super(Messages.getString("DocumentOutlinePage.action." + messageKey + ".title"), IAction.AS_CHECK_BOX);
+		this.actionGroup = actionGroup;
+		this.filterId = filterId;
+		this.messageKey = messageKey;
+		final Preferences preferences = InstanceScope.INSTANCE.getNode(VexPlugin.ID);
+		setChecked(preferences.getBoolean(messageKey, false));
+		setToolTipText(Messages.getString("DocumentOutlinePage.action." + messageKey + ".tooltip")); //$NON-NLS-1$
+		setDescription(Messages.getString("DocumentOutlinePage.action." + messageKey + ".tooltip")); //$NON-NLS-1$
+		if (contextHelpId != null) {
+			PlatformUI.getWorkbench().getHelpSystem().setHelp(this, contextHelpId);
+		}
+	}
+
+	@Override
+	public void run() {
+		final Preferences preferences = InstanceScope.INSTANCE.getNode(VexPlugin.ID);
+		preferences.putBoolean(messageKey, isChecked());
+		actionGroup.setFilter(filterId, isChecked());
+	}
+
+}
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/OutlineFilterActionGroup.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/OutlineFilterActionGroup.java
new file mode 100644
index 0000000..99a9d2c
--- /dev/null
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/OutlineFilterActionGroup.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Carsten Hiesserich 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:
+ *     Carsten Hiesserich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.ui.internal.outline;
+
+import java.util.ArrayList;
+
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.actions.ActionGroup;
+import org.eclipse.vex.core.internal.css.StyleSheet;
+import org.eclipse.vex.ui.internal.PluginImages;
+
+public class OutlineFilterActionGroup extends ActionGroup {
+	private static final String KEY_HIDEINLINEELEMENTS = "hideInlineElements"; //$NON-NLS-1$
+
+	private final OutlineFilterAction[] filterActions;
+	private final OutlineFilter filter;
+
+	private StructuredViewer viewer;
+
+	public OutlineFilterActionGroup(final StyleSheet styleSheet) {
+
+		final ArrayList<OutlineFilterAction> actions = new ArrayList<OutlineFilterAction>(4);
+
+		final OutlineFilterAction hideInlineElements = new OutlineFilterAction(this, OutlineFilter.FILTER_ID_INLINE_ELEMENTS, KEY_HIDEINLINEELEMENTS, null);
+		hideInlineElements.setImageDescriptor(PluginImages.DESC_HIDE_INLINE_ELEMENTS);
+		actions.add(hideInlineElements);
+
+		filter = new OutlineFilter(styleSheet);
+		filterActions = actions.toArray(new OutlineFilterAction[actions.size()]);
+	}
+
+	public void setFilter(final int filterId, final boolean isSet) {
+		if (isSet) {
+			filter.addFilter(filterId);
+		} else {
+			filter.removeFilter(filterId);
+		}
+
+		if (viewer != null) {
+			final ISelection currentSelection = viewer.getSelection();
+			viewer.getControl().setRedraw(false);
+			BusyIndicator.showWhile(viewer.getControl().getDisplay(), new Runnable() {
+				public void run() {
+					viewer.refresh();
+					viewer.setSelection(currentSelection, true);
+				}
+			});
+			viewer.getControl().setRedraw(true);
+		}
+	}
+
+	public void setViewer(final StructuredViewer viewer) {
+		this.viewer = viewer;
+		viewer.addFilter(filter);
+	}
+
+	public void setStyleSheet(final StyleSheet styleSheet) {
+		filter.setStyleSheet(styleSheet);
+	}
+
+	public boolean isElementFiltered(final Object element) {
+		return !filter.select(viewer, null, element);
+	}
+
+	@Override
+	public void fillActionBars(final IActionBars actionBars) {
+		final IToolBarManager toolBarManager = actionBars.getToolBarManager();
+		for (final OutlineFilterAction action : filterActions) {
+			toolBarManager.add(action);
+		}
+	}
+
+}
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/ToolBarToggleAction.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/ToolBarToggleAction.java
new file mode 100644
index 0000000..ec873b1
--- /dev/null
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/internal/outline/ToolBarToggleAction.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Carsten Hiesserich 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:
+ *     Carsten Hiesserich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.ui.internal.outline;
+
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.vex.ui.internal.VexPlugin;
+import org.eclipse.vex.ui.internal.editor.Messages;
+import org.osgi.service.prefs.Preferences;
+
+/**
+ * The Action used by all outline toggle buttons.
+ */
+public class ToolBarToggleAction extends Action {
+
+	private final DocumentOutlinePage outlinePage;
+	private final String commandId;
+
+	/**
+	 * @param outlinePage
+	 *            The DocumentOutlinePage that uses this action.
+	 * @param stateName
+	 *            The name of the toggle state. Constant defined in {@link IToolBarContributor}.
+	 * @param imageDescriptor
+	 *            The image to be displayed
+	 */
+	public ToolBarToggleAction(final DocumentOutlinePage outlinePage, final String stateName, final ImageDescriptor imageDescriptor) {
+		super(Messages.getString("DocumentOutlinePage.action." + stateName + ".title"), IAction.AS_CHECK_BOX); //$NON-NLS-1$
+		this.outlinePage = outlinePage;
+		commandId = stateName;
+		final Preferences preferences = InstanceScope.INSTANCE.getNode(VexPlugin.ID);
+		final boolean isChecked = preferences.getBoolean(stateName, true);
+		setChecked(isChecked);
+		setToolTipText(Messages.getString("DocumentOutlinePage.action." + stateName + ".tooltip")); //$NON-NLS-1$
+		setDescription(Messages.getString("DocumentOutlinePage.action." + stateName + ".tooltip")); //$NON-NLS-1$
+		setImageDescriptor(imageDescriptor);
+	}
+
+	@Override
+	public void run() {
+		final Preferences preferences = InstanceScope.INSTANCE.getNode(VexPlugin.ID);
+		preferences.putBoolean(commandId, isChecked());
+		outlinePage.setViewState(commandId, isChecked());
+	}
+
+}