Bug 422225 - [event spy] Replace plain Tree widget with TreeViewer

Signed-off-by: Daniel Rolka <daniel.rolka@pl.ibm.com>
diff --git a/bundles/org.eclipse.e4.tools.event.spy/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.tools.event.spy/META-INF/MANIFEST.MF
index 8b49e59..8879b35 100644
--- a/bundles/org.eclipse.e4.tools.event.spy/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.e4.tools.event.spy/META-INF/MANIFEST.MF
@@ -20,7 +20,11 @@
  org.eclipse.pde.ui;bundle-version="3.8.0",
  org.eclipse.pde.runtime;bundle-version="3.4.400",
  org.eclipse.osgi.services;bundle-version="3.3.100",
- org.eclipse.e4.ui.services;bundle-version="1.0.0"
+ org.eclipse.e4.ui.services;bundle-version="1.0.0",
+ org.eclipse.jface.databinding,
+ org.eclipse.core.databinding.observable,
+ org.eclipse.core.databinding.beans,
+ org.eclipse.core.databinding.property
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
 Bundle-ActivationPolicy: lazy
 Import-Package: javax.annotation;version="1.1.0"
diff --git a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/Constants.java b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/Constants.java
index 61c8a3e..c79b01e 100644
--- a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/Constants.java
+++ b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/Constants.java
@@ -13,6 +13,7 @@
 import org.eclipse.e4.tools.event.spy.util.PluginUtils;
 import org.eclipse.e4.ui.bindings.EBindingService;
 
