diff --git a/org.eclipse.emf.cdo.threedee.ui/src/org/eclipse/emf/cdo/threedee/ui/ThreeDeeView.java b/org.eclipse.emf.cdo.threedee.ui/src/org/eclipse/emf/cdo/threedee/ui/ThreeDeeView.java
index 04a6b50..59e9801 100644
--- a/org.eclipse.emf.cdo.threedee.ui/src/org/eclipse/emf/cdo/threedee/ui/ThreeDeeView.java
+++ b/org.eclipse.emf.cdo.threedee.ui/src/org/eclipse/emf/cdo/threedee/ui/ThreeDeeView.java
@@ -11,8 +11,8 @@
  */
 package org.eclipse.emf.cdo.threedee.ui;
 
+import org.eclipse.emf.cdo.threedee.AbstractView.CheckStateEvent;
 import org.eclipse.emf.cdo.threedee.DescriptorView;
-import org.eclipse.emf.cdo.threedee.DescriptorView.CheckStateEvent;
 import org.eclipse.emf.cdo.threedee.Frontend;
 import org.eclipse.emf.cdo.threedee.Session;
 import org.eclipse.emf.cdo.threedee.common.Element;
diff --git a/org.eclipse.emf.cdo.threedee/icons/synced.gif b/org.eclipse.emf.cdo.threedee/icons/synced.gif
new file mode 100644
index 0000000..870934b
--- /dev/null
+++ b/org.eclipse.emf.cdo.threedee/icons/synced.gif
Binary files differ
diff --git a/org.eclipse.emf.cdo.threedee/src/org/eclipse/emf/cdo/threedee/AbstractView.java b/org.eclipse.emf.cdo.threedee/src/org/eclipse/emf/cdo/threedee/AbstractView.java
new file mode 100644
index 0000000..0ae08ce
--- /dev/null
+++ b/org.eclipse.emf.cdo.threedee/src/org/eclipse/emf/cdo/threedee/AbstractView.java
@@ -0,0 +1,432 @@
+/**
+ * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) 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:
+ *    Martin Fluegge - initial API and implementation
+ */
+package org.eclipse.emf.cdo.threedee;
+
+import org.eclipse.net4j.util.ObjectUtil;
+import org.eclipse.net4j.util.container.ContainerEventAdapter;
+import org.eclipse.net4j.util.container.IContainer;
+import org.eclipse.net4j.util.event.Event;
+import org.eclipse.net4j.util.event.EventUtil;
+import org.eclipse.net4j.util.event.IListener;
+import org.eclipse.net4j.util.event.INotifier;
+
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerSorter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.ui.ISelectionListener;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.part.ViewPart;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Eike Stepper
+ */
+public abstract class AbstractView<CONTENT> extends ViewPart
+{
+  private Notifier notifier = new Notifier();
+
+  private PageSelectionListener pageSelectionListener = new PageSelectionListener();
+
+  private ICheckStateListener checkStateListener = new CheckStateListener();
+
+  private IListener frontendListener = new FrontendListener();
+
+  private boolean subTreeChecking = true;
+
+  private CheckboxTreeViewer viewer;
+
+  public AbstractView()
+  {
+  }
+
+  public INotifier getNotifier()
+  {
+    return notifier;
+  }
+
+  public CheckboxTreeViewer getViewer()
+  {
+    return viewer;
+  }
+
+  public boolean isSubTreeChecking()
+  {
+    return subTreeChecking;
+  }
+
+  public void setSubTreeChecking(boolean subTreeChecking)
+  {
+    this.subTreeChecking = subTreeChecking;
+  }
+
+  public Set<CONTENT> getAllChecked()
+  {
+    return getAllChecked(true);
+  }
+
+  public Set<CONTENT> getAllChecked(boolean checked)
+  {
+    Set<CONTENT> result = new HashSet<CONTENT>();
+    for (CONTENT object : getContents())
+    {
+      if (viewer.getChecked(object) == checked)
+      {
+        result.add(object);
+      }
+    }
+
+    return result;
+  }
+
+  public void setAllChecked(boolean checked)
+  {
+    try
+    {
+      viewer.removeCheckStateListener(checkStateListener);
+      for (CONTENT object : getContents())
+      {
+        viewer.setChecked(object, checked);
+      }
+    }
+    finally
+    {
+      viewer.addCheckStateListener(checkStateListener);
+    }
+
+    notifier.fireCheckStateChangedEvent();
+  }
+
+  @Override
+  public void createPartControl(Composite parent)
+  {
+    ViewContentProvider contentProvider = new ViewContentProvider();
+
+    viewer = new CheckboxTreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+    viewer.setContentProvider(contentProvider);
+    viewer.setLabelProvider(createLabelProvider(viewer.getControl().getDisplay()));
+    viewer.setSorter(createSorter());
+    viewer.setInput(getInput());
+    viewer.addCheckStateListener(checkStateListener);
+
+    setAllChecked(true);
+
+    Tree tree = viewer.getTree();
+    tree.addSelectionListener(new SubTreeSelectionAdapter(contentProvider, tree));
+
+    Frontend.INSTANCE.addListener(frontendListener);
+
+    getSite().setSelectionProvider(viewer);
+    // getSite().getPage().addSelectionListener(pageSelectionListener);
+
+    setInstance(this);
+  }
+
+  protected ViewerSorter createSorter()
+  {
+    return new NameSorter();
+  }
+
+  @Override
+  public void setFocus()
+  {
+    viewer.getControl().setFocus();
+  }
+
+  @Override
+  public void dispose()
+  {
+    setInstance(null);
+    getSite().getPage().removeSelectionListener(pageSelectionListener);
+    Frontend.INSTANCE.removeListener(frontendListener);
+    super.dispose();
+  }
+
+  public abstract Collection<CONTENT> getContents();
+
+  public abstract Object getInput();
+
+  public abstract Object[] getChildren(Object object);
+
+  public abstract Object getParent(Object object);
+
+  protected abstract IBaseLabelProvider createLabelProvider(Display display);
+
+  protected abstract void handlePageSelectionObject(Object pageSelectionObject, Collection<CONTENT> contents,
+      Set<Object> result);
+
+  protected abstract void setInstance(AbstractView<CONTENT> view);
+
+  /**
+   * @author Eike Stepper
+   */
+  public static final class CheckStateEvent extends Event
+  {
+    private static final long serialVersionUID = 1L;
+
+    public CheckStateEvent(INotifier notifier)
+    {
+      super(notifier);
+    }
+  }
+
+  /**
+   * @author Eike Stepper
+   */
+  private final class CheckStateListener implements ICheckStateListener
+  {
+    public void checkStateChanged(CheckStateChangedEvent event)
+    {
+      if (subTreeChecking)
+      {
+        try
+        {
+          viewer.removeCheckStateListener(checkStateListener);
+          viewer.setSubtreeChecked(event.getElement(), event.getChecked());
+        }
+        finally
+        {
+          viewer.addCheckStateListener(checkStateListener);
+        }
+      }
+
+      notifier.fireCheckStateChangedEvent();
+    }
+  }
+
+  /**
+   * @author Eike Stepper
+   */
+  private final class PageSelectionListener implements ISelectionListener
+  {
+    public void selectionChanged(IWorkbenchPart part, final ISelection selection)
+    {
+      if (selection instanceof IStructuredSelection)
+      {
+        IStructuredSelection ssel = (IStructuredSelection)selection;
+
+        final Set<Object> result = new HashSet<Object>();
+        Collection<CONTENT> contents = getContents();
+        for (Iterator<?> it = ssel.iterator(); it.hasNext();)
+        {
+          Object object = it.next();
+          handlePageSelectionObject(object, contents, result);
+        }
+
+        if (!result.isEmpty())
+        {
+          Display.getDefault().syncExec(new Runnable()
+          {
+            public void run()
+            {
+              @SuppressWarnings("unchecked")
+              List<Object> list = ((StructuredSelection)viewer.getSelection()).toList();
+              Set<Object> old = new HashSet<Object>(list);
+              if (!ObjectUtil.equals(old, result))
+              {
+                viewer.setSelection(new StructuredSelection(result.toArray()), true);
+              }
+            }
+          });
+        }
+      }
+    }
+  }
+
+  /**
+   * @author Eike Stepper
+   */
+  private final class SubTreeSelectionAdapter extends SelectionAdapter
+  {
+    private ITreeContentProvider contentProvider;
+
+    private Tree tree;
+
+    private SubTreeSelectionAdapter(ITreeContentProvider contentProvider, Tree tree)
+    {
+      this.contentProvider = contentProvider;
+      this.tree = tree;
+    }
+
+    @Override
+    public void widgetSelected(SelectionEvent e)
+    {
+      if ((e.stateMask & SWT.ALT) == SWT.ALT)
+      {
+        tree.removeSelectionListener(this);
+
+        try
+        {
+          Object object = e.item.getData();
+          List<Object> objects = new ArrayList<Object>();
+          objects.add(object);
+
+          addChildren(object, objects);
+          viewer.setSelection(new StructuredSelection(objects), true);
+        }
+        finally
+        {
+          tree.addSelectionListener(this);
+        }
+      }
+    }
+
+    private void addChildren(Object object, List<Object> objects)
+    {
+      Object[] children = contentProvider.getChildren(object);
+      for (Object child : children)
+      {
+        objects.add(child);
+        addChildren(child, objects);
+      }
+    }
+  }
+
+  /**
+   * @author Eike Stepper
+   */
+  private final class ViewContentProvider implements ITreeContentProvider
+  {
+    public ViewContentProvider()
+    {
+    }
+
+    public void inputChanged(Viewer v, Object oldInput, Object newInput)
+    {
+    }
+
+    public void dispose()
+    {
+    }
+
+    public Object[] getElements(Object object)
+    {
+      return getChildren(object);
+    }
+
+    public Object[] getChildren(Object object)
+    {
+      return AbstractView.this.getChildren(object);
+    }
+
+    public Object getParent(Object object)
+    {
+      return AbstractView.this.getParent(object);
+    }
+
+    public boolean hasChildren(Object object)
+    {
+      Object[] children = getChildren(object);
+      return children != null && children.length != 0;
+    }
+  }
+
+  /**
+   * @author Eike Stepper
+   */
+  private final class Notifier extends org.eclipse.net4j.util.event.Notifier
+  {
+    public void fireCheckStateChangedEvent()
+    {
+      fireEvent(new CheckStateEvent(this));
+    }
+  }
+
+  /**
+   * @author Eike Stepper
+   */
+  private final class FrontendListener extends ContainerEventAdapter<Object>
+  {
+    @Override
+    protected void onAdded(IContainer<Object> container, Object object)
+    {
+      addElement(object);
+      refreshViewer();
+    }
+
+    @Override
+    protected void onRemoved(IContainer<Object> container, Object object)
+    {
+      removeElement(object);
+      refreshViewer();
+    }
+
+    private void refreshViewer()
+    {
+      try
+      {
+        viewer.getControl().getDisplay().asyncExec(new Runnable()
+        {
+          public void run()
+          {
+            try
+            {
+              viewer.refresh();
+            }
+            catch (Exception ignore)
+            {
+            }
+          }
+        });
+      }
+      catch (Exception ignore)
+      {
+      }
+    }
+
+    private void addElement(Object object)
+    {
+      for (Object child : getChildren(object))
+      {
+        addElement(child);
+      }
+
+      EventUtil.addListener(object, this);
+    }
+
+    private void removeElement(Object object)
+    {
+      EventUtil.removeListener(object, this);
+      for (Object child : getChildren(object))
+      {
+        removeElement(child);
+      }
+    }
+  }
+
+  /**
+   * @author Eike Stepper
+   */
+  private static final class NameSorter extends ViewerSorter
+  {
+    public NameSorter()
+    {
+    }
+  }
+}
diff --git a/org.eclipse.emf.cdo.threedee/src/org/eclipse/emf/cdo/threedee/DescriptorView.java b/org.eclipse.emf.cdo.threedee/src/org/eclipse/emf/cdo/threedee/DescriptorView.java
index 2ea8bcd..6465054 100644
--- a/org.eclipse.emf.cdo.threedee/src/org/eclipse/emf/cdo/threedee/DescriptorView.java
+++ b/org.eclipse.emf.cdo.threedee/src/org/eclipse/emf/cdo/threedee/DescriptorView.java
@@ -15,43 +15,22 @@
 import org.eclipse.emf.cdo.threedee.common.ElementDescriptor;
 import org.eclipse.emf.cdo.threedee.common.ElementDescriptor.Registry;
 
