souped up examples runner
diff --git a/org.eclipse.nebula.widgets.nattable.core.example/.settings/org.eclipse.core.resources.prefs b/org.eclipse.nebula.widgets.nattable.core.example/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..99f26c0
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.core.example/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.nebula.widgets.nattable.core.example/META-INF/MANIFEST.MF b/org.eclipse.nebula.widgets.nattable.core.example/META-INF/MANIFEST.MF
index 28a2323..ca0c31a 100644
--- a/org.eclipse.nebula.widgets.nattable.core.example/META-INF/MANIFEST.MF
+++ b/org.eclipse.nebula.widgets.nattable.core.example/META-INF/MANIFEST.MF
@@ -9,5 +9,7 @@
  com.google.guava,

  org.eclipse.xtext.xbase.lib

 Export-Package: org.eclipse.nebula.widgets.nattable.core.example,

- org.eclipse.nebula.widgets.nattable.core.example.impl

+ org.eclipse.nebula.widgets.nattable.core.example.impl,

+ org.eclipse.nebula.widgets.nattable.core.example.index,

+ org.eclipse.nebula.widgets.nattable.core.example.index.node

 

diff --git a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/GenerateNatExamplesIndex.xtend b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/GenerateNatExamplesIndex.xtend
deleted file mode 100644
index 5a8441a..0000000
--- a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/GenerateNatExamplesIndex.xtend
+++ /dev/null
@@ -1,56 +0,0 @@
-package org.eclipse.nebula.widgets.nattable.core.example
-
-import java.io.BufferedWriter
-import java.io.File
-import java.io.FileWriter
-import java.util.Properties
-
-import static extension org.eclipse.nebula.widgets.nattable.core.example.NatExamplesIndex.*
-
-/**
- * Generates the natExamplesIndex.properties file. For details on the format of this file, see {@link NatExamplesIndex}.
- */
-class GenerateNatExamplesIndex {
-
-	def static void main(String[] args) {
-		new GenerateNatExamplesIndex().run
-	}
-	
-	//
-	
-	/**
-	 * Generate the NatExamplesIndex.properties file
-	 */
-	def void run() {
-		val examplesIndex = new Properties
-		
-		findExamples(new File("src" + BASE_PACKAGE_PATH), examplesIndex)
-		findExamples(new File("xtend-gen" + BASE_PACKAGE_PATH), examplesIndex)
-		
-		val examplesIndexFile = new File("src" + BASE_PACKAGE_PATH, NAT_EXAMPLES_INDEX_FILE_NAME)
-		val writer = new BufferedWriter(new FileWriter(examplesIndexFile))
-		examplesIndex.store(writer, "NatExamplesIndex")
-		writer.flush
-		writer.close
-	}
-	
-	/**
-	 * Recursively find all examples in the given directory and below, accumulating information about them in the examples index.
-	 */
-	def private void findExamples(File dir, Properties examplesIndex) {
-		for (String s : dir.list) {
-			val f = new File(dir, s)
-			if (f.directory)
-				findExamples(f, examplesIndex)
-			else {
-				var examplePath = (dir.canonicalPath + File::separator + s).replace(File::separator, "/")  // Convert to /-delimited path
-				if (examplePath.endsWith(".java")) {
-					val exampleClass = examplePath.replaceAll("\\.java$", "").exampleClass
-					if (exampleClass != null)
-						examplesIndex.put(exampleClass.simpleName, exampleClass.canonicalName)
-				}
-			}
-		}
-	}
-	
-}
\ No newline at end of file
diff --git a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/NatExample.xtend b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/NatExample.xtend
index 8f1fab5..e9a4f8f 100644
--- a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/NatExample.xtend
+++ b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/NatExample.xtend
@@ -4,6 +4,11 @@
 
 interface NatExample {
 	
+	def String getDescription()
+	
 	def Layer createLayer()
 	
+	def void start()
+	def void stop()
+	
 }
