Bug 515922 - Contribute Glace http://ystrot.github.io/glance/ to the e4
project

Change-Id: I55b2fd953d3064aa169c77cc5501decb3501b4ed
Signed-off-by: Lars Vogel <Lars.Vogel@vogella.com>
diff --git a/bundles/org.eclipse.ui.glance/.classpath b/bundles/org.eclipse.ui.glance/.classpath
new file mode 100644
index 0000000..64c5e31
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/.classpath
@@ -0,0 +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="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.ui.glance/.project b/bundles/org.eclipse.ui.glance/.project
new file mode 100644
index 0000000..d48c9ce
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.ui.glance</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.ui.glance/META-INF/MANIFEST.MF b/bundles/org.eclipse.ui.glance/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..2e672bb
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/META-INF/MANIFEST.MF
@@ -0,0 +1,25 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Eclipse Glance
+Bundle-SymbolicName: org.eclipse.ui.glance;singleton:=true
+Bundle-Version: 0.0.1.qualifier
+Bundle-Activator: org.eclipse.ui.glance.internal.GlancePlugin
+Bundle-Vendor: Exyte
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.ui.editors,
+ org.eclipse.jface.text,
+ org.eclipse.ui.ide
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-ActivationPolicy: lazy
+Export-Package: org.eclipse.ui.glance.panels,
+ org.eclipse.ui.glance.sources,
+ org.eclipse.ui.glance.utils,
+ org.eclipse.ui.glance.controls.decor,
+ org.eclipse.ui.glance.controls.descriptors,
+ org.eclipse.ui.glance.controls.items,
+ org.eclipse.ui.glance.controls.table,
+ org.eclipse.ui.glance.controls.text.styled,
+ org.eclipse.ui.glance.controls.tree,
+ org.eclipse.ui.glance.viewers.descriptors
+ 
diff --git a/bundles/org.eclipse.ui.glance/build.properties b/bundles/org.eclipse.ui.glance/build.properties
new file mode 100644
index 0000000..0bf3861
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/build.properties
@@ -0,0 +1,17 @@
+###############################################################################
+# Copyright (c) 2017 Exyte
+# 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:
+#     Yuri Strot - initial API and implementation
+###############################################################################
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               plugin.xml,\
+               icons/,\
+               schema/
diff --git a/bundles/org.eclipse.ui.glance/icons/marker.gif b/bundles/org.eclipse.ui.glance/icons/marker.gif
new file mode 100644
index 0000000..10c71c0
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/icons/marker.gif
Binary files differ
diff --git a/bundles/org.eclipse.ui.glance/icons/next.gif b/bundles/org.eclipse.ui.glance/icons/next.gif
new file mode 100644
index 0000000..8667900
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/icons/next.gif
Binary files differ
diff --git a/bundles/org.eclipse.ui.glance/icons/prev.gif b/bundles/org.eclipse.ui.glance/icons/prev.gif
new file mode 100644
index 0000000..cd9a705
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/icons/prev.gif
Binary files differ
diff --git a/bundles/org.eclipse.ui.glance/icons/search.png b/bundles/org.eclipse.ui.glance/icons/search.png
new file mode 100644
index 0000000..6c05b9b
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/icons/search.png
Binary files differ
diff --git a/bundles/org.eclipse.ui.glance/icons/update_1.gif b/bundles/org.eclipse.ui.glance/icons/update_1.gif
new file mode 100644
index 0000000..3ca04d0
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/icons/update_1.gif
Binary files differ
diff --git a/bundles/org.eclipse.ui.glance/icons/update_2.gif b/bundles/org.eclipse.ui.glance/icons/update_2.gif
new file mode 100644
index 0000000..3241d7c
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/icons/update_2.gif
Binary files differ
diff --git a/bundles/org.eclipse.ui.glance/icons/update_done.gif b/bundles/org.eclipse.ui.glance/icons/update_done.gif
new file mode 100644
index 0000000..e4a1d38
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/icons/update_done.gif
Binary files differ
diff --git a/bundles/org.eclipse.ui.glance/icons/wait.gif b/bundles/org.eclipse.ui.glance/icons/wait.gif
new file mode 100644
index 0000000..6bc0eeb
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/icons/wait.gif
Binary files differ
diff --git a/bundles/org.eclipse.ui.glance/plugin.xml b/bundles/org.eclipse.ui.glance/plugin.xml
new file mode 100644
index 0000000..bc87ba7
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/plugin.xml
@@ -0,0 +1,222 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+<!--
+    Copyright (c) 2017 Exyte
+    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:
+        Yuri Strot - initial API and implementation
+ -->
+
+<plugin>
+	<extension-point id="sources" name="sources" schema="schema/sources.exsd"/>
+	<extension-point id="excludedSources" name="excludedSources" schema="schema/excludedSources.exsd"/>
+	
+   <extension
+         point="org.eclipse.ui.handlers">
+      <handler
+            commandId="org.eclipse.ui.glance.commands.open"
+            class="org.eclipse.ui.glance.internal.OpenSearchPanelHandler">
+      </handler>
+   </extension>
+
+   <extension point="org.eclipse.ui.startup">
+      <startup class="org.eclipse.ui.glance.internal.GlanceStartup"/>
+   </extension>
+
+	<extension point="org.eclipse.ui.commands">
+		<command
+        categoryId="org.eclipse.ui.glance.commands"
+        description="Open Glance search panel"
+        id="org.eclipse.ui.glance.commands.open"
+        name="Open Glance">
+  </command>
+  <command
+        categoryId="org.eclipse.ui.glance.commands"
+        description="Close Glance search panel"
+        id="org.eclipse.ui.glance.commands.close"
+        name="Close Glance">
+  </command>
+  <command
+        categoryId="org.eclipse.ui.glance.commands"
+        description="Show next Glance match"
+        id="org.eclipse.ui.glance.nextResult"
+        name="Next Match">
+  </command>
+  <command
+        categoryId="org.eclipse.ui.glance.commands"
+        description="Show previous Glance match"
+        id="org.eclipse.ui.glance.prevResult"
+        name="Previous Match">
+  </command>
+  <command
+        categoryId="org.eclipse.ui.glance.commands"
+        description="Return focus back from Glance search panel to control"
+        id="org.eclipse.ui.glance.commands.focus"
+        name="Return Focus Back">
+  </command>
+  <command
+        categoryId="org.eclipse.ui.glance.commands"
+        description="Clear Glance search history"
+        id="org.eclipse.ui.glance.commands.clearHistory"
+        name="Clear History">
+  </command>
+  <category
+        description="Glance search commands"
+        id="org.eclipse.ui.glance.commands"
+        name="Glance">
+  </category>
+	</extension>
+	
+	<extension point="org.eclipse.ui.bindings">
+		<key
+			sequence="M1+M3+F"
+			contextId="org.eclipse.ui.contexts.dialogAndWindow"
+			commandId="org.eclipse.ui.glance.commands.open"
+			schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"/>
+  <key
+        commandId="org.eclipse.ui.glance.commands.focus"
+        contextId="org.eclipse.ui.glance.context"
+        schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+        sequence="M1+M3+F">
+  </key>
+  <key
+        commandId="org.eclipse.ui.glance.commands.close"
+        contextId="org.eclipse.ui.glance.context"
+        schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+        sequence="ESC">
+  </key>
+  <key
+        commandId="org.eclipse.ui.glance.nextResult"
+        contextId="org.eclipse.ui.glance.context"
+        schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+        sequence="ENTER">
+  </key>
+  <key
+        commandId="org.eclipse.ui.glance.prevResult"
+        contextId="org.eclipse.ui.glance.context"
+        schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+        sequence="M2+ENTER">
+  </key>
+  <key
+  		commandId="org.eclipse.ui.glance.commands.clearHistory"
+  		contextId="org.eclipse.ui.glance.context"
+  		schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+        sequence="M1+M2+BS">
+  </key>
+		<sequenceModifier find="M1+M3" replace="M1+M4" platforms="cocoa,carbon"/>
+	</extension>
+
+	<extension point="org.eclipse.ui.preferencePages">
+		<page
+			class="org.eclipse.ui.glance.internal.preferences.GlancePreferencePage"
+			id="org.eclipse.ui.glance.preference"
+			category="org.eclipse.ui.preferencePages.Workbench"
+			name="Glance Search"/>
+	</extension>
+
+	<extension point="org.eclipse.core.runtime.preferences">
+		<initializer class="org.eclipse.ui.glance.internal.preferences.GlancePreferenceInitializer"/>
+	</extension>
+
+	<extension point="org.eclipse.core.resources.markers" id="marker" name="Glance Results">
+		<super type="org.eclipse.core.resources.textmarker"/>
+		<attribute name="line"/>
+	</extension>
+	
+	<extension point="org.eclipse.ui.ide.markerImageProviders">
+		<imageprovider 
+			id="org.eclipse.ui.glance.markerProvider"
+			markertype="org.eclipse.ui.glance.marker"
+			icon="icons/marker.gif">
+		</imageprovider>
+	</extension>
+	
+	<extension point="org.eclipse.ui.editors.annotationTypes">
+		<type
+			name="org.eclipse.ui.glance.highlight"
+			markerType="org.eclipse.ui.glance.marker">
+		</type>
+  <type
+        markerType="org.eclipse.ui.glance.marker"
+        name="org.eclipse.ui.glance.select">
+  </type>
+	</extension>
+	
+	<extension point="org.eclipse.ui.editors.markerAnnotationSpecification">
+		<specification
+			annotationType="org.eclipse.ui.glance.highlight"
+			label="Glance Results"
+			icon="icons/marker.gif"
+			textPreferenceKey="glanceText"
+			textPreferenceValue="false"
+			highlightPreferenceKey="glanceHighlight"
+			highlightPreferenceValue="true"
+			overviewRulerPreferenceKey="glanceOverviewRuler"
+			overviewRulerPreferenceValue="true"
+			verticalRulerPreferenceKey="glanceVerticalRuler"
+			verticalRulerPreferenceValue="true"
+			colorPreferenceKey="glanceColorBackground"
+			colorPreferenceValue="255,255,128"
+			presentationLayer="5"
+			showInNextPrevDropdownToolbarActionKey="glanceShowNextPrev"
+			showInNextPrevDropdownToolbarAction="true"
+			isGoToNextNavigationTargetKey="glanceGoNext"
+			isGoToNextNavigationTarget="false"
+			isGoToPreviousNavigationTargetKey="glanceGoPrev"
+			isGoToPreviousNavigationTarget="false">
+		</specification>
+  <specification
+        annotationType="org.eclipse.ui.glance.select"
+        colorPreferenceKey="glanceSelectedColorBackground"
+        colorPreferenceValue="255,128,0"
+        highlightPreferenceKey="glanceHighlight"
+        highlightPreferenceValue="true"
+        icon="icons/marker.gif"
+        isGoToNextNavigationTarget="false"
+        isGoToNextNavigationTargetKey="glanceGoNext"
+        isGoToPreviousNavigationTarget="false"
+        isGoToPreviousNavigationTargetKey="glanceGoPrev"
+        label="Glance Selected Result"
+        overviewRulerPreferenceKey="glanceOverviewRuler"
+        overviewRulerPreferenceValue="true"
+        presentationLayer="6"
+        showInNextPrevDropdownToolbarAction="true"
+        showInNextPrevDropdownToolbarActionKey="glanceShowNextPrev"
+        textPreferenceKey="glanceText"
+        textPreferenceValue="false"
+        verticalRulerPreferenceKey="glanceVerticalRuler"
+        verticalRulerPreferenceValue="true">
+  </specification>
+	</extension>
+ <extension
+       point="org.eclipse.ui.contexts">
+    <context
+          id="org.eclipse.ui.glance.context"
+          name="Glance Search Context">
+    </context>
+ </extension>
+ <extension point="org.eclipse.ui.glance.sources">
+    <source
+      class="org.eclipse.ui.glance.controls.descriptors.StyledTextDescriptor"
+      priority="1"/>
+    <source
+      class="org.eclipse.ui.glance.controls.descriptors.ListeningStyledTextDescriptor"
+      priority="5"/>
+    <source
+      class="org.eclipse.ui.glance.controls.descriptors.TreeDescriptor"
+      priority="1"/>
+    <source
+      class="org.eclipse.ui.glance.controls.descriptors.TableDescriptor"
+      priority="1"/>
+    <source
+      class="org.eclipse.ui.glance.viewers.descriptors.SourceViewerDescriptor"
+      priority="3"/>
+    <source
+      class="org.eclipse.ui.glance.viewers.descriptors.TextViewerDescriptor"
+      priority="2"/>
+  </extension>
+</plugin>
diff --git a/bundles/org.eclipse.ui.glance/pom.xml b/bundles/org.eclipse.ui.glance/pom.xml
new file mode 100644
index 0000000..79a7588
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/pom.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (c) 2017 Exyte
+    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:
+        Yuri Strot - initial API and implementation
+ -->
+
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>plugins</artifactId>
+    <groupId>org.eclipse.ui.glance</groupId>
+    <version>0.0.1-SNAPSHOT</version>
+  </parent>
+
+  <groupId>org.eclipse.ui.glance</groupId>
+  <artifactId>org.eclipse.ui.glance</artifactId>
+  <version>0.0.1-SNAPSHOT</version>
+  <packaging>eclipse-plugin</packaging>
+  
+</project>
diff --git a/bundles/org.eclipse.ui.glance/schema/excludedSources.exsd b/bundles/org.eclipse.ui.glance/schema/excludedSources.exsd
new file mode 100644
index 0000000..7adf813
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/schema/excludedSources.exsd
@@ -0,0 +1,102 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.ui.glance" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+      <appInfo>
+         <meta.schema plugin="org.eclipse.ui.glance" id="excludedTextProvider" name="excludedTextProvider"/>
+      </appInfo>
+      <documentation>
+         [Enter description of this extension point.]
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <annotation>
+         <appInfo>
+            <meta.element />
+         </appInfo>
+      </annotation>
+      <complexType>
+         <sequence>
+            <element ref="source" minOccurs="1" maxOccurs="unbounded"/>
+         </sequence>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute translatable="true"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="source">
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="java" basedOn=":org.eclipse.ui.glance.sources.ITextSourceDescriptor"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="since"/>
+      </appInfo>
+      <documentation>
+         [Enter the first release in which this extension point appears.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="examples"/>
+      </appInfo>
+      <documentation>
+         [Enter extension point usage example here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="apiinfo"/>
+      </appInfo>
+      <documentation>
+         [Enter API information here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="implementation"/>
+      </appInfo>
+      <documentation>
+         [Enter information about supplied implementation of this extension point.]
+      </documentation>
+   </annotation>
+
+
+</schema>
diff --git a/bundles/org.eclipse.ui.glance/schema/sources.exsd b/bundles/org.eclipse.ui.glance/schema/sources.exsd
new file mode 100644
index 0000000..081c813
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/schema/sources.exsd
@@ -0,0 +1,109 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.ui.glance" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+      <appinfo>
+         <meta.schema plugin="org.eclipse.ui.glance" id="textProvider" name="textProvider"/>
+      </appinfo>
+      <documentation>
+         [Enter description of this extension point.]
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <annotation>
+         <appinfo>
+            <meta.element />
+         </appinfo>
+      </annotation>
+      <complexType>
+         <sequence>
+            <element ref="source" minOccurs="1" maxOccurs="unbounded"/>
+         </sequence>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appinfo>
+                  <meta.attribute translatable="true"/>
+               </appinfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="source">
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appinfo>
+                  <meta.attribute kind="java" basedOn=":org.eclipse.ui.glance.sources.ITextSourceDescriptor"/>
+               </appinfo>
+            </annotation>
+         </attribute>
+         <attribute name="priority" type="string" use="default" value="1">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="since"/>
+      </appinfo>
+      <documentation>
+         [Enter the first release in which this extension point appears.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="examples"/>
+      </appinfo>
+      <documentation>
+         [Enter extension point usage example here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="apiinfo"/>
+      </appinfo>
+      <documentation>
+         [Enter API information here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="implementation"/>
+      </appinfo>
+      <documentation>
+         [Enter information about supplied implementation of this extension point.]
+      </documentation>
+   </annotation>
+
+
+</schema>
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/Cell.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/Cell.java
new file mode 100644
index 0000000..cddaae8
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/Cell.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.decor;
+
+public abstract class Cell {
+
+	private int column;
+
+	public Cell(int column) {
+		this.column = column;
+	}
+
+	public int getColumn() {
+		return column;
+	}
+
+	@Override
+	public int hashCode() {
+		return getElement().hashCode() ^ column;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		Cell cell = (Cell) obj;
+		return cell.getElement().equals(this.getElement())
+				&& cell.column == column;
+	}
+
+	@Override
+	public String toString() {
+		StringBuffer buffer = new StringBuffer();
+		buffer.append("(");
+		buffer.append(getElement());
+		buffer.append(", ");
+		buffer.append(column);
+		buffer.append(")");
+		return buffer.toString();
+	}
+
+	protected abstract Object getElement();
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/IPath.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/IPath.java
new file mode 100644
index 0000000..2e20960
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/IPath.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.decor;
+
+import org.eclipse.swt.widgets.Composite;
+
+public interface IPath {
+
+	public void select(Composite composite);
+
+	public void discardSelection();
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/IStructContent.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/IStructContent.java
new file mode 100644
index 0000000..1fba4ee
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/IStructContent.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.decor;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.ITextSourceListener;
+
+public interface IStructContent {
+
+	public void index(IProgressMonitor monitor);
+
+	public ITextBlock[] getBlocks();
+
+	public ITextBlock getContent(StructCell cell);
+
+	public IPath getPath(ITextBlock block);
+
+	public void dispose();
+
+	public void addListener(ITextSourceListener listener);
+
+	public void removeListener(ITextSourceListener listener);
+
+	public ITextSourceListener[] getListeners();
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/IStructProvider.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/IStructProvider.java
new file mode 100644
index 0000000..9e218c8
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/IStructProvider.java
@@ -0,0 +1,19 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.decor;
+
+import org.eclipse.swt.widgets.Item;
+
+public interface IStructProvider {
+
+	StructCell getCell(Item item, int column);
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/StructCell.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/StructCell.java
new file mode 100644
index 0000000..05052eb
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/StructCell.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.decor;
+
+import org.eclipse.jface.util.Policy;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Item;
+
+import org.eclipse.ui.glance.utils.TextUtils;
+
+public abstract class StructCell extends Cell {
+
+	static final StyleRange[] NO_STYLES = new StyleRange[0];
+
+	static final String KEY_TEXT_LAYOUT = Policy.JFACE + "styled_label_key_"; //$NON-NLS-1$
+
+	public StyleRange[] styles = NO_STYLES;
+
+	public StructCell(int column) {
+		super(column);
+	}
+
+	public abstract Rectangle getBounds();
+
+	public abstract Color getForeground();
+
+	public abstract Color getBackground();
+
+	public abstract Image getImage();
+
+	public abstract Rectangle getImageBounds();
+
+	public abstract Rectangle getTextBounds();
+
+	public abstract String getText();
+
+	public abstract Font getFont();
+
+	public abstract boolean isSelected();
+
+	@Override
+	protected Object getElement() {
+		return getItem();
+	}
+
+	protected abstract Item getItem();
+
+	protected StyleRange[] nativeStyles() {
+		String key = KEY_TEXT_LAYOUT + getColumn();
+		Object data = getItem().getData(key);
+		if (data instanceof StyleRange[]) {
+			return TextUtils.copy((StyleRange[]) data);
+		}
+		return new StyleRange[0];
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/StructDecorator.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/StructDecorator.java
new file mode 100644
index 0000000..f78d23f
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/StructDecorator.java
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.decor;
+
+import java.lang.reflect.Field;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.TextLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Tree;
+
+import org.eclipse.ui.glance.sources.ColorManager;
+
+public class StructDecorator implements Listener {
+
+	private Composite composite;
+	private IStructProvider provider;
+
+	private TextLayout textLayout;
+
+	public StructDecorator(Composite composite, IStructProvider provider) {
+		this.composite = composite;
+		this.provider = provider;
+		init();
+	}
+
+	public void redraw() {
+		Rectangle rect = composite.getClientArea();
+		composite.redraw(rect.x, rect.y, rect.width, rect.height, true);
+	}
+
+	public void redraw(StructCell cell) {
+		Rectangle rect = cell.getBounds();
+		composite.redraw(rect.x, rect.y, rect.width, rect.height, true);
+	}
+
+	protected TextLayout getTextLayout() {
+		if (textLayout == null) {
+			int orientation = composite.getStyle() & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT);
+			textLayout = new TextLayout(composite.getDisplay());
+			textLayout.setOrientation(orientation);
+		} else {
+			textLayout.setText("");
+		}
+		return textLayout;
+	}
+
+	public void handleEvent(Event event) {
+		switch (event.type) {
+		case SWT.PaintItem:
+			paint(event);
+			break;
+		case SWT.EraseItem:
+			erase(event);
+			break;
+		}
+	}
+
+	protected void paint(Event event) {
+		Item item = (Item) event.item;
+		GC gc = event.gc;
+		// remember colors to restore the GC later
+		Color oldForeground = gc.getForeground();
+		Color oldBackground = gc.getBackground();
+
+		StructCell cell = provider.getCell(item, event.index);
+
+		Color foreground = cell.getForeground();
+		if (foreground != null) {
+			gc.setForeground(foreground);
+		}
+
+		Color background = cell.getBackground();
+		if (background != null) {
+			gc.setBackground(background);
+		}
+
+		if (!ColorManager.getInstance().isUseNative() && cell.isSelected()) {
+			gc.setBackground(ColorManager.getInstance().getTreeSelectionBg());
+			gc.setForeground(ColorManager.getInstance().getTreeSelectionFg());
+			gc.fillRectangle(cell.getBounds());
+		}
+
+		Image image = cell.getImage();
+		if (image != null) {
+			Rectangle imageBounds = cell.getImageBounds();
+			if (imageBounds != null) {
+				Rectangle bounds = image.getBounds();
+
+				// center the image in the given space
+				int x = imageBounds.x + Math.max(0, (imageBounds.width - bounds.width) / 2);
+				int y = imageBounds.y + Math.max(0, (imageBounds.height - bounds.height) / 2);
+				gc.drawImage(image, x, y);
+			}
+		}
+
+		Rectangle textBounds = cell.getTextBounds();
+		if (textBounds != null) {
+			TextLayout layout = getTextLayout();
+			layout.setText(cell.getText());
+			layout.setFont(cell.getFont());
+
+			StyleRange[] styles = cell.styles;
+			for (StyleRange range : styles) {
+				layout.setStyle(range, range.start, range.start + range.length - 1);
+			}
+
+			Rectangle layoutBounds = layout.getBounds();
+
+			int x = textBounds.x;
+			int avg = (textBounds.height - layoutBounds.height) / 2;
+			int y = textBounds.y + Math.max(0, avg);
+
+			layout.draw(gc, x, y);
+		}
+
+		gc.setForeground(oldForeground);
+		gc.setBackground(oldBackground);
+	}
+
+	public void erase(Event event) {
+		int style = SWT.BACKGROUND | SWT.FOREGROUND;
+		if (!ColorManager.getInstance().isUseNative()) {
+			style |= SWT.SELECTED | SWT.HOT;
+		}
+
+		event.detail &= ~style;
+	}
+
+	protected void init() {
+		eraseListeners = addListener(SWT.EraseItem);
+		paintListeners = addListener(SWT.PaintItem);
+		redraw();
+	}
+
+	private Listener[] addListener(int event) {
+		Listener[] listeners = composite.getListeners(event);
+		// should never happen, but just in case
+		if (listeners == null) {
+			listeners = new Listener[0];
+		}
+		for (Listener listener : listeners) {
+			composite.removeListener(event, listener);
+		}
+		composite.addListener(event, this);
+		return listeners;
+	}
+
+	public void dispose() {
+		if (!disposed) {
+			disposed = true;
+			if (!composite.isDisposed()) {
+				composite.removeListener(SWT.EraseItem, StructDecorator.this);
+				for (Listener listener : eraseListeners) {
+					composite.addListener(SWT.EraseItem, listener);
+				}
+
+				composite.removeListener(SWT.PaintItem, StructDecorator.this);
+				for (Listener listener : paintListeners) {
+					composite.addListener(SWT.PaintItem, listener);
+				}
+
+				if ("gtk".equalsIgnoreCase(Platform.getWS()) && composite instanceof Tree) {
+					try {
+						Field field = composite.getClass().getDeclaredField("drawForeground");
+						field.setAccessible(true);
+						if (field.get(composite) != null) {
+							// System.out.println("Fixed tree drawForeground bug");
+							field.set(composite, null);
+						}
+					} catch (Exception e) {
+						// ignore if no such field
+					}
+				}
+
+				redraw();
+			}
+		}
+	}
+
+	private Listener[] eraseListeners;
+	private Listener[] paintListeners;
+
+	public boolean isDisposed() {
+		return disposed;
+	}
+
+	private boolean disposed;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/StructSource.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/StructSource.java
new file mode 100644
index 0000000..9304fba
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/decor/StructSource.java
@@ -0,0 +1,253 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.decor;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Item;
+
+import org.eclipse.ui.glance.sources.ColorManager;
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.ITextSource;
+import org.eclipse.ui.glance.sources.ITextSourceListener;
+import org.eclipse.ui.glance.sources.Match;
+import org.eclipse.ui.glance.sources.SourceSelection;
+import org.eclipse.ui.glance.utils.TextUtils;
+
+public abstract class StructSource implements ITextSource, IStructProvider,
+		SelectionListener {
+
+	private final StructDecorator decorator;
+	private final Composite composite;
+	protected IStructContent content;
+
+	public StructSource(final Composite composite) {
+		this.composite = composite;
+		content = createContent();
+		decorator = new StructDecorator(composite, this);
+	}
+
+    public StructCell getCell(final Item item, final int column) {
+		final StructCell cell = createCell(item, column);
+		StyleRange[] styles = cellToStyles.get(cell);
+		if (styles == null) {
+			styles = calcStyles(cell);
+			cellToStyles.put(cell, styles);
+		}
+		cell.styles = styles;
+		return cell;
+	}
+
+    public boolean isIndexRequired() {
+        return true;
+	}
+
+	private StyleRange[] calcStyles(final StructCell cell) {
+		final ITextBlock block = content.getContent(cell);
+		blockToCell.put(block, cell);
+
+		final StyleRange[] cellStyles = cell.nativeStyles();
+		final StyleRange[] blockStyles = createStyles(block);
+
+		if (blockStyles == null || blockStyles.length == 0)
+			return cellStyles;
+		if (cellStyles.length == 0)
+			return blockStyles;
+		final Region region = new Region(0, cell.getText().length());
+		final int size = cellStyles.length + blockStyles.length;
+		final TextPresentation presentation = new TextPresentation(region, size);
+		presentation.replaceStyleRanges(cellStyles);
+		presentation.mergeStyleRanges(blockStyles);
+		return TextUtils.getStyles(presentation);
+	}
+
+	protected abstract StructCell createCell(Item item, int column);
+
+	protected abstract IStructContent createContent();
+
+	protected abstract SourceSelection getSourceSelection();
+	
+	/**
+	 * @return the composite
+	 */
+	public Composite getControl() {
+		return composite;
+	}
+
+    public ITextBlock[] getBlocks() {
+		return content.getBlocks();
+	}
+
+    public void dispose() {
+		content.dispose();
+		decorator.dispose();
+	}
+
+    public boolean isDisposed() {
+		return decorator.isDisposed();
+	}
+
+    public void index(final IProgressMonitor monitor) {
+		content.index(monitor);
+	}
+
+    public void widgetDefaultSelected(final SelectionEvent e) {
+		fireSelectionChanged();
+	}
+
+    public void widgetSelected(final SelectionEvent e) {
+		fireSelectionChanged();
+	}
+
+	protected void fireSelectionChanged() {
+		discardSelection();
+		final SourceSelection selection = getSourceSelection();
+		final ITextSourceListener[] listeners = content.getListeners();
+		for (final ITextSourceListener listener : listeners) {
+			listener.selectionChanged(selection);
+		}
+	}
+
+    public void select(final Match match) {
+		if (match != null) {
+			discardSelection();
+			path = content.getPath(match.getBlock());
+			path.select(getControl());
+		}
+		setMatch(match);
+	}
+
+    public void show(final Match[] matches) {
+		setMatches(matches);
+	}
+
+    public void addTextSourceListener(final ITextSourceListener listener) {
+		content.addListener(listener);
+	}
+
+    public void removeTextSourceListener(final ITextSourceListener listener) {
+		content.removeListener(listener);
+	}
+
+	private void discardSelection() {
+		if (path != null) {
+			path.discardSelection();
+			path = null;
+		}
+	}
+
+	private void setMatch(final Match match) {
+		final StructCell oldSel = updateSelection(false);
+		selection = match;
+		final StructCell newSel = updateSelection(true);
+		if (oldSel != null) {
+			decorator.redraw(oldSel);
+		}
+		if (newSel != null && newSel != oldSel) {
+			decorator.redraw(newSel);
+		}
+	}
+
+	private StructCell updateSelection(final boolean add) {
+		if (selection != null) {
+			final ITextBlock block = selection.getBlock();
+			final StructCell cell = blockToCell.get(block);
+			if (cell != null) {
+				cellToStyles.remove(cell);
+				return cell;
+			}
+		}
+		return null;
+	}
+
+	private void setMatches(final Match[] matches) {
+		init();
+		for (final Match match : matches) {
+			final ITextBlock block = match.getBlock();
+			List<Match> list = blockToMatches.get(block);
+			if (list == null) {
+				list = new ArrayList<Match>();
+				blockToMatches.put(block, list);
+			}
+			list.add(match);
+		}
+		decorator.redraw();
+	}
+
+	private StyleRange[] createStyles(final ITextBlock block) {
+		final List<StyleRange> list = new ArrayList<StyleRange>();
+		boolean selectionFound = false;
+		final List<Match> matches = blockToMatches.get(block);
+		if (matches != null) {
+			for (final Match match : matches) {
+				final boolean sel = match.equals(selection);
+				selectionFound = selectionFound | sel;
+				final StyleRange range = createStyle(match, sel);
+				list.add(range);
+			}
+		}
+
+		if (!selectionFound && selection != null) {
+			final ITextBlock sBlock = selection.getBlock();
+			if (sBlock.equals(block)) {
+				list.add(createStyle(selection, true));
+			}
+		}
+		return list.toArray(new StyleRange[list.size()]);
+	}
+
+	private StyleRange createStyle(final Match match, final boolean selection) {
+		final Display display = composite.getDisplay();
+		Color fgColor, bgColor;
+		if (selection) {
+			// Lighten to avoid search selection on system selection
+			fgColor = ColorManager.lighten(display
+					.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT), 50);
+			bgColor = ColorManager.getInstance().getSelectedBackgroundColor();
+		} else {
+			// To avoid white text on light-green background
+			fgColor = display.getSystemColor(SWT.COLOR_BLACK);
+			bgColor = ColorManager.getInstance().getBackgroundColor();
+		}
+		return new StyleRange(match.getOffset(), match.getLength(), fgColor,
+				bgColor);
+	}
+
+	public SourceSelection getSelection() {
+	    return null;
+	}
+	   
+	public void init() {
+		blockToMatches = new HashMap<ITextBlock, List<Match>>();
+		blockToCell = new HashMap<ITextBlock, StructCell>();
+		cellToStyles = new HashMap<StructCell, StyleRange[]>();
+	}
+
+	private Match selection;
+	private IPath path;
+
+	private Map<ITextBlock, List<Match>> blockToMatches;
+	private Map<ITextBlock, StructCell> blockToCell;
+	private Map<StructCell, StyleRange[]> cellToStyles;
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/descriptors/ListeningStyledTextDescriptor.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/descriptors/ListeningStyledTextDescriptor.java
new file mode 100644
index 0000000..1a00916
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/descriptors/ListeningStyledTextDescriptor.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.controls.descriptors;
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Control;
+
+import org.eclipse.ui.glance.controls.text.styled.ListeningStyledTextSource;
+import org.eclipse.ui.glance.sources.ITextSource;
+import org.eclipse.ui.glance.sources.ITextSourceDescriptor;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class ListeningStyledTextDescriptor implements ITextSourceDescriptor {
+
+	public ITextSource createSource(Control control) {
+		return new ListeningStyledTextSource((StyledText) control);
+	}
+
+	public boolean isValid(Control control) {
+		if (control instanceof StyledText) {
+			StyledText text = (StyledText) control;
+			return text.isListening(LineGetStyle);
+		}
+		return false;
+	}
+
+	static final int LineGetStyle = 3002;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/descriptors/StyledTextDescriptor.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/descriptors/StyledTextDescriptor.java
new file mode 100644
index 0000000..4d69641
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/descriptors/StyledTextDescriptor.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.controls.descriptors;
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Control;
+
+import org.eclipse.ui.glance.controls.text.styled.StyledTextSource;
+import org.eclipse.ui.glance.sources.ITextSource;
+import org.eclipse.ui.glance.sources.ITextSourceDescriptor;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class StyledTextDescriptor implements ITextSourceDescriptor {
+
+	public ITextSource createSource(Control control) {
+		return new StyledTextSource((StyledText) control);
+	}
+
+	public boolean isValid(Control control) {
+		return control instanceof StyledText;
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/descriptors/TableDescriptor.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/descriptors/TableDescriptor.java
new file mode 100644
index 0000000..bbf79fd
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/descriptors/TableDescriptor.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.controls.descriptors;
+
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Table;
+
+import org.eclipse.ui.glance.controls.table.TableSource;
+import org.eclipse.ui.glance.sources.ITextSource;
+import org.eclipse.ui.glance.sources.ITextSourceDescriptor;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class TableDescriptor implements ITextSourceDescriptor {
+
+	public ITextSource createSource(Control control) {
+		return new TableSource((Table) control);
+	}
+
+	public boolean isValid(Control control) {
+		return control instanceof Table;
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/descriptors/TreeDescriptor.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/descriptors/TreeDescriptor.java
new file mode 100644
index 0000000..0b5117b
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/descriptors/TreeDescriptor.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.controls.descriptors;
+
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Tree;
+
+import org.eclipse.ui.glance.controls.tree.TreeControlSource;
+import org.eclipse.ui.glance.sources.ITextSource;
+import org.eclipse.ui.glance.sources.ITextSourceDescriptor;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class TreeDescriptor implements ITextSourceDescriptor {
+
+	public ITextSource createSource(Control control) {
+		return new TreeControlSource((Tree) control);
+	}
+
+	public boolean isValid(Control control) {
+		return control instanceof Tree;
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/items/ItemCell.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/items/ItemCell.java
new file mode 100644
index 0000000..bb7af7d
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/items/ItemCell.java
@@ -0,0 +1,138 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.controls.items;
+
+import org.eclipse.jface.util.Policy;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Item;
+
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.ITextBlockListener;
+import org.eclipse.ui.glance.utils.TextUtils;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class ItemCell implements ITextBlock {
+
+	private Item item;
+	private int index;
+	private ItemProvider provider;
+
+	public static final String KEY_TEXT_LAYOUT = Policy.JFACE
+			+ "styled_label_key_"; //$NON-NLS-1$
+
+	public ItemCell(Item item, int index, ItemProvider provider) {
+		this.item = item;
+		this.index = index;
+		this.provider = provider;
+	}
+
+	public Image getImage() {
+		return provider.getImage(item, index);
+	}
+
+	public Object getKey() {
+		Object data = item.getData();
+		if (data != null)
+			return data;
+		return item;
+	}
+
+	/**
+	 * @return the item
+	 */
+	public Item getItem() {
+		return item;
+	}
+
+	public String getText() {
+		return provider.getColumnCount(item) == 0 ? item.getText() : provider
+				.getText(item, index);
+	}
+
+	public int getLength() {
+		return getText().length();
+	}
+
+	public StyleRange[] getStyles() {
+		String key = KEY_TEXT_LAYOUT + index;
+		Object data = item.getData(key);
+		if (data instanceof StyleRange[]) {
+			return TextUtils.copy((StyleRange[]) data);
+		}
+		return new StyleRange[0];
+	}
+
+	/**
+	 * @return the index
+	 */
+	public int getIndex() {
+		return index;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.lang.Object#hashCode()
+	 */
+	@Override
+	public int hashCode() {
+		return item.hashCode() ^ index;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.lang.Object#equals(java.lang.Object)
+	 */
+	@Override
+	public boolean equals(Object obj) {
+		ItemCell item = (ItemCell) obj;
+		return item.item.equals(this.item) && item.index == index;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.lang.Comparable#compareTo(java.lang.Object)
+	 */
+	public int compareTo(ITextBlock block) {
+		ItemCell cell = (ItemCell) block;
+		return provider.compare(item, cell.item);
+	}
+
+	public void addTextBlockListener(ITextBlockListener listener) {
+	}
+
+	public void removeTextBlockListener(ITextBlockListener listener) {
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.lang.Object#toString()
+	 */
+	@Override
+	public String toString() {
+		StringBuffer buffer = new StringBuffer();
+		buffer.append("{");
+		buffer.append(item);
+		buffer.append(", ");
+		buffer.append(index);
+		buffer.append("}");
+		return buffer.toString();
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/items/ItemDecorator.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/items/ItemDecorator.java
new file mode 100644
index 0000000..e6aaf5d
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/items/ItemDecorator.java
@@ -0,0 +1,283 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.controls.items;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.TextLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Listener;
+
+import org.eclipse.ui.glance.sources.ColorManager;
+import org.eclipse.ui.glance.sources.ITextSourceListener;
+import org.eclipse.ui.glance.utils.TextUtils;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class ItemDecorator implements Listener {
+
+	public static final int DEFAULT_STYLE = SWT.BACKGROUND | SWT.FOREGROUND | SWT.SELECTED | SWT.HOT;
+
+	protected Composite composite;
+	protected ItemProvider provider;
+	protected int style;
+
+	protected TextLayout textLayout;
+	protected Map<ItemCell, StyleRange[]> itemToMatches;
+	protected Map<ItemCell, StyleRange[]> cacheStyles;
+	protected List<ItemCell> cells;
+	protected HashSet<ItemCell> cellSet;
+
+	private ListenerList listeners = new ListenerList();
+
+	public ItemDecorator(Composite composite, ItemProvider provider) {
+		this(composite, provider, DEFAULT_STYLE);
+	}
+
+	public ItemDecorator(Composite composite, ItemProvider provider, int style) {
+		this.composite = composite;
+		this.provider = provider;
+		this.style = style;
+		clearStyles();
+		init();
+	}
+
+	public void addTextSourceListener(ITextSourceListener listener) {
+		listeners.add(listener);
+	}
+
+	public void removeTextSourceListener(ITextSourceListener listener) {
+		listeners.remove(listener);
+	}
+
+	public ITextSourceListener[] getListeners() {
+		Object[] objects = listeners.getListeners();
+		ITextSourceListener[] listeners = new ITextSourceListener[objects.length];
+		System.arraycopy(objects, 0, listeners, 0, objects.length);
+		return listeners;
+	}
+
+	public void blocksChanged(ItemCell[] removed, ItemCell[] added) {
+		for (ItemCell cell : removed) {
+			cells.remove(cell);
+			cellSet.remove(cell);
+		}
+		for (ItemCell cell : added) {
+			cells.add(cell);
+			cellSet.add(cell);
+			cell.getItem().addListener(SWT.Dispose, this);
+		}
+		for (ITextSourceListener listener : getListeners()) {
+			listener.blocksChanged(removed, added);
+		}
+	}
+
+	public void clearStyles() {
+		itemToMatches = new HashMap<ItemCell, StyleRange[]>();
+		cacheStyles = new HashMap<ItemCell, StyleRange[]>();
+	}
+
+	public List<ItemCell> getCells() {
+		return cells;
+	}
+
+	public void setCells(List<ItemCell> cells) {
+		this.cells = cells;
+		for (ItemCell cell : cells) {
+			cell.getItem().addListener(SWT.Dispose, this);
+		}
+		cellSet = new HashSet<ItemCell>(cells);
+	}
+
+	public void setStyles(ItemCell cell, StyleRange[] styles) {
+		if (styles == null)
+			itemToMatches.remove(cell);
+		else
+			itemToMatches.put(cell, styles);
+		cacheStyles.put(cell, calculateStyles(cell));
+	}
+
+	public void redraw() {
+		Rectangle rect = composite.getClientArea();
+		composite.redraw(rect.x, rect.y, rect.width, rect.height, true);
+	}
+
+	public void redraw(ItemCell cell) {
+		Rectangle rect = provider.getBounds(cell.getItem(), cell.getIndex());
+		composite.redraw(rect.x, rect.y, rect.width, rect.height, true);
+	}
+
+	protected TextLayout getTextLayout() {
+		if (textLayout == null) {
+			int orientation = composite.getStyle() & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT);
+			textLayout = new TextLayout(composite.getDisplay());
+			textLayout.setOrientation(orientation);
+		} else {
+			textLayout.setText("");
+		}
+		return textLayout;
+	}
+
+	protected StyleRange[] getRanges(ItemCell cell) {
+		StyleRange[] ranges = cacheStyles.get(cell);
+		if (ranges == null) {
+			ranges = calculateStyles(cell);
+			cacheStyles.put(cell, ranges);
+		}
+		return ranges;
+	}
+
+	protected StyleRange[] calculateStyles(ItemCell cell) {
+		StyleRange[] cellStyles = cell.getStyles();
+		StyleRange[] matchStyles = itemToMatches.get(cell);
+		if (matchStyles == null || matchStyles.length == 0)
+			return cellStyles;
+		if (cellStyles.length == 0)
+			return matchStyles;
+		Region region = new Region(0, cell.getLength());
+		int size = cellStyles.length + matchStyles.length;
+		TextPresentation presentation = new TextPresentation(region, size);
+		presentation.replaceStyleRanges(cellStyles);
+		presentation.mergeStyleRanges(matchStyles);
+		return TextUtils.getStyles(presentation);
+	}
+
+	public void handleEvent(Event event) {
+		switch (event.type) {
+		case SWT.PaintItem:
+			paint(event);
+			break;
+		case SWT.EraseItem:
+			erase(event);
+			break;
+		case SWT.Dispose:
+			if (event.widget instanceof Item) {
+				ItemCell cell = new ItemCell((Item) event.widget, event.index, provider);
+				blocksChanged(new ItemCell[] { cell }, new ItemCell[0]);
+			}
+			break;
+		}
+	}
+
+	protected void paint(Event event) {
+		Item item = (Item) event.item;
+		GC gc = event.gc;
+		// remember colors to restore the GC later
+		Color oldForeground = gc.getForeground();
+		Color oldBackground = gc.getBackground();
+
+		Color foreground = provider.getForeground(item, event.index);
+		if (foreground != null) {
+			gc.setForeground(foreground);
+		}
+
+		Color background = provider.getBackground(item, event.index);
+		if (background != null) {
+			gc.setBackground(background);
+		}
+
+		if (!ColorManager.getInstance().isUseNative() && (event.detail & SWT.SELECTED) != 0) {
+			gc.setBackground(ColorManager.getInstance().getTreeSelectionBg());
+			gc.setForeground(ColorManager.getInstance().getTreeSelectionFg());
+			gc.fillRectangle(provider.getBounds(item, event.index));
+		}
+
+		Image image = provider.getImage(item, event.index);
+		if (image != null) {
+			Rectangle imageBounds = provider.getImageBounds(item, event.index);
+			if (imageBounds != null) {
+				Rectangle bounds = image.getBounds();
+
+				// center the image in the given space
+				int x = imageBounds.x + Math.max(0, (imageBounds.width - bounds.width) / 2);
+				int y = imageBounds.y + Math.max(0, (imageBounds.height - bounds.height) / 2);
+				gc.drawImage(image, x, y);
+			}
+		}
+
+		Rectangle textBounds = provider.getTextBounds(item, event.index);
+		if (textBounds != null) {
+			TextLayout layout = getTextLayout();
+			layout.setText(provider.getText(item, event.index));
+			layout.setFont(provider.getFont(item, event.index));
+
+			ItemCell cell = new ItemCell(item, event.index, provider);
+			if (!cellSet.contains(cell)) {
+				blocksChanged(new ItemCell[0], new ItemCell[] { cell });
+			}
+			StyleRange[] ranges = getRanges(cell);
+			for (StyleRange range : ranges) {
+				layout.setStyle(range, range.start, range.start + range.length - 1);
+			}
+
+			Rectangle layoutBounds = layout.getBounds();
+
+			int x = textBounds.x;
+			int avg = (textBounds.height - layoutBounds.height) / 2;
+			int y = textBounds.y + Math.max(0, avg);
+
+			layout.draw(gc, x, y);
+		}
+
+		gc.setForeground(oldForeground);
+		gc.setBackground(oldBackground);
+	}
+
+	public void erase(Event event) {
+		int style = SWT.BACKGROUND | SWT.FOREGROUND;
+		if (!ColorManager.getInstance().isUseNative()) {
+			style |= SWT.SELECTED | SWT.HOT;
+		}
+
+		event.detail &= ~style;
+	}
+
+	protected void init() {
+		// FIXME
+		composite.addListener(SWT.EraseItem, this);
+		composite.addListener(SWT.PaintItem, this);
+		redraw();
+	}
+
+	public void dispose() {
+		if (!disposed) {
+			clearStyles();
+			composite.removeListener(SWT.PaintItem, this);
+			composite.removeListener(SWT.EraseItem, this);
+			disposed = true;
+			redraw();
+		}
+	}
+
+	public boolean isDisposed() {
+		return disposed;
+	}
+
+	private boolean disposed;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/items/ItemProvider.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/items/ItemProvider.java
new file mode 100644
index 0000000..4231b1c
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/items/ItemProvider.java
@@ -0,0 +1,50 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.controls.items;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Item;
+
+/**
+ * @author Yuri Strot
+ *
+ */
+public interface ItemProvider {
+	
+	public String getText(Item item, int index);
+	
+	public Image getImage(Item item, int index);
+	
+	public Rectangle getTextBounds(Item item, int index);
+	
+	public Rectangle getImageBounds(Item item, int index);
+	
+	public Rectangle getBounds(Item item, int index);
+	
+	public Color getBackground(Item item, int index);
+	
+	public Color getForeground(Item item, int index);
+	
+	public Font getFont(Item item, int index);
+	
+	public int getColumnCount(Item item);
+	
+	public void show(Item item);
+	
+	public int compare(Item item1, Item item2);
+	
+	public void select(Item item);
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/items/ItemSource.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/items/ItemSource.java
new file mode 100644
index 0000000..fede502
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/items/ItemSource.java
@@ -0,0 +1,209 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.controls.items;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Item;
+
+import org.eclipse.ui.glance.sources.BaseTextSource;
+import org.eclipse.ui.glance.sources.ColorManager;
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.ITextSourceListener;
+import org.eclipse.ui.glance.sources.Match;
+import org.eclipse.ui.glance.sources.SourceSelection;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public abstract class ItemSource extends BaseTextSource implements SelectionListener {
+
+	protected ItemDecorator decorator;
+	protected Composite composite;
+
+	public ItemSource(Composite composite) {
+		decorator = new ItemDecorator(composite, getItemProvider());
+		this.composite = composite;
+	}
+
+	/**
+	 * @return the composite
+	 */
+	public Composite getControl() {
+		return composite;
+	}
+
+	protected abstract ItemProvider getItemProvider();
+
+	protected abstract void collectCells(List<ItemCell> cells);
+
+	public List<ItemCell> getCells() {
+		List<ItemCell> cells = decorator.getCells();
+		if (cells == null) {
+			cells = new ArrayList<ItemCell>();
+			collectCells(cells);
+			decorator.setCells(cells);
+		}
+		return cells;
+	}
+
+	public ITextBlock[] getBlocks() {
+		return getCells().toArray(new ITextBlock[getCells().size()]);
+	}
+
+	public Font getFont() {
+		return composite.getFont();
+	}
+
+	public void select(Match match) {
+		if (match != null) {
+			Item item = getCell(match).getItem();
+			getItemProvider().show(item);
+			getItemProvider().select(item);
+		}
+
+		setMatch(match);
+	}
+
+	public void dispose() {
+		if (selection != null) {
+			getItemProvider().select(getCell(selection).getItem());
+		}
+		decorator.dispose();
+	}
+
+	public boolean isDisposed() {
+		return decorator.isDisposed();
+	}
+
+	public void widgetDefaultSelected(SelectionEvent e) {
+		fireSelectionChanged();
+	}
+
+	public void widgetSelected(SelectionEvent e) {
+		fireSelectionChanged();
+	}
+
+	protected void fireSelectionChanged() {
+		SourceSelection selection = getSelection();
+		ITextSourceListener[] listeners = decorator.getListeners();
+		for (ITextSourceListener listener : listeners) {
+			listener.selectionChanged(selection);
+		}
+	}
+
+	public void show(Match[] matches) {
+		setMatches(matches);
+	}
+
+	public void removeTextSourceListener(ITextSourceListener listener) {
+		decorator.removeTextSourceListener(listener);
+	}
+
+	public void addTextSourceListener(ITextSourceListener listener) {
+		decorator.addTextSourceListener(listener);
+	}
+
+	public void setMatch(Match match) {
+		ItemCell cell1 = null, cell2 = null;
+		if (selection != null) {
+			cell1 = getCell(selection);
+			selection = null;
+			updateCell(cell1);
+		}
+		selection = match;
+		if (selection != null) {
+			cell2 = getCell(selection);
+			updateCell(cell2);
+		}
+		if (cell1 != null)
+			decorator.redraw(cell1);
+		if (cell2 != null && cell2 != cell1)
+			decorator.redraw(cell2);
+	}
+
+	public void setMatches(Match[] matches) {
+		cellToMatches = new HashMap<ItemCell, List<Match>>();
+		for (Match match : matches) {
+			ItemCell cell = getCell(match);
+			List<Match> list = cellToMatches.get(cell);
+			if (list == null) {
+				list = new ArrayList<Match>();
+				cellToMatches.put(cell, list);
+			}
+			list.add(match);
+		}
+		decorator.clearStyles();
+		for (ItemCell cell : cellToMatches.keySet()) {
+			updateCell(cell);
+		}
+		decorator.redraw();
+	}
+
+	protected void updateCell(ItemCell cell) {
+		List<StyleRange> list = new ArrayList<StyleRange>();
+		boolean selectionFound = false;
+		if (cellToMatches != null) {
+			List<Match> matches = cellToMatches.get(cell);
+			if (matches != null) {
+				for (Match match : matches) {
+					boolean sel = match.equals(selection);
+					selectionFound = selectionFound | sel;
+					StyleRange range = createRange(match, sel);
+					list.add(range);
+				}
+			}
+		}
+		if (!selectionFound && selection != null) {
+			ItemCell selCell = (ItemCell) selection.getBlock();
+			if (selCell.equals(cell)) {
+				list.add(createRange(selection, true));
+			}
+		}
+		StyleRange[] ranges = list.toArray(new StyleRange[list.size()]);
+		decorator.setStyles(cell, ranges);
+	}
+
+	private StyleRange createRange(Match match, boolean selection) {
+		Display display = composite.getDisplay();
+		Color fgColor, bgColor;
+		if (selection) {
+			// Lighten to avoid search selection on system selection
+			fgColor = ColorManager.lighten(display.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT), 50);
+			bgColor = ColorManager.getInstance().getSelectedBackgroundColor();
+		} else {
+			// To avoid white text on light-green background
+			fgColor = display.getSystemColor(SWT.COLOR_BLACK);
+			bgColor = ColorManager.getInstance().getBackgroundColor();
+		}
+		return new StyleRange(match.getOffset(), match.getLength(), fgColor, bgColor);
+	}
+
+	protected ItemCell getCell(Match match) {
+		return (ItemCell) match.getBlock();
+	}
+
+	private Match selection;
+	private Map<ItemCell, List<Match>> cellToMatches;
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/table/TableCell.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/table/TableCell.java
new file mode 100644
index 0000000..2f681ca
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/table/TableCell.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.table;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.TableItem;
+
+import org.eclipse.ui.glance.controls.decor.StructCell;
+
+public class TableCell extends StructCell {
+
+	private TableItem item;
+
+	public TableCell(TableItem item, int column) {
+		super(column);
+		this.item = item;
+	}
+
+	@Override
+	public boolean isSelected() {
+		TableItem[] items = item.getParent().getSelection();
+		for (TableItem treeItem : items) {
+			if (treeItem == item)
+				return true;
+		}
+		return false;
+	}
+
+	public TableItem getTableItem() {
+		return item;
+	}
+
+	@Override
+	public Color getBackground() {
+		return item.getBackground(getColumn());
+	}
+
+	@Override
+	public Rectangle getBounds() {
+		return item.getBounds(getColumn());
+	}
+
+	@Override
+	public Font getFont() {
+		return item.getFont(getColumn());
+	}
+
+	@Override
+	public Color getForeground() {
+		return item.getForeground(getColumn());
+	}
+
+	@Override
+	public Image getImage() {
+		return item.getImage(getColumn());
+	}
+
+	@Override
+	public Rectangle getImageBounds() {
+		return item.getImageBounds(getColumn());
+	}
+
+	@Override
+	protected Item getItem() {
+		return item;
+	}
+
+	@Override
+	public String getText() {
+		return item.getText(getColumn());
+	}
+
+	@Override
+	public Rectangle getTextBounds() {
+		return item.getTextBounds(getColumn());
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/table/TableContent.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/table/TableContent.java
new file mode 100644
index 0000000..8660d73
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/table/TableContent.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.table;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.swt.widgets.Table;
+
+import org.eclipse.ui.glance.controls.decor.IPath;
+import org.eclipse.ui.glance.controls.decor.IStructContent;
+import org.eclipse.ui.glance.controls.decor.StructCell;
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.ITextSourceListener;
+
+public class TableContent implements IStructContent {
+
+	private final ListenerList listeners = new ListenerList();
+
+	public TableContent(Table table) {
+	}
+
+	public void addListener(ITextSourceListener listener) {
+		listeners.add(listener);
+	}
+
+	public void removeListener(ITextSourceListener listener) {
+		listeners.remove(listener);
+	}
+
+	public ITextSourceListener[] getListeners() {
+		Object[] objects = listeners.getListeners();
+		ITextSourceListener[] listeners = new ITextSourceListener[objects.length];
+		System.arraycopy(objects, 0, listeners, 0, objects.length);
+		return listeners;
+	}
+
+	public void dispose() {
+	}
+
+	public ITextBlock[] getBlocks() {
+		return null;
+	}
+
+	public ITextBlock getContent(StructCell cell) {
+		return null;
+	}
+
+	public IPath getPath(ITextBlock block) {
+		return null;
+	}
+
+	public void index(IProgressMonitor monitor) {
+		monitor.done();
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/table/TableItemProvider.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/table/TableItemProvider.java
new file mode 100644
index 0000000..ac7638b
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/table/TableItemProvider.java
@@ -0,0 +1,186 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.controls.table;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+
+import org.eclipse.ui.glance.controls.items.ItemProvider;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class TableItemProvider implements ItemProvider {
+
+	private TableItemProvider() {
+	}
+
+	private static TableItemProvider INSTANCE;
+
+	public static TableItemProvider getInstance() {
+		if (INSTANCE == null) {
+			INSTANCE = new TableItemProvider();
+		}
+		return INSTANCE;
+	}
+
+	public static TableItem getItem(Item item) {
+		return (TableItem) item;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.glance.internal.items.ItemProvider#getBackground(org.eclipse
+	 * .swt.widgets.Item, int)
+	 */
+	public Color getBackground(Item item, int index) {
+		return getItem(item).getBackground(index);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.glance.internal.items.ItemProvider#getColumnCount(org.eclipse
+	 * .swt.widgets.Item)
+	 */
+	public int getColumnCount(Item item) {
+		return getItem(item).getParent().getColumnCount();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.glance.internal.items.ItemProvider#getForeground(org.eclipse
+	 * .swt.widgets.Item, int)
+	 */
+	public Color getForeground(Item item, int index) {
+		return getItem(item).getForeground(index);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.glance.internal.items.ItemProvider#getText(org.eclipse.swt.
+	 * widgets.Item, int)
+	 */
+	public String getText(Item item, int index) {
+		return getItem(item).getText(index);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.glance.internal.items.ItemProvider#getImage(org.eclipse.swt
+	 * .widgets.Item, int)
+	 */
+	public Image getImage(Item item, int index) {
+		return getItem(item).getImage(index);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.glance.internal.items.ItemProvider#getImageBounds(org.eclipse
+	 * .swt.widgets.Item, int)
+	 */
+	public Rectangle getImageBounds(Item item, int index) {
+		return getItem(item).getImageBounds(index);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.glance.internal.items.ItemProvider#getTextBounds(org.eclipse
+	 * .swt.widgets.Item, int)
+	 */
+	public Rectangle getTextBounds(Item item, int index) {
+		return getItem(item).getTextBounds(index);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.glance.internal.items.ItemProvider#getBounds(org.eclipse.swt
+	 * .widgets.Item, int)
+	 */
+	public Rectangle getBounds(Item item, int index) {
+		return getItem(item).getBounds(index);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.glance.internal.items.ItemProvider#getFont(org.eclipse.swt.
+	 * widgets.Item, int)
+	 */
+	public Font getFont(Item item, int index) {
+		return getItem(item).getFont(index);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.glance.internal.items.ItemProvider#showItem(org.eclipse.swt
+	 * .widgets.Item)
+	 */
+	public void show(Item item) {
+		TableItem tItem = getItem(item);
+		tItem.getParent().showItem(tItem);
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.ui.glance.controls.items.ItemProvider#compare(org.eclipse.swt.widgets.Item, org.eclipse.swt.widgets.Item)
+	 */
+	public int compare(Item item1, Item item2) {
+		if (item1.equals(item2))
+			return 0;
+		Table table = getItem(item1).getParent();
+		TableItem[] items = table.getItems();
+		for (TableItem item : items) {
+			if (item1.equals(item))
+				return -1;
+			if (item2.equals(item))
+				return 1;
+		}
+		return 0;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.glance.internal.items.ItemProvider#select(org.eclipse.swt.widgets
+	 * .Item)
+	 */
+	public void select(Item item) {
+		TableItem tItem = getItem(item);
+		tItem.getParent().setSelection(tItem);
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/table/TableSource.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/table/TableSource.java
new file mode 100644
index 0000000..9aa725a
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/table/TableSource.java
@@ -0,0 +1,78 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.controls.table;
+
+import java.util.List;
+
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+
+import org.eclipse.ui.glance.controls.items.ItemCell;
+import org.eclipse.ui.glance.controls.items.ItemProvider;
+import org.eclipse.ui.glance.controls.items.ItemSource;
+import org.eclipse.ui.glance.sources.SourceSelection;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class TableSource extends ItemSource {
+
+	public TableSource(Table table) {
+		super(table);
+		table.addSelectionListener(this);
+	}
+
+	@Override
+	public Table getControl() {
+		return (Table) super.getControl();
+	}
+
+	@Override
+	public void dispose() {
+		getControl().removeSelectionListener(this);
+		super.dispose();
+	}
+
+	public SourceSelection getSelection() {
+		TableItem[] items = getControl().getSelection();
+		if (items.length > 0) {
+			List<ItemCell> cells = getCells();
+			for (ItemCell cell : cells) {
+				if (cell.getItem().equals(items[0])) {
+					return new SourceSelection(cell, 0, cell.getText().length());
+				}
+			}
+		}
+		return null;
+	}
+
+	@Override
+	protected void collectCells(List<ItemCell> cells) {
+		Table table = getControl();
+		TableItem[] items = table.getItems();
+		int columns = table.getColumnCount();
+		if (columns == 0)
+			columns = 1;
+		for (int i = 0; i < items.length; i++) {
+			for (int j = 0; j < columns; j++) {
+				cells.add(new ItemCell(items[i], j, getItemProvider()));
+			}
+		}
+	}
+
+	@Override
+	protected ItemProvider getItemProvider() {
+		return TableItemProvider.getInstance();
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/table/TableStructSource.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/table/TableStructSource.java
new file mode 100644
index 0000000..f677e26
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/table/TableStructSource.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.table;
+
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+
+import org.eclipse.ui.glance.controls.decor.StructCell;
+import org.eclipse.ui.glance.controls.decor.StructSource;
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.SourceSelection;
+
+public class TableStructSource extends StructSource {
+
+	public TableStructSource(Table table) {
+		super(table);
+		table.addSelectionListener(this);
+	}
+
+	@Override
+	public Table getControl() {
+		return (Table) super.getControl();
+	}
+
+	@Override
+	protected StructCell createCell(Item item, int column) {
+		return new TableCell((TableItem) item, column);
+	}
+
+	@Override
+	public void dispose() {
+		super.dispose();
+		getControl().removeSelectionListener(this);
+	}
+
+	@Override
+	protected TableContent createContent() {
+		return new TableContent(getControl());
+	}
+
+	protected SourceSelection getSourceSelection() {
+		TableItem[] items = getControl().getSelection();
+		if (items.length > 0) {
+			ITextBlock block = content.getContent(createCell(items[0], 0));
+			if (block != null) {
+				return new SourceSelection(block, 0, block.getText().length());
+			}
+		}
+		return null;
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/AbstractStyledTextSource.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/AbstractStyledTextSource.java
new file mode 100644
index 0000000..6324037
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/AbstractStyledTextSource.java
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.text.styled;
+
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Point;
+
+import org.eclipse.ui.glance.sources.BaseTextSource;
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.ITextSourceListener;
+import org.eclipse.ui.glance.sources.Match;
+import org.eclipse.ui.glance.sources.SourceSelection;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public abstract class AbstractStyledTextSource extends BaseTextSource implements SelectionListener {
+
+	public AbstractStyledTextSource(final StyledText text) {
+		this.text = text;
+		blocks = new StyledTextBlock[] { createTextBlock() };
+		list = new ListenerList();
+	}
+
+	protected StyledTextBlock createTextBlock() {
+		return new StyledTextBlock(text);
+	}
+
+	public final void dispose() {
+		if (text != null && !text.isDisposed() && !disposed) {
+			doDispose();
+		}
+		disposed = true;
+	}
+
+	protected void doDispose() {
+		focusKeeper.dispose();
+		text.removeSelectionListener(this);
+	}
+
+	public boolean isDisposed() {
+		return disposed;
+	}
+
+	public ITextBlock[] getBlocks() {
+		return blocks;
+	}
+
+	public void addTextSourceListener(final ITextSourceListener listener) {
+		list.add(listener);
+	}
+
+	public void removeTextSourceListener(final ITextSourceListener listener) {
+		list.remove(listener);
+	}
+
+	public void widgetDefaultSelected(final SelectionEvent e) {
+		fireSelectionChanged();
+	}
+
+	public void widgetSelected(final SelectionEvent e) {
+		fireSelectionChanged();
+	}
+
+	private void fireSelectionChanged() {
+		final SourceSelection selection = getSelection();
+		final Object[] objects = list.getListeners();
+		for (final Object object : objects) {
+			final ITextSourceListener listener = (ITextSourceListener) object;
+			listener.selectionChanged(selection);
+		}
+	}
+
+	public SourceSelection getSelection() {
+		final Point point = text.getSelection();
+		final SourceSelection selection = new SourceSelection(blocks[0], point.x, point.y - point.x);
+		return selection;
+	}
+
+	public void select(final Match match) {
+		this.selected = match;
+		focusKeeper.setMatch(match);
+	}
+
+	protected StyledText getText() {
+		return text;
+	}
+
+	@Override
+	public void init() {
+		focusKeeper = new StyledTextSelector(text);
+		text.addSelectionListener(this);
+	}
+
+	private StyledTextSelector focusKeeper;
+	private final StyledText text;
+
+	private boolean disposed;
+	private final ListenerList list;
+	private final StyledTextBlock[] blocks;
+	protected Match selected;
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/ListeningStyledTextSource.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/ListeningStyledTextSource.java
new file mode 100644
index 0000000..94ffdd8
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/ListeningStyledTextSource.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.controls.text.styled;
+
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.swt.custom.ExtendedModifyEvent;
+import org.eclipse.swt.custom.LineStyleEvent;
+import org.eclipse.swt.custom.LineStyleListener;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.ui.glance.internal.GlancePlugin;
+import org.eclipse.ui.glance.sources.Match;
+import org.eclipse.ui.glance.utils.TextUtils;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class ListeningStyledTextSource extends AbstractStyledTextSource
+		implements LineStyleListener {
+
+	/**
+	 * @param text
+	 */
+	public ListeningStyledTextSource(final StyledText text) {
+		super(text);
+		text.addLineStyleListener(this);
+	}
+
+	@Override
+	protected void doDispose() {
+		final StyledText text = getText();
+		try {
+			super.doDispose();
+			try {
+				text.removeLineStyleListener(this);
+			} finally {
+				refresh();
+			}
+		} catch (final Exception e) {
+			GlancePlugin.log("Problems with '" + text.getClass() + "'", e);
+		}
+	}
+
+	@Override
+	protected StyledTextBlock createTextBlock() {
+		return new StyledTextBlock(getText()) {
+			@Override
+			public void modifyText(final ExtendedModifyEvent event) {
+				matches = Match.EMPTY;
+				super.modifyText(event);
+			}
+		};
+	}
+
+    public void show(final Match[] matches) {
+		this.matches = matches;
+		refresh();
+	}
+	
+	@Override
+    public void select(final Match match){
+	    super.select(match);
+	    refresh();
+	}
+
+    public void lineGetStyle(final LineStyleEvent event) {
+		if (matches.length > 0) {
+			final int offset = event.lineOffset;
+			final int length = event.lineText.length();
+
+			int size = event.styles == null ? 0 : event.styles.length;
+			if (size == 0)
+				size = 1;
+			final TextPresentation presentation = new TextPresentation(new Region(
+					offset, length), size);
+			if (event.styles != null && event.styles.length > 0)
+				presentation.replaceStyleRanges(event.styles);
+			applyTextPresentation(presentation);
+			event.styles = TextUtils.getStyles(presentation);
+		}
+	}
+
+	private void refresh() {
+		final String text = getText().getText();
+		getText().redrawRange(0, text.length(), false);
+	}
+
+	private void applyTextPresentation(final TextPresentation presentation) {
+		TextUtils.applyStyles(presentation, matches, selected);
+	}
+
+	private Match[] matches = Match.EMPTY;
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/RangeGroup.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/RangeGroup.java
new file mode 100644
index 0000000..cf5d504
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/RangeGroup.java
@@ -0,0 +1,44 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.controls.text.styled;
+
+import org.eclipse.swt.custom.StyleRange;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class RangeGroup {
+
+	private int start;
+	private int end;
+	private StyleRange[] ranges;
+
+	public RangeGroup(int start, int end, StyleRange[] ranges) {
+		this.start = start;
+		this.end = end;
+		this.ranges = ranges;
+	}
+
+	public int getStart() {
+		return start;
+	}
+
+	public int getEnd() {
+		return end;
+	}
+
+	public StyleRange[] getRanges() {
+		return ranges;
+	}
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/StyledTextBlock.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/StyledTextBlock.java
new file mode 100644
index 0000000..559bc08
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/StyledTextBlock.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.controls.text.styled;
+
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.swt.custom.ExtendedModifyEvent;
+import org.eclipse.swt.custom.ExtendedModifyListener;
+import org.eclipse.swt.custom.StyledText;
+
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.ITextBlockListener;
+import org.eclipse.ui.glance.sources.TextChangedEvent;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class StyledTextBlock implements ITextBlock, ExtendedModifyListener {
+
+	public StyledTextBlock(StyledText text) {
+		this.text = text;
+		listeners = new ListenerList();
+		text.addExtendedModifyListener(this);
+	}
+
+	public String getText() {
+		return text.getText();
+	}
+
+	public void addTextBlockListener(ITextBlockListener listener) {
+		listeners.add(listener);
+	}
+
+	public void removeTextBlockListener(ITextBlockListener listener) {
+		listeners.remove(listener);
+	}
+	
+	public void modifyText(ExtendedModifyEvent event) {
+		Object[] objects = listeners.getListeners();
+		TextChangedEvent textEvent = new TextChangedEvent(event.start,
+				event.length, event.replacedText);
+		for (Object object : objects) {
+			ITextBlockListener listener = (ITextBlockListener) object;
+			listener.textChanged(textEvent);
+		}
+	}
+	
+	public int compareTo(ITextBlock o) {
+		//style text support only one text block
+		return 0;
+	}
+
+	private StyledText text;
+	private ListenerList listeners;
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/StyledTextSelector.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/StyledTextSelector.java
new file mode 100644
index 0000000..3a5eb62
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/StyledTextSelector.java
@@ -0,0 +1,58 @@
+/*******************************************************************************

+ * Copyright (c) 2017 Exyte

+ * 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:

+ *     Yuri Strot - initial API and Implementation

+ ******************************************************************************/

+package org.eclipse.ui.glance.controls.text.styled;

+

+import org.eclipse.jface.text.Region;

+import org.eclipse.swt.custom.StyledText;

+import org.eclipse.swt.graphics.Point;

+import org.eclipse.swt.widgets.Control;

+

+public class StyledTextSelector extends TextSelector {

+

+	private final StyledText text;

+

+	public StyledTextSelector(StyledText text) {

+		this.text = text;

+		init();

+	}

+

+	@Override

+	protected Control getControl() {

+		return text;

+	}

+

+	@Override

+	protected Region getSelection() {

+		final Point point = text.getSelection();

+		return new Region(point.x, point.y - point.x);

+	}

+

+	@Override

+	protected void setSelection(int offset, int length) {

+		text.setSelectionRange(offset, length);

+	}

+

+	@Override

+	protected void reveal(int offset, int length) {

+		Region region = getSelection();

+		if (region.getOffset() == offset && region.getLength() == length) {

+			text.showSelection();

+		} else {

+			setSelection(offset, length);

+			try {

+				text.showSelection();

+			} finally {

+				text.setSelectionRange(region.getOffset(), region.getLength());

+			}

+		}

+	}

+

+}

diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/StyledTextSource.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/StyledTextSource.java
new file mode 100644
index 0000000..c4cf30e
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/StyledTextSource.java
@@ -0,0 +1,127 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.controls.text.styled;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.Color;
+
+import org.eclipse.ui.glance.sources.ColorManager;
+import org.eclipse.ui.glance.sources.Match;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class StyledTextSource extends AbstractStyledTextSource {
+
+	public StyledTextSource(final StyledText text) {
+		super(text);
+	}
+
+	@Override
+	protected void doDispose() {
+		super.doDispose();
+        clearAll();
+    }
+
+    protected void clearAll() {
+        clearHighlight();
+        clearSelected();
+	}
+	protected void clearHighlight() {
+		if (previous != null && previous.length > 0) {
+			for (int i = 0; i < previous.length; i++) {
+				clearRangeGroup(previous[i]);
+			}
+		}
+		previous = null;
+	}
+	
+    protected void clearSelected() {
+        if (selectedRange != null) {
+            clearRangeGroup(selectedRange);
+            selectedRange = null;
+        }
+    }
+
+	private void clearRangeGroup(final RangeGroup group){
+	    getText().replaceStyleRanges(group.getStart(),
+            group.getEnd() - group.getStart(), new StyleRange[0]);
+	    final StyleRange[] ranges = group.getRanges();
+	    for (final StyleRange range : ranges) {
+	        getText().replaceStyleRanges(range.start, range.length,
+                new StyleRange[] { range });
+	    }
+	}
+
+	@Override
+	public void select(final Match match) {
+	    super.select(match);
+        clearSelected();
+        if (match != null) {
+            final List<StyleRange> ranges = createRanges(match, ColorManager.getInstance()
+                .getSelectedBackgroundColor());
+            selectedRange = new RangeGroup(match.getOffset(), match.getOffset() + match.getLength(),
+                ranges.toArray(new StyleRange[ranges.size()]));
+        }
+	}
+	
+	public void show(final Match[] matches) {
+        clearAll();
+        previous = new RangeGroup[matches.length];
+		for (int i = 0; i < matches.length; i++) {
+			final Match match = matches[i];
+            final List<StyleRange> ranges = createRanges(match, ColorManager.getInstance()
+                .getBackgroundColor());
+            previous[i] = new RangeGroup(match.getOffset(), match.getOffset() + match.getLength(),
+                ranges
+					.toArray(new StyleRange[ranges.size()]));
+		}
+
+        if (selected != null) {
+            select(selected);
+        }
+	}
+
+    private List<StyleRange> createRanges(final Match match, final Color bg) {
+        int index = match.getOffset();
+        final int lastIndex = index + match.getLength();
+        final StyleRange[] matchRanges = getText().getStyleRanges(index, match.getLength());
+        final List<StyleRange> ranges = new ArrayList<StyleRange>();
+        for (final StyleRange styleRange : matchRanges) {
+            ranges.add(styleRange);
+            if (styleRange.length < 0) {
+                continue;
+            }
+            final StyleRange newStyleRange = new StyleRange(styleRange.start, styleRange.length, null, bg,
+                styleRange.fontStyle);
+            if (styleRange.start - index > 0)
+                getText().replaceStyleRanges(index, styleRange.start - index,
+                    new StyleRange[] { newStyleRange });
+            getText().replaceStyleRanges(styleRange.start, styleRange.length,
+                new StyleRange[] { newStyleRange });
+            index = styleRange.start + styleRange.length;
+        }
+        if (lastIndex - index > 0) {
+            final StyleRange newStyleRange = new StyleRange(index, lastIndex - index, null, bg);
+            getText().replaceStyleRanges(index, lastIndex - index, new StyleRange[] { newStyleRange });
+        }
+        return ranges;
+    }
+
+	private RangeGroup[] previous;
+	private RangeGroup selectedRange;
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/TextSelector.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/TextSelector.java
new file mode 100644
index 0000000..fae6ef8
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/text/styled/TextSelector.java
@@ -0,0 +1,77 @@
+/*******************************************************************************

+ * Copyright (c) 2017 Exyte

+ * 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:

+ *     Yuri Strot - initial API and Implementation

+ ******************************************************************************/

+package org.eclipse.ui.glance.controls.text.styled;

+

+import org.eclipse.jface.text.Region;

+import org.eclipse.swt.events.FocusEvent;

+import org.eclipse.swt.events.FocusListener;

+import org.eclipse.swt.widgets.Control;

+

+import org.eclipse.ui.glance.sources.Match;

+

+/**

+ * For all rich text components we remove native selection (make it empty) to

+ * draw our own colorful current match. However if focus was moved back to text

+ * component, selection should be drawn as usually.

+ * 

+ * TextSelector manages this behavior.

+ * 

+ * @author Yuri Strot

+ */

+public abstract class TextSelector implements FocusListener {

+

+	public void setMatch(Match match) {

+		this.match = match;

+		if (match != null) {

+			reveal(match.getOffset(), match.getLength());

+		}

+	}

+

+	public void dispose() {

+		getControl().removeFocusListener(this);

+		showSelection();

+	}

+

+	public void focusLost(FocusEvent e) {

+		hideSelection();

+	}

+

+	public void focusGained(FocusEvent e) {

+		showSelection();

+	}

+

+	protected void init() {

+		getControl().addFocusListener(this);

+		hideSelection();

+	}

+

+	private void hideSelection() {

+		Region region = getSelection();

+		setSelection(region.getOffset() + region.getLength(), 0);

+	}

+

+	private void showSelection() {

+		if (match != null) {

+			setSelection(match.getOffset(), match.getLength());

+		}

+	}

+

+	protected abstract Control getControl();

+

+	protected abstract Region getSelection();

+

+	protected abstract void setSelection(int offset, int length);

+

+	protected abstract void reveal(int offset, int length);

+

+	private Match match;

+

+}

diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/BusyIndicatorUtils.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/BusyIndicatorUtils.java
new file mode 100644
index 0000000..e30ec07
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/BusyIndicatorUtils.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.tree;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class BusyIndicatorUtils {
+
+	static final String BUSYID_NAME = "SWT BusyIndicator"; //$NON-NLS-1$
+	static final Integer NO_BUSY = new Integer(0);
+
+	public static void withoutIndicator(Display display, Runnable runnable) {
+		if (runnable == null)
+			SWT.error(SWT.ERROR_NULL_ARGUMENT);
+		if (display == null) {
+			display = Display.getCurrent();
+			if (display == null) {
+				runnable.run();
+				return;
+			}
+		}
+
+		Shell[] shells = display.getShells();
+		for (int i = 0; i < shells.length; i++) {
+			Integer id = (Integer) shells[i].getData(BUSYID_NAME);
+			if (id == null) {
+				shells[i].setData(BUSYID_NAME, NO_BUSY);
+			}
+		}
+
+		try {
+			runnable.run();
+		} finally {
+			shells = display.getShells();
+			for (int i = 0; i < shells.length; i++) {
+				Integer id = (Integer) shells[i].getData(BUSYID_NAME);
+				if (id == NO_BUSY) {
+					shells[i].setData(BUSYID_NAME, null);
+				}
+			}
+		}
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeCell.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeCell.java
new file mode 100644
index 0000000..334d9ee
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeCell.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.tree;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.TreeItem;
+
+import org.eclipse.ui.glance.controls.decor.StructCell;
+
+public class TreeCell extends StructCell {
+
+	private TreeItem item;
+
+	public TreeCell(TreeItem item, int column) {
+		super(column);
+		this.item = item;
+	}
+
+	@Override
+	public boolean isSelected() {
+		TreeItem[] items = item.getParent().getSelection();
+		for (TreeItem treeItem : items) {
+			if (treeItem == item)
+				return true;
+		}
+		return false;
+	}
+
+	public TreeItem getTreeItem() {
+		return item;
+	}
+
+	@Override
+	public Color getBackground() {
+		return item.getBackground(getColumn());
+	}
+
+	@Override
+	public Rectangle getBounds() {
+		return item.getBounds(getColumn());
+	}
+
+	@Override
+	public Font getFont() {
+		return item.getFont(getColumn());
+	}
+
+	@Override
+	public Color getForeground() {
+		return item.getForeground(getColumn());
+	}
+
+	@Override
+	public Image getImage() {
+		return item.getImage(getColumn());
+	}
+
+	@Override
+	public Rectangle getImageBounds() {
+		return item.getImageBounds(getColumn());
+	}
+
+	@Override
+	protected Item getItem() {
+		return item;
+	}
+
+	@Override
+	public String getText() {
+		return item.getText(getColumn());
+	}
+
+	@Override
+	public Rectangle getTextBounds() {
+		return item.getTextBounds(getColumn());
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeContent.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeContent.java
new file mode 100644
index 0000000..fd8dc92
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeContent.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.tree;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ListenerList;
+
+import org.eclipse.ui.glance.controls.decor.IPath;
+import org.eclipse.ui.glance.controls.decor.IStructContent;
+import org.eclipse.ui.glance.controls.decor.StructCell;
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.ITextSourceListener;
+
+public abstract class TreeContent extends TreeNode implements IStructContent {
+
+	private ListenerList listeners = new ListenerList();
+
+	public TreeContent() {
+		super(null);
+		index = new int[0];
+	}
+
+	public abstract TreeItemContent getContent(TreeCell cell);
+
+	public abstract void index(IProgressMonitor monitor);
+
+	public void dispose() {
+	}
+
+	public final ITextBlock getContent(StructCell cell) {
+		return getContent((TreeCell) cell);
+	}
+
+	public IPath getPath(ITextBlock block) {
+		TreeItemContent content = (TreeItemContent) block;
+		return new TreePath(content.getNode());
+	}
+
+	public void addListener(ITextSourceListener listener) {
+		listeners.add(listener);
+	}
+
+	public void removeListener(ITextSourceListener listener) {
+		listeners.remove(listener);
+	}
+
+	public ITextSourceListener[] getListeners() {
+		Object[] objects = listeners.getListeners();
+		ITextSourceListener[] listeners = new ITextSourceListener[objects.length];
+		System.arraycopy(objects, 0, listeners, 0, objects.length);
+		return listeners;
+	}
+
+	public ITextBlock[] getBlocks() {
+		return blocks.toArray(new TreeItemContent[0]);
+	}
+
+	void changed(TreeItemContent[] removed, TreeItemContent[] added) {
+		for (TreeItemContent item : removed) {
+			blocks.remove(item);
+		}
+		for (TreeItemContent item : added) {
+			blocks.add(item);
+		}
+		for (ITextSourceListener listener : getListeners()) {
+			listener.blocksChanged(removed, added);
+		}
+	}
+
+	private Set<TreeItemContent> blocks = new HashSet<TreeItemContent>();
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeControlContent.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeControlContent.java
new file mode 100644
index 0000000..3350a06
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeControlContent.java
@@ -0,0 +1,183 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.tree;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+
+import org.eclipse.ui.glance.sources.ConfigurationManager;
+
+public class TreeControlContent extends TreeContent {
+
+	private Tree tree;
+	private final Map<TreeCell, TreeItemContent> cellToContent = new HashMap<TreeCell, TreeItemContent>();
+
+	public TreeControlContent(Tree tree) {
+		this.tree = tree;
+		collectCells(this, tree.getItems());
+	}
+
+	@Override
+	public void dispose() {
+		tree = null;
+	}
+
+	@Override
+	public TreeItemContent getContent(TreeCell cell) {
+		TreeItemContent content = cellToContent.get(cell);
+		if (content == null) {
+			TreeItem parent = null;
+			do {
+				parent = cell.getTreeItem().getParentItem();
+				if (parent == null) {
+					break;
+				}
+				TreeCell parentCell = new TreeCell(parent, 0);
+				cell = parentCell;
+				content = cellToContent.get(parentCell);
+			} while (content == null);
+			if (content != null && parent != null) {
+				collectCells(content.getNode(), parent.getItems());
+				content = cellToContent.get(cell);
+			}
+		}
+		return content;
+	}
+
+	@Override
+	public void index(final IProgressMonitor monitor) {
+		if (tree == null || tree.isDisposed()) {
+			monitor.done();
+			return;
+		}
+		tree.getDisplay().asyncExec(new Runnable() {
+			public void run() {
+				try {
+					if (tree == null || tree.isDisposed()) {
+						return;
+					}
+					final LinkedList<TreeItem> items = new LinkedList<TreeItem>();
+					for (TreeItem item : tree.getItems()) {
+						items.add(item);
+					}
+					if (items.size() > 0) {
+						expand(items, monitor);
+					}
+				} finally {
+					monitor.done();
+				}
+			}
+		});
+	}
+
+	private void expand(final LinkedList<TreeItem> items,
+			final IProgressMonitor monitor) {
+		if (tree == null || tree.isDisposed())
+			return;
+		final Display display = tree.getDisplay();
+		BusyIndicatorUtils.withoutIndicator(display, new Runnable() {
+			public void run() {
+				int maxIndexingDepth = ConfigurationManager.getInstance()
+						.getMaxIndexingDepth();
+				int level = 1;
+				TreeItem lastInLevel = items.getLast();
+				monitor.beginTask("1/" + maxIndexingDepth, items.size());
+				while (true) {
+					if (tree == null
+							|| tree.isDisposed()
+							|| (maxIndexingDepth >= 0 && level >= maxIndexingDepth))
+						return;
+					if (monitor.isCanceled())
+						return;
+					TreeItem item = items.poll();
+					if (item == null)
+						return;
+					try {
+						if (item.isDisposed())
+							continue;
+						if (!item.getExpanded()) {
+							Event event = new Event();
+							event.item = item;
+							event.type = SWT.Expand;
+							event.widget = item.getParent();
+							event.display = display;
+							event.widget.notifyListeners(SWT.Expand, event);
+						}
+						TreeItem[] kids = item.getItems();
+						TreeItemContent content = getContent(item);
+						if (content != null) {
+							collectCells(content.getNode(), item.getItems());
+						}
+						for (TreeItem child : kids) {
+							items.addLast(child);
+						}
+						while (display.readAndDispatch())
+							;
+					} finally {
+						monitor.worked(1);
+						if (item == lastInLevel && items.size() > 0) {
+							lastInLevel = items.getLast();
+							level++;
+							int total = items.size();
+							monitor.beginTask(level + "/?", total);
+						}
+					}
+				}
+			}
+		});
+	}
+
+	private void collectCells(TreeNode node, TreeItem[] items) {
+		if (items.length > 0) {
+			int columns = items[0].getParent().getColumnCount();
+			if (columns == 0) {
+				columns = 1;
+			}
+			List<TreeNode> nodes = new ArrayList<TreeNode>(items.length);
+			for (int i = 0; i < items.length; i++) {
+				TreeItem item = items[i];
+				TreeItemContent c = getContent(item);
+				if (c != null) {
+					continue;
+				}
+				TreeNode child = new TreeNode(item);
+				for (int j = 0; j < columns; j++) {
+					TreeCell cell = new TreeCell(item, j);
+					TreeItemContent itemContent = new TreeItemContent(child,
+							item.getText(j), j);
+					cellToContent.put(cell, itemContent);
+				}
+				if (item.getExpanded()) {
+					collectCells(child, item.getItems());
+				}
+				nodes.add(child);
+			}
+			if (nodes.size() > 0) {
+				node.add(nodes.toArray(new TreeNode[nodes.size()]));
+			}
+		}
+	}
+
+	private TreeItemContent getContent(TreeItem item) {
+		return cellToContent.get(new TreeCell(item, 0));
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeControlSource.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeControlSource.java
new file mode 100644
index 0000000..0fc5ac0
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeControlSource.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.tree;
+
+import org.eclipse.swt.widgets.Tree;
+
+
+public class TreeControlSource extends TreeStructSource {
+
+	public TreeControlSource(Tree tree) {
+		super(tree);
+	}
+
+	@Override
+	protected TreeContent createContent() {
+		return new TreeControlContent(getControl());
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeItemContent.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeItemContent.java
new file mode 100644
index 0000000..4a3ebe0
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeItemContent.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.tree;
+
+import org.eclipse.core.runtime.ListenerList;
+
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.ITextBlockListener;
+import org.eclipse.ui.glance.sources.TextChangedEvent;
+
+public class TreeItemContent implements ITextBlock {
+
+	private TreeNode node;
+	private String text;
+	private ListenerList listeners = new ListenerList();
+	private int column;
+
+	public TreeItemContent(TreeNode node, String text, int column) {
+		this.node = node;
+		node.items.add(this);
+		this.text = text;
+		this.column = column;
+	}
+
+	public TreeNode getNode() {
+		return node;
+	}
+
+	public String getText() {
+		return text;
+	}
+
+	public void setText(String text) {
+		if (text.equals(this.text))
+			return;
+		int length = text.length();
+		TextChangedEvent event = new TextChangedEvent(0, length, this.text);
+		this.text = text;
+		for (Object object : listeners.getListeners()) {
+			ITextBlockListener listener = (ITextBlockListener) object;
+			listener.textChanged(event);
+		}
+	}
+
+	public void addTextBlockListener(ITextBlockListener listener) {
+		listeners.add(listener);
+	}
+
+	public void removeTextBlockListener(ITextBlockListener listener) {
+		listeners.remove(listener);
+	}
+
+	public int compareTo(ITextBlock that) {
+		TreeItemContent item = (TreeItemContent) that;
+		int diff = this.node.compareTo(item.node);
+		if (diff != 0) {
+			return diff;
+		}
+		return column - item.column;
+	}
+
+	@Override
+	public String toString() {
+		StringBuffer buffer = new StringBuffer();
+		buffer.append("(");
+		buffer.append(text);
+		buffer.append(", ");
+		buffer.append(column);
+		buffer.append(")");
+		return buffer.toString();
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeNode.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeNode.java
new file mode 100644
index 0000000..d76c4b9
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeNode.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.tree;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.widgets.TreeItem;
+
+public class TreeNode implements DisposeListener {
+
+	public static final TreeNode[] EMPTY = new TreeNode[0];
+
+	List<TreeNode> kids = new ArrayList<TreeNode>();
+	List<TreeItemContent> items = new ArrayList<TreeItemContent>();
+	TreeNode parent;
+	int[] index;
+
+	TreeItem item;
+
+	public TreeNode(TreeItem item) {
+		this.item = item;
+		if (item != null) {
+			item.addDisposeListener(this);
+		}
+	}
+
+	public void widgetDisposed(DisposeEvent e) {
+		close();
+	}
+
+	public void add(TreeNode[] nodes) {
+		for (TreeNode node : nodes) {
+			doAdd(node);
+		}
+		notifyRoot(TreeNode.EMPTY, nodes);
+	}
+
+	public void remove(TreeNode[] nodes) {
+		doRemove(nodes);
+		notifyRoot(nodes, TreeNode.EMPTY);
+	}
+
+	public TreeNode[] getChildren() {
+		return kids.toArray(new TreeNode[0]);
+	}
+
+	void notifyRoot(TreeNode[] removed, TreeNode[] added) {
+		TreeContent root = getRoot();
+		if (root != null) {
+			root.changed(getContent(removed), getContent(added));
+		}
+	}
+
+	protected void close() {
+		if (item != null) {
+			if (!item.isDisposed()) {
+				item.removeDisposeListener(this);
+			}
+			for (TreeNode node : kids) {
+				node.close();
+			}
+			notifyRoot(new TreeNode[] { this }, TreeNode.EMPTY);
+			item = null;
+		}
+	}
+
+	TreeItemContent[] getContent(TreeNode[] nodes) {
+		List<TreeItemContent> content = new ArrayList<TreeItemContent>();
+		nodes = collect(nodes);
+		for (TreeNode node : nodes) {
+			content.addAll(node.items);
+		}
+		return content.toArray(new TreeItemContent[0]);
+	}
+
+	TreeNode[] collect(TreeNode[] items) {
+		List<TreeNode> result = new ArrayList<TreeNode>();
+		for (TreeNode item : items) {
+			collect(item, result);
+		}
+		return result.toArray(new TreeNode[0]);
+	}
+
+	void collect(TreeNode item, List<TreeNode> items) {
+		items.add(item);
+		for (TreeNode kid : item.kids) {
+			collect(kid, items);
+		}
+	}
+
+	TreeContent getRoot() {
+		TreeNode node = this;
+		while (node != null) {
+			if (node instanceof TreeContent)
+				return (TreeContent) node;
+			node = node.parent;
+		}
+		return null;
+	}
+
+	void doAdd(TreeNode node) {
+		node.recalc(index, kids.size());
+		kids.add(node);
+		node.parent = this;
+	}
+
+	void doRemove(TreeNode[] nodes) {
+		int minPos = Integer.MAX_VALUE;
+		for (TreeNode node : nodes) {
+			int pos = kids.indexOf(node);
+			if (pos >= 0) {
+				kids.remove(pos);
+				minPos = Math.min(minPos, pos);
+			}
+		}
+		for (int i = minPos; i < kids.size(); i++) {
+			kids.get(i).recalc(index, i);
+		}
+	}
+
+	void recalc(int[] parent, int cur) {
+		if (parent != null) {
+			index = new int[parent.length + 1];
+			System.arraycopy(parent, 0, index, 0, parent.length);
+			index[parent.length] = cur;
+			for (int i = 0; i < kids.size(); i++) {
+				kids.get(i).recalc(index, i);
+			}
+		}
+	}
+
+	public int compareTo(TreeNode that) {
+		int[] index = that.index;
+		for (int i = 0; i < index.length && i < this.index.length; i++) {
+			int diff = this.index[i] - index[i];
+			if (diff != 0) {
+				return diff;
+			}
+		}
+		return this.index.length - index.length;
+	}
+
+	public String toString() {
+		int iMax = items.size() - 1;
+		if (iMax == -1)
+			return "[]";
+		StringBuilder b = new StringBuilder();
+		b.append('[');
+		for (int i = 0;; i++) {
+			b.append(items.get(i));
+			if (i == iMax)
+				return b.append(']').toString();
+			b.append(", ");
+		}
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreePath.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreePath.java
new file mode 100644
index 0000000..32d9887
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreePath.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.tree;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.ui.progress.PendingUpdateAdapter;
+
+import org.eclipse.ui.glance.controls.decor.IPath;
+
+public class TreePath implements IPath {
+
+	protected List<TreeNode> list = new ArrayList<TreeNode>();
+	protected TreeContent content;
+
+	private boolean cancel;
+
+	public TreePath(TreeNode node) {
+		content = node.getRoot();
+		TreeNode cur = node;
+		while (cur != null && cur != content) {
+			list.add(0, cur);
+			cur = cur.parent;
+		}
+	}
+
+	public void select(Composite composite) {
+		Tree tree = (Tree) composite;
+		select(tree.getItems(), 0);
+	}
+
+	public void discardSelection() {
+		this.cancel = true;
+	}
+
+	private void select(final TreeItem[] items, final int index) {
+		if (cancel) {
+			return;
+		}
+		TreeNode node = list.get(index);
+		for (final TreeItem item : items) {
+			if (getNode(item) == node) {
+				final Tree tree = item.getParent();
+				if (index == list.size() - 1) {
+					// After tree item removes tree selection restores
+					// we should set selection after this
+					tree.getDisplay().asyncExec(new Runnable() {
+						public void run() {
+							tree.setSelection(item);
+							tree.showSelection();
+						}
+					});
+				} else {
+					if (!item.getExpanded()) {
+						expand(item);
+					}
+					select(item.getItems(), index + 1);
+				}
+				return;
+			}
+		}
+		if (items.length > 0) {
+			TreeItem item = items[0];
+			final TreeItem parent = item.getParentItem();
+			if (item.getData() instanceof PendingUpdateAdapter) {
+				item.addDisposeListener(new DisposeListener() {
+					public void widgetDisposed(DisposeEvent e) {
+						select(parent.getItems(), index);
+					}
+				});
+			}
+		}
+	}
+
+	private TreeNode getNode(TreeItem item) {
+		TreeCell cell = new TreeCell(item, 0);
+		TreeItemContent itemContent = content.getContent(cell);
+		return itemContent != null ? itemContent.getNode() : null;
+	}
+
+	private void expand(TreeItem item) {
+		Event event = new Event();
+		event.item = item;
+		event.type = SWT.Expand;
+		event.widget = item.getParent();
+		event.display = item.getDisplay();
+		event.widget.notifyListeners(SWT.Expand, event);
+		item.setExpanded(true);
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeStructSource.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeStructSource.java
new file mode 100644
index 0000000..24e160f
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/TreeStructSource.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.tree;
+
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+
+import org.eclipse.ui.glance.controls.decor.StructCell;
+import org.eclipse.ui.glance.controls.decor.StructSource;
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.SourceSelection;
+
+public abstract class TreeStructSource extends StructSource {
+
+	public TreeStructSource(Tree tree) {
+		super(tree);
+		tree.addSelectionListener(this);
+	}
+
+	@Override
+	public Tree getControl() {
+		return (Tree) super.getControl();
+	}
+
+	@Override
+	protected StructCell createCell(Item item, int column) {
+		return new TreeCell((TreeItem) item, column);
+	}
+
+	@Override
+	public void dispose() {
+		Tree tree = getControl();
+		try {
+			super.dispose();
+		} finally {
+			if (tree != null && !tree.isDisposed()) {
+				tree.removeSelectionListener(this);
+			}
+		}
+	}
+
+	@Override
+	protected abstract TreeContent createContent();
+
+	@Override
+	protected SourceSelection getSourceSelection() {
+	    TreeItem[] items = getControl().getSelection();
+        if (items.length > 0) {
+            ITextBlock block = content.getContent(createCell(items[0], 0));
+            if (block != null) {
+                return new SourceSelection(block, 0, block.getText().length());
+            }
+        }
+        return null;
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/UnknownTreeVisitor.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/UnknownTreeVisitor.java
new file mode 100644
index 0000000..b4b9529
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/controls/tree/UnknownTreeVisitor.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.controls.tree;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+
+public abstract class UnknownTreeVisitor {
+
+	public void visit(IProgressMonitor monitor) {
+		monitor.beginTask("Tree indexing", 100);
+		try {
+			Object[] roots = getRoots();
+			monitor.worked(1);
+			breadthVisit(roots, monitor, 1);
+		} finally {
+			monitor.done();
+		}
+	}
+
+	protected abstract Object[] getRoots();
+
+	protected abstract Object[] getChildren(Object element);
+
+	private void breadthVisit(Object[] elements, IProgressMonitor monitor,
+			int level) {
+		List<Object> next = new ArrayList<Object>();
+		for (Object element : elements) {
+			if (monitor.isCanceled())
+				return;
+			next.addAll(Arrays.asList(getChildren(element)));
+		}
+		if (monitor.isCanceled())
+			return;
+		int work = (int) Math.pow(2, level);
+		monitor.worked(work);
+		int totalWork = 2 * work - 1;
+		int size = next.size();
+		if (size == 0)
+			return;
+		int remains = 100 - totalWork;
+		if (remains > size) {
+			breadthVisit(next.toArray(), monitor, level + 1);
+		} else {
+			SubProgressMonitor subMonitor = new SubProgressMonitor(monitor,
+					remains);
+			try {
+				subMonitor.beginTask("", size);
+				for (Object element : next) {
+					depthVisit(element, monitor, level + 1);
+					subMonitor.worked(1);
+				}
+			} finally {
+				subMonitor.done();
+			}
+		}
+	}
+
+	private void depthVisit(Object element, IProgressMonitor monitor, int level) {
+		if (level > 4) {
+			return;
+		}
+		for (Object child : getChildren(element)) {
+			if (monitor.isCanceled())
+				return;
+			depthVisit(child, monitor, level + 1);
+		}
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/GlanceEventDispatcher.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/GlanceEventDispatcher.java
new file mode 100644
index 0000000..072ab6b
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/GlanceEventDispatcher.java
@@ -0,0 +1,87 @@
+/*******************************************************************************

+ * Copyright (c) 2017 Exyte

+ * 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:

+ *     Yuri Strot - initial API and implementation

+ *******************************************************************************/

+package org.eclipse.ui.glance.internal;

+

+import java.util.List;

+import java.util.Map;

+

+import org.eclipse.jface.bindings.Binding;

+import org.eclipse.jface.bindings.BindingManager;

+import org.eclipse.jface.bindings.keys.KeySequence;

+import org.eclipse.jface.bindings.keys.KeyStroke;

+import org.eclipse.swt.widgets.Event;

+import org.eclipse.ui.PlatformUI;

+import org.eclipse.ui.internal.keys.BindingService;

+import org.eclipse.ui.internal.keys.WorkbenchKeyboard;

+import org.eclipse.ui.keys.IBindingService;

+

+import org.eclipse.ui.glance.internal.search.SearchManager;

+

+@SuppressWarnings("restriction")

+public class GlanceEventDispatcher {

+

+	public static final String GLANCE_CTX = "org.eclipse.ui.glance.context";

+

+	public static final String NEXT_COMMAND = "org.eclipse.ui.glance.nextResult";

+	public static final String PREV_COMMAND = "org.eclipse.ui.glance.prevResult";

+	public static final String FOCUS_COMMAND = "org.eclipse.ui.glance.commands.focus";

+	public static final String CLOSE_COMMAND = "org.eclipse.ui.glance.commands.close";

+	public static final String CLEAR_COMMAND = "org.eclipse.ui.glance.commands.clearHistory";

+

+	public static GlanceEventDispatcher INSTANCE = new GlanceEventDispatcher();

+

+	private final BindingManager bindingManager;

+

+	private GlanceEventDispatcher() {

+		bindingManager = ((BindingService) PlatformUI.getWorkbench()

+				.getService(IBindingService.class)).getBindingManager();

+	}

+

+	public void dispatchKeyPressed(Event event) {

+		@SuppressWarnings("unchecked")

+		List<Object> potentialKeyStrokes = WorkbenchKeyboard

+				.generatePossibleKeyStrokes(event);

+		if (potentialKeyStrokes.isEmpty()) {

+			return;

+		}

+

+		String commandID = getBindCommand(KeySequence

+				.getInstance((KeyStroke) potentialKeyStrokes.get(0)));

+		if (commandID == null) {

+			return;

+		} else if (FOCUS_COMMAND.equals(commandID)) {

+			SearchManager.getIntance().sourceFocus();

+		} else if (NEXT_COMMAND.equals(commandID)) {

+			SearchManager.getIntance().findNext();

+		} else if (PREV_COMMAND.equals(commandID)) {

+			SearchManager.getIntance().findPrevious();

+		} else if (CLOSE_COMMAND.equals(commandID)) {

+			SearchManager.getIntance().close();

+		} else if (CLEAR_COMMAND.equals(commandID)) {

+			SearchManager.getIntance().clearHistory();

+		}

+	}

+

+	public String getBindCommand(KeySequence keySequence) {

+		Map<?, ?> map = bindingManager.getActiveBindingsDisregardingContext();

+		List<?> bindings = (List<?>) map.get(keySequence);

+		if (bindings != null) {

+			for (Object obj : bindings) {

+				Binding binding = (Binding) obj;

+				if (GLANCE_CTX.equals(binding.getContextId())) {

+					return binding.getParameterizedCommand().getId();

+				}

+			}

+		}

+		return null;

+	}

+

+}

diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/GlancePlugin.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/GlancePlugin.java
new file mode 100644
index 0000000..9fc4d0a
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/GlancePlugin.java
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.internal;
+
+import java.net.URL;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class GlancePlugin extends AbstractUIPlugin {
+
+	// The plug-in ID
+	public static final String PLUGIN_ID = "org.eclipse.ui.glance";
+
+	// IMAGES
+
+	private static final String IMG_PREFIX = "icons/";
+
+	public static final String IMG_WAIT = IMG_PREFIX + "wait.gif";
+	public static final String IMG_NEXT = IMG_PREFIX + "next.gif";
+	public static final String IMG_PREV = IMG_PREFIX + "prev.gif";
+	public static final String IMG_SEARCH = IMG_PREFIX + "search.png";
+	public static final String IMG_START_INDEXING = IMG_PREFIX + "update_1.gif";
+	public static final String IMG_END_INDEXING = IMG_PREFIX + "update_2.gif";
+	public static final String IMG_INDEXING_FINISHED = IMG_PREFIX
+			+ "update_done.gif";
+	public static final String[] IMG_INDEXING_LOOP = new String[] {
+			IMG_START_INDEXING, IMG_END_INDEXING };
+
+	// The shared instance
+	private static GlancePlugin plugin;
+
+	/**
+	 * The constructor
+	 */
+	public GlancePlugin() {
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext
+	 * )
+	 */
+	@Override
+	public void start(BundleContext context) throws Exception {
+		super.start(context);
+		plugin = this;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext
+	 * )
+	 */
+	@Override
+	public void stop(BundleContext context) throws Exception {
+		plugin = null;
+		super.stop(context);
+	}
+
+	/**
+	 * Returns the shared instance
+	 * 
+	 * @return the shared instance
+	 */
+	public static GlancePlugin getDefault() {
+		return plugin;
+	}
+
+	public static Image getImage(String path) {
+		Image image = getDefault().getImageRegistry().get(path);
+		if (image == null) {
+			ImageDescriptor imageDescriptor = getImageDescriptor(path, false);
+			image = imageDescriptor.createImage();
+			getDefault().getImageRegistry().put(path, image);
+		}
+		return image;
+	}
+
+	public static ImageDescriptor getImageDescriptor(String path) {
+		return getImageDescriptor(path, true);
+	}
+
+	private static ImageDescriptor getImageDescriptor(String path,
+			boolean addToRegistry) {
+		ImageDescriptor imageDescriptor = getDefault().getImageRegistry()
+				.getDescriptor(path);
+		if (imageDescriptor == null) {
+			imageDescriptor = ImageDescriptor.createFromURL(makeImageURL(path));
+			if (addToRegistry) {
+				getDefault().getImageRegistry().put(path, imageDescriptor);
+			}
+		}
+		return imageDescriptor;
+	}
+
+	public static void log(String message) {
+		log(message, null);
+	}
+
+	public static void log(Throwable t) {
+		log(t.getMessage(), t);
+	}
+
+	public static void log(String message, Throwable t) {
+		log(createStatus(message, t));
+	}
+
+	public static void log(IStatus status) {
+		getDefault().getLog().log(status);
+	}
+
+	public static IStatus createStatus(String message) {
+		return createStatus(message, null);
+	}
+
+	public static IStatus createStatus(String message, Throwable t) {
+		return new Status(Status.ERROR, PLUGIN_ID, message, t);
+	}
+
+	private static URL makeImageURL(String path) {
+		return FileLocator.find(getDefault().getBundle(), new Path(path), null);
+	}
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/GlanceStartup.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/GlanceStartup.java
new file mode 100644
index 0000000..2630015
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/GlanceStartup.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.internal;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.ui.IStartup;
+import org.eclipse.ui.PlatformUI;
+
+import org.eclipse.ui.glance.internal.preferences.IPreferenceConstants;
+import org.eclipse.ui.glance.internal.search.SearchManager;
+
+public class GlanceStartup implements IStartup, IPreferenceConstants {
+
+	public void earlyStartup() {
+		IPreferenceStore store = GlancePlugin.getDefault().getPreferenceStore();
+		if (store.getBoolean(PANEL_STARTUP)) {
+			PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
+				public void run() {
+					SearchManager.getIntance().startup();
+				}
+			});
+		}
+	}
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/OpenSearchPanelHandler.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/OpenSearchPanelHandler.java
new file mode 100644
index 0000000..3bdb10e
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/OpenSearchPanelHandler.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.internal;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+import org.eclipse.ui.glance.internal.search.SearchManager;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class OpenSearchPanelHandler extends AbstractHandler {
+
+	/**
+	 * The constructor.
+	 */
+	public OpenSearchPanelHandler() {
+	}
+
+	/**
+	 * the command has been executed, so extract extract the needed information
+	 * from the application context.
+	 */
+	public Object execute(ExecutionEvent event) throws ExecutionException {
+		SearchManager.getIntance().activate();
+		return null;
+	}
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/CheckAction.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/CheckAction.java
new file mode 100644
index 0000000..4550278
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/CheckAction.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.internal.panels;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+import org.eclipse.ui.glance.internal.GlancePlugin;
+
+/**
+ * @author Yuri Strot
+ */
+public class CheckAction extends Action {
+
+	public CheckAction(String name, String label) {
+		super(label, AS_CHECK_BOX);
+		this.name = name;
+		setChecked(getStore().getBoolean(name));
+	}
+
+	public IPreferenceStore getStore() {
+		return GlancePlugin.getDefault().getPreferenceStore();
+	}
+
+	@Override
+	public void run() {
+		getStore().setValue(name, isChecked());
+	}
+
+	private String name;
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/ImageAnimation.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/ImageAnimation.java
new file mode 100644
index 0000000..b598f76
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/ImageAnimation.java
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.internal.panels;
+
+import java.net.URL;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+
+import org.eclipse.ui.glance.internal.GlancePlugin;
+import org.eclipse.ui.glance.utils.UIUtils;
+
+public abstract class ImageAnimation extends Thread {
+
+	public ImageAnimation(URL url, Color bg) throws CoreException {
+		setDaemon(true);
+		this.bg = bg;
+		try {
+			imageDataArray = loader.load(url.openStream());
+		} catch (Exception e) {
+			throw new CoreException(GlancePlugin.createStatus(
+					"Can't read image '" + url.toString() + "'", e));
+		}
+		if (imageDataArray == null || imageDataArray.length <= 1) {
+			throw new CoreException(GlancePlugin.createStatus("Image '"
+					+ url.toString() + "' is not an animated image"));
+		}
+	}
+
+	@Override
+	public void run() {
+		Display display = PlatformUI.getWorkbench().getDisplay();
+		/*
+		 * Create an off-screen image to draw on, and fill it with the shell
+		 * background.
+		 */
+		final Image offScreenImage = new Image(display,
+				loader.logicalScreenWidth, loader.logicalScreenHeight);
+		GC offScreenImageGC = new GC(offScreenImage);
+		offScreenImageGC.setBackground(bg);
+		offScreenImageGC.fillRectangle(0, 0, loader.logicalScreenWidth,
+				loader.logicalScreenHeight);
+
+		Image image = null;
+
+		try {
+			/* Create the first image and draw it on the off-screen image. */
+			int imageDataIndex = 0;
+			ImageData imageData = imageDataArray[imageDataIndex];
+			image = new Image(display, imageData);
+			offScreenImageGC.drawImage(image, 0, 0, imageData.width,
+					imageData.height, imageData.x, imageData.y,
+					imageData.width, imageData.height);
+
+			/*
+			 * Now loop through the images, creating and drawing each one on the
+			 * off-screen image before drawing it on the shell.
+			 */
+			int repeatCount = loader.repeatCount;
+			while ((loader.repeatCount == 0 || repeatCount > 0)
+					&& !isTerminated()) {
+				switch (imageData.disposalMethod) {
+				case SWT.DM_FILL_BACKGROUND:
+					/* Fill with the background color before drawing. */
+					offScreenImageGC.setBackground(bg);
+					offScreenImageGC.fillRectangle(imageData.x, imageData.y,
+							imageData.width, imageData.height);
+					break;
+				case SWT.DM_FILL_PREVIOUS:
+					/* Restore the previous image before drawing. */
+					offScreenImageGC.drawImage(image, 0, 0, imageData.width,
+							imageData.height, imageData.x, imageData.y,
+							imageData.width, imageData.height);
+					break;
+				}
+
+				imageDataIndex = (imageDataIndex + 1) % imageDataArray.length;
+				imageData = imageDataArray[imageDataIndex];
+				image.dispose();
+				image = new Image(display, imageData);
+				offScreenImageGC.drawImage(image, 0, 0, imageData.width,
+						imageData.height, imageData.x, imageData.y,
+						imageData.width, imageData.height);
+				final Image newImage = image;
+
+				UIUtils.syncExec(display, new Runnable() {
+					public void run() {
+						updateImage(newImage);
+					}
+				});
+
+				/*
+				 * Sleep for the specified delay time (adding commonly-used
+				 * slow-down fudge factors).
+				 */
+				try {
+					int ms = imageData.delayTime * 10;
+					if (ms < 20)
+						ms += 30;
+					if (ms < 30)
+						ms += 10;
+					Thread.sleep(ms);
+				} catch (InterruptedException e) {
+				}
+
+				/*
+				 * If we have just drawn the last image, decrement the repeat
+				 * count and start again.
+				 */
+				if (imageDataIndex == imageDataArray.length - 1)
+					repeatCount--;
+			}
+		} catch (SWTException ex) {
+			GlancePlugin.log("There was an error animating the GIF", ex);
+		} finally {
+			if (offScreenImage != null && !offScreenImage.isDisposed())
+				offScreenImage.dispose();
+			if (offScreenImageGC != null && !offScreenImageGC.isDisposed())
+				offScreenImageGC.dispose();
+			if (image != null && !image.isDisposed())
+				image.dispose();
+		}
+	}
+
+	protected abstract boolean isTerminated();
+
+	protected abstract void updateImage(Image image);
+
+	private final ImageLoader loader = new ImageLoader();
+	private ImageData[] imageDataArray;
+	private final Color bg;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/MoveTracker.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/MoveTracker.java
new file mode 100644
index 0000000..a009981
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/MoveTracker.java
@@ -0,0 +1,70 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.internal.panels;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class MoveTracker implements Listener {
+
+	public MoveTracker(Control control) {
+		this.control = control;
+		cursor = new Cursor(control.getDisplay(), SWT.CURSOR_SIZEALL);
+		control.setCursor(cursor);
+		control.addListener(SWT.MouseDown, this);
+		control.addListener(SWT.Dispose, this);
+	}
+
+	public void handleEvent(Event event) {
+		if (control != null && !control.isDisposed()) {
+			switch (event.type) {
+			case SWT.MouseDown:
+				Point point = control.getDisplay().getCursorLocation();
+				handleClick(point.x, point.y);
+				break;
+			case SWT.MouseMove:
+				point = control.getDisplay().getCursorLocation();
+				handleDrag(point.x - iLocation.x, point.y - iLocation.y);
+				break;
+			case SWT.Dispose:
+				cursor.dispose();
+				cursor = null;
+			case SWT.MouseUp:
+				control.getDisplay().removeFilter(SWT.MouseMove, this);
+				control.getDisplay().removeFilter(SWT.MouseUp, this);
+			}
+
+		}
+	}
+
+	protected void handleClick(int x, int y) {
+		iLocation = new Point(x, y);
+		control.getDisplay().addFilter(SWT.MouseMove, this);
+		control.getDisplay().addFilter(SWT.MouseUp, this);
+	}
+
+	protected void handleDrag(int dx, int dy) {
+	}
+
+	private Cursor cursor;
+	private Point iLocation;
+	private Control control;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/PopupSearchDialog.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/PopupSearchDialog.java
new file mode 100644
index 0000000..f80ccfb
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/PopupSearchDialog.java
@@ -0,0 +1,214 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.internal.panels;
+
+import java.util.List;
+
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.ui.glance.panels.SearchPanel;
+import org.eclipse.ui.glance.sources.Match;
+import org.eclipse.ui.glance.utils.UIUtils;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class PopupSearchDialog extends SearchPanel {
+
+	public PopupSearchDialog(Control target) {
+		this.target = target;
+		popup = new SearchPopup(target.getShell());
+	}
+
+	public int open() {
+		return popup.open();
+	}
+
+	public boolean isApplicable(Control control) {
+		return true;
+	}
+
+	private int matchCount;
+
+	@Override
+	public void allFound(final Match[] matches) {
+		super.allFound(matches);
+		matchCount = matches.length;
+		updateInfo();
+	}
+
+	protected void updateInfo() {
+		UIUtils.asyncExec(popup.getShell(), new Runnable() {
+
+			public void run() {
+				StringBuffer buffer = new StringBuffer();
+				if (matchCount == 0) {
+					buffer.append("No matches");
+				} else if (matchCount == 1) {
+					buffer.append("1 match");
+				} else {
+					buffer.append(matchCount);
+					buffer.append(" matches");
+				}
+				buffer.append(" found");
+
+				popup.setInfoText(buffer.toString());
+			}
+		});
+	}
+
+	@Override
+	protected void textEmpty() {
+		super.textEmpty();
+		popup.setInfoText(SearchPopup.HELP_TEXT);
+	}
+
+	@Override
+	protected Control createText(Composite parent, int style) {
+		return super.createText(parent, SWT.NONE);
+	}
+
+	@Override
+	protected Label createIcon(Composite parent) {
+		Label label = super.createIcon(parent);
+		new MoveTracker(label) {
+
+			@Override
+			protected void handleClick(int x, int y) {
+				super.handleClick(x, y);
+				location = popup.getShell().getLocation();
+			}
+
+			@Override
+			protected void handleDrag(int dx, int dy) {
+				super.handleDrag(dx, dy);
+				popup.getShell().setLocation(location.x + dx, location.y + dy);
+			}
+
+			private Point location;
+
+		};
+		return label;
+	}
+
+	@Override
+	protected void setBackground(boolean found) {
+		popup.setBackground(found);
+	}
+
+	@Override
+	public void finished() {
+	}
+
+	public void closePanel() {
+		popup.close();
+	}
+
+	@Override
+	protected void showSettings() {
+		super.showSettings();
+		// popup.showDialogMenu();
+	}
+
+	private Point getTargetLocation() {
+		Shell shell = target.getShell();
+		Display display = target.getDisplay();
+		Point location = target.getLocation();
+		location = display.map(target.getParent(), shell, location);
+		return shell.toDisplay(location);
+	}
+
+	private final SearchPopup popup;
+	private final Control target;
+
+	private class SearchPopup extends SearchDialog {
+
+		/**
+		 * @param parent
+		 */
+		public SearchPopup(Shell parent) {
+			super(parent);
+		}
+
+		@Override
+		protected Control createTitleMenuArea(Composite parent) {
+			PopupSearchDialog.this.createContent(parent);
+			Control control = PopupSearchDialog.this.getControl();
+			control.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+			return control;
+		}
+
+		@Override
+		public void showDialogMenu() {
+			super.showDialogMenu();
+		}
+
+		@Override
+		protected void fillDialogMenu(IMenuManager dialogMenu) {
+			PopupSearchDialog.this.fillMenu(dialogMenu);
+		}
+
+		@Override
+		protected void handleClose() {
+			super.handleClose();
+			fireClose();
+		}
+
+		@Override
+		protected Point getInitialSize() {
+			return new Point(getPreferedWidth(), super.getInitialSize().y);
+		}
+
+		@Override
+		protected Point getInitialLocation(Point initialSize) {
+			Point location = getTargetLocation();
+			Point size = target.getSize();
+			int x = location.x + size.x / 2 - initialSize.x / 2;
+			int y = location.y + size.y;
+			Rectangle bounds = target.getMonitor().getBounds();
+			if (y + initialSize.y > bounds.y + bounds.height) {
+				y = location.y - initialSize.y;
+			}
+			return new Point(x, y);
+		}
+
+		@Override
+		protected Control getFocusControl() {
+			return PopupSearchDialog.this.title;
+		}
+
+		@Override
+		protected List<Control> getBackgroundColorExclusions() {
+			List<Control> list = super.getBackgroundColorExclusions();
+			list.add(PopupSearchDialog.this.title);
+			return list;
+		}
+
+		public void setBackground(boolean found) {
+			Color color = found ? getBackground() : BAD_COLOR;
+			applyBackgroundColor(color);
+		}
+
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/SearchDialog.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/SearchDialog.java
new file mode 100644
index 0000000..68cba09
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/SearchDialog.java
@@ -0,0 +1,183 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.internal.panels;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.PopupDialog;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+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.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class SearchDialog extends PopupDialog {
+
+	public SearchDialog(final Shell parent) {
+		super(parent, SWT.RESIZE, true, false, false, true, false, null, null);
+	}
+
+	@Override
+	protected Control createContents(final Composite parent) {
+		final Composite composite = new Composite(parent, SWT.NONE);
+		POPUP_LAYOUT_FACTORY.applyTo(composite);
+		LAYOUTDATA_GRAB_BOTH.applyTo(composite);
+
+		titleArea = (Composite) createTitleMenuArea(composite);
+		separator = createHorizontalSeparator(composite);
+		createInfoTextArea(composite);
+
+		applyColors(composite);
+		applyFonts(composite);
+		return composite;
+	}
+
+	@Override
+	protected Control createInfoTextArea(final Composite parent) {
+		final Composite composite = new Composite(parent, SWT.NONE);
+		final GridLayout layout = new GridLayout(3, false);
+		layout.horizontalSpacing = 0;
+		layout.verticalSpacing = 0;
+		layout.marginHeight = 0;
+		layout.marginWidth = 0;
+		composite.setLayout(layout);
+		composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+		progress = new Label(composite, SWT.LEFT);
+		// Status label
+		info = new Label(composite, SWT.RIGHT);
+
+		info.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+		progress.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+		// factory.applyTo(info);
+		// factory.applyTo(progress);
+		final Color color = parent.getDisplay().getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW);
+		info.setForeground(color);
+		progress.setForeground(color);
+		info.setText(HELP_TEXT);
+		return composite;
+	}
+
+	@Override
+	protected void setInfoText(final String text) {
+		info.setText(text);
+	}
+
+	protected void applyColors(final Composite composite) {
+		applyForegroundColor(getForeground(), composite);
+		applyBackgroundColor(getBackground(), composite);
+	}
+
+	protected void applyFonts(final Composite composite) {
+		Dialog.applyDialogFont(composite);
+
+		if (info != null) {
+			final Font font = info.getFont();
+			final FontData[] fontDatas = font.getFontData();
+			for (int i = 0; i < fontDatas.length; i++) {
+				fontDatas[i].setHeight(fontDatas[i].getHeight() * 9 / 10);
+			}
+			infoFont = new Font(info.getDisplay(), fontDatas);
+			info.setFont(infoFont);
+		}
+	}
+
+	@Override
+	protected void configureShell(final Shell shell) {
+		super.configureShell(shell);
+		shell.addDisposeListener(new DisposeListener() {
+			public void widgetDisposed(final DisposeEvent e) {
+				handleClose();
+			}
+		});
+	}
+
+	protected void handleClose() {
+		if (infoFont != null && !infoFont.isDisposed()) {
+			infoFont.dispose();
+		}
+		infoFont = null;
+	}
+
+	/**
+	 * Create a horizontal separator for the given parent.
+	 * 
+	 * @param parent
+	 *            The parent composite.
+	 * @return The Control representing the horizontal separator.
+	 */
+	private Control createHorizontalSeparator(final Composite parent) {
+		final Label separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL | SWT.LINE_DOT);
+		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).applyTo(separator);
+		return separator;
+	}
+
+	@Override
+	protected List<Control> getForegroundColorExclusions() {
+		final List<Control> list = copyControls(super.getForegroundColorExclusions());
+		if (info != null)
+			list.add(info);
+		if (separator != null)
+			list.add(separator);
+		return list;
+	}
+
+	@Override
+	protected List<Control> getBackgroundColorExclusions() {
+		final List<Control> list = copyControls(super.getBackgroundColorExclusions());
+		if (separator != null)
+			list.add(separator);
+		return list;
+	}
+
+	private List<Control> copyControls(List<?> list) {
+		List<Control> result = new ArrayList<Control>(list.size());
+		for (Control control : result) {
+			result.add(control);
+		}
+		return result;
+	}
+
+	protected void applyBackgroundColor(final Color color) {
+		applyBackgroundColor(color, titleArea);
+	}
+
+	private Composite titleArea;
+
+	protected Text titleText;
+	private Font infoFont;
+	private Label info;
+	private Label progress;
+	private Control separator;
+
+	private static final GridDataFactory LAYOUTDATA_GRAB_BOTH = GridDataFactory.fillDefaults().grab(true, true);
+	private static final GridLayoutFactory POPUP_LAYOUT_FACTORY = GridLayoutFactory.fillDefaults()
+			.margins(POPUP_MARGINWIDTH, POPUP_MARGINHEIGHT).spacing(POPUP_HORIZONTALSPACING, POPUP_VERTICALSPACING);
+
+	protected static final String HELP_TEXT = "Enter search text";
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/SearchPanelManager.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/SearchPanelManager.java
new file mode 100644
index 0000000..fc651d8
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/SearchPanelManager.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.internal.panels;
+
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IWorkbenchWindow;
+
+import org.eclipse.ui.glance.internal.GlancePlugin;
+import org.eclipse.ui.glance.internal.preferences.IPreferenceConstants;
+import org.eclipse.ui.glance.panels.ISearchPanel;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class SearchPanelManager {
+
+	public static SearchPanelManager getInstance() {
+		if (instance == null)
+			instance = new SearchPanelManager();
+		return instance;
+	}
+
+	public ISearchPanel getPanel(Control control) {
+		if (showStatusLine()) {
+			IWorkbenchWindow window = SearchStatusLine.getWindow(control);
+			if (window != null) {
+				return SearchStatusLine.getSearchLine(window);
+			}
+		}
+		PopupSearchDialog dialog = new PopupSearchDialog(control);
+		dialog.open();
+		return dialog;
+	}
+
+	private boolean showStatusLine() {
+		return GlancePlugin.getDefault().getPreferenceStore().getBoolean(
+				IPreferenceConstants.PANEL_STATUS_LINE);
+	}
+
+	private static SearchPanelManager instance;
+
+	private SearchPanelManager() {
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/SearchStatusLine.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/SearchStatusLine.java
new file mode 100644
index 0000000..284a10f
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/panels/SearchStatusLine.java
@@ -0,0 +1,252 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.internal.panels;
+
+import org.eclipse.jface.action.ContributionItem;
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.IStatusLineManager;
+import org.eclipse.jface.action.StatusLineLayoutData;
+import org.eclipse.jface.action.StatusLineManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.internal.WorkbenchWindow;
+import org.eclipse.ui.keys.IBindingService;
+import org.eclipse.ui.texteditor.IStatusField;
+import org.eclipse.ui.texteditor.IStatusFieldExtension;
+
+import org.eclipse.ui.glance.panels.SearchPanel;
+import org.eclipse.ui.glance.sources.Match;
+import org.eclipse.ui.glance.utils.UIUtils;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+@SuppressWarnings("restriction")
+public class SearchStatusLine extends SearchPanel {
+
+	@Override
+	protected Control createText(Composite parent, int style) {
+		Control textControl = super.createText(parent, style);
+		textControl.addFocusListener(new FocusListener() {
+			public void focusLost(FocusEvent e) {
+				setKeyFilter(true);
+			}
+
+			public void focusGained(FocusEvent e) {
+				setKeyFilter(false);
+			}
+		});
+		textControl.addDisposeListener(new DisposeListener() {
+			public void widgetDisposed(DisposeEvent e) {
+				setKeyFilter(true);
+			}
+		});
+		return textControl;
+	}
+
+	public static SearchStatusLine getSearchLine(IWorkbenchWindow window) {
+		IStatusLineManager manager = getManager(window);
+		if (manager != null) {
+			IContributionItem[] items = manager.getItems();
+			for (IContributionItem item : items) {
+				if (item instanceof SearchItem)
+					return ((SearchItem) item).getSearchPanel();
+			}
+		}
+		return new SearchStatusLine(window);
+	}
+
+	public static IWorkbenchWindow getWindow(Control control) {
+		Shell shell = control.getShell();
+		IWorkbenchWindow[] windows = PlatformUI.getWorkbench()
+				.getWorkbenchWindows();
+		for (IWorkbenchWindow window : windows) {
+			if (shell.equals(window.getShell()))
+				return window;
+		}
+		return null;
+	}
+
+	public boolean isApplicable(Control control) {
+		return window.equals(getWindow(control));
+	}
+
+	public IWorkbenchWindow getWindow() {
+		return window;
+	}
+
+	private int matchCount;
+
+	@Override
+	public void allFound(final Match[] matches) {
+		super.allFound(matches);
+		matchCount = matches.length;
+		updateInfo();
+	}
+
+	private void updateInfo() {
+		StringBuffer buffer = new StringBuffer();
+
+		if (matchCount == 0) {
+			buffer.append(DEFAULT_MATCH_LABEL);
+		} else {
+			buffer.append(matchCount);
+		}
+
+		matchText = buffer.toString();
+		UIUtils.asyncExec(matchLabel, new Runnable() {
+
+			public void run() {
+				matchLabel.setText(matchText);
+			}
+		});
+	}
+
+	public void closePanel() {
+		if (item != null) {
+			fireClose();
+			IStatusLineManager manager = getManager();
+			if (manager != null) {
+				manager.remove(item);
+				manager.update(false);
+			}
+			item = null;
+		}
+	}
+
+	@Override
+	public void createContent(Composite parent) {
+		super.createContent(parent);
+		StatusLineLayoutData data = new StatusLineLayoutData();
+		data.widthHint = getPreferedWidth();
+		data.heightHint = getPreferredHeight();
+		getControl().setLayoutData(data);
+		createMatchLabel(parent);
+	}
+
+	@Override
+	protected void textEmpty() {
+		super.textEmpty();
+		matchText = DEFAULT_MATCH_LABEL;
+		matchLabel.setText(matchText);
+	}
+
+	private void createMatchLabel(Composite parent) {
+		Label separator = new Label(parent, SWT.SEPARATOR);
+		setLayoutData(separator);
+		matchLabel = new CLabel(parent, SWT.SHADOW_NONE);
+		StatusLineLayoutData data = new StatusLineLayoutData();
+		data.widthHint = getTextWidth(parent, 10) + 15;
+		data.heightHint = getPreferredHeight();
+		matchLabel.setLayoutData(data);
+		matchLabel.setText(matchText);
+	}
+
+	protected void setKeyFilter(boolean enabled) {
+		IBindingService service = (IBindingService) PlatformUI.getWorkbench()
+				.getService(IBindingService.class);
+		if (service != null) {
+			service.setKeyFilterEnabled(enabled);
+		}
+	}
+
+	private class SearchItem extends ContributionItem implements IStatusField,
+			IStatusFieldExtension {
+
+		public void setImage(Image image) {
+		}
+
+		public void setText(String text) {
+		}
+
+		public void setErrorImage(Image image) {
+			setImage(image);
+		}
+
+		public void setErrorText(String text) {
+			setText(text);
+		}
+
+		public void setToolTipText(String string) {
+			setText(string);
+		}
+
+		@Override
+		public void fill(Composite parent) {
+			Label separator = new Label(parent, SWT.SEPARATOR);
+			createContent(parent);
+			setLayoutData(separator);
+		}
+
+		public SearchStatusLine getSearchPanel() {
+			return SearchStatusLine.this;
+		}
+
+		@Override
+		public void dispose() {
+			fireClose();
+		}
+
+	}
+
+	private void setLayoutData(Label separator) {
+		StatusLineLayoutData data = new StatusLineLayoutData();
+		data.heightHint = getPreferredHeight();
+		separator.setLayoutData(data);
+	}
+
+	private SearchStatusLine(IWorkbenchWindow window) {
+		this.window = window;
+		init();
+	}
+
+	private void init() {
+		item = new SearchItem();
+		IStatusLineManager manager = getManager();
+		if (manager != null) {
+			manager.remove(item);
+			manager.appendToGroup(StatusLineManager.BEGIN_GROUP, item);
+			manager.update(true);
+		}
+	}
+
+	private IStatusLineManager getManager() {
+		return getManager(window);
+	}
+
+	private static IStatusLineManager getManager(IWorkbenchWindow window) {
+		if (window != null) {
+			WorkbenchWindow ww = (WorkbenchWindow) window;
+			return ww.getActionBars().getStatusLineManager();
+		}
+		return null;
+	}
+
+	private static final String DEFAULT_MATCH_LABEL = "no matches";
+
+	private String matchText = DEFAULT_MATCH_LABEL;
+	private CLabel matchLabel;
+	private SearchItem item;
+	private final IWorkbenchWindow window;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/preferences/GlancePreferenceInitializer.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/preferences/GlancePreferenceInitializer.java
new file mode 100644
index 0000000..332003f
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/preferences/GlancePreferenceInitializer.java
@@ -0,0 +1,44 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.internal.preferences;
+
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+import org.eclipse.ui.glance.internal.GlancePlugin;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class GlancePreferenceInitializer extends AbstractPreferenceInitializer implements IPreferenceConstants {
+
+	@Override
+	public void initializeDefaultPreferences() {
+		IPreferenceStore preferences = GlancePlugin.getDefault().getPreferenceStore();
+		preferences.setDefault(SEARCH_CASE_SENSITIVE, false);
+		preferences.setDefault(SEARCH_CAMEL_CASE, false);
+		preferences.setDefault(SEARCH_REGEXP, false);
+		preferences.setDefault(SEARCH_WORD_PREFIX, false);
+
+		preferences.setDefault(PANEL_DIRECTIONS, true);
+		preferences.setDefault(PANEL_STATUS_LINE, true);
+		preferences.setDefault(PANEL_CLOSE, true);
+		preferences.setDefault(PANEL_TEXT_SIZE, 20);
+		preferences.setDefault(PANEL_LINK, true);
+		preferences.setDefault(PANEL_STARTUP, false);
+		preferences.setDefault(PANEL_AUTO_INDEXING, false);
+		preferences.setDefault(SEARCH_INCREMENTAL, true);
+		preferences.setDefault(PANEL_MAX_INDEXING_DEPTH, 4);
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/preferences/GlancePreferencePage.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/preferences/GlancePreferencePage.java
new file mode 100644
index 0000000..aefcf09
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/preferences/GlancePreferencePage.java
@@ -0,0 +1,179 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.internal.preferences;
+
+import org.eclipse.jface.preference.BooleanFieldEditor;
+import org.eclipse.jface.preference.ColorFieldEditor;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.IntegerFieldEditor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+import org.eclipse.ui.glance.internal.GlancePlugin;
+import org.eclipse.ui.glance.internal.search.SearchManager;
+import org.eclipse.ui.glance.sources.ColorManager;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class GlancePreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage,
+		IPreferenceConstants {
+
+	private Button currentWindow;
+
+	public GlancePreferencePage() {
+		super(GRID);
+	}
+
+	public void init(final IWorkbench workbench) {
+	}
+
+	@Override
+	protected IPreferenceStore doGetPreferenceStore() {
+		return GlancePlugin.getDefault().getPreferenceStore();
+	}
+
+	@Override
+	protected void createFieldEditors() {
+		createSearchSettings(getFieldEditorParent());
+		createColorSettings(getFieldEditorParent());
+		createPanelSettings(getFieldEditorParent());
+	}
+
+	/**
+	 * Adjust the layout of the field editors so that they are properly aligned.
+	 */
+	@Override
+	protected void adjustGridLayout() {
+		super.adjustGridLayout();
+		((GridLayout) getFieldEditorParent().getLayout()).numColumns = 1;
+	}
+
+	private Group createPanelSettings(final Composite parent) {
+		final Group group = new Group(parent, SWT.NONE);
+		group.setText("Panel");
+		group.setLayout(new GridLayout(1, false));
+		group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+		final Composite composite = new Composite(group, SWT.NONE);
+		composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+		createCurrentWindowOption(composite);
+		addField(new BooleanFieldEditor(PANEL_STARTUP, "Show at startup", composite));
+		addField(new BooleanFieldEditor(PANEL_STATUS_LINE, "Show panel in status line when possible", composite));
+		addField(new BooleanFieldEditor(PANEL_DIRECTIONS, "Show direction buttons", composite));
+		addField(new BooleanFieldEditor(PANEL_CLOSE, "Show close button", composite));
+		addField(new BooleanFieldEditor(PANEL_AUTO_INDEXING, "Enable auto indexing", composite));
+		addField(new BooleanFieldEditor(SEARCH_INCREMENTAL, "Enable incremental search", composite));
+		final IntegerFieldEditor maxIndexingDepthEditor = new IntegerFieldEditor(PANEL_MAX_INDEXING_DEPTH,
+				"Max indexing depth for trees:", composite);
+		maxIndexingDepthEditor.setValidRange(1, Integer.MAX_VALUE);
+		addField(maxIndexingDepthEditor);
+		addField(new IntegerFieldEditor(PANEL_TEXT_SIZE, "Default box width in chars:", composite));
+
+		return group;
+	}
+
+	private void createCurrentWindowOption(final Composite composite) {
+		currentWindow = new Button(composite, SWT.CHECK);
+		currentWindow.setText("Show in the current window");
+		initCurrentWindow();
+	}
+
+	@Override
+	protected void performDefaults() {
+		super.performDefaults();
+		initCurrentWindow();
+	}
+
+	private void initCurrentWindow() {
+		final IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+		if (window == null) {
+			currentWindow.setEnabled(false);
+		} else {
+			final boolean inWindow = SearchManager.getIntance().isInWindow(window);
+			currentWindow.setSelection(inWindow);
+		}
+	}
+
+	@Override
+	public boolean performOk() {
+		final IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+		if (window != null) {
+			final boolean inWindow = SearchManager.getIntance().isInWindow(window);
+			final boolean open = currentWindow.getSelection();
+			if (open != inWindow) {
+				SearchManager.getIntance().setStatusLine(window, open);
+			}
+		}
+		return super.performOk();
+	}
+
+	private Group createSearchSettings(final Composite parent) {
+		final Group group = new Group(parent, SWT.NONE);
+		group.setText("Search");
+		group.setLayout(new GridLayout(1, false));
+		group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+		final Composite composite = new Composite(group, SWT.NONE);
+		composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+		addField(new BooleanFieldEditor(SEARCH_CASE_SENSITIVE, LABEL_CASE_SENSITIVE, composite));
+		addField(new BooleanFieldEditor(SEARCH_CAMEL_CASE, LABEL_CAMEL_CASE, composite));
+		addField(new BooleanFieldEditor(SEARCH_WORD_PREFIX, LABEL_WORD_PREFIX, composite));
+		addField(new BooleanFieldEditor(SEARCH_REGEXP, LABEL_REGEXP, composite));
+
+		return group;
+	}
+
+	private Group createColorSettings(final Composite parent) {
+		final Group group = new Group(parent, SWT.NONE);
+		group.setText("Colors");
+		group.setLayout(new GridLayout(1, false));
+		group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+		final Composite composite = new Composite(group, SWT.NONE);
+		composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+		addField(new ColorEditor(composite, "Highlight:", COLOR_HIGHLIGHT));
+		addField(new ColorEditor(composite, "Selection:", COLOR_SELECTION));
+
+		return group;
+	}
+
+	private static class ColorEditor extends ColorFieldEditor {
+
+		public ColorEditor(final Composite parent, final String text, final String prefKey) {
+			this(parent, text, prefKey, ColorManager.getStore());
+		}
+
+		public ColorEditor(final Composite parent, final String text, final String prefKey, IPreferenceStore store) {
+			super(prefKey, text, parent);
+			super.setPreferenceStore(store);
+		}
+
+		@Override
+		public void setPreferenceStore(final IPreferenceStore store) {
+		}
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/preferences/IPreferenceConstants.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/preferences/IPreferenceConstants.java
new file mode 100644
index 0000000..5e8ef21
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/preferences/IPreferenceConstants.java
@@ -0,0 +1,49 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.internal.preferences;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public interface IPreferenceConstants {
+
+	final String PREFERENCE_PAGE_ID = "org.eclipse.ui.glance.preference";
+
+	final String SEARCH_PREFIX = "search.";
+	final String PANEL_PREFIX = "panel.";
+	final String HISTORY = "searchHistory";
+
+	final String PANEL_DIRECTIONS = PANEL_PREFIX + "directions";
+	final String PANEL_CLOSE = PANEL_PREFIX + "close";
+	final String PANEL_TEXT_SIZE = PANEL_PREFIX + "textSize";
+	final String PANEL_STATUS_LINE = PANEL_PREFIX + "statusLine";
+	final String PANEL_LINK = PANEL_PREFIX + "link";
+	final String PANEL_STARTUP = PANEL_PREFIX + "startup";
+	final String PANEL_AUTO_INDEXING = PANEL_PREFIX + "autoIndexing";
+	final String PANEL_MAX_INDEXING_DEPTH = PANEL_PREFIX + "maxIndexingDepth";
+
+	final String SEARCH_CASE_SENSITIVE = SEARCH_PREFIX + "caseSensitive";
+	final String SEARCH_WORD_PREFIX = SEARCH_PREFIX + "wordPrefix";
+	final String SEARCH_REGEXP = SEARCH_PREFIX + "regexp";
+	final String SEARCH_CAMEL_CASE = SEARCH_PREFIX + "camelCase";
+	final String SEARCH_INCREMENTAL = PANEL_PREFIX + "incremental";
+
+	final String LABEL_CASE_SENSITIVE = "Case Sensitive";
+	final String LABEL_WORD_PREFIX = "Word Prefix";
+	final String LABEL_REGEXP = "Regular Expressions";
+	final String LABEL_CAMEL_CASE = "Camel Case";
+
+	final String COLOR_HIGHLIGHT = "glanceColorBackground";
+	final String COLOR_SELECTION = "glanceSelectedColorBackground";
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/preferences/TreeColors.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/preferences/TreeColors.java
new file mode 100644
index 0000000..92764d3
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/preferences/TreeColors.java
@@ -0,0 +1,54 @@
+/******************************************************************************* 

+ * Copyright (c) 2017 Exyte  

+ * 

+ * 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: 

+ *     Yuri Strot - initial API and Implementation 

+ *******************************************************************************/

+package org.eclipse.ui.glance.internal.preferences;

+

+import org.eclipse.swt.graphics.RGB;

+

+public class TreeColors implements IPreferenceConstants {

+

+	public TreeColors(RGB bg, RGB fg, boolean useNative) {

+		this.bg = bg;

+		this.fg = fg;

+		this.useNative = useNative;

+	}

+

+	public RGB getBg() {

+		return bg;

+	}

+

+	public RGB getFg() {

+		return fg;

+	}

+

+	public boolean isUseNative() {

+		return useNative;

+	}

+

+	public static TreeColors getDefault() {

+		String osName = System.getProperty("os.name").toLowerCase();

+

+		if (osName.contains("windows")) {

+			if (osName.contains("7")) {

+				return new TreeColors(null, null, true);

+			}

+		} else if (osName.contains("mac")) {

+			return new TreeColors(new RGB(56, 117, 215),

+					new RGB(255, 255, 255), false);

+		}

+		return new TreeColors(null, null, false);

+	}

+

+	private final boolean useNative;

+	private final RGB bg;

+	private final RGB fg;

+

+}

diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/AbstractSearchScope.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/AbstractSearchScope.java
new file mode 100644
index 0000000..51d70ff
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/AbstractSearchScope.java
@@ -0,0 +1,238 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.internal.search;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.ui.glance.internal.search.SearchScopeEntry.IMatchListener;
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.ITextSource;
+import org.eclipse.ui.glance.sources.ITextSourceListener;
+import org.eclipse.ui.glance.sources.Match;
+import org.eclipse.ui.glance.sources.SourceSelection;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public abstract class AbstractSearchScope implements IMatchListener,
+		ITextSourceListener {
+
+	public AbstractSearchScope(ITextSource source) {
+		this.source = source;
+		init();
+	}
+
+	public void dispose() {
+		for (SearchScopeEntry entry : entries) {
+			entry.dispose();
+		}
+		source.removeTextSourceListener(this);
+	}
+
+	public abstract void added(SearchScopeEntry entry, Match match);
+
+	public abstract void cleared(SearchScopeEntry entry);
+
+	public void blocksReplaced(ITextBlock[] newBlocks) {
+		entries = new ArrayList<SearchScopeEntry>(newBlocks.length);
+		newBlocks(newBlocks);
+	}
+
+	public void blocksChanged(ITextBlock[] removed, ITextBlock[] added) {
+		for (int i = 0; i < entries.size(); i++) {
+			SearchScopeEntry entry = entries.get(i);
+			for (ITextBlock block : removed) {
+				if (block.equals(entry.getBlock())) {
+					entries.remove(i);
+					i--;
+					break;
+				}
+			}
+		}
+		newBlocks(added);
+	}
+
+	private void newBlocks(ITextBlock[] newBlocks) {
+		for (ITextBlock block : newBlocks) {
+			SearchScopeEntry entry = createEntry(block);
+			int index = Collections.binarySearch(entries, entry);
+			if (index < 0) {
+				index = -1 * index - 1;
+				entries.add(index, entry);
+			}
+		}
+		// TODO: need to test more without this updates
+		// updateSelection(source.getSelection());
+	}
+
+	public void selectNext() {
+		int index = getSelectedEntryIndex();
+		if (index < 0 && entries.size() > 0)
+			index = 0;
+		if (index >= 0) {
+			int offset = getOffset();
+			Match match = findNextMatch(index, entries.size(), offset,
+					Integer.MAX_VALUE);
+			if (match == null) {
+				match = findNextMatch(0, index, -1, Integer.MAX_VALUE);
+				if (match == null) {
+					match = findNextMatch(index, index + 1, -1, offset);
+				}
+			}
+			select(match);
+		}
+	}
+
+	public void selectPrev() {
+		int index = getSelectedEntryIndex();
+		if (index < 0 && entries.size() > 0)
+			index = 0;
+		if (index >= 0) {
+			int offset = getOffset();
+			Match match = findPrevMatch(index, -1, 0, offset);
+			if (match == null) {
+				match = findPrevMatch(entries.size() - 1, index, 0,
+						Integer.MAX_VALUE);
+				if (match == null) {
+					match = findPrevMatch(index, index - 1, offset,
+							Integer.MAX_VALUE);
+				}
+			}
+			select(match);
+		}
+	}
+
+	protected void select(Match match) {
+		if (match != null) {
+			selection = new SourceSelection(match.getBlock(),
+					match.getOffset(), match.getLength());
+			source.select(match);
+		}
+	}
+
+	public void showEmptyText() {
+		source.select(null);
+	}
+
+	public void showMatches() {
+		source.show(getMatches());
+	}
+
+	protected Match findNextMatch(int from, int to, int offsetStart,
+			int offsetEnd) {
+		for (int i = from; i < to; i++) {
+			SearchScopeEntry entry = entries.get(i);
+			for (Match match : entry.getMatches()) {
+				if (match.getOffset() > offsetStart
+						&& match.getOffset() < offsetEnd) {
+					return match;
+				}
+			}
+			offsetStart = -1;
+			offsetEnd = Integer.MAX_VALUE;
+		}
+		return null;
+	}
+
+	protected Match findPrevMatch(int from, int to, int offsetStart,
+			int offsetEnd) {
+		for (int i = from; i > to; i--) {
+			SearchScopeEntry entry = entries.get(i);
+			List<Match> matches = entry.getMatches();
+			for (int j = matches.size() - 1; j >= 0; j--) {
+				Match match = matches.get(j);
+				if (match.getOffset() >= offsetStart
+						&& match.getOffset() < offsetEnd) {
+					return match;
+				}
+			}
+			offsetStart = -1;
+			offsetEnd = Integer.MAX_VALUE;
+		}
+		return null;
+	}
+
+	public void selectionChanged(SourceSelection selection) {
+		updateSelection(selection);
+	}
+
+	public Match[] getMatches() {
+		List<Match> matches = new ArrayList<Match>();
+		for (SearchScopeEntry entry : entries) {
+			matches.addAll(entry.getMatches());
+		}
+		return matches.toArray(new Match[matches.size()]);
+	}
+
+	protected void init() {
+		source.addTextSourceListener(this);
+		ITextBlock[] blocks = source.getBlocks();
+		entries = new ArrayList<SearchScopeEntry>(blocks.length);
+		for (ITextBlock block : blocks) {
+			SearchScopeEntry entry = createEntry(block);
+			entries.add(entry);
+		}
+		Collections.sort(entries);
+		updateSelection(source.getSelection());
+	}
+
+	private void updateSelection(SourceSelection selection) {
+		if (selection != null && selection.equals(this.selection)) {
+			return;
+		}
+		if (selection == null && entries.size() > 0) {
+			ITextBlock block = entries.get(0).getBlock();
+			selection = new SourceSelection(block, 0, 0);
+		}
+		this.selection = selection;
+		updateStart();
+	}
+
+	protected void updateStart() {
+		int index = getSelectedEntryIndex();
+		if (index >= 0) {
+			SearchScopeEntry entry = entries.get(index);
+			entry.setStart(getOffset());
+			currentEntry = index;
+		}
+	}
+
+	private int getOffset() {
+		return selection == null ? 0 : selection.getOffset();
+	}
+
+	protected int getSelectedEntryIndex() {
+		if (selection != null) {
+			for (int i = 0; i < entries.size(); i++) {
+				SearchScopeEntry entry = entries.get(i);
+				ITextBlock block = entry.getBlock();
+				if (block.equals(selection.getBlock())) {
+					return i;
+				}
+			}
+		}
+		return -1;
+	}
+
+	protected SearchScopeEntry createEntry(ITextBlock block) {
+		return new SearchScopeEntry(block, this);
+	}
+
+	protected List<SearchScopeEntry> entries;
+	protected ITextSource source;
+	private SourceSelection selection;
+	protected int currentEntry;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/ISearchListener.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/ISearchListener.java
new file mode 100644
index 0000000..f0d4ed3
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/ISearchListener.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.internal.search;
+
+import org.eclipse.ui.glance.sources.Match;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public interface ISearchListener {
+
+	public void firstFound(Match match);
+
+	public void allFound(Match[] matches);
+
+	public void finished();
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchEngine.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchEngine.java
new file mode 100644
index 0000000..6698d8f
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchEngine.java
@@ -0,0 +1,253 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.internal.search;
+
+import java.util.regex.Matcher;
+
+import org.eclipse.ui.glance.internal.search.SearchJob.ISearchMonitor;
+import org.eclipse.ui.glance.sources.ConfigurationManager;
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.ITextSource;
+import org.eclipse.ui.glance.sources.Match;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class SearchEngine extends Thread {
+
+	public SearchEngine(ISearchListener listener) {
+		this.listener = listener;
+	}
+
+	public void setSource(SearchRule rule, ITextSource source, boolean paused) {
+		synchronized (monitor) {
+			if (scope != null) {
+				scope.dispose();
+				scope = null;
+			}
+			scope = new SearchScope(source);
+			this.paused = paused;
+			doSetRule(rule);
+		}
+	}
+
+	public void selectNext() {
+		if (scope != null) {
+			scope.selectNext();
+		}
+	}
+
+	public void selectPrev() {
+		if (scope != null) {
+			scope.selectPrev();
+		}
+	}
+
+	public void run() {
+		while (!exit) {
+			sleepWhileInterrupted(0);
+			if (scope == null)
+				continue;
+			if (exit)
+				break;
+			while (true) {
+				long start = System.currentTimeMillis();
+				if (!findMatches())
+					continue;
+				long waitPause = paused ? PAUSE
+						- (System.currentTimeMillis() - start) : 0;
+				if (waitPause > 0)
+					sleepWhileInterrupted(waitPause);
+				if (!scope.isCanceled())
+					break;
+			}
+			listener.finished();
+			scope.showMatches();
+		}
+	}
+
+	public void setRule(SearchRule rule) {
+		synchronized (monitor) {
+			doSetRule(rule);
+		}
+	}
+
+	protected void doSetRule(SearchRule rule) {
+		cancel = true;
+		boolean findFirst = false;
+		if (this.rule == null || !this.rule.equals(rule)) {
+			this.rule = rule;
+			findFirst = true;
+			matcher = rule.getText().length() == 0 ? null : rule.getPattern()
+					.matcher(new String());
+		}
+		if (scope != null) {
+			scope.updateMatcher(findFirst);
+		}
+		interrupt();
+	}
+
+	public void exit() {
+		synchronized (monitor) {
+			exit = true;
+			interrupt();
+		}
+	}
+
+	private boolean findMatches() {
+		cancel = false;
+		if (matcher == null) {
+			scope.showEmptyText();
+			return true;
+		}
+		SearchJob job = null;
+		while ((job = scope.getJob()) != null) {
+			if (!job.run())
+				return false;
+		}
+		scope.updateResult();
+		return true;
+	}
+
+	private void sleepWhileInterrupted(long millis) {
+		try {
+			if (millis == 0) {
+				while (true)
+					Thread.sleep(50);
+			} else
+				Thread.sleep(millis);
+		} catch (InterruptedException e1) {
+		}
+	}
+
+	private class SearchScope extends AbstractSearchScope implements
+			ISearchMonitor {
+
+		private boolean firstFound = false;
+
+		/**
+		 * @param source
+		 */
+		public SearchScope(ITextSource source) {
+			super(source);
+		}
+
+		public SearchJob getJob() {
+			synchronized (monitor) {
+				while (currentEntry < entries.size()) {
+					SearchJob job = (SearchJob) entries.get(currentEntry);
+					if (!job.isFinished())
+						return job;
+					currentEntry++;
+				}
+				return updateEntry();
+			}
+		}
+
+		public void updateResult() {
+			if (!firstFound) {
+				listener.firstFound(null);
+				firstFound = true;
+			}
+			listener.allFound(getMatches());
+		}
+
+		@Override
+		public void added(SearchScopeEntry entry, Match match) {
+			if (!firstFound) {
+			    if (ConfigurationManager.getInstance().incremenstalSearch()){
+			        select(match);
+			    }
+				listener.firstFound(match);
+				firstFound = true;
+			}
+		}
+
+		@Override
+		public void cleared(SearchScopeEntry entry) {
+			synchronized (monitor) {
+				cancel = true;
+				interrupt();
+			}
+		}
+
+		public boolean isCanceled() {
+			synchronized (monitor) {
+				return cancel;
+			}
+		}
+
+		@Override
+		public void blocksChanged(ITextBlock[] removed, ITextBlock[] added) {
+			synchronized (monitor) {
+				super.blocksChanged(removed, added);
+				cancel = true;
+				interrupt();
+			}
+		}
+
+		@Override
+		public void blocksReplaced(ITextBlock[] newBlocks) {
+			synchronized (monitor) {
+				super.blocksReplaced(newBlocks);
+				cancel = true;
+				interrupt();
+			}
+		}
+
+		@Override
+		public Match[] getMatches() {
+			synchronized (monitor) {
+				return super.getMatches();
+			}
+		}
+
+		@Override
+		protected SearchScopeEntry createEntry(ITextBlock block) {
+			return new SearchJob(block, matcher, this);
+		}
+
+		protected void updateMatcher(boolean findFirst) {
+			firstFound = !findFirst;
+			for (SearchScopeEntry entry : entries) {
+				SearchJob job = (SearchJob) entry;
+				job.update(matcher);
+			}
+			updateStart();
+		}
+
+		private SearchJob updateEntry() {
+			currentEntry = 0;
+			for (SearchScopeEntry entry : entries) {
+				SearchJob job = (SearchJob) entry;
+				if (!job.isFinished())
+					return job;
+				currentEntry++;
+			}
+			currentEntry = 0;
+			return null;
+		}
+	}
+
+	private static final long PAUSE = 100 * 5;// 100 s
+
+	private boolean cancel;
+	private SearchRule rule;
+	private Matcher matcher;
+	private Object monitor = new Object();
+	private boolean exit;
+	private boolean paused;
+
+	private SearchScope scope;
+	private ISearchListener listener;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchJob.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchJob.java
new file mode 100644
index 0000000..e6d8a88
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchJob.java
@@ -0,0 +1,121 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.internal.search;
+
+import java.util.regex.Matcher;
+
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.Match;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class SearchJob extends SearchScopeEntry {
+
+	public SearchJob(ITextBlock block, Matcher matcher, ISearchMonitor monitor) {
+		super(block, monitor);
+		update(matcher);
+	}
+
+	public void update(Matcher matcher) {
+		this.matcher = matcher;
+		clear();
+	}
+
+	@Override
+	protected void doClear() {
+		super.doClear();
+		finished = false;
+	}
+
+	/**
+	 * @return the finished
+	 */
+	public boolean isFinished() {
+		return finished;
+	}
+
+	public boolean run() {
+		if (matcher == null)
+			return false;
+		matcher.reset(getText());
+		int from = getStart();
+		if (!find(from, getText().length()))
+			return false;
+		addMatchToBegin();
+		if (!find(0, from - 1))
+			return false;
+		finished = true;
+		setStart(0);
+		return true;
+	}
+
+	private boolean find(int from, int to) {
+		int k = 1;
+		int limit = getText().length();
+		if (from >= to || from > limit)
+			return true;
+		Match match = find(from);
+		if (getMonitor().isCanceled())
+			return false;
+		if (match != null) {
+			from = match.getOffset() + 1;
+			if (from > to || from > limit)
+				return true;
+			addMatch(match);
+			match = find(from);
+			while ((match = find(from)) != null) {
+				if (match.getOffset() >= to)
+					return true;
+				addMatch(match);
+				if (k++ == 20) {
+					if (getMonitor().isCanceled())
+						return false;
+					k = 0;
+				}
+				from = match.getOffset() + 1;
+			}
+		}
+		return true;
+	}
+
+	private Match find(int from) {
+		try {
+			if (matcher.find(from)) {
+				int start = matcher.start();
+				int end = matcher.end();
+				if (end != start) { // don't report 0-length matches
+					return new Match(getBlock(), start, end - start);
+				}
+			}
+		} catch (Exception e) {
+			// It can be an exception while we matching.
+			// So return if exception occured
+		}
+		return null;
+	}
+
+	private ISearchMonitor getMonitor() {
+		return (ISearchMonitor) getListener();
+	}
+
+	interface ISearchMonitor extends IMatchListener {
+
+		boolean isCanceled();
+
+	}
+
+	private boolean finished;
+	private Matcher matcher;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchManager.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchManager.java
new file mode 100644
index 0000000..fc34ca6
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchManager.java
@@ -0,0 +1,470 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.internal.search;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IWindowListener;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+import org.eclipse.ui.glance.internal.GlancePlugin;
+import org.eclipse.ui.glance.internal.panels.SearchPanelManager;
+import org.eclipse.ui.glance.internal.panels.SearchStatusLine;
+import org.eclipse.ui.glance.internal.preferences.IPreferenceConstants;
+import org.eclipse.ui.glance.internal.sources.ISourceProviderListener;
+import org.eclipse.ui.glance.internal.sources.TextSourceMaker;
+import org.eclipse.ui.glance.internal.sources.TextSourceManager;
+import org.eclipse.ui.glance.panels.ISearchPanel;
+import org.eclipse.ui.glance.panels.ISearchPanelListener;
+import org.eclipse.ui.glance.sources.ITextSource;
+import org.eclipse.ui.glance.sources.Match;
+import org.eclipse.ui.glance.sources.SourceSelection;
+import org.eclipse.ui.glance.utils.UITextSource;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class SearchManager {
+
+	/** Searching from the current position */
+	public static final int FIND_HERE = 0;
+	/** Searching next occurrence of the string */
+	public static final int FIND_NEXT = 1;
+	/** Searching previous occurrence of the string */
+	public static final int FIND_PREVIOUS = 2;
+
+	public static SearchManager getIntance() {
+		if (manager == null) {
+			manager = new SearchManager();
+		}
+		return manager;
+	}
+
+	public boolean activate() {
+		final TextSourceMaker source = TextSourceManager.getInstance()
+				.getSource();
+		if (update(source, true)) {
+			forceFocus();
+			return true;
+		}
+		return false;
+	}
+
+	public void startup() {
+		PlatformUI.getWorkbench().addWindowListener(new IWindowListener() {
+			public void windowOpened(final IWorkbenchWindow window) {
+				setStatusLine(window, true);
+			}
+
+			public void windowDeactivated(final IWorkbenchWindow window) {
+			}
+
+			public void windowClosed(final IWorkbenchWindow window) {
+			}
+
+			public void windowActivated(final IWorkbenchWindow window) {
+			}
+		});
+		for (final IWorkbenchWindow window : PlatformUI.getWorkbench()
+				.getWorkbenchWindows()) {
+			setStatusLine(window, true);
+		}
+	}
+
+	public void setStatusLine(final IWorkbenchWindow window, final boolean open) {
+		final ISearchPanel panel = SearchStatusLine.getSearchLine(window);
+		if (!open) {
+			panel.closePanel();
+			return;
+		}
+		panels.add(panel);
+		final SearchPanelListener listener = new SearchPanelListener(panel);
+		panel.addPanelListener(listener);
+		panelToListener.put(panel, listener);
+		updateSourceListener();
+
+		final TextSourceMaker source = TextSourceManager.getInstance()
+				.getSource();
+		if (source != null && source.getControl() != null
+				&& panel.isApplicable(source.getControl())) {
+			this.panel = panel;
+			rule = panel.getRule();
+			if (setDescription(source))
+				updateEnabling();
+		}
+	}
+
+	public boolean isInWindow(final IWorkbenchWindow window) {
+		for (final ISearchPanel panel : panels) {
+			if (panel instanceof SearchStatusLine) {
+				final SearchStatusLine sl = ((SearchStatusLine) panel);
+				if (sl.getWindow() == window) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	public void findNext() {
+		if (panel != null) {
+			panel.findNext();
+		}
+	}
+
+	public void findPrevious() {
+		if (panel != null) {
+			panel.findPrevious();
+		}
+	}
+
+	public void clearHistory() {
+		if (panel != null) {
+			panel.clearHistory();
+		}
+	}
+
+	public void sourceFocus() {
+		ITextSource source = getSource();
+		if (source instanceof UITextSource) {
+			UITextSource uiTextSource = (UITextSource) source;
+			if (uiTextSource.getControl() != null) {
+				uiTextSource.getControl().setFocus();
+			}
+		}
+	}
+
+	public void close() {
+		if (panel != null) {
+			panel.closePanel();
+		}
+	}
+
+	public ITextSource getSource() {
+		return source;
+	}
+
+	private boolean update(final TextSourceMaker source,
+			final boolean openNewPanel) {
+		updatePanel(source, openNewPanel);
+		if (panel != null) {
+			rule = panel.getRule();
+			updateSourceListener();
+			if (setDescription(source))
+				updateEnabling();
+			return true;
+		}
+		return false;
+	}
+
+	private void updatePanel(final TextSourceMaker source,
+			final boolean openNewPanel) {
+		final Control control = source.getControl();
+		if (panel != null) {
+			if (panel.isApplicable(control))
+				return;
+			if (this.source != null) {
+				this.source.dispose();
+				this.source = null;
+			}
+			creator = null;
+			panel = null;
+		}
+		for (final ISearchPanel panel : panels) {
+			if (panel.isApplicable(control)) {
+				this.panel = panel;
+				break;
+			}
+		}
+		if (panel == null && openNewPanel) {
+			panel = SearchPanelManager.getInstance().getPanel(control);
+			if (panel != null) {
+				panels.add(panel);
+				final SearchPanelListener listener = new SearchPanelListener(
+						panel);
+				panel.addPanelListener(listener);
+				panelToListener.put(panel, listener);
+			}
+		}
+	}
+
+	private void forceFocus() {
+		String text = "";
+		if (source != null) {
+			final SourceSelection selection = source.getSelection();
+			if (selection != null) {
+				text = selection.getBlock().getText();
+				final int offset = selection.getOffset();
+				final int length = selection.getLength();
+				if (offset + length <= text.length())
+					text = text.substring(offset, offset + length);
+				else
+					text = "";
+			}
+		}
+		panel.setFocus(text);
+	}
+
+	private void find(final SearchRule rule, final int type) {
+		this.type = type;
+		if (rule != null) {
+			final boolean textEquals = rule.isTextEquals(this.rule);
+			final boolean settingsEqual = rule.isSettingsEqual(this.rule);
+			if (textEquals) {
+				if (settingsEqual) {
+					updateSelection();
+				} else {
+					updateSearch(rule, false);
+				}
+			} else {
+				updateSearch(rule, true);
+			}
+		} else {
+			updateSearch(rule, false);
+		}
+		this.rule = rule;
+	}
+
+	protected void dispose(final ISearchPanel panel) {
+		panels.remove(panel);
+		final SearchPanelListener listener = panelToListener.remove(panel);
+		panel.removePanelListener(listener);
+		if (panel == this.panel) {
+			this.panel = null;
+			if (source != null) {
+				source.dispose();
+				source = null;
+			}
+			if (creator != null) {
+				final Control control = creator.getControl();
+				if (control != null && !control.isDisposed())
+					control.forceFocus();
+				creator = null;
+			}
+			rule = null;
+		}
+		if (panels.size() == 0) {
+			if (engine != null) {
+				engine.exit();
+				engine = null;
+			}
+			if (sourceListener != null) {
+				TextSourceManager.getInstance().removeSourceProviderListener(
+						sourceListener);
+				sourceListener = null;
+			}
+		} else {
+			updateSourceListener();
+		}
+	}
+
+	protected void updateSearch(final SearchRule rule, final boolean paused) {
+		getSearchEngine().setRule(rule);
+	}
+
+	protected void updateSelection() {
+		if (type == FIND_NEXT) {
+			getSearchEngine().selectNext();
+		} else if (type == FIND_PREVIOUS) {
+			getSearchEngine().selectPrev();
+		}
+	}
+
+	protected boolean setDescription(final TextSourceMaker descriptor) {
+		// ignore panel controls
+		if (descriptor != null && panel != null && panel.getControl() != null) {
+			if (isParent(panel.getControl(), descriptor.getControl()))
+				return false;
+		}
+		// ignore the same source
+		if (descriptor == null) {
+			if (this.creator == null)
+				return false;
+		} else if (descriptor.equals(this.creator)) {
+			return false;
+		}
+		if (source != null) {
+			source.dispose();
+			source = null;
+			this.creator = null;
+		}
+		if (descriptor != null && descriptor.isValid()) {
+			this.creator = descriptor;
+			source = new UITextSource(descriptor.create(),
+					descriptor.getControl());
+			getSearchEngine().setSource(rule, source, true);
+			source.init();
+			updateIndexingState();
+		}
+		return true;
+	}
+
+	private void updateIndexingState() {
+		if (monitor != null) {
+			monitor.setCanceled(true);
+			monitor = null;
+		}
+
+		final boolean indexRequired = source != null && !source.isDisposed()
+				&& source.isIndexRequired();
+		if (GlancePlugin.getDefault().getPreferenceStore()
+				.getBoolean(IPreferenceConstants.PANEL_AUTO_INDEXING)
+				&& indexRequired) {
+			index();
+		} else {
+			panel.setIndexingState(indexRequired ? ISearchPanel.INDEXING_STATE_INITIAL
+					: ISearchPanel.INDEXING_STATE_DISABLE);
+		}
+	}
+
+	public void index() {
+		if (panel != null && source != null && !source.isDisposed()) {
+			monitor = new SearchProgressMonitor(panel);
+			new Thread() {
+				@Override
+				public void run() {
+					panel.setIndexingState(ISearchPanel.INDEXING_STATE_IN_PROGRESS);
+					if (source != null) {
+						source.index(monitor);
+					}
+				}
+			}.start();
+		}
+	}
+
+	private IProgressMonitor monitor;
+
+	protected void updateEnabling() {
+		if (panel != null) {
+			panel.setEnabled(source != null);
+			if (source == null) {
+				panel.setIndexingState(ISearchPanel.INDEXING_STATE_FINISHED);
+			}
+		}
+	}
+
+	protected boolean isParent(final Control parent, Control child) {
+		while (child != null) {
+			if (child.equals(parent))
+				return true;
+			child = child.getParent();
+		}
+		return false;
+	}
+
+	private SearchEngine getSearchEngine() {
+		if (engine == null) {
+			engine = new SearchEngine(searchListener);
+			engine.start();
+		}
+		return engine;
+	}
+
+	private void updateSourceListener() {
+		if (sourceListener == null) {
+			sourceListener = new SourceListener();
+			TextSourceManager.getInstance().addSourceProviderListener(
+					sourceListener);
+		}
+	}
+
+	private class SearchListener implements ISearchListener {
+
+		public void allFound(final Match[] matches) {
+			if (panel != null)
+				panel.allFound(matches);
+		}
+
+		public void finished() {
+			if (panel != null)
+				panel.finished();
+		}
+
+		public void firstFound(final Match match) {
+			if (panel != null)
+				panel.firstFound(match);
+		}
+	}
+
+	private class SourceListener implements ISourceProviderListener {
+		public void sourceChanged(final TextSourceMaker source) {
+			update(source, false);
+		}
+	}
+
+	private class SearchPanelListener implements ISearchPanelListener {
+
+		public SearchPanelListener(final ISearchPanel panel) {
+			this.panel = panel;
+		}
+
+		protected boolean isCurrent() {
+			return panel.equals(SearchManager.this.panel);
+		}
+
+		public void ruleChanged(final SearchRule rule) {
+			if (isCurrent())
+				find(rule, SearchManager.FIND_HERE);
+		}
+
+		public void findNext() {
+			if (isCurrent())
+				find(rule, SearchManager.FIND_NEXT);
+		}
+
+		public void findPrevious() {
+			if (isCurrent())
+				find(rule, SearchManager.FIND_PREVIOUS);
+		}
+
+		public void close() {
+			dispose(panel);
+		}
+
+		public void indexCanceled() {
+			if (monitor != null) {
+				monitor.setCanceled(true);
+				monitor = null;
+			}
+		}
+
+		private final ISearchPanel panel;
+
+	}
+
+	private static SearchManager manager;
+
+	private SearchManager() {
+		panels = new ArrayList<ISearchPanel>();
+		searchListener = new SearchListener();
+		panelToListener = new HashMap<ISearchPanel, SearchPanelListener>();
+	}
+
+	private final SearchListener searchListener;
+	private SourceListener sourceListener;
+	private final Map<ISearchPanel, SearchPanelListener> panelToListener;
+	private SearchEngine engine;
+	private final List<ISearchPanel> panels;
+	private ISearchPanel panel;
+	private ITextSource source;
+	private TextSourceMaker creator;
+
+	private int type;
+	private SearchRule rule;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchProgressMonitor.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchProgressMonitor.java
new file mode 100644
index 0000000..927483f
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchProgressMonitor.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.internal.search;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import org.eclipse.ui.glance.panels.ISearchPanel;
+
+public class SearchProgressMonitor implements IProgressMonitor {
+
+	private final ISearchPanel panel;
+	private double total = 1;
+	private double current;
+	private boolean cancel;
+
+	public SearchProgressMonitor(ISearchPanel panel) {
+		this.panel = panel;
+	}
+
+	public void beginTask(String name, int totalWork) {
+		total = totalWork;
+		panel.newTask(name);
+		current = 0;
+	}
+
+	public void done() {
+		if (isCanceled()) {
+			return;
+		}
+		panel.setIndexingState(ISearchPanel.INDEXING_STATE_FINISHED);
+	}
+
+	public void internalWorked(double work) {
+		if (isCanceled()) {
+			return;
+		}
+		current += work / total;
+		if (current > 1) {
+			current = 1;
+		}
+		panel.updateIndexingPercent(current);
+	}
+
+	public boolean isCanceled() {
+		return cancel || panel.getControl().isDisposed();
+	}
+
+	public void setCanceled(boolean value) {
+		this.cancel = value;
+		panel.setIndexingState(ISearchPanel.INDEXING_STATE_FINISHED);
+	}
+
+	public void setTaskName(String name) {
+	}
+
+	public void subTask(String name) {
+	}
+
+	public void worked(int work) {
+		internalWorked(work);
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchRule.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchRule.java
new file mode 100644
index 0000000..741d9ec
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchRule.java
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.internal.search;
+
+import java.util.regex.Pattern;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+
+import org.eclipse.ui.glance.internal.GlancePlugin;
+import org.eclipse.ui.glance.internal.preferences.IPreferenceConstants;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class SearchRule implements IPreferenceConstants {
+
+	/**
+	 * @param text
+	 * @param caseSensitive
+	 * @param prefix
+	 */
+	public SearchRule(String text) {
+		this.text = text;
+		loadFromPref();
+	}
+
+	/**
+	 * @param text
+	 * @param caseSensitive
+	 * @param prefix
+	 */
+	public SearchRule(String text, boolean caseSensitive, boolean camelCase,
+			boolean wordPrefix, boolean regExp) {
+		this.text = text;
+		this.caseSensitive = caseSensitive;
+		this.camelCase = camelCase;
+		this.wordPrefix = wordPrefix;
+		this.regExp = regExp;
+	}
+
+	/**
+	 * @return the text
+	 */
+	public String getText() {
+		return text;
+	}
+
+	/**
+	 * @return the caseSensitive
+	 */
+	public boolean isCaseSensitive() {
+		return caseSensitive;
+	}
+
+	/**
+	 * @return the regExp
+	 */
+	public boolean isRegExp() {
+		return regExp;
+	}
+
+	/**
+	 * @return the prefix
+	 */
+	public boolean isWordPrefix() {
+		return wordPrefix;
+	}
+
+	/**
+	 * @return the camelCase
+	 */
+	public boolean isCamelCase() {
+		return camelCase;
+	}
+
+	/**
+	 * @return the pattern
+	 */
+	public Pattern getPattern() {
+		if (pattern == null) {
+			pattern = SearchUtils.createPattern(text, caseSensitive, regExp,
+					wordPrefix, camelCase);
+		}
+		return pattern;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((text == null) ? 0 : text.hashCode());
+		result = prime * result + (caseSensitive ? 1231 : 1237);
+		result = prime * result + (regExp ? 1231 : 1237);
+		result = prime * result + (wordPrefix ? 1231 : 1237);
+		result = prime * result + (camelCase ? 1231 : 1237);
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		SearchRule other = (SearchRule) obj;
+		if (!isSettingsEqual(other))
+			return false;
+		return isTextEquals(other);
+	}
+
+	public boolean isSettingsEqual(SearchRule other) {
+		if (other == null)
+			return false;
+		if (caseSensitive != other.caseSensitive)
+			return false;
+		if (regExp != other.regExp)
+			return false;
+		if (wordPrefix != other.wordPrefix)
+			return false;
+		if (camelCase != other.camelCase)
+			return false;
+		return true;
+	}
+
+	public boolean isTextEquals(SearchRule other) {
+		if (other == null)
+			return false;
+		if (text == null) {
+			if (other.text != null)
+				return false;
+		} else if (!text.equals(other.text))
+			return false;
+		return true;
+	}
+
+	private void loadFromPref() {
+		IPreferenceStore preferences = GlancePlugin.getDefault()
+				.getPreferenceStore();
+		caseSensitive = preferences.getBoolean(SEARCH_CASE_SENSITIVE);
+		regExp = preferences.getBoolean(SEARCH_REGEXP);
+		wordPrefix = preferences.getBoolean(SEARCH_WORD_PREFIX);
+		camelCase = preferences.getBoolean(SEARCH_CAMEL_CASE);
+	}
+
+	private Pattern pattern;
+	private String text;
+	private boolean caseSensitive;
+	private boolean regExp;
+	private boolean wordPrefix;
+	private boolean camelCase;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchScopeEntry.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchScopeEntry.java
new file mode 100644
index 0000000..0ecde65
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchScopeEntry.java
@@ -0,0 +1,137 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.internal.search;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.ITextBlockListener;
+import org.eclipse.ui.glance.sources.Match;
+import org.eclipse.ui.glance.sources.TextChangedEvent;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class SearchScopeEntry implements ITextBlockListener, Comparable<SearchScopeEntry> {
+
+	public SearchScopeEntry(ITextBlock block, IMatchListener listener) {
+		this.block = block;
+		this.listener = listener;
+		init();
+	}
+	
+	public void setStart(int start) {
+		synchronized (MONITOR) {
+			this.start = start;
+		}
+	}
+	
+	public int getStart() {
+		synchronized (MONITOR) {
+			return start;
+		}
+	}
+
+	protected void addMatch(Match match) {
+		synchronized (MONITOR) {
+			matches.add(nextMatchIndex, match);
+			nextMatchIndex++;
+			listener.added(this, match);
+		}
+	}
+
+	public void dispose() {
+		block.removeTextBlockListener(this);
+	}
+
+	interface IMatchListener {
+
+		public void added(SearchScopeEntry entry, Match match);
+
+		public void cleared(SearchScopeEntry entry);
+	}
+
+	public void textChanged(TextChangedEvent event) {
+		clear();
+		listener.cleared(this);
+	}
+	
+	public int compareTo(SearchScopeEntry entry) {
+		return block.compareTo(entry.block);
+	}
+
+	protected void init() {
+		clear();
+		block.addTextBlockListener(this);
+	}
+	
+	protected void clear() {
+		synchronized (MONITOR) {
+			doClear();
+		}
+	}
+
+	protected void doClear() {
+		matches = new ArrayList<Match>();
+		text = block.getText();
+		nextMatchIndex = 0;
+	}
+	
+	protected void addMatchToBegin() {
+		synchronized (MONITOR) {
+			nextMatchIndex = 0;
+		}
+	}
+	
+	/**
+	 * @return the text
+	 */
+	public String getText() {
+		synchronized (MONITOR) {
+			return text;
+		}
+	}
+	
+	/**
+	 * @return the matches
+	 */
+	public List<Match> getMatches() {
+		synchronized (MONITOR) {
+			return matches;
+		}
+	}
+	
+	/**
+	 * @return the block
+	 */
+	public ITextBlock getBlock() {
+		return block;
+	}
+	
+	/**
+	 * @return the listener
+	 */
+	public IMatchListener getListener() {
+		return listener;
+	}
+
+	private Object MONITOR = new Object();
+	private IMatchListener listener;
+	private String text;
+	private ITextBlock block;
+	private int start;
+	private int nextMatchIndex;
+	private List<Match> matches;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchUtils.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchUtils.java
new file mode 100644
index 0000000..95626d9
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/search/SearchUtils.java
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.internal.search;
+
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class SearchUtils {
+
+	public static Pattern createPattern(String pattern, boolean caseSensitive,
+			boolean regExSearch, boolean wordPrefix, boolean camelCase) {
+		if (pattern == null || pattern.length() == 0)
+			return null;
+
+		int patternFlags = 0;
+
+		if (!caseSensitive)
+			patternFlags |= Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE;
+
+		if (regExSearch) {
+			patternFlags |= Pattern.MULTILINE;
+			pattern = substituteLinebreak(pattern);
+		} else {
+			RegExpBulder builder = camelCase ? new CamelCaseBuilder()
+					: new RegExpBulder();
+			pattern = asRegPattern(pattern, builder);
+			if (wordPrefix) {
+				pattern = "\\b" + pattern; //$NON-NLS-1$
+			}
+		}
+
+		return Pattern.compile(pattern, patternFlags);
+	}
+
+	/**
+	 * Substitutes \R in a regex find pattern with (?>\r\n?|\n)
+	 * 
+	 * @param findString
+	 *            the original find pattern
+	 * @return the transformed find pattern
+	 * @throws PatternSyntaxException
+	 *             if \R is added at an illegal position (e.g. in a character
+	 *             set)
+	 */
+	private static String substituteLinebreak(String findString)
+			throws PatternSyntaxException {
+		int length = findString.length();
+		StringBuffer buf = new StringBuffer(length);
+
+		int inCharGroup = 0;
+		int inBraces = 0;
+		boolean inQuote = false;
+		for (int i = 0; i < length; i++) {
+			char ch = findString.charAt(i);
+			switch (ch) {
+			case '[':
+				buf.append(ch);
+				if (!inQuote)
+					inCharGroup++;
+				break;
+
+			case ']':
+				buf.append(ch);
+				if (!inQuote)
+					inCharGroup--;
+				break;
+
+			case '{':
+				buf.append(ch);
+				if (!inQuote && inCharGroup == 0)
+					inBraces++;
+				break;
+
+			case '}':
+				buf.append(ch);
+				if (!inQuote && inCharGroup == 0)
+					inBraces--;
+				break;
+
+			case '\\':
+				if (i + 1 < length) {
+					char ch1 = findString.charAt(i + 1);
+					if (inQuote) {
+						if (ch1 == 'E')
+							inQuote = false;
+						buf.append(ch).append(ch1);
+						i++;
+
+					} else if (ch1 == 'R') {
+						if (inCharGroup > 0 || inBraces > 0) {
+							throw new PatternSyntaxException(
+									"Illegal position for \\R", findString, i);
+						}
+						buf.append("(?>\\r\\n?|\\n)"); //$NON-NLS-1$
+						i++;
+
+					} else {
+						if (ch1 == 'Q') {
+							inQuote = true;
+						}
+						buf.append(ch).append(ch1);
+						i++;
+					}
+				} else {
+					buf.append(ch);
+				}
+				break;
+
+			default:
+				buf.append(ch);
+				break;
+			}
+
+		}
+		return buf.toString();
+	}
+
+	/**
+	 * Converts a non-regex string to a pattern that can be used with the regex
+	 * search engine.
+	 * 
+	 * @param string
+	 *            the non-regex pattern
+	 * @return the string converted to a regex pattern
+	 */
+	private static String asRegPattern(String string, RegExpBulder builder) {
+		StringBuffer out = new StringBuffer(string.length());
+		boolean quoting = false;
+
+		for (int i = 0, length = string.length(); i < length; i++) {
+			char ch = string.charAt(i);
+			String re = builder.asRegExp(ch);
+			if (re != null) {
+				if (quoting) {
+					out.append("\\E"); //$NON-NLS-1$
+					quoting = false;
+				}
+				out.append(re); //$NON-NLS-1$
+				continue;
+			}
+			if (!quoting) {
+				out.append("\\Q"); //$NON-NLS-1$
+				quoting = true;
+			}
+			out.append(ch);
+		}
+		if (quoting)
+			out.append("\\E"); //$NON-NLS-1$
+
+		return out.toString();
+	}
+
+	private static class RegExpBulder {
+
+		public String asRegExp(char ch) {
+			if (ch == '\\') {
+				return "\\\\";
+			}
+			return null;
+		}
+
+	}
+
+	private static class CamelCaseBuilder extends RegExpBulder {
+
+		@Override
+		public String asRegExp(char ch) {
+			String regExp = super.asRegExp(ch);
+			if (regExp != null) {
+				wordPrev = false;
+			} else {
+				boolean word = isWord(ch);
+				if (word && wordPrev) {
+					regExp = CAMEL_CASE_SKIP + ch;
+				}
+				wordPrev = word;
+			}
+			return regExp;
+		}
+
+		private boolean isWord(char ch) {
+			return ch == '_' || Character.isLetterOrDigit(ch);
+		}
+
+		private boolean wordPrev = false;
+
+	}
+
+	private static final String CAMEL_CASE_SKIP = "\\w*";
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/sources/ISourceProviderListener.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/sources/ISourceProviderListener.java
new file mode 100644
index 0000000..1a47986
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/sources/ISourceProviderListener.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.internal.sources;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public interface ISourceProviderListener {
+
+	public void sourceChanged(TextSourceMaker description);
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/sources/TextSourceListener.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/sources/TextSourceListener.java
new file mode 100644
index 0000000..0daa2cf
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/sources/TextSourceListener.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.internal.sources;
+
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.PlatformUI;
+
+import org.eclipse.ui.glance.sources.ITextSourceDescriptor;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class TextSourceListener implements Listener {
+
+	public TextSourceListener(ITextSourceDescriptor[] descriptors) {
+		this.descriptors = descriptors;
+	}
+
+	public void addSourceProviderListener(ISourceProviderListener listener) {
+		if (listeners.size() == 0) {
+			getDisplay().addFilter(SWT.FocusIn, this);
+		}
+		listeners.add(listener);
+	}
+
+	public void removeSourceProviderListener(ISourceProviderListener listener) {
+		listeners.remove(listener);
+		if (listeners.size() == 0) {
+			getDisplay().removeFilter(SWT.FocusIn, this);
+			selection = null;
+		}
+	}
+
+	public void handleEvent(Event event) {
+		TextSourceMaker creator = getCreator(getDisplay().getFocusControl());
+		if (!creator.equals(selection)) {
+			selection = creator;
+			Object[] objects = listeners.getListeners();
+			for (Object object : objects) {
+				ISourceProviderListener listener = (ISourceProviderListener) object;
+				listener.sourceChanged(selection);
+			}
+		}
+	}
+
+	public TextSourceMaker getSelection() {
+		return getCreator(getDisplay().getFocusControl());
+	}
+
+	private TextSourceMaker getCreator(Control control) {
+		return new TextSourceMaker(getDescriptor(control), control);
+	}
+
+	private ITextSourceDescriptor getDescriptor(Control control) {
+		if (control != null) {
+			for (ITextSourceDescriptor descriptor : descriptors) {
+				if (descriptor.isValid(control)) {
+					return descriptor;
+				}
+			}
+		}
+		return null;
+	}
+
+	private Display getDisplay() {
+		return PlatformUI.getWorkbench().getDisplay();
+	}
+
+	private TextSourceMaker selection;
+	private ListenerList listeners = new ListenerList();
+	private ITextSourceDescriptor[] descriptors;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/sources/TextSourceMaker.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/sources/TextSourceMaker.java
new file mode 100644
index 0000000..f65acef
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/sources/TextSourceMaker.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.internal.sources;
+
+import org.eclipse.swt.widgets.Control;
+
+import org.eclipse.ui.glance.sources.ITextSource;
+import org.eclipse.ui.glance.sources.ITextSourceDescriptor;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class TextSourceMaker {
+
+	public TextSourceMaker(ITextSourceDescriptor description, Control control) {
+		this.description = description;
+		this.control = control;
+	}
+
+	public boolean isValid() {
+		return description == null ? false : description.isValid(control);
+	}
+
+	public ITextSource create() {
+		return description == null ? null : description.createSource(control);
+	}
+
+	/**
+	 * @return the control
+	 */
+	public Control getControl() {
+		return control;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((control == null) ? 0 : control.hashCode());
+		result = prime * result
+				+ ((description == null) ? 0 : description.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		TextSourceMaker other = (TextSourceMaker) obj;
+		if (control == null) {
+			if (other.control != null)
+				return false;
+		} else if (!control.equals(other.control))
+			return false;
+		if (description == null) {
+			if (other.description != null)
+				return false;
+		} else if (!description.equals(other.description))
+			return false;
+		return true;
+	}
+
+	private ITextSourceDescriptor description;
+	private Control control;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/sources/TextSourceManager.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/sources/TextSourceManager.java
new file mode 100644
index 0000000..297ce85
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/sources/TextSourceManager.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.internal.sources;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.Platform;
+
+import org.eclipse.ui.glance.internal.GlancePlugin;
+import org.eclipse.ui.glance.sources.ITextSourceDescriptor;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class TextSourceManager {
+
+    public static TextSourceManager getInstance() {
+        if (instance == null)
+            instance = new TextSourceManager();
+        return instance;
+    }
+
+    public void addSourceProviderListener(ISourceProviderListener listener) {
+        assert listener != null;
+        getListener().addSourceProviderListener(listener);
+    }
+
+    public void removeSourceProviderListener(ISourceProviderListener listener) {
+        getListener().removeSourceProviderListener(listener);
+    }
+
+    public TextSourceMaker getSource() {
+        return getListener().getSelection();
+    }
+
+    private TextSourceListener getListener() {
+        if (listener == null) {
+            listener = new TextSourceListener(getDescriptors());
+        }
+        return listener;
+    }
+
+    private ITextSourceDescriptor[] getDescriptors() {
+        if (descriptors == null) {
+
+            List<Class<?>> excludedClasses = new ArrayList<Class<?>>();
+
+            IConfigurationElement[] excludedConfigs = Platform.getExtensionRegistry()
+                .getConfigurationElementsFor(EXT_EXCLUDED_SOURCES);
+
+            for (IConfigurationElement config : excludedConfigs) {
+                try {
+                    ITextSourceDescriptor descriptor = (ITextSourceDescriptor) config
+                        .createExecutableExtension(ATTR_CLASS);
+                    excludedClasses.add(descriptor.getClass());
+                } catch (Exception e) {
+                    GlancePlugin.log(e);
+                }
+            }
+
+            IConfigurationElement[] configs = Platform.getExtensionRegistry().getConfigurationElementsFor(
+                EXT_SOURCES);
+            List<ITextSourceDescriptor> list = new ArrayList<ITextSourceDescriptor>();
+            final Map<ITextSourceDescriptor, Integer> prior = new HashMap<ITextSourceDescriptor, Integer>();
+
+            for (IConfigurationElement config : configs) {
+                try {
+                    ITextSourceDescriptor descriptor = (ITextSourceDescriptor) config
+                        .createExecutableExtension(ATTR_CLASS);
+                    if (excludedClasses.contains(descriptor.getClass())) {
+                        continue;
+                    }
+                    int priority = toInt(config.getAttribute(ATTR_PRIORITY));
+                    list.add(descriptor);
+                    prior.put(descriptor, priority);
+                } catch (Exception e) {
+                    GlancePlugin.log(e);
+                }
+            }
+
+            Collections.sort(list, new Comparator<ITextSourceDescriptor>() {
+
+                public int compare(ITextSourceDescriptor o1, ITextSourceDescriptor o2) {
+                    return prior.get(o2) - prior.get(o1);
+                }
+
+            });
+            descriptors = list.toArray(new ITextSourceDescriptor[list.size()]);
+        }
+        return descriptors;
+    }
+
+    private int toInt(String priority) {
+        try {
+            if (priority == null || priority.length() == 0)
+                return 1;
+            return Integer.parseInt(priority);
+        } catch (Exception e) {
+            GlancePlugin.log(e);
+        }
+        return 1;
+    }
+
+    private TextSourceManager() {
+    }
+
+    private final static String ATTR_PRIORITY = "priority";
+    private final static String ATTR_CLASS = "class";
+    private final static String EXT_SOURCES = GlancePlugin.PLUGIN_ID + ".sources";
+    private final static String EXT_EXCLUDED_SOURCES = GlancePlugin.PLUGIN_ID + ".excludedSources";
+
+    private static TextSourceManager instance;
+
+    private TextSourceListener listener;
+    private ITextSourceDescriptor[] descriptors;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/viewers/ColoredTextViewerBlock.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/viewers/ColoredTextViewerBlock.java
new file mode 100644
index 0000000..cae9917
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/viewers/ColoredTextViewerBlock.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.internal.viewers;
+
+import org.eclipse.jface.text.ITextPresentationListener;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.jface.text.TextViewer;
+
+import org.eclipse.ui.glance.sources.Match;
+import org.eclipse.ui.glance.sources.TextChangedEvent;
+import org.eclipse.ui.glance.utils.TextUtils;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class ColoredTextViewerBlock extends TextViewerBlock implements
+		ITextPresentationListener {
+
+	/**
+	 * @param viewer
+	 */
+	public ColoredTextViewerBlock(TextViewer viewer) {
+		super(viewer);
+	}
+
+	@Override
+	protected void addListeners() {
+		viewer.addTextPresentationListener(this);
+		super.addListeners();
+	}
+
+	@Override
+	public void dispose() {
+		super.dispose();
+		refresh();
+	}
+
+	@Override
+	protected void fireTextChanged(TextChangedEvent changedEvent) {
+		matches = Match.EMPTY;
+		super.fireTextChanged(changedEvent);
+	}
+
+	@Override
+	protected void removeListeners() {
+		super.removeListeners();
+		viewer.removeTextPresentationListener(this);
+	}
+
+	public void setMatches(Match[] matches) {
+		this.matches = matches;
+		refresh();
+	}
+	
+	public void setSelected(Match selected) {
+        this.selected = selected;
+        refresh();
+    }
+	
+	
+	public Match getSelected() {
+        return selected;
+    }
+	
+	private void refresh() {
+		viewer.invalidateTextPresentation();
+	}
+
+	public void applyTextPresentation(TextPresentation presentation) {
+		TextUtils.applyStyles(presentation, matches, selected);
+	}
+
+	private Match[] matches = Match.EMPTY;
+	private Match selected = null;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/viewers/SourceViewerControl.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/viewers/SourceViewerControl.java
new file mode 100644
index 0000000..f7d0c17
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/viewers/SourceViewerControl.java
@@ -0,0 +1,189 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.internal.viewers;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.TextSelection;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.IAnnotationModelExtension;
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.swt.graphics.Point;
+
+import org.eclipse.ui.glance.controls.text.styled.TextSelector;
+import org.eclipse.ui.glance.sources.BaseTextSource;
+import org.eclipse.ui.glance.sources.ColorManager;
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.ITextSourceListener;
+import org.eclipse.ui.glance.sources.Match;
+import org.eclipse.ui.glance.sources.SourceSelection;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class SourceViewerControl extends BaseTextSource implements ISelectionChangedListener {
+
+	public static String ANNOTATION_TYPE = ColorManager.ANNOTATION_ID;
+
+	public static String SELECTED_ANNOTATION_TYPE = ColorManager.ANNOTATION_SELECTED_ID;
+
+	public SourceViewerControl(final SourceViewer viewer) {
+		this.viewer = viewer;
+		listeners = new ListenerList();
+		blocks = new TextViewerBlock[] { new TextViewerBlock(viewer) };
+	}
+
+	public void addTextSourceListener(final ITextSourceListener listener) {
+		listeners.add(listener);
+	}
+
+	public void removeTextSourceListener(final ITextSourceListener listener) {
+		listeners.add(listener);
+	}
+
+	public void dispose() {
+		if (!disposed) {
+			selector.dispose();
+			select(null);
+			viewer.removeSelectionChangedListener(this);
+			replaceMatches(Match.EMPTY);
+			getBlock().dispose();
+			disposed = true;
+		}
+	}
+
+	public void selectionChanged(final SelectionChangedEvent event) {
+		final ISelection selection = event.getSelection();
+		if (selection instanceof TextSelection) {
+			final TextSelection tSelection = (TextSelection) selection;
+			final SourceSelection sSelection = new SourceSelection(getBlock(),
+					tSelection.getOffset(), tSelection.getLength());
+			final Object[] objects = listeners.getListeners();
+			for (final Object object : objects) {
+				final ITextSourceListener listener = (ITextSourceListener) object;
+				listener.selectionChanged(sSelection);
+			}
+		}
+	}
+
+	public boolean isDisposed() {
+		return disposed;
+	}
+
+	public TextViewerBlock getBlock() {
+		return blocks[0];
+	}
+
+	public ITextBlock[] getBlocks() {
+		return blocks;
+	}
+
+	public SourceSelection getSelection() {
+		final Point selection = viewer.getSelectedRange();
+		return new SourceSelection(getBlock(), selection.x, selection.y);
+	}
+
+	public void select(final Match match) {
+		final Annotation[] remove = getAnnotations(true);
+		final Map<Annotation, Position> add = match != null ? createAnnotations(
+				new Match[] { match }, true)
+				: new HashMap<Annotation, Position>();
+		final IAnnotationModel model = viewer.getAnnotationModel();
+		if (model instanceof IAnnotationModelExtension) {
+			final IAnnotationModelExtension eModel = (IAnnotationModelExtension) model;
+			eModel.replaceAnnotations(remove, add);
+		} else {
+			for (final Annotation annotation : remove) {
+				model.removeAnnotation(annotation);
+			}
+			for (final Annotation annotation : add.keySet()) {
+				model.addAnnotation(annotation, add.get(annotation));
+			}
+		}
+
+		selector.setMatch(match);
+	}
+
+	public void show(final Match[] matches) {
+		replaceMatches(matches);
+	}
+
+	private void replaceMatches(final Match[] matches) {
+		final Annotation[] remove = getAnnotations(false);
+		final Map<Annotation, Position> add = createAnnotations(matches, false);
+		final IAnnotationModel model = viewer.getAnnotationModel();
+		if (model instanceof IAnnotationModelExtension) {
+			final IAnnotationModelExtension eModel = (IAnnotationModelExtension) model;
+			eModel.replaceAnnotations(remove, add);
+		} else {
+			for (final Annotation annotation : remove) {
+				model.removeAnnotation(annotation);
+			}
+			for (final Annotation annotation : add.keySet()) {
+				model.addAnnotation(annotation, add.get(annotation));
+			}
+		}
+	}
+
+	private Map<Annotation, Position> createAnnotations(final Match[] matches,
+			final boolean selected) {
+		final Map<Annotation, Position> map = new HashMap<Annotation, Position>();
+		for (final Match match : matches) {
+			final Annotation annotation = new Annotation(
+					selected ? SELECTED_ANNOTATION_TYPE : ANNOTATION_TYPE,
+					false, null);
+			final Position position = new Position(match.getOffset(),
+					match.getLength());
+			map.put(annotation, position);
+		}
+		return map;
+	}
+
+	private Annotation[] getAnnotations(final boolean selected) {
+		final String type = selected ? SELECTED_ANNOTATION_TYPE
+				: ANNOTATION_TYPE;
+		final IAnnotationModel model = viewer.getAnnotationModel();
+		final List<Annotation> annotations = new ArrayList<Annotation>();
+		if (model != null) {
+			final Iterator<?> it = model.getAnnotationIterator();
+			while (it.hasNext()) {
+				final Annotation annotation = (Annotation) it.next();
+				if (type.equals(annotation.getType())) {
+					annotations.add(annotation);
+				}
+			}
+		}
+		return annotations.toArray(new Annotation[annotations.size()]);
+	}
+
+	@Override
+	public void init() {
+		selector = new ViewerSelector(viewer);
+		viewer.addSelectionChangedListener(this);
+	}
+
+	private TextSelector selector;
+	private final ListenerList listeners;
+	private boolean disposed;
+	private final TextViewerBlock[] blocks;
+	private final SourceViewer viewer;
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/viewers/TextViewerBlock.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/viewers/TextViewerBlock.java
new file mode 100644
index 0000000..278e953
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/viewers/TextViewerBlock.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.internal.viewers;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.text.ITextListener;
+import org.eclipse.jface.text.TextEvent;
+import org.eclipse.jface.text.TextViewer;
+
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.ITextBlockListener;
+import org.eclipse.ui.glance.sources.TextChangedEvent;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class TextViewerBlock implements ITextBlock, ITextListener {
+
+    public TextViewerBlock(TextViewer viewer) {
+        this.viewer = viewer;
+        listeners = new ListenerList();
+        addListeners();
+    }
+
+    public void dispose() {
+        removeListeners();
+    }
+
+    protected void addListeners() {
+        if (!addFirstListener()) {
+            viewer.addTextListener(this);
+        }
+    }
+
+    /**
+     * Add our text listener to the beginning of the listener list thro
+     * reflection. This method return true if this operation succeed and fail
+     * otherwise.
+     */
+    private boolean addFirstListener() {
+        try {
+            Field filed = TextViewer.class.getDeclaredField("fTextListeners");
+            filed.setAccessible(true);
+            @SuppressWarnings("unchecked")
+            List<Object> list = (List<Object>) filed.get(viewer);
+            if (list == null) {
+                // no listeners, can add it usual way
+                return false;
+            }
+            if (!list.contains(this)) {
+                list.add(0, this);
+            }
+            return true;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    protected void removeListeners() {
+        viewer.removeTextListener(this);
+    }
+
+    public void textChanged(TextEvent event) {
+        if (event.getDocumentEvent() != null) {
+            TextChangedEvent changedEvent = new TextChangedEvent(event.getOffset(), event.getLength(),
+                event.getReplacedText());
+            fireTextChanged(changedEvent);
+        }
+    }
+
+    protected void fireTextChanged(TextChangedEvent changedEvent) {
+        Object[] objects = listeners.getListeners();
+        for (Object object : objects) {
+            ITextBlockListener listener = (ITextBlockListener) object;
+            listener.textChanged(changedEvent);
+        }
+    }
+
+    public void addTextBlockListener(ITextBlockListener listener) {
+        listeners.add(listener);
+    }
+
+    public String getText() {
+        return viewer.getDocument().get();
+    }
+
+    public void removeTextBlockListener(ITextBlockListener listener) {
+        listeners.remove(listener);
+    }
+
+    public int compareTo(ITextBlock o) {
+        return 0;
+    }
+
+    private ListenerList listeners;
+    protected TextViewer viewer;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/viewers/TextViewerControl.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/viewers/TextViewerControl.java
new file mode 100644
index 0000000..5a46293
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/viewers/TextViewerControl.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.internal.viewers;
+
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.text.TextSelection;
+import org.eclipse.jface.text.TextViewer;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.swt.graphics.Point;
+
+import org.eclipse.ui.glance.controls.text.styled.TextSelector;
+import org.eclipse.ui.glance.sources.BaseTextSource;
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.ITextSourceListener;
+import org.eclipse.ui.glance.sources.Match;
+import org.eclipse.ui.glance.sources.SourceSelection;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class TextViewerControl extends BaseTextSource implements
+		ISelectionChangedListener {
+
+	public TextViewerControl(final TextViewer viewer) {
+		this.viewer = viewer;
+		listeners = new ListenerList();
+		blocks = new ColoredTextViewerBlock[] { new ColoredTextViewerBlock(
+				viewer) };
+	}
+
+	public void addTextSourceListener(final ITextSourceListener listener) {
+		listeners.add(listener);
+	}
+
+	public void removeTextSourceListener(final ITextSourceListener listener) {
+		listeners.remove(listener);
+	}
+
+	public void dispose() {
+		if (!disposed) {
+			selector.dispose();
+			viewer.removeSelectionChangedListener(this);
+			getBlock().dispose();
+			disposed = true;
+		}
+	}
+
+	public void selectionChanged(final SelectionChangedEvent event) {
+		final ISelection selection = event.getSelection();
+		if (selection instanceof TextSelection) {
+			final TextSelection tSelection = (TextSelection) selection;
+			final SourceSelection sSelection = new SourceSelection(getBlock(),
+					tSelection.getOffset(), tSelection.getLength());
+			final Object[] objects = listeners.getListeners();
+			for (final Object object : objects) {
+				final ITextSourceListener listener = (ITextSourceListener) object;
+				listener.selectionChanged(sSelection);
+			}
+		}
+	}
+
+	public boolean isDisposed() {
+		return disposed;
+	}
+
+	public ColoredTextViewerBlock getBlock() {
+		return blocks[0];
+	}
+
+	public ITextBlock[] getBlocks() {
+		return blocks;
+	}
+
+	public SourceSelection getSelection() {
+		final Point selection = viewer.getSelectedRange();
+		return new SourceSelection(getBlock(), selection.x, selection.y);
+	}
+
+	public void select(final Match match) {
+		getBlock().setSelected(match);
+		selector.setMatch(match);
+	}
+
+	public void show(final Match[] matches) {
+		getBlock().setMatches(matches);
+	}
+
+	@Override
+	public void init() {
+		selector = new ViewerSelector(viewer);
+		viewer.addSelectionChangedListener(this);
+	}
+
+	private TextSelector selector;
+	private final ListenerList listeners;
+	private boolean disposed;
+	private final ColoredTextViewerBlock[] blocks;
+	private final TextViewer viewer;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/viewers/ViewerSelector.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/viewers/ViewerSelector.java
new file mode 100644
index 0000000..1fc073b
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/internal/viewers/ViewerSelector.java
@@ -0,0 +1,52 @@
+/*******************************************************************************

+ * Copyright (c) 2017 Exyte

+ * 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:

+ *     Yuri Strot - initial API and Implementation

+ ******************************************************************************/

+package org.eclipse.ui.glance.internal.viewers;

+

+import org.eclipse.jface.text.Region;

+import org.eclipse.jface.text.TextSelection;

+import org.eclipse.jface.text.TextViewer;

+import org.eclipse.swt.custom.StyledText;

+

+import org.eclipse.ui.glance.controls.text.styled.TextSelector;

+

+public class ViewerSelector extends TextSelector {

+

+	private final TextViewer viewer;

+	private final StyledText control;

+

+	public ViewerSelector(TextViewer viewer) {

+		this.viewer = viewer;

+		this.control = viewer.getTextWidget();

+		init();

+	}

+

+	@Override

+	protected StyledText getControl() {

+		return control;

+	}

+

+	@Override

+	protected void setSelection(int offset, int length) {

+		viewer.setSelection(new TextSelection(offset, length));

+	}

+

+	@Override

+	protected Region getSelection() {

+		TextSelection selection = (TextSelection) viewer.getSelection();

+		return new Region(selection.getOffset(), selection.getLength());

+	}

+

+	@Override

+	protected void reveal(int offset, int length) {

+		viewer.revealRange(offset, length);

+	}

+

+}

diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/panels/ISearchPanel.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/panels/ISearchPanel.java
new file mode 100644
index 0000000..9d1566c
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/panels/ISearchPanel.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.panels;
+
+import org.eclipse.swt.widgets.Control;
+
+import org.eclipse.ui.glance.internal.search.ISearchListener;
+import org.eclipse.ui.glance.internal.search.SearchRule;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public interface ISearchPanel extends ISearchListener {
+
+	public static int INDEXING_STATE_DISABLE = 0;
+
+	public static int INDEXING_STATE_INITIAL = 1;
+
+	public static int INDEXING_STATE_IN_PROGRESS = 2;
+
+	public static int INDEXING_STATE_FINISHED = 3;
+
+	public void addPanelListener(ISearchPanelListener listener);
+
+	public void removePanelListener(ISearchPanelListener listener);
+
+	public void setEnabled(boolean enabled);
+
+	public boolean isApplicable(Control control);
+
+	public Control getControl();
+
+	public void setIndexingState(int state);
+
+	public void updateIndexingPercent(double percent);
+
+	public void newTask(String name);
+
+	/**
+	 * Set focus to search panel with some initial text
+	 */
+	public void setFocus(String text);
+
+	public SearchRule getRule();
+
+	public void closePanel();
+
+	public void findNext();
+
+	public void findPrevious();
+
+	public void clearHistory();
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/panels/ISearchPanelListener.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/panels/ISearchPanelListener.java
new file mode 100644
index 0000000..f8c8270
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/panels/ISearchPanelListener.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.panels;
+
+import org.eclipse.ui.glance.internal.search.SearchRule;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public interface ISearchPanelListener {
+
+	public void ruleChanged(SearchRule rule);
+
+	public void findNext();
+
+	public void findPrevious();
+
+	public void close();
+
+	public void indexCanceled();
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/panels/SearchPanel.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/panels/SearchPanel.java
new file mode 100644
index 0000000..aeec1ea
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/panels/SearchPanel.java
@@ -0,0 +1,680 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.panels;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.PopupDialog;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+
+import org.eclipse.ui.glance.internal.GlanceEventDispatcher;
+import org.eclipse.ui.glance.internal.GlancePlugin;
+import org.eclipse.ui.glance.internal.panels.CheckAction;
+import org.eclipse.ui.glance.internal.panels.ImageAnimation;
+import org.eclipse.ui.glance.internal.preferences.IPreferenceConstants;
+import org.eclipse.ui.glance.internal.search.SearchManager;
+import org.eclipse.ui.glance.internal.search.SearchRule;
+import org.eclipse.ui.glance.sources.Match;
+import org.eclipse.ui.glance.utils.SelectionAdapter;
+import org.eclipse.ui.glance.utils.UIUtils;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public abstract class SearchPanel implements ISearchPanel,
+		IPreferenceConstants, IPropertyChangeListener {
+
+	/**
+	 * @param parent
+	 * @param style
+	 */
+	public SearchPanel() {
+		rule = new SearchRule("");
+		getPreferences().addPropertyChangeListener(this);
+	}
+
+	public void setIndexingState(final int state) {
+		indexState = state;
+
+		if (updateInfoThread != null) {
+			final UpdateInfoThread thread = updateInfoThread;
+			thread.requestStop();
+			updateInfoThread = null;
+		}
+		if (state == INDEXING_STATE_IN_PROGRESS) {
+			indexPercent = 0;
+			try {
+				updateInfoThread = new UpdateInfoThread();
+				updateInfoThread.start();
+			} catch (final CoreException e) {
+				GlancePlugin.log(e.getStatus());
+			}
+		} else {
+			UIUtils.asyncExec(toolBar, new Runnable() {
+				public void run() {
+					updateInfo(null);
+				}
+			});
+		}
+	}
+
+	public void updateIndexingPercent(final double percent) {
+		indexPercent = percent;
+	}
+
+	public void newTask(final String name) {
+		this.taskName = name;
+		indexPercent = 0;
+	}
+
+	private static URL getWaitImageStream() throws CoreException {
+		URL url = FileLocator.find(GlancePlugin.getDefault().getBundle(),
+				new Path(GlancePlugin.IMG_WAIT), null);
+		try {
+			url = FileLocator.resolve(url);
+			return url;
+		} catch (final IOException e) {
+			throw new CoreException(GlancePlugin.createStatus(
+					"Can't find wait image", e));
+		}
+	}
+
+	private Color getWaitBGColor() {
+		final Display display = PlatformUI.getWorkbench().getDisplay();
+		final Color[] color = new Color[1];
+		display.syncExec(new Runnable() {
+			public void run() {
+				color[0] = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
+			}
+		});
+		return color[0];
+	}
+
+	private class UpdateInfoThread extends ImageAnimation {
+
+		public UpdateInfoThread() throws CoreException {
+			super(getWaitImageStream(), getWaitBGColor());
+		}
+
+		private volatile boolean stop = false;
+
+		@Override
+		protected boolean isTerminated() {
+			return stop || indexState != INDEXING_STATE_IN_PROGRESS;
+		}
+
+		@Override
+		protected void updateImage(final Image image) {
+			updateInfo(image);
+		}
+
+		public void requestStop() {
+			stop = true;
+		}
+	}
+
+	private void updateInfo(final Image image) {
+		if (bIndexing == null || bIndexing.isDisposed()) {
+			return;
+		}
+
+		if (indexState == INDEXING_STATE_DISABLE) {
+			bIndexing.setToolTipText("Index component");
+			bIndexing.setSelection(false);
+			bIndexing.setEnabled(false);
+			if (bIndexing.getImage() == null) {
+				bIndexing.setImage(GlancePlugin
+						.getImage(GlancePlugin.IMG_START_INDEXING));
+			}
+		} else if (indexState == INDEXING_STATE_INITIAL) {
+			bIndexing.setToolTipText("Index component");
+			bIndexing.setSelection(false);
+			bIndexing.setImage(GlancePlugin
+					.getImage(GlancePlugin.IMG_START_INDEXING));
+			bIndexing.setEnabled(true);
+		} else if (indexState == INDEXING_STATE_FINISHED) {
+			bIndexing.setToolTipText("Index finished");
+			bIndexing.setSelection(false);
+			bIndexing.setEnabled(false);
+		} else {
+			final StringBuffer buffer = new StringBuffer();
+			bIndexing.setSelection(true);
+			bIndexing.setImage(image);
+			if (taskName != null && taskName.length() > 0) {
+				buffer.append(taskName);
+				buffer.append(": ");
+			}
+			buffer.append((int) (indexPercent * 100));
+			buffer.append("%. Stop indexing.");
+			bIndexing.setToolTipText(buffer.toString());
+		}
+	}
+
+	public void propertyChange(final PropertyChangeEvent event) {
+		final String property = event.getProperty();
+		if (property != null && property.startsWith(SEARCH_PREFIX)) {
+			updateRule();
+		}
+	}
+
+	public void createContent(final Composite parent) {
+		final Composite container = createContainer(parent);
+		final GridLayout layout = new GridLayout(3, false);
+		layout.verticalSpacing = 0;
+		layout.marginHeight = 2;
+		layout.marginWidth = 0;
+		container.setLayout(layout);
+		createIcon(container);
+		createText(container, SWT.BORDER);
+		createToolBar(container);
+		initSize(container);
+	}
+
+	public Control getControl() {
+		return container;
+	}
+
+	protected Composite createContainer(final Composite parent) {
+		container = new Composite(parent, SWT.NONE);
+		return container;
+	}
+
+	public void firstFound(final Match match) {
+		UIUtils.asyncExec(title, new Runnable() {
+			public void run() {
+				setBackground(match != null);
+			}
+		});
+	}
+
+	public void allFound(final Match[] matches) {
+		result = matches;
+		UIUtils.asyncExec(title, new Runnable() {
+			public void run() {
+				setBackground(result.length > 0);
+			}
+		});
+	}
+
+	public void finished() {
+	}
+
+	protected Label createIcon(final Composite parent) {
+		final Label label = new Label(parent, SWT.NONE);
+		label.setImage(GlancePlugin.getImage(GlancePlugin.IMG_SEARCH));
+		return label;
+	}
+
+	protected Control createText(final Composite parent, final int style) {
+		title = new Combo(parent, style | SWT.DROP_DOWN);
+		final String text = rule.getText();
+		title.setText(text);
+		loadHistory();
+		if (text != null && text.length() > 0 && result.length == 0)
+			setBackground(false);
+		title.addModifyListener(modifyListener);
+		title.addListener(SWT.KeyDown, new Listener() {
+			public void handleEvent(Event event) {
+				GlanceEventDispatcher.INSTANCE.dispatchKeyPressed(event);
+			}
+		});
+		title.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+		title.setEnabled(titleEnabled);
+		return title;
+	}
+
+	protected ToolBar createToolBar(final Composite parent) {
+		toolBar = new ToolBar(parent, SWT.FLAT);
+		GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER)
+				.applyTo(toolBar);
+		if (getPreferences().getBoolean(PANEL_DIRECTIONS)) {
+			createNextItem(toolBar);
+			createPreviousItem(toolBar);
+		}
+		createIndexing(toolBar);
+		createSettingsMenu(toolBar);
+		if (getPreferences().getBoolean(PANEL_CLOSE)) {
+			createClose(toolBar);
+		}
+		return toolBar;
+	}
+
+	protected ToolItem createNextItem(final ToolBar bar) {
+		bNext = createTool(bar, "Next", GlancePlugin.IMG_NEXT,
+				new SelectionAdapter() {
+					@Override
+					public void selected(final SelectionEvent e) {
+						findNext();
+					}
+				});
+		return bNext;
+	}
+
+	protected ToolItem createPreviousItem(final ToolBar bar) {
+		bPrev = createTool(bar, "Previous", GlancePlugin.IMG_PREV,
+				new SelectionAdapter() {
+					@Override
+					public void selected(final SelectionEvent e) {
+						findPrevious();
+					}
+				});
+		return bPrev;
+	}
+
+	private void createIndexing(final ToolBar bar) {
+		bIndexing = new ToolItem(bar, SWT.CHECK);
+		bIndexing.setDisabledImage(GlancePlugin
+				.getImage(GlancePlugin.IMG_INDEXING_FINISHED));
+		bIndexing.addSelectionListener(new SelectionAdapter() {
+			@Override
+			public void selected(final SelectionEvent e) {
+				if (indexState == INDEXING_STATE_INITIAL) {
+					SearchManager.getIntance().index();
+				} else if (indexState != INDEXING_STATE_FINISHED) {
+					fireIndexCanceled();
+				}
+			}
+		});
+	}
+
+	private ToolItem createTool(final ToolBar bar, final String tip,
+			final String image, final SelectionListener listener) {
+		final ToolItem item = new ToolItem(bar, SWT.PUSH);
+		item.setToolTipText(tip);
+		item.setImage(GlancePlugin.getImage(image));
+		item.addSelectionListener(listener);
+		return item;
+	}
+
+	protected ToolItem createSettingsMenu(final ToolBar bar) {
+		final ToolItem settings = new ToolItem(bar, SWT.PUSH);
+		settings.setImage(JFaceResources.getImage(PopupDialog.POPUP_IMG_MENU));
+		settings.setDisabledImage(JFaceResources
+				.getImage(PopupDialog.POPUP_IMG_MENU_DISABLED));
+		settings.setToolTipText("Settings"); //$NON-NLS-1$
+		settings.addSelectionListener(new SelectionAdapter() {
+			@Override
+			public void selected(final SelectionEvent e) {
+				showSettings();
+			}
+		});
+		return settings;
+	}
+
+	protected ToolItem createClose(final ToolBar bar) {
+		final ToolItem close = new ToolItem(bar, SWT.PUSH);
+		final ImageDescriptor image = PlatformUI.getWorkbench()
+				.getSharedImages()
+				.getImageDescriptor(ISharedImages.IMG_TOOL_DELETE);
+		if (image != null)
+			close.setImage(image.createImage());
+		close.setToolTipText("Close"); //$NON-NLS-1$
+		close.addSelectionListener(new SelectionAdapter() {
+			@Override
+			public void selected(final SelectionEvent e) {
+				closePanel();
+			}
+		});
+		return close;
+	}
+
+	protected void showSettings() {
+		final MenuManager manager = new MenuManager();
+		fillMenu(manager);
+		final Menu menu = manager.createContextMenu(getControl());
+
+		final Point location = getControl().getDisplay().getCursorLocation();
+		menu.setLocation(location);
+		menu.setVisible(true);
+	}
+
+	protected void fillMenu(final IMenuManager menu) {
+		menu.add(new Separator());
+		newAction(menu, SEARCH_CASE_SENSITIVE, LABEL_CASE_SENSITIVE, true);
+		final boolean regExp = newAction(menu, SEARCH_REGEXP, LABEL_REGEXP,
+				true).isChecked();
+		newAction(menu, SEARCH_CAMEL_CASE, LABEL_CAMEL_CASE, !regExp);
+		newAction(menu, SEARCH_WORD_PREFIX, LABEL_WORD_PREFIX, !regExp);
+		menu.add(new Separator());
+		menu.add(new Action("Clear search history") {
+			@Override
+			public void run() {
+				clearHistory();
+			}
+		});
+		menu.add(new Separator());
+		menu.add(new Action("Preferences...") {
+			@Override
+			public void run() {
+				PreferencesUtil.createPreferenceDialogOn(container.getShell(),
+						PREFERENCE_PAGE_ID, null, null).open();
+			}
+		});
+	}
+
+	private CheckAction newAction(final IMenuManager menu, final String name,
+			final String label, final boolean enable) {
+		return newAction(menu, name, label, enable, null);
+	}
+
+	private CheckAction newAction(final IMenuManager menu, final String name,
+			final String label, final boolean enable, final String path) {
+		final CheckAction action = new CheckAction(name, label);
+		if (path != null) {
+			action.setImageDescriptor(GlancePlugin.getImageDescriptor(path));
+		}
+		action.setEnabled(enable);
+		menu.add(action);
+		return action;
+	}
+
+	protected void textChanged() {
+		final String text = title.getText();
+		final boolean empty = text.length() == 0;
+		if (empty)
+			textEmpty();
+		if (bNext != null && !bNext.isDisposed())
+			bNext.setEnabled(!empty);
+		if (bPrev != null && !bPrev.isDisposed())
+			bPrev.setEnabled(!empty);
+		updateRule();
+	}
+
+	protected void textEmpty() {
+		setBackground(true);
+	}
+
+	protected void updateRule() {
+		rule = new SearchRule(title.getText());
+		fireRuleChanged(rule);
+	}
+
+	/**
+	 * @return the rule
+	 */
+	public SearchRule getRule() {
+		return rule;
+	}
+
+	public void setFocus(String text) {
+		if (isReady()) {
+			if (text == null || text.length() == 0)
+				text = rule.getText();
+			if (text != null && text.length() > 0) {
+				title.setText(text);
+				title.setSelection(new Point(0, text.length()));
+				textChanged();
+			}
+			title.forceFocus();
+		}
+	}
+
+	public void setEnabled(final boolean enabled) {
+		if (isReady()) {
+			title.setEnabled(enabled);
+			titleEnabled = enabled;
+		}
+	}
+
+	private boolean isReady() {
+		return title != null && !title.isDisposed();
+	}
+
+	public void addPanelListener(final ISearchPanelListener listener) {
+		listeners.add(listener);
+	}
+
+	public void removePanelListener(final ISearchPanelListener listener) {
+		listeners.remove(listener);
+	}
+
+	private void fireRuleChanged(final SearchRule rule) {
+		historyDirty = true;
+		final Object[] objects = listeners.getListeners();
+		for (final Object object : objects) {
+			final ISearchPanelListener listener = (ISearchPanelListener) object;
+			listener.ruleChanged(rule);
+		}
+	}
+
+	protected void fireIndexCanceled() {
+		final Object[] objects = listeners.getListeners();
+		for (final Object object : objects) {
+			final ISearchPanelListener listener = (ISearchPanelListener) object;
+			listener.indexCanceled();
+		}
+	}
+
+	public void findNext() {
+		updateHistory();
+		final Object[] objects = listeners.getListeners();
+		for (final Object object : objects) {
+			final ISearchPanelListener listener = (ISearchPanelListener) object;
+			listener.findNext();
+		}
+	}
+
+	public void findPrevious() {
+		updateHistory();
+		final Object[] objects = listeners.getListeners();
+		for (final Object object : objects) {
+			final ISearchPanelListener listener = (ISearchPanelListener) object;
+			listener.findPrevious();
+		}
+	}
+
+	public void clearHistory() {
+		if (title != null && !title.isDisposed()) {
+			title.removeModifyListener(modifyListener);
+			try {
+				title.removeAll();
+				findHistory.clear();
+			} finally {
+				title.addModifyListener(modifyListener);
+				historyDirty = false;
+			}
+		}
+	}
+
+	protected void fireClose() {
+		updateHistory();
+		saveHistory();
+		final Object[] objects = listeners.getListeners();
+		for (final Object object : objects) {
+			final ISearchPanelListener listener = (ISearchPanelListener) object;
+			listener.close();
+		}
+		getPreferences().removePropertyChangeListener(this);
+		if (updateInfoThread != null) {
+			updateInfoThread.requestStop();
+		}
+	}
+
+	/**
+	 * @return the preferedWidth
+	 */
+	protected int getPreferedWidth() {
+		return preferredWidth;
+	}
+
+	/**
+	 * @return the preferredHeight
+	 */
+	protected int getPreferredHeight() {
+		return preferredHeight;
+	}
+
+	private void initSize(final Composite composite) {
+		final Point size = composite.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+		preferredWidth = size.x;
+		preferredHeight = size.y;
+		preferredWidth -= title.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
+		final int widthInChars = getPreferences().getInt(PANEL_TEXT_SIZE);
+		preferredWidth += getTextWidth(title, widthInChars) + 15;
+	}
+
+	protected int getTextWidth(final Control control, final int width) {
+		final GC gc = new GC(control);
+		try {
+			gc.setFont(title.getFont());
+			return gc.getFontMetrics().getAverageCharWidth() * width;
+		} finally {
+			gc.dispose();
+		}
+	}
+
+	/**
+	 * Updates history.
+	 */
+	private void updateHistory() {
+		if (title != null && !title.isDisposed() && historyDirty) {
+			title.removeModifyListener(modifyListener);
+			try {
+				String findString = title.getText();
+				final Point sel = title.getSelection();
+				findString = fixItem(findString);
+				if (findString != null) {
+					findHistory.remove(findString);
+					findHistory.add(0, findString);
+					title.removeAll();
+					for (final String string : findHistory) {
+						title.add(string);
+					}
+					title.setText(findString);
+					title.setSelection(sel);
+				}
+			} finally {
+				title.addModifyListener(modifyListener);
+				historyDirty = false;
+			}
+		}
+	}
+
+	private void saveHistory() {
+		final StringBuffer buffer = new StringBuffer();
+		for (int i = 0; i < findHistory.size() && i < 8; i++) {
+			final String item = findHistory.get(i);
+			if (i > 0) {
+				buffer.append("\n");
+			}
+			buffer.append(item);
+		}
+		getPreferences().putValue(HISTORY, buffer.toString());
+	}
+
+	private void loadHistory() {
+		findHistory = new ArrayList<String>();
+		final String content = getPreferences().getString(HISTORY);
+		final String[] items = content.split("\n");
+		for (final String item : items) {
+			findHistory.add(item);
+			title.add(item);
+		}
+	}
+
+	private String fixItem(final String item) {
+		if (item.length() == 0) {
+			return null;
+		}
+		final int index = item.indexOf("\n");
+		if (index == 0) {
+			return null;
+		} else if (index > 0) {
+			return item.substring(0, index);
+		} else {
+			return item;
+		}
+	}
+
+	private IPreferenceStore getPreferences() {
+		return GlancePlugin.getDefault().getPreferenceStore();
+	}
+
+	protected void setBackground(final boolean found) {
+		title.setBackground(found ? GOOD_COLOR : BAD_COLOR);
+	}
+
+	protected static final Color GOOD_COLOR = Display.getDefault()
+			.getSystemColor(SWT.COLOR_WHITE);
+	protected static final Color BAD_COLOR = new Color(Display.getDefault(),
+			255, 102, 102);
+
+	protected Composite container;
+	protected Combo title;
+	private boolean titleEnabled = true;
+
+	private final ListenerList listeners = new ListenerList();
+	private final ModifyListener modifyListener = new ModifyListener() {
+		public void modifyText(final ModifyEvent e) {
+			textChanged();
+		}
+	};
+
+	private List<String> findHistory;
+	private boolean historyDirty = true;
+	private ToolItem bNext;
+	private ToolItem bPrev;
+	private ToolItem bIndexing;
+	private ToolBar toolBar;
+	private SearchRule rule;
+	private Match[] result = Match.EMPTY;
+
+	private double indexState;
+	private double indexPercent;
+	private String taskName;
+
+	private int preferredHeight;
+	private int preferredWidth;
+
+	private UpdateInfoThread updateInfoThread;
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/BaseTextSource.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/BaseTextSource.java
new file mode 100644
index 0000000..fc5a203
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/BaseTextSource.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.sources;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+public abstract class BaseTextSource implements ITextSource {
+
+	public void index(IProgressMonitor monitor) {
+		monitor.done();
+	}
+
+	public boolean isIndexRequired() {
+		return false;
+	}
+	
+	public void init() {
+	}
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ColorManager.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ColorManager.java
new file mode 100644
index 0000000..43dc946
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ColorManager.java
@@ -0,0 +1,135 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.sources;
+
+import static org.eclipse.jface.preference.PreferenceConverter.getColor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.editors.text.EditorsUI;
+
+import org.eclipse.ui.glance.internal.GlancePlugin;
+import org.eclipse.ui.glance.internal.preferences.IPreferenceConstants;
+import org.eclipse.ui.glance.internal.preferences.TreeColors;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class ColorManager implements IPropertyChangeListener, IPreferenceConstants {
+
+	public static final String ANNOTATION_ID = "org.eclipse.ui.glance.highlight";
+	public static final String ANNOTATION_SELECTED_ID = "org.eclipse.ui.glance.select";
+
+	private ColorManager() {
+		getStore().addPropertyChangeListener(this);
+		GlancePlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this);
+		updateColors();
+	}
+
+	public static IPreferenceStore getStore() {
+		return EditorsUI.getPreferenceStore();
+	}
+
+	public static ColorManager getInstance() {
+		if (INSTANCE == null)
+			INSTANCE = new ColorManager();
+		return INSTANCE;
+	}
+
+	public Color getBackgroundColor() {
+		return selection;
+	}
+
+	public Color getSelectedBackgroundColor() {
+		return highlight;
+	}
+
+	public Color getTreeSelectionBg() {
+		return treeBg;
+	}
+
+	public Color getTreeSelectionFg() {
+		return treeFg;
+	}
+
+	public boolean isUseNative() {
+		return useNative;
+	}
+
+	public void propertyChange(final PropertyChangeEvent event) {
+		if (COLOR_HIGHLIGHT.equals(event.getProperty()) || COLOR_SELECTION.equals(event.getProperty())) {
+			updateColors();
+		}
+	}
+
+	public static Color lighten(Color color, int delta) {
+		int r = ensureColor(color.getRed() + delta);
+		int g = ensureColor(color.getGreen() + delta);
+		int b = ensureColor(color.getBlue() + delta);
+		return new Color(color.getDevice(), r, g, b);
+	}
+
+	private static int ensureColor(int value) {
+		return value > 255 ? 255 : value;
+	}
+
+	private void updateColors() {
+		for (Color color : toDispose) {
+			color.dispose();
+		}
+		toDispose = new ArrayList<Color>();
+
+		final Display display = PlatformUI.getWorkbench().getDisplay();
+		final IPreferenceStore store = getStore();
+
+		selection = new Color(display, getColor(store, COLOR_HIGHLIGHT));
+		highlight = new Color(display, getColor(store, COLOR_SELECTION));
+
+		TreeColors colors = TreeColors.getDefault();
+		useNative = colors.isUseNative();
+		if (colors.getBg() != null) {
+			treeBg = new Color(display, colors.getBg());
+			toDispose.add(treeBg);
+		} else {
+			treeBg = display.getSystemColor(SWT.COLOR_LIST_SELECTION);
+		}
+
+		if (colors.getFg() != null) {
+			treeFg = new Color(display, colors.getFg());
+			toDispose.add(treeFg);
+		} else {
+			treeFg = display.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT);
+		}
+
+		toDispose.add(selection);
+		toDispose.add(highlight);
+	}
+
+	private static ColorManager INSTANCE;
+
+	private Color selection;
+	private Color highlight;
+	private Color treeBg;
+	private Color treeFg;
+	private boolean useNative;
+
+	private List<Color> toDispose = new ArrayList<Color>();
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ConfigurationManager.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ConfigurationManager.java
new file mode 100644
index 0000000..21e0359
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ConfigurationManager.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.sources;
+
+import org.eclipse.ui.glance.internal.GlancePlugin;
+import org.eclipse.ui.glance.internal.preferences.IPreferenceConstants;
+
+public final class ConfigurationManager {
+
+	private static ConfigurationManager INSTANCE;
+
+	private ConfigurationManager() {
+	}
+
+	public static ConfigurationManager getInstance() {
+		if (INSTANCE == null) {
+			INSTANCE = new ConfigurationManager();
+		}
+		return INSTANCE;
+	}
+
+	public int getMaxIndexingDepth() {
+		return GlancePlugin.getDefault().getPreferenceStore().getInt(
+				IPreferenceConstants.PANEL_MAX_INDEXING_DEPTH);
+	}
+	
+	public boolean incremenstalSearch(){
+	    return GlancePlugin.getDefault().getPreferenceStore().getBoolean(
+            IPreferenceConstants.SEARCH_INCREMENTAL);
+	}
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ITextBlock.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ITextBlock.java
new file mode 100644
index 0000000..13b75fc
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ITextBlock.java
@@ -0,0 +1,26 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.sources;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public interface ITextBlock extends Comparable<ITextBlock> {
+
+	public String getText();
+
+	public void addTextBlockListener(ITextBlockListener listener);
+
+	public void removeTextBlockListener(ITextBlockListener listener);
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ITextBlockListener.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ITextBlockListener.java
new file mode 100644
index 0000000..c3f8729
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ITextBlockListener.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.sources;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public interface ITextBlockListener {
+
+	public void textChanged(TextChangedEvent event);
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ITextSource.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ITextSource.java
new file mode 100644
index 0000000..3824354
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ITextSource.java
@@ -0,0 +1,84 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.sources;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public interface ITextSource {
+
+	/**
+	 * Return text blocks associated with this source
+	 * 
+	 * @return text blocks
+	 */
+	public ITextBlock[] getBlocks();
+
+	/**
+	 * Add text source listener
+	 * 
+	 * @param listener
+	 *            text source listener
+	 */
+	public void addTextSourceListener(ITextSourceListener listener);
+
+	/**
+	 * Remove text source listener
+	 * 
+	 * @param listener
+	 *            text source listener
+	 */
+	public void removeTextSourceListener(ITextSourceListener listener);
+
+	/**
+	 * Return current source selection. This selection using to identify where
+	 * start search
+	 * 
+	 * @return source selection
+	 */
+	public SourceSelection getSelection();
+
+	/**
+	 * Focus match
+	 * 
+	 * @param match
+	 *            match to focus
+	 */
+	public void select(Match match);
+
+	/**
+	 * Highlight matches
+	 * 
+	 * @param matches
+	 */
+	public void show(Match[] matches);
+
+	/**
+	 * Called before search started
+	 */
+	public void init();
+	
+	/**
+	 * @param monitor
+	 */
+	public void index(IProgressMonitor monitor);
+
+	public boolean isIndexRequired();
+
+	public boolean isDisposed();
+
+	public void dispose();
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ITextSourceDescriptor.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ITextSourceDescriptor.java
new file mode 100644
index 0000000..e54cee5
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ITextSourceDescriptor.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.sources;
+
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public interface ITextSourceDescriptor {
+
+	/**
+	 * Return a boolean indicating whether text source can be created for this
+	 * control
+	 * 
+	 * @return <code>true</code> if the text source can be created, and
+	 *         <code>false</code> otherwise
+	 */
+	public boolean isValid(Control control);
+
+	/**
+	 * Creates text source for specified control
+	 * 
+	 * @param control
+	 * @return
+	 */
+	public ITextSource createSource(Control control);
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ITextSourceListener.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ITextSourceListener.java
new file mode 100644
index 0000000..e608808
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/ITextSourceListener.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.sources;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public interface ITextSourceListener {
+
+	/**
+	 * Notification about block changing
+	 * 
+	 * @param removed
+	 * @param added
+	 */
+	public void blocksChanged(ITextBlock[] removed, ITextBlock[] added);
+
+	/**
+	 * Notification about all blocks removed and added abother blocks
+	 * 
+	 * @param newBlocks
+	 */
+	public void blocksReplaced(ITextBlock[] newBlocks);
+
+	/**
+	 * Notification about selection changing
+	 * 
+	 * @param selection
+	 */
+	public void selectionChanged(SourceSelection selection);
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/Match.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/Match.java
new file mode 100644
index 0000000..22eb62e
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/Match.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.sources;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class Match implements Comparable<Match> {
+
+	public static final Match[] EMPTY = new Match[0];
+
+	public Match(ITextBlock block, int offset, int length) {
+		this.block = block;
+		this.offset = offset;
+		this.length = length;
+	}
+
+	/**
+	 * @return the block
+	 */
+	public ITextBlock getBlock() {
+		return block;
+	}
+
+	/**
+	 * @return the offset
+	 */
+	public int getOffset() {
+		return offset;
+	}
+
+	/**
+	 * @return the length
+	 */
+	public int getLength() {
+		return length;
+	}
+
+	public int compareTo(Match match) {
+		if (block != null && match.block != null) {
+			int diff = block.compareTo(match.block);
+			if (diff != 0)
+				return diff;
+		}
+		return offset - match.offset;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((block == null) ? 0 : block.hashCode());
+		result = prime * result + length;
+		result = prime * result + offset;
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		Match other = (Match) obj;
+		if (block == null) {
+			if (other.block != null)
+				return false;
+		} else if (!block.equals(other.block))
+			return false;
+		if (length != other.length)
+			return false;
+		if (offset != other.offset)
+			return false;
+		return true;
+	}
+
+	private ITextBlock block;
+	private int offset;
+	private int length;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/SourceSelection.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/SourceSelection.java
new file mode 100644
index 0000000..627707c
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/SourceSelection.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.sources;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class SourceSelection {
+
+	public SourceSelection(ITextBlock block, int offset, int length) {
+		this.block = block;
+		this.offset = offset;
+		this.length = length;
+	}
+
+	/**
+	 * @return the block
+	 */
+	public ITextBlock getBlock() {
+		return block;
+	}
+
+	/**
+	 * @return the length
+	 */
+	public int getLength() {
+		return length;
+	}
+
+	/**
+	 * @return the offset
+	 */
+	public int getOffset() {
+		return offset;
+	}
+
+	@Override
+	public String toString() {
+		return block + ": (" + offset + ", " + length + ")";
+	}
+
+	private ITextBlock block;
+	private int offset;
+	private int length;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/TextChangedEvent.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/TextChangedEvent.java
new file mode 100644
index 0000000..1e29dbc
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/sources/TextChangedEvent.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.sources;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class TextChangedEvent {
+
+	public TextChangedEvent(int start, int length, String replacedText) {
+		this.start = start;
+		this.length = length;
+		this.replacedText = replacedText;
+	}
+
+	/**
+	 * @return the start
+	 */
+	public int getStart() {
+		return start;
+	}
+
+	/**
+	 * @return the length
+	 */
+	public int getLength() {
+		return length;
+	}
+
+	/**
+	 * @return the replacedText
+	 */
+	public String getReplacedText() {
+		return replacedText;
+	}
+
+	/** start offset of the new text */
+	private int start;
+	/** length of the new text */
+	private int length;
+	/** replaced text or empty string if no text was replaced */
+	private String replacedText;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/utils/SelectionAdapter.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/utils/SelectionAdapter.java
new file mode 100644
index 0000000..9f8e8b9
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/utils/SelectionAdapter.java
@@ -0,0 +1,33 @@
+/******************************************************************************* 
+ * Copyright (c) 2017 Exyte  
+ * 
+ * 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: 
+ *     Yuri Strot - initial API and Implementation 
+ *******************************************************************************/
+package org.eclipse.ui.glance.utils;
+
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public abstract class SelectionAdapter implements SelectionListener {
+
+	public final void widgetDefaultSelected(SelectionEvent e) {
+		selected(e);
+	}
+
+	public final void widgetSelected(SelectionEvent e) {
+		selected(e);
+	}
+
+	public abstract void selected(SelectionEvent e);
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/utils/TextUtils.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/utils/TextUtils.java
new file mode 100644
index 0000000..a82f5b2
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/utils/TextUtils.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.utils;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.TextPresentation;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.graphics.Color;
+
+import org.eclipse.ui.glance.sources.ColorManager;
+import org.eclipse.ui.glance.sources.Match;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class TextUtils {
+
+	public static StyleRange[] copy(final StyleRange[] ranges) {
+		final StyleRange[] result = new StyleRange[ranges.length];
+		for (int i = 0; i < result.length; i++) {
+			result[i] = copy(ranges[i]);
+		}
+		return result;
+	}
+
+	public static StyleRange copy(final StyleRange range) {
+		final StyleRange result = new StyleRange(range);
+		result.start = range.start;
+		result.length = range.length;
+		result.fontStyle = range.fontStyle;
+		return result;
+	}
+
+	public static StyleRange[] getStyles(final TextPresentation presentation) {
+		final StyleRange[] ranges = new StyleRange[presentation
+				.getDenumerableRanges()];
+		final Iterator<?> e = presentation.getAllStyleRangeIterator();
+		for (int i = 0; e.hasNext(); i++) {
+			ranges[i] = (StyleRange) e.next();
+		}
+		return ranges;
+	}
+
+	public static void applyStyles(final TextPresentation presentation,
+			final Match[] matches, final Match selected) {
+
+        final StyleRange[] ranges = createStyleRanges(presentation.getExtent(), matches, ColorManager
+            .getInstance().getBackgroundColor());
+
+        presentation.mergeStyleRanges(ranges);
+
+        if (selected != null) {
+            final StyleRange[] selectedRanges = createStyleRanges(presentation.getExtent(),
+                new Match[] { selected }, ColorManager.getInstance().getSelectedBackgroundColor());
+            presentation.mergeStyleRanges(selectedRanges);
+
+        }
+	}
+
+    private static StyleRange[] createStyleRanges(final IRegion region, final Match[] matches,
+        final Color color) {
+        final Match[] regionMatches = getRangeMatches(region.getOffset(), region.getLength(), matches);
+        final StyleRange[] ranges = new StyleRange[regionMatches.length];
+        for (int i = 0; i < regionMatches.length; i++) {
+            final StyleRange range = new StyleRange(regionMatches[i].getOffset(),
+                regionMatches[i].getLength(), null, color);
+            ranges[i] = range;
+        }
+        return ranges;
+    }
+
+	private static Match[] getRangeMatches(final int start, final int length,
+			final Match[] matches) {
+		int from = getPosition(start, matches);
+		if (from >= matches.length)
+			return Match.EMPTY;
+		if (from > 0) {
+			final Match border = matches[from - 1];
+			if (border.getLength() + border.getOffset() > start)
+				from--;
+		}
+		final int to = getPosition(start + length, matches) - 1;
+		if (from <= to) {
+			final Match[] result = new Match[to - from + 1];
+			System.arraycopy(matches, from, result, 0, result.length);
+			return result;
+		}
+		return Match.EMPTY;
+	}
+
+	private static int getPosition(final int offset, final Match[] matches) {
+		final int index = Arrays.binarySearch(matches, new Match(null, offset, 0));
+		if (index >= 0)
+			return index;
+		return -index - 1;
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/utils/UITextBlock.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/utils/UITextBlock.java
new file mode 100644
index 0000000..5cc8ac6
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/utils/UITextBlock.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.utils;
+
+import org.eclipse.core.runtime.ListenerList;
+
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.ITextBlockListener;
+import org.eclipse.ui.glance.sources.TextChangedEvent;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class UITextBlock implements ITextBlock, ITextBlockListener {
+
+	public UITextBlock(ITextBlock block) {
+		this.block = block;
+		text = block.getText();
+		block.addTextBlockListener(this);
+	}
+
+	public void dispose() {
+		block.removeTextBlockListener(this);
+	}
+
+	/**
+	 * @return the block
+	 */
+	public ITextBlock getBlock() {
+		return block;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.glance.sources.ITextBlock#getText()
+	 */
+	public String getText() {
+		return text;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.glance.sources.ITextBlock#addTextBlockListener(org.eclipse.ui
+	 * .glance.sources.ITextBlockListener)
+	 */
+	public void addTextBlockListener(ITextBlockListener listener) {
+		listeners.add(listener);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.glance.sources.ITextBlock#removeTextBlockListener(org.eclipse.ui
+	 * .glance.sources.ITextBlockListener)
+	 */
+	public void removeTextBlockListener(ITextBlockListener listener) {
+		listeners.remove(listener);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.glance.sources.ITextBlockListener#textChanged(org.eclipse.ui.
+	 * glance.sources.TextChangedEvent)
+	 */
+	public void textChanged(TextChangedEvent event) {
+		text = block.getText();
+		Object[] objects = listeners.getListeners();
+		for (Object object : objects) {
+			ITextBlockListener listener = (ITextBlockListener) object;
+			listener.textChanged(event);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.lang.Comparable#compareTo(java.lang.Object)
+	 */
+	public int compareTo(ITextBlock block) {
+		return this.block.compareTo(((UITextBlock) block).block);
+	}
+
+	@Override
+	public String toString() {
+		return "UI(" + block + ")";
+	}
+
+	private ListenerList listeners = new ListenerList();
+	private ITextBlock block;
+	private String text;
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/utils/UITextSource.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/utils/UITextSource.java
new file mode 100644
index 0000000..df2b0ca
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/utils/UITextSource.java
@@ -0,0 +1,268 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.ui.glance.utils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.swt.widgets.Control;
+
+import org.eclipse.ui.glance.sources.ITextBlock;
+import org.eclipse.ui.glance.sources.ITextSource;
+import org.eclipse.ui.glance.sources.ITextSourceListener;
+import org.eclipse.ui.glance.sources.Match;
+import org.eclipse.ui.glance.sources.SourceSelection;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class UITextSource implements ITextSource, ITextSourceListener {
+
+    public UITextSource(final ITextSource source, final Control control) {
+        this.source = source;
+        this.control = control;
+
+        blocks = new ArrayList<UITextBlock>();
+        blockToBlock = new HashMap<ITextBlock, UITextBlock>();
+        listeners = new ListenerList();
+        source.addTextSourceListener(this);
+        addBlocks(source.getBlocks());
+        updateSelection();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.ui.glance.sources.ITextSource#getSelection()
+     */
+    public SourceSelection getSelection() {
+        return selection;
+    }
+    
+    public Control getControl() {
+        return control;
+    }
+
+    public boolean isIndexRequired() {
+        return source.isIndexRequired();
+    }
+
+    public void dispose() {
+        synchronized (blocks) {
+            for (final UITextBlock block : blocks) {
+                block.dispose();
+            }
+        }
+        source.removeTextSourceListener(this);
+        source.dispose();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.ui.glance.sources.ITextSource#isDisposed()
+     */
+    public boolean isDisposed() {
+        return source.isDisposed();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.ui.glance.sources.ITextSource#getBlocks()
+     */
+    public ITextBlock[] getBlocks() {
+        return blocks.toArray(new ITextBlock[blocks.size()]);
+    }
+
+    public void index(final IProgressMonitor monitor) {
+        source.index(monitor);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.eclipse.ui.glance.sources.ITextSource#select(org.eclipse.ui.glance.sources
+     * .Match)
+     */
+    public void select(final Match match) {
+        UIUtils.asyncExec(control, new Runnable() {
+
+            public void run() {
+                if (!source.isDisposed()) {
+                    if (match == null)
+                        source.select(null);
+                    else {
+                        final UITextBlock block = (UITextBlock) match.getBlock();
+                        source.select(new Match(block.getBlock(), match.getOffset(), match.getLength()));
+                    }
+                }
+            }
+        });
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.eclipse.ui.glance.sources.ITextSource#show(org.eclipse.ui.glance.sources
+     * .Match[])
+     */
+    public void show(final Match[] matches) {
+        UIUtils.asyncExec(control, new Runnable() {
+
+            public void run() {
+                if (!source.isDisposed()) {
+                    final Match[] newMatches = new Match[matches.length];
+                    for (int i = 0; i < matches.length; i++) {
+                        final Match match = matches[i];
+                        final UITextBlock block = (UITextBlock) match.getBlock();
+                        newMatches[i] = new Match(block.getBlock(), match.getOffset(), match.getLength());
+                    }
+                    source.show(newMatches);
+                }
+            }
+        });
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.eclipse.ui.glance.sources.ITextSource#addTextSourceListener(org.eclipse.ui
+     * .glance.sources.ITextSourceListener)
+     */
+    public void addTextSourceListener(final ITextSourceListener listener) {
+        listeners.add(listener);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.eclipse.ui.glance.sources.ITextSource#removeTextSourceListener(org.
+     * eclipse.ui.glance.sources.ITextSourceListener)
+     */
+    public void removeTextSourceListener(final ITextSourceListener listener) {
+        listeners.remove(listener);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.eclipse.ui.glance.sources.ITextSourceListener#blocksChanged(org.eclipse.ui
+     * .glance.sources.ITextBlock[],
+     * org.eclipse.ui.glance.sources.ITextBlock[])
+     */
+    public void blocksChanged(final ITextBlock[] removed, final ITextBlock[] added) {
+        final ITextBlock[] uiRemoved = removeBlocks(removed);
+        final ITextBlock[] uiAdded = addBlocks(added);
+        final Object[] objects = listeners.getListeners();
+        for (final Object object : objects) {
+            final ITextSourceListener listener = (ITextSourceListener) object;
+            listener.blocksChanged(uiRemoved, uiAdded);
+        }
+    }
+
+    public void blocksReplaced(final ITextBlock[] newBlocks) {
+        synchronized (this.blocks) {
+            for (final UITextBlock uiBlock : blockToBlock.values()) {
+                uiBlock.dispose();
+            }
+            blockToBlock = new HashMap<ITextBlock, UITextBlock>();
+            blocks = new ArrayList<UITextBlock>();
+        }
+        final ITextBlock[] uiAdded = addBlocks(newBlocks);
+        final Object[] objects = listeners.getListeners();
+        for (final Object object : objects) {
+            final ITextSourceListener listener = (ITextSourceListener) object;
+            listener.blocksReplaced(uiAdded);
+        }
+        selection = source.getSelection();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.eclipse.ui.glance.sources.ITextSourceListener#selectionChanged(org.
+     * eclipse.ui.glance.sources.SourceSelection)
+     */
+    public void selectionChanged(final SourceSelection selection) {
+        final SourceSelection newSelection = updateSelection();
+        final Object[] objects = listeners.getListeners();
+        for (final Object object : objects) {
+            final ITextSourceListener listener = (ITextSourceListener) object;
+            listener.selectionChanged(newSelection);
+        }
+    }
+
+    protected ITextBlock[] addBlocks(final ITextBlock[] blocks) {
+        synchronized (this.blocks) {
+            final ITextBlock[] added = new ITextBlock[blocks.length];
+            for (int i = 0; i < blocks.length; i++) {
+                final ITextBlock block = blocks[i];
+                final UITextBlock uiBlock = new UITextBlock(block);
+                added[i] = uiBlock;
+                this.blocks.add(uiBlock);
+                blockToBlock.put(block, uiBlock);
+            }
+            return added;
+        }
+    }
+
+    protected ITextBlock[] removeBlocks(final ITextBlock[] blocks) {
+        synchronized (this.blocks) {
+            final List<ITextBlock> removed = new ArrayList<ITextBlock>(blocks.length);
+            for (int i = 0; i < blocks.length; i++) {
+                final ITextBlock block = blocks[i];
+                final UITextBlock uiBlock = blockToBlock.remove(block);
+                if (uiBlock != null) {
+                    removed.add(uiBlock);
+                    this.blocks.remove(uiBlock);
+                    uiBlock.dispose();
+                }
+            }
+            return removed.toArray(new ITextBlock[removed.size()]);
+        }
+    }
+
+    protected SourceSelection updateSelection() {
+        final SourceSelection sourceSelection = source.getSelection();
+        if (sourceSelection == null) {
+            selection = null;
+        } else {
+            selection = new SourceSelection(blockToBlock.get(sourceSelection.getBlock()),
+                sourceSelection.getOffset(), sourceSelection.getLength());
+        }
+        return selection;
+    }
+
+    public void init() {
+        if (source != null) {
+            source.init();
+        }
+    }
+
+    private SourceSelection selection;
+    private Map<ITextBlock, UITextBlock> blockToBlock;
+    private final ListenerList listeners;
+    private List<UITextBlock> blocks;
+    private final ITextSource source;
+    private final Control control;
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/utils/UIUtils.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/utils/UIUtils.java
new file mode 100644
index 0000000..082728d
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/utils/UIUtils.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.utils;
+
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class UIUtils {
+
+	public static Display getDisplay() {
+		return PlatformUI.getWorkbench().getDisplay();
+	}
+
+	public static void asyncExec(final Control control, final Runnable runnable) {
+		if (control != null && !control.isDisposed()) {
+			control.getDisplay().asyncExec(new Runnable() {
+				public void run() {
+					if (!control.isDisposed())
+						runnable.run();
+				}
+			});
+		}
+	}
+
+	public static void asyncExec(final Display display, final Runnable runnable) {
+		if (display != null && !display.isDisposed()) {
+			display.asyncExec(new Runnable() {
+				public void run() {
+					if (!display.isDisposed())
+						runnable.run();
+				}
+			});
+		}
+	}
+
+	public static void syncExec(final Display display, final Runnable runnable) {
+		if (display != null && !display.isDisposed()) {
+			display.syncExec(new Runnable() {
+				public void run() {
+					if (!display.isDisposed())
+						runnable.run();
+				}
+			});
+		}
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/viewers/descriptors/AbstractViewerDescriptor.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/viewers/descriptors/AbstractViewerDescriptor.java
new file mode 100644
index 0000000..e244949
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/viewers/descriptors/AbstractViewerDescriptor.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.viewers.descriptors;
+
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Control;
+
+import org.eclipse.ui.glance.sources.ITextSourceDescriptor;
+import org.eclipse.ui.glance.viewers.utils.ViewerUtils;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public abstract class AbstractViewerDescriptor implements ITextSourceDescriptor {
+
+	protected ITextViewer getTextViewer(Control control) {
+		if (control instanceof StyledText) {
+			return getTextViewer((StyledText) control);
+		}
+		return null;
+	}
+
+	protected ITextViewer getTextViewer(StyledText text) {
+		return ViewerUtils.getTextViewer(text);
+	}
+
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/viewers/descriptors/SourceViewerDescriptor.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/viewers/descriptors/SourceViewerDescriptor.java
new file mode 100644
index 0000000..66b51f7
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/viewers/descriptors/SourceViewerDescriptor.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.viewers.descriptors;
+
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.swt.widgets.Control;
+
+import org.eclipse.ui.glance.internal.viewers.SourceViewerControl;
+import org.eclipse.ui.glance.sources.ITextSource;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class SourceViewerDescriptor extends AbstractViewerDescriptor {
+
+	public boolean isValid(Control control) {
+		ITextViewer viewer = getTextViewer(control);
+		if (viewer instanceof SourceViewer) {
+			SourceViewer sViewer = (SourceViewer) viewer;
+			if (sViewer.getAnnotationModel() != null
+					&& sViewer.getDocument() != null)
+				return true;
+		}
+		return false;
+	}
+
+	public ITextSource createSource(Control control) {
+		return new SourceViewerControl((SourceViewer) getTextViewer(control));
+	}
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/viewers/descriptors/TextViewerDescriptor.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/viewers/descriptors/TextViewerDescriptor.java
new file mode 100644
index 0000000..f57675e
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/viewers/descriptors/TextViewerDescriptor.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.viewers.descriptors;
+
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.TextViewer;
+import org.eclipse.swt.widgets.Control;
+
+import org.eclipse.ui.glance.internal.viewers.TextViewerControl;
+import org.eclipse.ui.glance.sources.ITextSource;
+
+/**
+ * @author Yuri Strot
+ * 
+ */
+public class TextViewerDescriptor extends AbstractViewerDescriptor {
+
+	public boolean isValid(Control control) {
+		ITextViewer viewer = getTextViewer(control);
+		return viewer instanceof TextViewer && viewer.getDocument() != null;
+	}
+
+	public ITextSource createSource(Control control) {
+		return new TextViewerControl((TextViewer) getTextViewer(control));
+	}
+}
diff --git a/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/viewers/utils/ViewerUtils.java b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/viewers/utils/ViewerUtils.java
new file mode 100644
index 0000000..121235c
--- /dev/null
+++ b/bundles/org.eclipse.ui.glance/src/org/eclipse/ui/glance/viewers/utils/ViewerUtils.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Exyte
+ * 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:
+ *     Yuri Strot - initial API and Implementation
+ ******************************************************************************/
+package org.eclipse.ui.glance.viewers.utils;
+
+import java.lang.reflect.Field;
+
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TypedListener;
+
+public class ViewerUtils {
+
+	public static ITextViewer getTextViewer(StyledText text) {
+		return getViewer(ITextViewer.class, text, SWT.Selection);
+	}
+
+	public static TreeViewer getTreeViewer(Tree tree) {
+		return getViewer(TreeViewer.class, tree, SWT.Expand);
+	}
+
+	private static <T> T getViewer(Class<T> clazz, Control control, int event) {
+		Listener[] listeners = control.getListeners(event);
+		for (Listener listener : listeners) {
+			Object lookFor = listener;
+			if (listener instanceof TypedListener) {
+				lookFor = ((TypedListener) listener).getEventListener();
+			}
+			try {
+				Field this$0 = lookFor.getClass().getDeclaredField("this$0");
+				this$0.setAccessible(true);
+				Object viewer = this$0.get(lookFor);
+				if (clazz.isInstance(viewer)) {
+					return clazz.cast(viewer);
+				}
+			} catch (Exception e) {
+				// ignore exceptions
+			}
+		}
+		return null;
+	}
+
+}