-import org.eclipse.net4j.util.ObjectUtil;
-import org.eclipse.net4j.util.container.ContainerEventAdapter;
-import org.eclipse.net4j.util.container.IContainer;
-import org.eclipse.net4j.util.event.Event;
-import org.eclipse.net4j.util.event.EventUtil;
-import org.eclipse.net4j.util.event.IListener;
-import org.eclipse.net4j.util.event.INotifier;
 import org.eclipse.net4j.util.event.ValueNotifier;
 
 import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.jface.viewers.CheckStateChangedEvent;
-import org.eclipse.jface.viewers.CheckboxTreeViewer;
-import org.eclipse.jface.viewers.ICheckStateListener;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.ITreeContentProvider;
-import org.eclipse.jface.viewers.StructuredSelection;
-import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
 import org.eclipse.jface.viewers.ViewerSorter;
-import org.eclipse.swt.SWT;
 import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.ISelectionListener;
-import org.eclipse.ui.IWorkbenchPart;
-import org.eclipse.ui.part.ViewPart;
 
 import java.awt.Color;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
+import java.util.Collection;
 import java.util.Set;
 
 /**
  * @author Eike Stepper
  */
-public class DescriptorView extends ViewPart
+public class DescriptorView extends AbstractView<ElementDescriptor>
 {
   public static final String ID = "org.eclipse.emf.cdo.threedee.DescriptorView";
 
@@ -59,219 +38,83 @@
 
   private static final Registry INPUT = ElementDescriptor.Registry.INSTANCE;
 
-  private Notifier notifier = new Notifier();
-
-  private ICheckStateListener checkStateListener = new CheckStateListener();
-
-  private IListener frontendListener = new FrontendListener();
-
-  private boolean subTreeChecking = true;
-
-  private CheckboxTreeViewer viewer;
-
   public DescriptorView()
   {
   }
 
-  public INotifier getNotifier()
+  @Override
+  public Collection<ElementDescriptor> getContents()
   {
-    return notifier;
-  }
-
-  public boolean isSubTreeChecking()
-  {
-    return subTreeChecking;
-  }
-
-  public void setSubTreeChecking(boolean subTreeChecking)
-  {
-    this.subTreeChecking = subTreeChecking;
-  }
-
-  public boolean isChecked(ElementDescriptor descriptor)
-  {
-    return viewer.getChecked(descriptor);
-  }
-
-  public Set<ElementDescriptor> getAllChecked()
-  {
-    return getAllChecked(true);
-  }
-
-  public Set<ElementDescriptor> getAllChecked(boolean checked)
-  {
-    Set<ElementDescriptor> result = new HashSet<ElementDescriptor>();
-    for (ElementDescriptor descriptor : INPUT.values())
-    {
-      if (viewer.getChecked(descriptor) == checked)
-      {
-        result.add(descriptor);
-      }
-    }
-
-    return result;
-  }
-
-  public void setAllChecked(boolean checked)
-  {
-    try
-    {
-      viewer.removeCheckStateListener(checkStateListener);
-      for (ElementDescriptor descriptor : INPUT.values())
-      {
-        viewer.setChecked(descriptor, checked);
-      }
-    }
-    finally
-    {
-      viewer.addCheckStateListener(checkStateListener);
-    }
-
-    notifier.fireCheckStateChangedEvent();
+    return INPUT.values();
   }
 
   @Override
-  public void createPartControl(Composite parent)
+  public Object[] getChildren(Object object)
   {
-    viewer = new CheckboxTreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
-    viewer.setContentProvider(new ViewContentProvider());
-    viewer.setLabelProvider(new LabelProvider(viewer.getControl().getDisplay()));
-    viewer.setSorter(new NameSorter());
-    viewer.setInput(INPUT);
-    viewer.addCheckStateListener(checkStateListener);
-
-    setAllChecked(true);
-    INSTANCE.setValue(this);
-
-    Frontend.INSTANCE.addListener(frontendListener);
-
-    getSite().setSelectionProvider(viewer);
-    getSite().getPage().addSelectionListener(new ISelectionListener()
+    if (object instanceof ElementDescriptor)
     {
-      public void selectionChanged(IWorkbenchPart part, final ISelection selection)
-      {
-        final Set<ElementDescriptor> descriptors = new HashSet<ElementDescriptor>();
-        if (selection instanceof IStructuredSelection)
-        {
-          IStructuredSelection ssel = (IStructuredSelection)selection;
-          for (Iterator<?> it = ssel.iterator(); it.hasNext();)
-          {
-            Object object = it.next();
-            if (object instanceof ElementDescriptor)
-            {
-              ElementDescriptor descriptor = (ElementDescriptor)object;
-              descriptors.add(descriptor);
-            }
-            else if (object instanceof Element)
-            {
-              Element element = (Element)object;
-              descriptors.add(element.getDescriptor());
-            }
-          }
-        }
+      ElementDescriptor descriptor = (ElementDescriptor)object;
+      return descriptor.getSubDescriptors().toArray();
+    }
 
-        if (!descriptors.isEmpty())
-        {
-          Display.getDefault().syncExec(new Runnable()
-          {
-            public void run()
-            {
-              @SuppressWarnings("unchecked")
-              List<Object> list = ((StructuredSelection)viewer.getSelection()).toList();
-              Set<Object> old = new HashSet<Object>(list);
-              if (!ObjectUtil.equals(old, descriptors))
-              {
-                ElementDescriptor[] array = descriptors.toArray(new ElementDescriptor[descriptors.size()]);
-                viewer.setSelection(new StructuredSelection(array), true);
-              }
-            }
-          });
-        }
-      }
-    });
+    if (object == INPUT)
+    {
+      return INPUT.getRootDescriptors().toArray();
+    }
+
+    return new Object[0];
   }
 
   @Override