\ No newline at end of file
diff --git a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/NatExamplesIndex.xtend b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/NatExamplesIndex.xtend
deleted file mode 100644
index 1827174..0000000
--- a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/NatExamplesIndex.xtend
+++ /dev/null
@@ -1,81 +0,0 @@
-package org.eclipse.nebula.widgets.nattable.core.example
-
-import java.lang.reflect.Modifier
-import java.util.HashMap
-import java.util.Properties
-
-/**
- * The examples index. The index is generated by {@link GenerateNatExamplesIndex} and stored as a properties file.
- * The properties file has the following format:
- * <ul>
- *   <li>exampleName=fully.qualified.example.class.name</li>
- * </ul>
- */
-class NatExamplesIndex {
-	
-	public static val NAT_EXAMPLES_INDEX_FILE_NAME = "NatExamplesIndex.properties"
-	public static val BASE_PACKAGE = "org.eclipse.nebula.widgets.nattable.core.example.impl"
-	public static val BASE_PACKAGE_PATH = "/" + BASE_PACKAGE.replace('.', '/')
-	
-	static NatExamplesIndex _singleton
-	
-	def private static getSingleton() {
-		if (_singleton == null) {
-			_singleton = new NatExamplesIndex
-		}
-		_singleton
-	}
-	
-	def static getExample(String exampleName) {
-		singleton.getExampleInstance(exampleName)
-	}
-	
-	/**
-	 * @return The example class in the given example path, or null if none exists.
-	 */
-	def static Class<? extends NatExample> getExampleClass(String examplePath) {
-		// Find class
-		var className = examplePath.replace("/", ".")
-		var Class<?> clazz = null
-		while (clazz == null && className.indexOf(".") >= 0) {
-			try {
-				clazz = Class::forName(className)
-			} catch (ClassNotFoundException e) {
-				// Chop off prefix and try again
-				className = className.replaceFirst("^[^.]*\\.", "")
-			}
-		}
-		
-		// Check if this is a concrete NatExample
-		if (clazz != null && !Modifier::isAbstract(clazz.modifiers) && typeof(NatExample).isAssignableFrom(clazz))
-			return clazz as Class<? extends NatExample>
-	}
-	
-	//
-	
-	val Properties natExamplesIndex
-	val cachedExamples = new HashMap<String, NatExample>
-	
-	new() {
-		val inputStream = typeof(NatExamplesIndex).getResourceAsStream(BASE_PACKAGE_PATH + "/" + NAT_EXAMPLES_INDEX_FILE_NAME)
-		if (inputStream == null)
-			throw new IllegalStateException("Examples index not found! Please run GenerateNatExamplesIndex from the org.eclipse.nebula.widgets.nattable.core.example project.")
-		
-		natExamplesIndex = new Properties
-		natExamplesIndex.load(inputStream)
-	}
-	
-	def NatExample getExampleInstance(String exampleName) {
-		var example = cachedExamples.get(exampleName)
-		if (example == null) {
-			val examplePath = natExamplesIndex.getProperty(exampleName)
-			val exampleClass = examplePath.exampleClass
-			if (exampleClass != null) {
-				example = exampleClass.newInstance as NatExample
-				cachedExamples.put(exampleName, example)
-			}
-		}
-		example
-	}
-
-}
\ No newline at end of file
diff --git a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/AbstractNatExample.xtend b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/AbstractNatExample.xtend
new file mode 100644
index 0000000..5db3027
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/AbstractNatExample.xtend
@@ -0,0 +1,12 @@
+package org.eclipse.nebula.widgets.nattable.core.example.impl
+
+import org.eclipse.nebula.widgets.nattable.core.example.NatExample
+
+abstract class AbstractNatExample implements NatExample {
+	
+	override getDescription() {}
+	
+	override start() {}
+	override stop() {}
+	
+}
\ No newline at end of file
diff --git a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/GridLayer.xtend b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/GridLayer.xtend
index 2c73eeb..3ea7074 100644
--- a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/GridLayer.xtend
+++ b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/GridLayer.xtend
@@ -1,6 +1,5 @@
 package org.eclipse.nebula.widgets.nattable.core.example.impl
 