+@SuppressWarnings("restriction")
 public class Constants {
 	public static final String PLUGIN_ID = PluginUtils.getBundleId(Constants.class);
 
diff --git a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/core/EventMonitor.java b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/core/EventMonitor.java
index 6d0f900..7cd7c3a 100644
--- a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/core/EventMonitor.java
+++ b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/core/EventMonitor.java
@@ -24,6 +24,7 @@
 import org.osgi.service.event.Event;
 import org.osgi.service.event.EventHandler;
 
+@SuppressWarnings("restriction")
 public class EventMonitor {
 
 	public interface NewEventListener {
@@ -32,7 +33,7 @@
 
 	private final static String TOPIC = UIEvents.UITopicBase + UIEvents.TOPIC_SEP + UIEvents.ALL_SUB_TOPICS;
 
-	@SuppressWarnings({"serial", "restriction"})
+	@SuppressWarnings({"serial"})
 	private static Set<Integer> EVENT_HELPER_CLASSES = new HashSet<Integer>() {{
 		add(UIEvents.class.getName().hashCode());
 		add(UIEventPublisher.class.getName().hashCode());
@@ -40,13 +41,13 @@
 
 	private Collection<CapturedEventFilter> filters;
 
-	private IEventBroker eventBroker;
+	private final IEventBroker eventBroker;
 
 	private NewEventListener listener;
 
 	private CapturedEventFilterMatcher eventFilterMatcher;
 
-	private EventHandler eventHandler = new EventHandler() {
+	private final EventHandler eventHandler = new EventHandler() {
 		public void handleEvent(Event event) {
 			if (listener == null) {
 				return;
diff --git a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/model/CapturedEvent.java b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/model/CapturedEvent.java
index 060b8f5..ffaf6ed 100644
--- a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/model/CapturedEvent.java
+++ b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/model/CapturedEvent.java
@@ -14,7 +14,7 @@
 import java.util.Collections;
 import java.util.List;
 
-public class CapturedEvent {
+public class CapturedEvent implements IEventItem {
 	private String topic;
 
 	private String publisherClassName = "";
@@ -67,4 +67,16 @@
 	public String toString() {
 		return topic;
 	}
+
+	public String getName() {
+		return getTopic();
+	}
+
+	public String getParam1() {
+		return getPublisherClassName();
+	}
+
+	public String getParam2() {
+		return getChangedElementClassName();
+	}
 }
diff --git a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/model/CapturedEventTreeSelection.java b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/model/CapturedEventTreeSelection.java
index 6dddc17..1a326ff 100644
--- a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/model/CapturedEventTreeSelection.java
+++ b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/model/CapturedEventTreeSelection.java
@@ -11,20 +11,13 @@
 package org.eclipse.e4.tools.event.spy.model;
 
 public class CapturedEventTreeSelection {
-	private String selection;
+	private final String selection;
 
-	private boolean parameter;
-
-	public CapturedEventTreeSelection(String selection, boolean parameter) {
+	public CapturedEventTreeSelection(String selection) {
 		this.selection = selection;
-		this.parameter = parameter;
 	}
 
 	public String getSelection() {
 		return selection;
 	}
-
-	public boolean isParameter() {
-		return parameter;
-	}
 }
diff --git a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/util/ParameterFormatter.java b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/model/IEventItem.java
similarity index 60%
rename from bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/util/ParameterFormatter.java
rename to bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/model/IEventItem.java
index 9ebc147..209cd76 100644
--- a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/util/ParameterFormatter.java
+++ b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/model/IEventItem.java
@@ -8,13 +8,12 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *******************************************************************************/
-package org.eclipse.e4.tools.event.spy.util;
+package org.eclipse.e4.tools.event.spy.model;
 
-import org.eclipse.e4.tools.event.spy.model.Parameter;
+public interface IEventItem {
+	String getName();
 
-public class ParameterFormatter {
-	//TODO: Add some parameter formatting and break to long strings into multiple lines
-	public static String toString(Parameter parameter) {
-		return String.format("%s = %s", parameter.getName(), parameter.getValue());
-	}
+	String getParam1();
+
+	String getParam2();
 }
diff --git a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/model/Parameter.java b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/model/Parameter.java
index 2de4d10..254230a 100644
--- a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/model/Parameter.java
+++ b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/model/Parameter.java
@@ -10,9 +10,15 @@
  *******************************************************************************/
 package org.eclipse.e4.tools.event.spy.model;
 
-public class Parameter {
-	private String name;
-	private Object value;
+import org.eclipse.e4.tools.event.spy.util.MultilineFormatter;
+
+public class Parameter implements IEventItem {
+	private static final String EMPTY_VALUE = "";
+
+	private final String name;
+	private final Object value;
+
+	private String formattedValue;
 
 	public Parameter(String name, Object value) {
 		this.name = name;
@@ -26,4 +32,18 @@
 	public Object getValue() {
 		return value;
 	}
+
+	public String getParam1() {
+		if (value == null) {
+			return SpecialValue.Null.toString();
+		}
+		if (formattedValue == null) {
+			formattedValue = MultilineFormatter.format(value.toString(), 70);
+		}
+		return formattedValue;
+	}
+
+	public String getParam2() {
+		return EMPTY_VALUE;
+	}
 }
diff --git a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/ui/CapturedEventTree.java b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/ui/CapturedEventTree.java
index 602978f..85608f9 100644
--- a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/ui/CapturedEventTree.java
+++ b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/ui/CapturedEventTree.java
@@ -11,145 +11,401 @@
 package org.eclipse.e4.tools.event.spy.ui;
 
 import java.util.ArrayList;
-import java.util.List;
 
+import org.eclipse.core.databinding.beans.PojoObservables;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
 import org.eclipse.e4.tools.event.spy.model.CapturedEvent;
 import org.eclipse.e4.tools.event.spy.model.CapturedEventTreeSelection;
+import org.eclipse.e4.tools.event.spy.model.IEventItem;
 import org.eclipse.e4.tools.event.spy.model.ItemToFilter;
-import org.eclipse.e4.tools.event.spy.model.Parameter;
-import org.eclipse.e4.tools.event.spy.util.ParameterFormatter;
+import org.eclipse.e4.tools.event.spy.util.PDEUtils;
+import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider;
+import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider;
+import org.eclipse.jface.databinding.viewers.TreeStructureAdvisor;
+import org.eclipse.jface.viewers.TreeViewer;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseAdapter;
 import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-import org.eclipse.swt.events.TreeEvent;
-import org.eclipse.swt.events.TreeListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.Tree;
 import org.eclipse.swt.widgets.TreeColumn;
 import org.eclipse.swt.widgets.TreeItem;
 
 
-
-public class CapturedEventTree {
+public class CapturedEventTree extends TreeViewer {
 
 	public interface SelectionListener {
 		void selectionChanged(CapturedEventTreeSelection selection);
 	}
 
-	private final Tree tree;
-
 	private SelectionListener selectionListener;
 
-	private final List<ItemToFilter> columns = new ArrayList<ItemToFilter>();
+	private final WritableList capturedEvents;
+	 
+	private final Clipboard clipboard;
 
+	private final TreeItemCursor treeItemCursor;
+	
+	private final TreeItemColor treeItemColor;
 
-	/* Layout scheme:
-	 *
-	 *  +-- parent---------------+
-	 *  |                        |
-	 *  |         Tree           |
-	 *  |                        |
-	 *  +------------------------+
-	 *
-	 * */
+	private final TreeItemFont treeItemFont;
+
+	private final SelectedClassItem selectedClassItem;
+	
+	private String selectedItemText;
 
 	public CapturedEventTree(Composite parent) {
-		tree = new Tree(parent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION);
-		tree.setHeaderVisible(true);
-		tree.setLinesVisible(true);
+		super(parent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION);
 
-		TreeColumn column = new TreeColumn(tree, SWT.LEFT);
+		getTree().setHeaderVisible(true);
+		getTree().setLinesVisible(true);
+		
+		TreeColumn column = new TreeColumn(getTree(), SWT.LEFT);
 		column.setText(ItemToFilter.Topic.toString());
 		column.setWidth(350);
-		columns.add(ItemToFilter.Topic);
 
-		column = new TreeColumn(tree, SWT.LEFT);
+		column = new TreeColumn(getTree(), SWT.LEFT);
 		column.setText(ItemToFilter.Publisher.toString());
 		column.setWidth(150);
-		columns.add(ItemToFilter.Publisher);
 
-		column = new TreeColumn(tree, SWT.LEFT);
+		column = new TreeColumn(getTree(), SWT.LEFT);
 		column.setText(ItemToFilter.ChangedElement.toString());
 		column.setWidth(150);
-		columns.add(ItemToFilter.ChangedElement);
+		
+		ObservableListTreeContentProvider contentProvider = new ObservableListTreeContentProvider(
+				new CapturedEventsObservableFactory(), new CapturedEventsTreeStructureAdvisor());
+		setContentProvider(contentProvider);
 
+		IObservableMap[] attributes = PojoObservables.observeMaps(
+				contentProvider.getKnownElements(), IEventItem.class,
+				new String[] {"name", "param1", "param2"});
+		setLabelProvider(new ObservableMapLabelProvider(attributes));
+
+		capturedEvents = new WritableList(new ArrayList<CapturedEvent>(), CapturedEvent.class);
+		setInput(capturedEvents);
+
+		clipboard = new Clipboard(getTree().getDisplay());
+
+		treeItemCursor = new TreeItemCursor(getTree().getCursor(),
+				getTree().getDisplay().getSystemCursor(SWT.CURSOR_HAND));
+
+		treeItemColor = new TreeItemColor(new Color(getTree().getDisplay(), new RGB(0, 0, 120)),
+				getTree().getDisplay().getSystemColor(SWT.COLOR_WHITE),
+				getTree().getDisplay().getSystemColor(SWT.COLOR_BLACK));
+
+		Font currentFont = getTree().getFont();
+		FontData currentFontData = currentFont.getFontData()[0];
+		treeItemFont = new TreeItemFont(currentFont, new Font(getTree().getDisplay(),
+				currentFontData.getName(), currentFontData.getHeight(), SWT.ITALIC));
+
+		selectedClassItem = new SelectedClassItem();
+		
 		addTreeEventListeners();
 	}
+	
+	private static class CapturedEventsTreeStructureAdvisor extends TreeStructureAdvisor {
+		@Override
+		public Boolean hasChildren(Object element) {
+			if (element instanceof CapturedEvent) {
+				return !((CapturedEvent) element).getParameters().isEmpty();
+		 	}
+			return false;
+		}
+	}
 
-	@SuppressWarnings("unchecked")
-	private void addTreeEventListeners() {
-		tree.addTreeListener(new TreeListener() {
-			public void treeExpanded(TreeEvent e) {
-				TreeItem item = (TreeItem) e.item;
-				TreeItem paramItem = item.getItem(0);
-				if (paramItem.getText().length() == 0) {
-					for (Parameter param: (List<Parameter>) paramItem.getData()) {
-						if (paramItem == null) {
-							paramItem = new TreeItem(item, SWT.NONE);
-						}
-						paramItem.setText(ParameterFormatter.toString(param));
-						paramItem = null;
-					}
-				}
-
+	private static class CapturedEventsObservableFactory implements IObservableFactory {
+		public IObservable createObservable(Object target) {
+			if (target instanceof IObservableList) {
+				return (IObservableList) target;
 			}
-			public void treeCollapsed(TreeEvent e) {
+			if (target instanceof CapturedEvent) {
+				return PojoObservables.observeList(target, "parameters");
+			}
+			return null;
+		}
+	}
+
+	private void addTreeEventListeners() {
+		getTree().addDisposeListener(new DisposeListener() {						
+			public void widgetDisposed(DisposeEvent e) {
+				if (clipboard != null && !clipboard.isDisposed()) {
+					clipboard.dispose();
+				}
+				if (treeItemColor.getParamColor() != null &&
+						!treeItemColor.getParamColor().isDisposed()) {
+					treeItemColor.getParamColor().dispose();
+				}
+				if (treeItemFont.getSelectedClassNameFont() != null &&
+						!treeItemFont.getSelectedClassNameFont().isDisposed()) {
+					treeItemFont.getSelectedClassNameFont().dispose();
+				}
 			}
 		});
-
-		tree.addMouseListener(new MouseListener() {
-			public void mouseUp(MouseEvent e) {
-			}
-			public void mouseDoubleClick(MouseEvent e) {
-
-			}
-			public void mouseDown(MouseEvent e) {
-				TreeItem[] items = tree.getSelection();
-				if (items == null || items.length == 0) {
+		
+		//TODO: Simplify the hit test for item
+		getTree().addMouseMoveListener(new MouseMoveListener() {
+			public void mouseMove(MouseEvent e) {
+				Tree tree = getTree();
+				clearSelectedClassItem();
+				
+				//we can select and finally open the class only when 'ctrl' is pressed
+				if ((e.stateMask & SWT.CTRL) != SWT.CTRL) {
 					return;
 				}
-				int selectedItemIndex = -1;
-				for (int i=0; i<columns.size(); i++) {
-					Rectangle rec = items[0].getBounds(i);
-					if (e.x >= rec.x && e.x <= rec.x + rec.width) {
-						selectedItemIndex = i;
-						break;
-					}
-				}
-				if (selectedItemIndex >= 0 && selectionListener != null) {
-					String selection = items[0].getText(selectedItemIndex);
-					if (selection.length() != 0) {
-						selectionListener.selectionChanged(new CapturedEventTreeSelection(selection,items[0].getItemCount() == 0));
+				
+				TreeItem item = getTree().getItem(new Point(e.x, e.y));
+				int selectedItemIndex = getSelectedColumnIndex(item, e.x, e.y);
+
+				if (selectedItemIndex > 0 /*we check the 2nd and 3rd column only*/ &&
+						item.getParentItem() == null /*we don't check parameters at this moment*/) {
+					String text = item.getText(selectedItemIndex);
+					if (PDEUtils.containsClassName(text)) {
+						selectedClassItem.setClassName(text);
+						selectedClassItem.setColumnIndex(selectedItemIndex);
+						selectedClassItem.setTreeItem(item);
+						tree.setCursor(treeItemCursor.getPointerCursor());
+						redrawTreeItem(item, selectedItemIndex);
 					}
 				}
 			}
 		});
+		
+		getTree().addMouseListener(new MouseAdapter() {
+			@Override
+			public void mouseDown(MouseEvent e) {
+				TreeItem item = getTree().getItem(new Point(e.x, e.y));
+				int index = getSelectedColumnIndex(item, e.x, e.y);
+				selectedItemText = index != -1? item.getText(index): null;
+
+				if ((e.stateMask & SWT.CTRL) == SWT.CTRL && selectedClassItem.getClassName() != null) {
+					selectionListener.selectionChanged(new CapturedEventTreeSelection(selectedClassItem.getClassName()));
+				}
+			}
+		});
+
+		getTree().addListener(SWT.EraseItem, new Listener() {
+			public void handleEvent(Event event) {
+				event.detail &= ~SWT.FOREGROUND;
+			}
+		});
+
+		getTree().addListener(SWT.PaintItem, new Listener() {
+			public void handleEvent(Event event) {
+				TreeItem item = (TreeItem) event.item;
+				String text = item.getText(event.index);
+				int xOffset = item.getParentItem() != null? 10: 2;
+				Color color = getColorForItem(item, event.detail);
+
+				event.gc.setFont(getFontForItem(item, event.index));
+				event.gc.setForeground(color);
+				event.gc.drawText(text, event.x + xOffset, event.y, true);
+			}
+		});
+		
+		getTree().addKeyListener(new KeyListener() {
+			public void keyPressed(KeyEvent e) {
+				boolean ctrlC = (e.stateMask & SWT.CTRL) == SWT.CTRL && e.keyCode == 'c';
+				if(ctrlC && selectedItemText != null && selectedItemText.length() > 0) {
+					clipboard.setContents(new Object[] {selectedItemText},
+							new Transfer[] {TextTransfer.getInstance()});
+				}
+			}
+			public void keyReleased(KeyEvent e) {
+				clearSelectedClassItem();
+			}
+		});
+
+		getTree().addFocusListener(new FocusAdapter() {
+			@Override
+			public void focusLost(FocusEvent e) {
+				clearSelectedClassItem();
+			}
+		});
+	}
+
+	private Color getColorForItem(TreeItem item, int eventDetails) {
+		if ((eventDetails & SWT.SELECTED) == SWT.SELECTED) {
+			return treeItemColor.getSelectedColor();
+		}
+		if (item.getParentItem() != null) {
+			return treeItemColor.getParamColor();
+		}
+		return treeItemColor.getDefaultColor();
+	}
+
+	private Font getFontForItem(TreeItem item, int columnIndex) {
+		if (selectedClassItem.getTreeItem() == item &&
+				selectedClassItem.getColumnIndex() == columnIndex) {
+			return treeItemFont.getSelectedClassNameFont();
+		}
+		return treeItemFont.getDefaultFont();
+	}
+
+	private void redrawTreeItem(TreeItem item, int columnIndex) {
+		if (item != null) {
+			Rectangle rec = item.getBounds(columnIndex);
+			getTree().redraw(rec.x, rec.y, rec.width, rec.height, true);
+		}
+	}
+	
+	private int getSelectedColumnIndex(TreeItem item, int mouseX, int mouseY) {
+		for (int i=0; item != null && i<getTree().getColumnCount(); i++) {
+			Rectangle rec = item.getBounds(i);
+			if (mouseX >= rec.x && mouseX <= rec.x + rec.width) {
+				return i;
+			}
+		}
+		return -1;
+	}
+
+	private void clearSelectedClassItem() {
+		redrawTreeItem(selectedClassItem.getTreeItem(), selectedClassItem.getColumnIndex());
+		selectedClassItem.clear();
+
+		Tree tree = getTree();
+		if (tree.getCursor() != treeItemCursor.getDefaultCursor()) {
+			tree.setCursor(treeItemCursor.getDefaultCursor());
+		}
 	}
 
 	public void addEvent(CapturedEvent event) {
-		TreeItem item = new TreeItem(tree, SWT.NONE);
-
-		item.setText(columns.indexOf(ItemToFilter.Topic), event.getTopic());
-		item.setText(columns.indexOf(ItemToFilter.Publisher), event.getPublisherClassName());
-		item.setText(columns.indexOf(ItemToFilter.ChangedElement), event.getChangedElementClassName());
-
-		if (event.hasParameters()) {
-			item = new TreeItem(item, SWT.NONE);
-			item.setData(event.getParameters());
-		}
+		capturedEvents.add(event);
 	}
 
 	public void setSelectionListener(SelectionListener selectionListener) {
 		this.selectionListener = selectionListener;
 	}
 
-	public Control getControl() {
-		return tree;
+	public void removeAll() {
+		capturedEvents.clear();
+	}
+	
+	private static class TreeItemColor {
+		private final Color paramColor;
+
+		private final Color selectedColor;
+		
+		private final Color defaultColor;
+
+		public TreeItemColor(Color paramColor, Color selectedColor, Color defaultColor) {
+			this.paramColor = paramColor;
+			this.selectedColor = selectedColor;
+			this.defaultColor = defaultColor;
+		}
+
+		public Color getParamColor() {
+			return paramColor;
+		}
+
+		public Color getSelectedColor() {
+			return selectedColor;
+		}
+		
+		public Color getDefaultColor() {
+			return defaultColor;
+		}
 	}
 
-	public void removeAll() {
-		tree.removeAll();
+	private static class TreeItemCursor {
+		private final Cursor defaultCursor;
+		
+		private final Cursor pointerCursor;
+		
+		public TreeItemCursor(Cursor defaultCursor, Cursor pointerCursor) {
+			this.defaultCursor = defaultCursor;
+			this.pointerCursor = pointerCursor;
+		}
+		
+		public Cursor getDefaultCursor() {
+			return defaultCursor;
+		}
+		
+		public Cursor getPointerCursor() {
+			return pointerCursor;
+		}
+	}
+	
+	private static class TreeItemFont {
+		private final Font defaultFont;
+		
+		private final Font selectedClassNameFont;
+		
+		public TreeItemFont(Font defaultFont, Font selectedClassNameFont) {
+			this.defaultFont = defaultFont;
+			this.selectedClassNameFont = selectedClassNameFont;
+		}
+
+		public Font getDefaultFont() {
+			return defaultFont;
+		}
+		
+		public Font getSelectedClassNameFont() {
+			return selectedClassNameFont;
+		}
+	}
+	
+	private static class SelectedClassItem {
+		private TreeItem treeItem;
+		
+		private int columnIndex;
+		
+		private String className;
+		
+		public SelectedClassItem() {
+			clear();
+		}
+		
+		public void setTreeItem(TreeItem treeItem) {
+			this.treeItem = treeItem;
+		}
+
+		public TreeItem getTreeItem() {
+			return treeItem;
+		}
+		
+		public void setColumnIndex(int columnIndex) {
+			this.columnIndex = columnIndex;
+		}
+		
+		public int getColumnIndex() {
+			return columnIndex;
+		}
+		
+		public void setClassName(String className) {
+			this.className = className;
+		}
+		
+		public String getClassName() {
+			return className;
+		}
+		
+		public void clear() {
+			treeItem = null;
+			columnIndex = -1;
+			className = null;
+		}
 	}
 }
diff --git a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/ui/SpyDialog.java b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/ui/SpyDialog.java
index e7a29f1..63461fa 100644
--- a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/ui/SpyDialog.java
+++ b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/ui/SpyDialog.java
@@ -179,17 +179,10 @@
 		capturedEventTree.addEvent(event);
 	}
 
+	@SuppressWarnings("restriction")
 	private void openResource(CapturedEventTreeSelection selection) {
-		String name = selection.getSelection();
-		if (selection.isParameter()) {
-			String[] splitted = selection.getSelection().split("=");
-			if (splitted.length == 2) {
-				name = splitted[1];
-			}
-		}
-
 		try {
-			PDEUtils.openClass(name);
+			PDEUtils.openClass(selection.getSelection());
 		} catch(ClassNotFoundException exc) {
 			logger.warn(exc.getMessage());
 		}
@@ -219,20 +212,4 @@
 		gridData.grabExcessHorizontalSpace = true;
 		return gridData;
 	}
-
-	/** for testing/modifying dialog UI
-	public static void main(String... args) {
-		Display display = new Display ();
-		Shell shell = new Shell (display);
-		shell.open ();
-
-		SpyDialog dialog = new SpyDialog(shell);
-
-		dialog.open();
-		while (!shell.isDisposed()) {
-			if (!display.readAndDispatch ()) display.sleep ();
-		}
-		display.dispose ();
-	}
-	*/
 }
diff --git a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/util/LoggerWrapper.java b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/util/LoggerWrapper.java
index cb3a95d..0c6ad6f 100644
--- a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/util/LoggerWrapper.java
+++ b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/util/LoggerWrapper.java
@@ -17,6 +17,7 @@
 import org.eclipse.e4.core.services.log.Logger;
 import org.eclipse.e4.tools.event.spy.Constants;
 
+@SuppressWarnings("restriction")
 @Creatable
 public class LoggerWrapper extends Logger {
 	@Optional
diff --git a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/util/MultilineFormatter.java b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/util/MultilineFormatter.java
new file mode 100644
index 0000000..0d38f7b
--- /dev/null
+++ b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/util/MultilineFormatter.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+/*******************************************************************************
+ * Copyright (c) 2013 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.e4.tools.event.spy.util;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class MultilineFormatter {
+	@SuppressWarnings("serial")
+	private static Set<Character> LINE_DELIMITERS = new HashSet<Character>() {{
+		add(','); add('('); add(')'); add(';'); add('-'); add('=');
+	}};
+
+	public static String format(String value, int lineLength) {
+		StringBuilder result = new StringBuilder();
+		int counter = 0;
+
+		for (int i=0; i<value.length(); i++) {
+			char c = value.charAt(i);
+			result.append(c);
+
+			if (++counter >= lineLength && LINE_DELIMITERS.contains(c)) {
+				result.append('\n');
+				counter = 0;
+			}
+		}
+		return result.toString();
+	}
+}
diff --git a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/util/PDEUtils.java b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/util/PDEUtils.java
index 274aadf..30ea9a4 100644
--- a/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/util/PDEUtils.java
+++ b/bundles/org.eclipse.e4.tools.event.spy/src/org/eclipse/e4/tools/event/spy/util/PDEUtils.java
@@ -35,6 +35,10 @@
 public class PDEUtils {
 	private final static Pattern CLASS_NAME_PATTERN = Pattern.compile("(([a-zA-Z_]+[0-9]*\\.)+[a-zA-Z_]+[a-z0-9]*)");
 
+	public static boolean containsClassName(String name) {
+		return CLASS_NAME_PATTERN.matcher(name).find();
+	}
+
 	public static void openClass(String clsName) throws ClassNotFoundException {
 		Matcher matcher = CLASS_NAME_PATTERN.matcher(clsName);
 		if (matcher.find()) {