-  public void setFocus()
+  public Object getParent(Object object)
   {
-    viewer.getControl().setFocus();
+    if (object instanceof ElementDescriptor)
+    {
+      ElementDescriptor descriptor = (ElementDescriptor)object;
+      return descriptor.getSuperDescriptor();
+    }
+
+    return null;
   }
 
   @Override
-  public void dispose()
+  public Object getInput()
   {
-    Frontend.INSTANCE.removeListener(frontendListener);
-    INSTANCE.setValue(null);
-    super.dispose();
+    return INPUT;
   }
 
-  /**
-   * @author Eike Stepper
-   */
-  public static final class CheckStateEvent extends Event
+  @Override
+  protected IBaseLabelProvider createLabelProvider(Display display)
   {
-    private static final long serialVersionUID = 1L;
+    return new LabelProvider(display);
+  }
 
-    public CheckStateEvent(INotifier notifier)
+  @Override
+  protected ViewerSorter createSorter()
+  {
+    return new NameSorter();
+  }
+
+  @Override
+  protected void handlePageSelectionObject(Object pageSelectionObject, Collection<ElementDescriptor> contents,
+      Set<Object> result)
+  {
+    if (pageSelectionObject instanceof ElementDescriptor)
     {
-      super(notifier);
+      ElementDescriptor descriptor = (ElementDescriptor)pageSelectionObject;
+      result.add(descriptor);
+    }
+    else if (pageSelectionObject instanceof Element)
+    {
+      Element element = (Element)pageSelectionObject;
+      result.add(element.getDescriptor());
     }
   }
 