-import org.eclipse.nebula.widgets.nattable.core.example.NatExample
 import org.eclipse.nebula.widgets.nattable.core.layer.axis.impl.AxisImpl
 import org.eclipse.nebula.widgets.nattable.core.layer.axis.impl.hideshow.HideShowAxis
 import org.eclipse.nebula.widgets.nattable.core.layer.axis.impl.reorder.ReorderAxis
@@ -12,7 +11,7 @@
 import org.eclipse.nebula.widgets.nattable.core.layer.impl.header.RowHeaderLayer
 import org.eclipse.nebula.widgets.nattable.core.layer.impl.viewport.ViewportLayer
 
-class GridLayer implements NatExample {
+class GridLayer extends AbstractNatExample {
 	
 	override createLayer() {
 		val bodyLayer = new ViewportLayer(new DummyLayer(
diff --git a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/BigLayer.xtend b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/big/BigLayer.xtend
similarity index 87%
rename from org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/BigLayer.xtend
rename to org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/big/BigLayer.xtend
index 6a115fc..294ef8e 100644
--- a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/BigLayer.xtend
+++ b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/big/BigLayer.xtend
@@ -1,6 +1,6 @@
-package org.eclipse.nebula.widgets.nattable.core.example.impl
+package org.eclipse.nebula.widgets.nattable.core.example.impl.big
 
-import org.eclipse.nebula.widgets.nattable.core.example.NatExample
+import org.eclipse.nebula.widgets.nattable.core.example.impl.AbstractNatExample
 import org.eclipse.nebula.widgets.nattable.core.layer.axis.impl.AxisImpl
 import org.eclipse.nebula.widgets.nattable.core.layer.impl.DimensionallyDependentLayer
 import org.eclipse.nebula.widgets.nattable.core.layer.impl.DummyLayer
@@ -10,7 +10,7 @@
 import org.eclipse.nebula.widgets.nattable.core.layer.impl.header.RowHeaderLayer
 import org.eclipse.nebula.widgets.nattable.core.layer.impl.viewport.ViewportLayer
 
-class BigLayer implements NatExample {
+class BigLayer extends AbstractNatExample {
 	
 	override createLayer() {
 		val bodyLayer = new ViewportLayer(new DummyLayer(
diff --git a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/big/index.properties b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/big/index.properties
new file mode 100644
index 0000000..ae2a358
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/big/index.properties
@@ -0,0 +1,2 @@
+#Fri May 31 12:59:51 EDT 2013
+BigLayer=org.eclipse.nebula.widgets.nattable.core.example.impl.big.BigLayer
diff --git a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/index.properties b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/index.properties
new file mode 100644
index 0000000..231b651
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/index.properties
@@ -0,0 +1,3 @@
+#Fri May 31 12:59:51 EDT 2013
+big=|big
+GridLayer=org.eclipse.nebula.widgets.nattable.core.example.impl.GridLayer
diff --git a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/natExamplesIndex.properties b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/natExamplesIndex.properties
deleted file mode 100644
index 359256b..0000000
--- a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/impl/natExamplesIndex.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-#NatExamplesIndex
-#Thu May 30 16:59:54 EDT 2013
-BigLayer=org.eclipse.nebula.widgets.nattable.core.example.impl.BigLayer
-GridLayer=org.eclipse.nebula.widgets.nattable.core.example.impl.GridLayer
diff --git a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/GenerateNatExamplesIndex.xtend b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/GenerateNatExamplesIndex.xtend
new file mode 100644
index 0000000..132de20
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/GenerateNatExamplesIndex.xtend
@@ -0,0 +1,94 @@
+package org.eclipse.nebula.widgets.nattable.core.example.index
+
+import java.io.File
+import java.io.FileReader
+import java.io.FileWriter
+import java.lang.reflect.Modifier
+import java.util.Properties
+import org.eclipse.nebula.widgets.nattable.core.example.NatExample
+
+import static org.eclipse.nebula.widgets.nattable.core.example.index.NatExamplesIndex.*
+
+/**
+ * Generates the natExamplesIndex.properties file. For details on the format of this file, see {@link NatExamplesIndex}.
+ */
+class GenerateNatExamplesIndex {
+
+	def static void main(String[] args) {
+		new GenerateNatExamplesIndex().run
+	}
+	
+	//
+	
+	/**
+	 * Generate the NatExamplesIndex.properties file
+	 */
+	def void run() {
+		findExamples(
+			new File(new File("src"), BASE_PACKAGE_PATH),
+			new File(new File("bin"), BASE_PACKAGE_PATH)
+		)
+	}
+	
+	/**
+	 * Recursively find all examples in the given directory and below, accumulating information about them in the examples index.
+	 */
+	def private void findExamples(File srcDir, File binDir) {
+		val indexProperties = new Properties
+		
+		val indexFile = new File(srcDir, INDEX_FILE_NAME)
+		
+		// If an index file already exists in this directory, load it
+		if (indexFile.exists) {
+			val reader = new FileReader(indexFile)
+			indexProperties.load(reader)
+			reader.close
+		}
+		
+		// Look through bin directory
+		for (String s : binDir.list) {
+			val f = new File(binDir, s)
+			if (f.directory) {
+				if (!indexProperties.containsKey(f.name))  // Don't overwrite existing entries
+					indexProperties.put(f.name, "|" + f.name)
+				findExamples(new File(srcDir, s), f)  // Recurse; use corresponding src directory
+			} else if (f.name.endsWith(".class")) {
+				val shortName = f.name.replaceFirst("\\.class$", "")
+				if (!indexProperties.containsKey(shortName)) {  // Don't overwrite existing entries
+					val exampleClass = f.canonicalPath.replaceAll("\\.class$", "").getExampleClass
+					if (exampleClass != null)
+						indexProperties.put(exampleClass.simpleName, exampleClass.canonicalName)
+				}
+			}
+		}
+		
+		// Write properties
+		if (!indexProperties.empty) {
+			val writer = new FileWriter(indexFile)
+			indexProperties.store(writer, null)
+			writer.close
+		}
+	}
+	
+	/**
+	 * @return The example class in the given example path, or null if none exists.
+	 */
+	def Class<? extends NatExample> getExampleClass(String examplePath) {
+		// Find class
+		var className = examplePath.replace("/", ".")
+		var Class<?> clazz = null
+		while (clazz == null && className.indexOf(".") >= 0) {
+			try {
+				clazz = Class::forName(className)
+			} catch (ClassNotFoundException e) {
+				// Chop off prefix and try again
+				className = className.replaceFirst("^[^.]*\\.", "")
+			}
+		}
+		
+		// Check if this is a concrete NatExample
+		if (clazz != null && !Modifier::isAbstract(clazz.modifiers) && typeof(NatExample).isAssignableFrom(clazz))
+			return clazz as Class<? extends NatExample>
+	}
+	
+}
\ No newline at end of file
diff --git a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/NatExamplesIndex.xtend b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/NatExamplesIndex.xtend
new file mode 100644
index 0000000..b03f0b7
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/NatExamplesIndex.xtend
@@ -0,0 +1,40 @@
+package org.eclipse.nebula.widgets.nattable.core.example.index
+
+import org.eclipse.nebula.widgets.nattable.core.example.index.node.CategoryNode
+import org.eclipse.nebula.widgets.nattable.core.example.index.node.IndexNode
+
+/**
+ * The examples index. The index is generated by {@link GenerateNatExamplesIndex} and stored as a properties file.
+ * The properties file has the following format:
+ * <ul>
+ *   <li>exampleName=fully.qualified.example.class.name|Display name</li>
+ * </ul>
+ */
+class NatExamplesIndex {
+	
+	public static val INDEX_FILE_NAME = "index.properties"
+	public static val BASE_PACKAGE = "org.eclipse.nebula.widgets.nattable.core.example.impl"
+	public static val BASE_PACKAGE_PATH = "/" + BASE_PACKAGE.replace('.', '/')
+	
+	static IndexNode rootNode
+	
+	def static getRootNode() {
+		if (rootNode == null)
+			rootNode = new CategoryNode(BASE_PACKAGE_PATH, "")
+		rootNode
+	}
+	
+	/**
+	 * @param nodePath A '/'-separated path string.
+	 *   Each segment prior to the last segment corresponds to a category node.
+	 *   The last segment of the path corresponds to a category or an example.
+	 * @return The index node associated with the given node path.
+	 */
+	def static IndexNode getNode(String nodePath) {
+		if (nodePath == null)
+			getRootNode
+		else
+			nodePath.split('/').fold(getRootNode, [ node, pathSegment | node.getChildNode(pathSegment) ])
+	}
+	
+}
\ No newline at end of file
diff --git a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/node/AbstractIndexNode.xtend b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/node/AbstractIndexNode.xtend
new file mode 100644
index 0000000..45a0a96
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/node/AbstractIndexNode.xtend
@@ -0,0 +1,20 @@
+package org.eclipse.nebula.widgets.nattable.core.example.index.node
+
+abstract class AbstractIndexNode implements IndexNode {
+	
+	val protected String path
+	val protected String displayName
+	
+	new(String path, String displayName) {
+		this.path = path
+		this.displayName = displayName
+	}
+	
+	override getDisplayName() {
+		if (displayName != null)
+			displayName
+		else
+			path.substring(path.lastIndexOf('/') + 1)
+	}
+	
+}
\ No newline at end of file
diff --git a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/node/CategoryNode.xtend b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/node/CategoryNode.xtend
new file mode 100644
index 0000000..8eeba4f
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/node/CategoryNode.xtend
@@ -0,0 +1,70 @@
+package org.eclipse.nebula.widgets.nattable.core.example.index.node
+
+import java.util.Collections
+import java.util.List
+import java.util.Properties
+
+import static org.eclipse.nebula.widgets.nattable.core.example.index.NatExamplesIndex.*
+
+class CategoryNode extends AbstractIndexNode {
+	
+	val Properties indexProperties = loadIndexProperties
+	
+	new(String path, String displayName) {
+		super(path, displayName)
+	}
+	
+	def loadIndexProperties() {
+		val inputStream = class.getResourceAsStream(path + '/' + INDEX_FILE_NAME)
+		if (inputStream == null)
+			throw new IllegalStateException('''«path»/«INDEX_FILE_NAME» not found! Please run GenerateNatExamplesIndex from the org.eclipse.nebula.widgets.nattable.core.example project.''')
+		
+		val properties = new Properties
+		properties.load(inputStream)
+		properties
+	}
+	
+	override getChildNodeNames() {
+		Collections::list(indexProperties.propertyNames) as List<String>
+	}
+	
+	override getChildNode(String name) {
+		val propertyValue = indexProperties.getProperty(name)
+		if (propertyValue != null)
+			createIndexNode(name, propertyValue)
+	}
+	
+	/**
+	 * Creates a concrete IndexNode from the given property name and value. The property value is a '|'-delimited string which can represent
+	 * a NatExample, or a category. If the property value represents a category, it takes the following form:
+	 * <ul>
+	 *   <li>|Display name</li>
+	 * </ul>
+	 * Note the leading '|' character.
+	 * <p>
+	 * If the property value represents a NatExample, it can take either of the following forms:
+	 * <ul>
+	 *   <li>fully.qualified.example.class.name</li>
+	 *   <li>fully.qualified.example.class.name|Display name</li>
+	 * </ul>
+	 * 
+	 * @param propertyName The name of the property.
+	 * @param propertyValue The value of the property.
+	 */
+	def createIndexNode(String propertyName, String propertyValue) {
+		val nodePath = path + '/' + propertyName
+		
+		val separatorIndex = propertyValue.indexOf('|')
+		if (separatorIndex == 0) {
+			// Category node
+			val nodeDisplayName = propertyValue.substring(1)
+			new CategoryNode(nodePath, nodeDisplayName)
+		} else {
+			// Example node
+			val nodeDisplayName = if (separatorIndex > 0) propertyValue.substring(separatorIndex + 1)
+			val exampleClassName = if (separatorIndex > 0) propertyValue.substring(0, separatorIndex) else propertyValue
+			new NatExampleNode(nodePath, nodeDisplayName, exampleClassName)
+		}
+	}
+	
+}
\ No newline at end of file
diff --git a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/node/IndexNode.xtend b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/node/IndexNode.xtend
new file mode 100644
index 0000000..663e80a
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/node/IndexNode.xtend
@@ -0,0 +1,12 @@
+package org.eclipse.nebula.widgets.nattable.core.example.index.node
+
+import java.util.List
+
+interface IndexNode {
+	
+	def String getDisplayName()
+	
+	def List<String> getChildNodeNames()
+	def IndexNode getChildNode(String name)
+	
+}
\ No newline at end of file
diff --git a/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/node/NatExampleNode.xtend b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/node/NatExampleNode.xtend
new file mode 100644
index 0000000..342772a
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.core.example/src/org/eclipse/nebula/widgets/nattable/core/example/index/node/NatExampleNode.xtend
@@ -0,0 +1,25 @@
+package org.eclipse.nebula.widgets.nattable.core.example.index.node
+
+import org.eclipse.nebula.widgets.nattable.core.example.NatExample
+
+class NatExampleNode extends AbstractIndexNode {
+	
+	NatExample natExample
+	
+	new(String path, String displayName, String className) {
+		super(path, displayName)
+		instantiateNatExample(className)
+	}
+	
+	def private instantiateNatExample(String className) {
+		natExample = Class::forName(className).newInstance as NatExample
+	}
+	
+	def NatExample getNatExample() {
+		natExample
+	}
+	
+	override getChildNodeNames() { emptyList }
+	override getChildNode(String name) {}
+	
+}
\ No newline at end of file
diff --git a/org.eclipse.nebula.widgets.nattable.renderer.swt.example/META-INF/MANIFEST.MF b/org.eclipse.nebula.widgets.nattable.renderer.swt.example/META-INF/MANIFEST.MF
index 1dd558c..e24131e 100644
--- a/org.eclipse.nebula.widgets.nattable.renderer.swt.example/META-INF/MANIFEST.MF
+++ b/org.eclipse.nebula.widgets.nattable.renderer.swt.example/META-INF/MANIFEST.MF
@@ -5,4 +5,6 @@
 Bundle-Version: 2.0.0.qualifier
 Fragment-Host: org.eclipse.nebula.widgets.nattable.renderer.swt;bundle-version="2.0.0"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
-Require-Bundle: org.eclipse.nebula.widgets.nattable.core.example;bundle-version="1.0.0"
+Require-Bundle: org.eclipse.nebula.widgets.nattable.core.example;bundle-version="1.0.0",
+ org.eclipse.jface;bundle-version="3.8.102",
+ org.eclipse.core.runtime;bundle-version="3.8.0"
diff --git a/org.eclipse.nebula.widgets.nattable.renderer.swt.example/src/org/eclipse/nebula/widgets/nattable/renderer/swt/example/NavContentProvider.xtend b/org.eclipse.nebula.widgets.nattable.renderer.swt.example/src/org/eclipse/nebula/widgets/nattable/renderer/swt/example/NavContentProvider.xtend
new file mode 100644
index 0000000..d7b4b98
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.renderer.swt.example/src/org/eclipse/nebula/widgets/nattable/renderer/swt/example/NavContentProvider.xtend
@@ -0,0 +1,42 @@
+package org.eclipse.nebula.widgets.nattable.renderer.swt.example
+
+import org.eclipse.jface.viewers.ITreeContentProvider
+import org.eclipse.jface.viewers.Viewer
+import org.eclipse.nebula.widgets.nattable.core.example.index.NatExamplesIndex
+import org.eclipse.nebula.widgets.nattable.core.example.index.node.IndexNode
+
+class NavContentProvider implements ITreeContentProvider {
+
+	override dispose() {}
+
+	override inputChanged(Viewer viewer, Object oldInput, Object newInput) {}
+
+	override getParent(Object element) {
+		val nodePath = element as String
+		val lastSlashIndex = nodePath.lastIndexOf('/')
+		if (lastSlashIndex < 0)
+			return null
+		else
+			return nodePath.substring(0, lastSlashIndex)
+	}
+
+	override hasChildren(Object element) {
+		val nodePath = element as String
+		val node = NatExamplesIndex::getNode(nodePath)
+		if (node == null)
+			println("no!")
+		!node.childNodeNames.empty
+	}
+
+	override getChildren(Object parent) {
+		val nodePath = parent as String
+		val node = NatExamplesIndex::getNode(nodePath)
+		node.childNodeNames.map [ nodePath + '/' + it ]
+	}
+
+	override getElements(Object inputElement) {
+		val node = inputElement as IndexNode
+		node.childNodeNames
+	}
+
+}
diff --git a/org.eclipse.nebula.widgets.nattable.renderer.swt.example/src/org/eclipse/nebula/widgets/nattable/renderer/swt/example/NavLabelProvider.xtend b/org.eclipse.nebula.widgets.nattable.renderer.swt.example/src/org/eclipse/nebula/widgets/nattable/renderer/swt/example/NavLabelProvider.xtend
new file mode 100644
index 0000000..ec0f3fc
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.renderer.swt.example/src/org/eclipse/nebula/widgets/nattable/renderer/swt/example/NavLabelProvider.xtend
@@ -0,0 +1,19 @@
+package org.eclipse.nebula.widgets.nattable.renderer.swt.example
+
+import org.eclipse.jface.viewers.LabelProvider
+import org.eclipse.nebula.widgets.nattable.core.example.index.NatExamplesIndex
+
+class NavLabelProvider extends LabelProvider {
+	
+	val NavContentProvider contentProvider
+
+	new(NavContentProvider contentProvider) {
+		this.contentProvider = contentProvider
+	}
+	
+	override getText(Object element) {
+		val nodePath = element as String
+		NatExamplesIndex::getNode(nodePath).displayName
+	}
+	
+}
\ No newline at end of file
diff --git a/org.eclipse.nebula.widgets.nattable.renderer.swt.example/src/org/eclipse/nebula/widgets/nattable/renderer/swt/example/SWTNatExamplesRunner.xtend b/org.eclipse.nebula.widgets.nattable.renderer.swt.example/src/org/eclipse/nebula/widgets/nattable/renderer/swt/example/SWTNatExamplesRunner.xtend
index d2e22dd..4bb3dd2 100644
--- a/org.eclipse.nebula.widgets.nattable.renderer.swt.example/src/org/eclipse/nebula/widgets/nattable/renderer/swt/example/SWTNatExamplesRunner.xtend
+++ b/org.eclipse.nebula.widgets.nattable.renderer.swt.example/src/org/eclipse/nebula/widgets/nattable/renderer/swt/example/SWTNatExamplesRunner.xtend
@@ -2,20 +2,33 @@
 
 import com.google.inject.Guice
 import com.google.inject.Injector
-import org.eclipse.nebula.widgets.nattable.core.example.NatExamplesIndex
+import java.util.HashMap
+import org.eclipse.jface.viewers.TreeSelection
+import org.eclipse.jface.viewers.TreeViewer
+import org.eclipse.nebula.widgets.nattable.core.example.NatExample
+import org.eclipse.nebula.widgets.nattable.core.example.index.NatExamplesIndex
+import org.eclipse.nebula.widgets.nattable.core.example.index.node.NatExampleNode
 import org.eclipse.nebula.widgets.nattable.renderer.swt.SWTNatTable
 import org.eclipse.nebula.widgets.nattable.renderer.swt.event.binding.KeyBinding
 import org.eclipse.nebula.widgets.nattable.renderer.swt.event.binding.UiBindings
 import org.eclipse.nebula.widgets.nattable.renderer.swt.event.binding.UiBindingsImpl
 import org.eclipse.swt.SWT
+import org.eclipse.swt.custom.CTabFolder
+import org.eclipse.swt.custom.CTabItem
 import org.eclipse.swt.layout.FillLayout
+import org.eclipse.swt.layout.GridData
+import org.eclipse.swt.layout.GridLayout
+import org.eclipse.swt.widgets.Composite
+import org.eclipse.swt.widgets.Control
 import org.eclipse.swt.widgets.Display
+import org.eclipse.swt.widgets.Group
+import org.eclipse.swt.widgets.Label
 import org.eclipse.swt.widgets.Shell
 
 class SWTNatExamplesRunner {
 	
 	def static void main(String[] args) {
-		new SWTNatExamplesRunner(400, 300).run
+		new SWTNatExamplesRunner(1000, 600)
 	}
 	
 	//
@@ -23,26 +36,47 @@
 	val int shellWidth
 	val int shellHeight
 	
+	val CTabFolder tabFolder
+	
+	val exampleControlMap = new HashMap<NatExampleNode, Control>
+	val examplePathMap = new HashMap<String, NatExample>
+	
 	new(int shellWidth, int shellHeight) {
 		this.shellWidth = shellWidth
 		this.shellHeight = shellHeight
-	}
-	
-	def void run() {
+		
 		val display = Display::getDefault
 		val shell = new Shell(display, SWT::SHELL_TRIM)
-		shell.setLayout(new FillLayout)
+		shell.layout = new GridLayout(2, false)
 		shell.setSize(shellWidth, shellHeight)
-		shell.setText("NatTable -> SWT")
+		shell.text = "NatTable -> SWT"
 		
-		// Create example control
-		val natExample = NatExamplesIndex::getExample("BigLayer")
-		
-		val natTable = new SWTNatTable(shell) => [
-			injector = createInjector
-			layer = natExample.createLayer
+		// Nav tree
+		new TreeViewer(shell) => [
+			control.layoutData = new GridData(GridData::FILL_VERTICAL) => [
+				widthHint = 200
+			]
+			
+			val navContentProvider = new NavContentProvider
+			contentProvider = navContentProvider
+			labelProvider = new NavLabelProvider(navContentProvider)
+			
+			input = NatExamplesIndex::rootNode
+			
+			addDoubleClickListener([ event |
+				val selection = event.selection as TreeSelection
+				for (path : selection.paths) {
+					val node = NatExamplesIndex::getNode(path.lastSegment.toString)
+					if (node instanceof NatExampleNode)
+						openExampleInTab(node as NatExampleNode)
+				}
+			])
 		]
-
+		
+		tabFolder = new CTabFolder(shell, SWT::BORDER) => [
+			layoutData = new GridData(GridData::FILL_BOTH)
+		]
+		
 		// Start
 		shell.open
 
@@ -51,12 +85,72 @@
 				display.sleep
 
 		// Stop
-		natTable.dispose
+		for (exampleNode : exampleControlMap.keySet) {
+			// Stop
+			exampleNode.natExample.stop
+			
+			val exampleControl = exampleControlMap.get(exampleNode)
+			exampleControl.dispose
+		}
+		
+		tabFolder.dispose
 
 		shell.dispose
 		display.dispose
 	}
 	
+	def private void openExampleInTab(NatExampleNode node) {
+		// Tab
+		val tabComposite = new Composite(tabFolder, SWT::NONE) => [
+			layout = new GridLayout(1, false)
+		]
+		
+		// Create example control
+		val exampleControl = new SWTNatTable(tabComposite) => [
+			layoutData = new GridData(GridData::FILL_BOTH)
+			injector = createInjector
+			layer = node.natExample.createLayer
+		]
+		exampleControlMap.put(node, exampleControl)
+		
+		// Description
+		val description = node.natExample.description
+		if (description != null && description.length() > 0) {
+			new Group(tabComposite, SWT::NONE) => [
+				text = "Description"
+				layoutData = new GridData(GridData::FILL_HORIZONTAL)
+				layout = new FillLayout
+				
+				new Label(it, SWT::WRAP) => [
+					text = description
+				]
+			]
+		}
+		
+		tabFolder.selection = new CTabItem(tabFolder, SWT::CLOSE) => [
+			text = node.displayName
+			control = tabComposite
+			
+			addDisposeListener([ event |
+				// Stop
+				node.natExample.stop
+				
+				// Dispose
+				val control = exampleControlMap.get(node)
+				if (control != null && !control.disposed) {
+					control.dispose
+				}
+				
+				// Remove from maps
+				exampleControlMap.remove(node)
+				examplePathMap.remove(node)
+			])
+		]
+		
+		// Start
+		node.natExample.start
+	}
+	
 	def Injector createInjector() {
 		// UI bindings
 		val uiBindings = new UiBindingsImpl