-  /**
-   * @author Eike Stepper
-   */
-  private static final class ViewContentProvider implements ITreeContentProvider
+  @Override
+  protected void setInstance(AbstractView<ElementDescriptor> view)
   {
-    public ViewContentProvider()
-    {
-    }
-
-    public void inputChanged(Viewer v, Object oldInput, Object newInput)
-    {
-    }
-
-    public void dispose()
-    {
-    }
-
-    public Object[] getElements(Object object)
-    {
-      return getChildren(object);
-    }
-
-    public Object[] getChildren(Object object)
-    {
-      if (object instanceof ElementDescriptor)
-      {
-        ElementDescriptor descriptor = (ElementDescriptor)object;
-        return descriptor.getSubDescriptors().toArray();
-      }
-
-      if (object == INPUT)
-      {
-        return INPUT.getRootDescriptors().toArray();
-      }
-
-      return new Object[0];
-    }
-
-    public Object getParent(Object object)
-    {
-      if (object instanceof ElementDescriptor)
-      {
-        ElementDescriptor descriptor = (ElementDescriptor)object;
-        return descriptor.getSuperDescriptor();
-      }
-
-      return null;
-    }
-
-    public boolean hasChildren(Object object)
-    {
-      Object[] children = getChildren(object);
-      return children != null && children.length != 0;
-    }
+    INSTANCE.setValue((DescriptorView)view);
   }
 
   /**
@@ -357,10 +200,6 @@
    */
   private static final class NameSorter extends ViewerSorter
   {
-    public NameSorter()
-    {
-    }
-
     @Override
     public int category(Object element)
     {
@@ -377,110 +216,4 @@
       return 3;
     }
   }
-
-  /**
-   * @author Eike Stepper
-   */
-  private final class CheckStateListener implements ICheckStateListener
-  {
-    public void checkStateChanged(CheckStateChangedEvent event)
-    {
-      if (subTreeChecking)
-      {
-        try
-        {
-          viewer.removeCheckStateListener(checkStateListener);
-          ElementDescriptor descriptor = (ElementDescriptor)event.getElement();
-          viewer.setSubtreeChecked(descriptor, event.getChecked());
-        }
-        finally
-        {
-          viewer.addCheckStateListener(checkStateListener);
-        }
-      }
-
-      notifier.fireCheckStateChangedEvent();
-    }
-  }
-
-  /**
-   * @author Eike Stepper
-   */
-  private final class Notifier extends org.eclipse.net4j.util.event.Notifier
-  {
-    public void fireCheckStateChangedEvent()
-    {
-      fireEvent(new CheckStateEvent(this));
-    }
-  }
-
-  /**
-   * @author Eike Stepper
-   */
-  private final class FrontendListener extends ContainerEventAdapter<Object>
-  {
-    @Override
-    protected void onAdded(IContainer<Object> container, Object object)
-    {
-      addElement(object);
-      refreshViewer();
-    }
-
-    @Override
-    protected void onRemoved(IContainer<Object> container, Object object)
-    {
-      removeElement(object);
-      refreshViewer();
-    }
-
-    private void refreshViewer()
-    {
-      try
-      {
-        viewer.getControl().getDisplay().asyncExec(new Runnable()
-        {
-          public void run()
-          {
-            try
-            {
-              viewer.refresh(true);
-            }
-            catch (Exception ignore)
-            {
-            }
-          }
-        });
-      }
-      catch (Exception ignore)
-      {
-      }
-    }
-
-    private void addElement(Object object)
-    {
-      if (object instanceof Element)
-      {
-        Element element = (Element)object;
-        for (Element child : element.getElements())
-        {
-          addElement(child);
-        }
-      }
-
-      EventUtil.addListener(object, this);
-    }
-
-    private void removeElement(Object object)
-    {
-      EventUtil.removeListener(object, this);
-      if (object instanceof Element)
-      {
-        Element element = (Element)object;
-        for (Element child : element.getElements())
-        {
-          removeElement(child);
-        }
-      }
-    }
-  }
 }
diff --git a/org.eclipse.emf.cdo.threedee/src/org/eclipse/emf/cdo/threedee/ElementView.java b/org.eclipse.emf.cdo.threedee/src/org/eclipse/emf/cdo/threedee/ElementView.java
index 8484de1..1baeb22 100644
--- a/org.eclipse.emf.cdo.threedee/src/org/eclipse/emf/cdo/threedee/ElementView.java
+++ b/org.eclipse.emf.cdo.threedee/src/org/eclipse/emf/cdo/threedee/ElementView.java
@@ -14,320 +14,120 @@
 import org.eclipse.emf.cdo.threedee.common.Element;
 import org.eclipse.emf.cdo.threedee.common.ElementDescriptor;
 
-import org.eclipse.net4j.util.ObjectUtil;
-import org.eclipse.net4j.util.container.ContainerEventAdapter;
 import org.eclipse.net4j.util.container.IContainer;
-import org.eclipse.net4j.util.event.Event;
-import org.eclipse.net4j.util.event.EventUtil;
-import org.eclipse.net4j.util.event.IListener;
-import org.eclipse.net4j.util.event.INotifier;
 import org.eclipse.net4j.util.event.ValueNotifier;
 import org.eclipse.net4j.util.ui.UIUtil;
 
 import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.jface.viewers.CheckStateChangedEvent;
-import org.eclipse.jface.viewers.CheckboxTreeViewer;
-import org.eclipse.jface.viewers.ICheckStateListener;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.jface.viewers.ITreeContentProvider;
-import org.eclipse.jface.viewers.StructuredSelection;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.jface.viewers.ViewerSorter;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
 import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Tree;
-import org.eclipse.ui.ISelectionListener;
-import org.eclipse.ui.IWorkbenchPart;
-import org.eclipse.ui.part.ViewPart;
 
 import java.awt.Color;
 import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
+import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 
 /**
  * @author Eike Stepper
  */
-public class ElementView extends ViewPart
+public class ElementView extends AbstractView<Element>
 {
   public static final String ID = "org.eclipse.emf.cdo.threedee.ElementView";
 
   public static final ValueNotifier<ElementView> INSTANCE = new ValueNotifier<ElementView>(ID);
 
-  private Notifier notifier = new Notifier();
-
-  private ICheckStateListener checkStateListener = new CheckStateListener();
-
-  private IListener frontendListener = new FrontendListener();
-
-  private boolean subTreeChecking = true;
-
-  private CheckboxTreeViewer viewer;
-
   public ElementView()
   {
   }
 
-  public INotifier getNotifier()
+  @Override
+  public List<Element> getContents()
   {
-    return notifier;
-  }
-
-  public boolean isSubTreeChecking()
-  {
-    return subTreeChecking;
-  }
-
-  public void setSubTreeChecking(boolean subTreeChecking)
-  {
-    this.subTreeChecking = subTreeChecking;
-  }
-
-  public Set<Element> getAllChecked()
-  {
-    return getAllChecked(true);
-  }
-
-  public Set<Element> getAllChecked(boolean checked)
-  {
-    Set<Element> result = new HashSet<Element>();
+    List<Element> result = new ArrayList<Element>();
     for (Session session : Frontend.INSTANCE.getElements())
     {
       for (Element element : session.getAllElements())
       {
-        if (viewer.getChecked(element) == checked)
-        {
-          result.add(element);
-        }
+        result.add(element);
       }
     }
 
     return result;
   }
 
-  public void setAllChecked(boolean checked)
+  @Override
+  public Object getInput()
   {
-    try
-    {
-      viewer.removeCheckStateListener(checkStateListener);
-      for (Session session : Frontend.INSTANCE.getElements())
-      {
-        for (Element element : session.getAllElements())
-        {
-          viewer.setChecked(element, checked);
-        }
-      }
-    }
-    finally
-    {
-      viewer.addCheckStateListener(checkStateListener);
-    }
-
-    notifier.fireCheckStateChangedEvent();
+    return Frontend.INSTANCE;
   }
 
   @Override
-  public void createPartControl(Composite parent)
+  public Object[] getChildren(Object object)
   {
-    final ViewContentProvider contentProvider = new ViewContentProvider();
-
-    viewer = new CheckboxTreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
-    viewer.setContentProvider(contentProvider);
-    viewer.setLabelProvider(new LabelProvider(viewer.getControl().getDisplay()));
-    viewer.setSorter(new NameSorter());
-    viewer.setInput(Frontend.INSTANCE);
-    viewer.addCheckStateListener(checkStateListener);
-
-    final Tree tree = viewer.getTree();
-    tree.addSelectionListener(new SelectionAdapter()
+    if (object instanceof IContainer<?>)
     {
-      @Override
-      public void widgetSelected(SelectionEvent e)
-      {
-        if ((e.stateMask & SWT.ALT) == SWT.ALT)
-        {
-          tree.removeSelectionListener(this);
+      IContainer<?> container = (IContainer<?>)object;
+      return container.getElements();
+    }
 
-          try
-          {
-            Object object = e.item.getData();
-            List<Object> objects = new ArrayList<Object>();
-            objects.add(object);
-
-            addChildren(contentProvider, object, objects);
-            viewer.setSelection(new StructuredSelection(objects), true);
-          }
-          finally
-          {
-            tree.addSelectionListener(this);
-          }
-        }
-      }
-
-      private void addChildren(final ViewContentProvider contentProvider, Object object, List<Object> objects)
-      {
-        Object[] children = contentProvider.getChildren(object);
-        for (Object child : children)
-        {
-          objects.add(child);
-          addChildren(contentProvider, child, objects);
-        }
-      }
-    });
-
-    setAllChecked(true);
-    INSTANCE.setValue(this);
-
-    Frontend.INSTANCE.addListener(frontendListener);
-
-    getSite().setSelectionProvider(viewer);
-    getSite().getPage().addSelectionListener(new ISelectionListener()
-    {
-      public void selectionChanged(IWorkbenchPart part, final ISelection selection)
-      {
-        final Set<Element> elements = new HashSet<Element>();
-        if (selection instanceof IStructuredSelection)
-        {
-          IStructuredSelection ssel = (IStructuredSelection)selection;
-          for (Iterator<?> it = ssel.iterator(); it.hasNext();)
-          {
-            Object object = it.next();
-            if (object instanceof ElementDescriptor)
-            {
-              ElementDescriptor descriptor = (ElementDescriptor)object;
-              for (Session session : Frontend.INSTANCE.getElements())
-              {
-                for (Element element : session.getAllElements())
-                {
-                  if (element.getDescriptor() == descriptor)
-                  {
-                    elements.add(element);
-                  }
-                }
-              }
-            }
-            else if (object instanceof Element)
-            {
-              Element element = (Element)object;
-              elements.add(element);
-            }
-          }
-        }
-
-        if (!elements.isEmpty())
-        {
-          Display.getDefault().syncExec(new Runnable()
-          {
-            public void run()
-            {
-              @SuppressWarnings("unchecked")
-              List<Object> list = ((StructuredSelection)viewer.getSelection()).toList();
-              Set<Object> old = new HashSet<Object>(list);
-              if (!ObjectUtil.equals(old, elements))
-              {
-                Element[] array = elements.toArray(new Element[elements.size()]);
-                viewer.setSelection(new StructuredSelection(array), true);
-              }
-            }
-          });
-        }
-      }
-    });
+    return new Object[0];
   }
 
   @Override
-  public void setFocus()
+  public Object getParent(Object object)
   {
-    viewer.getControl().setFocus();
+    if (object instanceof Element)
+    {
+      Element element = (Element)object;
+      Element container = element.getContainer();
+      if (container == null)
+      {
+        return element.getProvider();
+      }
+
+      return container;
+    }
+
+    if (object instanceof Session)
+    {
+      return Frontend.INSTANCE;
+    }
+
+    return null;
   }
 
   @Override
-  public void dispose()
+  protected void setInstance(AbstractView<Element> view)
   {
-    Frontend.INSTANCE.removeListener(frontendListener);
-    INSTANCE.setValue(null);
-    super.dispose();
+    INSTANCE.setValue((ElementView)view);
   }
 
-  /**
-   * @author Eike Stepper
-   */
-  public static final class CheckStateEvent extends Event
+  @Override
+  protected IBaseLabelProvider createLabelProvider(Display display)
   {
-    private static final long serialVersionUID = 1L;
-
-    public CheckStateEvent(INotifier notifier)
-    {
-      super(notifier);
-    }
+    return new LabelProvider(display);
   }
 
-  /**
-   * @author Eike Stepper
-   */
-  private static final class ViewContentProvider implements ITreeContentProvider
+  @Override
+  protected void handlePageSelectionObject(Object pageSelectionObject, Collection<Element> contents, Set<Object> result)
   {
-    public ViewContentProvider()
+    if (pageSelectionObject instanceof ElementDescriptor)
     {
-    }
-
-    public void inputChanged(Viewer v, Object oldInput, Object newInput)
-    {
-    }
-
-    public void dispose()
-    {
-    }
-
-    public Object[] getElements(Object object)
-    {
-      return getChildren(object);
-    }
-
-    public Object[] getChildren(Object object)
-    {
-      if (object instanceof IContainer<?>)
+      ElementDescriptor descriptor = (ElementDescriptor)pageSelectionObject;
+      for (Element element : contents)
       {
-        IContainer<?> container = (IContainer<?>)object;
-        return container.getElements();
-      }
-
-      return new Object[0];
-    }
-
-    public Object getParent(Object object)
-    {
-      if (object instanceof Element)
-      {
-        Element element = (Element)object;
-        Element container = element.getContainer();
-        if (container == null)
+        if (element.getDescriptor() == descriptor)
         {
-          return element.getProvider();
+          result.add(element);
         }
-
-        return container;
       }
-
-      if (object instanceof Session)
-      {
-        return Frontend.INSTANCE;
-      }
-
-      return null;
     }
-
-    public boolean hasChildren(Object object)
+    else if (pageSelectionObject instanceof Element)
     {
-      Object[] children = getChildren(object);
-      return children != null && children.length != 0;
+      Element element = (Element)pageSelectionObject;
+      result.add(element);
     }
   }
 
@@ -369,120 +169,4 @@
       super.dispose();
     }
   }
-
-  /**
-   * @author Eike Stepper
-   */
-  private static final class NameSorter extends ViewerSorter
-  {
-    public NameSorter()
-    {
-    }
-  }
-
-  /**
-   * @author Eike Stepper
-   */
-  private final class CheckStateListener implements ICheckStateListener
-  {
-    public void checkStateChanged(CheckStateChangedEvent event)
-    {
-      if (subTreeChecking)
-      {
-        try
-        {
-          viewer.removeCheckStateListener(checkStateListener);
-          Element descriptor = (Element)event.getElement();
-          viewer.setSubtreeChecked(descriptor, event.getChecked());
-        }
-        finally
-        {
-          viewer.addCheckStateListener(checkStateListener);
-        }
-      }
-
-      notifier.fireCheckStateChangedEvent();
-    }
-  }
-
-  /**
-   * @author Eike Stepper
-   */
-  private final class Notifier extends org.eclipse.net4j.util.event.Notifier
-  {
-    public void fireCheckStateChangedEvent()
-    {
-      fireEvent(new CheckStateEvent(this));
-    }
-  }
-
-  /**
-   * @author Eike Stepper
-   */
-  private final class FrontendListener extends ContainerEventAdapter<Object>
-  {
-    @Override
-    protected void onAdded(IContainer<Object> container, Object object)
-    {
-      addElement(object);
-      refreshViewer();
-    }
-
-    @Override
-    protected void onRemoved(IContainer<Object> container, Object object)
-    {
-      removeElement(object);
-      refreshViewer();
-    }
-
-    private void refreshViewer()
-    {
-      try
-      {
-        viewer.getControl().getDisplay().asyncExec(new Runnable()
-        {
-          public void run()
-          {
-            try
-            {
-              viewer.refresh();
-            }
-            catch (Exception ignore)
-            {
-            }
-          }
-        });
-      }
-      catch (Exception ignore)
-      {
-      }
-    }
-
-    private void addElement(Object object)
-    {
-      if (object instanceof Element)
-      {
-        Element element = (Element)object;
-        for (Element child : element.getElements())
-        {
-          addElement(child);
-        }
-      }
-
-      EventUtil.addListener(object, this);
-    }
-
-    private void removeElement(Object object)
-    {
-      EventUtil.removeListener(object, this);
-      if (object instanceof Element)
-      {
-        Element element = (Element)object;
-        for (Element child : element.getElements())
-        {
-          removeElement(child);
-        }
-      }
-    }
-  }
 }