Bug 540010: Add tools for R apps

  - Add launch config type R App and actions to start/stop/restart apps
  - Add app browser view R App Viewer
  - Add app variables view R App Variables

Also:
  - Replace IToolProvider / ToolRetargetableHandler by ecommons.ts
  - Replace ToolRetargetableHandler by AbstractToolHandler
  - Add Section to Queue to improve handling of stacked queues
  - Extract RElementInput utilities and handlers from object browser
  - CleanUp QueueView and HistoryView

Change-Id: Ifee5b79578719a19c97a5bc676c96a629c5ab259
diff --git a/r/_build/org.eclipse.statet.r-feature/feature.xml b/r/_build/org.eclipse.statet.r-feature/feature.xml
index 6ab035b..26c6f80 100644
--- a/r/_build/org.eclipse.statet.r-feature/feature.xml
+++ b/r/_build/org.eclipse.statet.r-feature/feature.xml
@@ -143,6 +143,13 @@
          unpack="false"/-->
 
    <plugin
+         id="org.eclipse.statet.r.apps"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
          id="org.eclipse.statet.r.doc"
          download-size="0"
          install-size="0"
diff --git a/r/org.eclipse.statet.ide.ui/src/org/eclipse/statet/internal/ide/ui/StatetPerspectiveFactory.java b/r/org.eclipse.statet.ide.ui/src/org/eclipse/statet/internal/ide/ui/StatetPerspectiveFactory.java
index a067e8d..fce06fe 100644
--- a/r/org.eclipse.statet.ide.ui/src/org/eclipse/statet/internal/ide/ui/StatetPerspectiveFactory.java
+++ b/r/org.eclipse.statet.ide.ui/src/org/eclipse/statet/internal/ide/ui/StatetPerspectiveFactory.java
@@ -59,8 +59,7 @@
 				"console-additions", IPageLayout.BOTTOM, 0.60f, "left"); //$NON-NLS-1$ //$NON-NLS-2$
 		consoleAddFolder.addView(NICO_OBJECTBROWSER_VIEW);
 		consoleAddFolder.addView(NICO_CMDHISTORY_VIEW);
-		
-//		layout.createPlaceholderFolder("console-additions", IPageLayout.BOTTOM, 0.80f, "left"); //$NON-NLS-1$
+		consoleAddFolder.addPlaceholder("org.eclipse.statet.r.apps.views.VariableViewer"); //$NON-NLS-1$
 		
 		layout.addActionSet(LAUNCH_ACTION_SET);
 		layout.addActionSet(BREAKPOINT_ACTION_SET);
diff --git a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/internal/nico/core/Messages.java b/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/internal/nico/core/Messages.java
index bfa3700..f5cdc62 100644
--- a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/internal/nico/core/Messages.java
+++ b/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/internal/nico/core/Messages.java
@@ -16,7 +16,11 @@
 
 import org.eclipse.osgi.util.NLS;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
 
+
+@NonNullByDefault
+@SuppressWarnings("null")
 public class Messages extends NLS {
 	
 	
@@ -40,6 +44,7 @@
 	public static String ToolController_FileOperation_error_CannotResolve_message;
 	public static String ToolController_SubmitCancelled_message;
 	public static String ToolController_ToolTerminated_message;
+	public static String ToolController_QueueSectionTerminated_message;
 	
 	public static String Progress_Starting_label;
 	public static String Progress_Terminating_label;
diff --git a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/internal/nico/core/Messages.properties b/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/internal/nico/core/Messages.properties
index e145d30..e530158 100644
--- a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/internal/nico/core/Messages.properties
+++ b/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/internal/nico/core/Messages.properties
@@ -29,10 +29,11 @@
 Runtime_error_CriticalError_message = Critical runtime error occurred.
 Runtime_error_UnexpectedTermination_message = An error occurred while running {0}. {1} terminates unexpected.
 
-ToolController_CommonStartTask_label = Start Tool
-ToolController_FileOperation_error_CannotResolve_message = Filename ''{0}'' cannot be resolved.
-ToolController_SubmitCancelled_message = Submit cancelled
-ToolController_ToolTerminated_message = {0} is terminated.
+ToolController_CommonStartTask_label= Start Tool
+ToolController_FileOperation_error_CannotResolve_message= Filename ''{0}'' cannot be resolved.
+ToolController_SubmitCancelled_message= Submit cancelled
+ToolController_ToolTerminated_message= {0} is terminated.
+ToolController_QueueSectionTerminated_message= Requested position in queue of {0} is not available.
 
 Progress_Starting_label = Starting
 Progress_Terminating_label = Terminating
diff --git a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/Queue.java b/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/Queue.java
index 1d6fcc5..ef3cd91 100644
--- a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/Queue.java
+++ b/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/Queue.java
@@ -29,6 +29,9 @@
 
 import org.eclipse.statet.jcommons.collections.ImCollections;
 import org.eclipse.statet.jcommons.collections.ImList;
+import org.eclipse.statet.jcommons.lang.NonNull;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
 
 import org.eclipse.statet.ecommons.ts.core.SystemRunnable;
 import org.eclipse.statet.ecommons.ts.core.ToolQueue;
@@ -39,7 +42,7 @@
 
 
 /**
- * Queue with IToolRunnable waiting to be processed by the tool/controller.
+ * Queue with ToolRunnable waiting to be processed by the tool/controller.
  * 
  * Usage: You get your queue via accessor of the ToolProcess.
  * 
@@ -60,6 +63,7 @@
  * The events of this type are sended by the queue (source element).
  * 
  */
+@NonNullByDefault
 public final class Queue implements ToolQueue {
 	
 	
@@ -74,7 +78,7 @@
 	 * Constant for detail of a DebugEvent, sending the complete queue.
 	 * This does not signalising, that the queue has changed.
 	 * <p>
-	 * The queue entries (<code>IToolRunnable[]</code>) are attached as
+	 * The queue entries (<code>List&lt;ToolRunnable&gt;</code>) are attached as
 	 * data to this event. The source of the event is the ToolProcess.
 	 * <p>
 	 * Usage: Events of this type are sended by the ToolProcess/its queue.
@@ -83,19 +87,6 @@
 	 */
 	public static final int QUEUE_INFO= 1;
 	
-//	/**
-//	 * Constant for detail of a DebugEvent, signalising that
-//	 * queue has changed e.g. reordered, cleared,... .
-//	 * <p>
-//	 * The queue entries (<code>IToolRunnable[]</code>) are attached as
-//	 * data to this event. The source of the event is the ToolProcess.
-//	 * <p>
-//	 * Usage: Events of this type are sended by the ToolProcess/its queue.
-//	 * The constant is applicable for DebugEvents of kind
-//	 * <code>MODEL_SPECIFIC</code>.</p>
-//	 */
-//	public static final int QUEUE_MAJOR_CHANGE= 2;
-	
 	public static final int STATE_REQUEST= 5;
 	
 	
@@ -148,6 +139,116 @@
 	}
 	
 	
+	public static final int IF_ABSENT= 1 << 0;
+	
+	
+	public static class RunnableStatus implements IStatus {
+		
+		
+		private static final IStatus[] NO_CHILDREN= new IStatus[0];
+		
+		
+		private final int severity;
+		
+		private final int code;
+		
+		private final String message;
+		
+		private final ToolRunnable runnable;
+		
+		
+		public RunnableStatus(final int severity, final int code, final String message,
+				final ToolRunnable runnable) {
+			this.severity= severity;
+			this.code= code;
+			this.message= message;
+			this.runnable= runnable;
+		}
+		
+		
+		@Override
+		public int getSeverity() {
+			return this.severity;
+		}
+		
+		@Override
+		public boolean isOK() {
+			return (this.severity == OK);
+		}
+		
+		@Override
+		public boolean matches(final int severityMask) {
+			return (this.severity & severityMask) != 0;
+		}
+		
+		@Override
+		public String getPlugin() {
+			return "Tool.Queue";
+		}
+		
+		@Override
+		public int getCode() {
+			return this.code;
+		}
+		
+		@Override
+		public String getMessage() {
+			return this.message;
+		}
+		
+		public ToolRunnable getRunnable() {
+			return this.runnable;
+		}
+		
+		@Override
+		public @Nullable Throwable getException() {
+			return null;
+		}
+		
+		@Override
+		public boolean isMultiStatus() {
+			return false;
+		}
+		
+		@Override
+		public IStatus[] getChildren() {
+			return NO_CHILDREN;
+		}
+		
+	}
+	
+	public static final IStatus ADDED_STATUS= Status.OK_STATUS;
+	
+	public static final IStatus ALREADY_PRESENT_STATUS= new Status(IStatus.INFO, ADDED_STATUS.getPlugin(), "Already " + ADDED_STATUS.getMessage());
+	
+	
+	private static ToolRunnable checkRunnable(final @Nullable ToolRunnable runnable) {
+		if (runnable == null) {
+			throw new NullPointerException("runnable"); //$NON-NLS-1$
+		}
+		return runnable;
+	}
+	
+	private static ImList<ToolRunnable> checkRunnableList(final @Nullable ToolRunnable runnable) {
+		if (runnable == null) {
+			throw new NullPointerException("runnable"); //$NON-NLS-1$
+		}
+		return ImCollections.newList(runnable);
+	}
+	
+	private static ImList<ToolRunnable> checkRunnableList(final @Nullable List<ToolRunnable> runnables) {
+		if (runnables == null) {
+			throw new NullPointerException("runnables"); //$NON-NLS-1$
+		}
+		final ImList<ToolRunnable> finalRunnables= ImCollections.toList(runnables);
+		final int i= finalRunnables.indexOf(null);
+		if (i >= 0) {
+			throw new NullPointerException("runnable[" + i + ']'); //$NON-NLS-1$
+		}
+		return finalRunnables;
+	}
+	
+	
 	static final int RUN_NONE= -2;
 	static final int RUN_SUSPEND= -1;
 	static final int RUN_CONTROL= 1;
@@ -155,6 +256,7 @@
 	static final int RUN_OTHER= 3;
 	static final int RUN_DEFAULT= 4;
 	
+	
 	private static class RankedItem {
 		
 		final ToolRunnable runnable;
@@ -182,19 +284,126 @@
 		
 	}
 	
-	private final LinkedList<ToolRunnable> list= new LinkedList<>();
-	private ImList<ToolRunnable> singleIOCache= null;
-	private ToolRunnable insertRunnable= null;
-	private final List<ToolRunnable> insertRunnableStack= new ArrayList<>();
-	private int insertIndex= -1;
+	
+	public static interface Section {
+		
+	}
+	
+	private static abstract class SubQueue implements Section {
+		
+		protected final LinkedList<ToolRunnable> list= new LinkedList<>();
+		
+		private @Nullable SubQueue next;
+		
+		private boolean state;
+		
+		
+		public abstract int getAllSize();
+		
+		public abstract int getAll(final ToolRunnable[] array, final int i);
+		
+		public int getStartIdx() {
+			int idx= 0;
+			SubQueue next= this.next;
+			while (next != null) {
+				idx+= next.getAllSize();
+				next= next.next;
+			}
+			return idx;
+		}
+		
+		public int getAppendIdx() {
+			return getStartIdx() + this.list.size();
+		}
+		
+		public void append(final ImList<ToolRunnable> runnables) {
+			if (runnables.size() == 1) {
+				this.list.add(runnables.get(0));
+			}
+			else {
+				this.list.addAll(runnables);
+			}
+		}
+		
+		public void append(final ToolRunnable runnable) {
+			this.list.add(runnable);
+		}
+		
+		public void dispose() {
+			this.state= true;
+			
+			this.list.clear();
+			
+			this.next= null;
+		}
+		
+		public boolean isDispose() {
+			return this.state;
+		}
+		
+	}
+	
+	private static final class TopLevelQueue extends SubQueue {
+		
+		
+		public TopLevelQueue() {
+		}
+		
+		
+		@Override
+		public int getAllSize() {
+			return this.list.size();
+		}
+		
+		@Override
+		public int getAll(final ToolRunnable[] array, int i) {
+			for (final ToolRunnable runnable : this.list) {
+				array[i++]= runnable;
+			}
+			return i;
+		}
+		
+	}
+	
+	private static final class InsertQueue extends SubQueue {
+		
+		protected final ToolRunnable insertRunnable;
+		
+		
+		public InsertQueue(final ToolRunnable insertRunnable) {
+			this.insertRunnable= insertRunnable;
+		}
+		
+		
+		@Override
+		public int getAllSize() {
+			return this.list.size() + 1;
+		}
+		
+		@Override
+		public int getAll(final ToolRunnable[] array, int i) {
+			for (final ToolRunnable runnable : this.list) {
+				array[i++]= runnable;
+			}
+			array[i++]= this.insertRunnable;
+			return i;
+		}
+		
+	}
+	
+	private final List<SubQueue> sectionStack= new ArrayList<>();
+	private final SubQueue topLevelSection; // sectionStack.first
+	private SubQueue currentSection; // sectionStack.last
+	private @Nullable ImList<ToolRunnable> singleIOCache= null;
+	
 	private final Deque<ImList<ToolRunnable>> finishedExpected= new ArrayDeque<>();
 	private final List<DebugEvent> eventList= new ArrayList<>(5);
 	
 	private final ToolProcess process;
 	
-	private boolean resetOnIdle= false;
+	private boolean resetOnIdle= true;
 	private final List<RankedItem> onIdleList= new ArrayList<>();
-	private final LinkedList<ToolRunnable> nextIdleList= new LinkedList<>();
+	private final LinkedList<ToolRunnable> currentIdleList= new LinkedList<>();
 	
 	private final LinkedList<ToolRunnable> hotList= new LinkedList<>();
 	
@@ -204,31 +413,59 @@
 	
 	Queue(final ToolProcess process) {
 		this.process= process;
+		
+		this.topLevelSection= new TopLevelQueue();
+		this.sectionStack.add(this.topLevelSection);
+		this.currentSection= this.topLevelSection;
 	}
 	
 	
 	private final IStatus acceptSubmit(final ToolStatus toolStatus) {
 		if (toolStatus == ToolStatus.TERMINATED) {
 			return new Status(IStatus.ERROR, NicoCore.BUNDLE_ID, -1,
-					NLS.bind(Messages.ToolController_ToolTerminated_message, this.process.getLabel(0)), null);
+					NLS.bind(Messages.ToolController_ToolTerminated_message, this.process.getLabel(0)),
+					null );
 		}
-		return Status.OK_STATUS;
+		return ADDED_STATUS;
+	}
+	
+	private final IStatus acceptSubmit(final ToolStatus toolStatus, final SubQueue section) {
+		if (toolStatus == ToolStatus.TERMINATED) {
+			return new Status(IStatus.ERROR, NicoCore.BUNDLE_ID, -1,
+					NLS.bind(Messages.ToolController_ToolTerminated_message, this.process.getLabel(0)),
+					null );
+		}
+		if (section.isDispose()) {
+			return new Status(IStatus.ERROR, NicoCore.BUNDLE_ID, -1,
+					NLS.bind(Messages.ToolController_QueueSectionTerminated_message, this.process.getLabel(0)),
+					null );
+		}
+		return ADDED_STATUS;
 	}
 	
 	public synchronized void sendElements() {
 		checkIOCache();
-		final ToolRunnable[] queueElements= this.list.toArray(new ToolRunnable[this.list.size()]);
+		final ImList<ToolRunnable> runnables= internal_createCompleteList();
 		final DebugEvent event= new DebugEvent(this, DebugEvent.MODEL_SPECIFIC, QUEUE_INFO);
-		event.setData(queueElements);
+		event.setData(runnables);
 		this.eventList.add(event);
-		internalFireEvents();
+		internal_fireEvents();
 	}
 	
-	public synchronized int size() {
+	public Section getTopLevelSection() {
+		return this.topLevelSection;
+	}
+	
+	public synchronized Section getCurrentSection() {
+		return this.currentSection;
+	}
+	
+	
+	public synchronized int getCurrentSize() {
 		if (this.singleIOCache != null) {
 			return 1;
 		}
-		return this.list.size();
+		return this.currentSection.list.size();
 	}
 	
 	/**
@@ -241,10 +478,9 @@
 	 */
 	@Override
 	public IStatus add(final ToolRunnable runnable) {
-		if (runnable == null) {
-			throw new NullPointerException("runnable"); //$NON-NLS-1$
-		}
-		return doAdd(ImCollections.newList(runnable));
+		final ImList<ToolRunnable> checkedRunnables= checkRunnableList(runnable);
+		
+		return doAdd(checkedRunnables, null);
 	}
 	
 	/**
@@ -256,128 +492,131 @@
 	 * @return the status of the queue operation.
 	 */
 	public IStatus add(final List<ToolRunnable> runnables) {
-		if (runnables == null) {
-			throw new NullPointerException("runnables"); //$NON-NLS-1$
-		}
-		final ImList<ToolRunnable> finalRunnables= ImCollections.toList(runnables);
-		for (int i= 0; i < finalRunnables.size(); i++) {
-			if (runnables.get(i) == null) {
-				throw new NullPointerException("runnable["+i+']'); //$NON-NLS-1$
-			}
-		}
-		return doAdd(finalRunnables);
+		final ImList<ToolRunnable> checkedRunnables= checkRunnableList(runnables);
+		
+		return doAdd(checkedRunnables, null);
 	}
 	
-	private synchronized IStatus doAdd(final ImList<ToolRunnable> runnables) {
+	/**
+	 * Submits the runnable for the tool.
+	 * <p>
+	 * The runnable will be added to the queue in the specified section
+	 * and will be run, if it's its turn.
+	 * 
+	 * @param runnable the runnable to add
+	 * @param section where to add the runnable, <code>null</code> for current section
+	 * @param strategy flags {@link #IF_ABSENT}
+	 * @return the status of the queue operation.
+	 */
+	public IStatus add(final ToolRunnable runnable,
+			final @Nullable Section section, final int strategy) {
+		final ToolRunnable checkedRunnable= checkRunnable(runnable);
+		
+		return doAdd(checkedRunnable, (SubQueue) section, strategy);
+	}
+	
+	/**
+	 * Submits the runnables for the tool.
+	 * <p>
+	 * The runnables will be added en block to the queue in the specified section
+	 * and will be runned, if it's its turn.
+	 * 
+	 * @param runnables the runnables to add.
+	 * @param section where to add the runnabler, <code>null</code> for current section
+	 * @return the status of the queue operation.
+	 */
+	public IStatus add(final List<ToolRunnable> runnables,
+			final @Nullable Section section) {
+		final ImList<ToolRunnable> checkedRunnables= checkRunnableList(runnables);
+		
+		return doAdd(checkedRunnables, (SubQueue) section);
+	}
+	
+	private synchronized IStatus doAdd(final ImList<ToolRunnable> runnables,
+			@Nullable SubQueue section) {
+		if (section == null) {
+			section= this.currentSection;
+		}
+		
 		final ToolStatus toolStatus= this.process.getToolStatus();
-		final IStatus status= acceptSubmit(toolStatus);
+		final IStatus status= acceptSubmit(toolStatus, section);
 		if (status.getSeverity() < IStatus.ERROR) {
 			if (toolStatus.isWaiting()) {
-				internalAdd(runnables, true);
+				internal_add(runnables, section, true);
 				notifyAll();
 			}
 			else {
-				internalAdd(runnables, false);
+				internal_add(runnables, section, false);
 			}
 		}
 		return status;
 	}
 	
+	private synchronized IStatus doAdd(final ToolRunnable runnable,
+			@Nullable SubQueue section, final int strategy) {
+		if (section == null) {
+			section= this.currentSection;
+		}
+		
+		final ToolStatus toolStatus= this.process.getToolStatus();
+		final IStatus status= acceptSubmit(toolStatus, section);
+		if (status.getSeverity() < IStatus.ERROR) {
+			if (strategy != 0) {
+				checkIOCache();
+				
+				int idx;
+				if ((strategy & IF_ABSENT) != 0 && (idx= section.list.indexOf(runnable)) >= 0) {
+					return new RunnableStatus(IStatus.INFO, IF_ABSENT, "OK", section.list.get(idx)); //$NON-NLS-1$
+				}
+			}
+			
+			if (toolStatus.isWaiting()) {
+				internal_add(ImCollections.newList(runnable), section, true);
+				notifyAll();
+			}
+			else {
+				internal_add(ImCollections.newList(runnable), section, false);
+			}
+			return new RunnableStatus(IStatus.OK, 0, "OK", runnable); //$NON-NLS-1$
+		}
+		return status;
+	}
+	
 	@Override
 	public void remove(final ToolRunnable runnable) {
-		if (runnable == null) {
-			throw new NullPointerException("runnable"); //$NON-NLS-1$
-		}
-		doRemove(new ToolRunnable[] { runnable });
-	}
-	
-	public void remove(final ToolRunnable[] runnables) {
-		if (runnables == null) {
-			throw new NullPointerException("runnables"); //$NON-NLS-1$
-		}
-		for (int i= 0; i < runnables.length; i++) {
-			if (runnables[i] == null) {
-				throw new NullPointerException("runnable["+i+']'); //$NON-NLS-1$
-			}
-		}
-		doRemove(runnables);
-	}
-	
-	private void doRemove(final ToolRunnable[] runnables) {
+		final ImList<ToolRunnable> checkedRunnables= checkRunnableList(runnable);
+		
 		synchronized (this) {
-			checkIOCache();
-			final List<ToolRunnable> removed= new ArrayList<>(runnables.length);
-			boolean checkInsert= false;
-			for (final ToolRunnable runnable : runnables) {
-				final int index= this.list.indexOf(runnable);
-				if (index >= 0 && runnable.changed(ToolRunnable.REMOVING_FROM, this.process)) {
-					this.list.remove(index);
-					removed.add(runnable);
-					if (!checkInsert && index < this.insertIndex) {
-						checkInsert= true;
-					}
-				}
-			}
-			if (checkInsert) {
-				this.insertIndex= this.list.indexOf(this.insertRunnable);
-			}
-	//		IToolRunnable[] queueElements= fList.toArray(new IToolRunnable[fList.size()]);
-	//		addDebugEvent(COMPLETE_CHANGE, queueElements);
-			final ImList<ToolRunnable> finalRunnables= ImCollections.toList(removed);
-			addChangeEvent(ToolRunnable.REMOVING_FROM, finalRunnables);
-			internalFireEvents();
+			internal_remove(checkedRunnables);
+			
+			internal_fireEvents();
 		}
 	}
 	
-	public void move(final ToolRunnable[] runnables, final Queue to) {
-		if (runnables == null) {
-			throw new NullPointerException("runnables"); //$NON-NLS-1$
+	public void remove(final List<ToolRunnable> runnables) {
+		final ImList<ToolRunnable> checkedRunnables= checkRunnableList(runnables);
+		
+		synchronized (this) {
+			internal_remove(checkedRunnables);
+			
+			internal_fireEvents();
 		}
+	}
+	
+	public void move(final ImList<ToolRunnable> runnables, final Queue to) {
+		final ImList<ToolRunnable> checkedRunnables= checkRunnableList(runnables);
 		if (to == null) {
 			throw new NullPointerException("to"); //$NON-NLS-1$
 		}
+		
 		final ImList<ToolRunnable> finalRunnables;
 		synchronized (this) {
-			checkIOCache();
-			final List<ToolRunnable> removed= new ArrayList<>(runnables.length);
-			boolean checkInsert= false;
-			for (final ToolRunnable runnable : runnables) {
-				final int index= this.list.indexOf(runnable);
-				if (index >= 0 && runnable.changed(ToolRunnable.MOVING_FROM, this.process)) {
-					this.list.remove(index);
-					removed.add(runnable);
-					if (!checkInsert && index < this.insertIndex) {
-						checkInsert= true;
-					}
-				}
-			}
-			if (checkInsert) {
-				this.insertIndex= this.list.indexOf(this.insertRunnable);
-			}
-			finalRunnables= ImCollections.toList(removed);
-			addChangeEvent(ToolRunnable.MOVING_FROM, finalRunnables);
-			internalFireEvents();
+			finalRunnables= internal_moveFrom(checkedRunnables);
 		}
 		
 		synchronized (to) {
-			to.checkIOCache();
-			if (to == this && this.insertIndex >= 0) {
-				this.list.addAll(this.insertIndex, finalRunnables);
-				for (final ToolRunnable runnable : finalRunnables) {
-					runnable.changed(ToolRunnable.MOVING_TO, to.process);
-				}
-				addContentEvent(DebugEvent.CHANGE, DebugEvent.CONTENT,
-						new TaskDelta(ToolRunnable.MOVING_TO, this.insertIndex, finalRunnables));
-				this.insertIndex+= finalRunnables.size();
-			}
-			else {
-				to.list.addAll(finalRunnables);
-				for (final ToolRunnable runnable : finalRunnables) {
-					runnable.changed(ToolRunnable.MOVING_TO, to.process);
-				}
-				to.addChangeEvent(ToolRunnable.MOVING_TO, finalRunnables);
-			}
-			to.internalFireEvents();
+			to.internal_moveTo(finalRunnables, to.currentSection);
+			
 			to.notifyAll();
 		}
 	}
@@ -389,49 +628,44 @@
 		final ImList<ToolRunnable> finalRunnables;
 		synchronized (this) {
 			checkIOCache();
-			final List<ToolRunnable> removed= new ArrayList<>(this.list.size());
-			for (final Iterator<ToolRunnable> iter= this.list.iterator(); iter.hasNext();) {
-				final ToolRunnable runnable= iter.next();
-				if (runnable.changed(ToolRunnable.MOVING_FROM, this.process)) {
-					iter.remove();
-					removed.add(runnable);
+			
+			{	final List<ToolRunnable> removed= new ArrayList<>(internal_getCompleteSize());
+				for (final Iterator<ToolRunnable> iter= this.topLevelSection.list.iterator(); iter.hasNext();) {
+					final ToolRunnable runnable= iter.next();
+					if (runnable.changed(ToolRunnable.MOVING_FROM, this.process)) {
+						iter.remove();
+						removed.add(runnable);
+					}
 				}
+				finalRunnables= ImCollections.toList(removed);
 			}
-			if (this.insertIndex >= 0) {
-				this.insertIndex= this.list.indexOf(this.insertRunnable);
-			}
-			finalRunnables= ImCollections.toList(removed);
-			addChangeEvent(ToolRunnable.MOVING_FROM, finalRunnables);
-			internalFireEvents();
+			
+			addContentChangeEvent(new TaskDelta(ToolRunnable.MOVING_FROM, -1, finalRunnables));
+			
+			internal_fireEvents();
 		}
 		
 		synchronized (to) {
-			to.checkIOCache();
-			to.list.addAll(finalRunnables);
-			for (final ToolRunnable runnable : finalRunnables) {
-				runnable.changed(ToolRunnable.MOVING_TO, to.process);
-			}
-			to.addChangeEvent(ToolRunnable.MOVING_TO, finalRunnables);
-			to.internalFireEvents();
+			to.internal_moveTo(finalRunnables, to.topLevelSection);
+			
 			to.notifyAll();
 		}
 	}
 	
 	public IStatus addOnIdle(final SystemRunnable runnable, final int rank) {
-		if (runnable == null) {
-			throw new NullPointerException("runnable"); //$NON-NLS-1$
-		}
+		final ToolRunnable checkedRunnable= checkRunnable(runnable);
+		
 		synchronized (this) {
 			final ToolStatus toolStatus= this.process.getToolStatus();
 			final IStatus status= acceptSubmit(toolStatus);
 			if (status.getSeverity() < IStatus.ERROR) {
-				final RankedItem item= new RankedItem(runnable, rank);
+				final RankedItem item= new RankedItem(checkedRunnable, rank);
 				
 				int idx= this.onIdleList.indexOf(item);
 				if (idx >= 0 && this.onIdleList.get(idx).rank != rank) {
 					this.onIdleList.remove(idx);
 					if (!this.resetOnIdle) {
-						this.nextIdleList.remove(item.runnable);
+						this.currentIdleList.remove(item.runnable);
 					}
 					idx= -1;
 				}
@@ -445,14 +679,14 @@
 					this.onIdleList.add(idx, item);
 					
 					if (!this.resetOnIdle) {
-						if (idx == this.onIdleList.size()-1) { // last
-							this.nextIdleList.add(item.runnable);
+						if (idx == this.onIdleList.size() - 1) { // last
+							this.currentIdleList.add(item.runnable);
 						}
 						else {
-							final RankedItem next= this.onIdleList.get(idx+1);
-							final int nextIdx= this.nextIdleList.indexOf(next.runnable);
+							final RankedItem next= this.onIdleList.get(idx + 1);
+							final int nextIdx= this.currentIdleList.indexOf(next.runnable);
 							if (nextIdx >= 0) {
-								this.nextIdleList.add(nextIdx, item.runnable);
+								this.currentIdleList.add(nextIdx, item.runnable);
 							}
 						}
 					}
@@ -464,12 +698,11 @@
 	}
 	
 	public void removeOnIdle(final ToolRunnable runnable) {
-		if (runnable == null) {
-			throw new NullPointerException("runnable"); //$NON-NLS-1$
-		}
+		final ToolRunnable checkedRunnable= checkRunnable(runnable);
+		
 		synchronized (this) {
-			this.onIdleList.remove(new RankedItem(runnable, 0));
-			this.nextIdleList.remove(runnable);
+			this.onIdleList.remove(new RankedItem(checkedRunnable, 0));
+			this.currentIdleList.remove(runnable);
 		}
 	}
 	
@@ -484,19 +717,18 @@
 	}
 	
 	public IStatus addHot(final ToolRunnable runnable, final int strategy) {
-		if (runnable == null) {
-			throw new NullPointerException("runnable"); //$NON-NLS-1$
-		}
+		final ToolRunnable checkedRunnable= checkRunnable(runnable);
+		
 		synchronized (this) {
 			final ToolStatus toolStatus= this.process.getToolStatus();
 			final IStatus status= acceptSubmit(toolStatus);
 			if (status.getSeverity() < IStatus.ERROR) {
-				if ((strategy & 1) != 0) {
-					if (this.hotList.contains(runnable)) {
-						return Status.OK_STATUS;
+				if ((strategy & IF_ABSENT) != 0) {
+					if (this.hotList.contains(checkedRunnable)) {
+						return ALREADY_PRESENT_STATUS;
 					}
 				}
-				this.hotList.add(runnable);
+				this.hotList.add(checkedRunnable);
 				if (this.hotList.size() > 0) {
 					notifyAll();
 					final ToolController controller= this.process.getController();
@@ -511,120 +743,191 @@
 	
 	@Override
 	public void removeHot(final ToolRunnable runnable) {
-		if (runnable == null) {
-			throw new NullPointerException("runnable"); //$NON-NLS-1$
-		}
+		final ToolRunnable checkedRunnable= checkRunnable(runnable);
 		synchronized (this) {
-			if (this.hotList.remove(runnable)) {
+			if (this.hotList.remove(checkedRunnable)) {
 				runnable.changed(ToolRunnable.REMOVING_FROM, this.process);
 			}
 		}
 	}
 	
 	
-	void internalAdd(final ImList<ToolRunnable> runnables, final boolean allowCache) {
-		if (allowCache && this.singleIOCache == null && this.list.isEmpty()
-				&& runnables.size() == 1) {
+	private void internal_add(final ImList<ToolRunnable> runnables, final SubQueue section,
+			final boolean allowCache) {
+		if (allowCache && this.singleIOCache == null
+				&& runnables.size() == 1
+				&& section == this.currentSection && this.currentSection.list.isEmpty() ) {
 			this.singleIOCache= runnables;
 			return;
 		}
 		
 		checkIOCache();
-		if (this.insertIndex >= 0)  {
-			if (runnables.size() == 1) {
-				this.list.add(this.insertIndex, runnables.get(0));
-			}
-			else {
-				this.list.addAll(this.insertIndex, runnables);
-			}
-			addContentEvent(DebugEvent.CHANGE, DebugEvent.CONTENT,
-					new TaskDelta(ToolRunnable.ADDING_TO, this.insertIndex, runnables) );
-			this.insertIndex += runnables.size();
-		}
-		else {
-			if (runnables.size() == 1) {
-				this.list.add(runnables.get(0));
-			}
-			else {
-				this.list.addAll(runnables);
-			}
-			addContentEvent(DebugEvent.CHANGE, DebugEvent.CONTENT,
-					new TaskDelta(ToolRunnable.ADDING_TO, -1, runnables) );
-		}
-		internalFireEvents();
+		
+		final int idx= section.getAppendIdx();
+		section.append(runnables);
+		addContentChangeEvent(new TaskDelta(ToolRunnable.ADDING_TO, idx, runnables));
+		
+		internal_fireEvents();
 	}
 	
-	void internalAddInsert(final ToolRunnable runnable) {
+	private void internal_remove(final ImList<ToolRunnable> runnables) {
 		checkIOCache();
 		
-		this.insertRunnableStack.add(runnable);
-		this.insertRunnable= runnable;
-		this.insertIndex= 0;
-		this.list.add(0, runnable);
-		addContentEvent(DebugEvent.CHANGE, DebugEvent.CONTENT,
-				new TaskDelta(ToolRunnable.ADDING_TO, 0, ImCollections.newList(runnable)) );
-		internalFireEvents();
-	}
-	
-	void internalRemoveInsert(final ToolRunnable runnable) {
-		checkIOCache();
-		
-		final List<ToolRunnable> removed= new ArrayList<>(this.insertIndex+1);
-		final int insertStackIdx= this.insertRunnableStack.indexOf(runnable);
-		if (insertStackIdx < 0) {
-			return;
-		}
-		final Iterator<ToolRunnable> iter= this.list.iterator();
-		if (insertStackIdx < this.insertRunnableStack.size()-1) {
-			final ToolRunnable start= this.insertRunnableStack.get(insertStackIdx+1);
-			while (iter.hasNext()) {
-				if (iter.next() == start) {
-					break;
+		final ImList<ToolRunnable> finalRunnables;
+		{	final List<ToolRunnable> removed= new ArrayList<>(runnables.size());
+			
+			ITER_RUNNABLE: for (final ToolRunnable runnable : runnables) {
+				for (int iSection= this.sectionStack.size() - 1; iSection >= 0; iSection--) {
+					final SubQueue section= this.sectionStack.get(iSection);
+					final int idx= section.list.indexOf(runnable);
+					if (idx >= 0) {
+						if (runnable.changed(ToolRunnable.REMOVING_FROM, this.process)) {
+							section.list.remove(idx);
+							removed.add(runnable);
+						}
+						continue ITER_RUNNABLE;
+					}
 				}
 			}
+			finalRunnables= ImCollections.toList(removed);
 		}
-		while (iter.hasNext()) {
-			final ToolRunnable toRemove= iter.next();
-			if (toRemove == runnable) {
-				iter.remove();
-				removed.add(toRemove);
-				break;
+		
+		addContentChangeEvent(new TaskDelta(ToolRunnable.REMOVING_FROM, -1, finalRunnables));
+		
+		internal_fireEvents();
+	}
+	
+	private ImList<ToolRunnable> internal_moveFrom(final ImList<ToolRunnable> runnables) {
+		checkIOCache();
+		
+		final ImList<ToolRunnable> finalRunnables;
+		{	final List<ToolRunnable> removed= new ArrayList<>(runnables.size());
+			
+			ITER_RUNNABLE: for (final ToolRunnable runnable : runnables) {
+				for (int iSection= this.sectionStack.size() - 1; iSection >= 0; iSection--) {
+					final SubQueue section= this.sectionStack.get(iSection);
+					final int idx= section.list.indexOf(runnable);
+					if (idx >= 0) {
+						if (runnable.changed(ToolRunnable.MOVING_FROM, this.process)) {
+							section.list.remove(idx);
+							removed.add(runnable);
+						}
+						continue ITER_RUNNABLE;
+					}
+				}
 			}
-			if (toRemove.changed(ToolRunnable.REMOVING_FROM, this.process)) {
-				iter.remove();
-				removed.add(toRemove);
+			finalRunnables= ImCollections.toList(removed);
+		}
+		
+		addContentChangeEvent(new TaskDelta(ToolRunnable.MOVING_FROM, -1, finalRunnables));
+		
+		internal_fireEvents();
+		
+		return finalRunnables;
+	}
+	
+	private void internal_moveTo(final ImList<ToolRunnable> runnables, final SubQueue section) {
+		checkIOCache();
+		
+		final int idx= section.getAppendIdx();
+		section.append(runnables);
+		addContentChangeEvent(new TaskDelta(ToolRunnable.MOVING_TO, idx, runnables));
+		
+		internal_fireEvents();
+	}
+	
+	
+	Section internal_addInsert(final ToolRunnable runnable) {
+		assert (runnable != null);
+		
+		checkIOCache();
+		
+		final SubQueue section= new InsertQueue(runnable);
+		this.sectionStack.add(section);
+		this.currentSection.next= section;
+		this.currentSection= section;
+		addContentChangeEvent(new TaskDelta(ToolRunnable.ADDING_TO, 0, ImCollections.newList(runnable)));
+		
+		internal_resetOnIdle();
+		
+		internal_fireEvents();
+		
+		return section;
+	}
+	
+	void internal_removeInsert(final Section section) {
+		assert (section != null);
+		
+		checkIOCache();
+		
+		final int sectionIdx= this.sectionStack.indexOf(section);
+		if (sectionIdx < 0) {
+			return;
+		}
+		if (sectionIdx == 0) {
+			throw new IllegalArgumentException();
+		}
+		
+		final ImList<ToolRunnable> finalRunnables;
+		{	final ToolRunnable[] runnables= new @NonNull ToolRunnable[
+					internal_getSize(sectionIdx, this.sectionStack.size()) ];
+			int i= 0;
+			for (int iSection= this.sectionStack.size() - 1; iSection >= sectionIdx; iSection--) {
+				final SubQueue removedSection= this.sectionStack.remove(iSection);
+				i= removedSection.getAll(runnables, i);
+				removedSection.dispose();
+			}
+			this.currentSection= this.sectionStack.get(this.sectionStack.size() - 1);
+			this.currentSection.next= null;
+			
+			finalRunnables= ImCollections.newList(runnables);
+		}
+		for (final ToolRunnable runnable : finalRunnables) {
+//			runnable.changed(ToolRunnable.BEING_ABANDONED, this.process);
+			if (!runnable.changed(ToolRunnable.REMOVING_FROM, this.process)) {
+				runnable.changed(ToolRunnable.BEING_ABANDONED, this.process);
 			}
 		}
-		final ImList<ToolRunnable> finalRunnables= ImCollections.toList(removed);
-		addChangeEvent(ToolRunnable.REMOVING_FROM, finalRunnables);
-		this.insertRunnableStack.remove(runnable);
-		if (this.insertRunnableStack.isEmpty()) {
-			this.insertRunnable= null;
-			this.insertIndex= -1;
-		}
-		else {
-			this.insertRunnable= this.insertRunnableStack.get(this.insertRunnableStack.size()-1);
-			this.insertIndex= this.list.indexOf(this.insertRunnable);
-		}
-		internalFireEvents();
-	}
-	
-	void internalScheduleIdle(final ToolRunnable runnable) {
-		if (runnable == null) {
-			throw new NullPointerException();
-		}
-		this.nextIdleList.add(runnable);
-	}
-	
-	void internalRemoveIdle(final ToolRunnable runnable) {
-		if (runnable == null) {
-			throw new NullPointerException();
-		}
-		this.nextIdleList.remove(runnable);
+		
+		addContentChangeEvent(new TaskDelta(ToolRunnable.REMOVING_FROM, -1, finalRunnables));
+		
+		internal_resetOnIdle();
+		
+		internal_fireEvents();
 	}
 	
 	
-	int internalNext() {
+	void internal_scheduleIdle(final ToolRunnable runnable) {
+		final ToolRunnable checkedRunnable= checkRunnable(runnable);
+		
+		this.currentIdleList.add(checkedRunnable);
+	}
+	
+	void internal_removeIdle(final ToolRunnable runnable) {
+		final ToolRunnable checkedRunnable= checkRunnable(runnable);
+		
+		this.currentIdleList.remove(checkedRunnable);
+	}
+	
+	
+	void internal_check() {
+		checkIOCache();
+		internal_fireEvents();
+	}
+	
+	void internal_resetOnIdle() {
+		this.resetOnIdle= true;
+	}
+	
+	private void internal_resetIdleList() {
+		this.resetOnIdle= false;
+		this.currentIdleList.clear();
+		for (int i= 0; i < this.onIdleList.size(); i++) {
+			this.currentIdleList.add(this.onIdleList.get(i).runnable);
+		}
+	}
+	
+	int internal_next() {
 		if (!this.hotList.isEmpty()) {
 			return RUN_HOT;
 		}
@@ -632,104 +935,99 @@
 		if (this.singleIOCache != null) {
 			runnable= this.singleIOCache.get(0);
 		}
-		else if (!this.list.isEmpty() && this.insertIndex != 0) {
-			runnable= this.list.get(0);
-		}
-		else if (!this.nextIdleList.isEmpty()) {
-			runnable= this.nextIdleList.get(0);
+		else if (!this.currentSection.list.isEmpty()) {
+			runnable= this.currentSection.list.peekFirst();
 		}
 		else {
-			return RUN_NONE;
+			if (this.resetOnIdle) {
+				internal_resetIdleList();
+			}
+			if (!this.currentIdleList.isEmpty()) {
+				runnable= this.currentIdleList.peekFirst();
+			}
+			else {
+				return RUN_NONE;
+			}
 		}
 		return (runnable instanceof SystemRunnable) ?
 				RUN_OTHER : RUN_DEFAULT;
 	}
 	
-	boolean internalNextHot() {
-		return !this.hotList.isEmpty();
-	}
-	
-	void internalCheck() {
-		checkIOCache();
-		internalFireEvents();
-	}
-	
-	void internalResetIdle() {
-		this.resetOnIdle= false;
-		for (int i= this.onIdleList.size()-1; i >= 0; i--) {
-			if (!this.nextIdleList.remove(this.onIdleList.get(i).runnable)) {
-				break;
-			}
-		}
-		for (int i= 0; i < this.onIdleList.size(); i++) {
-			this.nextIdleList.add(this.onIdleList.get(i).runnable);
-		}
-	}
-	
-	
-	ToolRunnable internalPoll() {
-		final ImList<ToolRunnable> finalRunnable;
+	/** After check with {@link #internal_next()} */
+	ToolRunnable internal_poll() {
+		final @NonNull ImList<ToolRunnable> finalRunnable;
 		if (this.singleIOCache != null) {
 			finalRunnable= this.singleIOCache;
-			if (this.insertIndex >= 0) {
-				addContentEvent(DebugEvent.CHANGE, DebugEvent.CONTENT,
-						new TaskDelta(ToolRunnable.ADDING_TO, this.insertIndex, this.singleIOCache));
-			}
-			else {
-				addContentEvent(DebugEvent.CHANGE, DebugEvent.CONTENT,
-						new TaskDelta(ToolRunnable.ADDING_TO, -1, this.singleIOCache));
-			}
+			final int idx= this.currentSection.getAppendIdx();
+			addContentChangeEvent(new TaskDelta(ToolRunnable.ADDING_TO, idx, finalRunnable));
 			this.singleIOCache= null;
-			if (this.resetOnIdle) {
-				internalResetIdle();
-			}
+			internal_resetOnIdle();
 		}
-		else if (!this.list.isEmpty() && this.insertIndex != 0) {
-			finalRunnable= ImCollections.newList(this.list.poll());
-			if (this.insertIndex >= 0) {
-				this.insertIndex--;
-			}
-			if (this.resetOnIdle) {
-				internalResetIdle();
-			}
+		else if (!this.currentSection.list.isEmpty()) {
+			finalRunnable= ImCollections.newList(this.currentSection.list.pollFirst());
+			internal_resetOnIdle();
 		}
 		else {
-			finalRunnable= ImCollections.newList(this.nextIdleList.poll());
-			this.resetOnIdle= true;
+			finalRunnable= ImCollections.newList(this.currentIdleList.pollFirst());
 		}
-		addChangeEvent(ToolRunnable.STARTING, finalRunnable);
+		addContentChangeEvent(new TaskDelta(ToolRunnable.STARTING, -1, finalRunnable));
 		
-		internalFireEvents();
+		internal_fireEvents();
 		this.finishedExpected.push(finalRunnable);
 		return finalRunnable.get(0);
 	}
 	
-	ToolRunnable internalPollHot() {
-		return this.hotList.poll();
+	boolean internal_nextHot() {
+		return !this.hotList.isEmpty();
+	}
+	
+	@Nullable ToolRunnable internal_pollHot() {
+		return this.hotList.pollFirst();
 	}
 	
 	/**
 	 * Not necessary in synchronized block
 	 */
-	void internalFinished(final ToolRunnable runnable, final int detail) {
+	void internal_onFinished(final ToolRunnable runnable, final int detail) {
 		assert (runnable == this.finishedExpected.peek().get(0));
 		
-		addChangeEvent(detail, this.finishedExpected.poll());
+		addContentChangeEvent(new TaskDelta(detail, -1, this.finishedExpected.poll()));
 	}
 	
-	List<ToolRunnable> internalGetList() {
-		internalCheck();
-		return this.list;
+	private int internal_getCompleteSize() {
+		int size= 0;
+		for (int iSection= this.sectionStack.size() - 1; iSection >= 0; iSection--) {
+			size+= this.sectionStack.get(iSection).getAllSize();
+		}
+		return size;
 	}
 	
-	List<ToolRunnable> internalGetCurrentList() {
-		internalCheck();
-		if (this.insertIndex >= 0) {
-			return this.list.subList(0, this.insertIndex);
+	private int internal_getSize(final int startSection, final int endSection) {
+		int size= 0;
+		for (int iSection= endSection - 1; iSection >= startSection; iSection--) {
+			size+= this.sectionStack.get(iSection).getAllSize();
 		}
-		else {
-			return this.list;
+		return size;
+	}
+	
+	private ImList<ToolRunnable> internal_createCompleteList() {
+		final ToolRunnable[] runnables= new @NonNull ToolRunnable[
+				internal_getCompleteSize() ];
+		int i= 0;
+		for (int iSection= this.sectionStack.size() - 1; iSection >= 0; iSection--) {
+			i= this.sectionStack.get(iSection).getAll(runnables, i);
 		}
+		return ImCollections.newList(runnables);
+	}
+	
+	List<ToolRunnable> internal_getList() {
+		internal_check();
+		return internal_createCompleteList();
+	}
+	
+	List<ToolRunnable> internal_getCurrentList() {
+		internal_check();
+		return this.currentSection.list;
 	}
 	
 	
@@ -746,7 +1044,7 @@
 			this.stateRequest= PAUSED_STATE;
 			addStateEvent(DebugEvent.MODEL_SPECIFIC, STATE_REQUEST,
 					new StateDelta(oldState, PAUSED_STATE));
-			internalFireEvents();
+			internal_fireEvents();
 			notifyAll();
 		}
 		return true;
@@ -761,13 +1059,13 @@
 			this.stateRequest= PROCESSING_STATE;
 			addStateEvent(DebugEvent.MODEL_SPECIFIC, STATE_REQUEST,
 					new StateDelta(oldState, PROCESSING_STATE) );
-			internalFireEvents();
+			internal_fireEvents();
 			notifyAll();
 		}
 		return true;
 	}
 	
-	void internalStatusChanged(final ToolStatus newStatus) {
+	void internal_onStatusChanged(final ToolStatus newStatus) {
 		final byte oldState= this.state;
 		switch (newStatus) {
 		case STARTED_IDLING:
@@ -800,12 +1098,12 @@
 		}
 	}
 	
-	boolean internalIsPauseRequested() {
+	boolean internal_isPauseRequested() {
 		return (this.stateRequest == PAUSED_STATE);
 	}
 	
 	
-	void dispose() {
+	void internal_dispose() {
 		checkIOCache();
 		
 		final byte oldState= this.state;
@@ -815,57 +1113,62 @@
 					new StateDelta(oldState, TERMINATED_STATE) );
 		}
 		
-		if (!this.list.isEmpty()) {
-			final ImList<ToolRunnable> finalRunnables= ImCollections.toList(this.list);
-			for (final ToolRunnable runnable : finalRunnables) {
+		final ImList<ToolRunnable> finalRunnables;
+		{	final ToolRunnable[] runnables= new @NonNull ToolRunnable[
+					internal_getCompleteSize() ];
+			int i= 0;
+			for (int iSection= this.sectionStack.size() - 1; iSection >= 1; iSection--) {
+				final SubQueue removedSection= this.sectionStack.remove(iSection);
+				i= removedSection.getAll(runnables, i);
+				removedSection.dispose();
+			}
+			{	// iSection == 0
+				i= this.topLevelSection.getAll(runnables, i);
+				this.topLevelSection.dispose();
+			}
+			this.currentSection= this.topLevelSection;
+			this.currentSection.next= null;
+			
+			finalRunnables= ImCollections.newList(runnables);
+		}
+		for (final ToolRunnable runnable : finalRunnables) {
+			runnable.changed(ToolRunnable.BEING_ABANDONED, this.process);
+		}
+		addTerminateEvent(new TaskDelta(ToolRunnable.BEING_ABANDONED, -1, finalRunnables));
+		
+		if (!this.hotList.isEmpty()){
+			final ImList<ToolRunnable> leftRunnable= ImCollections.toList(this.hotList);
+			this.hotList.clear();
+			
+			for (final ToolRunnable runnable : leftRunnable) {
 				runnable.changed(ToolRunnable.BEING_ABANDONED, this.process);
 			}
-			addContentEvent(DebugEvent.TERMINATE, DebugEvent.UNSPECIFIED,
-					new TaskDelta(ToolRunnable.BEING_ABANDONED, -1, finalRunnables) );
-			this.list.clear();
-		}
-		if (!this.hotList.isEmpty()){
-			final ToolRunnable[] array= this.hotList.toArray(new ToolRunnable[this.hotList.size()]);
-			for (int i= 0; i < array.length; i++) {
-				array[i].changed(ToolRunnable.BEING_ABANDONED, this.process);
-			}
-			this.hotList.clear();
 		}
 		if (!this.onIdleList.isEmpty()){
-			final RankedItem[] array= this.onIdleList.toArray(new RankedItem[this.onIdleList.size()]);
-			for (int i= 0; i < array.length; i++) {
-				array[i].runnable.changed(ToolRunnable.BEING_ABANDONED, this.process);
-			}
+			final ImList<RankedItem> leftRunnable= ImCollections.toList(this.onIdleList);
 			this.onIdleList.clear();
+			
+			for (final RankedItem item : leftRunnable) {
+				item.runnable.changed(ToolRunnable.BEING_ABANDONED, this.process);
+			}
 		}
 		
-		internalFireEvents();
+		internal_fireEvents();
 	}
 	
 	
 	private void checkIOCache() {
 		if (this.singleIOCache != null) {
-			if (this.insertIndex >= 0) {
-				addContentEvent(DebugEvent.CHANGE, DebugEvent.CONTENT,
-						new TaskDelta(ToolRunnable.ADDING_TO, this.insertIndex, this.singleIOCache) );
-				this.list.add(this.insertIndex, this.singleIOCache.get(0));
-				this.insertIndex++;
-			}
-			else {
-				addContentEvent(DebugEvent.CHANGE, DebugEvent.CONTENT,
-						new TaskDelta(ToolRunnable.ADDING_TO, -1, this.singleIOCache) );
-				this.list.add(this.singleIOCache.get(0));
-			}
+			final int idx= this.currentSection.getAppendIdx();
+			this.currentSection.append(this.singleIOCache.get(0));
+			addContentChangeEvent(new TaskDelta(ToolRunnable.ADDING_TO, idx, this.singleIOCache));
 			this.singleIOCache= null;
 		}
 	}
 	
-	private void addChangeEvent(final int deltaType, final ImList<ToolRunnable> deltaData) {
-		addContentEvent(DebugEvent.CHANGE, DebugEvent.CONTENT, new TaskDelta(deltaType, -1, deltaData));
-	}
 	
-	private void addContentEvent(final int code, final int detail, final TaskDelta delta) {
-		final DebugEvent event= new DebugEvent(this, code, detail);
+	private void addContentChangeEvent(final TaskDelta delta) {
+		final DebugEvent event= new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT);
 		event.setData(delta);
 		this.eventList.add(event);
 	}
@@ -876,11 +1179,17 @@
 		this.eventList.add(event);
 	}
 	
-	List<DebugEvent> internalGetEventList() {
+	private void addTerminateEvent(final TaskDelta delta) {
+		final DebugEvent event= new DebugEvent(this, DebugEvent.TERMINATE, DebugEvent.UNSPECIFIED);
+		event.setData(delta);
+		this.eventList.add(event);
+	}
+	
+	List<DebugEvent> internal_getEventList() {
 		return this.eventList;
 	}
 	
-	void internalFireEvents() {
+	void internal_fireEvents() {
 		if (this.eventList.isEmpty()) {
 			return;
 		}
diff --git a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/ToolController.java b/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/ToolController.java
index 484a63d..6f53571 100644
--- a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/ToolController.java
+++ b/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/ToolController.java
@@ -56,6 +56,7 @@
 import org.eclipse.statet.internal.nico.core.RunnableProgressMonitor;
 import org.eclipse.statet.nico.core.NicoCore;
 import org.eclipse.statet.nico.core.NicoCoreMessages;
+import org.eclipse.statet.nico.core.runtime.Queue.Section;
 
 
 /**
@@ -360,14 +361,13 @@
 		
 		protected void submitToConsole(final String print, final String send,
 				final IProgressMonitor monitor) throws CoreException {
-			final ToolRunnable savedCurrentRunnable= ToolController.this.currentRunnable;
-			setCurrentRunnable(this);
+			ToolController.this.currentSubmitType= getSubmitTypeL(this);
 			try {
 				ToolController.this.fCurrentInput= print;
 				doBeforeSubmitL();
 			}
 			finally {
-				setCurrentRunnable(savedCurrentRunnable);
+				ToolController.this.currentSubmitType= ToolController.this.currentRunnable.submitType;
 			}
 			if (send != null) {
 				ToolController.this.fCurrentInput= send;
@@ -409,6 +409,21 @@
 	}
 	
 	
+	protected static class RunnableData {
+		
+		private final ToolRunnable runnable;
+		private final SubmitType submitType;
+		private final Section queueSection;
+		
+		public RunnableData(final ToolRunnable runnable, final SubmitType submitType, final Section queueSection) {
+			this.runnable= runnable;
+			this.submitType= submitType;
+			this.queueSection= queueSection;
+		}
+		
+	}
+	
+	
 	public static final String START_TYPE_ID= "common/start"; //$NON-NLS-1$
 	public static final String QUIT_TYPE_ID= "common/quit"; //$NON-NLS-1$
 	
@@ -427,6 +442,10 @@
 	protected static final int SUSPENDED_TOPLEVEL= 0x1;
 	protected static final int SUSPENDED_DEEPLEVEL= 0x2;
 	
+	private static final byte REGULAR= 0;
+	private static final byte HOT_REGULAR= 1;
+	private static final byte HOT_NESTED= 2;
+	
 	
 	private ToolStreamProxy streams;
 	
@@ -435,7 +454,7 @@
 	
 	private int counter= 0;
 	
-	private ToolRunnable currentRunnable;
+	private RunnableData currentRunnable;
 	private SubmitType currentSubmitType;
 	private final List<SystemRunnable> controllerRunnables= new ArrayList<>();
 	private SystemRunnable postControllerRunnable;
@@ -450,8 +469,8 @@
 	private volatile boolean isTerminated;
 	
 	private boolean hotModeDeferred;
-	private boolean hotMode;
 	private boolean hotModeNested= true;
+	private byte hotMode= REGULAR;
 	private final IProgressMonitor hotModeMonitor= new NullProgressMonitor();
 	
 	private boolean isDebugEnabled;
@@ -468,7 +487,9 @@
 	
 	private ToolWorkspace workspaceData;
 	
-	private volatile int changeStamp;
+	private volatile int currentStamp;
+	private int changeStamp;
+	private int hotStamp;
 	
 	private final Map<String, ToolCommandHandler> actionHandlers= new HashMap<>();
 	
@@ -481,9 +502,9 @@
 	private final CopyOnWriteIdentityListSet<Disposable> disposables= new CopyOnWriteIdentityListSet<>();
 	
 	
-	protected ToolController(final ToolProcess process, final Map<String, Object> initData) {
+	protected ToolController(final ToolProcess process, final Map<String, Object> connectionInfo) {
 		this.process= process;
-		this.process.fInitData= initData;
+		this.process.connectionInfo= connectionInfo;
 		
 		this.streams= new ToolStreamProxy();
 		
@@ -573,11 +594,6 @@
 	}
 	
 	
-	protected Map<String, Object> getInitData() {
-		return this.process.fInitData;
-	}
-	
-	
 	/**
 	 * Runs the tool.
 	 * 
@@ -587,15 +603,17 @@
 	public final void run() throws CoreException {
 		assert (this.status == ToolStatus.STARTING);
 		try {
+			final Section queueSection= this.queue.getTopLevelSection();
+			
 			this.controllerThread= Thread.currentThread();
-			setCurrentRunnable(createStartRunnable());
+			setCurrentRunnable(createRunnableData(createStartRunnable(), queueSection));
 			startToolL(this.runnableProgressMonitor);
-			setCurrentRunnable(null);
+			setCurrentRunnable(new RunnableData(null, SubmitType.CONSOLE, queueSection));
 			synchronized (this.queue) {
 				loopChangeStatus((this.controllerRunnables.isEmpty()) ?
 						ToolStatus.STARTED_IDLING : ToolStatus.STARTED_PROCESSING, null);
 			}
-			loop();
+			loopTopLevel(queueSection);
 		}
 		finally {
 			synchronized (this.queue) {
@@ -612,15 +630,12 @@
 	
 	public final int getHotTasksState() {
 		synchronized (this.queue) {
-			if (this.hotMode) {
-				return (this.hotModeNested) ? 2 : 1;
-			}
-			return 0;
+			return this.hotMode;
 		}
 	}
 	
 	protected final boolean isInHotModeL() {
-		return this.hotMode;
+		return (this.hotMode != 0);
 	}
 	
 	/**
@@ -657,7 +672,7 @@
 	/**
 	 * Tries to apply "cancel".
 	 * 
-	 * The return value signalises if the command was applicable and/or
+	 * The return value signals if the command was applicable and/or
 	 * was succesful (depends on implementation).
 	 * 
 	 * @return hint about success.
@@ -665,8 +680,8 @@
 	public final boolean cancelTask(final int options) {
 		synchronized (this.queue) {
 			if ((options & CANCEL_ALL) != 0) {
-				final List<ToolRunnable> list= this.queue.internalGetList();
-				this.queue.remove(list.toArray(new ToolRunnable[list.size()]));
+				final List<ToolRunnable> list= this.queue.internal_getCurrentList();
+				list.clear();
 			}
 			if ((options & CANCEL_PAUSE) != 0) {
 				this.queue.pause();
@@ -719,7 +734,7 @@
 			final ToolCommandHandler handler= this.actionHandlers.get(SCHEDULE_QUIT_EVENT_ID);
 			if (handler != null) {
 				final Map<String, Object> data= new HashMap<>();
-				data.put("scheduledQuitTasks", getQuitTasks()); //$NON-NLS-1$
+				data.put("scheduledQuitRunnables", getQuitRunnables()); //$NON-NLS-1$
 				final IStatus status= executeHandler(SCHEDULE_QUIT_EVENT_ID, handler, data, new NullProgressMonitor());
 				if (status != null && !status.isOK()) {
 					schedule= false;
@@ -774,7 +789,7 @@
 	 */
 	public final void cancelQuit() {
 		synchronized(this.queue) {
-			this.queue.remove(getQuitTasks());
+			this.queue.remove(getQuitRunnables());
 			
 			if (this.status == ToolStatus.TERMINATED) {
 				return;
@@ -782,25 +797,29 @@
 		}
 		
 		// cancel task should not be synch
-		final ToolRunnable current= this.currentRunnable;
+		final ToolRunnable current= this.currentRunnable.runnable;
 		if (current != null && current.getTypeId() == QUIT_TYPE_ID) {
 			cancelTask(0);
 		}
 	}
 	
-	private final ToolRunnable[] getQuitTasks() {
-		final List<ToolRunnable> quit= new ArrayList<>();
-		final ToolRunnable current= this.currentRunnable;
-		if (current != null && current.getTypeId() == QUIT_TYPE_ID) {
-			quit.add(current);
+	private final List<ToolRunnable> getQuitRunnables() {
+		final ToolRunnable current;
+		final List<ToolRunnable> waiting;
+		synchronized (this.queue) {
+			current= this.currentRunnable.runnable;
+			waiting= this.queue.internal_getCurrentList();
 		}
-		final List<ToolRunnable> list= this.queue.internalGetCurrentList();
-		for (final ToolRunnable runnable : list) {
+		final List<ToolRunnable> quitRunnables= new ArrayList<>();
+		if (current != null && current.getTypeId() == QUIT_TYPE_ID) {
+			quitRunnables.add(current);
+		}
+		for (final ToolRunnable runnable : waiting) {
 			if (runnable.getTypeId() == QUIT_TYPE_ID) {
-				quit.add(runnable);
+				quitRunnables.add(runnable);
 			}
 		}
-		return quit.toArray(new ToolRunnable[quit.size()]);
+		return quitRunnables;
 	}
 	
 	public final void kill(final IProgressMonitor monitor) throws CoreException {
@@ -845,7 +864,7 @@
 			if (newStatus == ToolStatus.STARTED_PROCESSING && this.loopCurrentLevel > 0) {
 				// for changed resume detail
 				final ImIdentityList<IToolStatusListener> listeners= this.toolStatusListeners.toList();
-				final List<DebugEvent> eventList= this.queue.internalGetEventList();
+				final List<DebugEvent> eventList= this.queue.internal_getEventList();
 				for (final IToolStatusListener listener : listeners) {
 					if (listener instanceof IThread) {
 						listener.controllerStatusChanged(this.statusPrevious, newStatus, eventList);
@@ -862,16 +881,16 @@
 			}
 			if (newStatus == ToolStatus.STARTED_PROCESSING
 					&& (this.status != ToolStatus.STARTED_PAUSED || this.statusPrevious != ToolStatus.STARTED_PROCESSING)) {
-				this.queue.internalResetIdle();
+				this.queue.internal_resetOnIdle();
 			}
 			
-			this.queue.internalStatusChanged(newStatus);
+			this.queue.internal_onStatusChanged(newStatus);
 			
 			this.statusPrevious= this.status;
 			this.status= newStatus;
 			
 			final ImIdentityList<IToolStatusListener> listeners= this.toolStatusListeners.toList();
-			final List<DebugEvent> eventList= this.queue.internalGetEventList();
+			final List<DebugEvent> eventList= this.queue.internal_getEventList();
 			for (final IToolStatusListener listener : listeners) {
 				listener.controllerStatusChanged(this.statusPrevious, newStatus, eventList);
 			}
@@ -881,7 +900,7 @@
 			logEvents("loopChangeStatus", newStatus); //$NON-NLS-1$
 		}
 		
-		this.queue.internalFireEvents();
+		this.queue.internal_fireEvents();
 	}
 	
 //	protected final void loopBusyChanged(final boolean isBusy) {
@@ -1009,7 +1028,7 @@
 	public abstract ToolRunnable createCommandRunnable(final String command, final SubmitType type);
 	
 	
-	private final void loop() {
+	private final void loopTopLevel(final Section queueSection) {
 		if (this.hotModeDeferred) {
 			this.hotModeDeferred= false;
 			scheduleHotMode();
@@ -1022,11 +1041,11 @@
 				runSuspendedLoopL(SUSPENDED_TOPLEVEL);
 			}
 			else {
-				loopRunTask();
+				loopRunTask(queueSection);
 			}
 			
 			synchronized (this.queue) { // if interrupted run loop, all states are checked
-				this.queue.internalCheck();
+				this.queue.internal_check();
 				
 				if (this.internalTask > 0) {
 					try {
@@ -1047,7 +1066,7 @@
 					enterSuspended= true;
 					continue;
 				}
-				if (this.queue.internalIsPauseRequested()) {
+				if (this.queue.internal_isPauseRequested()) {
 					loopChangeStatus(ToolStatus.STARTED_PAUSED, null);
 					try {
 						this.queue.wait();
@@ -1055,7 +1074,7 @@
 					catch (final InterruptedException e) {}
 					continue;
 				}
-				if (this.queue.internalNext() < 0) {
+				if (this.queue.internal_next() < 0) {
 					loopChangeStatus(ToolStatus.STARTED_IDLING, null);
 					try {
 						this.queue.wait();
@@ -1067,7 +1086,7 @@
 		}
 	}
 	
-	private final void loopSuspended(final int level) {
+	private final void loopSuspended(final int level, final Section queueSection) {
 		boolean enterSuspended= false;
 		
 		while (true) {
@@ -1076,11 +1095,11 @@
 				runSuspendedLoopL(SUSPENDED_TOPLEVEL);
 			}
 			else {
-				loopRunTask();
+				loopRunTask(queueSection);
 			}
 			
 			synchronized (this.queue) { // if interrupted run loop, all states are checked
-				this.queue.internalCheck();
+				this.queue.internal_check();
 				
 				if (this.internalTask > 0) {
 					try {
@@ -1102,7 +1121,7 @@
 					enterSuspended= true;
 					continue;
 				}
-				if (this.queue.internalIsPauseRequested()) {
+				if (this.queue.internal_isPauseRequested()) {
 					loopChangeStatus(ToolStatus.STARTED_PAUSED, null);
 					try {
 						this.queue.wait();
@@ -1110,7 +1129,7 @@
 					catch (final InterruptedException e) {}
 					continue;
 				}
-				if (this.queue.internalNext() < 0) {
+				if (this.queue.internal_next() < 0) {
 					loopChangeStatus(ToolStatus.STARTED_SUSPENDED, null);
 					try {
 						this.queue.wait();
@@ -1122,27 +1141,29 @@
 		}
 	}
 	
-	private final void loopRunTask() {
+	private final void loopRunTask(final Section queueSection) {
 		while (true) {
 			final int type;
-			final ToolRunnable savedCurrentRunnable= this.currentRunnable;
+			final RunnableData savedCurrentRunnable= this.currentRunnable;
+			final ToolRunnable runnable;
 			synchronized (this.queue) {
 				if (this.controllerRunnables.size() > 0) {
 					type= Queue.RUN_CONTROL;
-					setCurrentRunnable(this.controllerRunnables.remove(0));
+					runnable= this.controllerRunnables.remove(0);
 				}
 				else if (this.loopCurrentLevel != this.suspendedRequestLevel || this.isTerminated
-						|| this.internalTask > 0 || this.queue.internalIsPauseRequested()) {
+						|| this.internalTask > 0 || this.queue.internal_isPauseRequested()) {
 					return;
 				}
 				else {
-					type= this.queue.internalNext();
+					type= this.queue.internal_next();
 					switch (type) {
 					case Queue.RUN_HOT:
+						runnable= null;
 						break;
 					case Queue.RUN_OTHER:
 					case Queue.RUN_DEFAULT:
-						setCurrentRunnable(this.queue.internalPoll());
+						runnable= this.queue.internal_poll();
 						break;
 					default:
 						return;
@@ -1151,49 +1172,50 @@
 				if (type != Queue.RUN_HOT) {
 					if (this.loopCurrentLevel > 0) {
 						if (type != Queue.RUN_CONTROL
-								&& (this.currentRunnable instanceof ConsoleCommandRunnable)
-								&& !runConsoleCommandInSuspend(((ConsoleCommandRunnable) this.currentRunnable).fText) ) {
+								&& (runnable instanceof ConsoleCommandRunnable)
+								&& !runConsoleCommandInSuspend(((ConsoleCommandRunnable) runnable).fText) ) {
 							try {
-								this.queue.internalFinished(this.currentRunnable, ToolRunnable.FINISHING_CANCEL);
+								this.queue.internal_onFinished(runnable, ToolRunnable.FINISHING_CANCEL);
 							}
 							finally {
 								setCurrentRunnable(savedCurrentRunnable);
 							}
 							return;
 						}
-						if (this.currentRunnable instanceof ToolController.SuspendResumeRunnable) {
-							this.suspendExitDetail= ((ToolController.SuspendResumeRunnable) this.currentRunnable).detail;
+						if (runnable instanceof ToolController.SuspendResumeRunnable) {
+							this.suspendExitDetail= ((ToolController.SuspendResumeRunnable) runnable).detail;
 						}
 						else {
-							final int detail= getDebugResumeDetailL(this.currentRunnable);
+							final int detail= getDebugResumeDetailL(runnable);
 							if (this.status == ToolStatus.STARTED_SUSPENDED
 									|| getDebugResumeDetailPriority(detail) >= getDebugResumeDetailPriority(this.suspendExitDetail)) {
 								this.suspendExitDetail= detail;
 							}
 						}
 					}
+					setCurrentRunnable(createRunnableData(runnable, queueSection));
 					loopChangeStatus(ToolStatus.STARTED_PROCESSING,
-							new RunnableProgressMonitor(this.currentRunnable));
+							new RunnableProgressMonitor(runnable));
 				}
 			}
 			switch (type) {
 			case Queue.RUN_CONTROL:
 				try {
-					this.currentRunnable.run(this, this.runnableProgressMonitor);
-					safeRunnableChanged(this.currentRunnable, ToolRunnable.FINISHING_OK);
+					runnable.run(this, this.runnableProgressMonitor);
+					safeRunnableChanged(runnable, ToolRunnable.FINISHING_OK);
 					continue;
 				}
 				catch (final Throwable e) {
 					final IStatus status= (e instanceof CoreException) ? ((CoreException) e).getStatus() : null;
 					if (status != null && (status.getSeverity() == IStatus.CANCEL || status.getSeverity() <= IStatus.INFO)) {
-						safeRunnableChanged(this.currentRunnable, ToolRunnable.FINISHING_CANCEL);
+						safeRunnableChanged(runnable, ToolRunnable.FINISHING_CANCEL);
 						// ignore
 					}
 					else {
 						NicoCorePlugin.logError(-1, NLS.bind(
 								"An Error occurred when running internal controller task ''{0}''.", //$NON-NLS-1$
-								this.currentRunnable.getLabel() ), e);
-						safeRunnableChanged(this.currentRunnable, ToolRunnable.FINISHING_ERROR);
+								runnable.getLabel() ), e);
+						safeRunnableChanged(runnable, ToolRunnable.FINISHING_ERROR);
 					}
 					
 					if (!isToolAlive()) {
@@ -1203,7 +1225,6 @@
 				}
 				finally {
 					setCurrentRunnable(savedCurrentRunnable);
-					this.currentSubmitType= null;
 					this.runnableProgressMonitor.done();
 				}
 			case Queue.RUN_HOT:
@@ -1223,7 +1244,7 @@
 			case Queue.RUN_DEFAULT:
 				try {
 					this.counter++;
-					this.currentRunnable.run(this, this.runnableProgressMonitor);
+					runnable.run(this, this.runnableProgressMonitor);
 					onTaskFinished(this.currentRunnable, ToolRunnable.FINISHING_OK,
 							this.runnableProgressMonitor );
 					continue;
@@ -1241,7 +1262,7 @@
 						status= new Status(IStatus.ERROR, NicoCore.BUNDLE_ID, NicoCorePlugin.EXTERNAL_ERROR,
 								NLS.bind(Messages.ToolRunnable_error_RuntimeError_message,
 										this.process.getLabel(Tool.LONG_LABEL),
-										this.currentRunnable.getLabel() ),
+										runnable.getLabel() ),
 								e);
 						if (type == Queue.RUN_DEFAULT) {
 							handleStatus(status, this.runnableProgressMonitor);
@@ -1271,10 +1292,10 @@
 	}
 	
 	/** Only called for regular tasks */
-	protected void onTaskFinished(final ToolRunnable runnable, final int event,
+	protected void onTaskFinished(final RunnableData runnableData, final int event,
 			final IProgressMonitor monitor) {
-		this.queue.internalFinished(runnable, event);
-		safeRunnableChanged(runnable, event);
+		this.queue.internal_onFinished(runnableData.runnable, event);
+		safeRunnableChanged(runnableData.runnable, event);
 	}
 	
 	private void safeRunnableChanged(final ToolRunnable runnable, final int event) {
@@ -1317,7 +1338,7 @@
 	private final ToolRunnable pollHotRunnable() {
 		ToolRunnable runnable= null;
 		if (!this.isTerminated) {
-			runnable= this.queue.internalPollHot();
+			runnable= this.queue.internal_pollHot();
 			if (runnable == null && !this.hotModeNested) {
 				try {
 					this.queue.wait(100);
@@ -1325,7 +1346,7 @@
 				catch (final InterruptedException e) {
 				}
 				if (!this.isTerminated) {
-					runnable= this.queue.internalPollHot();
+					runnable= this.queue.internal_pollHot();
 				}
 			}
 		}
@@ -1333,21 +1354,20 @@
 	}
 	
 	protected final void runHotModeLoop() {
-		final SubmitType savedSubmitType= this.currentSubmitType;
 		while (true) {
 			final ToolRunnable runnable;
 			synchronized (this.queue) {
 				runnable= pollHotRunnable();
 				if (runnable == null) {
-					if (this.hotMode) {
+					if (this.hotMode != 0) {
 						onHotModeExit(this.hotModeMonitor);
-						this.hotMode= false;
-						this.currentSubmitType= savedSubmitType;
+						this.hotMode= 0;
+						this.currentSubmitType= this.currentRunnable.submitType;
 					}
 					return;
 				}
-				if (!this.hotMode) {
-					this.hotMode= true;
+				if (this.hotMode == 0) {
+					this.hotMode= (this.hotModeNested) ? HOT_NESTED : HOT_REGULAR;
 					this.currentSubmitType= SubmitType.OTHER;
 					onHotModeEnter(this.hotModeMonitor);
 				}
@@ -1377,9 +1397,13 @@
 	}
 	
 	protected void onHotModeEnter(final IProgressMonitor monitor) {
+		if (this.hotMode > HOT_REGULAR) {
+			enableHotStamp();
+		}
 	}
 	
 	protected void onHotModeExit(final IProgressMonitor monitor) {
+		disableHotStamp();
 	}
 	
 	
@@ -1463,10 +1487,10 @@
 	}
 	
 	protected void runSuspendedLoopL(final int o) {
-		ToolRunnable insertMarker= null;
+		Queue.Section queueSection= null;
 		SystemRunnable updater= null;
 		
-		final ToolRunnable savedCurrentRunnable= this.currentRunnable;
+		final RunnableData savedCurrentRunnable= this.currentRunnable;
 		final RunnableProgressMonitor savedProgressMonitor= this.runnableProgressMonitor;
 		final ToolStatus savedStatus= this.status;
 		
@@ -1481,11 +1505,11 @@
 						setSuspended(this.suspendedRequestLevel, 0, null);
 						return;
 					}
-					if (this.loopCurrentLevel != thisLevel || insertMarker == null) {
+					if (this.loopCurrentLevel != thisLevel || queueSection == null) {
 						this.loopCurrentLevel= this.suspendedRunLevel= thisLevel;
 						
-						insertMarker= new SuspendedInsertRunnable(thisLevel);
-						this.queue.internalAddInsert(insertMarker);
+						queueSection= this.queue.internal_addInsert(
+								new SuspendedInsertRunnable(thisLevel) );
 						if (savedLevel == 0 && updater == null) {
 							updater= new SuspendedUpdateRunnable();
 							this.queue.addOnIdle(updater, 6000);
@@ -1495,7 +1519,7 @@
 				}
 				
 				// run suspended loop
-				doRunSuspendedLoopL(o, thisLevel);
+				doRunSuspendedLoopL(o, thisLevel, queueSection);
 				
 				// resume main runnable
 				final SuspendResumeRunnable runnable;
@@ -1508,8 +1532,8 @@
 					this.loopCurrentLevel= savedLevel;
 					this.suspendEnterDetail= DebugEvent.UNSPECIFIED;
 					
-					this.queue.internalRemoveInsert(insertMarker);
-					insertMarker= null;
+					this.queue.internal_removeInsert(queueSection);
+					queueSection= null;
 					
 					if (thisLevel <= this.suspendedRequestLevel) {
 						continue;
@@ -1525,7 +1549,8 @@
 				if (runnable != null) { // resume with runnable
 					try {
 						setCurrentRunnable((savedCurrentRunnable != null) ?
-								savedCurrentRunnable : runnable);
+								savedCurrentRunnable :
+								createRunnableData(runnable, this.queue.getCurrentSection()) );
 						if (runnable.canExec(savedProgressMonitor)) { // exec resume
 							synchronized (this.queue) {
 								this.suspendExitDetail= runnable.detail;
@@ -1570,8 +1595,8 @@
 				if (updater != null) {
 					this.queue.removeOnIdle(updater);
 				}
-				if (insertMarker != null) {
-					this.queue.internalRemoveInsert(insertMarker);
+				if (queueSection != null) {
+					this.queue.internal_removeInsert(queueSection);
 				}
 				
 				this.suspendExitRunnable= null;
@@ -1580,8 +1605,8 @@
 		}
 	}
 	
-	protected void doRunSuspendedLoopL(final int o, final int level) {
-		loopSuspended(level);
+	protected void doRunSuspendedLoopL(final int o, final int level, final Section queueSection) {
+		loopSuspended(level, queueSection);
 	}
 	
 	protected int getCurrentLevelL() {
@@ -1679,7 +1704,9 @@
 				disposable.dispose();
 			}
 			catch (final Exception e) {
-				NicoCorePlugin.log(new Status(IStatus.ERROR, NicoCore.BUNDLE_ID, -1, "An unexepected exception is thrown when disposing a controller extension.", e));
+				NicoCorePlugin.log(new Status(IStatus.ERROR, NicoCore.BUNDLE_ID, -1,
+						"An unexepected exception is thrown when disposing a controller extension.",
+						e ));
 			}
 		}
 	}
@@ -1709,7 +1736,8 @@
 		}
 		catch (final Exception e) {
 			NicoCorePlugin.log(new Status(IStatus.ERROR, NicoCore.BUNDLE_ID,
-					NLS.bind("An error occurred when executing tool command ''{0}''.", commandID)));
+					NLS.bind("An error occurred when executing tool command ''{0}''.", commandID),
+					e ));
 			return null;
 		}
 	}
@@ -1729,9 +1757,13 @@
 	}
 	
 	
-	private void setCurrentRunnable(final ToolRunnable runnable) {
+	private RunnableData createRunnableData(final ToolRunnable runnable, final Section queueSection) {
+		return new RunnableData(runnable, getSubmitTypeL(runnable), queueSection);
+	}
+	
+	private void setCurrentRunnable(final RunnableData runnable) {
 		this.currentRunnable= runnable;
-		this.currentSubmitType= getSubmitTypeL(runnable);
+		this.currentSubmitType= runnable.submitType;
 	}
 	
 	protected SubmitType getSubmitTypeL(final ToolRunnable runnable) {
@@ -1748,7 +1780,11 @@
 	
 	@Override
 	public ToolRunnable getCurrentRunnable() {
-		return this.currentRunnable;
+		return this.currentRunnable.runnable;
+	}
+	
+	public Queue.Section getCurrentQueueSection() {
+		return this.currentRunnable.queueSection;
 	}
 	
 	public SubmitType getCurrentSubmitType() {
@@ -1808,8 +1844,24 @@
 	}
 	
 	
+	public final int getChangeStamp() {
+		return this.currentStamp;
+	}
+	
+	private void touchChangeStamp() {
+		this.currentStamp= this.changeStamp= ((this.changeStamp + 1) & ~(1 << 31));
+	}
+	
+	private void enableHotStamp() {
+		this.currentStamp= this.hotStamp= ((this.hotStamp + 1) | (1 << 31));
+	}
+	
+	private void disableHotStamp() {
+		this.currentStamp= this.changeStamp;
+	}
+	
 	public void briefAboutToChange() {
-		this.changeStamp++;
+		touchChangeStamp();
 	}
 	
 	public void briefChanged(final int flags) {
@@ -1817,23 +1869,19 @@
 	}
 	
 	public void briefChanged(final Object obj, final int flags) {
-		this.changeStamp++;
+		touchChangeStamp();
 		this.workspaceData.controlBriefChanged(obj, flags);
 		if (DEBUG_LOG_STATE) {
 			logChanged();
 		}
 	}
 	
-	public final int getChangeStamp() {
-		return this.changeStamp;
-	}
-	
 	
 	private void logEvents(final String label, final ToolStatus status) {
 		final ToStringBuilder sb= new ObjectUtils.ToStringBuilder(label);
 		sb.addProp("status", this.status); //$NON-NLS-1$
 		sb.addProp("changeStamp", this.changeStamp); //$NON-NLS-1$
-		sb.addProp("events", this.queue.internalGetEventList()); //$NON-NLS-1$
+		sb.addProp("events", this.queue.internal_getEventList()); //$NON-NLS-1$
 		NicoCorePlugin.log(new Status(IStatus.INFO, NicoCore.BUNDLE_ID, sb.toString()));
 	}
 	
diff --git a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/ToolProcess.java b/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/ToolProcess.java
index 9bb13b3..89797f8 100644
--- a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/ToolProcess.java
+++ b/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/ToolProcess.java
@@ -63,7 +63,7 @@
 	private final long fConnectionTimestamp;
 	private long fStartupTimestamp;
 	private String fStartupWD;
-	Map<String, Object> fInitData;
+	Map<String, Object> connectionInfo;
 	
 	private ToolController fController;
 	private final Queue fQueue;
@@ -341,6 +341,10 @@
 		doDispose();
 	}
 	
+	public Map<String, Object> getConnectionInfo() {
+		return this.connectionInfo;
+	}
+	
 	public void prepareRestart(final Map<String, Object> data) {
 		if (fStatus != ToolStatus.TERMINATED) {
 			throw new IllegalStateException();
@@ -351,7 +355,7 @@
 		data.put("process", this);
 		data.put("processDispose", poseponeDispose());
 		data.put("address", fAddress);
-		data.put("initData", fInitData);
+		data.put("connectionInfo", this.connectionInfo);
 	}
 	
 	public void restartCompleted(final Map<String, Object> data) {
@@ -398,7 +402,9 @@
 	
 	protected void doDispose() {
 		if (fQueue != null) {
-			fQueue.dispose();
+			synchronized (fQueue) {
+				fQueue.internal_dispose();
+			}
 		}
 		if (fHistory != null) {
 			fHistory.dispose();
diff --git a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/ToolStreamProxy.java b/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/ToolStreamProxy.java
index 0650fbb..06a8426 100644
--- a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/ToolStreamProxy.java
+++ b/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/ToolStreamProxy.java
@@ -18,18 +18,21 @@
 
 import org.eclipse.debug.core.model.IStreamsProxy;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
 
 /**
  * None buffered streams
  */
+@NonNullByDefault
 public class ToolStreamProxy implements IStreamsProxy {
 	
 	
-	private final ToolStreamMonitor inputMonitor = new ToolStreamMonitor();
-	private final ToolStreamMonitor infoMonitor = new ToolStreamMonitor();
-	private final ToolStreamMonitor standardOutputMonitor = new ToolStreamMonitor();
-	private final ToolStreamMonitor standardErrorMonitor = new ToolStreamMonitor();
-	private final ToolStreamMonitor systemOutputMonitor = new ToolStreamMonitor();
+	private final ToolStreamMonitor inputMonitor= new ToolStreamMonitor();
+	private final ToolStreamMonitor infoMonitor= new ToolStreamMonitor();
+	private final ToolStreamMonitor standardOutputMonitor= new ToolStreamMonitor();
+	private final ToolStreamMonitor standardErrorMonitor= new ToolStreamMonitor();
+	private final ToolStreamMonitor systemOutputMonitor= new ToolStreamMonitor();
 	
 	
 	public ToolStreamProxy() {
@@ -65,11 +68,11 @@
 	
 	
 	public void dispose() {
-		inputMonitor.dispose();
-		infoMonitor.dispose();
-		standardOutputMonitor.dispose();
-		standardErrorMonitor.dispose();
-		systemOutputMonitor.dispose();
+		this.inputMonitor.dispose();
+		this.infoMonitor.dispose();
+		this.standardOutputMonitor.dispose();
+		this.standardErrorMonitor.dispose();
+		this.systemOutputMonitor.dispose();
 	}
 	
 }
diff --git a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/ToolWorkspace.java b/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/ToolWorkspace.java
index 2950ae2..69a0fec 100644
--- a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/ToolWorkspace.java
+++ b/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/runtime/ToolWorkspace.java
@@ -228,7 +228,7 @@
 				final ToolStatus status= this.process.getToolStatus();
 				if (status != ToolStatus.TERMINATED) {
 					if (enable && status.isWaiting()) {
-						this.process.getQueue().internalResetIdle();
+						this.process.getQueue().internal_resetOnIdle();
 						this.process.getQueue().notifyAll();
 					}
 					addPropertyChanged("AutoRefresh.enabled", enable);
diff --git a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/util/IToolProvider.java b/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/util/IToolProvider.java
deleted file mode 100644
index e273eb3..0000000
--- a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/util/IToolProvider.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*=============================================================================#
- # Copyright (c) 2006, 2018 Stephan Wahlbrink and others.
- # 
- # This program and the accompanying materials are made available under the
- # terms of the Eclipse Public License 2.0 which is available at
- # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
- # which is available at https://www.apache.org/licenses/LICENSE-2.0.
- # 
- # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
- # 
- # Contributors:
- #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
- #=============================================================================*/
-
-package org.eclipse.statet.nico.core.util;
-
-import org.eclipse.statet.nico.core.runtime.ToolProcess;
-
-
-/**
- * Provides access to a selected (the active) tool instance. 
- * Counterpart of {@link IToolRetargetable}.
- */
-public interface IToolProvider {
-	
-	
-	public ToolProcess getTool();
-	
-	public void addToolRetargetable(IToolRetargetable action);
-	
-	public void removeToolRetargetable(IToolRetargetable action);
-	
-}
diff --git a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/util/IToolRetargetable.java b/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/util/IToolRetargetable.java
deleted file mode 100644
index 1a7cff6..0000000
--- a/r/org.eclipse.statet.nico.core/src/org/eclipse/statet/nico/core/util/IToolRetargetable.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*=============================================================================#
- # Copyright (c) 2006, 2018 Stephan Wahlbrink and others.
- # 
- # This program and the accompanying materials are made available under the
- # terms of the Eclipse Public License 2.0 which is available at
- # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
- # which is available at https://www.apache.org/licenses/LICENSE-2.0.
- # 
- # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
- # 
- # Contributors:
- #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
- #=============================================================================*/
-
-package org.eclipse.statet.nico.core.util;
-
-import org.eclipse.statet.nico.core.runtime.ToolProcess;
-
-
-/**
- * Objects accepts tool instances. Can be connected to a {@link IToolProvider}.
- */
-public interface IToolRetargetable {
-	
-	/**
-	 * Is called when the tool changed
-	 * 
-	 * @param tool the new tool
-	 */
-	void setTool(ToolProcess tool);
-	
-	/**
-	 * Is called when the set tool is terminated
-	 */
-	void toolTerminated();
-	
-}
diff --git a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/internal/nico/ui/LocalTaskTransfer.java b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/internal/nico/ui/LocalTaskTransfer.java
index e762d8e..4b32d0c 100644
--- a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/internal/nico/ui/LocalTaskTransfer.java
+++ b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/internal/nico/ui/LocalTaskTransfer.java
@@ -21,6 +21,10 @@
 import org.eclipse.swt.dnd.TransferData;
 import org.eclipse.ui.statushandlers.StatusManager;
 
+import org.eclipse.statet.jcommons.collections.ImList;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
 import org.eclipse.statet.ecommons.ts.core.ToolRunnable;
 
 import org.eclipse.statet.nico.core.runtime.ToolProcess;
@@ -30,16 +34,17 @@
 /**
  * Transfer type for {@link ToolRunnable}
  */
+@NonNullByDefault
 public final class LocalTaskTransfer extends ByteArrayTransfer {
 	
 	// First attempt to create a UUID for the type name to make sure that
 	// different Eclipse applications use different "types" of
 	// <code>LocalTaskTransfer</code>
-	private static final String TYPE_NAME = "org.eclipse.statet.nico-task-transfer-format" + (new Long(System.currentTimeMillis())).toString(); //$NON-NLS-1$;
+	private static final String TYPE_NAME= "org.eclipse.statet.nico-task-transfer-format" + (new Long(System.currentTimeMillis())).toString(); //$NON-NLS-1$;
 	
-	private static final int TYPEID = registerType(TYPE_NAME);
+	private static final int TYPEID= registerType(TYPE_NAME);
 	
-	private static final LocalTaskTransfer INSTANCE = new LocalTaskTransfer();
+	private static final LocalTaskTransfer INSTANCE= new LocalTaskTransfer();
 	
 	
 	/**
@@ -48,10 +53,10 @@
 	public static class Data {
 		
 		public final ToolProcess process;
-		public ToolRunnable[] runnables;
+		public @Nullable ImList<ToolRunnable> runnables;
 		
 		private Data(final ToolProcess process) {
-			this.process = process;
+			this.process= process;
 		}
 		
 	}
@@ -66,12 +71,12 @@
 	}
 	
 	
-	private ToolProcess fProcess;
-	private Data fData;
+	private @Nullable ToolProcess process;
+	private @Nullable Data data;
 	
 	
 	/**
-	 * Only the singleton instance of this class may be used. 
+	 * Only the singleton instance of this class may be used.
 	 */
 	protected LocalTaskTransfer() {
 		// do nothing
@@ -119,10 +124,8 @@
 	 */
 	@Override
 	public void javaToNative(final Object object, final TransferData transferData) {
-		if (object instanceof Data) {
-			fData = (Data) object;
-		}
-		final byte[] check = TYPE_NAME.getBytes();
+		this.data= (Data) object;
+		final byte[] check= TYPE_NAME.getBytes();
 		super.javaToNative(check, transferData);
 	}
 	
@@ -133,13 +136,13 @@
 	 * @see org.eclipse.swt.dnd.ByteArrayTransfer#nativeToJava(TransferData)
 	 */
 	@Override
-	public Object nativeToJava(final TransferData transferData) {
-		final Object result = super.nativeToJava(transferData);
+	public @Nullable Object nativeToJava(final TransferData transferData) {
+		final Object result= super.nativeToJava(transferData);
 		if (isInvalidNativeType(result)) {
 			StatusManager.getManager().handle(new Status(IStatus.ERROR, NicoUI.BUNDLE_ID, 0,
 					"invalid transfer type", null)); //$NON-NLS-1$
 		}
-		return fData;
+		return this.data;
 	}
 	
 	
@@ -149,7 +152,7 @@
 	 * @param process
 	 */
 	public void init(final ToolProcess process) {
-		fProcess = process;
+		this.process= process;
 	}
 	
 	/**
@@ -158,9 +161,9 @@
 	 * 
 	 * @return new data object or <code>null</code> if no current dnd
 	 */
-	public Data createData() {
-		if (fProcess != null) {
-			return new Data(fProcess);
+	public @Nullable Data createData() {
+		if (this.process != null) {
+			return new Data(this.process);
 		}
 		return null;
 	}
@@ -170,8 +173,8 @@
 	 * (usually in {@link DragSourceListener#dragFinished(org.eclipse.swt.dnd.DragSourceEvent)})
 	 */
 	public void finished() {
-		fProcess = null;
-		fData = null;
+		this.process= null;
+		this.data= null;
 	}
 	
 	/**
@@ -179,9 +182,9 @@
 	 * so the drop adapter can check the type
 	 * @return main type or <code>null</code> if no current dnd
 	 */
-	public String getMainType() {
-		if (fProcess != null) {
-			return fProcess.getMainType();
+	public @Nullable String getMainType() {
+		if (this.process != null) {
+			return this.process.getMainType();
 		}
 		return null;
 	}
diff --git a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/internal/nico/ui/actions/DisconnectEngineHandler.java b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/internal/nico/ui/actions/DisconnectEngineHandler.java
index 862257f..327939d 100644
--- a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/internal/nico/ui/actions/DisconnectEngineHandler.java
+++ b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/internal/nico/ui/actions/DisconnectEngineHandler.java
@@ -14,6 +14,8 @@
 
 package org.eclipse.statet.internal.nico.ui.actions;
 
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
+
 import org.eclipse.core.commands.ExecutionEvent;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
@@ -24,16 +26,22 @@
 import org.eclipse.ui.services.IServiceLocator;
 import org.eclipse.ui.statushandlers.StatusManager;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.ts.core.util.ToolProvider;
+import org.eclipse.statet.ecommons.ui.workbench.WorkbenchUIUtils;
+
 import org.eclipse.statet.nico.core.runtime.IRemoteEngineController;
 import org.eclipse.statet.nico.core.runtime.ToolController;
 import org.eclipse.statet.nico.core.runtime.ToolProcess;
-import org.eclipse.statet.nico.core.util.IToolProvider;
 import org.eclipse.statet.nico.ui.NicoUI;
 import org.eclipse.statet.nico.ui.NicoUITools;
-import org.eclipse.statet.nico.ui.actions.ToolRetargetableHandler;
+import org.eclipse.statet.nico.ui.actions.AbstractToolHandler;
 
 
-public class DisconnectEngineHandler extends ToolRetargetableHandler {
+@NonNullByDefault
+public class DisconnectEngineHandler extends AbstractToolHandler<ToolProcess> {
 	
 	
 	private static class DisconnectJob extends Job {
@@ -43,7 +51,7 @@
 		}
 		
 		
-		private final ToolController fController;
+		private final ToolController controller;
 		
 		
 		DisconnectJob(final ToolProcess process, final ToolController controller) {
@@ -52,13 +60,13 @@
 			setUser(true);
 			setPriority(INTERACTIVE);
 			
-			fController = controller;
+			this.controller= controller;
 		}
 		
 		@Override
 		protected IStatus run(final IProgressMonitor monitor) {
 			try {
-				((IRemoteEngineController) fController).disconnect(monitor);
+				((IRemoteEngineController) this.controller).disconnect(monitor);
 				return Status.OK_STATUS;
 			}
 			catch (final CoreException e) {
@@ -72,36 +80,31 @@
 	}
 	
 	
-	public DisconnectEngineHandler(final IToolProvider toolProvider, final IServiceLocator serviceLocator) {
-		super(toolProvider, serviceLocator);
+	public DisconnectEngineHandler(final ToolProvider toolProvider, final IServiceLocator serviceLocator) {
+		super(null, IRemoteEngineController.FEATURE_SET_ID,
+				toolProvider, serviceLocator );
 		init();
 	}
 	
 	
 	@Override
-	protected boolean evaluateEnabled() {
-		final ToolProcess tool = getTool();
-		return ((tool != null)
-				&& tool.isProvidingFeatureSet(IRemoteEngineController.FEATURE_SET_ID)
-				&& !tool.isTerminated() );
-	}
-	
-	@Override
-	protected Object doExecute(final ExecutionEvent event) {
-		final ToolProcess tool = getCheckedTool();
+	protected @Nullable Object execute(final ToolProcess tool, final ExecutionEvent event) {
 		final ToolController controller;
 		try {
-			controller = NicoUITools.accessController(null, tool);
+			controller= NicoUITools.accessController(null, tool);
 		}
 		catch (final CoreException e) {
 			StatusManager.getManager().handle(e.getStatus(), StatusManager.SHOW | StatusManager.LOG);
 			return null;
 		}
 		
-		final IProgressService progressService = getProgressService();
-		final Job job = new DisconnectJob(tool, controller);
+		final IServiceLocator serviceLocator= getServiceLocator(event.getApplicationContext());
+		final IProgressService progressService= nonNullAssert(
+				serviceLocator.getService(IProgressService.class) );
+		final Job job= new DisconnectJob(tool, controller);
 		job.schedule();
-		progressService.showInDialog(getShell(), job);
+		progressService.showInDialog(WorkbenchUIUtils.getShell(event.getApplicationContext()), job);
+		
 		return null;
 	}
 	
diff --git a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/internal/nico/ui/actions/PauseHandler.java b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/internal/nico/ui/actions/PauseHandler.java
index 2cf0933..a4b94e4 100644
--- a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/internal/nico/ui/actions/PauseHandler.java
+++ b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/internal/nico/ui/actions/PauseHandler.java
@@ -24,29 +24,32 @@
 import org.eclipse.ui.menus.UIElement;
 import org.eclipse.ui.services.IServiceLocator;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.ts.core.util.ToolProvider;
+import org.eclipse.statet.ecommons.ui.util.UIAccess;
 import org.eclipse.statet.ecommons.ui.workbench.WorkbenchUIUtils;
 
 import org.eclipse.statet.nico.core.runtime.Queue;
 import org.eclipse.statet.nico.core.runtime.ToolProcess;
-import org.eclipse.statet.nico.core.util.IToolProvider;
 import org.eclipse.statet.nico.ui.NicoUI;
-import org.eclipse.statet.nico.ui.actions.ToolRetargetableHandler;
+import org.eclipse.statet.nico.ui.actions.AbstractToolHandler;
 
 
 /**
  * Handler to toggle pause state of engine
  */
-public class PauseHandler extends ToolRetargetableHandler implements IElementUpdater,
+@NonNullByDefault
+public class PauseHandler extends AbstractToolHandler<ToolProcess> implements IElementUpdater,
 		IDebugEventSetListener {
 	
 	
-	private boolean fIsChecked;
-	
-	private final ElementUpdater fUpdater = new ElementUpdater(NicoUI.PAUSE_COMMAND_ID);
+	private boolean isChecked;
 	
 	
-	public PauseHandler(final IToolProvider toolProvider, final IServiceLocator serviceLocator) {
-		super(toolProvider, serviceLocator);
+	public PauseHandler(final ToolProvider toolProvider, final IServiceLocator serviceLocator) {
+		super(null, null, toolProvider, serviceLocator);
 		init();
 	}
 	
@@ -67,118 +70,99 @@
 	}
 	
 	
-	@Override
-	protected void doRefresh() {
-		if (getState() == S_ONAIR) {
-			fUpdater.schedule();
+	private void setChecked(final boolean isChecked) {
+		if (isChecked != this.isChecked) {
+			this.isChecked= isChecked;
+			refreshUI();
 		}
 	}
 	
 	@Override
+	public void setEnabled(@Nullable Object evaluationContext) {
+		super.setEnabled(evaluationContext);
+		
+		final ToolProcess enabledTool= getActiveTool();
+		setChecked((enabledTool != null) ?
+				enabledTool.getQueue().isRequested(Queue.PAUSED_STATE) :
+				false );
+	}
+	
+	protected void refreshUI() {
+		WorkbenchUIUtils.refreshCommandElements(NicoUI.PAUSE_COMMAND_ID, this, null);
+	}
+	
+	@Override
 	public void updateElement(final UIElement element, final Map parameters) {
 		WorkbenchUIUtils.aboutToUpdateCommandsElements(this, element);
 		try {
-			element.setChecked(fIsChecked);
+			element.setChecked(this.isChecked);
 		}
 		finally {
 			WorkbenchUIUtils.finalizeUpdateCommandsElements(this);
 		}
 	}
 	
-	@Override
-	public boolean handleToolChanged() {
-		final ToolProcess tool = getTool();
-		final boolean wasChecked = fIsChecked;
-		fIsChecked = (tool != null) ?
-				tool.getQueue().isRequested(Queue.PAUSED_STATE) :
-				false;
-		
-		setBaseEnabled(evaluateEnabled());
-		return (wasChecked != fIsChecked);
-	}
 	
 	@Override
 	public void handleDebugEvents(final DebugEvent[] events) {
-		if (getState() != S_ONAIR) {
-			return;
-		}
-		boolean update = false;
-		final ToolProcess tool = getTool();
+		final ToolProcess tool= getActiveTool();
 		if (tool == null) {
 			return;
 		}
 		ITER_EVENTS: for (final DebugEvent event : events) {
 			if (event.getSource() == tool) {
 				if (event.getKind() == DebugEvent.TERMINATE) {
-					synchronized (this) {
-						if (getTool() != tool) {
-							return;
-						}
-						setBaseEnabled(false);
-					}
-					break;
+					update(tool);
 				}
 				continue ITER_EVENTS;
 			}
 			if (event.getSource() == tool.getQueue()) {
-				Boolean checked= null;
 				if (Queue.isStateRequest(event)) {
 					final Queue.StateDelta delta= (Queue.StateDelta) event.getData();
 					if (delta.newState == Queue.PAUSED_STATE) {
-						checked= Boolean.TRUE;
+						updateChecked(tool, true);
 					}
 					else {
-						checked= Boolean.FALSE;
+						updateChecked(tool, false);
 					}
 				}
 				else if (Queue.isStateChange(event)) {
 					final Queue.StateDelta delta= (Queue.StateDelta) event.getData();
 					if (delta.newState == Queue.PAUSED_STATE) {
-						checked= Boolean.TRUE;
-					}
-				}
-				if (checked != null) {
-					synchronized (this) {
-						if (getState() != S_ONAIR || getTool() != tool) {
-							return;
-						}
-						final boolean wasChecked = fIsChecked;
-						fIsChecked = checked;
-						
-						update = (wasChecked != fIsChecked);
+						updateChecked(tool, true);
 					}
 				}
 				continue ITER_EVENTS;
 			}
 		}
-		
-		if (update) {
-			doRefresh();
-		}
 	}
 	
+	private void update(final ToolProcess tool) {
+		UIAccess.getDisplay().asyncExec(() -> {
+			if (getState() != S_INITIALIZED || getActiveTool() != tool) {
+				return;
+			}
+			setEnabled(null);
+		});
+	}
+	
+	private void updateChecked(final ToolProcess tool, final boolean isChecked) {
+		UIAccess.getDisplay().asyncExec(() -> {
+			if (getState() != S_INITIALIZED || getActiveTool() != tool) {
+				return;
+			}
+			setChecked(isChecked);
+		});
+	}
+	
+	
 	@Override
-	protected Object doExecute(final ExecutionEvent event) {
-		boolean update = false;
-		synchronized (this) {
-			final ToolProcess tool = getCheckedTool();
-			final boolean wasChecked= fIsChecked;
-			if (tool != null) {
-				final boolean success= (!wasChecked) ?
-						tool.getQueue().pause() : tool.getQueue().resume();
-				if (success) {
-					fIsChecked= !wasChecked;
-				}
-			}
-			else {
-				fIsChecked= false;
-			}
-			update = (wasChecked != fIsChecked);
-		}
+	protected @Nullable Object execute(final ToolProcess tool, final ExecutionEvent event) {
+		final boolean wasChecked= this.isChecked;
+		final boolean success= (!wasChecked) ?
+				tool.getQueue().pause() : tool.getQueue().resume();
+		setChecked((success) ? !wasChecked : false);
 		
-		if (update) {
-			doRefresh();
-		}
 		return null;
 	}
 	
diff --git a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/internal/nico/ui/actions/ReconnectEngineHandler.java b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/internal/nico/ui/actions/ReconnectEngineHandler.java
index dcef072..471d94e 100644
--- a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/internal/nico/ui/actions/ReconnectEngineHandler.java
+++ b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/internal/nico/ui/actions/ReconnectEngineHandler.java
@@ -14,6 +14,8 @@
 
 package org.eclipse.statet.internal.nico.ui.actions;
 
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
+
 import java.lang.reflect.InvocationTargetException;
 import java.util.HashMap;
 import java.util.Map;
@@ -31,31 +33,33 @@
 import org.eclipse.ui.services.IServiceLocator;
 import org.eclipse.ui.statushandlers.StatusManager;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
 import org.eclipse.statet.ecommons.debug.core.util.OverlayLaunchConfiguration;
+import org.eclipse.statet.ecommons.ts.core.util.ToolProvider;
 
 import org.eclipse.statet.nico.core.runtime.IRemoteEngineController;
 import org.eclipse.statet.nico.core.runtime.ToolProcess;
-import org.eclipse.statet.nico.core.util.IToolProvider;
 import org.eclipse.statet.nico.ui.NicoUI;
-import org.eclipse.statet.nico.ui.actions.ToolRetargetableHandler;
+import org.eclipse.statet.nico.ui.actions.AbstractToolHandler;
 
 
-public class ReconnectEngineHandler extends ToolRetargetableHandler {
+@NonNullByDefault
+public class ReconnectEngineHandler extends AbstractToolHandler<ToolProcess> {
 	
 	
-	public ReconnectEngineHandler(final IToolProvider toolProvider, final IServiceLocator serviceLocator) {
-		super(toolProvider, serviceLocator);
+	public ReconnectEngineHandler(final ToolProvider toolProvider, final IServiceLocator serviceLocator) {
+		super(null, IRemoteEngineController.FEATURE_SET_ID,
+				toolProvider, serviceLocator );
 		init();
 	}
 	
 	
 	@Override
-	protected boolean evaluateEnabled() {
-		final ToolProcess tool = getTool();
+	protected boolean evaluateIsEnabled(final ToolProcess tool, final @Nullable Object evaluationContext) {
 		try {
-			return ((tool != null)
-					&& tool.isProvidingFeatureSet(IRemoteEngineController.FEATURE_SET_ID)
-					&& tool.isTerminated()
+			return (tool.isTerminated()
 					&& (tool.getExitValue() == ToolProcess.EXITCODE_DISCONNECTED) );
 		}
 		catch (final DebugException e) {
@@ -63,17 +67,20 @@
 		}
 	}
 	
+	
 	@Override
-	protected Object doExecute(final ExecutionEvent event) {
-		final ToolProcess tool = getCheckedTool();
-		
-		final IProgressService progressService = getProgressService();
+	protected @Nullable Object execute(final ToolProcess tool, final ExecutionEvent event) {
+		final IServiceLocator serviceLocator= getServiceLocator(event.getApplicationContext());
+		final IProgressService progressService= nonNullAssert(
+				serviceLocator.getService(IProgressService.class) );
 		try {
 			progressService.busyCursorWhile(createRunnable(tool));
 		}
 		catch (final InvocationTargetException e) {
 			StatusManager.getManager().handle(new Status(IStatus.ERROR, NicoUI.BUNDLE_ID, -1,
-					"Reconnecting failed.", e.getCause()), StatusManager.SHOW | StatusManager.LOG);
+							"Reconnecting failed.",
+							e.getCause() ),
+					StatusManager.SHOW | StatusManager.LOG );
 		}
 		catch (final InterruptedException e) {
 		}
@@ -84,10 +91,10 @@
 		return new IRunnableWithProgress() {
 			@Override
 			public void run(final IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
-				final ILaunch originallaunch = process.getLaunch();
-				ILaunchConfiguration originalConfig = originallaunch.getLaunchConfiguration();
+				final ILaunch originallaunch= process.getLaunch();
+				ILaunchConfiguration originalConfig= originallaunch.getLaunchConfiguration();
 				if (originalConfig instanceof OverlayLaunchConfiguration) {
-					originalConfig = ((OverlayLaunchConfiguration) originalConfig).getOriginal();
+					originalConfig= ((OverlayLaunchConfiguration) originalConfig).getOriginal();
 				}
 				
 				final Map<String, Object> reconnect= new HashMap<>();
@@ -95,13 +102,14 @@
 				
 				final Map<String, Object> add= new HashMap<>();
 				add.put(IRemoteEngineController.LAUNCH_RECONNECT_ATTRIBUTE, reconnect);
-				final ILaunchConfiguration reconnectConfig = new OverlayLaunchConfiguration(originalConfig, add);
+				final ILaunchConfiguration reconnectConfig= new OverlayLaunchConfiguration(originalConfig, add);
 				try {
-					final ILaunch reconnectLaunch = reconnectConfig.launch(originallaunch.getLaunchMode(), monitor, false);
+					final ILaunch reconnectLaunch= reconnectConfig.launch(
+							originallaunch.getLaunchMode(), monitor, false );
 					
 //					if (dispose != null) {
-//						final ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
-//						final ILaunchesListener2 launchListener = new ILaunchesListener2() {
+//						final ILaunchManager launchManager= DebugPlugin.getDefault().getLaunchManager();
+//						final ILaunchesListener2 launchListener= new ILaunchesListener2() {
 //							public void launchesAdded(final ILaunch[] launches) {
 //							}
 //							public void launchesChanged(final ILaunch[] launches) {
@@ -133,18 +141,17 @@
 					}
 					throw new InvocationTargetException(e);
 				}
-				
 			}
 		};
 	}
 	
-	private boolean contains(final ILaunch[] launches, final ILaunch search) {
-		for (final ILaunch launch : launches) {
-			if (search == launch) {
-				return true;
-			}
-		}
-		return false;
-	}
+//	private boolean contains(final ILaunch[] launches, final ILaunch search) {
+//		for (final ILaunch launch : launches) {
+//			if (search == launch) {
+//				return true;
+//			}
+//		}
+//		return false;
+//	}
 	
 }
diff --git a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/AbstractToolHandler.java b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/AbstractToolHandler.java
index 9bbb5fc..1273cfd 100644
--- a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/AbstractToolHandler.java
+++ b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/AbstractToolHandler.java
@@ -18,61 +18,155 @@
 import org.eclipse.core.commands.ExecutionEvent;
 import org.eclipse.core.commands.ExecutionException;
 import org.eclipse.ui.handlers.HandlerUtil;
+import org.eclipse.ui.services.IServiceLocator;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
 
 import org.eclipse.statet.ecommons.ts.core.Tool;
+import org.eclipse.statet.ecommons.ts.core.util.ActiveToolListener;
+import org.eclipse.statet.ecommons.ts.core.util.ToolProvider;
+import org.eclipse.statet.ecommons.ui.workbench.WorkbenchUIUtils;
 
 import org.eclipse.statet.internal.nico.ui.ToolSourceProvider;
 
 
-public abstract class AbstractToolHandler extends AbstractHandler {
+@NonNullByDefault
+public abstract class AbstractToolHandler<TTool extends Tool> extends AbstractHandler
+		implements ActiveToolListener {
 	
 	
-	private final String requiredMainType;
-	private final String requiredFeatureSet;
+	protected static final byte S_INITIALIZING=              0;
+	protected static final byte S_INITIALIZED=               1;
+	protected static final byte S_DISPOSED=                 -1;
 	
 	
-	protected AbstractToolHandler(final String mainType) {
-		this(mainType, null);
-	}
+	/** internal state of this handler */
+	private byte state;
 	
-	protected AbstractToolHandler(final String mainType, final String featureSet) {
+	private final @Nullable String requiredMainType;
+	private final @Nullable String requiredFeatureSet;
+	
+	private final @Nullable ToolProvider toolProvider;
+	
+	private final @Nullable IServiceLocator serviceLocator;
+	
+	private @Nullable TTool activeTool;
+	
+	
+	protected AbstractToolHandler(final @Nullable String mainType, final @Nullable String featureSet,
+			final @Nullable ToolProvider toolProvider, final @Nullable IServiceLocator serviceLocator) {
 		this.requiredMainType= mainType;
 		this.requiredFeatureSet= featureSet;
+		
+		this.toolProvider= toolProvider;
+		this.serviceLocator= serviceLocator;
+		
+		this.state= S_INITIALIZING;
+	}
+	
+	protected AbstractToolHandler(final @Nullable String mainType, final @Nullable String featureSet) {
+		this(mainType, featureSet, null, null);
+	}
+	
+	protected AbstractToolHandler(final @Nullable String mainType) {
+		this(mainType, null, null, null);
 	}
 	
 	
-	protected Tool getTool(final Object evaluationContext) {
+	protected final int getState() {
+		return this.state;
+	}
+	
+	/**
+	 * Must be call at the end of the constructor to finish initialization.
+	 */
+	protected void init() {
+		if (this.toolProvider != null) {
+			this.toolProvider.addToolListener(this);
+		}
+		this.state= S_INITIALIZED;
+	}
+	
+	@Override
+	public void dispose() {
+		if (this.state == S_DISPOSED) {
+			return;
+		}
+		synchronized (this) {
+			this.state= S_DISPOSED;
+		}
+		
+		if (this.toolProvider != null) {
+			this.toolProvider.removeToolListener(this);
+		}
+		
+		super.dispose();
+	}
+	
+	
+	protected @Nullable Tool getTool(final @Nullable Object evaluationContext) {
+		if (this.toolProvider != null) {
+			return this.toolProvider.getTool();
+		}
 		return (Tool) HandlerUtil.getVariable(evaluationContext, ToolSourceProvider.ACTIVE_TOOL_NAME);
 	}
 	
-	protected boolean isValid(final Tool tool) {
-		return (tool != null
-				&& (this.requiredMainType == null || tool.getMainType() == this.requiredMainType)
-				&& (this.requiredFeatureSet == null || tool.isProvidingFeatureSet(this.requiredFeatureSet))
-				&& isSupported(tool) );
+	@Override
+	public void onToolChanged(final ActiveToolEvent event) {
+		if (this.state == S_DISPOSED) {
+			return;
+		}
+		
+		setEnabled(null);
 	}
 	
-	protected boolean isSupported(final Tool tool) {
+	
+	protected boolean isValid(final @Nullable Tool tool) {
+		return (tool != null
+				&& (this.requiredMainType == null || tool.getMainType() == this.requiredMainType)
+				&& (this.requiredFeatureSet == null || tool.isProvidingFeatureSet(this.requiredFeatureSet)));
+	}
+	
+	
+	protected boolean evaluateIsEnabled(final TTool tool, final @Nullable Object evaluationContext) {
 		return (!tool.isTerminated());
 	}
 	
-	
 	@Override
-	public void setEnabled(final Object evaluationContext) {
+	public void setEnabled(final @Nullable Object evaluationContext) {
 		final Tool tool= getTool(evaluationContext);
-		setBaseEnabled(isValid(tool));
+		this.activeTool= isValid(tool) ? (TTool) tool : null;
+		setBaseEnabled(this.activeTool != null
+				&& evaluateIsEnabled(this.activeTool, evaluationContext) );
 	}
 	
+	protected @Nullable TTool getActiveTool() {
+		return this.activeTool;
+	}
+	
+	
+	protected IServiceLocator getServiceLocator(final @Nullable Object evaluationContext) {
+		if (this.serviceLocator != null) {
+			return this.serviceLocator;
+		}
+		return WorkbenchUIUtils.getServiceLocator(evaluationContext);
+	}
+	
+	
 	@Override
-	public Object execute(final ExecutionEvent event) throws ExecutionException {
+	public @Nullable Object execute(final ExecutionEvent event) throws ExecutionException {
 		final Tool tool= getTool(event.getApplicationContext());
-		if (!isValid(tool)) {
-			return null;
+		final TTool execTool;
+		if ((tool == this.activeTool || isValid(tool))
+				&& evaluateIsEnabled(execTool= (TTool) tool, event.getApplicationContext()) ) {
+			return execute(execTool, event);
 		}
-		return execute(tool, event);
+		return null;
 	}
 	
 	
-	protected abstract Object execute(Tool tool, ExecutionEvent event) throws ExecutionException;
+	protected abstract @Nullable Object execute(TTool tool,
+			ExecutionEvent event) throws ExecutionException;
 	
 }
diff --git a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/CancelHandler.java b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/CancelHandler.java
index 08adda3..ec36ca5 100644
--- a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/CancelHandler.java
+++ b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/CancelHandler.java
@@ -17,52 +17,55 @@
 import org.eclipse.core.commands.ExecutionEvent;
 import org.eclipse.swt.widgets.Display;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.ts.core.util.ToolProvider;
+
 import org.eclipse.statet.nico.core.runtime.ToolController;
 import org.eclipse.statet.nico.core.runtime.ToolProcess;
-import org.eclipse.statet.nico.core.util.IToolProvider;
 
 
 /**
  * Handler to cancel tool tasks.
  */
-public class CancelHandler extends ToolRetargetableHandler {
+@NonNullByDefault
+public class CancelHandler extends AbstractToolHandler<ToolProcess> {
 	
 	
-	public static final String MENU_ID = "org.eclipse.statet.nico.menus.Cancel"; //$NON-NLS-1$
+	public static final String MENU_ID= "org.eclipse.statet.nico.menus.Cancel"; //$NON-NLS-1$
 	
-	public static final String PAR_OPTIONS = "options"; //$NON-NLS-1$
+	public static final String PAR_OPTIONS= "options"; //$NON-NLS-1$
 	
 	
-	private final int fOptions;
+	private final int options;
 	
 	
-	public CancelHandler(final IToolProvider toolProvider) {
-		super(toolProvider, null);
-		fOptions = 0;
+	public CancelHandler(final ToolProvider toolProvider, final int options) {
+		super(null, null, toolProvider, null);
+		
+		this.options= options;
 		init();
 	}
 	
-	public CancelHandler(final IToolProvider toolProvider, final int options) {
-		super(toolProvider, null);
-		fOptions = options;
-		init();
+	public CancelHandler(final ToolProvider toolProvider) {
+		this(toolProvider, 0);
 	}
 	
 	
 	@Override
-	protected Object doExecute(final ExecutionEvent event) {
-		final String optionsParameter = event.getParameter(PAR_OPTIONS);
-		int options = fOptions;
+	protected @Nullable Object execute(final ToolProcess tool, final ExecutionEvent event) {
+		final String optionsParameter= event.getParameter(PAR_OPTIONS);
+		int options= this.options;
 		if (optionsParameter != null) {
 			try {
-				options = Integer.decode(optionsParameter);
+				options= Integer.decode(optionsParameter);
 			}
 			catch (final NumberFormatException e) {
 			}
 		}
 		
-		final ToolProcess tool = getCheckedTool();
-		final ToolController controller = (tool != null) ? tool.getController() : null;
+		final ToolController controller= (tool != null) ? tool.getController() : null;
 		if (controller == null) {
 			return null;
 		}
diff --git a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/LoadHistoryAction.java b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/LoadHistoryAction.java
index a5f1510..802d27d 100644
--- a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/LoadHistoryAction.java
+++ b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/LoadHistoryAction.java
@@ -16,20 +16,17 @@
 
 import org.eclipse.jface.wizard.WizardDialog;
 
+import org.eclipse.statet.ecommons.ts.core.util.ToolProvider;
 import org.eclipse.statet.ecommons.ui.util.UIAccess;
 
 import org.eclipse.statet.nico.core.runtime.ToolProcess;
-import org.eclipse.statet.nico.core.util.IToolProvider;
 import org.eclipse.statet.nico.ui.NicoUIMessages;
 
 
-/**
- * 
- */
 public class LoadHistoryAction extends ToolAction {
 	
 	
-	public LoadHistoryAction(final IToolProvider support) {
+	public LoadHistoryAction(final ToolProvider support) {
 		super(support, false);
 		
 		setText(NicoUIMessages.LoadHistoryAction_name);
@@ -37,13 +34,12 @@
 //		setImageDescriptor();
 //		setDisabledImageDescriptor();
 		
-		handleToolChanged();
+		update();
 	}
 	
 	
 	@Override
 	public void run() {
-		
 		final ToolProcess tool = getTool();
 		if (tool == null) {
 			return;
diff --git a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/SaveHistoryAction.java b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/SaveHistoryAction.java
index e63fab4..be1ccdc 100644
--- a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/SaveHistoryAction.java
+++ b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/SaveHistoryAction.java
@@ -16,20 +16,17 @@
 
 import org.eclipse.jface.wizard.WizardDialog;
 
+import org.eclipse.statet.ecommons.ts.core.util.ToolProvider;
 import org.eclipse.statet.ecommons.ui.util.UIAccess;
 
 import org.eclipse.statet.nico.core.runtime.ToolProcess;
-import org.eclipse.statet.nico.core.util.IToolProvider;
 import org.eclipse.statet.nico.ui.NicoUIMessages;
 
 
-/**
- * 
- */
 public class SaveHistoryAction extends ToolAction  {
 	
 	
-	public SaveHistoryAction(final IToolProvider support) {
+	public SaveHistoryAction(final ToolProvider support) {
 		super(support, false);
 		
 		setText(NicoUIMessages.SaveHistoryAction_name);
@@ -37,7 +34,7 @@
 //		setImageDescriptor();
 //		setDisabledImageDescriptor();
 		
-		handleToolChanged();
+		update();
 	}
 	
 	
diff --git a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/ToolAction.java b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/ToolAction.java
index db2dd69..9fb82da 100644
--- a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/ToolAction.java
+++ b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/ToolAction.java
@@ -17,58 +17,55 @@
 import org.eclipse.jface.action.Action;
 import org.eclipse.swt.SWT;
 
+import org.eclipse.statet.ecommons.ts.core.Tool;
+import org.eclipse.statet.ecommons.ts.core.util.ActiveToolListener;
+import org.eclipse.statet.ecommons.ts.core.util.ToolProvider;
+
 import org.eclipse.statet.nico.core.runtime.ToolProcess;
-import org.eclipse.statet.nico.core.util.IToolProvider;
-import org.eclipse.statet.nico.core.util.IToolRetargetable;
 
 
 /**
  * Can be used for actions for tools.
  * <p>
- * Same as {@link ToolRetargetableHandler} for actions
+ * Same as {@link AbstractLocalToolHandler} for actions
  */
-public class ToolAction extends Action implements IToolRetargetable {
+@Deprecated
+public class ToolAction extends Action implements ActiveToolListener {
 	
 	
 	private ToolProcess fTool;
 	private final boolean fDisableOnTermination;
 	
 	
-	public ToolAction(final IToolProvider support, final boolean disableOnTermination) {
+	public ToolAction(final ToolProvider support, final boolean disableOnTermination) {
 		this(support, SWT.NONE, disableOnTermination);
 	}
 	
-	public ToolAction(final IToolProvider support, final int style, final boolean disableOnTermination) {
+	public ToolAction(final ToolProvider support, final int style, final boolean disableOnTermination) {
 		super(null, style);
 		
-		support.addToolRetargetable(this);
-		fTool = support.getTool();
-		fDisableOnTermination = disableOnTermination;
+		support.addToolListener(this);
+		setTool(support.getTool());
+		this.fDisableOnTermination = disableOnTermination;
 	}
 	
 	
+	private void setTool(final Tool tool) {
+		this.fTool = (tool instanceof ToolProcess) ? (ToolProcess) tool : null;
+	}
 	@Override
-	public void setTool(final ToolProcess tool) {
-		fTool = tool;
-		handleToolChanged();
-	}
-	
-	public void handleToolChanged() {
-		update();
-	}
-	
-	@Override
-	public void toolTerminated() {
+	public void onToolChanged(final ActiveToolEvent event) {
+		setTool(event.getTool());
 		update();
 	}
 	
 	public void update() {
-		setEnabled(fTool != null
-				&& (!fDisableOnTermination || !fTool.isTerminated()));
+		setEnabled(this.fTool != null
+				&& (!this.fDisableOnTermination || !this.fTool.isTerminated()));
 	}
 	
 	public ToolProcess getTool() {
-		return fTool;
+		return this.fTool;
 	}
 	
 }
diff --git a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/ToolRetargetableHandler.java b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/ToolRetargetableHandler.java
deleted file mode 100644
index 6ca2ffa..0000000
--- a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/ToolRetargetableHandler.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*=============================================================================#
- # Copyright (c) 2007, 2018 Stephan Wahlbrink and others.
- # 
- # This program and the accompanying materials are made available under the
- # terms of the Eclipse Public License 2.0 which is available at
- # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
- # which is available at https://www.apache.org/licenses/LICENSE-2.0.
- # 
- # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
- # 
- # Contributors:
- #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
- #=============================================================================*/
-
-package org.eclipse.statet.nico.ui.actions;
-
-import org.eclipse.core.commands.AbstractHandler;
-import org.eclipse.core.commands.ExecutionEvent;
-import org.eclipse.core.commands.ExecutionException;
-import org.eclipse.jface.window.IShellProvider;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.progress.IProgressService;
-import org.eclipse.ui.services.IServiceLocator;
-
-import org.eclipse.statet.ecommons.ui.util.UIAccess;
-import org.eclipse.statet.ecommons.ui.workbench.WorkbenchUIUtils;
-
-import org.eclipse.statet.nico.core.runtime.ToolProcess;
-import org.eclipse.statet.nico.core.util.IToolProvider;
-import org.eclipse.statet.nico.core.util.IToolRetargetable;
-
-
-/**
- * Abstract command handler (not tool event handler)
- * supporting tool assignment by the {@link IToolRetargetable} interface
- */
-public abstract class ToolRetargetableHandler extends AbstractHandler implements IToolRetargetable {
-	
-	
-	protected static final int S_INIT = 1;
-	protected static final int S_ONAIR = 2;
-	protected static final int S_DISPOSED = 3;
-	
-	protected static class ChangedStateException extends IllegalStateException {
-		private static final long serialVersionUID = 1L;
-	}
-	
-	
-	protected class ElementUpdater implements Runnable {
-		
-		private final String fCommandId;
-		
-		public ElementUpdater(final String commandId) {
-			fCommandId = commandId;
-			assert (getServiceLocator() != null);
-		}
-		
-		@Override
-		public void run() {
-			WorkbenchUIUtils.refreshCommandElements(fCommandId, ToolRetargetableHandler.this, null);
-		}
-		
-		public void schedule() {
-			final Display display = UIAccess.getDisplay(getShell());
-			if (display != null) {
-				if (display.getThread() == Thread.currentThread()) {
-					run();
-				}
-				else {
-					display.asyncExec(this);
-				}
-			}
-		}
-	}
-	
-	
-	/** providing the active tool */
-	private final IToolProvider fToolProvider;
-	
-	/** optional service locator */
-	private final IServiceLocator fServiceLocator;
-	
-	/** internal state of this handler */
-	private int fState;
-	
-	private ToolProcess fTool;
-	
-	
-	public ToolRetargetableHandler(final IToolProvider toolProvider, final IServiceLocator serviceLocator) {
-		super();
-		fState = S_INIT;
-		fServiceLocator = serviceLocator;
-		fToolProvider = toolProvider;
-		if (fToolProvider != null) {
-			fToolProvider.addToolRetargetable(this);
-		}
-	}
-	
-	/**
-	 * Must be call at the end of the constructor to finish initialization.
-	 */
-	protected void init() {
-		setTool(fToolProvider.getTool());
-		fState = S_ONAIR;
-	}
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void dispose() {
-		if (fState >= S_DISPOSED) {
-			return;
-		}
-		fState = S_DISPOSED;
-		
-		if (fToolProvider != null) {
-			fToolProvider.removeToolRetargetable(this);
-		}
-		
-		synchronized (this) {
-			fTool = null;
-		}
-		
-		super.dispose();
-	}
-	
-	
-	@Override
-	public final void setTool(final ToolProcess tool) {
-		boolean update = false;
-		
-		synchronized (this) {
-			if (fState == S_DISPOSED) {
-				return;
-			}
-			if ((fTool == null) ? 
-					(tool == null && getState() == S_ONAIR) :
-					(tool == fTool) ) {
-				return;
-			}
-			
-			fTool = tool;
-			update = handleToolChanged();
-		}
-		
-		if (update) {
-			doRefresh();
-		}
-	}
-	
-	@Override
-	public void toolTerminated() {
-		boolean update = false;
-		synchronized (this) {
-			update = handleToolChanged();
-		}
-		
-		if (update) {
-			doRefresh();
-		}
-	}
-	
-	
-	protected final int getState() {
-		return fState;
-	}
-	
-	/**
-	 * @return current associated tool
-	 */
-	public final ToolProcess getTool() {
-		return fTool;
-	}
-	
-	/**
-	 * Checks if action is enabled for the current tool and returns it
-	 * 
-	 * @return current associated tool
-	 */
-	protected final ToolProcess getCheckedTool() {
-		synchronized (this) {
-			if (fState != S_ONAIR || !isEnabled()) {
-				throw new ChangedStateException();
-			}
-			return fTool;
-		}
-	}
-	
-	/**
-	 * Optional service locator (window, view, page,...)
-	 * 
-	 * @return the service locator or <code>null</code>
-	 */
-	protected IServiceLocator getServiceLocator() {
-		return fServiceLocator;
-	}
-	
-	protected IProgressService getProgressService() {
-		return getServiceLocator().getService(IProgressService.class);
-	}
-	
-	
-	protected Shell getShell() {
-		if (fServiceLocator instanceof IShellProvider) {
-			return ((IShellProvider) fServiceLocator).getShell();
-		}
-		return null; 
-	}
-	
-	
-	/**
-	 * Is called when the tool changed. Can be overwritten to
-	 * update the handler state.
-	 * 
-	 * @return if {@link #doRefresh()} should be called
-	 */
-	public boolean handleToolChanged() {
-		final boolean isEnabled = evaluateEnabled();
-		if (isEnabled != isEnabled()) {
-			setBaseEnabled(isEnabled);
-			return true;
-		}
-		return false;
-	}
-	
-	/**
-	 * Is called when the tool was terminated. Can be overwritten to
-	 * update the handler state or do nothing.
-	 * 
-	 * @return if {@link #doRefresh()} should be called
-	 */
-	public boolean handleToolTerminated() {
-		return handleToolChanged();
-	}
-	
-	/**
-	 * Computes the enablement state of the handler. Can be overwritten
-	 * to change the criteria.
-	 * 
-	 * @return if handler should set to enabled
-	 */
-	protected boolean evaluateEnabled() {
-		final ToolProcess tool = getTool();
-		return (tool != null
-				&& !tool.isTerminated());
-	}
-	
-	@Override
-	public final Object execute(final ExecutionEvent event) throws ExecutionException {
-		if (fToolProvider != null) {
-			final ToolProcess tool = fToolProvider.getTool();
-			if (tool != fTool) {
-				setTool(tool);
-			}
-		}
-		
-		try {
-			doExecute(event);
-		}
-		catch (final ChangedStateException e) {
-		}
-		return null;
-	}
-	
-	protected void doRefresh() {
-	}
-	
-	protected abstract Object doExecute(final ExecutionEvent event);
-	
-}
diff --git a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/WindowToolProvider.java b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/WindowToolProvider.java
index 5df11a4..6155e5c 100644
--- a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/WindowToolProvider.java
+++ b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/actions/WindowToolProvider.java
@@ -17,55 +17,60 @@
 import org.eclipse.ui.IWorkbenchPage;
 import org.eclipse.ui.IWorkbenchWindow;
 
+import org.eclipse.statet.ecommons.ts.core.util.ActiveToolListener;
+import org.eclipse.statet.ecommons.ts.core.util.ActiveToolListener.ActiveToolEvent;
+import org.eclipse.statet.ecommons.ts.core.util.ToolProvider;
+
 import org.eclipse.statet.nico.core.runtime.ToolProcess;
-import org.eclipse.statet.nico.core.util.IToolProvider;
-import org.eclipse.statet.nico.core.util.IToolRetargetable;
 import org.eclipse.statet.nico.ui.IToolRegistryListener;
 import org.eclipse.statet.nico.ui.NicoUI;
 import org.eclipse.statet.nico.ui.ToolSessionUIData;
 
 
-public class WindowToolProvider implements IToolProvider, IToolRegistryListener {
+@Deprecated
+public class WindowToolProvider implements ToolProvider, IToolRegistryListener {
 	
 	
-	private final IWorkbenchPage fPage;
+	private final IWorkbenchPage page;
 	
-	private IToolRetargetable fListener;
+	private ActiveToolListener listener;
 	
 	
 	public WindowToolProvider(final IWorkbenchWindow window) {
-		fPage = window.getActivePage();
-		assert (fPage != null);
+		this.page= window.getActivePage();
+		assert (this.page != null);
 	}
 	
 	
 	@Override
-	public void addToolRetargetable(final IToolRetargetable listener) {
-		assert (fListener == null);
-		fListener = listener;
-		NicoUI.getToolRegistry().addListener(this, fPage);
+	public void addToolListener(final ActiveToolListener listener) {
+		assert (this.listener == null);
+		this.listener= listener;
+		NicoUI.getToolRegistry().addListener(this, this.page);
 	}
 	
 	@Override
-	public void removeToolRetargetable(final IToolRetargetable listener) {
-		assert (fListener == listener || fListener == null);
-		fListener = null;
+	public void removeToolListener(final ActiveToolListener listener) {
+		assert (this.listener == listener || this.listener == null);
+		this.listener= null;
 		NicoUI.getToolRegistry().removeListener(this);
 	}
 	
 	@Override
 	public ToolProcess getTool() {
-		return NicoUI.getToolRegistry().getActiveToolSession(fPage).getProcess();
+		return NicoUI.getToolRegistry().getActiveToolSession(this.page).getProcess();
 	}
 	
 	@Override
-	public void toolSessionActivated(final ToolSessionUIData informations) {
-		fListener.setTool(informations.getProcess());
+	public void toolSessionActivated(final ToolSessionUIData sessionData) {
+		this.listener.onToolChanged(
+				new ActiveToolEvent(ActiveToolEvent.TOOL_ACTIVATED, sessionData.getProcess()) );
 	}
 	
 	@Override
 	public void toolTerminated(final ToolSessionUIData sessionData) {
-		fListener.toolTerminated();
+		this.listener.onToolChanged(
+				new ActiveToolEvent(ActiveToolEvent.TOOL_TERMINATED, sessionData.getProcess()) );
 	}
 	
-}
\ No newline at end of file
+}
diff --git a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/console/NIConsolePage.java b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/console/NIConsolePage.java
index 79f6d9f..94ef4c9 100644
--- a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/console/NIConsolePage.java
+++ b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/console/NIConsolePage.java
@@ -26,7 +26,6 @@
 import org.eclipse.core.commands.ExecutionException;
 import org.eclipse.core.commands.IHandler2;
 import org.eclipse.core.runtime.IAdaptable;
-import org.eclipse.core.runtime.ListenerList;
 import org.eclipse.debug.core.DebugEvent;
 import org.eclipse.debug.core.DebugPlugin;
 import org.eclipse.debug.core.IDebugEventSetListener;
@@ -98,10 +97,15 @@
 import org.eclipse.ui.services.IServiceLocator;
 import org.eclipse.ui.texteditor.FindReplaceAction;
 
+import org.eclipse.statet.jcommons.collections.CopyOnWriteIdentityListSet;
+
 import org.eclipse.statet.ecommons.preferences.PreferencesUtil;
 import org.eclipse.statet.ecommons.preferences.SettingsChangeNotifier.ChangeListener;
 import org.eclipse.statet.ecommons.text.ui.TextViewerAction;
 import org.eclipse.statet.ecommons.text.ui.TextViewerEditorColorUpdater;
+import org.eclipse.statet.ecommons.ts.core.util.ActiveToolListener;
+import org.eclipse.statet.ecommons.ts.core.util.ActiveToolListener.ActiveToolEvent;
+import org.eclipse.statet.ecommons.ts.core.util.ToolProvider;
 import org.eclipse.statet.ecommons.ui.ISettingsChangedHandler;
 import org.eclipse.statet.ecommons.ui.SharedMessages;
 import org.eclipse.statet.ecommons.ui.actions.HandlerCollection;
@@ -126,8 +130,6 @@
 import org.eclipse.statet.nico.core.runtime.ToolProcess;
 import org.eclipse.statet.nico.core.runtime.ToolStatus;
 import org.eclipse.statet.nico.core.runtime.ToolWorkspace;
-import org.eclipse.statet.nico.core.util.IToolProvider;
-import org.eclipse.statet.nico.core.util.IToolRetargetable;
 import org.eclipse.statet.nico.ui.NicoUI;
 import org.eclipse.statet.nico.ui.actions.CancelHandler;
 import org.eclipse.statet.nico.ui.util.ExportConsoleOutputWizard;
@@ -143,7 +145,7 @@
  */
 public abstract class NIConsolePage implements IPageBookViewPage,
 		IAdaptable, IShowInSource, IShowInTargetList,
-		IPropertyChangeListener, ScrollLockAction.Receiver, IToolProvider, ChangeListener {
+		IPropertyChangeListener, ScrollLockAction.Receiver, ToolProvider, ChangeListener {
 	
 	
 	private static final String DIALOG_ID= "Console"; //$NON-NLS-1$
@@ -400,7 +402,7 @@
 	
 	// Actions
 	private MultiActionHandler multiActionHandler;
-	private final ListenerList toolActions= new ListenerList();
+	private final CopyOnWriteIdentityListSet<ActiveToolListener> toolListeners= new CopyOnWriteIdentityListSet<>();
 	NestedServices inputServices;
 	
 	private FindReplaceUpdater findReplaceUpdater;
@@ -980,13 +982,13 @@
 	}
 	
 	@Override
-	public void addToolRetargetable(final IToolRetargetable listener) {
-		this.toolActions.add(listener);
+	public void addToolListener(final ActiveToolListener listener) {
+		this.toolListeners.add(listener);
 	}
 	
 	@Override
-	public void removeToolRetargetable(final IToolRetargetable listener) {
-		this.toolActions.remove(listener);
+	public void removeToolListener(final ActiveToolListener listener) {
+		this.toolListeners.remove(listener);
 	}
 	
 	public TextConsoleViewer getOutputViewer() {
@@ -1088,8 +1090,9 @@
 	protected void onToolTerminated() {
 		if (this.isCreated) {
 			this.terminateAction.update();
-			for (final Object action : this.toolActions.getListeners()) {
-				((IToolRetargetable) action).toolTerminated();
+			final ActiveToolEvent event= new ActiveToolEvent(ActiveToolEvent.TOOL_TERMINATED, getTool());
+			for (final ActiveToolListener listener : this.toolListeners) {
+				listener.onToolChanged(event);
 			}
 			this.outputPasteAction.setEnabled(false);
 			final Button button= this.inputGroup.getSubmitButton();
diff --git a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/console/SubmitDropAdapter.java b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/console/SubmitDropAdapter.java
index 3ec2b9b..cbce93d 100644
--- a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/console/SubmitDropAdapter.java
+++ b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/console/SubmitDropAdapter.java
@@ -72,7 +72,8 @@
 		if (LocalTaskTransfer.getTransfer().isSupportedType(event.currentDataType)) {
 			final LocalTaskTransfer.Data data= (Data) event.data;
 			final ToolProcess process= this.page.getConsole().getProcess();
-			if (data == null || process.isTerminated()) {
+			if (data == null || data.process == null || data.runnables == null
+					|| process.isTerminated()) {
 				return;
 			}
 			data.process.getQueue().move(data.runnables, process.getQueue());
diff --git a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/util/QuitHandler.java b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/util/QuitHandler.java
index 54b0f66..7c5515a 100644
--- a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/util/QuitHandler.java
+++ b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/util/QuitHandler.java
@@ -15,6 +15,7 @@
 package org.eclipse.statet.nico.ui.util;
 
 import java.lang.reflect.InvocationTargetException;
+import java.util.List;
 import java.util.Map;
 
 import org.eclipse.core.runtime.CoreException;
@@ -50,26 +51,26 @@
 	
 	private static class UIRunnable implements Runnable {
 		
-		private ToolController fController;
-		private String fDialogTitle;
-		private String fDialogMessage;
-		private String[] fDialogOptions;
-		private volatile int fResult;
+		private ToolController controller;
+		private String dialogTitle;
+		private String dialogMessage;
+		private String[] dialogOptions;
+		private volatile int result;
 		
 		@Override
 		public void run() {
-			final IWorkbenchWindow window = UIAccess.getActiveWorkbenchWindow(true);
-			final MessageDialog dialog = new MessageDialog(window.getShell(), fDialogTitle, null,
-					fDialogMessage, MessageDialog.QUESTION, fDialogOptions, 0 );
-			fResult = dialog.open();
+			final IWorkbenchWindow window= UIAccess.getActiveWorkbenchWindow(true);
+			final MessageDialog dialog= new MessageDialog(window.getShell(), this.dialogTitle, null,
+					this.dialogMessage, MessageDialog.QUESTION, this.dialogOptions, 0 );
+			this.result= dialog.open();
 			
-			if (fResult == 1) {
+			if (this.result == 1) {
 				try {
 					window.run(true, true, new IRunnableWithProgress() {
 						@Override
 						public void run(final IProgressMonitor monitor) throws InvocationTargetException {
 							try {
-								fController.kill(monitor);
+								UIRunnable.this.controller.kill(monitor);
 							} catch (final CoreException e) {
 								throw new InvocationTargetException(e);
 							}
@@ -91,9 +92,9 @@
 	@Override
 	public IStatus execute(final String id, final ToolService service, final Map<String, Object> data,
 			final IProgressMonitor monitor) {
-		final IConsoleService console = (IConsoleService) service;
+		final IConsoleService console= (IConsoleService) service;
 		if (PlatformUI.getWorkbench().isClosing()) {
-			final ToolController controller = console.getController();
+			final ToolController controller= console.getController();
 			if (controller != null) {
 				if (console.getTool().isProvidingFeatureSet(IRemoteEngineController.FEATURE_SET_ID)) {
 					try {
@@ -111,25 +112,25 @@
 			return Status.CANCEL_STATUS;
 		}
 		
-		final ToolRunnable[] quitRunnables = (ToolRunnable[]) data.get("scheduledQuitTasks");
-		if (quitRunnables.length == 0) {
-			return Status.OK_STATUS; // run default = schedule quit
+		final List<ToolRunnable> quitRunnables= (List<ToolRunnable>) data.get("scheduledQuitRunnables");
+		if (quitRunnables.isEmpty()) {
+			return Status.OK_STATUS; // run default= schedule quit
 		}
 		
-		final UIRunnable runner = new UIRunnable();
-		runner.fController = console.getController();
-		final ToolProcess process = runner.fController.getTool();
-		runner.fDialogTitle = NLS.bind(Messages.TerminatingMonitor_title, process.getLabel(Tool.DEFAULT_LABEL));
-		runner.fDialogMessage = NLS.bind(Messages.TerminatingMonitor_message, process.getLabel(Tool.LONG_LABEL));
-		runner.fDialogOptions = new String[] {
+		final UIRunnable runner= new UIRunnable();
+		runner.controller= console.getController();
+		final ToolProcess process= runner.controller.getTool();
+		runner.dialogTitle= NLS.bind(Messages.TerminatingMonitor_title, process.getLabel(Tool.DEFAULT_LABEL));
+		runner.dialogMessage= NLS.bind(Messages.TerminatingMonitor_message, process.getLabel(Tool.LONG_LABEL));
+		runner.dialogOptions= new String[] {
 				Messages.TerminatingMonitor_WaitButton_label,
 				Messages.TerminatingMonitor_ForceButton_label,
 				Messages.TerminatingMonitor_CancelButton_label
 		};
 		
 		UIAccess.getDisplay().syncExec(runner);
-		if (runner.fResult == 2) {
-			runner.fController.cancelQuit();
+		if (runner.result == 2) {
+			runner.controller.cancelQuit();
 		}
 		return Status.CANCEL_STATUS; // do nothing
 	}
diff --git a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/views/HistoryView.java b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/views/HistoryView.java
index aaf715d..8b83ebb 100644
--- a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/views/HistoryView.java
+++ b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/views/HistoryView.java
@@ -49,7 +49,6 @@
 import org.eclipse.swt.events.SelectionListener;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Event;
@@ -72,11 +71,15 @@
 import org.eclipse.ui.part.ViewPart;
 import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
 
+import org.eclipse.statet.jcommons.collections.CopyOnWriteIdentityListSet;
 import org.eclipse.statet.jcommons.collections.ImCollections;
 import org.eclipse.statet.jcommons.collections.ImList;
 
 import org.eclipse.statet.ecommons.collections.FastList;
 import org.eclipse.statet.ecommons.preferences.core.Preference.EnumSetPref;
+import org.eclipse.statet.ecommons.ts.core.util.ActiveToolListener;
+import org.eclipse.statet.ecommons.ts.core.util.ActiveToolListener.ActiveToolEvent;
+import org.eclipse.statet.ecommons.ts.core.util.ToolProvider;
 import org.eclipse.statet.ecommons.ui.SharedMessages;
 import org.eclipse.statet.ecommons.ui.SharedUIResources;
 import org.eclipse.statet.ecommons.ui.actions.HandlerContributionItem;
@@ -95,8 +98,6 @@
 import org.eclipse.statet.nico.core.runtime.IHistoryListener;
 import org.eclipse.statet.nico.core.runtime.SubmitType;
 import org.eclipse.statet.nico.core.runtime.ToolProcess;
-import org.eclipse.statet.nico.core.util.IToolProvider;
-import org.eclipse.statet.nico.core.util.IToolRetargetable;
 import org.eclipse.statet.nico.ui.IToolRegistry;
 import org.eclipse.statet.nico.ui.IToolRegistryListener;
 import org.eclipse.statet.nico.ui.NicoUI;
@@ -112,7 +113,7 @@
  * 
  * Usage: This class is not intend to be subclassed.
  */
-public class HistoryView extends ViewPart implements IToolProvider {
+public class HistoryView extends ViewPart implements ToolProvider {
 	
 	
 	public static interface EntryFilter {
@@ -162,15 +163,15 @@
 		private final EnumSet<SubmitType> fSubmitTypes;
 		
 		public SubmitTypeFilter(final EnumSet<SubmitType> types) {
-			fSubmitTypes = types;
+			this.fSubmitTypes = types;
 		}
 		
 		@Override
 		public boolean select(final Entry e) {
 			final SubmitType type = e.getSubmitType();
 			return (type == null
-					|| fSubmitTypes.contains(type));
-		} 
+					|| this.fSubmitTypes.contains(type));
+		}
 		
 	}
 	
@@ -187,12 +188,12 @@
 		
 		@Override
 		protected IStatus run(final IProgressMonitor monitor) {
-			final ToolProcess process = fProcess;
+			final ToolProcess process = HistoryView.this.fProcess;
 			if (monitor.isCanceled()) {
 				return Status.CANCEL_STATUS;
 			}
 			if (process == null) {
-				fContentProvider.setNewSource(null, new Entry[0]);
+				HistoryView.this.fContentProvider.setNewSource(null, new Entry[0]);
 				return Status.OK_STATUS;
 			}
 			final History history = process.getHistory();
@@ -207,7 +208,7 @@
 			if (monitor.isCanceled()) {
 				return Status.CANCEL_STATUS;
 			}
-			fContentProvider.setNewSource(history, entries);
+			HistoryView.this.fContentProvider.setNewSource(history, entries);
 			return Status.OK_STATUS;
 		}
 		
@@ -224,55 +225,55 @@
 		private Entry[] fNewEntrys;
 		
 		public synchronized void setNewSource(final History source, final Entry[] es) {
-			fCurrentSource = source;
+			this.fCurrentSource = source;
 			
-			fNewEntrys = es;
-			fToAdd.clear();
-			fToRemove.clear();
+			this.fNewEntrys = es;
+			this.fToAdd.clear();
+			this.fToRemove.clear();
 			
-			if (!fIsScheduled) {
-				fIsScheduled = true;
+			if (!this.fIsScheduled) {
+				this.fIsScheduled = true;
 				UIAccess.getDisplay().asyncExec(this);
 			}
 		}
 		
 		@Override
 		public synchronized void completeChange(final History source, final Entry[] es) {
-			if (fCurrentSource != source) {
+			if (this.fCurrentSource != source) {
 				return;
 			}
-			fNewEntrys = es;
-			fToAdd.clear();
-			fToRemove.clear();
+			this.fNewEntrys = es;
+			this.fToAdd.clear();
+			this.fToRemove.clear();
 			
-			if (!fIsScheduled) {
-				fIsScheduled = true;
+			if (!this.fIsScheduled) {
+				this.fIsScheduled = true;
 				UIAccess.getDisplay().asyncExec(this);
 			}
 		}
 		
 		@Override
 		public synchronized void entryAdded(final History source, final Entry e) {
-			if (fCurrentSource != source) {
+			if (this.fCurrentSource != source) {
 				return;
 			}
-			fToAdd.add(e);
+			this.fToAdd.add(e);
 			
-			if (!fIsScheduled) {
-				fIsScheduled = true;
+			if (!this.fIsScheduled) {
+				this.fIsScheduled = true;
 				UIAccess.getDisplay().asyncExec(this);
 			}
 		}
 		
 		@Override
 		public synchronized void entryRemoved(final History source, final Entry e) {
-			if (fCurrentSource != source) {
+			if (this.fCurrentSource != source) {
 				return;
 			}
-			fToRemove.add(e);
+			this.fToRemove.add(e);
 			
-			if (!fIsScheduled) {
-				fIsScheduled = true;
+			if (!this.fIsScheduled) {
+				this.fIsScheduled = true;
 				UIAccess.getDisplay().asyncExec(this);
 			}
 		}
@@ -286,30 +287,30 @@
 			final ImList<Entry> toRemoveEntries;
 			final EntryFilter[] filter;
 			synchronized (this) {
-				fIsScheduled = false;
-				if (!UIAccess.isOkToUse(fTable)) {
+				this.fIsScheduled = false;
+				if (!UIAccess.isOkToUse(HistoryView.this.fTable)) {
 					return;
 				}
-				if ((fProcess != null && fCurrentSource != fProcess.getHistory())
-						|| (fProcess == null && fCurrentSource != null)) {
+				if ((HistoryView.this.fProcess != null && this.fCurrentSource != HistoryView.this.fProcess.getHistory())
+						|| (HistoryView.this.fProcess == null && this.fCurrentSource != null)) {
 					return;
 				}
 				
-				newEntries = fNewEntrys;
-				fNewEntrys = null;
-				toAdd = fToAdd.size();
-				toAddEntries = (toAdd > 0) ? ImCollections.clearToList(fToAdd) : null;
-				toRemove = fToRemove.size();
-				toRemoveEntries = (toRemove > REMOVE_THRESHOLD) ? ImCollections.clearToList(fToRemove) : null;
-				filter = fFilter.toArray();
+				newEntries = this.fNewEntrys;
+				this.fNewEntrys = null;
+				toAdd = this.fToAdd.size();
+				toAddEntries = (toAdd > 0) ? ImCollections.clearToList(this.fToAdd) : null;
+				toRemove = this.fToRemove.size();
+				toRemoveEntries = (toRemove > REMOVE_THRESHOLD) ? ImCollections.clearToList(this.fToRemove) : null;
+				filter = HistoryView.this.fFilter.toArray();
 			}
-			fTable.setRedraw(false);
+			HistoryView.this.fTable.setRedraw(false);
 			
 			TableItem addedItem = null;
 			if (newEntries != null) {
-				fTable.deselectAll();
+				HistoryView.this.fTable.deselectAll();
 				
-				final int reusableItemCount = fTable.getItemCount();
+				final int reusableItemCount = HistoryView.this.fTable.getItemCount();
 				int reuseItemIdx = 0;
 				final int n = newEntries.length;
 				ITER_ENTRY : for (int i = 0; i < n; i++) {
@@ -320,20 +321,20 @@
 						}
 					}
 					if (reuseItemIdx < reusableItemCount) {
-						addedItem = fTable.getItem(reuseItemIdx++);
+						addedItem = HistoryView.this.fTable.getItem(reuseItemIdx++);
 					}
 					else {
-						addedItem = new TableItem(fTable, SWT.NONE);
+						addedItem = new TableItem(HistoryView.this.fTable, SWT.NONE);
 					}
 					addedItem.setData(e);
 					updateItem(addedItem);
 				}
 				if (reuseItemIdx < reusableItemCount) {
-					fTable.remove(reuseItemIdx, reusableItemCount-1);
+					HistoryView.this.fTable.remove(reuseItemIdx, reusableItemCount-1);
 				}
 				
 				if (addedItem != null) {
-					fTable.showItem(addedItem);
+					HistoryView.this.fTable.showItem(addedItem);
 				}
 			}
 			
@@ -345,18 +346,18 @@
 							continue ITER_ENTRY;
 						}
 					}
-					addedItem = new TableItem(fTable, SWT.NONE);
+					addedItem = new TableItem(HistoryView.this.fTable, SWT.NONE);
 					addedItem.setData(e);
 					updateItem(addedItem);
 				}
 			}
 			if (toRemove > REMOVE_THRESHOLD) {
-				final int itemCount = fTable.getItemCount();
+				final int itemCount = HistoryView.this.fTable.getItemCount();
 				int[] removeIdxs = new int[toRemove];
 				int count = 0;
 				for (int i = 0; i < toRemove; i++) {
 					for (int j = 0; j < itemCount; j++) {
-						final TableItem removedItem = fTable.getItem(j);
+						final TableItem removedItem = HistoryView.this.fTable.getItem(j);
 						if (removedItem.getData() == toRemoveEntries.get(i)) {
 							removedItem.setData(null);
 							removeIdxs[count++] = j;
@@ -367,14 +368,14 @@
 					if (count < removeIdxs.length) {
 						System.arraycopy(removeIdxs, 0, removeIdxs = new int[count], 0, count);
 					}
-					fTable.remove(removeIdxs);
+					HistoryView.this.fTable.remove(removeIdxs);
 				}
 			}
 			
-			if (fDoAutoscroll && addedItem != null) {
-				fTable.showItem(addedItem);
+			if (HistoryView.this.fDoAutoscroll && addedItem != null) {
+				HistoryView.this.fTable.showItem(addedItem);
 			}
-			fTable.setRedraw(true);
+			HistoryView.this.fTable.setRedraw(true);
 		}
 	}
 	
@@ -386,15 +387,15 @@
 			setToolTipText(Messages.FilterEmptyAction_tooltip);
 			setImageDescriptor(SharedUIResources.getImages().getDescriptor(SharedUIResources.LOCTOOL_FILTER_IMAGE_ID));
 			
-			setChecked(fDoFilterEmpty);
+			setChecked(HistoryView.this.fDoFilterEmpty);
 		}
 		
 		@Override
 		public void run() {
 			final boolean switchOn = isChecked();
-			fDoFilterEmpty = switchOn;
+			HistoryView.this.fDoFilterEmpty = switchOn;
 			if (switchOn) {
-				addFilter(EMPTY_FILTER); 
+				addFilter(EMPTY_FILTER);
 			}
 			else {
 				removeFilter(EMPTY_FILTER);
@@ -409,24 +410,24 @@
 		
 		public FilterBySourceAction(final SubmitType type) {
 			super(type.getLabel(), null, SimpleContributionItem.STYLE_CHECK);
-			fType = type;
-			setChecked(fFilterBySource.fSubmitTypes.contains(type));
+			this.fType = type;
+			setChecked(HistoryView.this.fFilterBySource.fSubmitTypes.contains(type));
 		}
 		
 		@Override
 		protected void execute() throws ExecutionException {
-			final SubmitTypeFilter currentFilter = fFilterBySource;
+			final SubmitTypeFilter currentFilter = HistoryView.this.fFilterBySource;
 			final EnumSet<SubmitType> types = EnumSet.copyOf(currentFilter.fSubmitTypes);
-			if (types.contains(fType)) {
-				types.remove(fType);
+			if (types.contains(this.fType)) {
+				types.remove(this.fType);
 				setChecked(false);
 			}
 			else {
-				types.add(fType);
+				types.add(this.fType);
 				setChecked(true);
 			}
 			final SubmitTypeFilter newFilter = new SubmitTypeFilter(types);
-			fFilterBySource = newFilter;
+			HistoryView.this.fFilterBySource = newFilter;
 			replaceFilter(currentFilter, newFilter);
 		}
 		
@@ -452,7 +453,7 @@
 	private static final String M_FILTER_BY_SOURCE = "FilterBySource.include"; //$NON-NLS-1$
 	private SubmitTypeFilter fFilterBySource;
 	
-	private final FastList<IToolRetargetable> fToolListenerList= new FastList<>(IToolRetargetable.class);
+	private final CopyOnWriteIdentityListSet<ActiveToolListener> fToolListeners= new CopyOnWriteIdentityListSet<>();
 	
 	private Action fSelectAllAction;
 	private Action fCopyAction;
@@ -476,8 +477,8 @@
 	 * The constructor.
 	 */
 	public HistoryView() {
-		fReloadJob = new ViewReloadJob();
-		fContentProvider = new ViewContentProvider();
+		this.fReloadJob = new ViewReloadJob();
+		this.fContentProvider = new ViewContentProvider();
 	}
 	
 	
@@ -487,28 +488,28 @@
 		
 		final String autoscroll = (memento != null) ? memento.getString(M_AUTOSCROLL) : null;
 		if (autoscroll == null || autoscroll.equals("on")) { // default  //$NON-NLS-1$
-			fDoAutoscroll = true;
+			this.fDoAutoscroll = true;
 		}
 		else {
-			fDoAutoscroll = false;
+			this.fDoAutoscroll = false;
 		}
 		
 		final String filterBySource = (memento != null) ? memento.getString(M_FILTER_BY_SOURCE) : null;
 		if (filterBySource == null) {
-			fFilterBySource = new SubmitTypeFilter(EnumSet.range(SubmitType.CONSOLE, SubmitType.TOOLS));
+			this.fFilterBySource = new SubmitTypeFilter(EnumSet.range(SubmitType.CONSOLE, SubmitType.TOOLS));
 		}
 		else {
-			fFilterBySource = new SubmitTypeFilter(SOURCE_ENCODER.store2Usage(filterBySource));
+			this.fFilterBySource = new SubmitTypeFilter(SOURCE_ENCODER.store2Usage(filterBySource));
 		}
-		fFilter.add(fFilterBySource);
+		this.fFilter.add(this.fFilterBySource);
 		
 		final String filterEmpty = (memento != null) ? memento.getString(M_FILTER_EMPTY) : null;
 		if (filterEmpty == null || filterEmpty.equals("off")) { // default  //$NON-NLS-1$
-			fDoFilterEmpty = false;
+			this.fDoFilterEmpty = false;
 		}
 		else {
-			fDoFilterEmpty = true;
-			fFilter.add(EMPTY_FILTER);
+			this.fDoFilterEmpty = true;
+			this.fFilter.add(EMPTY_FILTER);
 		}
 	}
 	
@@ -516,64 +517,64 @@
 	public void saveState(final IMemento memento) {
 		super.saveState(memento);
 		
-		memento.putString(M_AUTOSCROLL, (fDoAutoscroll) ? "on" : "off"); //$NON-NLS-1$ //$NON-NLS-2$
-		memento.putString(M_FILTER_BY_SOURCE, SOURCE_ENCODER.usage2Store(fFilterBySource.fSubmitTypes));
-		memento.putString(M_FILTER_EMPTY, (fDoFilterEmpty) ? "on" : "off"); //$NON-NLS-1$ //$NON-NLS-2$
+		memento.putString(M_AUTOSCROLL, (this.fDoAutoscroll) ? "on" : "off"); //$NON-NLS-1$ //$NON-NLS-2$
+		memento.putString(M_FILTER_BY_SOURCE, SOURCE_ENCODER.usage2Store(this.fFilterBySource.fSubmitTypes));
+		memento.putString(M_FILTER_EMPTY, (this.fDoFilterEmpty) ? "on" : "off"); //$NON-NLS-1$ //$NON-NLS-2$
 	}
 	
 	@Override
 	public void createPartControl(final Composite parent) {
 		parent.setLayout(LayoutUtils.newSashGrid());
 		
-		fTable = new Table(parent, SWT.MULTI | SWT.V_SCROLL | SWT.FULL_SELECTION);
-		fTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-		fTable.setLinesVisible(false);
-		fTable.setHeaderVisible(false);
-		new DefaultToolTip(fTable) {
+		this.fTable = new Table(parent, SWT.MULTI | SWT.V_SCROLL | SWT.FULL_SELECTION);
+		this.fTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+		this.fTable.setLinesVisible(false);
+		this.fTable.setHeaderVisible(false);
+		new DefaultToolTip(this.fTable) {
 			
 			private final DateFormat fFormat = DateFormat.getDateTimeInstance();
 			
 			@Override
 			protected boolean shouldCreateToolTip(final Event event) {
 				return (super.shouldCreateToolTip(event)
-						&& fTable.getItem(new Point(event.x, event.y)) != null );
+						&& HistoryView.this.fTable.getItem(new Point(event.x, event.y)) != null );
 			}
 			
 			@Override
 			protected String getText(final Event event) {
-				final TableItem item = fTable.getItem(new Point(event.x, event.y));
+				final TableItem item = HistoryView.this.fTable.getItem(new Point(event.x, event.y));
 				if (item != null) {
 					final Entry e = (Entry) item.getData();
 					if (e.getTimeStamp() < 0) {
 						return "[-]\n"+e.getCommand(); //$NON-NLS-1$
 					}
 					else {
-						return "["+fFormat.format(new Date(e.getTimeStamp()))+"]\n" + e.getCommand(); //$NON-NLS-1$ //$NON-NLS-2$
+						return "["+this.fFormat.format(new Date(e.getTimeStamp()))+"]\n" + e.getCommand(); //$NON-NLS-1$ //$NON-NLS-2$
 					}
 				}
 				return null;
 			}
 			
 		};
-		final TableColumn column = new TableColumn(fTable, SWT.DEFAULT);
-		fTable.addListener(SWT.Resize, new Listener() {
+		final TableColumn column = new TableColumn(this.fTable, SWT.DEFAULT);
+		this.fTable.addListener(SWT.Resize, new Listener() {
 			@Override
 			public void handleEvent(final Event event) {
 				// adapt the column width to the width of the table
-				final int tableWidth = fTable.getClientArea().width;
+				final int tableWidth = HistoryView.this.fTable.getClientArea().width;
 				if (tableWidth == 0) {
 					return;
 				}
 				column.setWidth(tableWidth);
 			}
 		});
-		fTable.addKeyListener(new KeyListener() {
+		this.fTable.addKeyListener(new KeyListener() {
 			@Override
 			public void keyPressed(final KeyEvent e) {
-				if (e.keyCode == SWT.ARROW_UP && 
-						fTable.getSelectionCount() == 1 && fTable.getSelectionIndex() == 0) {
-					fSearchTextItem.show();
-					fTable.deselectAll();
+				if (e.keyCode == SWT.ARROW_UP &&
+						HistoryView.this.fTable.getSelectionCount() == 1 && HistoryView.this.fTable.getSelectionIndex() == 0) {
+					HistoryView.this.fSearchTextItem.show();
+					HistoryView.this.fTable.deselectAll();
 					e.doit = false;
 				}
 			}
@@ -586,13 +587,13 @@
 		hookContextMenu();
 		contributeToActionBars();
 		
-		final DragSource dragSource = new DragSource(fTable, DND.DROP_COPY);
+		final DragSource dragSource = new DragSource(this.fTable, DND.DROP_COPY);
 		dragSource.setTransfer(new Transfer[] { TextTransfer.getInstance() });
 		dragSource.addDragListener(new HistoryDragAdapter(this));
 		
 		// listen on console changes
 		final IToolRegistry toolRegistry = NicoUI.getToolRegistry();
-		fToolRegistryListener = new IToolRegistryListener() {
+		this.fToolRegistryListener = new IToolRegistryListener() {
 			@Override
 			public void toolSessionActivated(final ToolSessionUIData sessionData) {
 				final ToolProcess process = sessionData.getProcess();
@@ -609,14 +610,14 @@
 				UIAccess.getDisplay().syncExec(new Runnable() {
 					@Override
 					public void run() {
-						if (fProcess != null && fProcess == process) {
+						if (HistoryView.this.fProcess != null && HistoryView.this.fProcess == process) {
 							connect(null);
 						}
 					}
 				});
 			}
 		};
-		toolRegistry.addListener(fToolRegistryListener, getViewSite().getPage());
+		toolRegistry.addListener(this.fToolRegistryListener, getViewSite().getPage());
 		connect(toolRegistry.getActiveToolSession(getViewSite().getPage()).getProcess());
 		
 		PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, "org.eclipse.statet.nico.ui.cmd_history_view"); //$NON-NLS-1$
@@ -631,28 +632,28 @@
 	private void createActions() {
 		final IHandlerService handlerService = getSite().getService(IHandlerService.class);
 		
-		fFilterEmptyAction = new FilterEmptyAction();
-		fScrollLockAction = new ScrollLockAction(new Receiver() {
+		this.fFilterEmptyAction = new FilterEmptyAction();
+		this.fScrollLockAction = new ScrollLockAction(new Receiver() {
 			@Override
 			public void setAutoScroll(final boolean enabled) {
-				fDoAutoscroll = enabled;
+				HistoryView.this.fDoAutoscroll = enabled;
 			}
-		}, !fDoAutoscroll);
-		fSelectAllAction = new Action() {
+		}, !this.fDoAutoscroll);
+		this.fSelectAllAction = new Action() {
 			@Override
 			public void run() {
-				fTable.selectAll();
+				HistoryView.this.fTable.selectAll();
 			}
 		};
 		
-		fCopyAction = new HistoryCopyAction(this);
-		fSubmitAction = new HistorySubmitAction(this);
+		this.fCopyAction = new HistoryCopyAction(this);
+		this.fSubmitAction = new HistorySubmitAction(this);
 		
 		enabledSelectionActions(false);
-		fTable.addSelectionListener(new SelectionListener() {
+		this.fTable.addSelectionListener(new SelectionListener() {
 			@Override
 			public void widgetSelected(final SelectionEvent e) {
-				if (fTable.getSelectionCount() > 0) {
+				if (HistoryView.this.fTable.getSelectionCount() > 0) {
 					enabledSelectionActions(true);
 				}
 				else {
@@ -661,46 +662,46 @@
 			};
 			@Override
 			public void widgetDefaultSelected(final SelectionEvent e) {
-				fSubmitAction.run();
+				HistoryView.this.fSubmitAction.run();
 			}
 		} );
 		
-		fLoadHistoryAction = new LoadHistoryAction(this);
-		fSaveHistoryAction = new SaveHistoryAction(this);
+		this.fLoadHistoryAction = new LoadHistoryAction(this);
+		this.fSaveHistoryAction = new SaveHistoryAction(this);
 		
-		fSearchStartHandler = new AbstractHandler() {
+		this.fSearchStartHandler = new AbstractHandler() {
 			@Override
 			public Object execute(final ExecutionEvent arg0) {
-				fSearchTextItem.show();
+				HistoryView.this.fSearchTextItem.show();
 				return null;
 			}
 		};
-		handlerService.activateHandler(IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE, fSearchStartHandler);
+		handlerService.activateHandler(IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE, this.fSearchStartHandler);
 		
-		fSearchNextHandler = new AbstractHandler() {
+		this.fSearchNextHandler = new AbstractHandler() {
 			@Override
 			public Object execute(final ExecutionEvent arg0) {
 				search(true, -1);
 				return null;
 			}
 		};
-		handlerService.activateHandler(SharedUIResources.FIND_NEXT_COMMAND_ID, fSearchNextHandler);
-		handlerService.activateHandler("org.eclipse.ui.navigate.next", fSearchNextHandler); //$NON-NLS-1$
+		handlerService.activateHandler(SharedUIResources.FIND_NEXT_COMMAND_ID, this.fSearchNextHandler);
+		handlerService.activateHandler("org.eclipse.ui.navigate.next", this.fSearchNextHandler); //$NON-NLS-1$
 		
-		fSearchPrevHandler = new AbstractHandler() {
+		this.fSearchPrevHandler = new AbstractHandler() {
 			@Override
 			public Object execute(final ExecutionEvent arg0) {
 				search(false, -1);
 				return null;
 			}
 		};
-		handlerService.activateHandler(SharedUIResources.FIND_PREVIOUS_COMMAND_ID, fSearchPrevHandler);
-		handlerService.activateHandler("org.eclipse.ui.navigate.previous", fSearchPrevHandler); //$NON-NLS-1$
+		handlerService.activateHandler(SharedUIResources.FIND_PREVIOUS_COMMAND_ID, this.fSearchPrevHandler);
+		handlerService.activateHandler("org.eclipse.ui.navigate.previous", this.fSearchPrevHandler); //$NON-NLS-1$
 	}
 	
 	protected void enabledSelectionActions(final boolean enable) {
-		fCopyAction.setEnabled(enable);
-		fSubmitAction.setEnabled(enable);
+		this.fCopyAction.setEnabled(enable);
+		this.fSubmitAction.setEnabled(enable);
 	}
 	
 	
@@ -713,24 +714,24 @@
 				HistoryView.this.fillContextMenu(manager);
 			}
 		});
-		final Menu menu = menuMgr.createContextMenu(fTable);
-		fTable.setMenu(menu);
+		final Menu menu = menuMgr.createContextMenu(this.fTable);
+		this.fTable.setMenu(menu);
 //		getSite().registerContextMenu(menuMgr, fTableViewer);
 	}
 	
 	private void contributeToActionBars() {
 		final IActionBars bars = getViewSite().getActionBars();
 		
-		bars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), fSelectAllAction);
-		bars.setGlobalActionHandler(ActionFactory.COPY.getId(), fCopyAction);
+		bars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), this.fSelectAllAction);
+		bars.setGlobalActionHandler(ActionFactory.COPY.getId(), this.fCopyAction);
 				
 		fillLocalPullDown(bars.getMenuManager());
 		fillLocalToolBar(bars.getToolBarManager());
 	}
 	
 	private void fillLocalPullDown(final IMenuManager manager) {
-		manager.add(fLoadHistoryAction);
-		manager.add(fSaveHistoryAction);
+		manager.add(this.fLoadHistoryAction);
+		manager.add(this.fSaveHistoryAction);
 		
 		manager.add(new Separator());
 		final IMenuManager filterBySourceMenu = new MenuManager("Include Commands &From", SharedUIResources.getImages().getDescriptor(SharedUIResources.LOCTOOL_FILTER_IMAGE_ID), null);
@@ -740,108 +741,110 @@
 			filterBySourceMenu.add(new FilterBySourceAction(submitType));
 		}
 		manager.add(filterBySourceMenu);
-		manager.add(fFilterEmptyAction);
+		manager.add(this.fFilterEmptyAction);
 		
 		manager.add(new Separator());
-		manager.add(fScrollLockAction);
+		manager.add(this.fScrollLockAction);
 		
 		manager.add(new Separator());
 	}
 	
 	private void fillContextMenu(final IMenuManager manager) {
-		manager.add(fCopyAction);
-		manager.add(fSubmitAction);
+		manager.add(this.fCopyAction);
+		manager.add(this.fSubmitAction);
 		
 		// Other plug-ins can contribute there actions here
 		manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
 	}
 	
 	private void fillLocalToolBar(final IToolBarManager manager) {
-		fSearchTextItem = new SearchContributionItem("search.text", //$NON-NLS-1$
+		this.fSearchTextItem = new SearchContributionItem("search.text", //$NON-NLS-1$
 				SearchContributionItem.VIEW_TOOLBAR ) {
 			@Override
 			protected void search() {
 				HistoryView.this.search(true, -1);
 			}
 		};
-		fSearchTextItem.setToolTip(Messages.HistorySearch_Pattern_tooltip);
-		fSearchTextItem.setSizeControl(fTable.getParent());
-		fSearchTextItem.setResultControl(fTable);
+		this.fSearchTextItem.setToolTip(Messages.HistorySearch_Pattern_tooltip);
+		this.fSearchTextItem.setSizeControl(this.fTable.getParent());
+		this.fSearchTextItem.setResultControl(this.fTable);
 		
-		manager.add(fSearchTextItem);
+		manager.add(this.fSearchTextItem);
 		
 		final ImageRegistry ecommonsImages = SharedUIResources.getImages();
 		manager.add(new HandlerContributionItem(new CommandContributionItemParameter(
 				getSite(), "search.next", SharedUIResources.FIND_NEXT_COMMAND_ID, null, //$NON-NLS-1$
 				ecommonsImages.getDescriptor(SharedUIResources.LOCTOOL_DOWN_IMAGE_ID), null, ecommonsImages.getDescriptor(SharedUIResources.LOCTOOL_DOWN_H_IMAGE_ID),
-				SharedMessages.FindNext_tooltip, null, null, SWT.PUSH, null, false), fSearchNextHandler));
+				SharedMessages.FindNext_tooltip, null, null, SWT.PUSH, null, false), this.fSearchNextHandler));
 		manager.add(new HandlerContributionItem(new CommandContributionItemParameter(
 				getSite(), "search.previous", SharedUIResources.FIND_PREVIOUS_COMMAND_ID, null, //$NON-NLS-1$
 				ecommonsImages.getDescriptor(SharedUIResources.LOCTOOL_UP_IMAGE_ID), null, ecommonsImages.getDescriptor(SharedUIResources.LOCTOOL_UP_H_IMAGE_ID),
-				SharedMessages.FindPrevious_tooltip, null, null, SWT.PUSH, null, false), fSearchPrevHandler));
+				SharedMessages.FindPrevious_tooltip, null, null, SWT.PUSH, null, false), this.fSearchPrevHandler));
 		
 		manager.add(new Separator());
 		
-		manager.add(fScrollLockAction);
+		manager.add(this.fScrollLockAction);
 	}
 	
 	/** May only be called in UI thread */
 	public void connect(final ToolProcess process) {
-		if (fProcess == process) {
+		if (this.fProcess == process) {
 			return;
 		}
-		if (fProcess != null) {
-			fProcess.getHistory().removeListener(fContentProvider);
+		if (this.fProcess != null) {
+			this.fProcess.getHistory().removeListener(this.fContentProvider);
 		}
-		fProcess = process;
-		if (fProcess != null) {
-			fProcess.getHistory().addListener(fContentProvider);
+		this.fProcess = process;
+		if (this.fProcess != null) {
+			this.fProcess.getHistory().addListener(this.fContentProvider);
 		}
 		scheduleRefresh(true);
-		for (final IToolRetargetable action : fToolListenerList.toArray()) {
-			action.setTool(fProcess);
+		
+		final ActiveToolEvent event= new ActiveToolEvent(ActiveToolEvent.TOOL_ACTIVATED, this.fProcess);
+		for (final ActiveToolListener listener : this.fToolListeners) {
+			listener.onToolChanged(event);
 		}
 	}
 	
 	private void scheduleRefresh(final boolean change) {
 		final IWorkbenchSiteProgressService context = getSite().getAdapter(IWorkbenchSiteProgressService.class);
 		if (change) {
-			fReloadJob.cancel();
-			context.schedule(fReloadJob, 200);
+			this.fReloadJob.cancel();
+			context.schedule(this.fReloadJob, 200);
 		}
 		else {
-			context.schedule(fReloadJob, 0);
+			context.schedule(this.fReloadJob, 0);
 		}
 	}
 	
 	@Override
-	public void addToolRetargetable(final IToolRetargetable action) {
-		fToolListenerList.add(action);
+	public void addToolListener(final ActiveToolListener action) {
+		this.fToolListeners.add(action);
 	}
 	
 	@Override
-	public void removeToolRetargetable(final IToolRetargetable action) {
-		fToolListenerList.remove(action);
+	public void removeToolListener(final ActiveToolListener action) {
+		this.fToolListeners.remove(action);
 	}
 	
 	public void addFilter(final EntryFilter filter) {
-		fFilter.add(filter);
+		this.fFilter.add(filter);
 		scheduleRefresh(false);
 	}
 	
 	public void removeFilter(final EntryFilter filter) {
-		fFilter.remove(filter);
+		this.fFilter.remove(filter);
 		scheduleRefresh(false);
 	}
 	
 	public void replaceFilter(final EntryFilter oldFilter, final EntryFilter newFilter) {
-		fFilter.replace(oldFilter, newFilter);
+		this.fFilter.replace(oldFilter, newFilter);
 		scheduleRefresh(false);
 	}
 	
 	public void search(final String pattern, final boolean forward) {
-		fSearchTextItem.getSearchText().setText(pattern);
-		search(forward, forward ? 0 : fTable.getItemCount()-1);
+		this.fSearchTextItem.getSearchText().setText(pattern);
+		search(forward, forward ? 0 : this.fTable.getItemCount()-1);
 	}
 	
 	
@@ -852,7 +855,7 @@
 	 */
 	@Override
 	public ToolProcess getTool() {
-		return fProcess;
+		return this.fProcess;
 	}
 	
 	/**
@@ -861,15 +864,15 @@
 	 * @return a clipboard object.
 	 */
 	public Clipboard getClipboard() {
-		if (fClipboard == null) {
-			fClipboard = new Clipboard(Display.getCurrent());
+		if (this.fClipboard == null) {
+			this.fClipboard = new Clipboard(Display.getCurrent());
 		}
 		
-		return fClipboard;
+		return this.fClipboard;
 	}
 	
 	public Entry[] getSelection() {
-		final TableItem[] items = fTable.getSelection();
+		final TableItem[] items = this.fTable.getSelection();
 		final int n = items.length;
 		final Entry[] selection = new Entry[n];
 		for (int i = 0; i < n; i++) {
@@ -881,16 +884,16 @@
 	@Override
 	public void setFocus() {
 		// Passing the focus request to the viewer's control.
-		fTable.setFocus();
+		this.fTable.setFocus();
 	}
 	
 	private void search(final boolean forward, final int startIdx) {
-		if (!UIAccess.isOkToUse(fTable)) {
+		if (!UIAccess.isOkToUse(this.fTable)) {
 			return;
 		}
 		
-		final int itemCount = fTable.getItemCount();
-		final String text = fSearchTextItem.getText();
+		final int itemCount = this.fTable.getItemCount();
+		final String text = this.fSearchTextItem.getText();
 		if (itemCount == 0 || text.isEmpty()) {
 			return;
 		}
@@ -905,11 +908,11 @@
 				break;
 			}
 		} while (start < text.length());
-		fSearchPattern.setPattern(text.substring(start));
+		this.fSearchPattern.setPattern(text.substring(start));
 		
 		int idx;
 		if (startIdx < 0) {
-			idx = fTable.getSelectionIndex();
+			idx = this.fTable.getSelectionIndex();
 		}
 		else {
 			idx = (forward) ? startIdx-1 : startIdx+1;
@@ -917,11 +920,11 @@
 		if (forward) {
 			idx++;
 			while (idx < itemCount) {
-				final Entry e = (Entry) fTable.getItem(idx).getData();
+				final Entry e = (Entry) this.fTable.getItem(idx).getData();
 				final int offset = e.getCommandMarker();
-				if (fSearchPattern.matches(e.getCommand().substring(
+				if (this.fSearchPattern.matches(e.getCommand().substring(
 						offset >= 0 ? offset : -1-offset))) {
-					fTable.setSelection(idx);
+					this.fTable.setSelection(idx);
 					return;
 				}
 				idx++;
@@ -930,11 +933,11 @@
 		else {
 			idx--;
 			while (idx >= 0) {
-				final Entry e = (Entry) fTable.getItem(idx).getData();
+				final Entry e = (Entry) this.fTable.getItem(idx).getData();
 				final int offset = e.getCommandMarker();
-				if (fSearchPattern.matches(e.getCommand().substring(
+				if (this.fSearchPattern.matches(e.getCommand().substring(
 						offset >= 0 ? offset : -1-offset))) {
-					fTable.setSelection(idx);
+					this.fTable.setSelection(idx);
 					return;
 				}
 				idx--;
@@ -945,42 +948,42 @@
 	
 	@Override
 	public void dispose() {
-		if (fToolRegistryListener != null) {
-			NicoUI.getToolRegistry().removeListener(fToolRegistryListener);
-			fToolRegistryListener = null;
+		if (this.fToolRegistryListener != null) {
+			NicoUI.getToolRegistry().removeListener(this.fToolRegistryListener);
+			this.fToolRegistryListener = null;
 		}
-		fReloadJob.cancel();
-		final ToolProcess process = fProcess;
+		this.fReloadJob.cancel();
+		final ToolProcess process = this.fProcess;
 		if (process != null) {
-			process.getHistory().removeListener(fContentProvider);
+			process.getHistory().removeListener(this.fContentProvider);
 		}
-		fToolListenerList.clear();
-		fProcess = null;
-		fCopyAction = null;
-		fSubmitAction = null;
-		fLoadHistoryAction = null;
-		fSaveHistoryAction = null;
-		if (fSearchStartHandler != null) {
-			fSearchStartHandler.dispose();
-			fSearchStartHandler = null;
+		this.fToolListeners.clear();
+		this.fProcess = null;
+		this.fCopyAction = null;
+		this.fSubmitAction = null;
+		this.fLoadHistoryAction = null;
+		this.fSaveHistoryAction = null;
+		if (this.fSearchStartHandler != null) {
+			this.fSearchStartHandler.dispose();
+			this.fSearchStartHandler = null;
 		}
-		if (fSearchPrevHandler != null) {
-			fSearchPrevHandler.dispose();
-			fSearchPrevHandler = null;
+		if (this.fSearchPrevHandler != null) {
+			this.fSearchPrevHandler.dispose();
+			this.fSearchPrevHandler = null;
 		}
-		if (fSearchNextHandler != null) {
-			fSearchNextHandler.dispose();
-			fSearchNextHandler = null;
+		if (this.fSearchNextHandler != null) {
+			this.fSearchNextHandler.dispose();
+			this.fSearchNextHandler = null;
 		}
 		
 		super.dispose();
 		
-		if (fClipboard != null) {
-			fClipboard.dispose();
-			fClipboard = null;
+		if (this.fClipboard != null) {
+			this.fClipboard.dispose();
+			this.fClipboard = null;
 		}
 		
-		fTable = null;
+		this.fTable = null;
 	}
 	
 }
diff --git a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/views/QueueView.java b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/views/QueueView.java
index ec4d265..f3b3b60 100644
--- a/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/views/QueueView.java
+++ b/r/org.eclipse.statet.nico.ui/src/org/eclipse/statet/nico/ui/views/QueueView.java
@@ -14,8 +14,6 @@
 
 package org.eclipse.statet.nico.ui.views;
 
-import java.util.List;
-
 import org.eclipse.debug.core.DebugEvent;
 import org.eclipse.debug.core.DebugPlugin;
 import org.eclipse.debug.core.IDebugEventSetListener;
@@ -52,6 +50,9 @@
 import org.eclipse.ui.menus.CommandContributionItemParameter;
 import org.eclipse.ui.part.ViewPart;
 
+import org.eclipse.statet.jcommons.collections.ImCollections;
+import org.eclipse.statet.jcommons.collections.ImList;
+
 import org.eclipse.statet.ecommons.ts.core.ToolRunnable;
 import org.eclipse.statet.ecommons.ui.util.UIAccess;
 
@@ -75,16 +76,10 @@
 public class QueueView extends ViewPart {
 	
 	
-	private static ToolRunnable[] toArray(final IStructuredSelection selection) {
-		final List<?> list = selection.toList();
-		return list.toArray(new ToolRunnable[list.size()]);
-	}
-	
-	
 	private class ViewContentProvider implements IStructuredContentProvider, IDebugEventSetListener {
 		
-		private volatile boolean fExpectInfoEvent = false;
-		private ToolRunnable[] fRefreshData;
+		private volatile boolean expectInfoEvent= false;
+		private ImList<ToolRunnable> refreshData;
 		
 		@Override
 		public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) {
@@ -92,9 +87,9 @@
 				unregister();
 			}
 			if (newInput != null) {
-				final ToolProcess newProcess = (ToolProcess) newInput;
+				final ToolProcess newProcess= (ToolProcess) newInput;
 				
-				final DebugPlugin manager = DebugPlugin.getDefault();
+				final DebugPlugin manager= DebugPlugin.getDefault();
 				if (manager != null) {
 					manager.addDebugEventListener(this);
 				}
@@ -104,15 +99,15 @@
 		@Override
 		public Object[] getElements(final Object inputElement) {
 			ToolRunnable[] elements;
-			if (fRefreshData != null) {
-				elements = fRefreshData;
-				fRefreshData = null;
+			if (this.refreshData != null) {
+				elements= this.refreshData.toArray(new ToolRunnable[this.refreshData.size()]);
+				this.refreshData= null;
 			}
 			else {
-				elements = new ToolRunnable[0];
-				final Queue queue = getQueue();
+				elements= new ToolRunnable[0];
+				final Queue queue= getQueue();
 				if (queue != null) {
-					fExpectInfoEvent = true;
+					this.expectInfoEvent= true;
 					queue.sendElements();
 				}
 			}
@@ -120,7 +115,7 @@
 		}
 		
 		private void unregister() {
-			final DebugPlugin manager = DebugPlugin.getDefault();
+			final DebugPlugin manager= DebugPlugin.getDefault();
 			if (manager != null) {
 				manager.removeDebugEventListener(this);
 			}
@@ -131,30 +126,30 @@
 			unregister();
 		}
 		
-		private void setElements(final ToolRunnable[] elements) {
+		private void setElements(final ImList<ToolRunnable> elements) {
 			UIAccess.getDisplay().syncExec(new Runnable() {
 				@Override
 				public void run() {
-					if (!UIAccess.isOkToUse(fTableViewer)) {
+					if (!UIAccess.isOkToUse(QueueView.this.tableViewer)) {
 						return;
 					}
-					fRefreshData = elements;
-					fTableViewer.refresh();
+					ViewContentProvider.this.refreshData= elements;
+					QueueView.this.tableViewer.refresh();
 				}
 			});
 		}
 		
 		@Override
 		public void handleDebugEvents(final DebugEvent[] events) {
-			final ToolProcess process = fProcess;
+			final ToolProcess process= QueueView.this.process;
 			if (process == null) {
 				return;
 			}
-			boolean updateProgress = false;
-			final Queue queue = process.getQueue();
-			EVENT: for (int i = 0; i < events.length; i++) {
-				final DebugEvent event = events[i];
-				final Object source = event.getSource();
+			boolean updateProgress= false;
+			final Queue queue= process.getQueue();
+			EVENT: for (int i= 0; i < events.length; i++) {
+				final DebugEvent event= events[i];
+				final Object source= event.getSource();
 				if (source == queue) {
 					switch (event.getKind()) {
 					
@@ -162,21 +157,21 @@
 						if (event.getDetail() != DebugEvent.CONTENT) {
 							continue EVENT;
 						}
-						final Queue.TaskDelta taskDelta = (Queue.TaskDelta) event.getData();
+						final Queue.TaskDelta taskDelta= (Queue.TaskDelta) event.getData();
 						switch (taskDelta.type) {
 						case ToolRunnable.ADDING_TO:
 						case ToolRunnable.MOVING_TO:
-							if (!fExpectInfoEvent) {
+							if (!this.expectInfoEvent) {
 								if (events.length > i + 1 && taskDelta.data.size() == 1) {
 									// Added and removed in same set
-									final DebugEvent next = events[i + 1];
+									final DebugEvent next= events[i + 1];
 									if (next.getSource() == queue
 											&& next.getKind() == DebugEvent.CHANGE
 											&& next.getDetail() == DebugEvent.CONTENT) {
-										final Queue.TaskDelta nextDelta = (Queue.TaskDelta) next.getData();
+										final Queue.TaskDelta nextDelta= (Queue.TaskDelta) next.getData();
 										if (nextDelta.type == ToolRunnable.STARTING
 												&& taskDelta.data.get(0) == nextDelta.data.get(0)) {
-											updateProgress = true;
+											updateProgress= true;
 											i++;
 											continue EVENT;
 										}
@@ -185,16 +180,16 @@
 								UIAccess.getDisplay().syncExec(new Runnable() {
 									@Override
 									public void run() {
-										if (!UIAccess.isOkToUse(fTableViewer)) {
+										if (!UIAccess.isOkToUse(QueueView.this.tableViewer)) {
 											return;
 										}
 										if (taskDelta.position >= 0) {
-											for (int j = 0; j < taskDelta.data.size(); j++) {
-												fTableViewer.insert(taskDelta.data.get(j), taskDelta.position + j);
+											for (int j= 0; j < taskDelta.data.size(); j++) {
+												QueueView.this.tableViewer.insert(taskDelta.data.get(j), taskDelta.position + j);
 											}
 										}
 										else {
-											fTableViewer.add(taskDelta.data.toArray());
+											QueueView.this.tableViewer.add(taskDelta.data.toArray());
 										}
 									}
 								});
@@ -202,18 +197,18 @@
 							continue EVENT;
 						
 						case ToolRunnable.STARTING:
-							updateProgress = true;
+							updateProgress= true;
 							//$FALL-THROUGH$ continue with delete
 						case ToolRunnable.REMOVING_FROM:
 						case ToolRunnable.MOVING_FROM:
-							if (!fExpectInfoEvent) {
+							if (!this.expectInfoEvent) {
 								UIAccess.getDisplay().syncExec(new Runnable() {
 									@Override
 									public void run() {
-										if (!UIAccess.isOkToUse(fTableViewer)) {
+										if (!UIAccess.isOkToUse(QueueView.this.tableViewer)) {
 											return;
 										}
-										fTableViewer.remove(taskDelta.data.toArray());
+										QueueView.this.tableViewer.remove(taskDelta.data.toArray());
 									}
 								});
 							}
@@ -228,9 +223,9 @@
 						continue EVENT;
 						
 					case DebugEvent.MODEL_SPECIFIC:
-						if (event.getDetail() == Queue.QUEUE_INFO && fExpectInfoEvent) {
-							fExpectInfoEvent = false;
-							setElements((ToolRunnable[]) event.getData());
+						if (event.getDetail() == Queue.QUEUE_INFO && this.expectInfoEvent) {
+							this.expectInfoEvent= false;
+							setElements((ImList<ToolRunnable>) event.getData());
 						}
 						continue EVENT;
 						
@@ -243,8 +238,8 @@
 					}
 				}
 			}
-			if (updateProgress && fShowProgress) {
-				final ToolProgressGroup progress = fProgressControl;
+			if (updateProgress && QueueView.this.showProgress) {
+				final ToolProgressGroup progress= QueueView.this.progressControl;
 				if (progress != null) {
 					progress.refresh(false);
 				}
@@ -277,7 +272,7 @@
 		
 		@Override
 		public String getText(final Object element) {
-			final ToolRunnable runnable = (ToolRunnable) element;
+			final ToolRunnable runnable= (ToolRunnable) element;
 			return runnable.getLabel();
 		}
 	}
@@ -287,13 +282,13 @@
 		public ShowDescriptionAction() {
 			setText(Messages.ShowToolDescription_name);
 			setToolTipText(Messages.ShowToolDescription_tooltip);
-			setChecked(fShowDescription);
+			setChecked(QueueView.this.showDescription);
 		}
 		
 		@Override
 		public void run() {
-			fShowDescription = isChecked();
-			updateContentDescription(fProcess);
+			QueueView.this.showDescription= isChecked();
+			updateContentDescription(QueueView.this.process);
 		}
 	}
 	
@@ -302,45 +297,45 @@
 		public ShowProgressAction() {
 			setText(Messages.ShowProgress_name);
 			setToolTipText(Messages.ShowProgress_tooltip);
-			setChecked(fShowProgress);
+			setChecked(QueueView.this.showProgress);
 		}
 		
 		@Override
 		public void run() {
-			fShowProgress = isChecked();
-			if (fShowProgress) {
+			QueueView.this.showProgress= isChecked();
+			if (QueueView.this.showProgress) {
 				createProgressControl();
-				fProgressControl.setTool(fProcess, true);
-				fProgressControl.getControl().moveAbove(fTableViewer.getControl());
+				QueueView.this.progressControl.setTool(QueueView.this.process, true);
+				QueueView.this.progressControl.getControl().moveAbove(QueueView.this.tableViewer.getControl());
 			}
 			else {
-				if (fProgressControl != null) {
-					fProgressControl.getControl().dispose();
-					fProgressControl = null;
+				if (QueueView.this.progressControl != null) {
+					QueueView.this.progressControl.getControl().dispose();
+					QueueView.this.progressControl= null;
 				}
 			}
-			fComposite.layout(true);
+			QueueView.this.composite.layout(true);
 		}
 	}
 	
 	
-	private Composite fComposite;
-	private ToolProgressGroup fProgressControl;
-	private TableViewer fTableViewer;
+	private Composite composite;
+	private ToolProgressGroup progressControl;
+	private TableViewer tableViewer;
 	
-	private ToolProcess fProcess;
-	private IToolRegistryListener fToolRegistryListener;
+	private ToolProcess process;
+	private IToolRegistryListener toolRegistryListener;
 	
-	private static final String M_SHOW_DESCRIPTION = "QueueView.ShowDescription"; //$NON-NLS-1$
-	private boolean fShowDescription;
-	private Action fShowDescriptionAction;
+	private static final String M_SHOW_DESCRIPTION= "QueueView.ShowDescription"; //$NON-NLS-1$
+	private boolean showDescription;
+	private Action showDescriptionAction;
 	
-	private static final String M_SHOW_PROGRESS = "QueueView.ShowProgress"; //$NON-NLS-1$
-	private boolean fShowProgress;
-	private Action fShowProgressAction;
+	private static final String M_SHOW_PROGRESS= "QueueView.ShowProgress"; //$NON-NLS-1$
+	private boolean showProgress;
+	private Action showProgressAction;
 	
-	private Action fSelectAllAction;
-	private Action fDeleteAction;
+	private Action selectAllAction;
+	private Action deleteAction;
 	
 	
 	public QueueView() {
@@ -351,18 +346,18 @@
 	public void init(final IViewSite site, final IMemento memento) throws PartInitException {
 		super.init(site, memento);
 		
-		final String showDescription = (memento != null) ? memento.getString(M_SHOW_DESCRIPTION) : null;
+		final String showDescription= (memento != null) ? memento.getString(M_SHOW_DESCRIPTION) : null;
 		if (showDescription == null || showDescription.equals("off")) { // default  //$NON-NLS-1$
-			fShowDescription = false;
+			this.showDescription= false;
 		} else {
-			fShowDescription = true;
+			this.showDescription= true;
 		}
 		
-		final String showProgress = (memento != null) ? memento.getString(M_SHOW_PROGRESS) : null;
+		final String showProgress= (memento != null) ? memento.getString(M_SHOW_PROGRESS) : null;
 		if (showProgress== null || showProgress.equals("on")) { // default  //$NON-NLS-1$
-			fShowProgress = true;
+			this.showProgress= true;
 		} else {
-			fShowProgress = false;
+			this.showProgress= false;
 		}
 	}
 	
@@ -370,55 +365,55 @@
 	public void saveState(final IMemento memento) {
 		super.saveState(memento);
 		
-		memento.putString(M_SHOW_DESCRIPTION, (fShowDescription) ? "on" : "off"); //$NON-NLS-1$ //$NON-NLS-2$
-		memento.putString(M_SHOW_PROGRESS, (fShowProgress) ? "on" : "off"); //$NON-NLS-1$ //$NON-NLS-2$
+		memento.putString(M_SHOW_DESCRIPTION, (this.showDescription) ? "on" : "off"); //$NON-NLS-1$ //$NON-NLS-2$
+		memento.putString(M_SHOW_PROGRESS, (this.showProgress) ? "on" : "off"); //$NON-NLS-1$ //$NON-NLS-2$
 	}
 	
 	@Override
 	public void createPartControl(final Composite parent) {
 		updateContentDescription(null);
 		
-		fComposite = parent;
-		final GridLayout layout = new GridLayout();
-		layout.marginHeight = 0;
-		layout.marginWidth = 0;
-		layout.verticalSpacing = 0;
+		this.composite= parent;
+		final GridLayout layout= new GridLayout();
+		layout.marginHeight= 0;
+		layout.marginWidth= 0;
+		layout.verticalSpacing= 0;
 		parent.setLayout(layout);
 		
-		if (fShowProgress) {
+		if (this.showProgress) {
 			createProgressControl();
 		}
 		
-		fTableViewer = new TableViewer(parent, SWT.MULTI | SWT.V_SCROLL);
-		fTableViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
-		fTableViewer.getTable().setLinesVisible(false);
-		fTableViewer.getTable().setHeaderVisible(false);
-		new TableColumn(fTableViewer.getTable(), SWT.DEFAULT);
-		fTableViewer.getTable().addControlListener(new ControlAdapter() {
+		this.tableViewer= new TableViewer(parent, SWT.MULTI | SWT.V_SCROLL);
+		this.tableViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+		this.tableViewer.getTable().setLinesVisible(false);
+		this.tableViewer.getTable().setHeaderVisible(false);
+		new TableColumn(this.tableViewer.getTable(), SWT.DEFAULT);
+		this.tableViewer.getTable().addControlListener(new ControlAdapter() {
 			@Override
 			public void controlResized(final ControlEvent e) {
 				// adapt the column width to the width of the table
-				final Table table = fTableViewer.getTable();
-				final Rectangle area = table.getClientArea();
-				final TableColumn column = table.getColumn(0);
+				final Table table= QueueView.this.tableViewer.getTable();
+				final Rectangle area= table.getClientArea();
+				final TableColumn column= table.getColumn(0);
 				column.setWidth(area.width-3); // it looks better with a small gap
 			}
 		});
 		
-		fTableViewer.setContentProvider(new ViewContentProvider());
-		fTableViewer.setLabelProvider(new TableLabelProvider());
+		this.tableViewer.setContentProvider(new ViewContentProvider());
+		this.tableViewer.setLabelProvider(new TableLabelProvider());
 		
 		createActions();
 		contributeToActionBars();
 		hookDND();
 		
 		// listen on console changes
-		final IToolRegistry toolRegistry = NicoUI.getToolRegistry();
+		final IToolRegistry toolRegistry= NicoUI.getToolRegistry();
 		connect(toolRegistry.getActiveToolSession(getViewSite().getPage()).getProcess());
-		fToolRegistryListener = new IToolRegistryListener() {
+		this.toolRegistryListener= new IToolRegistryListener() {
 			@Override
 			public void toolSessionActivated(final ToolSessionUIData sessionData) {
-				final ToolProcess process = sessionData.getProcess();
+				final ToolProcess process= sessionData.getProcess();
 				UIAccess.getDisplay().syncExec(new Runnable() {
 					@Override
 					public void run() {
@@ -431,18 +426,18 @@
 				// handled by debug events
 			}
 		};
-		toolRegistry.addListener(fToolRegistryListener, getViewSite().getPage());
+		toolRegistry.addListener(this.toolRegistryListener, getViewSite().getPage());
 	}
 	
 	
 	private void createProgressControl() {
-		fProgressControl = new ToolProgressGroup(fComposite);
-		fProgressControl.getControl().setLayoutData(
+		this.progressControl= new ToolProgressGroup(this.composite);
+		this.progressControl.getControl().setLayoutData(
 				new GridData(SWT.FILL, SWT.FILL, true, false));
 	}
 	
 	protected void updateContentDescription(final ToolProcess process) {
-		if (fShowDescription) {
+		if (this.showDescription) {
 			setContentDescription(process != null ? process.getLabel(0) : " "); //$NON-NLS-1$
 		}
 		else {
@@ -451,40 +446,40 @@
 	}
 	
 	private void createActions() {
-		fShowDescriptionAction = new ShowDescriptionAction();
-		fShowProgressAction = new ShowProgressAction();
+		this.showDescriptionAction= new ShowDescriptionAction();
+		this.showProgressAction= new ShowProgressAction();
 		
-		fSelectAllAction = new Action() {
+		this.selectAllAction= new Action() {
 			@Override
 			public void run() {
-				fTableViewer.getTable().selectAll();
+				QueueView.this.tableViewer.getTable().selectAll();
 			}
 		};
-		fDeleteAction = new Action() {
+		this.deleteAction= new Action() {
 			@Override
 			public void run() {
-				final Queue queue = getQueue();
+				final Queue queue= getQueue();
 				if (queue != null) {
-					final IStructuredSelection selection = (IStructuredSelection) fTableViewer.getSelection();
-					queue.remove(toArray(selection));
+					final IStructuredSelection selection= (IStructuredSelection) QueueView.this.tableViewer.getSelection();
+					queue.remove(ImCollections.toList(selection.toList()));
 				}
 			}
 		};
 	}
 	
 	private void contributeToActionBars() {
-		final IActionBars bars = getViewSite().getActionBars();
+		final IActionBars bars= getViewSite().getActionBars();
 		
-		bars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), fSelectAllAction);
-		bars.setGlobalActionHandler(ActionFactory.DELETE.getId(), fDeleteAction);
+		bars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), this.selectAllAction);
+		bars.setGlobalActionHandler(ActionFactory.DELETE.getId(), this.deleteAction);
 		
 		fillLocalPullDown(bars.getMenuManager());
 		fillLocalToolBar(bars.getToolBarManager());
 	}
 	
 	private void fillLocalPullDown(final IMenuManager manager) {
-		manager.add(fShowDescriptionAction);
-		manager.add(fShowProgressAction);
+		manager.add(this.showDescriptionAction);
+		manager.add(this.showProgressAction);
 	}
 	
 	private void fillLocalToolBar(final IToolBarManager manager) {
@@ -496,29 +491,30 @@
 	}
 	
 	private void hookDND() {
-		fTableViewer.addDragSupport(DND.DROP_MOVE, 
-				new Transfer[] { LocalTaskTransfer.getTransfer() }, 
-				new TableDragSourceEffect(fTableViewer.getTable()) {
+		this.tableViewer.addDragSupport(DND.DROP_MOVE,
+				new Transfer[] { LocalTaskTransfer.getTransfer() },
+				new TableDragSourceEffect(this.tableViewer.getTable()) {
 			@Override
 			public void dragStart(final DragSourceEvent event) {
-				if (fTableViewer.getTable().getSelectionCount() > 0) {
-					event.doit = true;
+				if (QueueView.this.tableViewer.getTable().getSelectionCount() > 0) {
+					event.doit= true;
 				} else {
-					event.doit = false;
+					event.doit= false;
 				}
-				LocalTaskTransfer.getTransfer().init(fProcess);
+				LocalTaskTransfer.getTransfer().init(QueueView.this.process);
 				super.dragStart(event);
 			}
 			@Override
 			public void dragSetData(final DragSourceEvent event) {
 				super.dragSetData(event);
-				final LocalTaskTransfer.Data data = LocalTaskTransfer.getTransfer().createData();
-				if (data.process != fProcess) {
-					event.doit = false;
+				final LocalTaskTransfer.Data data= LocalTaskTransfer.getTransfer().createData();
+				if (data.process != QueueView.this.process) {
+					event.doit= false;
 					return;
 				}
-				data.runnables = toArray((IStructuredSelection) fTableViewer.getSelection());
-				event.data = data;
+				data.runnables= ImCollections.toList(
+						((IStructuredSelection) QueueView.this.tableViewer.getSelection()).toList() );
+				event.data= data;
 			}
 			@Override
 			public void dragFinished(final DragSourceEvent event) {
@@ -532,7 +528,7 @@
 		UIAccess.getDisplay().syncExec(new Runnable() {
 			@Override
 			public void run() {
-				if (fProcess != null && fProcess == process) {
+				if (QueueView.this.process != null && QueueView.this.process == process) {
 					connect(null);
 				}
 			}
@@ -541,18 +537,18 @@
 	
 	/** May only be called in UI thread */
 	public void connect(final ToolProcess process) {
-		final Runnable runnable = new Runnable() {
+		final Runnable runnable= new Runnable() {
 			@Override
 			public void run() {
-				if (!UIAccess.isOkToUse(fTableViewer)) {
+				if (!UIAccess.isOkToUse(QueueView.this.tableViewer)) {
 					return;
 				}
-				fProcess = process;
+				QueueView.this.process= process;
 				updateContentDescription(process);
-				if (fProgressControl != null) {
-					fProgressControl.setTool(process, true);
+				if (QueueView.this.progressControl != null) {
+					QueueView.this.progressControl.setTool(process, true);
 				}
-				fTableViewer.setInput(process);
+				QueueView.this.tableViewer.setInput(process);
 			}
 		};
 		BusyIndicator.showWhile(UIAccess.getDisplay(), runnable);
@@ -564,12 +560,12 @@
 	 * @return a tool process or <code>null</code>, if no process is connected.
 	 */
 	public ToolProcess getProcess() {
-		return fProcess;
+		return this.process;
 	}
 	
 	public Queue getQueue() {
-		if (fProcess != null) {
-			return fProcess.getQueue();
+		if (this.process != null) {
+			return this.process.getQueue();
 		}
 		return null;
 	}
@@ -578,7 +574,7 @@
 	@Override
 	public void setFocus() {
 		// Passing the focus request to the viewer's control.
-		fTableViewer.getControl().setFocus();
+		this.tableViewer.getControl().setFocus();
 	}
 	
 	@Override
diff --git a/r/org.eclipse.statet.r.apps/.classpath b/r/org.eclipse.statet.r.apps/.classpath
new file mode 100644
index 0000000..7728a88
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/.classpath
@@ -0,0 +1,15 @@
+<?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/JavaSE-1.8">
+		<attributes>
+			<attribute name="annotationpath" value="/org.eclipse.statet/eea/"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins">
+		<attributes>
+			<attribute name="annotationpath" value="/org.eclipse.statet/eea/"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/r/org.eclipse.statet.r.apps/.gitignore b/r/org.eclipse.statet.r.apps/.gitignore
new file mode 100644
index 0000000..5e56e04
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/.gitignore
@@ -0,0 +1 @@
+/bin
diff --git a/r/org.eclipse.statet.r.apps/.project b/r/org.eclipse.statet.r.apps/.project
new file mode 100644
index 0000000..90e3c6b
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.statet.r.apps</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.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.PluginNature</nature>
+	</natures>
+</projectDescription>
diff --git a/r/org.eclipse.statet.r.apps/.settings/org.eclipse.core.resources.prefs b/r/org.eclipse.statet.r.apps/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..99f26c0
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/r/org.eclipse.statet.r.apps/.settings/org.eclipse.core.runtime.prefs b/r/org.eclipse.statet.r.apps/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000..5a0ad22
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/r/org.eclipse.statet.r.apps/.settings/org.eclipse.jdt.core.prefs b/r/org.eclipse.statet.r.apps/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..ccb1eaa
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,325 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
+org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=85
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0
+org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=2
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=1
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=2
+org.eclipse.jdt.core.formatter.blank_lines_before_package=1
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=100
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=false
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=100
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=false
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=false
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
+org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
diff --git a/r/org.eclipse.statet.r.apps/.settings/org.eclipse.jdt.ui.prefs b/r/org.eclipse.statet.r.apps/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..e62a4f5
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,131 @@
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_missing_override_annotations_interface_methods=true
+cleanup.add_serial_version_id=false
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=true
+cleanup.always_use_this_for_non_static_field_access=true
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_functional_interfaces=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.correct_indentation=false
+cleanup.format_source_code=false
+cleanup.format_source_code_changes_only=false
+cleanup.insert_inferred_type_arguments=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=true
+cleanup.make_private_fields_final=true
+cleanup.make_type_abstract_if_missing_method=false
+cleanup.make_variable_declarations_final=true
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=false
+cleanup.organize_imports=true
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=false
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_redundant_type_arguments=true
+cleanup.remove_trailing_whitespaces=false
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.use_anonymous_class_creation=false
+cleanup.use_blocks=true
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_lambda=true
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_this_for_non_static_field_access=true
+cleanup.use_this_for_non_static_field_access_only_if_necessary=false
+cleanup.use_this_for_non_static_method_access=true
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup.use_type_arguments=false
+cleanup_profile=_StatET
+cleanup_settings_version=2
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_StatET
+formatter_settings_version=12
+org.eclipse.jdt.ui.exception.name=e
+org.eclipse.jdt.ui.gettersetter.use.is=true
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=\#;java;javax;com.ibm.icu;org.osgi;org.eclipse;;org.eclipse.statet.jcommons;org.eclipse.statet.ecommons;org.eclipse.statet;
+org.eclipse.jdt.ui.javadoc=true
+org.eclipse.jdt.ui.keywordthis=true
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.overrideannotation=true
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\n * @return the ${bare_field_name}\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\n * @param ${param} the ${bare_field_name} to set\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\n * ${tags}\n */</template><template autoinsert\="false" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/*\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\#\n \# Copyright (c) ${year} ${user} and others.\n # \n # This program and the accompanying materials are made available under the\n # terms of the Eclipse Public License 2.0 which is available at\n # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0\n # which is available at https://www.apache.org/licenses/LICENSE-2.0.\n # \n # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0\n # \n \# Contributors\:\n \#     ${user} - initial API and implementation\n \#\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=\=*/</template><template autoinsert\="false" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\n * \n * ${tags}\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\n * \n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\n * ${tags}\n */</template><template autoinsert\="false" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment"/><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\n * ${tags}\n * ${see_to_target}\n */</template><template autoinsert\="false" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\n\n${package_declaration}\n\n${typecomment}\n${type_declaration}</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\n</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template></templates>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=true
+sp_cleanup.always_use_this_for_non_static_field_access=true
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_functional_interfaces=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.insert_inferred_type_arguments=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=false
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=true
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_redundant_type_arguments=true
+sp_cleanup.remove_trailing_whitespaces=false
+sp_cleanup.remove_trailing_whitespaces_all=false
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=true
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=false
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=false
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_anonymous_class_creation=false
+sp_cleanup.use_blocks=true
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_lambda=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=true
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=false
+sp_cleanup.use_this_for_non_static_method_access=true
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+sp_cleanup.use_type_arguments=false
diff --git a/r/org.eclipse.statet.r.apps/META-INF/MANIFEST.MF b/r/org.eclipse.statet.r.apps/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..2edec13
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/META-INF/MANIFEST.MF
@@ -0,0 +1,26 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: org.eclipse.statet.r.apps;singleton:=true
+Bundle-Version: 3.6.0.qualifier
+Bundle-Vendor: Eclipse.org
+Bundle-Name: StatET R - Add-on for R Apps
+Bundle-ActivationPolicy: lazy
+Bundle-Activator: org.eclipse.statet.internal.r.apps.ui.RAppUIPlugin
+Bundle-Localization: plugin
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Require-Bundle: org.eclipse.statet.ide.ui;bundle-version="[4.0.0,4.1.0)",
+ org.eclipse.core.variables,
+ org.eclipse.ui.browser,
+ org.eclipse.statet.ecommons.debug.ui,
+ org.eclipse.statet.ltk.ui,
+ org.eclipse.statet.r.ui,
+ org.eclipse.statet.ecommons.coremisc,
+ org.eclipse.statet.ecommons.debug.core,
+ org.eclipse.statet.r.console.core,
+ org.eclipse.statet.nico.core,
+ org.eclipse.statet.nico.ui
+Import-Package: com.jcraft.jsch,
+ org.eclipse.statet.ecommons.databinding.core.validation;version="4.0.0",
+ org.eclipse.statet.jcommons.collections;version="4.0.0",
+ org.eclipse.statet.jcommons.lang;version="4.0.0"
+Export-Package: org.eclipse.statet.r.apps.ui
diff --git a/r/org.eclipse.statet.r.apps/about.html b/r/org.eclipse.statet.r.apps/about.html
new file mode 100644
index 0000000..e71ae66
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/about.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+		"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>December 1, 2017</p>
+<h3>License</h3>
+
+<p>
+	The Eclipse Foundation makes available all content in this plug-in
+	(&quot;Content&quot;). Unless otherwise indicated below, the Content
+	is provided to you under the terms and conditions of the Eclipse
+	Public License Version 2.0 (&quot;EPL&quot;), or the Apache License,
+	Version 2.0 (AL). A copy of the EPL is available at <a href="https://www.eclipse.org/legal/epl-2.0">https://www.eclipse.org/legal/epl-2.0</a>.
+	For purposes of the EPL, &quot;Program&quot; will mean the Content.
+	A copy of the AL is available at <a href="https://www.apache.org/licenses/LICENSE-2.0">https://www.apache.org/licenses/LICENSE-2.0</a>.
+</p>
+
+<p>
+	If you did not receive this Content directly from the Eclipse
+	Foundation, the Content is being redistributed by another party
+	(&quot;Redistributor&quot;) and different terms and conditions may
+	apply to your use of any object code in the Content. Check the
+	Redistributor's license that was provided with the Content. If no such
+	license exists, contact the Redistributor. Unless otherwise indicated
+	below, the terms and conditions of the EPL or AL still apply to any
+	source code in the Content and such source code may be obtained at <a
+	href="https://www.eclipse.org/">https://www.eclipse.org</a>.
+</p>
+
+</body>
+</html>
diff --git a/r/org.eclipse.statet.r.apps/build.properties b/r/org.eclipse.statet.r.apps/build.properties
new file mode 100644
index 0000000..976d098
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/build.properties
@@ -0,0 +1,11 @@
+source..= src/
+output..= bin/
+javacDefaultEncoding..= UTF-8
+
+bin.includes= META-INF/,\
+              plugin.xml,\
+              plugin.properties,\
+              .,\
+              icons/,\
+              about.html
+src.includes= .settings/org.eclipse.core.resources.prefs
diff --git a/r/org.eclipse.statet.r.apps/icons/obj_16/r_app.png b/r/org.eclipse.statet.r.apps/icons/obj_16/r_app.png
new file mode 100644
index 0000000..a40d16b
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/icons/obj_16/r_app.png
Binary files differ
diff --git a/r/org.eclipse.statet.r.apps/icons/tool_16/run-r_app.png b/r/org.eclipse.statet.r.apps/icons/tool_16/run-r_app.png
new file mode 100644
index 0000000..d607896
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/icons/tool_16/run-r_app.png
Binary files differ
diff --git a/r/org.eclipse.statet.r.apps/icons/tool_16/view-app.png b/r/org.eclipse.statet.r.apps/icons/tool_16/view-app.png
new file mode 100644
index 0000000..b7dcb79
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/icons/tool_16/view-app.png
Binary files differ
diff --git a/r/org.eclipse.statet.r.apps/icons/view_16/r_app_var_browser.png b/r/org.eclipse.statet.r.apps/icons/view_16/r_app_var_browser.png
new file mode 100644
index 0000000..270cc7a
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/icons/view_16/r_app_var_browser.png
Binary files differ
diff --git a/r/org.eclipse.statet.r.apps/plugin.properties b/r/org.eclipse.statet.r.apps/plugin.properties
new file mode 100644
index 0000000..1b598fd
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/plugin.properties
@@ -0,0 +1,23 @@
+ #=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================#
+
+launchConfigurations_RApp_name= R App
+
+launchShortcuts_RShinyApp_name= R Shiny App
+commands_RunApp_name= Run R App
+commands_RunApp_description= Runs the R app of the active editor (active configuration)
+toolbars_AppTools_label= R App Tools\u2002[StatET]
+
+views_AppViewer_name= R App Viewer
+views_AppVarBrowser_name= R App Variables
diff --git a/r/org.eclipse.statet.r.apps/plugin.xml b/r/org.eclipse.statet.r.apps/plugin.xml
new file mode 100644
index 0000000..0238068
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/plugin.xml
@@ -0,0 +1,246 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<!--
+ #=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================#
+-->
+
+<plugin>
+   
+   <extension
+         point="org.eclipse.core.resources.natures"
+         id="org.eclipse.statet.r.apps.resourceProjects.RApp"
+         name="R App&#x2002;[StatET]">
+      <requires-nature
+            id="org.eclipse.statet.r.resourceProjects.R"/>
+      <runtime>
+         <run
+               class="org.eclipse.statet.intern.r.apps.core.RAppProject"/>
+      </runtime>
+   </extension>
+   
+   <extension
+         point="org.eclipse.core.expressions.propertyTesters">
+      <propertyTester
+            id="org.eclipse.statet.r.apps.expressionProperties.RShinyResources"
+            namespace="org.eclipse.statet.r.apps"
+            properties="isRShinyAppResource"
+            type="org.eclipse.core.resources.IResource"
+            class="org.eclipse.statet.internal.r.apps.core.shiny.RShinyResourceTester">
+      </propertyTester>
+   </extension>
+   <extension
+         point="org.eclipse.core.expressions.definitions">
+      <definition
+            id="org.eclipse.statet.r.apps.expressions.isEditorInputActive.RShinyResource">
+         <with
+               variable="activeEditorInput">
+            <adapt
+                  type="org.eclipse.core.resources.IResource">
+               <test
+                     property="org.eclipse.core.resources.projectNature"
+                     value="org.eclipse.statet.r.resourceProjects.R"/>
+               <or>
+                  <test
+                        property="org.eclipse.core.resources.projectNature"
+                        value="org.eclipse.statet.r.apps.resourceProjects.RApp"/>
+                  <test
+                        property="org.eclipse.statet.r.apps.isRShinyAppResource"
+                        forcePluginActivation="true"/>
+               </or>
+            </adapt>
+         </with>
+      </definition>
+      <definition
+            id="org.eclipse.statet.r.apps.expressions.isSelectionActive.RShinyResource">
+         <with
+               variable="selection">
+            <count
+                  value="1"/>
+            <iterate>
+               <adapt
+                     type="org.eclipse.core.resources.IResource">
+                  <test
+                        property="org.eclipse.core.resources.projectNature"
+                        value="org.eclipse.statet.r.resourceProjects.R"/>
+                  <or>
+                     <test
+                           property="org.eclipse.core.resources.projectNature"
+                           value="org.eclipse.statet.r.apps.resourceProjects.RApp"/>
+                     <test
+                           property="org.eclipse.statet.r.apps.isRShinyAppResource"
+                           forcePluginActivation="true"/>
+                  </or>
+               </adapt>
+            </iterate>
+         </with>
+      </definition>
+   </extension>
+   
+   <extension
+         point="org.eclipse.statet.ecommons.ts.UIDecorators">
+      <runnable
+            typeId="org.eclipse.statet.r.apps/RunApp"
+            icon="icons/obj_16/r_app.png">
+      </runnable>
+      
+   </extension>
+   
+   <extension
+         point="org.eclipse.debug.core.launchConfigurationTypes">
+      <launchConfigurationType
+            id="org.eclipse.statet.r.apps.launchConfigurations.RAppControl"
+            name="%launchConfigurations_RApp_name"
+            modes="run"
+            delegate="org.eclipse.statet.internal.r.apps.ui.launching.AppControlLaunchDelegate">
+      </launchConfigurationType>
+   </extension>
+   
+   <extension
+         point="org.eclipse.debug.ui.launchConfigurationTypeImages">
+      <launchConfigurationTypeImage
+            id="org.eclipse.statet.r.images.RApp"
+            configTypeID="org.eclipse.statet.r.apps.launchConfigurations.RAppControl"
+            icon="icons/obj_16/r_app.png">
+      </launchConfigurationTypeImage>
+   </extension>
+   <extension
+         point="org.eclipse.debug.ui.launchConfigurationTabGroups">
+      <launchConfigurationTabGroup
+            id="org.eclipse.statet.r.apps.launchConfigurationTabGroups.RAppControl"
+            type="org.eclipse.statet.r.apps.launchConfigurations.RAppControl"
+            class="org.eclipse.statet.internal.r.apps.ui.launching.AppControlConfigTabGroup">
+      </launchConfigurationTabGroup>
+   </extension>
+   
+   <extension
+         point="org.eclipse.debug.ui.launchShortcuts">
+      <shortcut
+            id="org.eclipse.statet.r.launchShortcuts.RShinyApp"
+            label="%launchShortcuts_RShinyApp_name"
+            icon="icons/tool_16/run-r_app.png"
+            class="org.eclipse.statet.internal.r.apps.ui.shiny.actions.AppLaunchShortcut"
+            modes="run">
+         <configurationType
+               id="org.eclipse.statet.r.apps.launchConfigurations.RAppControl"/>
+         <contextualLaunch>
+            <enablement>
+               <reference
+                     definitionId="org.eclipse.statet.r.apps.expressions.isSelectionActive.RShinyResource"/>
+            </enablement>
+         </contextualLaunch>
+      </shortcut>
+   </extension>
+   
+   <extension
+         point="org.eclipse.ui.commands">
+      <command
+            id="org.eclipse.statet.r.apps.commands.RunAppDefault"
+            categoryId="org.eclipse.debug.ui.category.run"
+            name="%commands_RunApp_name"
+            description="%commands_RunApp_description">
+      </command>
+   </extension>
+   <extension
+         point="org.eclipse.ui.commandImages">
+      <image
+            commandId="org.eclipse.statet.r.apps.commands.RunAppDefault"
+            icon="icons/tool_16/run-r_app.png">
+      </image>
+   </extension>
+   <extension
+         point="org.eclipse.ui.handlers">
+      <handler
+            commandId="org.eclipse.statet.r.apps.commands.RunAppDefault"
+            class="org.eclipse.statet.internal.r.apps.ui.shiny.actions.RunActiveAppConfigWorkbenchHandler">
+         <activeWhen>
+            <reference
+                  definitionId="org.eclipse.statet.r.apps.expressions.isEditorInputActive.RShinyResource"/>
+         </activeWhen>
+      </handler>
+   </extension>
+   
+   <extension
+         point="org.eclipse.ui.bindings">
+      <key
+            commandId="org.eclipse.statet.r.apps.commands.RunAppDefault"
+            contextId="org.eclipse.ui.contexts.window"
+            schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+            sequence="M1+R A">
+      </key>
+   </extension>
+   
+   <extension
+         point="org.eclipse.ui.menus">
+      <menuContribution
+            locationURI="toolbar:org.eclipse.ui.main.toolbar?before=org.eclipse.ui.workbench.navigate">
+         <toolbar
+               id="org.eclipse.statet.r.apps.menus.AppTools"
+               label="%toolbars_AppTools_label">
+            <separator
+                  name="run_app"
+                  visible="false">
+            </separator>
+            <command
+                  id="org.eclipse.statet.r.apps.menus.RunApp"
+                  commandId="org.eclipse.statet.r.apps.commands.RunAppDefault"
+                  style="pulldown">
+               <visibleWhen>
+                  <reference
+                        definitionId="org.eclipse.statet.r.apps.expressions.isEditorInputActive.RShinyResource"/>
+               </visibleWhen>
+            </command>
+            <separator
+                  name="additions"
+                  visible="false">
+            </separator>
+         </toolbar>
+      </menuContribution>
+      <menuContribution
+            locationURI="menu:org.eclipse.statet.r.apps.menus.RunApp">
+         <dynamic
+               id="org.eclipse.statet.r.apps.menus.RunAppItems"
+               class="org.eclipse.statet.internal.r.apps.ui.shiny.actions.RunAppConfigsDropdownContribution">
+         </dynamic>
+         <separator
+               name="additions"
+               visible="true">
+         </separator>
+      </menuContribution>
+   </extension>
+   
+   <extension
+         point="org.eclipse.ui.views">
+      <view
+            id="org.eclipse.statet.r.apps.views.AppViewer"
+            category="org.eclipse.statet.workbench.views.StatetCategory"
+            name="%views_AppViewer_name"
+            icon="icons/obj_16/r_app.png"
+            class="org.eclipse.statet.internal.r.apps.ui.viewer.AppBrowserView"
+            restorable="true">
+      </view>
+   </extension>
+   <extension
+         point="org.eclipse.ui.views">
+      <view
+            id="org.eclipse.statet.r.apps.views.VariableViewer"
+            category="org.eclipse.statet.workbench.views.StatetCategory"
+            name="%views_AppVarBrowser_name"
+            icon="icons/view_16/r_app_var_browser.png"
+            class="org.eclipse.statet.internal.r.apps.ui.variables.AppVarView"
+            restorable="true">
+      </view>
+   </extension>
+   
+</plugin>
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/intern/r/apps/core/RAppProject.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/intern/r/apps/core/RAppProject.java
new file mode 100644
index 0000000..22cac35
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/intern/r/apps/core/RAppProject.java
@@ -0,0 +1,55 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.intern.r.apps.core;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectNature;
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+
+@NonNullByDefault
+public class RAppProject implements IProjectNature {
+	
+	
+	private IProject project;
+	
+	
+	/** plugin.xml */
+	@SuppressWarnings("null")
+	public RAppProject() {
+	}
+	
+	
+	@Override
+	public void setProject(final IProject project) {
+		this.project= project;
+	}
+	
+	@Override
+	public final IProject getProject() {
+		return this.project;
+	}
+	
+	@Override
+	public void configure() throws CoreException {
+	}
+	
+	@Override
+	public void deconfigure() throws CoreException {
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/core/shiny/RShinyResourceTester.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/core/shiny/RShinyResourceTester.java
new file mode 100644
index 0000000..8591350
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/core/shiny/RShinyResourceTester.java
@@ -0,0 +1,121 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.core.shiny;
+
+import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+
+@NonNullByDefault
+public class RShinyResourceTester extends PropertyTester {
+	
+	
+	private static final IPath APP_FILE_PATH= new Path("app.R"); //$NON-NLS-1$
+	
+	private static final IPath UI_FILE_PATH= new Path("ui.R"); //$NON-NLS-1$
+	private static final IPath SERVER_FILE_PATH= new Path("server.R"); //$NON-NLS-1$
+	
+	
+	@SuppressWarnings("null")
+	private static final IContainer getContainer(final IResource resource) {
+		return (resource instanceof IContainer) ?
+				(IContainer) resource :
+				resource.getParent();
+	}
+	
+	public static final boolean isAppContainer(final IContainer container) {
+		return (((container.getFile(SERVER_FILE_PATH).exists()
+						&& container.getFile(UI_FILE_PATH).exists() )
+				|| (container.getFile(APP_FILE_PATH).exists()) ));
+	}
+	
+	public static final @Nullable IContainer getAppContainer(IContainer container) {
+		while (true) {
+			if (isAppContainer(container)) {
+				return container;
+			}
+			if (container.getType() == IResource.FOLDER) {
+				container= container.getParent();
+				continue;
+			}
+			return null;
+		}
+	}
+	
+//	public static final IContainer getContainingShinyContainer(IContainer container) {
+//		try {
+//			container.accept(new IResourceVisitor() {
+//				IContainer appContainer;
+//				@Override
+//				public boolean visit(final IResource resource) throws CoreException {
+//					if (resource.getType() == IResource.FOLDER
+//							|| resource.getType() == IResource.PROJECT) {
+//						if (isShinyContainer((IContainer) resource)) {
+//							if (this.appContainer != null) {
+//								
+//							}
+//							this.appContainer= (IContainer) resource;
+//							return false;
+//						}
+//						return true;
+//					}
+//					return false;
+//				}
+//			});
+//		}
+//		catch (final CoreException e) {
+//			return null;
+//		}
+//	}
+	
+	public static final boolean isInsideAppContainer(final IResource resource) {
+		return (getAppContainer(getContainer(resource)) != null);
+	}
+	
+	public static final @Nullable IContainer getAppContainer(final IResource resource) {
+		return getAppContainer(getContainer(resource));
+	}
+	
+	
+	/** plugin.xml */
+	public RShinyResourceTester() {
+	}
+	
+	
+	@Override
+	public boolean test(final Object receiver,
+			final String property, final Object[] args, final @Nullable Object expectedValue) {
+		if (receiver instanceof IResource) {
+			final IResource resource= (IResource) receiver;
+			switch (property) {
+			case "isAppResource": //$NON-NLS-1$
+//				System.out.println("isAppResource" + resource.getFullPath() + "= " + isInsideAppContainer(resource));
+				return (isInsideAppContainer(resource));
+//			case "containsApp": //$NON-NLS-1$
+			default:
+				break;
+			}
+		}
+		return false;
+	}
+	
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/Messages.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/Messages.java
new file mode 100644
index 0000000..d3ad6ca
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/Messages.java
@@ -0,0 +1,71 @@
+/*=============================================================================#
+ # Copyright (c) 2007, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui;
+
+import org.eclipse.osgi.util.NLS;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+
+@NonNullByDefault
+@SuppressWarnings("null")
+public class Messages extends NLS {
+	
+	
+	public static String Variable_AppHost_description;
+	public static String Variable_AppPort_description;
+	
+	public static String RunAction_RunApp_label;
+	
+	public static String Operation_MainTab_name;
+	
+	public static String Operation_MainTab_AppFolder_label;
+	public static String Operation_AppFolder_info;
+	
+	public static String Operation_MainTab_AppAddress_group;
+	public static String Operation_MainTab_AppHost_label;
+	public static String Operation_AppHost_info;
+	public static String Operation_MainTab_AppPort_label;
+	public static String Operation_AppPort_error_SpecInvalid_message;
+	public static String Operation_AppPort_info;
+	
+	public static String Operation_MainTab_Start_group;
+	public static String Operation_StartApp_RCode_label;
+	public static String Operation_StartApp_RCode_error_SpecMissing_message;
+	public static String Operation_StartApp_RCode_error_SpecInvalid_message;
+	public static String Operation_StopApp_RCode_label;
+	public static String Operation_StartApp_StopRunningApp_label;
+	
+	public static String Operation_ViewTab_name;
+	public static String Operation_ViewTab_label;
+	public static String Operation_ViewTab_Operation_label;
+	public static String Operation_Viewer_None_label;
+	public static String Operation_Viewer_WorkbenchExternal_label;
+	public static String Operation_Viewer_WorkbenchView_label;
+	public static String Operation_Viewer_error_Run_message;
+	public static String Operation_Variables_label;
+	public static String Operation_Variables_ShowView_label;
+	public static String Operation_Variables_error_Run_message;
+	
+	public static String Action_RestartApp_label;
+	public static String Action_StopApp_label;
+	
+	
+	static {
+		NLS.initializeMessages(Messages.class.getName(), Messages.class);
+	}
+	private Messages() {}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/Messages.properties b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/Messages.properties
new file mode 100644
index 0000000..2be577a
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/Messages.properties
@@ -0,0 +1,51 @@
+ #=============================================================================#
+ # Copyright (c) 2007, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================#
+
+Variable_AppHost_description= Returns the specific host of the app address.
+Variable_AppPort_description= Returns the specific port of the app address.
+
+RunAction_RunApp_label= &Run App
+
+Operation_MainTab_name= &Main
+
+Operation_MainTab_AppFolder_label= App &folder
+Operation_AppFolder_info= (empty= selected resource)
+
+Operation_MainTab_AppAddress_group= Network Address:
+Operation_MainTab_AppHost_label= &Host
+Operation_AppHost_info= (empty= automatic)
+Operation_MainTab_AppPort_label= &Port
+Operation_AppPort_error_SpecInvalid_message= Invalid port number specified (0-65535).
+Operation_AppPort_info= (empty= automatic)
+
+Operation_MainTab_Start_group= App Start:
+Operation_StartApp_RCode_label= R code to r&un the app
+Operation_StartApp_RCode_error_SpecMissing_message= The spec of R code snippet is missing.
+Operation_StartApp_RCode_error_SpecInvalid_message= In spec of R code snippet, {0}
+Operation_StartApp_StopRunningApp_label= S&top app already running in R
+Operation_StopApp_RCode_label= R code to st&op the app
+
+Operation_ViewTab_name= &View
+Operation_ViewTab_label= Show app when started
+Operation_ViewTab_Operation_label= View &Operation
+Operation_Viewer_None_label= 
+Operation_Viewer_WorkbenchExternal_label= Open app in external web browser
+Operation_Viewer_WorkbenchView_label= Open app in Eclipse view
+Operation_Viewer_error_Run_message= An error occurred when opening viewer to show the app.
+Operation_Variables_label= R App Variables (Reactive Variables)
+Operation_Variables_ShowView_label= &Show view
+Operation_Variables_error_Run_message= An error occurred when opening the R App Variables view.
+
+Action_RestartApp_label= Restart App
+Action_StopApp_label= Stop App
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/RAppUIPlugin.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/RAppUIPlugin.java
new file mode 100644
index 0000000..7d5dd46
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/RAppUIPlugin.java
@@ -0,0 +1,131 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.osgi.framework.BundleContext;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+import org.eclipse.statet.jcommons.lang.Disposable;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.ui.util.ImageRegistryUtil;
+
+import org.eclipse.statet.internal.r.apps.ui.launching.AppControlManager;
+import org.eclipse.statet.internal.r.apps.ui.launching.AppType;
+import org.eclipse.statet.r.apps.ui.RAppUIResources;
+
+
+@NonNullByDefault
+public class RAppUIPlugin extends AbstractUIPlugin {
+	
+	
+	public static final String BUNDLE_ID= "org.eclipse.statet.r.apps"; //$NON-NLS-1$
+	
+	
+	private static RAppUIPlugin instance;
+	
+	/**
+	 * Returns the shared plug-in instance
+	 *
+	 * @return the shared instance
+	 */
+	public static RAppUIPlugin getInstance() {
+		return RAppUIPlugin.instance;
+	}
+	
+	
+	public static void log(final IStatus status) {
+		final Plugin plugin= getInstance();
+		if (plugin != null) {
+			plugin.getLog().log(status);
+		}
+	}
+	
+	
+	private boolean started;
+	
+	private List<Disposable> disposables;
+	
+	private @Nullable AppControlManager launchConfigManager;
+	
+	
+	@SuppressWarnings("null")
+	public RAppUIPlugin() {
+	}
+	
+	@Override
+	public void start(final BundleContext context) throws Exception {
+		super.start(context);
+		instance= this;
+		
+		this.disposables= new ArrayList<>();
+		
+		this.started= true;
+	}
+	
+	@Override
+	public void stop(final BundleContext context) throws Exception {
+		try {
+			synchronized (this) {
+				this.started= false;
+			}
+			
+			for (final Disposable listener : this.disposables) {
+				try {
+					listener.dispose();
+				}
+				catch (final Throwable e) {
+					getLog().log(new Status(IStatus.ERROR, BUNDLE_ID, 0,
+							"Error occured while dispose module", e ));
+				}
+			}
+			this.disposables= null;
+		}
+		finally {
+			instance= null;
+			super.stop(context);
+		}
+	}
+	
+	
+	@Override
+	protected void initializeImageRegistry(final ImageRegistry reg) {
+		if (!this.started) {
+			throw new IllegalStateException("Plug-in is not started.");
+		}
+		final ImageRegistryUtil util= new ImageRegistryUtil(this);
+		
+		util.register(RAppUIResources.TOOL_VIEW_IMAGE_ID, ImageRegistryUtil.T_TOOL, "view-app.png"); //$NON-NLS-1$
+	}
+	
+	
+	public synchronized AppControlManager getRunAppConfigManager(final AppType appType) {
+		if (this.launchConfigManager == null) {
+			this.launchConfigManager= new AppControlManager(appType);
+			this.disposables.add(this.launchConfigManager);
+		}
+		return this.launchConfigManager;
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlConfigMainTab.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlConfigMainTab.java
new file mode 100644
index 0000000..66475b1
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlConfigMainTab.java
@@ -0,0 +1,403 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.launching;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.UpdateValueStrategy;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.variables.IStringVariable;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import org.eclipse.statet.ecommons.databinding.IntegerValidator;
+import org.eclipse.statet.ecommons.databinding.core.validation.UpdateableErrorValidator;
+import org.eclipse.statet.ecommons.debug.ui.config.LaunchConfigPresets;
+import org.eclipse.statet.ecommons.debug.ui.config.LaunchConfigTabWithPresets;
+import org.eclipse.statet.ecommons.templates.TemplateVariableProcessor;
+import org.eclipse.statet.ecommons.ui.SharedMessages;
+import org.eclipse.statet.ecommons.ui.SharedUIResources;
+import org.eclipse.statet.ecommons.ui.components.CustomizableVariableSelectionDialog;
+import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
+import org.eclipse.statet.ecommons.ui.util.VariableFilterUtils;
+import org.eclipse.statet.ecommons.variables.core.VariableText2;
+import org.eclipse.statet.ecommons.variables.core.VariableTextValidator;
+import org.eclipse.statet.ecommons.variables.core.VariableUtils;
+
+import org.eclipse.statet.internal.r.apps.ui.Messages;
+import org.eclipse.statet.ltk.ui.sourceediting.SnippetEditor;
+import org.eclipse.statet.ltk.ui.sourceediting.SnippetEditor1;
+import org.eclipse.statet.r.core.RCore;
+import org.eclipse.statet.r.launching.ui.RLaunchingUI;
+import org.eclipse.statet.r.ui.sourceediting.RSourceViewerConfigurator;
+import org.eclipse.statet.r.ui.sourceediting.RTemplateSourceViewerConfigurator;
+
+
+public class AppControlConfigMainTab extends LaunchConfigTabWithPresets {
+	
+	
+	static void initDefaults(final ILaunchConfigurationWorkingCopy config) {
+		config.setAttribute(AppControlConfigs.START_STOP_BLOCKING_TASKS_MODE_ATTR_NAME, 1);
+	}
+	
+	
+	private final IObservableValue<String> appPathValue;
+	
+	private final IObservableValue<String> appHostValue;
+	private final IObservableValue<Integer> appPortValue;
+	
+	private final IObservableValue<Boolean> startStopRunningValue;
+	private final IObservableValue<String> startRSnippetValue;
+	private final IObservableValue<String> stopRSnippetValue;
+	
+//	private ResourceInputComposite appPathControl;
+	
+	private Text sHostControl;
+	private Text sPortControl;
+	
+	private Button startStopRunningControl;
+	private VariableText2 rSnippetVariableResolver;
+	private SnippetEditor startRSnippetEditor;
+	private SnippetEditor stopRSnippetEditor;
+	
+	
+	public AppControlConfigMainTab(final LaunchConfigPresets presets) {
+		
+		final Realm realm= getRealm();
+		this.appHostValue= new WritableValue<>(realm, null, String.class);
+		this.appPortValue= new WritableValue<>(realm, null, Integer.class);
+		this.appPathValue= new WritableValue<>(realm, null, String.class);
+		this.startRSnippetValue= new WritableValue<>(realm, null, String.class);
+		this.startStopRunningValue= new WritableValue<>(realm, null, Boolean.class);
+		this.stopRSnippetValue= new WritableValue<>(realm, null, String.class);
+		
+		setPresets(presets);
+	}
+	
+	
+	@Override
+	public Image getImage() {
+		return SharedUIResources.getImages().get(SharedUIResources.OBJ_MAIN_TAB_ID);
+	}
+	
+	@Override
+	public String getName() {
+		return Messages.Operation_MainTab_name;
+	}
+	
+	
+	@Override
+	public void createControl(final Composite parent) {
+		final Composite mainComposite= new Composite(parent, SWT.NONE);
+		setControl(mainComposite);
+		mainComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+		mainComposite.setLayout(LayoutUtils.newTabGrid(1));
+		
+		addPresetsButton(mainComposite);
+		
+		{	final Composite composite= createAddressSettings(mainComposite);
+			composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+		}
+		{	final Composite composite= createSnippetSettings(mainComposite);
+			composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+		}
+		Dialog.applyDialogFont(parent);
+		
+		initBindings();
+	}
+	
+	private Composite createAddressSettings(final Composite parent) {
+		final Group composite= new Group(parent, SWT.NONE);
+		composite.setText(Messages.Operation_MainTab_AppAddress_group);
+		composite.setLayout(LayoutUtils.newGroupGrid(4));
+		
+		{	// Address
+			final Label label= new Label(composite, SWT.LEFT);
+			label.setText(Messages.Operation_MainTab_AppHost_label + ':');
+			label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
+			
+			final Text text= new Text(composite, SWT.BORDER | SWT.SINGLE | SWT.LEFT);
+			text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+			this.sHostControl= text;
+			
+			final Label info= new Label(composite, SWT.LEFT);
+			info.setText(Messages.Operation_AppHost_info);
+			info.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
+		}
+		{	// Port
+			final Label label= new Label(composite, SWT.LEFT);
+			label.setText(Messages.Operation_MainTab_AppPort_label + ':');
+			label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
+			
+			final Text number= new Text(composite, SWT.BORDER | SWT.SINGLE | SWT.LEFT);
+			final GridData gd= new GridData(SWT.LEFT, SWT.CENTER, false, false);
+			gd.widthHint= LayoutUtils.hintWidth(number, 5);
+			number.setLayoutData(gd);
+			this.sPortControl= number;
+			
+			final Label info= new Label(composite, SWT.LEFT);
+			info.setText(Messages.Operation_AppPort_info);
+			info.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1));
+		}
+		
+		return composite;
+	}
+	
+	private Map<String, IStringVariable> createSnippetVariables() {
+		final Map<String, IStringVariable> variables= new HashMap<>();
+		VariableUtils.add(variables, AppControlConfigs.APP_HOST_VAR);
+		VariableUtils.add(variables, AppControlConfigs.APP_PORT_VAR);
+		return variables;
+	}
+	
+	private Composite createSnippetSettings(final Composite parent) {
+		final Group composite= new Group(parent, SWT.NONE);
+		composite.setText(Messages.Operation_MainTab_Start_group);
+		composite.setLayout(LayoutUtils.newGroupGrid(1));
+		
+//		{	final Label label= new Label(composite, SWT.NONE);
+//			label.setText(Messages.Operation_MainTab_AppFolder_label + ' ' +
+//					Messages.Operation_AppFolder_info + ':' );
+//			label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+//			
+//			final ResourceInputComposite input= new ResourceInputComposite(composite,
+//					ResourceInputComposite.STYLE_TEXT,
+//					ResourceInputComposite.MODE_DIRECTORY | ResourceInputComposite.MODE_OPEN
+//							| ResourceInputComposite.MODE_WS_ONLY,
+//					Messages.Operation_MainTab_AppFolder_label );
+//			input.getValidator().setOnEmpty(IStatus.OK);
+//			input.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+//			this.appPathControl= input;
+//		}
+		
+		this.rSnippetVariableResolver= new VariableText2(createSnippetVariables());
+		
+		{	final Label label= new Label(composite, SWT.NONE);
+			label.setText(Messages.Operation_StartApp_RCode_label + ':');
+			label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+			
+			final TemplateVariableProcessor templateVariableProcessor= new TemplateVariableProcessor();
+			final RSourceViewerConfigurator configurator= new RTemplateSourceViewerConfigurator(
+					RCore.getWorkbenchAccess(),
+					templateVariableProcessor );
+			final SnippetEditor1 editor= new SnippetEditor1(configurator, null,
+					PlatformUI.getWorkbench(), RLaunchingUI.LAUNCH_CONFIG_QUALIFIER, true ) {
+				@Override
+				protected void fillToolMenu(final Menu menu) {
+					{	final MenuItem item= new MenuItem(menu, SWT.PUSH);
+						item.setText(SharedMessages.InsertVariable_label);
+						item.addSelectionListener(new SelectionAdapter() {
+							@Override
+							public void widgetSelected(final SelectionEvent e) {
+								final CustomizableVariableSelectionDialog dialog= new CustomizableVariableSelectionDialog(getTextControl().getShell());
+								dialog.addVariableFilter(VariableFilterUtils.EXCLUDE_JAVA_FILTER);
+								dialog.setAdditionals(AppControlConfigMainTab
+										.this.rSnippetVariableResolver.getExtraVariables().values() );
+								if (dialog.open() != Dialog.OK) {
+									return;
+								}
+								final String variable= dialog.getVariableExpression();
+								if (variable == null) {
+									return;
+								}
+								getTextControl().insert(variable);
+								getTextControl().setFocus();
+							}
+						});
+					}
+				}
+			};
+			editor.create(composite, SnippetEditor.DEFAULT_MULTI_LINE_STYLE);
+			final GridData gd= new GridData(SWT.FILL, SWT.FILL, true, true);
+			gd.heightHint= LayoutUtils.hintHeight(editor.getSourceViewer().getTextWidget(), 8);
+			editor.getControl().setLayoutData(gd);
+			this.startRSnippetEditor= editor;
+		}
+		{	final Button button= new Button(composite, SWT.CHECK);
+			button.setText(Messages.Operation_StartApp_StopRunningApp_label);
+			button.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+			this.startStopRunningControl= button;
+		}
+		LayoutUtils.addSmallFiller(composite, false);
+		{	final Label label= new Label(composite, SWT.NONE);
+			label.setText(Messages.Operation_StopApp_RCode_label + ':');
+			label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
+			
+			final TemplateVariableProcessor templateVariableProcessor= new TemplateVariableProcessor();
+			final RSourceViewerConfigurator configurator= new RTemplateSourceViewerConfigurator(
+					RCore.getWorkbenchAccess(),
+					templateVariableProcessor );
+			final SnippetEditor1 editor= new SnippetEditor1(configurator, null,
+					PlatformUI.getWorkbench(), RLaunchingUI.LAUNCH_CONFIG_QUALIFIER );
+			editor.create(composite, SnippetEditor.DEFAULT_SINGLE_LINE_STYLE);
+			final GridData gd= new GridData(SWT.FILL, SWT.FILL, true, false);
+			gd.heightHint= LayoutUtils.hintHeight(editor.getSourceViewer().getTextWidget(), 1);
+			editor.getControl().setLayoutData(gd);
+			this.stopRSnippetEditor= editor;
+		}
+		return composite;
+	}
+	
+	
+	@Override
+	protected void addBindings(final DataBindingContext dbc) {
+//		dbc.bindValue(
+//				this.appPathControl.getObservable(),
+//				this.appPathValue,
+//				new UpdateValueStrategy().setAfterGetValidator(this.appPathControl.getValidator()),
+//				null );
+		
+		dbc.bindValue(
+				WidgetProperties.text(SWT.Modify).observe(this.sHostControl),
+				this.appHostValue );
+		dbc.bindValue(
+				WidgetProperties.text(SWT.Modify).observe(this.sPortControl),
+				this.appPortValue,
+				new UpdateValueStrategy().setAfterGetValidator(new IntegerValidator(0, 65535, true,
+						Messages.Operation_AppPort_error_SpecInvalid_message )),
+				null );
+		
+		dbc.bindValue(
+				WidgetProperties.text(SWT.Modify).observe(this.startRSnippetEditor.getTextControl()),
+				this.startRSnippetValue,
+				new UpdateValueStrategy().setAfterGetValidator(
+						new UpdateableErrorValidator(new VariableTextValidator(
+								this.rSnippetVariableResolver,
+								Messages.Operation_StartApp_RCode_error_SpecInvalid_message ))),
+				null );
+		dbc.bindValue(
+				WidgetProperties.selection().observe(this.startStopRunningControl),
+				this.startStopRunningValue );
+		dbc.bindValue(
+				WidgetProperties.text(SWT.Modify).observe(this.stopRSnippetEditor.getTextControl()),
+				this.stopRSnippetValue );
+	}
+	
+	
+	@Override
+	public void setDefaults(final ILaunchConfigurationWorkingCopy configuration) {
+		initDefaults(configuration);
+	}
+	
+	@Override
+	protected void doInitialize(final ILaunchConfiguration configuration) {
+//		{	String path= ""; //$NON-NLS-1$
+//			try {
+//				path= configuration.getAttribute(AppControlConfig.EXPL_SOURCE_PATH_ATTR_NAME, path);
+//			}
+//			catch (final CoreException e) {
+//				logReadingError(e);
+//			}
+//			this.appPathValue.setValue(path);
+//		}
+		
+		{	String text= ""; //$NON-NLS-1$
+			try {
+				text= configuration.getAttribute(AppControlConfigs.APP_HOST_ATTR_NAME, text);
+			}
+			catch (final CoreException e) {
+				logReadingError(e);
+			}
+			this.appHostValue.setValue(text);
+		}
+		{	int port= 0;
+			try {
+				port= configuration.getAttribute(AppControlConfigs.APP_PORT_ATTR_NAME, port);
+			}
+			catch (final CoreException e) {
+				logReadingError(e);
+			}
+			this.appPortValue.setValue((port != 0) ? Integer.valueOf(port) : null);
+		}
+		
+		{	int mode= 0;
+			try {
+				mode= configuration.getAttribute(AppControlConfigs.START_STOP_BLOCKING_TASKS_MODE_ATTR_NAME, mode);
+			}
+			catch (final CoreException e) {
+				logReadingError(e);
+			}
+			this.startStopRunningValue.setValue(mode > 0);
+		}
+		{	String code= ""; //$NON-NLS-1$
+			try {
+				code= configuration.getAttribute(AppControlConfigs.START_R_SNIPPET_CODE_ATTR_NAME, code);
+			}
+			catch (final CoreException e) {
+				logReadingError(e);
+			}
+			this.startRSnippetValue.setValue(code);
+		}
+		{	String code= ""; //$NON-NLS-1$
+			try {
+				code= configuration.getAttribute(AppControlConfigs.STOP_R_SNIPPET_CODE_ATTR_NAME, code);
+			}
+			catch (final CoreException e) {
+				logReadingError(e);
+			}
+			this.stopRSnippetValue.setValue(code);
+		}
+	}
+	
+	@Override
+	protected void doSave(final ILaunchConfigurationWorkingCopy configuration) {
+//		{	final String path= this.appPathValue.getValue();
+//			configuration.setAttribute(AppControlConfig.EXPL_SOURCE_PATH_ATTR_NAME, path);
+//		}
+		
+		{	final String text= this.appHostValue.getValue();
+			configuration.setAttribute(AppControlConfigs.APP_HOST_ATTR_NAME, text);
+		}
+		{	final Integer port= this.appPortValue.getValue();
+			if (port != null) {
+				configuration.setAttribute(AppControlConfigs.APP_PORT_ATTR_NAME, port.intValue());
+			}
+			else {
+				configuration.removeAttribute(AppControlConfigs.APP_PORT_ATTR_NAME);
+			}
+		}
+		
+		{	final int mode= (this.startStopRunningValue.getValue()) ? 1 : 0;
+			configuration.setAttribute(AppControlConfigs.START_STOP_BLOCKING_TASKS_MODE_ATTR_NAME, mode);
+		}
+		{	final String code= this.startRSnippetValue.getValue();
+			configuration.setAttribute(AppControlConfigs.START_R_SNIPPET_CODE_ATTR_NAME, code);
+		}
+		{	final String code= this.stopRSnippetValue.getValue();
+			configuration.setAttribute(AppControlConfigs.STOP_R_SNIPPET_CODE_ATTR_NAME, code);
+		}
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlConfigTabGroup.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlConfigTabGroup.java
new file mode 100644
index 0000000..cc48137
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlConfigTabGroup.java
@@ -0,0 +1,76 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.launching;
+
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
+import org.eclipse.debug.ui.CommonTab;
+import org.eclipse.debug.ui.ILaunchConfigurationDialog;
+import org.eclipse.debug.ui.ILaunchConfigurationTab;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+import org.eclipse.statet.ecommons.debug.ui.config.LaunchConfigPresets;
+
+
+@NonNullByDefault
+public class AppControlConfigTabGroup extends AbstractLaunchConfigurationTabGroup {
+	
+	
+	private static final LaunchConfigPresets PRESETS;
+	static {
+		final LaunchConfigPresets presets= new LaunchConfigPresets(
+				AppControlConfigs.TYPE_ID );
+		
+		final ILaunchConfigurationWorkingCopy config= presets.add("R Shiny App");
+		config.setAttribute(AppControlConfigs.START_R_SNIPPET_CODE_ATTR_NAME,
+				"library(shiny);\n" + //$NON-NLS-1$
+				"shiny::runApp(\"${resource_loc}\", " + //$NON-NLS-1$
+						"host= \"${" + AppControlConfigs.APP_HOST_VAR_NAME + "}\", " +
+						"port= ${" + AppControlConfigs.APP_PORT_VAR_NAME + "}, " + //$NON-NLS-1$
+						"launch.browser= FALSE)" ); //$NON-NLS-1$
+		config.setAttribute(AppControlConfigs.STOP_R_SNIPPET_CODE_ATTR_NAME,
+				"shiny::stopApp()");
+		config.setAttribute(AppControlConfigs.VARIABLES_CODE_ATTR_NAME,
+				".rj.tmp$appDomains" );
+		
+		PRESETS= presets;
+	}
+	
+	public static LaunchConfigPresets getPresets() {
+		return PRESETS;
+	}
+	
+	public static void initDefaults(final ILaunchConfigurationWorkingCopy config) {
+		AppControlConfigMainTab.initDefaults(config);
+		AppControlConfigViewTab.initDefaults(config);
+	}
+	
+	
+	public AppControlConfigTabGroup() {
+	}
+	
+	
+	@Override
+	public void createTabs(final ILaunchConfigurationDialog dialog, final String mode) {
+		final ILaunchConfigurationTab[] tabs= new ILaunchConfigurationTab[] {
+				new AppControlConfigMainTab(PRESETS),
+				new AppControlConfigViewTab(),
+				new CommonTab()
+		};
+		setTabs(tabs);
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlConfigViewTab.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlConfigViewTab.java
new file mode 100644
index 0000000..33e3e30
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlConfigViewTab.java
@@ -0,0 +1,376 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.launching;
+
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.IValueChangeListener;
+import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.databinding.viewers.ViewerProperties;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StackLayout;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+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.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.ui.PlatformUI;
+
+import org.eclipse.statet.jcommons.collections.ImCollections;
+import org.eclipse.statet.jcommons.collections.ImList;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.debug.ui.config.LaunchConfigTabWithDbc;
+import org.eclipse.statet.ecommons.templates.TemplateVariableProcessor;
+import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
+
+import org.eclipse.statet.internal.r.apps.ui.Messages;
+import org.eclipse.statet.ltk.ui.sourceediting.SnippetEditor;
+import org.eclipse.statet.ltk.ui.sourceediting.SnippetEditor1;
+import org.eclipse.statet.r.apps.ui.RAppUIResources;
+import org.eclipse.statet.r.core.RCore;
+import org.eclipse.statet.r.launching.ui.RLaunchingUI;
+import org.eclipse.statet.r.ui.sourceediting.RSourceViewerConfigurator;
+import org.eclipse.statet.r.ui.sourceediting.RTemplateSourceViewerConfigurator;
+
+
+@NonNullByDefault
+public class AppControlConfigViewTab extends LaunchConfigTabWithDbc
+		implements IValueChangeListener<Object> {
+	
+	
+	static void initDefaults(final ILaunchConfigurationWorkingCopy config) {
+		config.setAttribute(AppControlConfigs.VIEWER_ID_ATTR_NAME, AppControlConfigs.WORKBENCH_VIEW_BROWSER_ID);
+		config.setAttribute(AppControlConfigs.VARIABLES_VIEWER_ACTION_ATTR_NAME, AppControlConfigs.SHOW_ACTION_ID);
+	}
+	
+	
+	private class ViewerItem {
+		
+		
+		private final @Nullable String id;
+		
+		private final String label;
+		
+		private @Nullable Composite detailControl;
+		
+		
+		public ViewerItem(final @Nullable String id, final String label) {
+			this.id= id;
+			this.label= label;
+		}
+		
+		
+		public @Nullable String getId() {
+			return this.id;
+		}
+		
+		@Override // for LabelProvider
+		public String toString() {
+			return this.label;
+		}
+		
+		public Composite enable() {
+			if (this.id != null) {
+				if (this.detailControl == null) {
+					this.detailControl= createControl(AppControlConfigViewTab.this.viewerDetailControl);
+				}
+			}
+			return this.detailControl;
+		}
+		
+		public void disable() {
+		}
+		
+		protected Composite createControl(final Composite parent) {
+			final Composite composite= new Composite(parent, SWT.NONE);
+			return composite;
+		}
+		
+	}
+	
+	
+	private ImList<ViewerItem> viewers;
+	
+	private IObservableValue<ViewerItem> viewerValue;
+	
+	private IObservableValue<String> variablesCodeValue;
+	private IObservableValue<Boolean> variablesActionValue;
+	
+	private ComboViewer viewerSelectionViewer;
+	private StackLayout viewerDetailLayout;
+	private Composite viewerDetailControl;
+	
+	private SnippetEditor variablesCodeEditor;
+	private Button variablesActionControl;
+	
+	
+	public AppControlConfigViewTab() {
+		
+		final Realm realm= getRealm();
+		this.viewerValue= new WritableValue<>(realm, null, String.class);
+		this.viewerValue.addValueChangeListener(this);
+		
+		this.viewers= ImCollections.newList(
+				new ViewerItem(null, Messages.Operation_Viewer_None_label),
+				new ViewerItem(AppControlConfigs.WORKBENCH_VIEW_BROWSER_ID,
+						Messages.Operation_Viewer_WorkbenchView_label),
+				new ViewerItem(AppControlConfigs.WORKBENCH_EXTERNAL_BROWSER_ID,
+						Messages.Operation_Viewer_WorkbenchExternal_label) {
+					@Override
+					protected Composite createControl(final Composite parent) {
+						final Composite composite= super.createControl(parent);
+						
+						composite.setLayout(LayoutUtils.newCompositeGrid(1));
+						
+						LayoutUtils.addSmallFiller(composite, true);
+						
+						final Link link= new Link(composite, SWT.NONE);
+						link.setText("Global preferences: "
+								+ "<a href=\"org.eclipse.ui.browser.preferencePage\">Web Browser</a>.");
+						composite.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false));
+						link.addSelectionListener(new SelectionAdapter() {
+							@Override
+							public void widgetSelected(final SelectionEvent e) {
+								final PreferenceDialog dialog= org.eclipse.ui.dialogs.PreferencesUtil.createPreferenceDialogOn(null, e.text, null, null);
+								if (dialog != null) {
+									dialog.open();
+								}
+							}
+						});
+						
+						return composite;
+					}
+				});
+		
+		this.variablesCodeValue= new WritableValue<>(realm, "", String.class); //$NON-NLS-1$
+		this.variablesActionValue= new WritableValue<>(realm, true, Boolean.TYPE);
+	}
+	
+	
+	@Override
+	public Image getImage() {
+		return nonNullAssert(RAppUIResources.INSTANCE.getImage(RAppUIResources.TOOL_VIEW_IMAGE_ID));
+	}
+	
+	@Override
+	public String getName() {
+		return Messages.Operation_ViewTab_name;
+	}
+	
+	public String getLabel() {
+		return Messages.Operation_ViewTab_label;
+	}
+	
+	
+	@Override
+	public void createControl(final Composite parent) {
+		final Composite mainComposite= new Composite(parent, SWT.NONE);
+		setControl(mainComposite);
+		mainComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+		mainComposite.setLayout(new GridLayout());
+		
+		{	final Label label= new Label(mainComposite, SWT.NONE);
+			label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+			label.setText(getLabel() + ':');
+		}
+		{	final Composite composite= createViewerSettings(mainComposite);
+			composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+		}
+		{	final Composite composite= createVariablesSettings(mainComposite);
+			composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+		}
+		Dialog.applyDialogFont(parent);
+		
+		initBindings();
+	}
+	
+	
+	private ViewerItem getViewerItem(final String id) {
+		for (final ViewerItem item : this.viewers) {
+			if (item.getId() == id) {
+				return item;
+			}
+		}
+		return this.viewers.get(0);
+	}
+	
+	private Composite createViewerSettings(final Composite parent) {
+		final Group composite= new Group(parent, SWT.NONE);
+		composite.setText(Messages.Operation_ViewTab_Operation_label + ':');
+		composite.setLayout(LayoutUtils.newGroupGrid(1));
+		
+		{	final ComboViewer viewer= new ComboViewer(composite);
+			
+			viewer.setLabelProvider(new LabelProvider());
+			viewer.setContentProvider(new ArrayContentProvider());
+			viewer.setInput(this.viewers);
+			
+			viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+			this.viewerSelectionViewer= viewer;
+		}
+		{	final Composite detailControl= new Composite(composite, SWT.NONE);
+			
+			this.viewerDetailLayout= new StackLayout();
+			detailControl.setLayout(this.viewerDetailLayout);
+			
+			detailControl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+			this.viewerDetailControl= detailControl;
+		}
+		
+		return composite;
+	}
+	
+	private Composite createVariablesSettings(final Composite parent) {
+		final Group composite= new Group(parent, SWT.NONE);
+		composite.setText(Messages.Operation_Variables_label + ':');
+		composite.setLayout(LayoutUtils.newGroupGrid(2));
+		
+		{	final Label label= new Label(composite, SWT.NONE);
+			label.setText(Messages.Operation_StartApp_RCode_label + ':');
+			label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
+			
+			final TemplateVariableProcessor templateVariableProcessor= new TemplateVariableProcessor();
+			final RSourceViewerConfigurator configurator= new RTemplateSourceViewerConfigurator(
+					RCore.getWorkbenchAccess(),
+					templateVariableProcessor );
+			final SnippetEditor1 editor= new SnippetEditor1(configurator, null,
+					PlatformUI.getWorkbench(), RLaunchingUI.LAUNCH_CONFIG_QUALIFIER );
+			editor.create(composite, SnippetEditor.DEFAULT_SINGLE_LINE_STYLE);
+			final GridData gd= new GridData(SWT.FILL, SWT.FILL, true, false);
+			gd.heightHint= LayoutUtils.hintHeight(editor.getSourceViewer().getTextWidget(), 1);
+			editor.getControl().setLayoutData(gd);
+			this.variablesCodeEditor= editor;
+		}
+		
+		{	final Button button= new Button(composite, SWT.CHECK);
+			button.setText(Messages.Operation_Variables_ShowView_label);
+			
+			button.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+			this.variablesActionControl= button;
+		}
+		
+		return composite;
+	}
+	
+	
+	@Override
+	protected void addBindings(final DataBindingContext dbc) {
+		dbc.bindValue(
+				ViewerProperties.singleSelection().observe(this.viewerSelectionViewer),
+				this.viewerValue );
+		
+		dbc.bindValue(
+				WidgetProperties.text(SWT.Modify).observe(this.variablesCodeEditor.getTextControl()),
+				this.variablesCodeValue );
+		dbc.bindValue(
+				WidgetProperties.selection().observe(this.variablesActionControl),
+				this.variablesActionValue );
+	}
+	
+	@Override
+	public void handleValueChange(final ValueChangeEvent<?> event) {
+		if (event.getObservable() == this.viewerValue) {
+			final ViewerItem oldItem= (ViewerItem) event.diff.getOldValue();
+			if (oldItem != null) {
+				oldItem.disable();
+			}
+			
+			final ViewerItem newItem= (ViewerItem) event.diff.getNewValue();
+			if (newItem != null) {
+				this.viewerDetailLayout.topControl= newItem.enable();
+			}
+			else {
+				this.viewerDetailLayout.topControl= null;
+			}
+			this.viewerDetailControl.layout();
+		}
+	}
+	
+	
+	@Override
+	public void setDefaults(final ILaunchConfigurationWorkingCopy configuration) {
+		initDefaults(configuration);
+	}
+	
+	@Override
+	protected void doInitialize(final ILaunchConfiguration configuration) {
+		{	String id= ""; //$NON-NLS-1$
+			try {
+				id= configuration.getAttribute(AppControlConfigs.VIEWER_ID_ATTR_NAME, id);
+			}
+			catch (final CoreException e) {
+				logReadingError(e);
+			}
+			id= id.intern();
+			final ViewerItem item= getViewerItem(id);
+			this.viewerValue.setValue(item);
+		}
+		
+		{	String code= ""; //$NON-NLS-1$
+			try {
+				code= configuration.getAttribute(AppControlConfigs.VARIABLES_CODE_ATTR_NAME, code);
+			}
+			catch (final CoreException e) {
+				logReadingError(e);
+			}
+			this.variablesCodeValue.setValue(code);
+		}
+		{	String action= ""; //$NON-NLS-1$
+			try {
+				action= configuration.getAttribute(AppControlConfigs.VARIABLES_VIEWER_ACTION_ATTR_NAME, action);
+			}
+			catch (final CoreException e) {
+				logReadingError(e);
+			}
+			this.variablesActionValue.setValue(action.equals(AppControlConfigs.SHOW_ACTION_ID));
+		}
+	}
+	
+	@Override
+	protected void doSave(final ILaunchConfigurationWorkingCopy configuration) {
+		{	final ViewerItem item= this.viewerValue.getValue();
+			configuration.setAttribute(AppControlConfigs.VIEWER_ID_ATTR_NAME, item.getId());
+		}
+		
+		{	final String code= this.variablesCodeValue.getValue();
+			configuration.setAttribute(AppControlConfigs.VARIABLES_CODE_ATTR_NAME, code);
+		}
+		{	final Boolean enabled= this.variablesActionValue.getValue();
+			configuration.setAttribute(AppControlConfigs.VARIABLES_VIEWER_ACTION_ATTR_NAME,
+					(enabled) ? AppControlConfigs.SHOW_ACTION_ID : null );
+		}
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlConfigs.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlConfigs.java
new file mode 100644
index 0000000..61f0cae
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlConfigs.java
@@ -0,0 +1,66 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.launching;
+
+import org.eclipse.core.variables.IStringVariable;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+import org.eclipse.statet.ecommons.variables.core.DynamicVariable;
+
+import org.eclipse.statet.internal.r.apps.ui.Messages;
+
+
+@NonNullByDefault
+public class AppControlConfigs {
+	
+	
+	public static final String TYPE_ID= "org.eclipse.statet.r.apps.launchConfigurations.RAppControl"; //$NON-NLS-1$
+	
+	
+	static final String ATTR_ROOT= "org.eclipse.statet.r.apps/"; //$NON-NLS-1$
+	
+	public static final String SOURCE_PATH_ATTR_NAME= ATTR_ROOT + "AppSource.path"; //$NON-NLS-1$
+	
+	public static final String APP_HOST_ATTR_NAME= ATTR_ROOT + "App.host"; //$NON-NLS-1$
+	public static final String APP_PORT_ATTR_NAME= ATTR_ROOT + "App.port"; //$NON-NLS-1$
+	
+	public static final String START_STOP_BLOCKING_TASKS_MODE_ATTR_NAME= ATTR_ROOT + "Start.StopBlockingTasks.mode"; //$NON-NLS-1$
+	
+	public static final String START_R_SNIPPET_CODE_ATTR_NAME= ATTR_ROOT + "Start.RSnippet.code"; //$NON-NLS-1$
+	
+	public static final String STOP_R_SNIPPET_CODE_ATTR_NAME= ATTR_ROOT + "Stop.RSnippet.code"; //$NON-NLS-1$
+	
+	public static final String VIEWER_ID_ATTR_NAME= ATTR_ROOT + "Viewer.id"; //$NON-NLS-1$
+	
+	public static final String VARIABLES_CODE_ATTR_NAME= ATTR_ROOT + "Variables.code"; //$NON-NLS-1$
+	public static final String VARIABLES_VIEWER_ACTION_ATTR_NAME= ATTR_ROOT + "Variables.Viewer.action"; //$NON-NLS-1$
+	
+	
+	public static final String APP_HOST_VAR_NAME= "host"; //$NON-NLS-1$
+	public static final String APP_PORT_VAR_NAME= "port"; //$NON-NLS-1$
+	
+	public static final IStringVariable APP_HOST_VAR= new DynamicVariable(
+			APP_HOST_VAR_NAME, Messages.Variable_AppHost_description, false );
+	public static final IStringVariable APP_PORT_VAR= new DynamicVariable(
+			APP_PORT_VAR_NAME, Messages.Variable_AppPort_description, false );
+	
+	public static final String WORKBENCH_EXTERNAL_BROWSER_ID= "workbench-external"; //$NON-NLS-1$
+	public static final String WORKBENCH_VIEW_BROWSER_ID= "workbench-view"; //$NON-NLS-1$
+	
+	public static final String SHOW_ACTION_ID= "show"; //$NON-NLS-1$
+	
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlLaunchConfig.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlLaunchConfig.java
new file mode 100644
index 0000000..63ee3c9
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlLaunchConfig.java
@@ -0,0 +1,272 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.launching;
+
+import static org.eclipse.statet.jcommons.lang.NullDefaultLocation.PARAMETER;
+import static org.eclipse.statet.jcommons.lang.NullDefaultLocation.RETURN_TYPE;
+import static org.eclipse.statet.jcommons.lang.NullDefaultLocation.TYPE_ARGUMENT;
+import static org.eclipse.statet.jcommons.lang.NullDefaultLocation.TYPE_BOUND;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.variables.IStringVariable;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.IWorkbenchPage;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.io.FileValidator;
+import org.eclipse.statet.ecommons.resources.core.variables.ResourceVariableResolver;
+import org.eclipse.statet.ecommons.resources.core.variables.ResourceVariables;
+import org.eclipse.statet.ecommons.ui.util.UIAccess;
+import org.eclipse.statet.ecommons.ui.workbench.workspace.ResourceVariableUtil;
+import org.eclipse.statet.ecommons.variables.core.VariableText2;
+import org.eclipse.statet.ecommons.variables.core.VariableText2.Severities;
+import org.eclipse.statet.ecommons.variables.core.VariableUtils;
+
+import org.eclipse.statet.internal.r.apps.core.shiny.RShinyResourceTester;
+import org.eclipse.statet.internal.r.apps.ui.Messages;
+import org.eclipse.statet.internal.r.apps.ui.RAppUIPlugin;
+
+
+@NonNullByDefault({ PARAMETER, RETURN_TYPE, TYPE_BOUND, TYPE_ARGUMENT })
+public class AppControlLaunchConfig {
+	
+	
+	protected static CoreException createMissingConfigAttr(final String attrName) {
+		return new CoreException(new Status(IStatus.ERROR, RAppUIPlugin.BUNDLE_ID,
+				NLS.bind("Invalid configuration: configuration attribute ''{0}'' is missing.", attrName) ));
+	}
+	
+	protected static CoreException createValidationFailed(final FileValidator validator) {
+		final IStatus status= validator.getStatus();
+		return new CoreException(new Status(IStatus.ERROR, RAppUIPlugin.BUNDLE_ID,
+				status.getMessage() ));
+	}
+	
+	
+	private ResourceVariableUtil selectedResourceUtil;
+	
+	private IResource appFolder;
+	private ResourceVariableUtil appFolderrUtil;
+	
+	private Map<String, IStringVariable> globalVariables;
+	
+	private String appHost;
+	private int appPort;
+	
+	private String startCode;
+	private String stopCode;
+	
+	private String viewerId;
+	
+	private String variablesCode;
+	private int variablesViewAction;
+	
+	private int stopBlocking;
+	
+	
+	public AppControlLaunchConfig() {
+	}
+	
+	
+	public Map<String, IStringVariable> getVariables() {
+		if (this.globalVariables == null) {
+			this.globalVariables= new HashMap<>();
+		}
+		return this.globalVariables;
+	}
+	
+	public void initSource(final ILaunchConfiguration configuration,
+			final SubMonitor m) throws CoreException {
+		final FileValidator validator= new FileValidator(true);
+		validator.setResourceLabel("app resource");
+		validator.setRequireWorkspace(true, true);
+		
+		IPath explicitePath= null;
+		{	final String path= configuration.getAttribute(AppControlConfigs.SOURCE_PATH_ATTR_NAME,
+					(String) null );
+			if (path != null) {
+				explicitePath= Path.fromPortableString(path);
+			}
+		}
+		
+		if (explicitePath != null) {
+			validator.setExplicit(explicitePath);
+		}
+		else {
+			UIAccess.getDisplay().syncExec(() -> {
+				final ResourceVariableUtil util= new ResourceVariableUtil();
+				util.getResource();
+				AppControlLaunchConfig.this.selectedResourceUtil= util;
+			});
+			if (this.selectedResourceUtil.getResource() == null) {
+				throw new CoreException(new Status(IStatus.ERROR, RAppUIPlugin.BUNDLE_ID,
+						"No resource selected in the active Workbench window." ));
+			}
+			validator.setExplicit(this.selectedResourceUtil.getResource());
+		}
+		
+		if (validator.getStatus().getSeverity() == IStatus.ERROR) {
+			throw createValidationFailed(validator);
+		}
+		
+		setSelectedResource(validator.getWorkspaceResource());
+		
+		final IResource appFolder;
+		if (explicitePath != null) {
+			appFolder= this.selectedResourceUtil.getResource();
+		}
+		else {
+			appFolder= RShinyResourceTester.getAppContainer(
+					this.selectedResourceUtil.getResource() );
+				if (appFolder == null) {
+				throw new CoreException(new Status(IStatus.ERROR, RAppUIPlugin.BUNDLE_ID,
+						NLS.bind("Could not find app folder for ''{0}''.",
+								this.selectedResourceUtil.getResource() )));
+			}
+		}
+		initAppFolder(appFolder);
+	}
+	
+	protected void setSelectedResource(final IResource resource) {
+		if (this.selectedResourceUtil == null) {
+			UIAccess.getDisplay().syncExec(() -> {
+				final ResourceVariableUtil util= new ResourceVariableUtil(resource);
+				AppControlLaunchConfig.this.selectedResourceUtil= util;
+			});
+		}
+	}
+	
+	private void initAppFolder(final IResource resource) {
+		this.appFolder= resource;
+		this.appFolderrUtil= new ResourceVariableUtil(this.selectedResourceUtil, resource);
+		
+		{	final Map<String, IStringVariable> variables= getVariables();
+			VariableUtils.add(variables,
+					ResourceVariables.getSingleResourceVariables(),
+					new ResourceVariableResolver(this.appFolderrUtil) );
+		}
+	}
+	
+	public IResource getAppFolder() {
+		return this.appFolder;
+	}
+	
+	public IWorkbenchPage getWorkbenchPage() {
+		return this.appFolderrUtil.getWorkbenchPage();
+	}
+	
+	
+	public void initAddress(final ILaunchConfiguration configuration,
+			final SubMonitor m) throws CoreException {
+		this.appHost= configuration.getAttribute(AppControlConfigs.APP_HOST_ATTR_NAME, ""); //$NON-NLS-1$
+		this.appPort= configuration.getAttribute(AppControlConfigs.APP_PORT_ATTR_NAME, 0);
+		
+		final Map<String, IStringVariable> variables= getVariables();
+		VariableUtils.add(variables, AppControlConfigs.APP_HOST_VAR);
+		VariableUtils.add(variables, AppControlConfigs.APP_PORT_VAR);
+	}
+	
+	public String getAppHost() {
+		return this.appHost;
+	}
+	
+	public int getAppPort() {
+		return this.appPort;
+	}
+	
+	
+	public void initOperation(final ILaunchConfiguration configuration,
+			final SubMonitor m) throws CoreException {
+		this.stopBlocking= configuration.getAttribute(AppControlConfigs.START_STOP_BLOCKING_TASKS_MODE_ATTR_NAME, 0);
+		
+		{	final String code= configuration.getAttribute(AppControlConfigs.START_R_SNIPPET_CODE_ATTR_NAME, ""); //$NON-NLS-1$
+			if (code.isEmpty()) {
+				throw new CoreException(new Status(IStatus.ERROR, RAppUIPlugin.BUNDLE_ID,
+						Messages.Operation_StartApp_RCode_error_SpecMissing_message ));
+			}
+			
+			try {
+				final VariableText2 variableResolver= new VariableText2(getVariables());
+				variableResolver.validate(code, Severities.CHECK_SYNTAX, null);
+				this.startCode= code;
+			}
+			catch (final CoreException e) {
+				throw new CoreException(new Status(IStatus.ERROR, RAppUIPlugin.BUNDLE_ID,
+						NLS.bind(Messages.Operation_StartApp_RCode_error_SpecInvalid_message,
+						e.getMessage() )));
+			}
+		}
+		
+		{	final String code= configuration.getAttribute(AppControlConfigs.STOP_R_SNIPPET_CODE_ATTR_NAME, ""); //$NON-NLS-1$
+			this.stopCode= (!code.isEmpty()) ? code : null;
+		}
+		
+		this.viewerId= configuration.getAttribute(AppControlConfigs.VIEWER_ID_ATTR_NAME, (String) null);
+		
+		{	String code= configuration.getAttribute(AppControlConfigs.VARIABLES_CODE_ATTR_NAME, ""); //$NON-NLS-1$
+			if (code.isEmpty()) {
+				code= null;
+			}
+			this.variablesCode= code;
+			
+			final String viewAction= configuration.getAttribute(AppControlConfigs.VARIABLES_VIEWER_ACTION_ATTR_NAME, ""); //$NON-NLS-1$
+			int viewActionMode= 0;
+			if (viewAction.equals(AppControlConfigs.SHOW_ACTION_ID)) {
+				viewActionMode= IWorkbenchPage.VIEW_VISIBLE;
+			}
+			this.variablesViewAction= viewActionMode;
+		}
+	}
+	
+	
+	public int getStopBlocking() {
+		return this.stopBlocking;
+	}
+	
+	public String getStartCode() {
+		return this.startCode;
+	}
+	
+	public String getStopCode() {
+		return this.stopCode;
+	}
+	
+	
+	public @Nullable String getViewerId() {
+		return this.viewerId;
+	}
+	
+	
+	public @Nullable String getVariablesCode() {
+		return this.variablesCode;
+	}
+	
+	public int getVariablesViewAction() {
+		return this.variablesViewAction;
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlLaunchDelegate.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlLaunchDelegate.java
new file mode 100644
index 0000000..2257eab
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlLaunchDelegate.java
@@ -0,0 +1,66 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.launching;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.model.LaunchConfigurationDelegate;
+import org.eclipse.debug.ui.DebugUITools;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+
+@NonNullByDefault
+public class AppControlLaunchDelegate extends LaunchConfigurationDelegate {
+	
+	
+	public AppControlLaunchDelegate() {
+	}
+	
+	
+	@Override
+	protected IProject @Nullable [] getBuildOrder(final ILaunchConfiguration configuration,
+			final String mode) throws CoreException {
+		final IResource resource= DebugUITools.getSelectedResource();
+		final IProject project;
+		if (resource != null && (project= resource.getProject()) != null) {
+			return computeReferencedBuildOrder(new IProject[] { project });
+		}
+		return null;
+	}
+	
+	
+	@Override
+	public void launch(final ILaunchConfiguration configuration,
+			final String mode, final @Nullable ILaunch launch,
+			final @Nullable IProgressMonitor monitor) throws CoreException {
+		final SubMonitor m= SubMonitor.convert(monitor, "Configuring App Operation...", 3 + 3);
+		
+		final AppControlLaunchConfig config= new AppControlLaunchConfig();
+		config.initSource(configuration, m.newChild(1));
+		config.initAddress(configuration, m.newChild(1));
+		config.initOperation(configuration, m.newChild(1));
+		
+		final AppRunner runner= new AppRunner(config);
+		runner.startApp(config.getWorkbenchPage());
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlManager.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlManager.java
new file mode 100644
index 0000000..a494894
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppControlManager.java
@@ -0,0 +1,93 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.launching;
+
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.osgi.util.NLS;
+
+import org.eclipse.statet.jcommons.collections.IdentitySet;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+import org.eclipse.statet.ecommons.debug.ui.config.LaunchConfigManager;
+import org.eclipse.statet.ecommons.debug.ui.config.LaunchConfigPresets;
+
+import org.eclipse.statet.internal.r.apps.ui.Messages;
+
+
+/**
+ * 
+ * element= app container
+ */
+@NonNullByDefault
+public class AppControlManager extends LaunchConfigManager<IContainer> {
+	
+	
+	private final AppType appType;
+	
+	
+	public AppControlManager(final AppType appType) {
+		super(appType.getId(), AppControlConfigs.TYPE_ID );
+		
+		this.appType= appType;
+	}
+	
+	
+	@Override
+	protected Map<String, Object> createRunAttributes(final IContainer element,
+			final IdentitySet<String> flags) {
+		final Map<String, Object> map= new IdentityHashMap<>(4);
+		if (element != null) {
+			map.put(AppControlConfigs.SOURCE_PATH_ATTR_NAME, element.getFullPath().toPortableString());
+		}
+		return map;
+	}
+	
+	
+	@Override
+	protected String getActionLabel(final byte bits) {
+		switch (bits) {
+		case 0:
+			return Messages.RunAction_RunApp_label;
+		default:
+			throw new IllegalArgumentException();
+		}
+	}
+	
+	
+	@Override
+	protected String getAutogenConfigName() {
+		String name= this.appType.getName();
+		if (name.startsWith("R ")) { //$NON-NLS-1$
+			name= name.substring(2);
+		}
+		return NLS.bind("Run {0} and Show in Eclipse", name);
+	}
+	
+	@Override
+	protected void initAutogenConfig(final ILaunchConfigurationWorkingCopy config) {
+		AppControlConfigTabGroup.initDefaults(config);
+		final ILaunchConfiguration preset= AppControlConfigTabGroup.getPresets().get(
+				this.appType.getName() );
+		if (preset != null) {
+			LaunchConfigPresets.apply(preset, config);
+		}
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppRCommandHandler.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppRCommandHandler.java
new file mode 100644
index 0000000..898f91f
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppRCommandHandler.java
@@ -0,0 +1,92 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.launching;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.ts.core.util.ToolCommandHandlerUtil;
+
+import org.eclipse.statet.nico.core.runtime.ToolController;
+import org.eclipse.statet.r.console.core.RProcess;
+import org.eclipse.statet.rj.eclient.core.AbstractRToolCommandHandler;
+import org.eclipse.statet.rj.eclient.core.RToolService;
+
+
+@NonNullByDefault
+class AppRCommandHandler extends AbstractRToolCommandHandler {
+	
+	
+	private static final String ON_APP_STARTED_ID= "org.eclipse.statet.r.apps.onAppStarted"; //$NON-NLS-1$
+	
+	private static final String ON_APP_STOPPED_ID= "org.eclipse.statet.r.apps.onAppStopped"; //$NON-NLS-1$
+	
+	
+	static AppRCommandHandler connect(final AppRunner runner,
+			final RToolService r, final IProgressMonitor monitor) throws CoreException {
+		final ToolController controller= ((RProcess) r.getTool()).getController();
+		AppRCommandHandler handler= (AppRCommandHandler) controller.getCommandHandler(ON_APP_STARTED_ID);
+		if (handler == null) {
+			handler= new AppRCommandHandler();
+			controller.addCommandHandler(ON_APP_STARTED_ID, handler);
+			controller.addCommandHandler(ON_APP_STOPPED_ID, handler);
+		}
+		handler.currentRunner= runner;
+		
+		return handler;
+	}
+	
+	
+	private @Nullable AppRunner currentRunner;
+	
+	
+	private AppRCommandHandler() {
+	}
+	
+	
+	@Override
+	protected IStatus execute(final String id, final RToolService r,
+			final Map<String, Object> data,
+			final IProgressMonitor monitor) throws CoreException {
+		if (id.equals(ON_APP_STARTED_ID)) {
+			if (this.currentRunner != null) {
+				this.currentRunner.onAppStarted(
+						ToolCommandHandlerUtil.getCheckedData(data, "url", String.class, false), //$NON-NLS-1$
+						ToolCommandHandlerUtil.getCheckedData(data, "typeId", String.class, false) ); //$NON-NLS-1$
+			}
+		}
+		if (id.equals(ON_APP_STOPPED_ID)) {
+			if (this.currentRunner != null) {
+				this.currentRunner.onAppStopped(
+						ToolCommandHandlerUtil.getCheckedData(data, "url", String.class, false) ); //$NON-NLS-1$
+			}
+		}
+		return Status.OK_STATUS;
+	}
+	
+	void disconnect(final AppRunner runner) {
+		if (this.currentRunner == runner) {
+			this.currentRunner= null;
+		}
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppRunner.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppRunner.java
new file mode 100644
index 0000000..445bf0e
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppRunner.java
@@ -0,0 +1,764 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.launching;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Map;
+import java.util.Objects;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.variables.IStringVariable;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IViewReference;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.browser.IWebBrowser;
+import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
+import org.eclipse.ui.statushandlers.StatusManager;
+
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+import org.eclipse.statet.jcommons.collections.CopyOnWriteIdentityListSet;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.ts.core.Tool;
+import org.eclipse.statet.ecommons.ts.core.ToolRunnable;
+import org.eclipse.statet.ecommons.ui.mpbv.BrowserSession;
+import org.eclipse.statet.ecommons.ui.util.UIAccess;
+import org.eclipse.statet.ecommons.variables.core.StaticVariable;
+import org.eclipse.statet.ecommons.variables.core.VariableText2;
+import org.eclipse.statet.ecommons.variables.core.VariableUtils;
+
+import org.eclipse.statet.internal.r.apps.ui.Messages;
+import org.eclipse.statet.internal.r.apps.ui.RAppUIPlugin;
+import org.eclipse.statet.internal.r.apps.ui.variables.AppVarView;
+import org.eclipse.statet.internal.r.apps.ui.viewer.AppBrowserSession;
+import org.eclipse.statet.internal.r.apps.ui.viewer.AppBrowserView;
+import org.eclipse.statet.nico.core.runtime.Queue;
+import org.eclipse.statet.nico.core.runtime.Queue.RunnableStatus;
+import org.eclipse.statet.nico.core.runtime.ToolController;
+import org.eclipse.statet.nico.core.runtime.ToolProcess;
+import org.eclipse.statet.nico.ui.NicoUI;
+import org.eclipse.statet.nico.ui.NicoUITools;
+import org.eclipse.statet.r.apps.ui.AppRegistry;
+import org.eclipse.statet.r.apps.ui.RApp;
+import org.eclipse.statet.r.apps.ui.VariablesData;
+import org.eclipse.statet.r.console.core.RConsoleTool;
+import org.eclipse.statet.r.console.core.RProcess;
+import org.eclipse.statet.r.console.core.RWorkspace;
+import org.eclipse.statet.r.console.core.util.RCodeVariableText;
+import org.eclipse.statet.r.core.tool.AbstractStatetRRunnable;
+import org.eclipse.statet.r.core.tool.IRConsoleService;
+import org.eclipse.statet.r.nico.impl.RjsUtil;
+import org.eclipse.statet.rj.eclient.core.AbstractRToolRunnable;
+import org.eclipse.statet.rj.eclient.core.RToolService;
+
+
+@NonNullByDefault
+public class AppRunner extends AbstractStatetRRunnable implements RApp {
+	
+	
+	public static final String RUN_TASK_ID= "org.eclipse.statet.r.apps/RunApp"; //$NON-NLS-1$
+	public static final String STOP_TASK_ID= "org.eclipse.statet.r.apps/StopApp"; //$NON-NLS-1$
+	
+	private static final String LOCALHOST= "127.0.0.1"; //$NON-NLS-1$
+	
+	private static final IStatus NOT_RUNNING_DATA_STATUS= new Status(IStatus.INFO, RAppUIPlugin.BUNDLE_ID,
+			"The app is not running." );
+	private static final IStatus NOT_LOADED_DATA_STATUS= new Status(IStatus.INFO, RAppUIPlugin.BUNDLE_ID,
+			"Variabes are not yet available." );
+	
+	
+	public static RProcess fetchRProcess(final IWorkbenchPage page) throws CoreException {
+		final ToolProcess process= NicoUI.getToolRegistry().getActiveToolSession(page).getProcess();
+		NicoUITools.accessTool(RConsoleTool.TYPE, process);
+		return (RProcess) process;
+	}
+	
+	
+	private class AppSession {
+		
+		private final RProcess rProcess;
+		
+		private Queue.Section queueSection;
+		
+		private String host;
+		private String remoteHost;
+		private int remotePort;
+		private @Nullable Session sshSession;
+		private int sshLocalPort= -1;
+		
+		private @Nullable URL localUrl;
+		private @Nullable URL idUrl;
+		
+		private boolean isRunning;
+		
+		
+		public AppSession(final RProcess tool) {
+			this.rProcess= tool;
+		}
+		
+		
+		public RProcess getTool() {
+			return this.rProcess;
+		}
+		
+		public boolean isRunning() {
+			return this.isRunning;
+		}
+		
+		public void init(final IProgressMonitor monitor) throws CoreException {
+			{	this.queueSection= this.rProcess.getController().getCurrentQueueSection();
+			}
+			{	final String host= AppRunner.this.config.getAppHost();
+				if (host.isEmpty()) {
+					final RWorkspace workspaceData= this.rProcess.getWorkspaceData();
+					if (workspaceData.isRemote()) {
+						this.host= workspaceData.getRemoteAddress();
+						final Map<String, Object> connectionInfo= this.rProcess.getConnectionInfo();
+						if (connectionInfo != null && Objects.equals(connectionInfo.get("protocol"), "ssh")) {
+							this.remoteHost= LOCALHOST;
+							this.sshSession= RjsUtil.getSession(connectionInfo, monitor);
+						}
+					}
+					else {
+						this.host= LOCALHOST;
+					}
+				}
+				else {
+					this.host= host;
+				}
+				if (this.remoteHost == null) {
+					this.remoteHost= this.host;
+				}
+				this.remotePort= AppRunner.this.config.getAppPort();
+			}
+		}
+		
+		public String getStartCode() throws CoreException {
+			{	final String code= AppRunner.this.config.getStartCode();
+				
+				final Map<String, IStringVariable> variables= AppRunner.this.config.getVariables();
+				VariableUtils.add(variables, new StaticVariable(
+						AppControlConfigs.APP_HOST_VAR,
+						this.remoteHost ));
+				VariableUtils.add(variables, new StaticVariable(
+						AppControlConfigs.APP_PORT_VAR,
+						(this.remotePort > 0) ? Integer.toString(this.remotePort) : "NULL" )); //$NON-NLS-1$
+				
+				final VariableText2 variableText= new RCodeVariableText(
+						this.rProcess.getWorkspaceData(), variables );
+				try {
+					return variableText.performStringSubstitution(code, null);
+				}
+				catch (final CoreException e) {
+					throw new CoreException(new Status(IStatus.ERROR, RAppUIPlugin.BUNDLE_ID,
+							NLS.bind(Messages.Operation_StartApp_RCode_error_SpecInvalid_message,
+							 e.getMessage() )));
+				}
+			}
+		}
+		
+		public boolean onStarted(final String url) {
+			try {
+				final URL rUrl;
+				try {
+					rUrl= new URL(url);
+				}
+				catch (final MalformedURLException e) {
+					throw new CoreException(new Status(IStatus.ERROR, RAppUIPlugin.BUNDLE_ID,
+							NLS.bind("Invalid URL of the R app from R= ''{0}''.", url),
+							e ));
+				}
+				
+				if (this.sshSession != null) {
+					try {
+						this.sshLocalPort= this.sshSession.setPortForwardingL(0, LOCALHOST,
+								(rUrl.getPort() != -1) ? rUrl.getPort() : 80 );
+						this.localUrl= new URL(rUrl.getProtocol(), LOCALHOST, this.sshLocalPort,
+								rUrl.getFile() );
+						this.idUrl= new URL(rUrl.getProtocol(), this.host, rUrl.getPort(),
+								rUrl.getFile() );
+					}
+					catch (final JSchException e) {
+						throw new CoreException(new Status(IStatus.ERROR, RAppUIPlugin.BUNDLE_ID,
+								"Failed create SSH tunnel for http connection of the R app.",
+								e ));
+					}
+				}
+				else if (!this.host.equals(rUrl.getHost())) {
+					this.localUrl= new URL(rUrl.getProtocol(), this.host, rUrl.getPort(),
+							rUrl.getFile() );
+					this.idUrl= this.localUrl;
+				}
+				else {
+					this.localUrl= rUrl;
+					this.idUrl= this.localUrl;
+				}
+			}
+			catch (final Exception e) {
+				StatusManager.getManager().handle(new Status(IStatus.ERROR, RAppUIPlugin.BUNDLE_ID,
+							"An error occurred when preparing to show the R app.", e ),
+							(AppRunner.this.config.getViewerId() != null) ?
+									StatusManager.LOG | StatusManager.SHOW :
+									StatusManager.LOG );
+				return false;
+			}
+			
+			synchronized (AppRunner.this) {
+				this.isRunning= true;
+				return true;
+			}
+		}
+		
+		public URL getLocalUrl() {
+			return this.localUrl;
+		}
+		
+		public URL getIdUrl() {
+			return this.idUrl;
+		}
+		
+		public void onAppStop() {
+			synchronized (AppRunner.this) {
+				this.isRunning= false;
+			}
+			
+			this.queueSection= null;
+			
+			if (this.sshSession != null && this.sshLocalPort > 0) {
+				try {
+					this.sshSession.delPortForwardingL(this.sshLocalPort);
+				}
+				catch (final JSchException e) {
+					RAppUIPlugin.log(new Status(IStatus.ERROR, RAppUIPlugin.BUNDLE_ID,
+							"Failed delete SSH tunnel for http connection of the R app.",
+							e ));
+				}
+				this.sshLocalPort= -1;
+			}
+		}
+		
+	}
+	
+	public class StopRunnable extends AbstractRToolRunnable {
+		
+		
+		public StopRunnable() {
+			super(STOP_TASK_ID, "Stop R App"); //$NON-NLS-1$
+		}
+		
+		
+		@Override
+		public boolean changed(final int event, final Tool tool) {
+			switch (event) {
+			case MOVING_FROM:
+				return false;
+			default:
+				return true;
+			}
+		}
+		
+		@Override
+		protected void run(final RToolService r,
+				final IProgressMonitor monitor) throws CoreException {
+			AppSession session;
+			synchronized (AppRunner.this) {
+				session= AppRunner.this.session;
+				if (session == null || !session.isRunning()) {
+					return;
+				}
+			}
+			
+			final String code= AppRunner.this.config.getStopCode();
+			r.evalVoid(code, monitor);
+		}
+		
+	}
+	
+	
+	private final AppControlLaunchConfig config;
+	
+	private final CopyOnWriteIdentityListSet<Listener> listeners= new CopyOnWriteIdentityListSet<>();
+	
+	private @Nullable AppSession session;
+	
+	private @Nullable RProcess tool;
+	private @Nullable IWorkbenchPage workbenchPage;
+	
+	private @Nullable DataLoader variablesLoader;
+	private @Nullable volatile VariablesData variablesData;
+	
+	
+	public AppRunner(final AppControlLaunchConfig config) {
+		super(RUN_TASK_ID, NLS.bind("Run R App ''{0}''",
+				config.getAppFolder().getFullPath().toString() ));
+		
+		this.config= config;
+		initVars();
+	}
+	
+	
+	@Override
+	public IResource getResource() {
+		return this.config.getAppFolder();
+	}
+	
+	
+	@Override
+	protected void run(final IRConsoleService r,
+			final IProgressMonitor monitor) throws CoreException {
+		final AppSession session;
+		AppRCommandHandler listener= null;
+		try {
+			synchronized (this) {
+				this.tool= (RProcess) r.getTool();
+				session= new AppSession(this.tool);
+				this.session= session;
+			}
+			
+			session.init(monitor);
+			
+			listener= AppRCommandHandler.connect(this, r, monitor);
+			
+			r.briefAboutToChange();
+			
+			r.submitToConsole(session.getStartCode(), monitor);
+			
+		}
+		finally {
+			r.briefChanged(IRConsoleService.AUTO_CHANGE);
+			
+			onAppStopped(null);
+			
+			if (listener != null) {
+				listener.disconnect(this);
+			}
+		}
+	}
+	
+	protected void onAppStarted(final String url, final String typeId) {
+		final AppSession session;
+		synchronized (this) {
+			session= this.session;
+			if (session == null) {
+				return;
+			}
+		}
+		
+		final boolean isRunning= session.onStarted(url);
+		updateVarsOnStarted();
+		
+		if (isRunning) {
+			AppRegistry.getInstance().onAppStarted(session.getIdUrl(), this);
+			
+			if (this.config.getViewerId() != null) {
+				showViewer(session, this.config.getViewerId());
+			}
+			if (this.config.getVariablesViewAction() != 0 && this.variablesData != null) {
+				showVariablesView(session, this.config.getVariablesViewAction());
+			}
+			refreshVariables();
+		}
+	}
+	
+	protected void onAppStopped(final @Nullable String url) {
+		final AppSession session;
+		synchronized (this) {
+			session= this.session;
+			if (session == null) {
+				return;
+			}
+			
+			this.session= null;
+		}
+		
+		session.onAppStop();
+		
+		if (session.getIdUrl() != null) {
+			AppRegistry.getInstance().onAppStopped(session.getIdUrl(), this);
+		}
+		
+		updateVarsOnStopped();
+	}
+	
+	
+	@Override
+	public @Nullable RProcess getTool() {
+		return this.tool;
+	}
+	
+	protected IWorkbenchPage getWorkbenchPage() {
+		IWorkbenchPage page= this.workbenchPage;
+		if (page == null) {
+			page= this.config.getWorkbenchPage();
+		}
+		if (page != null && page.getWorkbenchWindow().getActivePage() == page) {
+			return page;
+		}
+		page= UIAccess.getActiveWorkbenchPage(true);
+		this.workbenchPage= page;
+		return page;
+	}
+	
+	@Override
+	public boolean isRunning() {
+		final AppSession session;
+		synchronized (this) {
+			session= this.session;
+			
+			return (session != null && session.isRunning());
+		}
+	}
+	
+	@Override
+	public void startApp(final IWorkbenchPage page) throws CoreException {
+		final RProcess tool= fetchRProcess(page);
+		
+		final IStatus status= tryStart(tool);
+		if (status.getSeverity() < IStatus.ERROR) {
+			return;
+		}
+		
+		throw new CoreException(status);
+	}
+	
+	@Override
+	public boolean canRestartApp() { // (!isRunning() || canStopApp())
+		final AppSession session;
+		synchronized (this) {
+			session= this.session;
+			if (session == null || !session.isRunning()) {
+				return true;
+			}
+		}
+		
+		return (this.config.getStopCode() != null);
+	}
+	
+	@Override
+	public void restartApp(final IWorkbenchPage page) throws CoreException {
+		final AppSession session;
+		RProcess tool;
+		Queue.Section queueSection;
+		synchronized (this) {
+			tool= this.tool;
+			session= this.session;
+			
+			queueSection= (session != null && session.isRunning()) ? session.queueSection : null;
+		}
+		
+		final AppRunner runner= new AppRunner(this.config);
+		runner.workbenchPage= this.workbenchPage;
+		
+		IStatus status= null;
+		if (tool != null) {
+			if (queueSection != null && queueSection != tool.getQueue().getTopLevelSection()) {
+				final IStatus status0= runner.tryRestart(tool, queueSection);
+				if (status0.getSeverity() < IStatus.ERROR) {
+					return;
+				}
+			}
+			{	final IStatus status0= runner.tryRestart(tool, tool.getQueue().getTopLevelSection());
+				if (status0.getSeverity() < IStatus.ERROR) {
+					return;
+				}
+				if (status == null) {
+					status= status0;
+				}
+			}
+		}
+		
+		{	tool= fetchRProcess(page);
+			final IStatus status0= runner.tryRestart(tool, tool.getQueue().getTopLevelSection());
+			if (status0.getSeverity() < IStatus.ERROR) {
+				return;
+			}
+			if (status == null) {
+				status= status0;
+			}
+		}
+		
+		throw new CoreException(status);
+	}
+	
+	private IStatus tryStart(final RProcess tool) {
+		final Queue queue= tool.getQueue();
+		final IStatus status= queue.add(this);
+		
+		if (status.getSeverity() < IStatus.ERROR) {
+			stopBlocking(tool, this.config.getStopBlocking());
+		}
+		
+		return status;
+	}
+	
+	private IStatus tryRestart(final RProcess tool, final Queue.Section queueSection) {
+		final Queue queue= tool.getQueue();
+		final IStatus status= queue.add(this, queueSection, Queue.IF_ABSENT);
+		
+		if (status.getSeverity() < IStatus.ERROR) {
+			final AppRunner runner= (AppRunner) ((RunnableStatus) status).getRunnable();
+			
+			runner.stopBlocking(tool, this.config.getStopBlocking() | 2);
+		}
+		
+		return status;
+	}
+	
+	private void stopBlocking(final RProcess tool, final int mode) {
+		if (mode == 0) {
+			return;
+		}
+		final ToolController controller= tool.getController();
+		if (controller != null) {
+			final ToolRunnable currentRunnable= controller.getCurrentRunnable();
+			if (currentRunnable != this && currentRunnable instanceof AppRunner) {
+				final AppRunner runner= (AppRunner) currentRunnable;
+				if ((mode & 1) != 0
+						|| ((mode & 2) != 0 && runner.config == this.config) ) {
+					runner.stopApp();
+				}
+			}
+		}
+	}
+	
+	@Override
+	public boolean canStopApp() {
+		final AppSession session;
+		synchronized (this) {
+			session= this.session;
+			if (session == null || !session.isRunning()) {
+				return false;
+			}
+		}
+		
+		return (this.config.getStopCode() != null);
+	}
+	
+	@Override
+	public void stopApp() {
+		final AppSession session;
+		synchronized (this) {
+			session= this.session;
+			if (session == null) {
+				return;
+			}
+		}
+		
+		if (this.config.getStopCode() != null) {
+			session.getTool().getQueue().addHot(new StopRunnable());
+		}
+	}
+	
+	
+	private void showViewer(final AppSession session, final String viewerId) {
+		if (session.getLocalUrl() == null) {
+			StatusManager.getManager().handle(new Status(IStatus.ERROR, RAppUIPlugin.BUNDLE_ID,
+							"Cannot open viewer to show the R app: URL is missing." ),
+					StatusManager.SHOW );
+			return;
+		}
+		
+		UIAccess.getDisplay().asyncExec(() -> {
+			synchronized (AppRunner.this) {
+				if (session != this.session || !session.isRunning()) {
+					return;
+				}
+			}
+			try {
+				switch (viewerId) {
+				case AppControlConfigs.WORKBENCH_EXTERNAL_BROWSER_ID:
+					openExternalBrowser(session);
+					return;
+				case AppControlConfigs.WORKBENCH_VIEW_BROWSER_ID:
+					openViewBrowser(getWorkbenchPage(), session);
+					return;
+				default:
+					throw new UnsupportedOperationException("viewerId= " + viewerId); //$NON-NLS-1$
+				}
+			}
+			catch (final Exception e) {
+				StatusManager.getManager().handle(new Status(IStatus.ERROR, RAppUIPlugin.BUNDLE_ID,
+								Messages.Operation_Viewer_error_Run_message, e ),
+						StatusManager.LOG | StatusManager.SHOW );
+			}
+		});
+	}
+	
+	private void openExternalBrowser(final AppSession session) throws PartInitException {
+		final IWorkbenchBrowserSupport browserSupport= PlatformUI.getWorkbench().getBrowserSupport();
+		final IWebBrowser webBrowser= browserSupport.getExternalBrowser();
+		webBrowser.openURL(session.getLocalUrl());
+	}
+	
+	private AppBrowserView getView(final IWorkbenchPage page) throws PartInitException {
+		return (AppBrowserView) page.showView(AppBrowserView.VIEW_ID, null,
+				IWorkbenchPage.VIEW_VISIBLE );
+	}
+	
+	private void openViewBrowser(final IWorkbenchPage page, final AppSession session) throws PartInitException {
+		final AppBrowserView view= getView(page);
+		AppBrowserSession viewSession= (AppBrowserSession) BrowserSession.findSessionById(
+				view.getSessions(), session.getIdUrl() );
+		if (viewSession == null) {
+			viewSession= new AppBrowserSession(session.getIdUrl());
+		}
+		view.openUrl(session.getLocalUrl().toExternalForm(), viewSession);
+	}
+	
+	
+	private void showVariablesView(final AppSession session, final int viewActionMode) {
+		UIAccess.getDisplay().asyncExec(() -> {
+			synchronized (AppRunner.this) {
+				if (session != this.session || !session.isRunning()) {
+					return;
+				}
+			}
+			try {
+				final IWorkbenchPage page= getWorkbenchPage();
+				final IViewReference viewRef= page.findViewReference(AppVarView.VIEW_ID);
+				if (viewRef != null) {
+					if (viewRef.isFastView()) {
+						return;
+					}
+					final IViewPart view= viewRef.getView(false);
+					if (view != null && page.isPartVisible(view)) {
+						return;
+					}
+				}
+				final AppVarView view= (AppVarView) page.showView(AppVarView.VIEW_ID, null,
+						viewActionMode );
+				view.setShownByLauncher(this);
+			}
+			catch (final PartInitException e) {
+				StatusManager.getManager().handle(new Status(IStatus.ERROR, RAppUIPlugin.BUNDLE_ID,
+						Messages.Operation_Variables_error_Run_message, e ));
+			}
+		});
+	}
+	
+	
+	@Override
+	public void addListener(final Listener listener) {
+		boolean refresh;
+		synchronized (this.listeners) {
+			refresh= (this.listeners.isEmpty() && this.variablesLoader == null);
+			
+			this.listeners.add(listener);
+		}
+		if (refresh) {
+			refreshVariables();
+		}
+	}
+	
+	@Override
+	public void removeListener(final Listener listener) {
+		this.listeners.remove(listener);
+	}
+	
+	@Override
+	public @Nullable VariablesData getVariables() {
+		return this.variablesData;
+	}
+	
+	private void initVars() {
+		final String code= this.config.getVariablesCode();
+		if (code == null) {
+			return;
+		}
+		this.variablesData= new VariablesData(code, NOT_RUNNING_DATA_STATUS);
+	}
+	
+	private void updateVarsOnStarted() {
+		final VariablesData data= this.variablesData;
+		if (data == null || data.getStatus() != NOT_RUNNING_DATA_STATUS) {
+			return;
+		}
+		setData(new VariablesData(data.getExpression(), NOT_LOADED_DATA_STATUS));
+	}
+	
+	@Override
+	public void refreshVariables() {
+		final VariablesData data= this.variablesData;
+		if (data == null) {
+			return;
+		}
+		DataLoader loader;
+		synchronized (this.listeners) {
+			loader= this.variablesLoader;
+			if (loader == null) {
+				if (this.listeners.isEmpty() || !isRunning()) {
+					return;
+				}
+				loader= new DataLoader(this, data.getExpression());
+				this.variablesLoader= loader;
+			}
+		}
+		loader.schedule();
+	}
+	
+	private void updateVarsOnStopped() {
+		final VariablesData data= this.variablesData;
+		if (data == null) {
+			return;
+		}
+		DataLoader loader;
+		synchronized (this.listeners) {
+			loader= this.variablesLoader;
+			this.variablesLoader= null;
+		}
+		if (loader != null) {
+			loader.stop();
+		}
+		setData(new VariablesData(data.getExpression(), NOT_RUNNING_DATA_STATUS));
+	}
+	
+	void setData(final VariablesData variables) {
+		this.variablesData= variables;
+		
+		final AppEvent event= new AppEvent(this);
+		for (final Listener listener : this.listeners) {
+			listener.onVariablesChanged(event);
+		}
+	}
+	
+	
+	@Override
+	public int hashCode() {
+		return this.config.hashCode();
+	}
+	
+	@Override
+	public boolean equals(final @Nullable Object obj) {
+		if (this == obj) {
+			return true;
+		}
+		if (obj instanceof AppRunner) {
+			return (this.config == ((AppRunner) obj).config);
+		}
+		return false;
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppType.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppType.java
new file mode 100644
index 0000000..e7cd3ec
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/AppType.java
@@ -0,0 +1,49 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.launching;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+
+@NonNullByDefault
+public class AppType {
+	
+	
+	private final String id;
+	
+	private final String name;
+	
+	
+	public AppType(final String id, final String name) {
+		this.id= id;
+		this.name= name;
+	}
+	
+	
+	public String getId() {
+		return this.id;
+	}
+	
+	public String getName() {
+		return this.name;
+	}
+	
+	
+	@Override
+	public String toString() {
+		return this.id;
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/DataLoader.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/DataLoader.java
new file mode 100644
index 0000000..ef0c30c
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/launching/DataLoader.java
@@ -0,0 +1,274 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.launching;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+
+import org.eclipse.statet.jcommons.collections.ImCollections;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.ObjectUtils;
+import org.eclipse.statet.jcommons.lang.ObjectUtils.ToStringBuilder;
+
+import org.eclipse.statet.ecommons.ts.core.SystemRunnable;
+import org.eclipse.statet.ecommons.ts.core.Tool;
+
+import org.eclipse.statet.internal.r.apps.ui.RAppUIPlugin;
+import org.eclipse.statet.nico.core.runtime.Queue;
+import org.eclipse.statet.r.apps.ui.VariablesData;
+import org.eclipse.statet.r.console.core.RProcess;
+import org.eclipse.statet.r.console.core.RWorkspace;
+import org.eclipse.statet.r.core.data.CombinedRElement;
+import org.eclipse.statet.r.core.model.RElementName;
+import org.eclipse.statet.r.core.tool.TmpUtils;
+import org.eclipse.statet.r.nico.ICombinedRDataAdapter;
+import org.eclipse.statet.rj.data.RDataUtils;
+import org.eclipse.statet.rj.data.RReference;
+import org.eclipse.statet.rj.data.UnexpectedRDataException;
+import org.eclipse.statet.rj.eclient.core.AbstractRToolRunnable;
+import org.eclipse.statet.rj.eclient.core.RToolService;
+import org.eclipse.statet.rj.services.RService;
+
+
+@NonNullByDefault
+public class DataLoader extends AbstractRToolRunnable implements SystemRunnable {
+	
+	
+	public static final String TASK_ID= "org.eclipse.statet.r.apps/LoadVariables"; //$NON-NLS-1$
+	
+	
+	private final AppRunner app;
+	
+	private final String expression;
+	
+	private boolean isScheduled;
+	
+	private RWorkspace workspace;
+	
+	private final Set<Long> envs= new HashSet<>();
+	
+	
+	@SuppressWarnings("null")
+	public DataLoader(final AppRunner app, final String expression) {
+		super(TASK_ID, "Load elements of R app variables");
+		
+		this.app= app;
+		this.expression= expression;
+		
+		final Queue queue= getTool().getQueue();
+		queue.addOnIdle(this, 5100);
+	}
+	
+	
+	@SuppressWarnings("null")
+	private RProcess getTool() {
+		return this.app.getTool();
+	}
+	
+	public void schedule() {
+		synchronized (this) {
+			final Queue queue= getTool().getQueue();
+			if (!this.isScheduled) {
+				queue.addHot(this);
+			}
+		}
+	}
+	
+	public void stop() {
+		synchronized (this) {
+			final Queue queue= getTool().getQueue();
+			if (this.isScheduled) {
+				queue.removeHot(this);
+				this.isScheduled= false;
+			}
+			queue.removeOnIdle(this);
+		}
+	}
+	
+	
+	@Override
+	public boolean canRunIn(final Tool tool) {
+		return (tool == this.app.getTool());
+	}
+	
+	@Override
+	protected void run(final RToolService r, final IProgressMonitor monitor) throws CoreException {
+		if (!this.app.isRunning()) {
+			return;
+		}
+		
+		final SubMonitor m= SubMonitor.convert(monitor);
+		try {
+			final TmpUtils.Item tmpItem= TmpUtils.newItem("appvars", r, m); //$NON-NLS-1$
+			try {
+				evalExpression(tmpItem, (ICombinedRDataAdapter) r, m);
+			}
+			finally {
+				tmpItem.disposeChecked(m);
+			}
+		}
+		catch (final CoreException | UnexpectedRDataException e) {
+			final ToStringBuilder sb= new ObjectUtils.ToStringBuilder(
+					"An error occurred when evaluating the app variables expression." );
+			sb.addProp("expression", this.expression); //$NON-NLS-1$
+			RAppUIPlugin.log(new Status(IStatus.ERROR, RAppUIPlugin.BUNDLE_ID, 0,
+					sb.toString(), e ));
+			this.app.setData(new VariablesData(this.expression,
+					new Status(IStatus.ERROR, RAppUIPlugin.BUNDLE_ID, 0,
+							"Evaluation failed (internal error).", e )));
+		}
+		finally {
+			this.envs.clear();
+			
+			synchronized (this) {
+				final Queue queue= getTool().getQueue();
+				if (this.isScheduled) {
+					queue.removeHot(this);
+					this.isScheduled= false;
+				}
+			}
+		}
+	}
+	
+	private void evalExpression(final TmpUtils.Item tmpItem, final ICombinedRDataAdapter r,
+			final SubMonitor m) throws UnexpectedRDataException, CoreException {
+		final String valueName= tmpItem.createSub("value"); //$NON-NLS-1$
+		final RElementName valueElementName= RElementName.create(RElementName.MAIN_DEFAULT,
+				valueName );
+		final RElementName fqElementName= TmpUtils.createFQElementName(valueElementName);
+		
+		CombinedRElement element= null;
+		try {
+//			System.out.println("App Data Update= " + r.getChangeStamp());
+//			int nframes= RDataUtil.checkSingleIntValue(r.evalData("sys.nframe()", m));
+//			System.out.println("nframes= " + nframes);
+//			for (int i= 0; i < nframes + 1; i++) {
+//				String s= "evalq(shiny::getDefaultReactiveDomain(), envir=sys.frame(" + i + "L))";
+//				System.out.println("expr= " + s);
+//				
+//				element= r.evalCombinedStruct(s,
+//						0, RService.DEPTH_REFERENCE, fqElementName, m );
+//				
+//				System.out.println(((RReference)element).getRClassName());
+//			}
+			element= r.evalCombinedStruct(this.expression,
+					0, RService.DEPTH_REFERENCE, fqElementName, m );
+		}
+		catch (final CoreException e) {
+			final IStatus status= e.getStatus();
+			this.app.setData(new VariablesData(this.expression,
+					new Status(status.getSeverity(), RAppUIPlugin.BUNDLE_ID, 0,
+							"Evaluation failed: " + e.getLocalizedMessage(), e )));
+			return;
+		}
+		final RReference ref= RDataUtils.checkRReference(element);
+		
+		tmpItem.set(valueName, ref, m);
+		
+		element= r.getWorkspaceData().resolve(ref,
+				RWorkspace.RESOLVE_UPTODATE | RWorkspace.RESOLVE_RECURSIVE, 0, m);
+		if (element == null) {
+			element= r.findCombinedStruct(valueElementName, TmpUtils.ENV_FQ_ELEMENT_NAME, false,
+					0, RService.DEPTH_INFINITE, m );
+			if (element == null) {
+				throw new UnexpectedRDataException("null"); //$NON-NLS-1$
+			}
+		}
+		RElementName name= RElementName.parseDefault(this.expression);
+		if (name == null) {
+			name= RElementName.create(RElementName.MAIN_OTHER, "variables");
+		}
+		element= ICombinedRDataAdapter.createReference(element, name);
+		
+		this.app.setData(new VariablesData(this.expression,
+				ImCollections.newList(element) ));
+	}
+	
+//	private void check(final ICombinedRElement element,
+//			final SubMonitor m) throws CoreException {
+//		if (element instanceof ICombinedRList) {
+//			check((ICombinedRList) element, m);
+//		}
+//	}
+//	
+//	private void resolve(final RReference ref, final @Nullable RElementName fullName,
+//			final int loadOptions, final SubMonitor m) throws CoreException {
+//		if (!this.envs.add(ref.getHandle())) {
+//			return;
+//		}
+//		final ICombinedRElement element= this.workspace.resolve(ref,
+//				RWorkspace.RESOLVE_UPTODATE, loadOptions, m );
+//		check(element, m);
+//	}
+//	
+//	private void check(final ICombinedRList list,
+//			final SubMonitor m) throws CoreException {
+//		if (list.hasModelChildren(null)) {
+//			final long length= list.getLength();
+//			if (length <= Integer.MAX_VALUE) {
+//				final int l= (int) length;
+//				ITER_CHILDREN : for (int i= 0; i < l; i++) {
+//					final RObject object= list.get(i);
+//					if (object != null) {
+//						switch (object.getRObjectType()) {
+//						case RObject.TYPE_REFERENCE:
+//							if (((RReference) object).getReferencedRObjectType() == RObject.TYPE_ENV) {
+//								resolve((RReference) object, null, 0, m);
+//							}
+//							else {
+//							}
+//							continue ITER_CHILDREN;
+//						case RObject.TYPE_LIST:
+//						case RObject.TYPE_S4OBJECT:
+//							check((ICombinedRList) object, m);
+//							continue ITER_CHILDREN;
+//						default:
+//							continue ITER_CHILDREN;
+//						}
+//					}
+//				}
+//			}
+//			else {
+//				ITER_CHILDREN : for (long i= 0; i < length; i++) {
+//					final RObject object= list.get(i);
+//					if (object != null) {
+//						switch (object.getRObjectType()) {
+//						case RObject.TYPE_REFERENCE:
+//							if (((RReference) object).getReferencedRObjectType() == RObject.TYPE_ENV) {
+//								resolve((RReference) object, null, 0, m);
+//							}
+//							else {
+//							}
+//							continue ITER_CHILDREN;
+//						case RObject.TYPE_LIST:
+//						case RObject.TYPE_S4OBJECT:
+//							check((ICombinedRList) object, m);
+//							continue ITER_CHILDREN;
+//						default:
+//							continue ITER_CHILDREN;
+//						}
+//					}
+//				}
+//			}
+//		}
+//	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/shiny/actions/AppActionUtil.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/shiny/actions/AppActionUtil.java
new file mode 100644
index 0000000..f4767f7
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/shiny/actions/AppActionUtil.java
@@ -0,0 +1,76 @@
+/*=============================================================================#
+ # Copyright (c) 2015, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.shiny.actions;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchWindow;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.debug.ui.config.LaunchConfigManager;
+import org.eclipse.statet.ecommons.debug.ui.config.actions.ActionUtil;
+
+import org.eclipse.statet.internal.r.apps.core.shiny.RShinyResourceTester;
+import org.eclipse.statet.internal.r.apps.ui.RAppUIPlugin;
+import org.eclipse.statet.internal.r.apps.ui.launching.AppType;
+
+
+/**
+ * 
+ * element= app container
+ */
+@NonNullByDefault
+public class AppActionUtil extends ActionUtil<IContainer> {
+	
+	
+	private static final AppType APP_TYPE= new AppType("org.eclipse.statet.r.apps.appTypes.RShiny", //$NON-NLS-1$
+			"R Shiny App" );
+	
+	
+	public AppActionUtil(final byte initialMode) {
+		super(initialMode);
+	}
+	
+	
+	@Override
+	public @Nullable IContainer getLaunchElement(final @Nullable IEditorPart editor) {
+		IResource resource= null;
+		if (editor != null) {
+			resource= getSingleResource(editor.getEditorInput());
+		}
+		return (resource != null) ? RShinyResourceTester.getAppContainer(resource) : null;
+	}
+	
+	@Override
+	public @Nullable IContainer getLaunchElement(final @Nullable ISelection selection) {
+		IResource resource= null;
+		if (selection instanceof IStructuredSelection) {
+			resource= getSingleResource((IStructuredSelection) selection);
+		}
+		return (resource != null) ? RShinyResourceTester.getAppContainer(resource) : null;
+	}
+	
+	@Override
+	public @Nullable LaunchConfigManager<IContainer> getManager(final IWorkbenchWindow window,
+			final IContainer element) {
+		return RAppUIPlugin.getInstance().getRunAppConfigManager(APP_TYPE);
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/shiny/actions/AppLaunchShortcut.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/shiny/actions/AppLaunchShortcut.java
new file mode 100644
index 0000000..c40ec90
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/shiny/actions/AppLaunchShortcut.java
@@ -0,0 +1,34 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.shiny.actions;
+
+import org.eclipse.core.resources.IContainer;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+import org.eclipse.statet.ecommons.debug.ui.config.actions.ActionUtil;
+import org.eclipse.statet.ecommons.debug.ui.config.actions.RunActiveConfigLaunchShortcut;
+
+
+@NonNullByDefault
+public class AppLaunchShortcut extends RunActiveConfigLaunchShortcut<IContainer> {
+	
+	
+	public AppLaunchShortcut() {
+		super(new AppActionUtil(ActionUtil.ACTIVE_MENU_SELECTION_MODE));
+	}
+	
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/shiny/actions/RunActiveAppConfigWorkbenchHandler.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/shiny/actions/RunActiveAppConfigWorkbenchHandler.java
new file mode 100644
index 0000000..9106b56
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/shiny/actions/RunActiveAppConfigWorkbenchHandler.java
@@ -0,0 +1,70 @@
+/*=============================================================================#
+ # Copyright (c) 2008, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.shiny.actions;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExecutableExtension;
+import org.eclipse.ui.commands.IElementUpdater;
+
+import org.eclipse.statet.jcommons.collections.ImCollections;
+import org.eclipse.statet.jcommons.collections.ImIdentitySet;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.debug.ui.config.actions.ActionUtil;
+import org.eclipse.statet.ecommons.debug.ui.config.actions.RunActiveConfigScopeHandler;
+import org.eclipse.statet.ecommons.ui.actions.WorkbenchScopingHandler;
+
+
+/**
+ * Handlers for document output creation toolchain running with the active configuration.
+ */
+@NonNullByDefault
+public class RunActiveAppConfigWorkbenchHandler extends WorkbenchScopingHandler
+		implements IElementUpdater, IExecutableExtension {
+	
+	
+	private ImIdentitySet<String> launchFlags;
+	
+	
+	/** For instantiation via plugin.xml */
+	@SuppressWarnings("null")
+	public RunActiveAppConfigWorkbenchHandler() {
+	}
+	
+	
+	protected ImIdentitySet<String> getLaunchFlags() {
+		return this.launchFlags;
+	}
+	
+	
+	@Override
+	public void setInitializationData(final IConfigurationElement config,
+			final String propertyName, final @Nullable Object data) throws CoreException {
+		super.setInitializationData(config, propertyName, data);
+		
+		this.launchFlags= ImCollections.newIdentitySet();
+	}
+	
+	@Override
+	protected RunActiveConfigScopeHandler<IContainer> createScopeHandler(final Object scope) {
+		return new RunActiveConfigScopeHandler<>(scope, getCommandId(),
+				new AppActionUtil(ActionUtil.ACTIVE_EDITOR_MODE), getLaunchFlags() );
+	}
+	
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/shiny/actions/RunAppConfigsDropdownContribution.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/shiny/actions/RunAppConfigsDropdownContribution.java
new file mode 100644
index 0000000..c617577
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/shiny/actions/RunAppConfigsDropdownContribution.java
@@ -0,0 +1,43 @@
+/*=============================================================================#
+ # Copyright (c) 2007, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.shiny.actions;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.runtime.IExecutableExtension;
+import org.eclipse.ui.menus.IWorkbenchContribution;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+import org.eclipse.statet.ecommons.debug.ui.config.actions.RunConfigsDropdownContribution;
+
+
+@NonNullByDefault
+public class RunAppConfigsDropdownContribution extends RunConfigsDropdownContribution<IContainer>
+		implements IWorkbenchContribution, IExecutableExtension {
+	
+	
+	/** For instantiation via plugin.xml */
+	public RunAppConfigsDropdownContribution() {
+		super(new AppActionUtil(AppActionUtil.ACTIVE_EDITOR_MODE));
+	}
+	
+	
+	@Override
+	protected AppActionUtil getUtil() {
+		return (AppActionUtil) super.getUtil();
+	}
+	
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/shiny/actions/RunAppConfigsMenuContribution.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/shiny/actions/RunAppConfigsMenuContribution.java
new file mode 100644
index 0000000..4b08fc0
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/shiny/actions/RunAppConfigsMenuContribution.java
@@ -0,0 +1,89 @@
+/*=============================================================================#
+ # Copyright (c) 2007, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.shiny.actions;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.runtime.IExecutableExtension;
+import org.eclipse.ui.menus.IWorkbenchContribution;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+import org.eclipse.statet.ecommons.debug.ui.config.actions.ActionUtil;
+import org.eclipse.statet.ecommons.debug.ui.config.actions.RunConfigsMenuContribution;
+
+
+@NonNullByDefault
+public class RunAppConfigsMenuContribution extends RunConfigsMenuContribution<IContainer>
+		implements IWorkbenchContribution, IExecutableExtension {
+	
+	
+//	protected class AppConfigContribution extends ConfigContribution {
+//		
+//		
+//		public AppConfigContribution(final Image icon, final String label,
+//				final ILaunchConfiguration configuration) {
+//			super(icon, label, configuration);
+//		}
+//		
+//		
+//		@Override
+//		protected void fillMenu(final Menu menu) {
+//			addLaunchItems(menu);
+//			
+//			new MenuItem(menu, SWT.SEPARATOR);
+//			addActivateItem(menu, DocProcessingUI.ACTIONS_ACTIVATE_CONFIG_HELP_CONTEXT_ID);
+//			addEditItem(menu, DocProcessingUI.ACTIONS_EDIT_CONFIG_HELP_CONTEXT_ID);
+//		}
+//		
+//		protected void addLaunchItems(final Menu menu) {
+//		}
+//		
+//		
+//		protected String createDetail(final String inputExt, final String outputExt) {
+//			if (inputExt == null || outputExt == null) {
+//				return null;
+//			}
+//			final StringBuilder sb= getStringBuilder();
+//			sb.append("\u2002["); //$NON-NLS-1$
+//			sb.append(inputExt);
+//			sb.append("\u2002\u2192\u2002"); //$NON-NLS-1$
+//			sb.append(outputExt);
+//			sb.append("]"); //$NON-NLS-1$
+//			
+//			return sb.toString();
+//		}
+//		
+//	}
+	
+	
+	/** For instantiation via plugin.xml */
+	public RunAppConfigsMenuContribution() {
+		super(new AppActionUtil(ActionUtil.ACTIVE_MENU_SELECTION_MODE));
+	}
+	
+	
+	@Override
+	protected AppActionUtil getUtil() {
+		return (AppActionUtil) super.getUtil();
+	}
+	
+	
+//	@Override
+//	protected ConfigContribution createConfigContribution(
+//			Image icon, StringBuilder label, ILaunchConfiguration configuration) {
+//		return new AppConfigContribution(icon, label.toString(), configuration);
+//	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/variables/AppVarInput.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/variables/AppVarInput.java
new file mode 100644
index 0000000..4aa539d
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/variables/AppVarInput.java
@@ -0,0 +1,35 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.variables;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ltk.model.core.elements.IModelElement.Filter;
+import org.eclipse.statet.r.apps.ui.RApp;
+import org.eclipse.statet.r.ui.util.RElementInput;
+
+
+@NonNullByDefault
+public class AppVarInput extends RElementInput<RApp> {
+	
+	
+	public AppVarInput(final RApp app,
+			final @Nullable Filter envFilter, final @Nullable Filter otherFilter) {
+		super(app, envFilter, otherFilter);
+	}
+	
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/variables/AppVarView.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/variables/AppVarView.java
new file mode 100644
index 0000000..fcc9650
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/variables/AppVarView.java
@@ -0,0 +1,661 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.variables;
+
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
+import static org.eclipse.ui.IWorkbenchCommandConstants.NAVIGATE_COLLAPSE_ALL;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IPostSelectionProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+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.Menu;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbenchCommandConstants;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.commands.IElementUpdater;
+import org.eclipse.ui.handlers.CollapseAllHandler;
+import org.eclipse.ui.handlers.IHandlerService;
+import org.eclipse.ui.menus.CommandContributionItemParameter;
+import org.eclipse.ui.menus.UIElement;
+import org.eclipse.ui.part.ViewPart;
+import org.eclipse.ui.services.IServiceLocator;
+
+import org.eclipse.statet.jcommons.collections.CopyOnWriteIdentityListSet;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.ts.core.Tool;
+import org.eclipse.statet.ecommons.ts.core.util.ActiveToolListener;
+import org.eclipse.statet.ecommons.ts.core.util.ActiveToolListener.ActiveToolEvent;
+import org.eclipse.statet.ecommons.ts.core.util.ToolProvider;
+import org.eclipse.statet.ecommons.ui.actions.HandlerCollection;
+import org.eclipse.statet.ecommons.ui.actions.HandlerContributionItem;
+import org.eclipse.statet.ecommons.ui.actions.UIActions;
+import org.eclipse.statet.ecommons.ui.components.StatusInfo;
+import org.eclipse.statet.ecommons.ui.dialogs.DialogUtils;
+import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
+import org.eclipse.statet.ecommons.ui.util.UIAccess;
+import org.eclipse.statet.ecommons.ui.util.ViewActionUtil;
+import org.eclipse.statet.ecommons.ui.workbench.ContextHandlers;
+import org.eclipse.statet.ecommons.ui.workbench.WorkbenchUIUtils;
+
+import org.eclipse.statet.base.ui.StatetImages;
+import org.eclipse.statet.internal.r.apps.ui.RAppUIPlugin;
+import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditorCommandIds;
+import org.eclipse.statet.ltk.ui.util.ViewerDragSupport;
+import org.eclipse.statet.nico.core.runtime.ToolProcess;
+import org.eclipse.statet.nico.ui.IToolRegistry;
+import org.eclipse.statet.nico.ui.IToolRegistryListener;
+import org.eclipse.statet.nico.ui.NicoUI;
+import org.eclipse.statet.nico.ui.ToolSessionUIData;
+import org.eclipse.statet.nico.ui.actions.AbstractToolHandler;
+import org.eclipse.statet.r.apps.ui.AppRegistry;
+import org.eclipse.statet.r.apps.ui.AppRegistry.AppStateEvent;
+import org.eclipse.statet.r.apps.ui.RApp;
+import org.eclipse.statet.r.apps.ui.VariablesData;
+import org.eclipse.statet.r.console.core.RConsoleTool;
+import org.eclipse.statet.r.console.core.RProcess;
+import org.eclipse.statet.r.console.core.RProcessREnvironment;
+import org.eclipse.statet.r.ui.rtool.CopyRElementHandler;
+import org.eclipse.statet.r.ui.rtool.PrintRElementHandler;
+import org.eclipse.statet.r.ui.rtool.RElementViewerDragSourceListener;
+import org.eclipse.statet.r.ui.util.CopyRElementNameHandler;
+import org.eclipse.statet.r.ui.util.RElementInputContentProvider;
+import org.eclipse.statet.r.ui.util.RElementInputLabelProvider;
+import org.eclipse.statet.r.ui.util.RElementInputUtils;
+import org.eclipse.statet.rj.data.RReference;
+
+
+@NonNullByDefault
+public class AppVarView extends ViewPart implements ToolProvider {
+	
+	
+	public static final String VIEW_ID= "org.eclipse.statet.r.apps.views.VariableViewer"; //$NON-NLS-1$
+	
+	
+	private static final String REFRESH_COMMAND_ID= IWorkbenchCommandConstants.FILE_REFRESH;
+	
+	private static final String FILTER_INCLUDE_INTERNAL_COMMAND_ID= "Filter.IncludeInternal"; //$NON-NLS-1$
+	
+	private static final String PRINT_COMMAND_ID= "org.eclipse.statet.r.commands.RunPrintInR"; //$NON-NLS-1$
+	
+	
+	private static final String FILTER_INCLUDE_INTERNAL_SETTINGS_KEY= "Filter.IncludeInternal.enabled"; //$NON-NLS-1$
+	
+	
+	private class RefreshHandler extends AbstractToolHandler<RProcess> {
+		
+		public RefreshHandler() {
+			super(RConsoleTool.TYPE, null, AppVarView.this, getSite());
+			init();
+		}
+		
+		
+		@Override
+		protected boolean evaluateIsEnabled(final RProcess tool, final @Nullable Object evaluationContext) {
+			return (super.evaluateIsEnabled(tool, evaluationContext)
+					&& getApp() != null );
+		}
+		
+		protected void refreshElements() {
+			WorkbenchUIUtils.refreshCommandElements(REFRESH_COMMAND_ID, this, null);
+		}
+		
+		@Override
+		protected @Nullable Object execute(final RProcess tool, final ExecutionEvent event) {
+			final RApp app= getApp();
+			if (app != null) {
+				app.refreshVariables();
+			}
+			return null;
+		}
+		
+	}
+	
+	private class FilterInternalHandler extends AbstractHandler implements IElementUpdater {
+		
+		@Override
+		public @Nullable Object execute(final ExecutionEvent event) throws ExecutionException {
+			AppVarView.this.filterIncludeInternal= !AppVarView.this.filterIncludeInternal;
+			AppVarView.this.settings.put(FILTER_INCLUDE_INTERNAL_SETTINGS_KEY, AppVarView.this.filterIncludeInternal);
+			updateFilter();
+			return null;
+		}
+		
+		@Override
+		public void updateElement(final UIElement element, final Map parameters) {
+			WorkbenchUIUtils.aboutToUpdateCommandsElements(this, element);
+			try {
+				element.setChecked(AppVarView.this.filterIncludeInternal);
+			}
+			finally {
+				WorkbenchUIUtils.finalizeUpdateCommandsElements(this);
+			}
+		}
+		
+	}
+	
+	
+	private IDialogSettings settings;
+	
+	final Object sourceLock= new Object();
+	private IToolRegistryListener toolRegistryListener;
+	private @Nullable RProcess process;
+	private final CopyOnWriteIdentityListSet<ActiveToolListener> toolListeners= new CopyOnWriteIdentityListSet<>();
+	
+	private AppRegistry.Listener appRegistryListener;
+	private @Nullable RApp app;
+	
+	
+	private TreeViewer treeViewer;
+	
+	private final ContentJob inputUpdater= new ContentJob(this);
+	private boolean isUpdating;
+	
+	private boolean filterIncludeInternal;
+	private String filterText;
+	
+	private RElementInputContentProvider<AppVarInput> inputContentProvider;
+	
+	
+	private ViewActionUtil actionUtil;
+	private ContextHandlers handlers;
+	
+	private @Nullable Object currentInfoObject;
+	
+	
+	private @Nullable RApp shownByLauncher;
+	
+	
+	@SuppressWarnings("null")
+	public AppVarView() {
+	}
+	
+	@Override
+	public void dispose() {
+		if (this.appRegistryListener != null) {
+			AppRegistry.getInstance().removeListener(this.appRegistryListener);
+			this.appRegistryListener= null;
+		}
+		if (this.toolRegistryListener != null) {
+			NicoUI.getToolRegistry().removeListener(this.toolRegistryListener);
+			this.toolRegistryListener= null;
+		}
+		setTool(null, false);
+		
+		if (this.handlers != null) {
+			this.handlers.dispose();
+			this.handlers= null;
+		}
+		
+		super.dispose();
+	}
+	
+	
+	@Override
+	public void init(final IViewSite site, final @Nullable IMemento memento) throws PartInitException {
+		super.init(site, memento);
+		
+		this.settings= DialogUtils.getDialogSettings(RAppUIPlugin.getInstance(), "AppVarBrowser");
+		
+		this.filterIncludeInternal= this.settings.getBoolean(FILTER_INCLUDE_INTERNAL_SETTINGS_KEY);
+	}
+	
+	@Override
+	public void createPartControl(final Composite parent) {
+		parent.setLayout(LayoutUtils.newSashGrid());
+		
+		this.treeViewer= createTreeViewer(parent);
+		this.treeViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+		
+		final IPostSelectionProvider treeSelectionProvider= this.treeViewer;
+		treeSelectionProvider.addPostSelectionChangedListener(new ISelectionChangedListener() {
+			@Override
+			public void selectionChanged(final SelectionChangedEvent event) {
+				updateSelectionInfo((ITreeSelection) event.getSelection());
+			}
+		});
+		
+		final IViewSite site= getViewSite();
+		site.setSelectionProvider(treeSelectionProvider);
+		this.actionUtil= new ViewActionUtil(this);
+		this.handlers= new ContextHandlers(site.getService(IHandlerService.class));
+		initActions(site, this.handlers);
+		contributeToActionBars(site, site.getActionBars(), this.handlers);
+		hookContextMenu();
+		
+		// listen on console changes
+		final IToolRegistry toolRegistry= NicoUI.getToolRegistry();
+		this.toolRegistryListener= new IToolRegistryListener() {
+			@Override
+			public void toolSessionActivated(final ToolSessionUIData sessionData) {
+				final ToolProcess process= sessionData.getProcess();
+				UIAccess.getDisplay().syncExec(new Runnable() {
+					@Override
+					public void run() {
+						setTool(process, true);
+					}
+				});
+			}
+			@Override
+			public void toolTerminated(final ToolSessionUIData sessionData) {
+				final ToolProcess process= sessionData.getProcess();
+				UIAccess.getDisplay().syncExec(new Runnable() {
+					@Override
+					public void run() {
+						if (process == getTool()) {
+							setTool(null, true);
+						}
+					}
+				});
+			}
+		};
+		toolRegistry.addListener(this.toolRegistryListener, getViewSite().getPage());
+		
+		this.appRegistryListener= new AppRegistry.Listener() {
+			@Override
+			public void onAppStateChanged(final AppStateEvent event) {
+				UIAccess.getDisplay(getSite().getShell()).asyncExec(() -> {
+					switch (event.getType()) {
+					case AppRegistry.APP_STARTED:
+						if (event.getApp().getTool() == getTool()) {
+							setApp(event.getApp(), true);
+						}
+						break;
+					case AppRegistry.APP_STOPPED:
+						if (event.getApp() == getApp()) {
+							setApp(null, true);
+						}
+						break;
+					default:
+						break;
+					}
+				});
+			}
+		};
+		AppRegistry.getInstance().addListener(this.appRegistryListener);
+		
+		setTool(toolRegistry.getActiveToolSession(getViewSite().getPage()).getProcess(), true);
+	}
+	
+	private TreeViewer createTreeViewer(final Composite parent) {
+		final TreeViewer viewer= new TreeViewer(parent, SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI);
+		
+		viewer.setLabelProvider(new RElementInputLabelProvider());
+		
+		viewer.setUseHashlookup(true);
+		this.inputContentProvider= new RElementInputContentProvider();
+		viewer.setContentProvider(this.inputContentProvider);
+		viewer.setInput(this);
+		
+		return viewer;
+	}
+	
+	protected void initActions(final IServiceLocator serviceLocator, final ContextHandlers handlers) {
+		handlers.addActivate(REFRESH_COMMAND_ID, new RefreshHandler());
+		
+		final CopyRElementHandler copyHandler= new CopyRElementHandler(this.actionUtil,
+				(ILabelProvider) this.treeViewer.getLabelProvider() );
+		handlers.addActivate(IWorkbenchCommandConstants.EDIT_COPY, copyHandler);
+		handlers.addActivate(ISourceEditorCommandIds.COPY_ELEMENT_NAME,
+				new CopyRElementNameHandler(this.actionUtil) );
+		
+		final ViewerDragSupport dragSupport= new ViewerDragSupport(this.treeViewer);
+		dragSupport.addDragSourceListener(new RElementViewerDragSourceListener(
+				copyHandler, this.treeViewer ));
+		dragSupport.init();
+		
+		handlers.addActivate(PRINT_COMMAND_ID,
+				new PrintRElementHandler(this.actionUtil) );
+		
+		handlers.add(FILTER_INCLUDE_INTERNAL_COMMAND_ID,
+				new FilterInternalHandler() );
+		
+		handlers.addActivate(NAVIGATE_COLLAPSE_ALL,
+				new CollapseAllHandler(this.treeViewer) );
+		RElementInputUtils.addDoubleClickExpansion(this.treeViewer);
+	}
+	
+	protected void contributeToActionBars(final IServiceLocator serviceLocator,
+			final IActionBars actionBars, final HandlerCollection handlers) {
+		final IMenuManager menuManager= actionBars.getMenuManager();
+		final IToolBarManager toolbarManager= actionBars.getToolBarManager();
+		
+		menuManager.add(new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						null, HandlerContributionItem.NO_COMMAND_ID, null,
+						null, null, null,
+						"Show &Internal Variables ('.*')", null, null,
+						HandlerContributionItem.STYLE_CHECK, null, false ),
+				nonNullAssert(handlers.get(FILTER_INCLUDE_INTERNAL_COMMAND_ID)) ));
+		
+		menuManager.add(new Separator());
+		menuManager.add(new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						"Refresh", REFRESH_COMMAND_ID, null, //$NON-NLS-1$
+						StatetImages.getDescriptor(StatetImages.TOOL_REFRESH), StatetImages.getDescriptor(StatetImages.TOOLD_REFRESH), null,
+						"&Refresh", null, null,
+						HandlerContributionItem.STYLE_PUSH, null, false ),
+				handlers ));
+		
+		toolbarManager.add(new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						null, CollapseAllHandler.COMMAND_ID, null,
+						null, null, null,
+						null, null, null,
+						HandlerContributionItem.STYLE_PUSH, null, false ),
+				nonNullAssert(handlers.get(NAVIGATE_COLLAPSE_ALL)) ));
+	}
+	
+	private void hookContextMenu() {
+		final MenuManager menuManager= new MenuManager("ContextMenu", //$NON-NLS-1$
+				"org.eclipse.statet.r.apps.menus.VariablesViewContextMenu" ); //$NON-NLS-1$
+		menuManager.setRemoveAllWhenShown(true);
+		menuManager.addMenuListener(this::fillContextMenu);
+		final Menu contextMenu= menuManager.createContextMenu(this.treeViewer.getTree());
+		this.treeViewer.getTree().setMenu(contextMenu);
+		getSite().registerContextMenu(menuManager, this.treeViewer);
+	}
+	
+	private void fillContextMenu(final IMenuManager m) {
+		final IServiceLocator serviceLocator= getSite();
+		final ContextHandlers handlers= this.handlers;
+		
+		m.add(new Separator(UIActions.EDIT_GROUP_ID));
+		m.add(new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						"Copy", IWorkbenchCommandConstants.EDIT_COPY, null, //$NON-NLS-1$
+						null, null, null,
+						null, null, null,
+						HandlerContributionItem.STYLE_PUSH, null, false ),
+				handlers ));
+		m.add(new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						"Copy.ElementName", ISourceEditorCommandIds.COPY_ELEMENT_NAME, null, //$NON-NLS-1$
+						null, null, null,
+						null, null, null,
+						HandlerContributionItem.STYLE_PUSH, null, false ),
+				handlers ));
+		m.add(new Separator());
+		m.add(new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						null, PRINT_COMMAND_ID, null,
+						null, null, null,
+						null, null, null,
+						HandlerContributionItem.STYLE_PUSH, null, false ),
+				handlers ));
+		
+		m.add(new Separator(UIActions.ADDITIONS_GROUP_ID));
+	}
+	
+	@Override
+	public void setFocus() {
+		this.treeViewer.getControl().setFocus();
+	}
+	
+	
+	/** UI thread only (called by update job) */
+	void updateView(final @Nullable AppVarInput input,
+			final @Nullable List<RProcessREnvironment> updateEnvirs) {
+		if (!UIAccess.isOkToUse(this.treeViewer)) {
+			return;
+		}
+		this.isUpdating= true;
+		
+//		this.hoveringController.stop();
+		
+		this.inputContentProvider.setInput(input);
+		
+		final Set<RReference> previousReferences= this.inputContentProvider.resetUsedReferences();
+		if (input != null && updateEnvirs != null) {
+			for (final RProcessREnvironment entry : updateEnvirs) {
+				this.treeViewer.refresh(entry, true);
+			}
+			if (!previousReferences.isEmpty()) {
+				final Set<RReference> usedReferences= this.inputContentProvider.getUsedReferences();
+				ITER_REFS: for (final RReference reference : previousReferences) {
+					if (!usedReferences.contains(reference)) {
+						// Update the envir copy in the viewer, if it refers to an updated envir
+						for (final RProcessREnvironment entry : updateEnvirs) {
+							if (entry.getHandle() == reference.getHandle()) {
+								this.treeViewer.refresh(reference, true);
+								// reference is readded automatically to new set, if necessary
+								continue ITER_REFS;
+							}
+						}
+						// Keep/readd the reference, if it refers to an envir in the search path
+//						for (final ICombinedREnvironment entry : input.searchEnvirs) {
+//							if (entry.getHandle() == reference.getHandle()) {
+//								usedReferences.add(reference);
+//								continue ITER_REFS;
+//							}
+//						}
+					}
+				}
+			}
+		}
+		else {
+			this.treeViewer.refresh(true);
+		}
+		
+		updateSelectionInfo((ITreeSelection) this.actionUtil.getSelectionProvider().getSelection());
+	}
+	
+	private void updateFilter() {
+		this.inputUpdater.schedule();
+	}
+	
+	private void clearActionInfo() {
+		this.actionUtil.getStatusLine().clearAll();
+	}
+	
+	private void updateSelectionInfo(final ITreeSelection selection) {
+		if (this.isUpdating) {
+			return;
+		}
+		final Object infoObject= null;
+		final String message= null;
+		
+//		if (tool != null && !selection.isEmpty()) {
+//			if (selection.size() == 1) {
+//				final TreePath treePath= selection.getPaths()[0];
+//				final IElementName elementName= getFQElementName(treePath);
+//				final String name= (elementName != null) ? elementName.getDisplayName() : null;
+//				if (name != null) {
+//					infoObject= selection.getFirstElement();
+//					message= name;
+//				}
+//			}
+//			else {
+//				message= NLS.bind("{0} items selected", selection.size());
+//			}
+//			if (message != null) {
+//				message= NLS.bind("{0}  \u2012  {1}", message, tool.getLabel(ITool.DEFAULT_LABEL)); //$NON-NLS-1$
+//			}
+//		}
+		
+		if (infoObject == null || !infoObject.equals(this.currentInfoObject)) {
+			clearActionInfo();
+		}
+		this.currentInfoObject= infoObject;
+		this.actionUtil.getStatusLine().setSelectionMessage(
+				(message != null) ? new StatusInfo(IStatus.OK, message) : null );
+	}
+	
+	
+	@Override
+	public @Nullable RProcess getTool() {
+		return this.process;
+	}
+	
+	@Override
+	public void addToolListener(final ActiveToolListener action) {
+		this.toolListeners.add(action);
+	}
+	
+	@Override
+	public void removeToolListener(final ActiveToolListener action) {
+		this.toolListeners.remove(action);
+	}
+	
+	/** UI thread only */
+	private void setTool(final @Nullable ToolProcess tool, final boolean update) {
+		final RProcess process= (tool != null
+						&& tool.isProvidingFeatureSet(RConsoleTool.R_DATA_FEATURESET_ID)
+						&& !tool.isTerminated() ) ?
+				(RProcess) tool : null;
+		if (this.process == tool) {
+			return;
+		}
+		final RProcess oldProcess= process;
+		synchronized (this.sourceLock) {
+			this.process= process;
+		}
+		
+		final ActiveToolEvent event= new ActiveToolEvent(ActiveToolEvent.TOOL_ACTIVATED, process);
+		for (final ActiveToolListener listener : this.toolListeners) {
+			listener.onToolChanged(event);
+		}
+		
+		setApp((process != null) ? AppRegistry.getInstance().getApp(process) : null, update);
+	}
+	
+	public @Nullable RApp getApp() {
+		return this.app;
+	}
+	
+	/** UI thread only */
+	private void setApp(@Nullable RApp app, final boolean update) {
+		if (app != null && app.getVariables() == null) {
+			app= null;
+		}
+		if (this.app == app) {
+			return;
+		}
+		
+		final RApp oldApp= this.app;
+		if (oldApp != null) {
+			oldApp.removeListener(this.inputUpdater);
+		}
+		synchronized (this.sourceLock) {
+			this.app= app;
+		}
+		clearActionInfo();
+		
+		this.inputUpdater.forceUpdate(app);
+		if (app != null) {
+			setContentDescription(computeContentDescription(app));
+			app.addListener(this.inputUpdater);
+		}
+		else {
+			setContentDescription("No app at this time.");
+		}
+		if (update) {
+			this.inputUpdater.schedule();
+			
+			if (app == null && oldApp == this.shownByLauncher) {
+				Display.getCurrent().timerExec(200, () -> {
+					showPreviousView(oldApp);
+				});
+			}
+		}
+	}
+	
+	private String computeContentDescription(final RApp app) {
+		final StringBuilder sb= new StringBuilder();
+		
+		final VariablesData vars= app.getVariables();
+		if (vars != null) {
+			sb.append(vars.getExpression());
+		}
+		else {
+			sb.append("<no available>");
+		}
+		
+		final IResource resource= app.getResource();
+		if (resource != null) {
+			if (sb.length() > 0) {
+				sb.append("\u2002\u2013\u2002"); //$NON-NLS-1$
+			}
+			sb.append(resource.getFullPath().toString());
+		}
+		
+		return sb.toString();
+	}
+	
+	
+	public boolean getFilterIncludeInternal() {
+		return this.filterIncludeInternal;
+	}
+	
+	public String getFilterSearchText() {
+		return this.filterText;
+	}
+	
+	
+	public void setShownByLauncher(final RApp app) {
+		this.shownByLauncher= app;
+	}
+	
+	private void showPreviousView(final RApp oldApp) {
+		if (this.app == null && oldApp == this.shownByLauncher) {
+			final IWorkbenchPage page= getSite().getPage();
+			final IViewPart[] viewStack= page.getViewStack(this);
+			if (viewStack != null && viewStack.length >= 2 && viewStack[0] == this) {
+				page.bringToTop(viewStack[1]);
+			}
+		}
+	}
+	
+	
+	@Override
+	@SuppressWarnings("unchecked")
+	public <T> T getAdapter(final Class<T> adapterType) {
+		if (adapterType == Control.class) {
+			return (T) this.treeViewer.getTree();
+		}
+		if (adapterType == Tool.class) {
+			return (T) this.process;
+		}
+		return super.getAdapter(adapterType);
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/variables/ContentJob.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/variables/ContentJob.java
new file mode 100644
index 0000000..3386438
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/variables/ContentJob.java
@@ -0,0 +1,265 @@
+/*=============================================================================#
+ # Copyright (c) 2009, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.variables;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.ui.dialogs.SearchPattern;
+import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
+
+import org.eclipse.statet.jcommons.lang.NonNull;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.ui.util.UIAccess;
+
+import org.eclipse.statet.ltk.model.core.elements.IModelElement;
+import org.eclipse.statet.r.apps.ui.RApp;
+import org.eclipse.statet.r.apps.ui.RApp.AppEvent;
+import org.eclipse.statet.r.apps.ui.VariablesData;
+import org.eclipse.statet.r.console.core.RProcessREnvironment;
+import org.eclipse.statet.r.core.data.CombinedRElement;
+import org.eclipse.statet.r.ui.util.RNameSearchPattern;
+
+
+@NonNullByDefault
+class ContentJob extends Job implements RApp.Listener {
+	
+	
+	static class ContentFilter implements IModelElement.Filter {
+		
+		private final boolean filterInternal;
+		private final @Nullable SearchPattern searchPattern;
+		
+		
+		public ContentFilter(final boolean filterInternal, final @Nullable SearchPattern pattern) {
+			this.filterInternal= filterInternal;
+			this.searchPattern= pattern;
+		}
+		
+		
+		@Override
+		public boolean include(final IModelElement element) {
+			final String name= element.getElementName().getSegmentName();
+			if (name != null) {
+				if (this.filterInternal && name.length() > 0 && name.charAt(0) == '.') {
+					return false;
+				}
+				return (this.searchPattern == null || this.searchPattern.matches(name));
+			}
+			else {
+				return true;
+			}
+		}
+		
+	}
+	
+	
+	private final AppVarView view;
+	
+	/** the app to update */
+	private @Nullable RApp updateSource;
+	/** the app of last update */
+	private @Nullable RApp lastSource;
+	/** update all environment */
+	private boolean force;
+	
+	private @Nullable VariablesData rawInput;
+	
+	private volatile boolean isScheduled;
+	
+	
+	public ContentJob(final AppVarView view) {
+		super("R Object Browser Update");
+		this.view= view;
+		setSystem(true);
+		setUser(false);
+	}
+	
+	
+	@Override
+	public void onVariablesChanged(final AppEvent event) {
+		schedule(event.getApp());
+	}
+	
+	public void forceUpdate(final @Nullable RApp app) {
+		synchronized (this.view.sourceLock) {
+			if (app != this.view.getApp()) {
+				return;
+			}
+			this.updateSource= app;
+			this.force= true;
+		}
+	}
+	
+	public void schedule(final RApp app) {
+		if (app != null) {
+			synchronized (this.view.sourceLock) {
+				if (app != this.view.getApp()) {
+					return;
+				}
+				this.updateSource= app;
+			}
+		}
+		schedule();
+	}
+	
+	@Override
+	public boolean shouldSchedule() {
+		this.isScheduled= true;
+		return true;
+	}
+	
+	@Override
+	protected IStatus run(final IProgressMonitor monitor) {
+		if (!this.isScheduled) {
+			return Status.CANCEL_STATUS;
+		}
+		
+		final IWorkbenchSiteProgressService progressService= this.view.getViewSite().getService(IWorkbenchSiteProgressService.class);
+		if (progressService != null) {
+			progressService.incrementBusy();
+		}
+		
+		try {
+			final RApp app;
+			final boolean sourceChanged;
+			final boolean updateInput;
+			synchronized (this.view.sourceLock) {
+				this.isScheduled= false;
+				
+				app= this.view.getApp();
+				sourceChanged= (app != this.lastSource);
+				updateInput= (sourceChanged || this.updateSource != null);
+				this.lastSource= app;
+				this.updateSource= null;
+				this.force= false;
+			}
+			
+			final AppVarInput input= (app != null) ? createInput(app) : null;
+			
+			// Update input and refresh
+			final List<RProcessREnvironment> toUpdate;
+			if (updateInput) {
+				toUpdate= updateFromSource(input);
+			}
+			else {
+				toUpdate= null;
+			}
+			
+			prepare(input);
+			
+			synchronized (this.view.sourceLock) {
+				if (app != this.view.getApp()) {
+					this.lastSource= null;
+					return Status.CANCEL_STATUS;
+				}
+				if ((!sourceChanged && this.isScheduled) || monitor.isCanceled()) {
+					return Status.CANCEL_STATUS;
+				}
+			}
+			UIAccess.getDisplay().syncExec(() -> {
+				if (app != ContentJob.this.view.getApp()) {
+					return;
+				}
+				ContentJob.this.view.updateView(input, toUpdate);
+			});
+			
+			return Status.OK_STATUS;
+		}
+		finally {
+			if (progressService != null) {
+				progressService.decrementBusy();
+			}
+		}
+	}
+	
+	private AppVarInput createInput(final RApp source) {
+		final boolean filterInternal= !this.view.getFilterIncludeInternal();
+		final String filterText= this.view.getFilterSearchText();
+		IModelElement.Filter envFilter;
+		IModelElement.Filter otherFilter;
+		if (filterText != null && filterText.length() > 0) {
+			final SearchPattern filterPattern= new RNameSearchPattern();
+			filterPattern.setPattern(filterText);
+			envFilter= new ContentFilter(filterInternal, filterPattern);
+			otherFilter= (filterInternal) ? new ContentFilter(filterInternal, null) : null;
+		}
+		else if (filterInternal) {
+			envFilter= new ContentFilter(filterInternal, null);
+			otherFilter= new ContentFilter(filterInternal, null);
+		}
+		else {
+			envFilter= null;
+			otherFilter= null;
+		}
+		return new AppVarInput(source, envFilter, otherFilter);
+	}
+	
+	private @Nullable List<RProcessREnvironment> updateFromSource(final @Nullable AppVarInput input) {
+		if (input == null) {
+			this.rawInput= null;
+			return null;
+		}
+		
+		final RApp app= input.getSource();
+		final VariablesData oldInput= this.rawInput;
+		final VariablesData data= app.getVariables();
+		this.rawInput= data;
+		if (data != null) {
+			// If search path (environments) is not changed and not in force mode, refresh only the updated entries
+			final List<RProcessREnvironment> updateEntries= null;
+			
+			return updateEntries;
+		}
+		else {
+			return null;
+		}
+	}
+	
+	private void prepare(final @Nullable AppVarInput input) {
+		final VariablesData rawInput= this.rawInput;
+		if (input == null || rawInput == null) {
+			return;
+		}
+		
+		@NonNull CombinedRElement[] array;
+		
+		final List<? extends CombinedRElement> elements= rawInput.getElements();
+		if (elements != null) {
+//			if (elements.size() == 1) {
+//				ICombinedRElement single= elements.get(0);
+//			}
+			
+			array= elements.toArray(new @NonNull CombinedRElement[elements.size()]);
+			
+			if (input.hasEnvFilter()) { // prepare env filter
+				for (int i= 0; i < array.length; i++) {
+					input.getEnvChildren(array[i]);
+				}
+			}
+		}
+		else {
+			array= null;
+		}
+		
+		input.setRootElements(array);
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/viewer/AppBrowserPage.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/viewer/AppBrowserPage.java
new file mode 100644
index 0000000..bf3c63c
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/viewer/AppBrowserPage.java
@@ -0,0 +1,153 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.viewer;
+
+import static org.eclipse.statet.ecommons.debug.ui.ECommonsDebugUI.TERMINATE_COMMAND_ID;
+import static org.eclipse.statet.ecommons.debug.ui.ECommonsDebugUI.TERMINATE_RELAUNCH_COMMAND_ID;
+import static org.eclipse.statet.internal.r.apps.ui.viewer.AppBrowserView.APP_CONTROL_GROUP_ID;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.menus.CommandContributionItemParameter;
+import org.eclipse.ui.services.IServiceLocator;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.ui.actions.HandlerCollection;
+import org.eclipse.statet.ecommons.ui.actions.HandlerContributionItem;
+import org.eclipse.statet.ecommons.ui.mpbv.PageBookBrowserPage;
+import org.eclipse.statet.ecommons.ui.workbench.ContextHandlers;
+
+import org.eclipse.statet.internal.r.apps.ui.Messages;
+import org.eclipse.statet.r.apps.ui.RApp;
+
+
+@NonNullByDefault
+public class AppBrowserPage extends PageBookBrowserPage {
+	
+	
+	private class StopAppHandler extends AbstractHandler {
+		
+		@Override
+		public void setEnabled(final @Nullable Object evaluationContext) {
+			final AppBrowserSession session= getSession();
+			final RApp app= session.getLatestApp();
+			setBaseEnabled(app != null && app.canStopApp());
+		}
+		
+		@Override
+		public @Nullable Object execute(final ExecutionEvent event) throws ExecutionException {
+			final AppBrowserSession session= getSession();
+			final RApp app= session.getLatestApp();
+			if (app != null) {
+				app.stopApp();
+			}
+			return null;
+		}
+		
+	}
+	
+	private class RestartAppHandler extends AbstractHandler {
+		
+		@Override
+		public void setEnabled(final @Nullable Object evaluationContext) {
+			final AppBrowserSession session= getSession();
+			final RApp app= session.getLatestApp();
+			setBaseEnabled(app != null && app.canRestartApp());
+		}
+		
+		@Override
+		public @Nullable Object execute(final ExecutionEvent event) throws ExecutionException {
+			try {
+				final AppBrowserSession session= getSession();
+				final RApp app= session.getLatestApp();
+				if (app != null) {
+					app.restartApp(getSite().getPage());
+				}
+				return null;
+			}
+			catch (final CoreException e) {
+				// show message?
+				return null;
+			}
+		}
+		
+	}
+	
+	
+	public AppBrowserPage(final AppBrowserView view, final AppBrowserSession session) {
+		super(view, session);
+	}
+	
+	@Override
+	public void dispose() {
+		super.dispose();
+	}
+	
+	
+	@Override
+	public AppBrowserSession getSession() {
+		return (AppBrowserSession) super.getSession();
+	}
+	
+	
+	@Override
+	protected @Nullable Control createAddressBar(final Composite parent) {
+		return null;
+	}
+	
+	@Override
+	protected void initActions(final IServiceLocator serviceLocator, final ContextHandlers handlers) {
+		super.initActions(serviceLocator, handlers);
+		
+		handlers.addActivate(TERMINATE_COMMAND_ID, new StopAppHandler());
+		handlers.addActivate(TERMINATE_RELAUNCH_COMMAND_ID, new RestartAppHandler());
+	}
+	
+	@Override
+	protected void contributeToActionBars(final IServiceLocator serviceLocator,
+			final IActionBars actionBars, final HandlerCollection handlers) {
+		super.contributeToActionBars(serviceLocator, actionBars, handlers);
+		
+		final IToolBarManager toolBarManager= actionBars.getToolBarManager();
+		
+		toolBarManager.appendToGroup(APP_CONTROL_GROUP_ID, new HandlerContributionItem(
+				new CommandContributionItemParameter(
+						serviceLocator, TERMINATE_RELAUNCH_COMMAND_ID,
+						TERMINATE_RELAUNCH_COMMAND_ID, null,
+						null, null, null,
+						Messages.Action_RestartApp_label, null, Messages.Action_RestartApp_label,
+						HandlerContributionItem.STYLE_PUSH,
+						null, false ),
+				handlers ));
+		toolBarManager.appendToGroup(APP_CONTROL_GROUP_ID, new HandlerContributionItem(
+				new CommandContributionItemParameter(
+						serviceLocator, TERMINATE_COMMAND_ID,
+						TERMINATE_COMMAND_ID, null,
+						null, null, null,
+						Messages.Action_StopApp_label, null, Messages.Action_StopApp_label,
+						HandlerContributionItem.STYLE_PUSH,
+						null, false ),
+				handlers ));
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/viewer/AppBrowserSession.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/viewer/AppBrowserSession.java
new file mode 100644
index 0000000..f4695b5
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/viewer/AppBrowserSession.java
@@ -0,0 +1,77 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.viewer;
+
+import java.net.URL;
+
+import org.eclipse.core.resources.IResource;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.ts.core.Tool;
+import org.eclipse.statet.ecommons.ui.mpbv.BrowserSession;
+
+import org.eclipse.statet.r.apps.ui.AppRegistry;
+import org.eclipse.statet.r.apps.ui.RApp;
+
+
+@NonNullByDefault
+public class AppBrowserSession extends BrowserSession {
+	
+	
+	private final URL id;
+	
+	
+	public AppBrowserSession(final URL id) {
+		this.id= id;
+	}
+	
+	
+	@Override
+	public URL getId() {
+		return this.id;
+	}
+	
+	@Override
+	public String getLabel() {
+		final StringBuilder sb= new StringBuilder();
+		sb.append(super.getLabel());
+		
+		final RApp app= getLatestApp();
+		if (app != null) {
+			final IResource resource= app.getResource();
+			if (resource != null) {
+				if (sb.length() > 0) {
+					sb.append("\u2002\u2013\u2002"); //$NON-NLS-1$
+				}
+				sb.append(resource.getFullPath().toString());
+			}
+			
+			final Tool tool= app.getTool();
+			if (tool != null) {
+				sb.append(" │ "); //$NON-NLS-1$
+				sb.append(tool.getLabel(Tool.DEFAULT_LABEL));
+			}
+		}
+		
+		return sb.toString();
+	}
+	
+	public @Nullable RApp getLatestApp() {
+		return AppRegistry.getInstance().getApp(this.id);
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/viewer/AppBrowserView.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/viewer/AppBrowserView.java
new file mode 100644
index 0000000..3fb628c
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/internal/r/apps/ui/viewer/AppBrowserView.java
@@ -0,0 +1,109 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.apps.ui.viewer;
+
+import java.net.URL;
+
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.services.IServiceLocator;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+import org.eclipse.statet.ecommons.ui.actions.HandlerCollection;
+import org.eclipse.statet.ecommons.ui.actions.UIActions;
+import org.eclipse.statet.ecommons.ui.mpbv.BrowserSession;
+import org.eclipse.statet.ecommons.ui.mpbv.PageBookBrowserPage;
+import org.eclipse.statet.ecommons.ui.mpbv.PageBookBrowserView;
+import org.eclipse.statet.ecommons.ui.util.UIAccess;
+
+import org.eclipse.statet.r.apps.ui.AppRegistry;
+import org.eclipse.statet.r.apps.ui.AppRegistry.AppStateEvent;
+
+
+@NonNullByDefault
+public class AppBrowserView extends PageBookBrowserView {
+	
+	
+	public static final String VIEW_ID= "org.eclipse.statet.r.apps.views.AppViewer"; //$NON-NLS-1$
+	
+	public static final String APP_CONTROL_GROUP_ID= "app.Control"; //$NON-NLS-1$
+	
+	
+	private AppRegistry.Listener appRegistryListener;
+	
+	
+	/** plugin.xml */
+	public AppBrowserView() {
+		super();
+	}
+	
+	@Override
+	public void dispose() {
+		if (this.appRegistryListener != null) {
+			AppRegistry.getInstance().removeListener(this.appRegistryListener);
+			this.appRegistryListener= null;
+		}
+		
+		super.dispose();
+	}
+	
+	
+	@Override
+	public void createPartControl(final Composite parent) {
+		super.createPartControl(parent);
+		
+		this.appRegistryListener= new AppRegistry.Listener() {
+			@Override
+			public void onAppStateChanged(final AppStateEvent event) {
+				UIAccess.getDisplay(getSite().getShell()).asyncExec(() -> {
+					if (isCurrent(event.getId())) {
+						updateTitle();
+						updateState();
+					}
+				});
+			}
+		};
+		AppRegistry.getInstance().addListener(this.appRegistryListener);
+	}
+	
+	@Override
+	protected void contributeToActionBars(final IServiceLocator serviceLocator,
+			final IActionBars actionBars, final HandlerCollection handlers) {
+		super.contributeToActionBars(serviceLocator, actionBars, handlers);
+		
+		final IToolBarManager toolBarManager= actionBars.getToolBarManager();
+		toolBarManager.insertBefore(UIActions.ADDITIONS_GROUP_ID,
+				new Separator(APP_CONTROL_GROUP_ID) );
+	}
+	
+	@Override
+	protected PageBookBrowserPage doCreatePage(final BrowserSession session) {
+		if (session instanceof AppBrowserSession) {
+			return new AppBrowserPage(this, (AppBrowserSession) session);
+		}
+		return super.doCreatePage(session);
+	}
+	
+	
+	private boolean isCurrent(final URL id) {
+		final BrowserSession session= getCurrentSession();
+		return (session != null && id.equals(session.getId())
+				&& UIAccess.isOkToUse(getPageBook()) );
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/r/apps/ui/AppRegistry.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/r/apps/ui/AppRegistry.java
new file mode 100644
index 0000000..ed42ca5
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/r/apps/ui/AppRegistry.java
@@ -0,0 +1,150 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.r.apps.ui;
+
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.statet.jcommons.collections.CopyOnWriteIdentityListSet;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.ts.core.Tool;
+
+
+@NonNullByDefault
+public class AppRegistry {
+	
+	
+	public static final byte APP_STARTED=                   1;
+	public static final byte APP_STOPPED=                   2;
+	
+	
+	public static class AppStateEvent extends RApp.AppEvent {
+		
+		
+		private final byte type;
+		
+		private final URL id;
+		
+		
+		public AppStateEvent(final byte type, final URL id, final RApp app) {
+			super(app);
+			this.type= type;
+			this.id= id;
+		}
+		
+		
+		public byte getType() {
+			return this.type;
+		}
+		
+		public URL getId() {
+			return this.id;
+		}
+		
+	}
+	
+	public static interface Listener {
+		
+		
+		void onAppStateChanged(AppStateEvent event);
+		
+	}
+	
+	
+	private static class AppEntry {
+		
+		
+		private final URL id;
+		
+		private RApp app;
+		private boolean isRunning;
+		
+		
+		public AppEntry(final URL id) {
+			this.id= id;
+		}
+		
+	}
+	
+	
+	private static final AppRegistry INSTANCE= new AppRegistry();
+	
+	public static AppRegistry getInstance() {
+		return INSTANCE;
+	}
+	
+	
+	private final Map<URL, AppEntry> sessions= new HashMap<>();
+	
+	private final CopyOnWriteIdentityListSet<Listener> listeners= new CopyOnWriteIdentityListSet<>();
+	
+	
+	public AppRegistry() {
+	}
+	
+	
+	public void addListener(final Listener listener) {
+		this.listeners.add(listener);
+	}
+	
+	public void removeListener(final Listener listener) {
+		this.listeners.remove(listener);
+	}
+	
+	private void notifyListeners(final AppStateEvent event) {
+		for (final Listener listener : this.listeners) {
+			listener.onAppStateChanged(event);
+		}
+	}
+	
+	
+	public synchronized void onAppStarted(final URL id, final RApp app) {
+		final AppEntry entry= this.sessions.computeIfAbsent(id, AppEntry::new);
+		
+		if (entry.isRunning) {
+			notifyListeners(new AppStateEvent(APP_STOPPED, id, entry.app));
+		}
+		
+		entry.app= app;
+		entry.isRunning= true;
+		notifyListeners(new AppStateEvent(APP_STARTED, id, app));
+	}
+	
+	public synchronized void onAppStopped(final URL id, final RApp app) {
+		final AppEntry entry= this.sessions.get(id);
+		if (entry != null && entry.app == app) {
+			entry.isRunning= false;
+			notifyListeners(new AppStateEvent(APP_STOPPED, id, app));
+		}
+	}
+	
+	public synchronized @Nullable RApp getApp(final URL id) {
+		final AppEntry entry= this.sessions.get(id);
+		return (entry != null) ? entry.app : null;
+	}
+	
+	public synchronized @Nullable RApp getApp(final Tool tool) {
+		for (final AppEntry entry : this.sessions.values()) {
+			if (entry.isRunning && entry.app.getTool() == tool) {
+				return entry.app;
+			}
+		}
+		return null;
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/r/apps/ui/RApp.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/r/apps/ui/RApp.java
new file mode 100644
index 0000000..ab08bc5
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/r/apps/ui/RApp.java
@@ -0,0 +1,75 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.r.apps.ui;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.ui.IWorkbenchPage;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.ts.core.Tool;
+
+
+@NonNullByDefault
+public interface RApp {
+	
+	
+	class AppEvent {
+		
+		private final RApp app;
+		
+		
+		public AppEvent(final RApp app) {
+			this.app= app;
+		}
+		
+		
+		public RApp getApp() {
+			return this.app;
+		}
+		
+	}
+	
+	interface Listener {
+		
+		
+		void onVariablesChanged(AppEvent event);
+		
+	}
+	
+	
+	IResource getResource();
+	
+	@Nullable Tool getTool();
+	
+	boolean isRunning();
+	
+	void startApp(IWorkbenchPage page) throws CoreException;
+	boolean canRestartApp();
+	void restartApp(IWorkbenchPage page) throws CoreException;
+	
+	boolean canStopApp();
+	void stopApp();
+	
+	
+	void addListener(Listener listener);
+	void removeListener(Listener listener);
+	
+	@Nullable VariablesData getVariables();
+	void refreshVariables();
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/r/apps/ui/RAppUIResources.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/r/apps/ui/RAppUIResources.java
new file mode 100644
index 0000000..bff8066
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/r/apps/ui/RAppUIResources.java
@@ -0,0 +1,56 @@
+/*=============================================================================#
+ # Copyright (c) 2009, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.r.apps.ui;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.internal.r.apps.ui.RAppUIPlugin;
+
+
+@NonNullByDefault
+public class RAppUIResources {
+	
+	
+	private static final String NS= "org.eclipse.statet.r.apps"; //$NON-NLS-1$
+	
+	
+	public static final String TOOL_VIEW_IMAGE_ID= NS + "/image/tool/View"; //$NON-NLS-1$
+	
+	
+	public static final RAppUIResources INSTANCE= new RAppUIResources();
+	
+	
+	private final ImageRegistry registry;
+	
+	
+	private RAppUIResources() {
+		this.registry= RAppUIPlugin.getInstance().getImageRegistry();
+	}
+	
+	
+	public @Nullable ImageDescriptor getImageDescriptor(final String id) {
+		return this.registry.getDescriptor(id);
+	}
+	
+	public @Nullable Image getImage(final String id) {
+		return this.registry.get(id);
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/r/apps/ui/VariablesData.java b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/r/apps/ui/VariablesData.java
new file mode 100644
index 0000000..1d11a94
--- /dev/null
+++ b/r/org.eclipse.statet.r.apps/src/org/eclipse/statet/r/apps/ui/VariablesData.java
@@ -0,0 +1,76 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.r.apps.ui;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import org.eclipse.statet.jcommons.collections.ImList;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+import org.eclipse.statet.jcommons.lang.ObjectUtils.ToStringBuilder;
+
+import org.eclipse.statet.r.core.data.CombinedRElement;
+
+
+@NonNullByDefault
+public class VariablesData {
+	
+	
+	private final String expression;
+	
+	private final IStatus status;
+	
+	private final @Nullable ImList<CombinedRElement> elements;
+	
+	
+	public VariablesData(final String expression,
+			final ImList<CombinedRElement> elements) {
+		this.expression= expression;
+		this.status= Status.OK_STATUS;
+		this.elements= elements;
+	}
+	
+	public VariablesData(final String expression,
+			final IStatus status) {
+		this.expression= expression;
+		this.status= status;
+		this.elements= null;
+	}
+	
+	
+	public String getExpression() {
+		return this.expression;
+	}
+	
+	public IStatus getStatus() {
+		return this.status;
+	}
+	
+	public @Nullable ImList<CombinedRElement> getElements() {
+		return this.elements;
+	}
+	
+	
+	@Override
+	public String toString() {
+		final ToStringBuilder sb= new ToStringBuilder("VariablesData"); //$NON-NLS-1$
+		sb.addProp("expression", this.expression); //$NON-NLS-1$
+		sb.addProp("status", this.status); //$NON-NLS-1$
+		sb.addProp("elements", this.elements); //$NON-NLS-1$
+		return sb.toString();
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/console/core/AbstractRController.java b/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/console/core/AbstractRController.java
index 1ca2a6c..4e8cd2f 100644
--- a/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/console/core/AbstractRController.java
+++ b/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/console/core/AbstractRController.java
@@ -30,6 +30,7 @@
 
 import org.eclipse.statet.ltk.model.core.elements.ISourceUnit;
 import org.eclipse.statet.nico.core.runtime.ITrack;
+import org.eclipse.statet.nico.core.runtime.Queue.Section;
 import org.eclipse.statet.nico.core.runtime.SubmitType;
 import org.eclipse.statet.nico.core.runtime.ToolController;
 import org.eclipse.statet.nico.core.runtime.ToolStreamProxy;
@@ -87,8 +88,9 @@
 	private List<TrackingConfiguration> trackingConfigurations;
 	
 	
-	public AbstractRController(final RProcess process, final Map<String, Object> initData) {
-		super(process, initData);
+	public AbstractRController(final RProcess process,
+			final Map<String, Object> connectionInfo) {
+		super(process, connectionInfo);
 		process.registerFeatureSet(RConsoleTool.R_BASIC_FEATURESET_ID);
 	}
 	
@@ -199,11 +201,11 @@
 	
 	
 	@Override
-	protected void doRunSuspendedLoopL(final int o, final int level) {
+	protected void doRunSuspendedLoopL(final int o, final int level, final Section queueSection) {
 		briefChanged(RWorkspace.REFRESH_AUTO);
 		final Changes savedChanges= getWorkspaceData().saveChanges();
 		try {
-			super.doRunSuspendedLoopL(o, level);
+			super.doRunSuspendedLoopL(o, level, queueSection);
 		}
 		finally {
 			getWorkspaceData().restoreChanges(savedChanges);
diff --git a/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/console/core/RWorkspace.java b/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/console/core/RWorkspace.java
index 62d463b..ead9cf9 100644
--- a/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/console/core/RWorkspace.java
+++ b/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/console/core/RWorkspace.java
@@ -363,7 +363,7 @@
 		RReferenceVar ref= null;
 		if (reference instanceof RReferenceVar) {
 			ref= (RReferenceVar) reference;
-			if (ref.getHandle() == 0 || controller.getHotTasksState() > 1 || !isUptodate(ref)) {
+			if (ref.getHandle() == 0 || !isUptodate(ref)) {
 				ref= verifyVar(RModel.getFQElementName(ref),
 						(ICombinedRDataAdapter) controller, monitor );
 			}
diff --git a/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/console/core/util/RCodeVariableText.java b/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/console/core/util/RCodeVariableText.java
new file mode 100644
index 0000000..a9efaa5
--- /dev/null
+++ b/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/console/core/util/RCodeVariableText.java
@@ -0,0 +1,59 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.r.console.core.util;
+
+import java.util.Map;
+
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.variables.IStringVariable;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+import org.eclipse.statet.ecommons.io.FileUtil;
+import org.eclipse.statet.ecommons.variables.core.VariableText2;
+
+import org.eclipse.statet.r.console.core.RWorkspace;
+import org.eclipse.statet.r.core.RUtil;
+
+
+@NonNullByDefault
+public class RCodeVariableText extends VariableText2 {
+	
+	
+	private final RWorkspace rWorkspace;
+	
+	
+	public RCodeVariableText(final RWorkspace rWorkspace,
+			final Map<String, IStringVariable> variables) {
+		super(variables);
+		
+		this.rWorkspace= rWorkspace;
+	}
+	
+	
+	@Override
+	protected String checkValue(final IStringVariable variable, String value) throws CoreException {
+		if (variable.getName().endsWith("_loc")) { //$NON-NLS-1$
+			if (this.rWorkspace.isRemote()) {
+				final IFileStore store= FileUtil.getFileStore(value);
+				value= this.rWorkspace.toToolPath(store);
+			}
+			return RUtil.escapeBackslash(value);
+		}
+		return value;
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/nico/AbstractRDbgController.java b/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/nico/AbstractRDbgController.java
index a0fedaf..110a59f 100644
--- a/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/nico/AbstractRDbgController.java
+++ b/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/nico/AbstractRDbgController.java
@@ -183,7 +183,7 @@
 						initTopLevelBrowser(monitor);
 						break;
 					case TOPLEVELBROWSER_CHECK_SUSPENDED:
-						if (getQueue().size() > 0) {
+						if (getQueue().getCurrentSize() > 0) {
 							AbstractRDbgController.this.topLevelBrowserAction= TOPLEVELBROWSER_CHECK_SUBMIT;
 							break;
 						}
@@ -239,11 +239,12 @@
 	/**
 	 * 
 	 * @param process the R process the controller belongs to
-	 * @param initData the initialization data
+	 * @param connectionInfo the initialization data
 	 * @param enableDebug if debug features should be enabled
 	 */
-	public AbstractRDbgController(final RProcess process, final Map<String, Object> initData) {
-		super(process, initData);
+	public AbstractRDbgController(final RProcess process,
+			final Map<String, Object> connectionInfo) {
+		super(process, connectionInfo);
 	}
 	
 	
diff --git a/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/nico/impl/RjsController.java b/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/nico/impl/RjsController.java
index b3f6f5d..1bb06eb 100644
--- a/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/nico/impl/RjsController.java
+++ b/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/nico/impl/RjsController.java
@@ -458,19 +458,19 @@
 	 * 
 	 * @param process the R process the controller belongs to
 	 * @param address the RMI address
-	 * @param initData the initialization data
+	 * @param connectionInfo the initialization data
 	 * @param rjsFlags controller mode flags
 	 * @param startup flag to start R (otherwise connect only)
 	 * @param rArgs R arguments (required only if startup is <code>true</code>)
 	 * @param initialWD
 	 */
 	public RjsController(final RProcess process,
-			final RMIAddress address, final RjsConnection connection, final Map<String, Object> initData,
+			final RMIAddress address, final RjsConnection connection, final Map<String, Object> connectionInfo,
 			final int rjsFlags, final String[] rArgs,
 			final Map<String, Object> rjsProperties, final IFileStore initialWD,
 			final RWorkspaceConfig workspaceConfig,
 			final List<TrackingConfiguration> trackingConfigurations) {
-		super(process, initData);
+		super(process, connectionInfo);
 		if (address == null || connection == null) {
 			throw new IllegalArgumentException();
 		}
@@ -579,7 +579,7 @@
 			String msg= null;
 			boolean connected= false;
 			while (!connected) {
-				final Map<String, Object> initData= getInitData();
+				final Map<String, Object> connectionInfo= getTool().getConnectionInfo();
 				final ServerLogin login= this.rjsConnection.getServer().createLogin(Server.C_CONSOLE_CONNECT);
 				try {
 					final Callback[] callbacks= login.getCallbacks();
@@ -595,8 +595,8 @@
 							}
 						}
 						
-						if (initData != null) {
-							data.putAll(initData);
+						if (connectionInfo != null) {
+							data.putAll(connectionInfo);
 						}
 						data.put(LOGIN_ADDRESS_DATA_KEY, (fx != null) ? this.address.getHost() : this.address.getAddress());
 						data.put(LOGIN_MESSAGE_DATA_KEY, msg);
@@ -635,8 +635,8 @@
 					
 					if (callbacks != null) {
 						loginHandler.execute(LOGIN_OK_EVENT_ID, this, data, monitor);
-						if (initData != null) {
-							initData.put(LOGIN_USERNAME_DATA_KEY, data.get(LOGIN_USERNAME_DATA_KEY));
+						if (connectionInfo != null) {
+							connectionInfo.put(LOGIN_USERNAME_DATA_KEY, data.get(LOGIN_USERNAME_DATA_KEY));
 						}
 					}
 				}
@@ -780,6 +780,7 @@
 	
 	@Override
 	protected void onHotModeExit(final IProgressMonitor monitor) {
+		super.onHotModeExit(monitor);
 		try {
 			this.fRjs.finishTask(monitor);
 		}
@@ -787,14 +788,14 @@
 	}
 	
 	@Override
-	protected void onTaskFinished(final ToolRunnable runnable, final int event,
+	protected void onTaskFinished(final RunnableData runnableData, final int event,
 			final IProgressMonitor monitor) {
 		try {
 			this.fRjs.finishTask(monitor);
 		}
 		catch (final Throwable e) {}
 		
-		super.onTaskFinished(runnable, event, monitor);
+		super.onTaskFinished(runnableData, event, monitor);
 	}
 	
 	
diff --git a/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/internal/r/console/ui/actions/REnvIndexUpdateHandler.java b/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/internal/r/console/ui/actions/REnvIndexUpdateHandler.java
index 17b1c3e..045a176 100644
--- a/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/internal/r/console/ui/actions/REnvIndexUpdateHandler.java
+++ b/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/internal/r/console/ui/actions/REnvIndexUpdateHandler.java
@@ -17,6 +17,9 @@
 import org.eclipse.core.commands.ExecutionEvent;
 import org.eclipse.core.commands.ExecutionException;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
 import org.eclipse.statet.ecommons.ts.core.Tool;
 
 import org.eclipse.statet.nico.ui.actions.AbstractToolHandler;
@@ -27,7 +30,8 @@
 /**
  * Command handler scheduling the update of an R environment index.
  */
-public class REnvIndexUpdateHandler extends AbstractToolHandler {
+@NonNullByDefault
+public class REnvIndexUpdateHandler extends AbstractToolHandler<Tool> {
 	
 	
 	public static final int INDEX_COMPLETELY=               0x0000_0001;
@@ -58,9 +62,11 @@
 	
 	
 	@Override
-	protected Object execute(final Tool tool, final ExecutionEvent event) throws ExecutionException {
+	protected @Nullable Object execute(final Tool tool,
+			final ExecutionEvent event) throws ExecutionException {
 		tool.getQueue().add(new REnvIndexAutoUpdater.UpdateRunnable(
 				((this.mode & 0xf) == INDEX_COMPLETELY) ));
+		
 		return null;
 	}
 	
diff --git a/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/internal/r/console/ui/launching/RRemoteConsoleLaunchDelegate.java b/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/internal/r/console/ui/launching/RRemoteConsoleLaunchDelegate.java
index e5f0bed..2f61483 100644
--- a/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/internal/r/console/ui/launching/RRemoteConsoleLaunchDelegate.java
+++ b/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/internal/r/console/ui/launching/RRemoteConsoleLaunchDelegate.java
@@ -306,7 +306,7 @@
 			}
 			
 			final boolean sshTunnel= configuration.getAttribute(RConsoleLaunching.ATTR_SSH_TUNNEL_ENABLED, false);
-			final Map<String, Object> loginData= new HashMap<>();
+			final Map<String, Object> connectionInfo= new HashMap<>();
 			
 			RMIAddress rmiAddress= null;
 			RMIClientSocketFactory socketFactory= null;
@@ -320,18 +320,19 @@
 				rmiAddress= new RMIAddress(address);
 				
 				// init login data
-				loginData.put(LOGIN_USERNAME_DATA_KEY, username);
+				connectionInfo.put(LOGIN_USERNAME_DATA_KEY, username);
 				if (type.equals(RConsoleLaunching.REMOTE_RJS_SSH)) {
-					loginData.put(LOGIN_USERNAME_FORCE_DATA_KEY, true);
+					connectionInfo.put(LOGIN_USERNAME_FORCE_DATA_KEY, true);
 				}
 				final int sshPort= configuration.getAttribute(RConsoleLaunching.ATTR_SSH_PORT, DEFAULT_SSH_PORT);
-				loginData.put(LOGIN_SSH_HOST_DATA_KEY, rmiAddress.getHostAddress().getHostAddress());
-				loginData.put(LOGIN_SSH_PORT_DATA_KEY, Integer.valueOf(sshPort));
+				connectionInfo.put(LOGIN_SSH_HOST_DATA_KEY, rmiAddress.getHostAddress().getHostAddress());
+				connectionInfo.put(LOGIN_SSH_PORT_DATA_KEY, Integer.valueOf(sshPort));
 				
 				final Remote remote;
 				if (sshTunnel) {
+					connectionInfo.put("protocol", "ssh");
 					if (sshSession == null) {
-						sshSession= RjsUtil.getSession(loginData, m.newChild(5));
+						sshSession= RjsUtil.getSession(connectionInfo, m.newChild(5));
 					}
 					
 					socketFactory= RjsUtil.createRMIOverSshClientSocketFactory(sshSession);
@@ -425,9 +426,9 @@
 			final String[] args= LaunchUtils.getProcessArguments(configuration, RConsoleLaunching.ATTR_OPTIONS);
 			
 			if (reconnect != null) {
-				final Map<String, String> reconnectData= (Map<String, String>) reconnect.get("initData"); //$NON-NLS-1$
+				final Map<String, String> reconnectData= (Map<String, String>) reconnect.get("connectionInfo"); //$NON-NLS-1$
 				if (reconnectData != null) {
-					loginData.putAll(reconnectData);
+					connectionInfo.putAll(reconnectData);
 				}
 			}
 			
@@ -472,7 +473,7 @@
 					envp.put("LC_ALL", "C"); //$NON-NLS-1$ //$NON-NLS-2$
 					envp.put("LANG", "C"); //$NON-NLS-1$ //$NON-NLS-2$
 					envp.put("LC_NUMERIC", "C"); //$NON-NLS-1$ //$NON-NLS-2$
-					RjsUtil.startRemoteServerOverSsh(RjsUtil.getSession(loginData, m.newChild(5)), command, envp, m.newChild(5));
+					RjsUtil.startRemoteServerOverSsh(RjsUtil.getSession(connectionInfo, m.newChild(5)), command, envp, m.newChild(5));
 					
 					m.subTask(Messages.LaunchDelegate_WaitForR_subtask);
 					final long t= System.nanoTime();
@@ -484,7 +485,7 @@
 							if (registry == null) {
 								if (sshTunnel) {
 									if (sshSession == null) {
-										sshSession= RjsUtil.getSession(loginData, m.newChild(5));
+										sshSession= RjsUtil.getSession(connectionInfo, m.newChild(5));
 									}
 									if (socketFactory == null) {
 										socketFactory= RjsUtil.createRMIOverSshClientSocketFactory(sshSession);
@@ -566,7 +567,7 @@
 			rjsProperties.put(RjsComConfig.RJ_DATA_STRUCTS_ENVS_MAX_LENGTH_PROPERTY_ID,
 					configuration.getAttribute(RConsoleLaunching.ATTR_OBJECTDB_ENVS_MAX_LENGTH, 10000));
 			rjsProperties.put("rj.session.startup.time", timestamp); //$NON-NLS-1$
-			final RjsController controller= new RjsController(process, rmiAddress, connection, loginData,
+			final RjsController controller= new RjsController(process, rmiAddress, connection, connectionInfo,
 					(startup) ? RjsController.RJS_SETUP_CONSOLE : 0, args, rjsProperties, null,
 					RConsoleRJLaunchDelegate.createWorkspaceConfig(configuration), trackingConfigs);
 			
diff --git a/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/internal/r/console/ui/launching/RRemoteConsoleMainTab.java b/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/internal/r/console/ui/launching/RRemoteConsoleMainTab.java
index ffbb444..28d3193 100644
--- a/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/internal/r/console/ui/launching/RRemoteConsoleMainTab.java
+++ b/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/internal/r/console/ui/launching/RRemoteConsoleMainTab.java
@@ -23,6 +23,7 @@
 import static org.eclipse.statet.r.console.ui.launching.RConsoleLaunching.REMOTE_RJS_SSH;
 
 import java.net.MalformedURLException;
+import java.net.UnknownHostException;
 import java.rmi.registry.Registry;
 import java.rmi.server.RMIClientSocketFactory;
 import java.util.ArrayList;
@@ -55,7 +56,6 @@
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
 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.Control;
@@ -110,7 +110,11 @@
 				final RMIAddress rmiAddress= new RMIAddress(this.address);
 				sb.append(rmiAddress.getHostAddress().getHostAddress());
 			}
-			catch (final Exception e) {}
+			catch (final UnknownHostException e) {
+				sb.append("<unknown>");
+			}
+			catch (final Exception e) {
+			}
 			if (monitor.isCanceled()) {
 				return Status.CANCEL_STATUS;
 			}
diff --git a/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/internal/r/console/ui/page/RConsolePage.java b/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/internal/r/console/ui/page/RConsolePage.java
index 8fefff9..e07e7de 100644
--- a/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/internal/r/console/ui/page/RConsolePage.java
+++ b/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/internal/r/console/ui/page/RConsolePage.java
@@ -39,7 +39,6 @@
 
 import org.eclipse.statet.jcommons.collections.ImCollections;
 
-import org.eclipse.statet.ecommons.ts.core.Tool;
 import org.eclipse.statet.ecommons.ui.actions.HandlerCollection;
 import org.eclipse.statet.ecommons.ui.actions.HandlerContributionItem;
 import org.eclipse.statet.ecommons.ui.util.UIAccess;
@@ -174,8 +173,8 @@
 								CommandContributionItem.STYLE_PUSH, null, false ),
 						new OpenRPkgManagerHandler() {
 							@Override
-							protected Object execute(final Tool tool, final ExecutionEvent event)
-									throws ExecutionException {
+							protected Object execute(final RProcess tool,
+									final ExecutionEvent event) throws ExecutionException {
 								final IRPkgManager packageManager= RPkgManagerUI.getPackageManager(tool);
 								if (packageManager != null) {
 									packageManager.clear();
diff --git a/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/r/console/ui/tools/ChangeWorkingDirectoryWizard.java b/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/r/console/ui/tools/ChangeWorkingDirectoryWizard.java
index e3be97f..cd82d74 100644
--- a/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/r/console/ui/tools/ChangeWorkingDirectoryWizard.java
+++ b/r/org.eclipse.statet.r.console.ui/src/org/eclipse/statet/r/console/ui/tools/ChangeWorkingDirectoryWizard.java
@@ -32,6 +32,7 @@
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Composite;
 
+import org.eclipse.statet.ecommons.ts.core.util.ToolProvider;
 import org.eclipse.statet.ecommons.ui.dialogs.DialogUtils;
 import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
 import org.eclipse.statet.ecommons.ui.util.UIAccess;
@@ -40,7 +41,6 @@
 import org.eclipse.statet.internal.r.console.ui.Messages;
 import org.eclipse.statet.internal.r.console.ui.RConsoleUIPlugin;
 import org.eclipse.statet.nico.core.runtime.ToolProcess;
-import org.eclipse.statet.nico.core.util.IToolProvider;
 import org.eclipse.statet.nico.ui.actions.ToolAction;
 import org.eclipse.statet.nico.ui.util.NicoWizardDialog;
 import org.eclipse.statet.nico.ui.util.ToolInfoGroup;
@@ -54,7 +54,7 @@
 	
 	public static class ChangeAction extends ToolAction {
 		
-		public ChangeAction(final IToolProvider support) {
+		public ChangeAction(final ToolProvider support) {
 			super(support, true);
 			
 			setId("org.eclipse.statet.r.tools.ChangeWorkingDirectory"); //$NON-NLS-1$
diff --git a/r/org.eclipse.statet.r.ui/plugin.xml b/r/org.eclipse.statet.r.ui/plugin.xml
index 8e4a099..0661269 100644
--- a/r/org.eclipse.statet.r.ui/plugin.xml
+++ b/r/org.eclipse.statet.r.ui/plugin.xml
@@ -1162,11 +1162,11 @@
          </commandParameter>
       </command>
       <command
-            categoryId="org.eclipse.statet.workbench.commandCategorys.Source"
-            defaultHandler="org.eclipse.statet.internal.r.ui.rtools.RunPrintInR"
-            description="%commands.RunPrintInR.description"
             id="org.eclipse.statet.r.commands.RunPrintInR"
-            name="%commands.RunPrintInR.name">
+            categoryId="org.eclipse.statet.workbench.commandCategorys.Source"
+            name="%commands.RunPrintInR.name"
+            description="%commands.RunPrintInR.description"
+            defaultHandler="org.eclipse.statet.internal.r.ui.rtools.RunPrintInR">
          <commandParameter
                id="var"
                name="Variable to Print"
@@ -1188,12 +1188,12 @@
             schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
             sequence="M1+R M2+1">
       </key>
-      <!-- key
+      <key
             commandId="org.eclipse.statet.r.commands.RunPrintInR"
             contextId="org.eclipse.statet.r.contexts.REditor"
             schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
-            sequence="M1+R P">
-      </key -->
+            sequence="M1+R 2">
+      </key>
    </extension>
    
    <extension
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/tools/LoadRImageHandler.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/tools/LoadRImageHandler.java
index cdc9d84..f1983bd 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/tools/LoadRImageHandler.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/tools/LoadRImageHandler.java
@@ -18,16 +18,19 @@
 import org.eclipse.core.commands.ExecutionException;
 import org.eclipse.jface.wizard.WizardDialog;
 
-import org.eclipse.statet.ecommons.ts.core.Tool;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
 import org.eclipse.statet.ecommons.ui.util.UIAccess;
 
-import org.eclipse.statet.nico.core.runtime.ToolProcess;
 import org.eclipse.statet.nico.ui.actions.AbstractToolHandler;
 import org.eclipse.statet.nico.ui.util.NicoWizardDialog;
 import org.eclipse.statet.r.console.core.RConsoleTool;
+import org.eclipse.statet.r.console.core.RProcess;
 
 
-public class LoadRImageHandler extends AbstractToolHandler {
+@NonNullByDefault
+public class LoadRImageHandler extends AbstractToolHandler<RProcess> {
 	
 	
 	public LoadRImageHandler() {
@@ -36,8 +39,9 @@
 	
 	
 	@Override
-	protected Object execute(final Tool tool, final ExecutionEvent event) throws ExecutionException {
-		final LoadRImageWizard wizard = new LoadRImageWizard((ToolProcess) tool);
+	protected @Nullable Object execute(final RProcess tool,
+			final ExecutionEvent event) throws ExecutionException {
+		final LoadRImageWizard wizard = new LoadRImageWizard(tool);
 		final WizardDialog dialog = new NicoWizardDialog(UIAccess.getActiveWorkbenchShell(true), wizard);
 		dialog.setBlockOnOpen(false);
 		dialog.open();
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/RLabelProvider.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/RLabelProvider.java
index b883d89..4520da7 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/RLabelProvider.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/RLabelProvider.java
@@ -38,8 +38,8 @@
 import org.eclipse.statet.ltk.core.LTKUtils;
 import org.eclipse.statet.ltk.model.core.elements.IModelElement;
 import org.eclipse.statet.ltk.ui.IElementLabelProvider;
-import org.eclipse.statet.r.console.core.RProcessREnvironment;
 import org.eclipse.statet.r.core.data.CombinedRElement;
+import org.eclipse.statet.r.core.data.CombinedRList;
 import org.eclipse.statet.r.core.model.ArgsDefinition;
 import org.eclipse.statet.r.core.model.ArgsDefinition.Arg;
 import org.eclipse.statet.r.core.model.IRClass;
@@ -468,6 +468,16 @@
 		return text;
 	}
 	
+	public String getText(final ElementPartition partition) {
+		final StringBuilder text= new StringBuilder();
+		text.append("["); //$NON-NLS-1$
+		text.append(Long.toString((partition.getPartitionStart() + 1)));
+		text.append(" ... "); //$NON-NLS-1$
+		text.append(Long.toString(partition.getPartitionStart() + partition.getPartitionLength()));
+		text.append("]"); //$NON-NLS-1$
+		return text.toString();
+	}
+	
 	
 	@Override
 	public Image getImage(final Object element) {
@@ -483,6 +493,9 @@
 	
 	@Override
 	public String getText(final Object element) {
+		if (element instanceof ElementPartition) {
+			return getText((ElementPartition) element);
+		}
 		final IModelElement modelElement= LTKUtils.getModelElement(element);
 		if (modelElement != null) {
 //			if (modelElement instanceof ICombinedRElement) {
@@ -988,9 +1001,9 @@
 	}
 	
 	protected void appendEnvDetail(final StyledString text, final CombinedRElement element, final RList elementAttr) {
-		final RProcessREnvironment envir= (RProcessREnvironment) element;
+		final CombinedRList env= (CombinedRList) element;
 		if ((this.style & COUNT) != 0) { // count info
-			final String countInfo= getEnvCountInfo(envir);
+			final String countInfo= getEnvCountInfo(env);
 			text.append(countInfo, StyledString.COUNTER_STYLER);
 		}
 		
@@ -999,7 +1012,7 @@
 		}
 	}
 	
-	protected String getEnvCountInfo(final RProcessREnvironment envir) {
+	protected String getEnvCountInfo(final CombinedRList envir) {
 		final StringBuilder textBuilder= getTextBuilder();
 		textBuilder.append(" ("); //$NON-NLS-1$
 		textBuilder.append(envir.getLength());
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/dataeditor/ShowElementCommandHandler.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/dataeditor/ShowElementCommandHandler.java
index 89b6c56..6afe6b7 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/dataeditor/ShowElementCommandHandler.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/dataeditor/ShowElementCommandHandler.java
@@ -77,7 +77,6 @@
 		public boolean changed(final int event, final Tool tool) {
 			switch (event) {
 			case MOVING_FROM:
-			case MOVING_TO:
 				return false;
 			default:
 				return true;
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/pkgmanager/OpenRPkgManagerHandler.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/pkgmanager/OpenRPkgManagerHandler.java
index 71e1826..772a47c 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/pkgmanager/OpenRPkgManagerHandler.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/pkgmanager/OpenRPkgManagerHandler.java
@@ -19,14 +19,16 @@
 import org.eclipse.ui.IWorkbenchWindow;
 import org.eclipse.ui.handlers.HandlerUtil;
 
-import org.eclipse.statet.ecommons.ts.core.Tool;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
 
 import org.eclipse.statet.nico.ui.actions.AbstractToolHandler;
 import org.eclipse.statet.r.console.core.RConsoleTool;
 import org.eclipse.statet.r.console.core.RProcess;
 
 
-public class OpenRPkgManagerHandler extends AbstractToolHandler {
+@NonNullByDefault
+public class OpenRPkgManagerHandler extends AbstractToolHandler<RProcess> {
 	
 	
 	public OpenRPkgManagerHandler() {
@@ -34,17 +36,19 @@
 	}
 	
 	
-	protected StartAction getStartAction() {
+	protected @Nullable StartAction getStartAction() {
 		return null;
 	}
 	
 	
 	@Override
-	protected Object execute(final Tool tool, final ExecutionEvent event) throws ExecutionException {
+	protected @Nullable Object execute(final RProcess tool,
+			final ExecutionEvent event) throws ExecutionException {
 		final IWorkbenchWindow window= HandlerUtil.getActiveWorkbenchWindow(event);
 		
-		RPkgManagerUI.openDialog((RProcess) tool, (window !=  null) ? window.getShell() : null,
+		RPkgManagerUI.openDialog(tool, (window !=  null) ? window.getShell() : null,
 				getStartAction() );
+		
 		return null;
 	}
 	
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/util/CopyRElementNameHandler.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/util/CopyRElementNameHandler.java
new file mode 100644
index 0000000..de35feb
--- /dev/null
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/util/CopyRElementNameHandler.java
@@ -0,0 +1,126 @@
+/*=============================================================================#
+ # Copyright (c) 2009, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.r.ui.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+
+import org.eclipse.statet.jcommons.collections.CollectionUtils;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.models.core.util.ElementPartition;
+import org.eclipse.statet.ecommons.ui.components.StatusInfo;
+import org.eclipse.statet.ecommons.ui.util.DNDUtils;
+import org.eclipse.statet.ecommons.ui.util.UIAccess;
+import org.eclipse.statet.ecommons.ui.util.ViewActionUtil;
+
+import org.eclipse.statet.r.core.model.RElementName;
+
+
+@NonNullByDefault
+public class CopyRElementNameHandler extends AbstractHandler {
+	
+	
+	private final ViewActionUtil actionUtil;
+	
+	
+	public CopyRElementNameHandler(final ViewActionUtil actionUtil) {
+		this.actionUtil= actionUtil;
+	}
+	
+	
+	private ITreeSelection getSelection() {
+		final ISelectionProvider selectionProvider= this.actionUtil.getSelectionProvider();
+		return (ITreeSelection) selectionProvider.getSelection();
+	}
+	
+	protected boolean isValidSelection(final ITreeSelection selection) {
+		if (selection == null || selection.isEmpty()) {
+			return false;
+		}
+		for (final Object element : selection.toList()) {
+			if (element instanceof ElementPartition) {
+				return false;
+			}
+		}
+		return true;
+	}
+	
+	protected @Nullable RElementName getRElementName(final TreePath treePath,
+			final ITreeSelection selection) {
+		return RElementInputUtils.getRElementName(treePath, selection);
+	}
+	
+	
+	@Override
+	public void setEnabled(final @Nullable Object evaluationContext) {
+		setBaseEnabled(isValidSelection(getSelection()));
+	}
+	
+	@Override
+	public @Nullable Object execute(final ExecutionEvent event) throws ExecutionException {
+		if (!UIAccess.isOkToUse(this.actionUtil.getControl())) {
+			return null;
+		}
+		final ITreeSelection selection= getSelection();
+		if (!isValidSelection(selection)) {
+			return null;
+		}
+		
+		final String text= createData(selection);
+		if (text != null) {
+			copy(text);
+		}
+		
+		return null;
+	}
+	
+	protected @Nullable String createData(final ITreeSelection selection) {
+		final TreePath[] treePaths= selection.getPaths();
+		final List<String> names= new ArrayList<>(selection.size());
+		for (int i= 0; i < treePaths.length; i++) {
+			final RElementName elementName= getRElementName(treePaths[i], selection);
+			final String name= (elementName != null) ? elementName.getDisplayName() : null;
+			if (name != null) {
+				names.add(name);
+			}
+			else {
+				this.actionUtil.getStatusLine().setMessage(new StatusInfo(IStatus.WARNING,
+						"Could not copy element name for the selected objects." ));
+				return null;
+			}
+		}
+		return (names.size() == 1) ?
+				names.get(0) : CollectionUtils.toString(names, ", "); //$NON-NLS-1$
+	}
+	
+	private void copy(final String text) {
+		DNDUtils.setContent(this.actionUtil.getClipboard(),
+				new String[] { text },
+				new Transfer[] { TextTransfer.getInstance() } );
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/util/RElementInput.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/util/RElementInput.java
new file mode 100644
index 0000000..c29b48f
--- /dev/null
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/util/RElementInput.java
@@ -0,0 +1,106 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink 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:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.r.ui.util;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.statet.jcommons.lang.NonNull;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ltk.model.core.elements.IModelElement;
+import org.eclipse.statet.ltk.model.core.elements.IModelElement.Filter;
+import org.eclipse.statet.r.core.data.CombinedRElement;
+
+
+@NonNullByDefault
+public class RElementInput<TSource> {
+	
+	
+	private final TSource source;
+	
+	private final @Nullable Filter envFilter;
+	private final @Nullable Filter otherFilter;
+	
+	private final @Nullable Map<CombinedRElement, CombinedRElement[]> envFilteredChildren;
+	
+	
+	private CombinedRElement @Nullable [] rootElements;
+	
+	
+	public RElementInput(final TSource source,
+			final IModelElement. @Nullable Filter envFilter,
+			final IModelElement. @Nullable Filter otherFilter) {
+		this.source= source;
+		
+		this.envFilter= envFilter;
+		this.otherFilter= otherFilter;
+		
+		this.envFilteredChildren= (envFilter != null) ? new HashMap<>() : null;
+	}
+	
+	
+	public TSource getSource() {
+		return this.source;
+	}
+	
+	
+	public boolean hasEnvFilter() {
+		return (this.envFilter != null);
+	}
+	
+	public IModelElement. @Nullable Filter getEnvFilter() {
+		return this.envFilter;
+	}
+	
+	public CombinedRElement[] getEnvChildren(final CombinedRElement rElement) {
+		CombinedRElement[] children= this.envFilteredChildren.get(rElement);
+		if (children == null) {
+			final List<? extends CombinedRElement> list= rElement.getModelChildren(this.envFilter);
+			children= list.toArray(new @NonNull CombinedRElement[list.size()]);
+			this.envFilteredChildren.put(rElement, children);
+		}
+		return children;
+	}
+	
+	public List<CombinedRElement> filterEnvChildren(final List<? extends CombinedRElement> children) {
+		final List<CombinedRElement> list= new ArrayList<>(children.size());
+		for (final CombinedRElement rElement : children) {
+			if (this.envFilter.include(rElement)) {
+				list.add(rElement);
+			}
+		}
+		return list;
+	}
+	
+	
+	public boolean hasOtherFilter() {
+		return (this.otherFilter != null);
+	}
+	
+	public IModelElement. @Nullable Filter getOtherFilter() {
+		return this.otherFilter;
+	}
+	
+	
+	public void setRootElements(final CombinedRElement @Nullable [] elements) {
+		this.rootElements= elements;
+	}
+	
+	public CombinedRElement @Nullable [] getRootElements() {
+		return this.rootElements;
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ContentProvider.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/util/RElementInputContentProvider.java
similarity index 71%
rename from r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ContentProvider.java
rename to r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/util/RElementInputContentProvider.java
index 0c18d01..72c60bf 100644
--- a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ContentProvider.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/util/RElementInputContentProvider.java
@@ -12,7 +12,7 @@
  #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
  #=============================================================================*/
 
-package org.eclipse.statet.internal.r.objectbrowser;
+package org.eclipse.statet.r.ui.util;
 
 import java.util.Collections;
 import java.util.HashSet;
@@ -22,6 +22,10 @@
 import org.eclipse.jface.viewers.ITreeContentProvider;
 import org.eclipse.jface.viewers.Viewer;
 
+import org.eclipse.statet.jcommons.lang.NonNull;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
 import org.eclipse.statet.ecommons.models.core.util.BasicElementProxy;
 import org.eclipse.statet.ecommons.models.core.util.ElementPartition;
 import org.eclipse.statet.ecommons.models.core.util.ElementPartitionFactory;
@@ -34,15 +38,16 @@
 import org.eclipse.statet.rj.data.RReference;
 
 
-class ContentProvider implements ITreeContentProvider {
+@NonNullByDefault
+public class RElementInputContentProvider<TInput extends RElementInput<?>> implements ITreeContentProvider {
 	
 	
-	static CombinedRElement getCombinedRElement(final Object object) {
+	public static @Nullable CombinedRElement getCombinedRElement(final Object object) {
 		if (object instanceof CombinedRElement) {
 			return (CombinedRElement) object;
 		}
 		if (object instanceof IAdaptable) {
-			final IModelElement modelElement = ((IAdaptable) object).getAdapter(IModelElement.class);
+			final IModelElement modelElement= ((IAdaptable) object).getAdapter(IModelElement.class);
 			if (modelElement instanceof CombinedRElement) {
 				return (CombinedRElement) modelElement;
 			}
@@ -50,15 +55,17 @@
 		return null;
 	}
 	
-	private static final Object[] NO_CHILDREN = new Object[0];
+	
+	private static final Object[] NO_CHILDREN= new Object[0];
 	
 	static class RElementPartition extends BasicElementProxy implements ElementPartition {
 		
-		private final PartitionFactory.PartitionHandle partition;
+		private final RElementInputContentProvider<?>.PartitionFactory.PartitionHandle partition;
 		
-		public RElementPartition(final CombinedRElement value, final PartitionFactory.PartitionHandle partition) {
+		public RElementPartition(final CombinedRElement value,
+				final RElementInputContentProvider<?>.PartitionFactory.PartitionHandle partition) {
 			super(value);
-			this.partition = partition;
+			this.partition= partition;
 		}
 		
 		
@@ -79,15 +86,15 @@
 		}
 		
 		@Override
-		public boolean equals(final Object obj) {
+		public boolean equals(final @Nullable Object obj) {
 			if (this == obj) {
 				return true;
 			}
-			if (!(obj instanceof RElementPartition)) {
-				return false;
+			if (obj instanceof RElementPartition) {
+				return (super.equals(obj)
+						&& this.partition.equals(((RElementPartition) obj).partition) );
 			}
-			return (super.equals(obj)
-					&& this.partition.equals(((RElementPartition) obj).partition) );
+			return false;
 		}
 		
 	}
@@ -106,13 +113,13 @@
 		
 		@Override
 		protected Object[] getChildren(final CombinedRList value, final long start, final int length) {
-			if (ContentProvider.this.activeInput.hasEnvFilter()
+			if (RElementInputContentProvider.this.activeInput.hasEnvFilter()
 					&& value instanceof RProcessREnvironment) {
-				final Object[] all = ContentProvider.this.activeInput.getEnvFilterChildren(value);
+				final Object[] all= RElementInputContentProvider.this.activeInput.getEnvChildren(value);
 				if (start == 0 && length == all.length) {
 					return all;
 				}
-				final Object[] children = new Object[length];
+				final Object[] children= new @NonNull Object[length];
 				System.arraycopy(all, (int) start, children, 0, length);
 				return children;
 			}
@@ -120,9 +127,9 @@
 			if (start == 0 && length == value.getLength()) {
 				return value.getModelChildren(null).toArray();
 			}
-			final Object[] children = new Object[length];
-			for (int i = 0; i < length; i++) {
-				children[i] = value.get(start + i);
+			final Object[] children= new @NonNull Object[length];
+			for (int i= 0; i < length; i++) {
+				children[i]= value.get(start + i);
 			}
 			return children;
 		}
@@ -130,15 +137,15 @@
 	}
 	
 	
-	private final PartitionFactory partitionFactory = new PartitionFactory();
+	private final PartitionFactory partitionFactory= new PartitionFactory();
 	
-	private ContentInput activeInput;
+	private @Nullable TInput activeInput;
 	
 	/** References used by the viewer. Use only in UI thread */
 	private Set<RReference> usedReferences= new HashSet<>();
 	
 	
-	public ContentProvider() {
+	public RElementInputContentProvider() {
 	}
 	
 	
@@ -146,11 +153,11 @@
 	public void dispose() {
 	}
 	
-	public void setInput(final ContentInput input) {
-		this.activeInput = input;
+	public void setInput(final @Nullable TInput input) {
+		this.activeInput= input;
 	}
 	
-	public ContentInput getInput() {
+	public @Nullable TInput getInput() {
 		return this.activeInput;
 	}
 	
@@ -160,8 +167,9 @@
 	
 	@Override
 	public Object[] getElements(final Object inputElement) {
-		if (this.activeInput != null && this.activeInput.rootElements != null) {
-			return this.activeInput.rootElements;
+		final Object[] elements;
+		if (this.activeInput != null && (elements= this.activeInput.getRootElements()) != null) {
+			return elements;
 		}
 		return NO_CHILDREN;
 	}
@@ -173,7 +181,7 @@
 		}
 		
 //		if (element instanceof TreeElement) {
-//			final TreeElement treeElement = (TreeElement) element;
+//			final TreeElement treeElement= (TreeElement) element;
 //			if (treeElement.children != null) {
 //				return (treeElement.children.length > 0);
 //			}
@@ -200,18 +208,18 @@
 			return (rElement.getLength() > 0);
 		case RObject.TYPE_ENVIRONMENT:
 			if (this.activeInput.hasEnvFilter()) {
-				return (this.activeInput.getEnvFilterChildren(rElement).length > 0);
+				return (this.activeInput.getEnvChildren(rElement).length > 0);
 			}
 			return (rElement.getLength() > 0);
 		case RObject.TYPE_REFERENCE: {
-			final RObject realObject = ((RReference) rElement).getResolvedRObject();
+			final RObject realObject= ((RReference) rElement).getResolvedRObject();
 			if (realObject != null) {
 				this.usedReferences.add((RReference) rElement);
 				return hasChildren((CombinedRElement) realObject);
 			}
 			return false; }
 		case RObject.TYPE_S4OBJECT:
-			return rElement.hasModelChildren(this.activeInput.otherFilter);
+			return rElement.hasModelChildren(this.activeInput.getOtherFilter());
 		default:
 			return false;
 		}
@@ -228,7 +236,7 @@
 			return NO_CHILDREN;
 		case RObject.TYPE_ENVIRONMENT:
 			if (this.activeInput.hasEnvFilter()) {
-				final Object[] children = this.activeInput.getEnvFilterChildren(rElement);
+				final Object[] children= this.activeInput.getEnvChildren(rElement);
 				if (children.length > 5000) {
 					return this.partitionFactory.getElements((CombinedRList) rElement, children.length);
 				}
@@ -239,20 +247,20 @@
 			}
 			return rElement.getModelChildren(null).toArray();
 		case RObject.TYPE_REFERENCE: {
-			final RObject realObject = ((RReference) rElement).getResolvedRObject();
+			final RObject realObject= ((RReference) rElement).getResolvedRObject();
 			if (realObject != null) {
 				return getChildren((CombinedRElement) realObject);
 			}
 			return NO_CHILDREN; }
 		case RObject.TYPE_S4OBJECT:
-			return rElement.getModelChildren(this.activeInput.otherFilter).toArray();
+			return rElement.getModelChildren(this.activeInput.getOtherFilter()).toArray();
 		default:
 			return NO_CHILDREN;
 		}
 	}
 	
 	@Override
-	public Object getParent(final Object element) {
+	public @Nullable Object getParent(final Object element) {
 		return null;
 	}
 	
@@ -261,7 +269,7 @@
 		if (this.usedReferences.isEmpty()) {
 			return Collections.emptySet();
 		}
-		final Set<RReference> previousReferences = this.usedReferences;
+		final Set<RReference> previousReferences= this.usedReferences;
 		this.usedReferences= new HashSet<>();
 		return previousReferences;
 	}
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/util/RElementInputLabelProvider.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/util/RElementInputLabelProvider.java
new file mode 100644
index 0000000..c2d6832
--- /dev/null
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/util/RElementInputLabelProvider.java
@@ -0,0 +1,61 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.r.ui.util;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.r.core.data.CombinedRList;
+import org.eclipse.statet.r.ui.RLabelProvider;
+
+
+@NonNullByDefault
+public class RElementInputLabelProvider extends RLabelProvider {
+	
+	
+	public RElementInputLabelProvider(final int style) {
+		super(style);
+	}
+	
+	public RElementInputLabelProvider() {
+		this(COUNT);
+	}
+	
+	
+	protected @Nullable RElementInput<?> getInput() {
+		return ((RElementInputContentProvider) getViewer().getContentProvider()).getInput();
+	}
+	
+	@Override
+	protected String getEnvCountInfo(final CombinedRList envir) {
+		final StringBuilder textBuilder= getTextBuilder();
+		textBuilder.append(" ("); //$NON-NLS-1$
+		final RElementInput<?> input= getInput();
+		if (input != null && input.hasEnvFilter()) {
+			final Object[] children= input.getEnvChildren(envir);
+			if (children != null) {
+				textBuilder.append(children.length);
+			}
+			else {
+				textBuilder.append('-');
+			}
+			textBuilder.append('/');
+		}
+		textBuilder.append(envir.getLength());
+		textBuilder.append(')');
+		return textBuilder.toString();
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/util/RElementInputUtils.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/util/RElementInputUtils.java
new file mode 100644
index 0000000..048a224
--- /dev/null
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/util/RElementInputUtils.java
@@ -0,0 +1,108 @@
+/*=============================================================================#
+ # Copyright (c) 2017, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.r.ui.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.TreePath;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.models.core.util.ElementPartition;
+
+import org.eclipse.statet.ltk.ui.ElementNameProvider;
+import org.eclipse.statet.r.core.data.CombinedRElement;
+import org.eclipse.statet.r.core.model.RElementName;
+import org.eclipse.statet.rj.data.RObject;
+
+
+@NonNullByDefault
+public class RElementInputUtils {
+	
+	
+	public static class DoubleClickListener implements IDoubleClickListener {
+		
+		
+		private final AbstractTreeViewer viewer;
+		
+		
+		public DoubleClickListener(final AbstractTreeViewer viewer) {
+			this.viewer= viewer;
+		}
+		
+		
+		@Override
+		public void doubleClick(final DoubleClickEvent event) {
+			final IStructuredSelection selection= (IStructuredSelection) event.getSelection();
+			if (selection.size() != 1) {
+				return;
+			}
+			final Object element= selection.getFirstElement();
+			if (element instanceof RObject) {
+				final RObject object= (RObject) element;
+				switch (object.getRObjectType()) {
+				case RObject.TYPE_ENVIRONMENT:
+				case RObject.TYPE_LIST:
+				case RObject.TYPE_DATAFRAME:
+				case RObject.TYPE_S4OBJECT:
+				case RObject.TYPE_REFERENCE:
+					this.viewer.setExpandedState(element, !this.viewer.getExpandedState(element));
+				}
+			}
+		}
+		
+	}
+	
+	
+	public static void addDoubleClickExpansion(final AbstractTreeViewer viewer) {
+		viewer.addDoubleClickListener(new DoubleClickListener(viewer));
+	}
+	
+	public static @Nullable RElementName getRElementName(final TreePath treePath,
+			final ITreeSelection selection) {
+		if (selection instanceof ElementNameProvider) {
+			return (RElementName) ((ElementNameProvider) selection).getElementName(treePath);
+		}
+		
+		if (treePath.getSegmentCount() == 0) {
+			return null;
+		}
+		final List<RElementName> names= new ArrayList<>(treePath.getSegmentCount() + 4);
+		int segmentIdx= 0;
+		while (segmentIdx < treePath.getSegmentCount()) {
+			final Object segment= treePath.getSegment(segmentIdx++);
+			if (segment instanceof ElementPartition) {
+				continue;
+			}
+			final CombinedRElement rElement= RElementInputContentProvider.getCombinedRElement(segment);
+			if (rElement == null) {
+				return null;
+			}
+			RElementName.addSegments(names, rElement.getElementName());
+		}
+		return RElementName.create(names);
+	}
+	
+	
+	private RElementInputUtils() {}
+	
+}
diff --git a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ContentInput.java b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ContentInput.java
index 1ec3d0b..3e5b3bc 100644
--- a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ContentInput.java
+++ b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ContentInput.java
@@ -14,67 +14,38 @@
 
 package org.eclipse.statet.internal.r.objectbrowser;
 
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
 
 import org.eclipse.statet.ltk.model.core.elements.IModelElement;
+import org.eclipse.statet.r.console.core.RProcess;
 import org.eclipse.statet.r.console.core.RProcessREnvironment;
-import org.eclipse.statet.r.core.data.CombinedRElement;
+import org.eclipse.statet.r.ui.util.RElementInput;
 
 
-class ContentInput {
+@NonNullByDefault
+class ContentInput extends RElementInput<RProcess> {
 	
 	
 	List<? extends RProcessREnvironment> searchEnvirs;
-	boolean processChanged;
-	boolean inputChanged;
-	final boolean showCondensedUserspace;
 	
-	final IModelElement.Filter otherFilter;
-	
-	private final IModelElement.Filter envFilter;
-	private final Map<CombinedRElement, Object[]> envFiltered;
-	
-	CombinedRElement[] rootElements;
+	private final boolean filterUserspace;
 	
 	
-	public ContentInput(final boolean processChanged, final boolean inputChanged,
-			final boolean showCondensedUserspace,
-			final IModelElement.Filter envFilter, final IModelElement.Filter otherFilter) {
-		this.processChanged = processChanged;
-		this.inputChanged = inputChanged;
-		this.showCondensedUserspace = showCondensedUserspace;
+	public ContentInput(final RProcess source,
+			final boolean filterUserspace,
+			final IModelElement. @Nullable Filter envFilter,
+			final IModelElement. @Nullable Filter otherFilter) {
+		super(source, envFilter, otherFilter);
 		
-		this.otherFilter = otherFilter;
-		
-		this.envFilter = envFilter;
-		this.envFiltered = (envFilter != null) ? new HashMap<CombinedRElement, Object[]>() : null;
+		this.filterUserspace= filterUserspace;
 	}
 	
 	
-	public boolean hasEnvFilter() {
-		return (this.envFilter != null);
-	}
-	
-	public Object[] getEnvFilterChildren(final CombinedRElement rElement) {
-		Object[] children = this.envFiltered.get(rElement);
-		if (children == null) {
-			children = rElement.getModelChildren(this.envFilter).toArray();
-			this.envFiltered.put(rElement, children);
-		}
-		return children;
-	}
-	
-	public List<CombinedRElement> filterEnvChildren(final List<? extends CombinedRElement> children) {
-		final List<CombinedRElement> list= new ArrayList<>(children.size());
-		for (final CombinedRElement rElement : children) {
-			if (this.envFilter.include(rElement)) {
-				list.add(rElement);
-			}
-		}
-		return list;
+	public boolean isFilterUserspace() {
+		return this.filterUserspace;
 	}
 	
 }
diff --git a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ContentJob.java b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ContentJob.java
index d50186d..f675f32 100644
--- a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ContentJob.java
+++ b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ContentJob.java
@@ -14,8 +14,9 @@
 
 package org.eclipse.statet.internal.r.objectbrowser;
 
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
+
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -29,6 +30,9 @@
 import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
 
 import org.eclipse.statet.jcommons.collections.ImCollections;
+import org.eclipse.statet.jcommons.lang.NonNull;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
 
 import org.eclipse.statet.ecommons.ui.util.UIAccess;
 
@@ -42,31 +46,30 @@
 import org.eclipse.statet.rj.data.REnvironment;
 
 
+@NonNullByDefault
 class ContentJob extends Job implements ToolWorkspace.Listener {
 	
 	
 	static class ContentFilter implements IModelElement.Filter {
 		
 		private final boolean filterInternal;
-		private final boolean filterNoPattern;
-		private final SearchPattern searchPattern;
+		private final @Nullable SearchPattern searchPattern;
 		
 		
-		public ContentFilter(final boolean filterInternal, final SearchPattern pattern) {
-			this.filterInternal = filterInternal;
-			this.filterNoPattern = (pattern == null);
-			this.searchPattern = pattern;
+		public ContentFilter(final boolean filterInternal, final @Nullable SearchPattern pattern) {
+			this.filterInternal= filterInternal;
+			this.searchPattern= pattern;
 		}
 		
 		
 		@Override
 		public boolean include(final IModelElement element) {
-			final String name = element.getElementName().getSegmentName();
+			final String name= element.getElementName().getSegmentName();
 			if (name != null) {
 				if (this.filterInternal && name.length() > 0 && name.charAt(0) == '.') {
 					return false;
 				}
-				return (this.filterNoPattern || this.searchPattern.matches(name));
+				return (this.searchPattern == null || this.searchPattern.matches(name));
 			}
 			else {
 				return true;
@@ -78,26 +81,26 @@
 	
 	private final ObjectBrowserView view;
 	
+	private volatile boolean isScheduled;
+	
 	/** true if RefreshR is running */
 	private boolean forceOnWorkspaceChange;
 	/** the process to update */
-	private RProcess updateProcess;
+	private @Nullable RProcess updateSource;
 	/** the process of last update */
-	private RProcess lastProcess;
+	private @Nullable RProcess lastSource;
 	/** update all environment */
 	private boolean force;
 	/** environments to update, if force is false*/
 	private final Set<RProcessREnvironment> updateSet= new HashSet<>();
 	
-	private List<? extends RProcessREnvironment> rawInput;
-	private List<? extends CombinedRElement> userspaceInput;
-	
-	private volatile boolean isScheduled;
+	private @Nullable List<? extends RProcessREnvironment> rawInput;
+	private @Nullable List<? extends CombinedRElement> userspaceInput;
 	
 	
 	public ContentJob(final ObjectBrowserView view) {
 		super("R Object Browser Update");
-		this.view = view;
+		this.view= view;
 		setSystem(true);
 		setUser(false);
 	}
@@ -105,21 +108,21 @@
 	
 	@Override
 	public void propertyChanged(final ToolWorkspace workspace, final Map<String, Object> properties) {
-		final RWorkspace rWorkspace = (RWorkspace) workspace;
+		final RWorkspace rWorkspace= (RWorkspace) workspace;
 		if (properties.containsKey("REnvironments")) {
 			if (this.forceOnWorkspaceChange) {
-				this.forceOnWorkspaceChange = false;
-				final RProcess process = rWorkspace.getProcess();
+				this.forceOnWorkspaceChange= false;
+				final RProcess process= rWorkspace.getProcess();
 				forceUpdate(process);
 				schedule();
 			}
 			else {
-				final List<RProcessREnvironment> envirs = (List<RProcessREnvironment>) properties.get("REnvironments");
+				final List<RProcessREnvironment> envirs= (List<RProcessREnvironment>) properties.get("REnvironments");
 				schedule(rWorkspace.getProcess(), envirs);
 			}
 		}
 		
-		final Object autorefresh = properties.get("AutoRefresh.enabled");
+		final Object autorefresh= properties.get("AutoRefresh.enabled");
 		if (autorefresh instanceof Boolean) {
 			UIAccess.getDisplay().asyncExec(new Runnable() {
 				@Override
@@ -132,7 +135,7 @@
 			});
 		}
 		else { // autorefresh already updates dirty
-			final Object dirty = properties.get("RObjectDB.dirty");
+			final Object dirty= properties.get("RObjectDB.dirty");
 			if (dirty instanceof Boolean) {
 				UIAccess.getDisplay().asyncExec(new Runnable() {
 					@Override
@@ -147,28 +150,28 @@
 		}
 	}
 	
-	public void forceUpdate(final RProcess process) {
-		synchronized (this.view.processLock) {
+	public void forceUpdate(final @Nullable RProcess process) {
+		synchronized (this.view.sourceLock) {
 			if (process != this.view.getTool()) {
 				return;
 			}
-			this.updateProcess = process;
-			this.force = true;
+			this.updateSource= process;
+			this.force= true;
 			this.updateSet.clear();
 		}
 	}
 	
 	public void forceOnWorkspaceChange() {
-		this.forceOnWorkspaceChange = true;
+		this.forceOnWorkspaceChange= true;
 	}
 	
 	public void schedule(final RProcess process, final List<RProcessREnvironment> envirs) {
 		if (envirs != null && process != null) {
-			synchronized (this.view.processLock) {
+			synchronized (this.view.sourceLock) {
 				if (process != this.view.getTool()) {
 					return;
 				}
-				this.updateProcess = process;
+				this.updateSource= process;
 				if (!this.force) {
 					this.updateSet.removeAll(envirs);
 					this.updateSet.addAll(envirs);
@@ -180,7 +183,7 @@
 	
 	@Override
 	public boolean shouldSchedule() {
-		this.isScheduled = true;
+		this.isScheduled= true;
 		return true;
 	}
 	
@@ -189,79 +192,70 @@
 		if (!this.isScheduled) {
 			return Status.CANCEL_STATUS;
 		}
-		this.isScheduled = false;
 		
-		final IWorkbenchSiteProgressService progressService = this.view.getViewSite().getService(IWorkbenchSiteProgressService.class);
+		final IWorkbenchSiteProgressService progressService= this.view.getViewSite().getService(IWorkbenchSiteProgressService.class);
 		if (progressService != null) {
 			progressService.incrementBusy();
 		}
 		
 		try {
-			final List<RProcessREnvironment> updateList;
-			final boolean force;
-			final boolean updateInput;
 			final RProcess process;
-			synchronized (this.view.processLock) {
-				force = this.force;
-				updateInput = (force || this.updateProcess != null);
-				process = (updateInput) ? this.updateProcess : this.view.getTool();
-				if (process != this.view.getTool() || this.forceOnWorkspaceChange) {
+			final boolean sourceChanged;
+			final boolean updateInput;
+			List<RProcessREnvironment> updateList= null;
+			synchronized (this.view.sourceLock) {
+				this.isScheduled= false;
+				
+				process= this.view.getTool();
+				sourceChanged= (process != this.lastSource);
+				updateInput= (sourceChanged || this.updateSource != null);
+				if (this.forceOnWorkspaceChange) {
 					return Status.OK_STATUS;
 				}
-				updateList= new ArrayList<>(this.updateSet.size());
-				updateList.addAll(this.updateSet);
+				if (!(sourceChanged || this.force)) {
+					updateList= new ArrayList<>(this.updateSet.size());
+					updateList.addAll(this.updateSet);
+				}
+				this.lastSource= process;
+				this.updateSource= null;
+				this.force= false;
 				this.updateSet.clear();
-				this.updateProcess = null;
-				this.force = false;
 			}
 			
-			final ContentInput input = createHandler(process, updateInput);
+			final ContentInput input= (process != null) ? createInput(process) : null;
 			
 			// Update input and refresh
 			final List<RProcessREnvironment> toUpdate;
 			if (updateInput) {
-				toUpdate = updateInput((!force) ? updateList : null, process, input);
+				toUpdate= updateFromSource(input, updateList);
 			}
 			else {
-				toUpdate = null;
+				toUpdate= null;
 			}
 			
-			if (this.rawInput != null) {
-				prescan(input);
-			}
-			else if (process != null) {
-				input.processChanged = false;
-			}
+			prepare(input);
 			
-			synchronized (this.view.processLock) {
+			synchronized (this.view.sourceLock) {
 				if (process != this.view.getTool()) {
+					this.lastSource= null;
 					return Status.CANCEL_STATUS;
 				}
-				if ((!input.processChanged && this.isScheduled) || monitor.isCanceled()) {
-					this.updateSet.addAll(updateList);
-					this.force |= force;
-					if (updateInput && this.updateProcess == null) {
-						this.updateProcess = process;
+				if ((!sourceChanged && this.isScheduled) || monitor.isCanceled()) {
+					if (updateList != null
+							&& (this.updateSource == process || this.updateSource == null) ) {
+						this.updateSource= process;
+						this.updateSet.addAll(updateList);
 					}
 					return Status.CANCEL_STATUS;
 				}
 			}
-			UIAccess.getDisplay().syncExec(new Runnable() {
-				@Override
-				public void run() {
-					if (process != ContentJob.this.view.getTool()) {
-						return;
-					}
-					ContentJob.this.view.updateViewer(toUpdate, input);
+			UIAccess.getDisplay().syncExec(() -> {
+				if (process != ContentJob.this.view.getTool()) {
+					return;
 				}
+				ContentJob.this.view.updateView(input, toUpdate);
 			});
 			
-			if (this.rawInput != null) {
-				this.lastProcess = process;
-			}
-			else {
-				this.lastProcess = null;
-			}
 			return Status.OK_STATUS;
 		}
 		finally {
@@ -271,119 +265,124 @@
 		}
 	}
 	
-	private ContentInput createHandler(final RProcess process, final boolean updateInput) {
-		final boolean processChanged = ((process != null) ? process != this.lastProcess : this.lastProcess != null);
-		final boolean filterInternal = !this.view.getShowInternal();
-		final String filterText = this.view.getSearchText();
+	private ContentInput createInput(final RProcess source) {
+		final boolean filterInternal= !this.view.getFilterIncludeInternal();
+		final String filterText= this.view.getFilterSearchText();
 		IModelElement.Filter envFilter;
 		IModelElement.Filter otherFilter;
 		if (filterText != null && filterText.length() > 0) {
-			final SearchPattern filterPattern = new RNameSearchPattern();
+			final SearchPattern filterPattern= new RNameSearchPattern();
 			filterPattern.setPattern(filterText);
-			envFilter = new ContentFilter(filterInternal, filterPattern);
-			otherFilter = (filterInternal) ? new ContentFilter(filterInternal, null) : null;
+			envFilter= new ContentFilter(filterInternal, filterPattern);
+			otherFilter= (filterInternal) ? new ContentFilter(filterInternal, null) : null;
 		}
 		else if (filterInternal) {
-			envFilter = new ContentFilter(filterInternal, null);
-			otherFilter = new ContentFilter(filterInternal, null);
+			envFilter= new ContentFilter(filterInternal, null);
+			otherFilter= new ContentFilter(filterInternal, null);
 		}
 		else {
-			envFilter = null;
-			otherFilter = null;
+			envFilter= null;
+			otherFilter= null;
 		}
-		return new ContentInput(processChanged, updateInput, this.view.getShowConsenseUserspace(),
+		return new ContentInput(source,
+				this.view.getShowCondensedUserspace(),
 				envFilter, otherFilter );
 	}
 	
-	private List<RProcessREnvironment> updateInput(final List<RProcessREnvironment> updateList, 
-			final RProcess process, final ContentInput input) {
-		if (process != null) {
-			final List<? extends RProcessREnvironment> oldInput = this.rawInput;
-			final RWorkspace workspaceData = process.getWorkspaceData();
-			this.rawInput = workspaceData.getRSearchEnvironments();
-			if (this.rawInput == null || this.rawInput.size() == 0) {
-				this.rawInput = null;
-				this.userspaceInput = null;
-				return null;
-			}
-			input.searchEnvirs = this.rawInput;
-			// If search path (environments) is not changed and not in force mode, refresh only the updated entries
-			List<RProcessREnvironment> updateEntries = null;
-			TRY_PARTIAL : if (!input.showCondensedUserspace
-					&& this.rawInput != null && oldInput != null && this.rawInput.size() == oldInput.size()
-					&& updateList != null && updateList.size() < this.rawInput.size() ) {
-				updateEntries= new ArrayList<>(updateList.size());
-				for (int i = 0; i < this.rawInput.size(); i++) {
-					final RProcessREnvironment envir = this.rawInput.get(i);
-					if (envir.equals(oldInput.get(i))) {
-						if (updateList.remove(envir)) {
-							updateEntries.add(envir);
-						}
-					}
-					else { // search path is changed
-						updateEntries = null;
-						break TRY_PARTIAL;
+	private @Nullable List<RProcessREnvironment> updateFromSource(final @Nullable ContentInput input,
+			final @Nullable List<RProcessREnvironment> updateList) {
+		final List<? extends RProcessREnvironment> oldInput= this.rawInput;
+		this.rawInput= null;
+		this.userspaceInput= null;
+		
+		if (input == null) {
+			return null;
+		}
+		final RProcess process= input.getSource();
+		final RWorkspace workspaceData= process.getWorkspaceData();
+		final List<? extends RProcessREnvironment> rawInput= workspaceData.getRSearchEnvironments();
+		if (rawInput == null || rawInput.size() == 0) {
+			return null;
+		}
+		this.rawInput= rawInput;
+		input.searchEnvirs= rawInput;
+		// If search path (environments) is not changed and not in force mode, refresh only the updated entries
+		List<RProcessREnvironment> updateEntries= null;
+		TRY_PARTIAL : if (!input.isFilterUserspace()
+				&& oldInput != null && rawInput.size() == oldInput.size()
+				&& updateList != null && updateList.size() < rawInput.size() ) {
+			updateEntries= new ArrayList<>(updateList.size());
+			for (int i= 0; i < rawInput.size(); i++) {
+				final RProcessREnvironment envir= rawInput.get(i);
+				if (envir.equals(oldInput.get(i))) {
+					if (updateList.remove(envir)) {
+						updateEntries.add(envir);
 					}
 				}
-				if (!updateList.isEmpty()) {
-					updateEntries = null;
+				else { // search path is changed
+					updateEntries= null;
 					break TRY_PARTIAL;
 				}
 			}
-			
-			// Prepare Userspace filter
-			if (input.showCondensedUserspace) {
-				int length = 0;
-				final List<RProcessREnvironment> userEntries= new ArrayList<>(this.rawInput.size());
-				for (final RProcessREnvironment env : this.rawInput) {
-					if (env.getSpecialType() > 0 && env.getSpecialType() <= REnvironment.ENVTYPE_PACKAGE) {
-						continue;
-					}
-					userEntries.add(env);
-					length += env.getLength();
-				}
-				final List<IModelElement> elements= new ArrayList<>(length);
-				for (final RProcessREnvironment entry : userEntries) {
-					elements.addAll(entry.getModelChildren((IModelElement.Filter) null));
-				}
-				
-				final CombinedRElement[] array = elements.toArray(new CombinedRElement[elements.size()]);
-				Arrays.sort(array, ObjectBrowserView.ELEMENTNAME_COMPARATOR);
-				this.userspaceInput= ImCollections.newList(array);
+			if (!updateList.isEmpty()) {
+				updateEntries= null;
+				break TRY_PARTIAL;
 			}
-			else {
-				this.userspaceInput = null;
-			}
-			return updateEntries;
 		}
-		else {
-			this.rawInput = null;
-			this.userspaceInput = null;
-			return null;
-		}
+		
+		return updateEntries;
 	}
 	
-	private void prescan(final ContentInput input) {
-		// Prescan filter
-		if (!input.showCondensedUserspace) {
-			final CombinedRElement[] array = this.rawInput.toArray(new CombinedRElement[this.rawInput.size()]);
-			if (input.hasEnvFilter()) {
-				for (int i = 0; i < array.length; i++) {
-					input.getEnvFilterChildren(array[i]);
+	private List<? extends CombinedRElement> getUserspaceInput() {
+		if (this.userspaceInput == null) {
+			final List<? extends RProcessREnvironment> rawInput= nonNullAssert(this.rawInput);
+			final List<RProcessREnvironment> userEntries= new ArrayList<>(rawInput.size());
+			int length= 0;
+			for (final RProcessREnvironment env : rawInput) {
+				if (env.getSpecialType() > 0 && env.getSpecialType() <= REnvironment.ENVTYPE_PACKAGE) {
+					continue;
 				}
+				userEntries.add(env);
+				length+= env.getLength();
 			}
-			input.rootElements = array;
+			final List<CombinedRElement> elements= new ArrayList<>(length);
+			for (final RProcessREnvironment entry : userEntries) {
+				elements.addAll(entry.getModelChildren((IModelElement.Filter) null));
+			}
+			
+			final CombinedRElement[] array= elements.toArray(new @NonNull CombinedRElement[elements.size()]);
+			this.userspaceInput= ImCollections.newList(array,
+					ObjectBrowserView.ELEMENTNAME_COMPARATOR );
+		}
+		return this.userspaceInput;
+	}
+	
+	private void prepare(final @Nullable ContentInput input) {
+		final List<? extends RProcessREnvironment> rawInput= this.rawInput;
+		if (input == null || rawInput == null) {
+			return;
+		}
+		
+		@NonNull CombinedRElement[] array;
+		
+		if (input.isFilterUserspace()) {
+			// prepare userspace filter
+			List<? extends CombinedRElement> list= getUserspaceInput();
+			if (input.hasEnvFilter()) { // prepare env filter
+				list= input.filterEnvChildren(list);
+			}
+			array= list.toArray(new @NonNull CombinedRElement[list.size()]);
 		}
 		else {
-			final List<? extends CombinedRElement> list;
-			if (input.hasEnvFilter()) {
-				list = input.filterEnvChildren(this.userspaceInput);
+			array= rawInput.toArray(new @NonNull CombinedRElement[rawInput.size()]);
+			if (input.hasEnvFilter()) { // prepare env filter
+				for (int i= 0; i < array.length; i++) {
+					input.getEnvChildren(array[i]);
+				}
 			}
-			else {
-				list = this.userspaceInput;
-			}
-			input.rootElements = list.toArray(new CombinedRElement[list.size()]);
 		}
+		
+		input.setRootElements(array);
 	}
 	
 }
diff --git a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/CopyElementNameHandler.java b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/CopyElementNameHandler.java
deleted file mode 100644
index 8a7c250..0000000
--- a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/CopyElementNameHandler.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*=============================================================================#
- # Copyright (c) 2009, 2018 Stephan Wahlbrink and others.
- # 
- # This program and the accompanying materials are made available under the
- # terms of the Eclipse Public License 2.0 which is available at
- # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
- # which is available at https://www.apache.org/licenses/LICENSE-2.0.
- # 
- # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
- # 
- # Contributors:
- #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
- #=============================================================================*/
-
-package org.eclipse.statet.internal.r.objectbrowser;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import org.eclipse.core.commands.AbstractHandler;
-import org.eclipse.core.commands.ExecutionEvent;
-import org.eclipse.core.commands.ExecutionException;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.jface.viewers.ITreeSelection;
-import org.eclipse.jface.viewers.TreePath;
-import org.eclipse.swt.dnd.TextTransfer;
-import org.eclipse.swt.dnd.Transfer;
-
-import org.eclipse.statet.jcommons.collections.CollectionUtils;
-
-import org.eclipse.statet.ecommons.models.core.util.ElementPartition;
-import org.eclipse.statet.ecommons.ui.components.StatusInfo;
-import org.eclipse.statet.ecommons.ui.util.DNDUtils;
-import org.eclipse.statet.ecommons.ui.util.UIAccess;
-
-import org.eclipse.statet.ltk.core.ElementName;
-
-
-class CopyElementNameHandler extends AbstractHandler {
-	
-	
-	private final ObjectBrowserView view;
-	
-	
-	CopyElementNameHandler(final ObjectBrowserView objectBrowserView) {
-		this.view = objectBrowserView;
-	}
-	
-	
-	private boolean isValidSelection(final ITreeSelection selection) {
-		if (selection == null || selection.isEmpty()) {
-			return false;
-		}
-		for (final Iterator<?> iter = selection.iterator(); iter.hasNext(); ) {
-			final Object element = iter.next();
-			if (element instanceof ElementPartition) {
-				return false;
-			}
-		}
-		return true;
-	}
-	
-	@Override
-	public void setEnabled(final Object evaluationContext) {
-		setBaseEnabled(isValidSelection(this.view.getSelection()));
-	}
-	
-	@Override
-	public Object execute(final ExecutionEvent event) throws ExecutionException {
-		if (!UIAccess.isOkToUse(this.view.getViewer())) {
-			return null;
-		}
-		final ITreeSelection selection = this.view.getSelection();
-		if (!isValidSelection(selection)) {
-			return null;
-		}
-		final TreePath[] treePaths = selection.getPaths();
-		if (treePaths.length == 0) {
-			return null;
-		}
-		final List<String> names= new ArrayList<>();
-		int failed = 0;
-		for (int i = 0; i < treePaths.length; i++) {
-			final ElementName elementName = this.view.getFQElementName(treePaths[i]);
-			final String name = (elementName != null) ? elementName.getDisplayName() : null;
-			if (name != null) {
-				names.add(name);
-			}
-			else {
-				failed++;
-			}
-		}
-		
-		if (names.size() == 1) {
-			copy(names.get(0));
-		}
-		else if (names.size() > 1) {
-			copy(CollectionUtils.toString(names, ", ")); //$NON-NLS-1$
-		}
-		else {
-			copy(""); //$NON-NLS-1$
-		}
-		if (failed > 0) {
-			this.view.getStatusLine().setMessage(new StatusInfo(IStatus.WARNING, "Could not copy element name for " + failed + " of " + treePaths.length + " objects."));
-		}
-		
-		return null;
-	}
-	
-	private void copy(final String text) {
-		DNDUtils.setContent(this.view.getClipboard(), 
-				new String[] { text }, 
-				new Transfer[] { TextTransfer.getInstance() } );
-	}
-	
-}
diff --git a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/DeleteHandler.java b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/DeleteHandler.java
index c22e1cf..6ea22d3 100644
--- a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/DeleteHandler.java
+++ b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/DeleteHandler.java
@@ -36,6 +36,8 @@
 import org.eclipse.ui.ISharedImages;
 import org.eclipse.ui.PlatformUI;
 
+import org.eclipse.statet.jcommons.lang.Nullable;
+
 import org.eclipse.statet.ecommons.models.core.util.ElementPartition;
 import org.eclipse.statet.ecommons.ts.core.Tool;
 import org.eclipse.statet.ecommons.ts.ui.ToolRunnableDecorator;
@@ -48,6 +50,7 @@
 import org.eclipse.statet.r.console.core.IRDataAdapter;
 import org.eclipse.statet.r.core.data.CombinedRElement;
 import org.eclipse.statet.r.core.model.RElementName;
+import org.eclipse.statet.r.ui.util.RElementInputContentProvider;
 import org.eclipse.statet.rj.data.RObject;
 
 
@@ -120,14 +123,14 @@
 	}
 	
 	@Override
-	public void setEnabled(final Object evaluationContext) {
+	public void setEnabled(final @Nullable Object evaluationContext) {
 		final ToolProcess process = this.view.getTool();
 		setBaseEnabled(process != null && !process.isTerminated()
 				&& isValidSelection(this.view.getSelection()) );
 	}
 	
 	@Override
-	public Object execute(final ExecutionEvent event) throws ExecutionException {
+	public @Nullable Object execute(final ExecutionEvent event) throws ExecutionException {
 		if (!UIAccess.isOkToUse(this.view.getViewer())) {
 			return null;
 		}
@@ -164,8 +167,8 @@
 			}
 			
 			final TreePath treePath = treePaths[i];
-			final CombinedRElement element = ContentProvider.getCombinedRElement(treePath.getLastSegment());
-			final CombinedRElement parent = element.getModelParent();
+			final CombinedRElement element= RElementInputContentProvider.getCombinedRElement(treePath.getLastSegment());
+			final CombinedRElement parent= element.getModelParent();
 			
 			final RElementName elementName = this.view.getFQElementName(treePath);
 			if (parent != null && elementName != null) {
diff --git a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ObjectBrowserView.java b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ObjectBrowserView.java
index 50e34b1..4bfcb61 100644
--- a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ObjectBrowserView.java
+++ b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ObjectBrowserView.java
@@ -15,17 +15,18 @@
 package org.eclipse.statet.internal.r.objectbrowser;
 
 import static org.eclipse.statet.ecommons.ui.actions.UIActions.ADDITIONS_GROUP_ID;
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
+import static org.eclipse.ui.IWorkbenchCommandConstants.NAVIGATE_COLLAPSE_ALL;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 import org.eclipse.core.commands.AbstractHandler;
 import org.eclipse.core.commands.ExecutionEvent;
-import org.eclipse.core.commands.ExecutionException;
-import org.eclipse.core.commands.IHandler2;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
@@ -37,11 +38,10 @@
 import org.eclipse.jface.action.Separator;
 import org.eclipse.jface.dialogs.IDialogSettings;
 import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.jface.viewers.DoubleClickEvent;
-import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IPostSelectionProvider;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.ISelectionChangedListener;
-import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.jface.viewers.ITreeSelection;
 import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.jface.viewers.TreePath;
@@ -54,7 +54,7 @@
 import org.eclipse.swt.dnd.Clipboard;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Menu;
 import org.eclipse.ui.IActionBars;
 import org.eclipse.ui.IMemento;
@@ -69,13 +69,22 @@
 import org.eclipse.ui.menus.UIElement;
 import org.eclipse.ui.part.ViewPart;
 import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
+import org.eclipse.ui.services.IServiceLocator;
 
-import org.eclipse.statet.ecommons.collections.FastList;
+import org.eclipse.statet.jcommons.collections.CopyOnWriteIdentityListSet;
+import org.eclipse.statet.jcommons.lang.NonNull;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
 import org.eclipse.statet.ecommons.models.core.util.ElementPartition;
 import org.eclipse.statet.ecommons.ts.core.Tool;
-import org.eclipse.statet.ecommons.ts.core.ToolRunnable;
+import org.eclipse.statet.ecommons.ts.core.util.ActiveToolListener;
+import org.eclipse.statet.ecommons.ts.core.util.ActiveToolListener.ActiveToolEvent;
+import org.eclipse.statet.ecommons.ts.core.util.ToolProvider;
+import org.eclipse.statet.ecommons.ui.actions.HandlerCollection;
 import org.eclipse.statet.ecommons.ui.actions.HandlerContributionItem;
 import org.eclipse.statet.ecommons.ui.actions.SearchContributionItem;
+import org.eclipse.statet.ecommons.ui.actions.UIActions;
 import org.eclipse.statet.ecommons.ui.components.StatusInfo;
 import org.eclipse.statet.ecommons.ui.dialogs.DialogUtils;
 import org.eclipse.statet.ecommons.ui.util.ColumnHoverManager;
@@ -84,9 +93,10 @@
 import org.eclipse.statet.ecommons.ui.util.InformationDispatchHandler;
 import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
 import org.eclipse.statet.ecommons.ui.util.PostSelectionProviderProxy;
-import org.eclipse.statet.ecommons.ui.util.StatusLineMessageManager;
 import org.eclipse.statet.ecommons.ui.util.TreeSelectionProxy;
 import org.eclipse.statet.ecommons.ui.util.UIAccess;
+import org.eclipse.statet.ecommons.ui.util.ViewActionUtil;
+import org.eclipse.statet.ecommons.ui.workbench.ContextHandlers;
 import org.eclipse.statet.ecommons.ui.workbench.WorkbenchUIUtils;
 
 import org.eclipse.statet.base.ui.StatetImages;
@@ -98,13 +108,11 @@
 import org.eclipse.statet.ltk.ui.sourceediting.assist.IInfoHover;
 import org.eclipse.statet.ltk.ui.util.ViewerDragSupport;
 import org.eclipse.statet.nico.core.runtime.ToolProcess;
-import org.eclipse.statet.nico.core.util.IToolProvider;
-import org.eclipse.statet.nico.core.util.IToolRetargetable;
 import org.eclipse.statet.nico.ui.IToolRegistry;
 import org.eclipse.statet.nico.ui.IToolRegistryListener;
 import org.eclipse.statet.nico.ui.NicoUI;
 import org.eclipse.statet.nico.ui.ToolSessionUIData;
-import org.eclipse.statet.nico.ui.actions.ToolRetargetableHandler;
+import org.eclipse.statet.nico.ui.actions.AbstractToolHandler;
 import org.eclipse.statet.r.console.core.AbstractRDataRunnable;
 import org.eclipse.statet.r.console.core.IRDataAdapter;
 import org.eclipse.statet.r.console.core.RConsoleTool;
@@ -114,24 +122,38 @@
 import org.eclipse.statet.r.core.data.CombinedRElement;
 import org.eclipse.statet.r.core.model.RElementComparator;
 import org.eclipse.statet.r.core.model.RElementName;
-import org.eclipse.statet.r.ui.RLabelProvider;
+import org.eclipse.statet.r.ui.rtool.CopyRElementHandler;
+import org.eclipse.statet.r.ui.rtool.PrintRElementHandler;
 import org.eclipse.statet.r.ui.rtool.RElementInfoHoverCreator;
 import org.eclipse.statet.r.ui.rtool.RElementInfoTask;
-import org.eclipse.statet.rj.data.RObject;
+import org.eclipse.statet.r.ui.rtool.RElementViewerDragSourceListener;
+import org.eclipse.statet.r.ui.util.CopyRElementNameHandler;
+import org.eclipse.statet.r.ui.util.RElementInputContentProvider;
+import org.eclipse.statet.r.ui.util.RElementInputLabelProvider;
+import org.eclipse.statet.r.ui.util.RElementInputUtils;
 import org.eclipse.statet.rj.data.RReference;
 
 
-public class ObjectBrowserView extends ViewPart implements IToolProvider {
+@NonNullByDefault
+public class ObjectBrowserView extends ViewPart implements ToolProvider {
 	
 	
-	private static final String FILTER_USERSPACEONLY_SETTINGS_KEY = "filter.only_userspace.enabled"; //$NON-NLS-1$
-	private static final String FILTER_INTERNALINCLUDE_SETTINGS_KEY = "filter.include_internal.enabled"; //$NON-NLS-1$
-	private static final String SORT_BYTYPE_SETTINGS_KEY = "sort.by_name.enabled"; //$NON-NLS-1$
+	private static final String REFRESH_COMMAND_ID= IWorkbenchCommandConstants.FILE_REFRESH;
 	
-	static final RElementComparator ELEMENTNAME_COMPARATOR = new RElementComparator();
-	private static final ViewerComparator TYPE_COMPARATOR = new SortByTypeComparator();
+	private static final String OPEN_COMMAND_ID= "org.eclipse.jdt.ui.edit.text.java.open.editor"; //$NON-NLS-1$
+	private static final String PRINT_COMMAND_ID= RunPrintInR.COMMAND_ID;
 	
-	private static final String OPEN_COMMAND_ID = "org.eclipse.jdt.ui.edit.text.java.open.editor"; //$NON-NLS-1$
+	private static final String FILTER_ONLY_USERSPACE_COMMAND_ID= "Filter.OnlyUserspace";
+	private static final String FILTER_INCLUDE_INTERNAL_COMMAND_ID= "Filter.IncludeInternal";
+	
+	
+	private static final String FILTER_ONLY_USERSPACE_SETTINGS_KEY= "Filter.OnlyUserspace.enabled"; //$NON-NLS-1$
+	private static final String FILTER_INCLUDE_INTERNAL_SETTINGS_KEY= "Filter.IncludeInternal.enabled"; //$NON-NLS-1$
+	private static final String SORT_BY_TYPE_SETTINGS_KEY= "Sort.ByType.enabled"; //$NON-NLS-1$
+	
+	
+	static final RElementComparator ELEMENTNAME_COMPARATOR= new RElementComparator();
+	private static final ViewerComparator TYPE_COMPARATOR= new SortByTypeComparator();
 	
 	
 	private class RefreshWorkspaceR extends AbstractRDataRunnable {
@@ -152,15 +174,15 @@
 		protected void run(final IRDataAdapter r,
 				final IProgressMonitor monitor) throws CoreException {
 			boolean current;
-			synchronized (ObjectBrowserView.this.processLock) {
-				current = (r.getTool() != getTool());
+			synchronized (ObjectBrowserView.this.sourceLock) {
+				current= (r.getTool() != getTool());
 			}
-			final IWorkbenchSiteProgressService progressService = getViewSite().getService(IWorkbenchSiteProgressService.class);
+			final IWorkbenchSiteProgressService progressService= getViewSite().getService(IWorkbenchSiteProgressService.class);
 			if (current && progressService != null) {
 				progressService.incrementBusy();
 			}
 			try {
-				ObjectBrowserView.this.fInputUpdater.forceOnWorkspaceChange();
+				ObjectBrowserView.this.inputUpdater.forceOnWorkspaceChange();
 				r.refreshWorkspaceData(RWorkspace.REFRESH_COMPLETE, monitor);
 			}
 			finally {
@@ -172,25 +194,22 @@
 		
 	}
 	
-	private class RefreshHandler extends ToolRetargetableHandler {
-		
-		private final ElementUpdater fUpdater = new ElementUpdater(IWorkbenchCommandConstants.FILE_REFRESH);
+	private class RefreshHandler extends AbstractToolHandler<RProcess> {
 		
 		public RefreshHandler() {
-			super(ObjectBrowserView.this, getSite());
+			super(RConsoleTool.TYPE, null, ObjectBrowserView.this, getSite());
 			init();
 		}
 		
-		@Override
-		protected Object doExecute(final ExecutionEvent event) {
-			scheduleUpdateAll();
-			return null;
+		
+		protected void refreshElements() {
+			WorkbenchUIUtils.refreshCommandElements(REFRESH_COMMAND_ID, this, null);
 		}
 		
 		@Override
-		protected void doRefresh() {
-			super.doRefresh();
-			this.fUpdater.schedule();
+		protected @Nullable Object execute(final RProcess tool, final ExecutionEvent event) {
+			scheduleUpdateAll();
+			return null;
 		}
 		
 	}
@@ -198,71 +217,71 @@
 	private class FilterUserspaceHandler extends AbstractHandler implements IElementUpdater {
 		
 		@Override
-		public Object execute(final ExecutionEvent event) throws ExecutionException {
-			ObjectBrowserView.this.fFilterUserspace = !ObjectBrowserView.this.fFilterUserspace;
-			ObjectBrowserView.this.fSettings.put(FILTER_USERSPACEONLY_SETTINGS_KEY, ObjectBrowserView.this.fFilterUserspace);
-			ObjectBrowserView.this.fInputUpdater.forceUpdate(ObjectBrowserView.this.process);
-			ObjectBrowserView.this.fInputUpdater.schedule();
-			return null;
-		}
-		
-		@Override
 		public void updateElement(final UIElement element, final Map parameters) {
 			WorkbenchUIUtils.aboutToUpdateCommandsElements(this, element);
 			try {
-				element.setChecked(ObjectBrowserView.this.fFilterUserspace);
+				element.setChecked(ObjectBrowserView.this.filterUserspace);
 			}
 			finally {
 				WorkbenchUIUtils.finalizeUpdateCommandsElements(this);
 			}
 		}
 		
+		@Override
+		public @Nullable Object execute(final ExecutionEvent event) {
+			ObjectBrowserView.this.filterUserspace= !ObjectBrowserView.this.filterUserspace;
+			ObjectBrowserView.this.settings.put(FILTER_ONLY_USERSPACE_SETTINGS_KEY, ObjectBrowserView.this.filterUserspace);
+			ObjectBrowserView.this.inputUpdater.forceUpdate(ObjectBrowserView.this.process);
+			ObjectBrowserView.this.inputUpdater.schedule();
+			return null;
+		}
+		
 	}
 	
 	private class FilterInternalHandler extends AbstractHandler implements IElementUpdater {
 		
 		@Override
-		public Object execute(final ExecutionEvent event) throws ExecutionException {
-			ObjectBrowserView.this.fFilterIncludeInternal = !ObjectBrowserView.this.fFilterIncludeInternal;
-			ObjectBrowserView.this.fSettings.put(FILTER_INTERNALINCLUDE_SETTINGS_KEY, ObjectBrowserView.this.fFilterIncludeInternal);
-			updateFilter();
-			return null;
-		}
-		
-		@Override
 		public void updateElement(final UIElement element, final Map parameters) {
 			WorkbenchUIUtils.aboutToUpdateCommandsElements(this, element);
 			try {
-				element.setChecked(ObjectBrowserView.this.fFilterIncludeInternal);
+				element.setChecked(ObjectBrowserView.this.filterIncludeInternal);
 			}
 			finally {
 				WorkbenchUIUtils.finalizeUpdateCommandsElements(this);
 			}
 		}
 		
+		@Override
+		public @Nullable Object execute(final ExecutionEvent event) {
+			ObjectBrowserView.this.filterIncludeInternal= !ObjectBrowserView.this.filterIncludeInternal;
+			ObjectBrowserView.this.settings.put(FILTER_INCLUDE_INTERNAL_SETTINGS_KEY, ObjectBrowserView.this.filterIncludeInternal);
+			updateFilter();
+			return null;
+		}
+		
 	}
 	
 	private class SortByTypeHandler extends AbstractHandler implements IElementUpdater {
 		
 		@Override
-		public Object execute(final ExecutionEvent event) throws ExecutionException {
-			ObjectBrowserView.this.fSortByType = !ObjectBrowserView.this.fSortByType;
-			ObjectBrowserView.this.fSettings.put(SORT_BYTYPE_SETTINGS_KEY, ObjectBrowserView.this.fSortByType);
-			updateSorter();
-			return null;
-		}
-		
-		@Override
 		public void updateElement(final UIElement element, final Map parameters) {
 			WorkbenchUIUtils.aboutToUpdateCommandsElements(this, element);
 			try {
-				element.setChecked(ObjectBrowserView.this.fSortByType);
+				element.setChecked(ObjectBrowserView.this.sortByType);
 			}
 			finally {
 				WorkbenchUIUtils.finalizeUpdateCommandsElements(this);
 			}
 		}
 		
+		@Override
+		public @Nullable Object execute(final ExecutionEvent event) {
+			ObjectBrowserView.this.sortByType= !ObjectBrowserView.this.sortByType;
+			ObjectBrowserView.this.settings.put(SORT_BY_TYPE_SETTINGS_KEY, ObjectBrowserView.this.sortByType);
+			updateSorter();
+			return null;
+		}
+		
 	}
 	
 	private class TreeElementSelection extends TreeSelectionProxy implements ElementNameProvider {
@@ -272,8 +291,8 @@
 		}
 		
 		@Override
-		public ElementName getElementName(final Object selectionElement) {
-			if (selectionElement instanceof TreePath) { 
+		public @Nullable ElementName getElementName(final Object selectionElement) {
+			if (selectionElement instanceof TreePath) {
 				return getFQElementName((TreePath) selectionElement);
 			}
 			return null;
@@ -284,7 +303,7 @@
 	private class TreeSelectionProvider extends PostSelectionProviderProxy {
 		
 		public TreeSelectionProvider() {
-			super(ObjectBrowserView.this.fTreeViewer);
+			super(ObjectBrowserView.this.treeViewer);
 		}
 		
 		@Override
@@ -297,16 +316,16 @@
 	private class HoverManager extends ColumnHoverManager {
 		
 		HoverManager() {
-			super(ObjectBrowserView.this.fTreeViewer, ObjectBrowserView.this.fTokenOwner, new RElementInfoHoverCreator(IInfoHover.MODE_TOOLTIP));
+			super(ObjectBrowserView.this.treeViewer, ObjectBrowserView.this.tokenOwner, new RElementInfoHoverCreator(IInfoHover.MODE_TOOLTIP));
 			
-			final ColumnHoverStickyManager stickyManager = new ColumnHoverStickyManager(ObjectBrowserView.this.fTokenOwner, this);
+			final ColumnHoverStickyManager stickyManager= new ColumnHoverStickyManager(ObjectBrowserView.this.tokenOwner, this);
 			getInternalAccessor().setInformationControlReplacer(stickyManager);
 		}
 		
 		@Override
-		protected Object prepareHoverInformation(final ViewerCell cell) {
-			final TreePath treePath = cell.getViewerRow().getTreePath();
-			final RElementName elementName = getFQElementName(treePath);
+		protected @Nullable Object prepareHoverInformation(final ViewerCell cell) {
+			final TreePath treePath= cell.getViewerRow().getTreePath();
+			final RElementName elementName= getFQElementName(treePath);
 			if (elementName != null && elementName.getScope() != null) {
 				return elementName;
 			}
@@ -314,9 +333,9 @@
 		}
 		
 		@Override
-		protected Object getHoverInformation(final Object element) {
+		protected @Nullable Object getHoverInformation(final Object element) {
 			if (element instanceof RElementName && ObjectBrowserView.this.process != null) {
-				final RElementInfoTask updater = new RElementInfoTask((RElementName) element);
+				final RElementInfoTask updater= new RElementInfoTask((RElementName) element);
 				return updater.load(getTool(), getSubjectControl(), ObjectBrowserView.this);
 			}
 			return null;
@@ -325,72 +344,94 @@
 	}
 	
 	
-	private TreeViewer fTreeViewer;
-	private TreeSelectionProvider fTreeSelectionProvider;
-	private ColumnWidgetTokenOwner fTokenOwner;
-	private Clipboard fClipboard;
-	private StatusLineMessageManager statusLine;
+	private IDialogSettings settings;
 	
-	private IDialogSettings fSettings;
+	final Object sourceLock= new Object();
+	private IToolRegistryListener toolRegistryListener;
+	private @Nullable RProcess process; // note: we write only in ui thread
+	private final CopyOnWriteIdentityListSet<ActiveToolListener> toolListeners= new CopyOnWriteIdentityListSet<>();
 	
-	final Object processLock = new Object();
-	private RProcess process; // note: we write only in ui thread
 	
-	private IToolRegistryListener fToolRegistryListener;
-	private final FastList<IToolRetargetable> fToolListenerList= new FastList<>(IToolRetargetable.class);
+	private TreeViewer treeViewer;
+	private ColumnWidgetTokenOwner tokenOwner;
+	private Clipboard clipboard;
 	
-	private final RefreshWorkspaceR fManualRefreshRunnable = new RefreshWorkspaceR();
 	
-	private final ContentJob fInputUpdater = new ContentJob(this);
+	private final ContentJob inputUpdater= new ContentJob(this);
+	private boolean isUpdating;
 	
-	private ContentProvider contentProvider;
+	private final RefreshWorkspaceR manualRefreshRunnable= new RefreshWorkspaceR();
 	
-	private boolean fFilterUserspace;
-	private boolean fFilterIncludeInternal;
-	private String fFilterText;
-	private boolean fSortByType;
+	private boolean filterUserspace;
+	private boolean filterIncludeInternal;
+	private String filterText;
+	private boolean sortByType;
 	
-	private SearchContributionItem fSearchTextItem;
+	private SearchContributionItem searchTextItem;
 	
-	private boolean fFilterUserspaceActivated;
+	private RElementInputContentProvider<ContentInput> inputContentProvider;
 	
-	private CopyElementNameHandler fCopyElementNameHandler;
-	private DeleteHandler fDeleteElementHandler;
-	private PrintHandler fPrintElementHandler;
-	private IHandler2 fOpenInEditorHandler;
+	private boolean filterUserspaceActivated;
 	
-	private Object fCurrentInfoObject;
 	
-	private ColumnHoverManager fHoveringController;
+	private ViewActionUtil actionUtil;
+	private ContextHandlers handlers;
 	
-	private AbstractHandler fSearchStartHandler;
+	private @Nullable Object currentInfoObject;
 	
-	private HandlerContributionItem fRefreshToolbarItem;
-	private HandlerContributionItem fRefreshMenuItem;
-	private boolean fRefreshDirtyIndicator;
+	private ColumnHoverManager hoveringController;
+	
+	private HandlerContributionItem refreshToolbarItem;
+	private HandlerContributionItem refreshMenuItem;
+	private boolean refreshDirtyIndicator;
 	
 	
 	public ObjectBrowserView() {
 	}
 	
 	@Override
+	public void dispose() {
+		if (this.toolRegistryListener != null) {
+			NicoUI.getToolRegistry().removeListener(this.toolRegistryListener);
+			this.toolRegistryListener= null;
+		}
+		setTool(null, false);
+		this.inputUpdater.cancel();
+		
+		if (this.hoveringController != null) {
+			this.hoveringController.dispose();
+			this.hoveringController= null;
+		}
+		if (this.handlers != null) {
+			this.handlers.dispose();
+			this.handlers= null;
+		}
+		
+		super.dispose();
+		
+		if (this.clipboard != null) {
+			this.clipboard.dispose();
+			this.clipboard= null;
+		}
+	}
+	
+	
+	@Override
 	public void init(final IViewSite site, final IMemento memento) throws PartInitException {
 		super.init(site, memento);
 		
-		this.statusLine = new StatusLineMessageManager(site.getActionBars().getStatusLineManager());
+		this.settings= DialogUtils.getDialogSettings(RUIPlugin.getInstance(), "ObjectBrowser");
 		
-		this.fSettings = DialogUtils.getDialogSettings(RUIPlugin.getInstance(), "ObjectBrowser");
-		
-		this.fFilterUserspaceActivated = this.fFilterUserspace = this.fSettings.getBoolean(FILTER_USERSPACEONLY_SETTINGS_KEY);
-		this.fFilterIncludeInternal = this.fSettings.getBoolean(FILTER_INTERNALINCLUDE_SETTINGS_KEY);
-		this.fSortByType = this.fSettings.getBoolean(SORT_BYTYPE_SETTINGS_KEY);
+		this.filterUserspaceActivated= this.filterUserspace= this.settings.getBoolean(FILTER_ONLY_USERSPACE_SETTINGS_KEY);
+		this.filterIncludeInternal= this.settings.getBoolean(FILTER_INCLUDE_INTERNAL_SETTINGS_KEY);
+		this.sortByType= this.settings.getBoolean(SORT_BY_TYPE_SETTINGS_KEY);
 	}
 	
 	@Override
 	public void saveState(final IMemento memento) {
 		super.saveState(memento);
 		
-		this.fSettings = DialogUtils.getDialogSettings(RUIPlugin.getInstance(), "ObjectBrowser");
+		this.settings= DialogUtils.getDialogSettings(RUIPlugin.getInstance(), "ObjectBrowser");
 	}
 	
 	
@@ -398,220 +439,200 @@
 	public void createPartControl(final Composite parent) {
 		parent.setLayout(LayoutUtils.newSashGrid());
 		
-		this.fTreeViewer = new TreeViewer(parent, SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI);
-		this.fTreeViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 3));
-		this.fTreeViewer.setUseHashlookup(true);
+		this.treeViewer= new TreeViewer(parent, SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI);
+		this.treeViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
 		
-		this.fTreeSelectionProvider = new TreeSelectionProvider();
-		
-		this.fTreeViewer.setLabelProvider(new RLabelProvider(RLabelProvider.COUNT) {
-			@Override
-			protected String getEnvCountInfo(final RProcessREnvironment envir) {
-				final StringBuilder textBuilder = getTextBuilder();
-				textBuilder.append(" ("); //$NON-NLS-1$
-				final ContentInput input = ObjectBrowserView.this.contentProvider.getInput();
-				if (input != null && input.hasEnvFilter()) {
-					final Object[] children = input.getEnvFilterChildren(envir);
-					if (children != null) {
-						textBuilder.append(children.length);
-					}
-					else {
-						textBuilder.append('-');
-					}
-					textBuilder.append('/');
-				}
-				textBuilder.append(envir.getLength());
-				textBuilder.append(')');
-				return textBuilder.toString();
-			}
-		});
-		this.fTreeViewer.addDoubleClickListener(new IDoubleClickListener() {
-			@Override
-			public void doubleClick(final DoubleClickEvent event) {
-				final IStructuredSelection selection = (IStructuredSelection) event.getSelection();
-				if (selection.size() != 1) {
-					return;
-				}
-				final Object element = selection.getFirstElement();
-				if (element instanceof RObject) {
-					final RObject object = (RObject) element;
-					switch (object.getRObjectType()) {
-					case RObject.TYPE_ENVIRONMENT:
-					case RObject.TYPE_LIST:
-					case RObject.TYPE_DATAFRAME:
-					case RObject.TYPE_S4OBJECT:
-					case RObject.TYPE_REFERENCE:
-						ObjectBrowserView.this.fTreeViewer.setExpandedState(element, !ObjectBrowserView.this.fTreeViewer.getExpandedState(element));
-					}
-				}
-			}
-		});
-		this.fTreeSelectionProvider.addPostSelectionChangedListener(new ISelectionChangedListener() {
+		final IPostSelectionProvider treeSelectionProvider= new TreeSelectionProvider();
+		treeSelectionProvider.addPostSelectionChangedListener(new ISelectionChangedListener() {
 			@Override
 			public void selectionChanged(final SelectionChangedEvent event) {
 				updateSelectionInfo((ITreeSelection) event.getSelection());
 			}
 		});
-		this.contentProvider = new ContentProvider();
-		this.fTreeViewer.setContentProvider(this.contentProvider);
+		
+		this.treeViewer.setLabelProvider(new RElementInputLabelProvider());
+		
+		this.treeViewer.setUseHashlookup(true);
+		this.inputContentProvider= new RElementInputContentProvider();
+		this.treeViewer.setContentProvider(this.inputContentProvider);
 		updateSorter();
-		this.fTreeViewer.setInput(this);
+		this.treeViewer.setInput(this);
 		
-		this.fTokenOwner = new ColumnWidgetTokenOwner(this.fTreeViewer);
-		this.fHoveringController = new HoverManager();
-		this.fHoveringController.setSizeConstraints(100, 12, false, true);
-		this.fHoveringController.install(this.fTreeViewer.getTree());
+		this.tokenOwner= new ColumnWidgetTokenOwner(this.treeViewer);
+		this.hoveringController= new HoverManager();
+		this.hoveringController.setSizeConstraints(100, 12, false, true);
+		this.hoveringController.install(this.treeViewer.getTree());
 		
-		createActions();
+		final IViewSite site= getViewSite();
+		site.setSelectionProvider(treeSelectionProvider);
+		this.actionUtil= new ViewActionUtil(this);
+		this.handlers= new ContextHandlers(site.getService(IHandlerService.class));
+		initActions(site, this.handlers);
+		contributeToActionBars(site, site.getActionBars(), this.handlers);
 		hookContextMenu();
-		getSite().setSelectionProvider(this.fTreeSelectionProvider);
-		
-		final ViewerDragSupport dragSupport = new ViewerDragSupport(this.fTreeViewer);
-		dragSupport.addDragSourceListener(new ViewerDragSupport.TextDragSourceListener(this.fTreeViewer));
-		dragSupport.init();
 		
 		// listen on console changes
-		final IToolRegistry toolRegistry = NicoUI.getToolRegistry();
-		this.fToolRegistryListener = new IToolRegistryListener() {
+		final IToolRegistry toolRegistry= NicoUI.getToolRegistry();
+		this.toolRegistryListener= new IToolRegistryListener() {
 			@Override
 			public void toolSessionActivated(final ToolSessionUIData sessionData) {
-				final ToolProcess process = sessionData.getProcess();
+				final ToolProcess process= sessionData.getProcess();
 				UIAccess.getDisplay().syncExec(new Runnable() {
 					@Override
 					public void run() {
-						connect(process);
+						setTool(process, true);
 					}
 				});
 			}
 			@Override
 			public void toolTerminated(final ToolSessionUIData sessionData) {
-				final ToolProcess process = sessionData.getProcess();
+				final ToolProcess process= sessionData.getProcess();
 				UIAccess.getDisplay().syncExec(new Runnable() {
 					@Override
 					public void run() {
 						if (process == getTool()) {
-							connect(null);
+							setTool(null, true);
 						}
 					}
 				});
 			}
 		};
-		toolRegistry.addListener(this.fToolRegistryListener, getViewSite().getPage());
-		connect(toolRegistry.getActiveToolSession(getViewSite().getPage()).getProcess());
+		toolRegistry.addListener(this.toolRegistryListener, getViewSite().getPage());
+		setTool(toolRegistry.getActiveToolSession(getViewSite().getPage()).getProcess(), true);
 	}
 	
 	TreeViewer getViewer() {
-		return this.fTreeViewer;
+		return this.treeViewer;
 	}
 	
-	private void createActions() {
-		final IHandlerService handlerService = getSite().getService(IHandlerService.class);
-		final IContextService contexts = getSite().getService(IContextService.class);
+	protected void initActions(final IServiceLocator serviceLocator, final ContextHandlers handlers) {
+		final IContextService contexts= serviceLocator.getService(IContextService.class);
 		
 		contexts.activateContext("org.eclipse.statet.workbench.contexts.StructuredElementViewer"); //$NON-NLS-1$
 		
-		final RefreshHandler refreshHandler = new RefreshHandler();
-		handlerService.activateHandler(IWorkbenchCommandConstants.FILE_REFRESH, refreshHandler);
-		final CollapseAllHandler collapseAllHandler = new CollapseAllHandler(this.fTreeViewer);
-		handlerService.activateHandler(CollapseAllHandler.COMMAND_ID, collapseAllHandler);
-		this.fCopyElementNameHandler = new CopyElementNameHandler(this);
-		handlerService.activateHandler(ISourceEditorCommandIds.COPY_ELEMENT_NAME, this.fCopyElementNameHandler);
-		handlerService.activateHandler(IWorkbenchCommandConstants.EDIT_COPY, this.fCopyElementNameHandler);
-		this.fDeleteElementHandler = new DeleteHandler(this);
-		handlerService.activateHandler(IWorkbenchCommandConstants.EDIT_DELETE, this.fDeleteElementHandler);
-		this.fPrintElementHandler = new PrintHandler(this);
-		handlerService.activateHandler(RunPrintInR.COMMAND_ID, this.fPrintElementHandler);
-		final InformationDispatchHandler infoHandler = new InformationDispatchHandler(this.fTokenOwner);
-		handlerService.activateHandler(InformationDispatchHandler.COMMAND_ID, infoHandler);
-		this.fOpenInEditorHandler = new OpenInEditorHandler();
-		handlerService.activateHandler(OPEN_COMMAND_ID, this.fOpenInEditorHandler);
+		handlers.addActivate(REFRESH_COMMAND_ID, new RefreshHandler());
 		
-		this.fSearchTextItem = new SearchContributionItem("search.text", //$NON-NLS-1$
+		final CopyRElementHandler copyHandler= new CopyRElementHandler(this.actionUtil,
+				(ILabelProvider) this.treeViewer.getLabelProvider() );
+		handlers.addActivate(IWorkbenchCommandConstants.EDIT_COPY, copyHandler);
+		handlers.addActivate(ISourceEditorCommandIds.COPY_ELEMENT_NAME,
+				new CopyRElementNameHandler(this.actionUtil) );
+		handlers.addActivate(IWorkbenchCommandConstants.EDIT_DELETE,
+				new DeleteHandler(this) );
+		
+		final ViewerDragSupport dragSupport= new ViewerDragSupport(this.treeViewer);
+		dragSupport.addDragSourceListener(new RElementViewerDragSourceListener(
+				copyHandler, this.treeViewer ));
+		dragSupport.init();
+		
+		handlers.addActivate(OPEN_COMMAND_ID,
+				new OpenInEditorHandler() );
+		handlers.addActivate(PRINT_COMMAND_ID,
+				new PrintRElementHandler(this.actionUtil) );
+		handlers.addActivate(InformationDispatchHandler.COMMAND_ID,
+				new InformationDispatchHandler(this.tokenOwner) );
+		
+		handlers.add(FILTER_INCLUDE_INTERNAL_COMMAND_ID, new FilterInternalHandler());
+		handlers.add(FILTER_ONLY_USERSPACE_COMMAND_ID, new FilterUserspaceHandler());
+		
+		handlers.addActivate(NAVIGATE_COLLAPSE_ALL, new CollapseAllHandler(this.treeViewer));
+		RElementInputUtils.addDoubleClickExpansion(this.treeViewer);
+		
+		this.searchTextItem= new SearchContributionItem("search.text", //$NON-NLS-1$
 				SearchContributionItem.VIEW_TOOLBAR, true ) {
 			@Override
 			protected void search() {
-				ObjectBrowserView.this.fFilterText = getText();
+				ObjectBrowserView.this.filterText= getText();
 				updateFilter();
 			}
 		};
-		this.fSearchTextItem.setToolTip("Filter Elements");
-		this.fSearchTextItem.setSizeControl(this.fTreeViewer.getControl().getParent());
-		this.fSearchTextItem.setResultControl(this.fTreeViewer.getTree());
+		this.searchTextItem.setToolTip("Filter Elements");
+		this.searchTextItem.setSizeControl(this.treeViewer.getControl().getParent());
+		this.searchTextItem.setResultControl(this.treeViewer.getTree());
 		
-		this.fSearchStartHandler = new AbstractHandler() {
-			@Override
-			public Object execute(final ExecutionEvent arg0) {
-				ObjectBrowserView.this.fSearchTextItem.show();
-				return null;
-			}
-		};
-		handlerService.activateHandler(IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE, this.fSearchStartHandler);
+		handlers.addActivate(IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE,
+				new AbstractHandler() {
+					@Override
+					public Object execute(final ExecutionEvent arg0) {
+						ObjectBrowserView.this.searchTextItem.show();
+						return null;
+					}
+				});
 		// add next/previous handler
+	}
+	
+	protected void contributeToActionBars(final IServiceLocator serviceLocator,
+			final IActionBars actionBars, final HandlerCollection handlers) {
+		final IMenuManager menuManager= actionBars.getMenuManager();
+		final IToolBarManager toolbarManager= actionBars.getToolBarManager();
 		
-		final ToggleAutoRefreshHandler autoRefreshHandler = new ToggleAutoRefreshHandler(this);
+		final ToggleAutoRefreshHandler autoRefreshHandler= new ToggleAutoRefreshHandler(this);
 		
-		final IActionBars actionBars = getViewSite().getActionBars();
-		final IMenuManager viewMenu = actionBars.getMenuManager();
-		final IToolBarManager viewToolbar = actionBars.getToolBarManager();
-		
-		viewMenu.add(new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
-				"Filter.OnlyUserspace", HandlerContributionItem.NO_COMMAND_ID, null, //$NON-NLS-1$
-				null, null, null,
-				"Show non-&package Variables only", null, null,
-				HandlerContributionItem.STYLE_CHECK, null, false),
-				new FilterUserspaceHandler() ));
-		viewMenu.add(new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
-				"Filter.IncludeInternal", HandlerContributionItem.NO_COMMAND_ID, null, //$NON-NLS-1$
-				null, null, null,
-				"Show &Internal Variables ('.*')", null, null,
-				HandlerContributionItem.STYLE_CHECK, null, false),
-				new FilterInternalHandler() ));
-		viewMenu.add(new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
-				"Sort.ByType", HandlerContributionItem.NO_COMMAND_ID, null, //$NON-NLS-1$
-				null, null, null,
-				"Sort by &Type", null, null,
-				HandlerContributionItem.STYLE_CHECK, null, false),
+		menuManager.add(new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						"Filter.OnlyUserspace", HandlerContributionItem.NO_COMMAND_ID, null, //$NON-NLS-1$
+						null, null, null,
+						"Show non-&package Variables only", null, null,
+						HandlerContributionItem.STYLE_CHECK, null, false ),
+				nonNullAssert(handlers.get(FILTER_ONLY_USERSPACE_COMMAND_ID)) ));
+		menuManager.add(new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						null, HandlerContributionItem.NO_COMMAND_ID, null, //$NON-NLS-1$
+						null, null, null,
+						"Show &Internal Variables ('.*')", null, null,
+						HandlerContributionItem.STYLE_CHECK, null, false ),
+				nonNullAssert(handlers.get(FILTER_INCLUDE_INTERNAL_COMMAND_ID)) ));
+		menuManager.add(new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						"Sort.ByType", HandlerContributionItem.NO_COMMAND_ID, null, //$NON-NLS-1$
+						null, null, null,
+						"Sort by &Type", null, null,
+						HandlerContributionItem.STYLE_CHECK, null, false ),
 				new SortByTypeHandler() ));
 		
-		viewMenu.add(new Separator());
-		final HandlerContributionItem autoRefreshItem = new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
-				null, HandlerContributionItem.NO_COMMAND_ID, null, 
-				null, null, null,
-				"Refresh &automatically", null, null,
-				HandlerContributionItem.STYLE_CHECK, null, false),
+		menuManager.add(new Separator());
+		final HandlerContributionItem autoRefreshItem= new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						null, HandlerContributionItem.NO_COMMAND_ID, null,
+						null, null, null,
+						"Refresh &automatically", null, null,
+						HandlerContributionItem.STYLE_CHECK, null, false ),
 				autoRefreshHandler );
-		viewMenu.add(autoRefreshItem);
-		this.fRefreshMenuItem = new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
-				null, IWorkbenchCommandConstants.FILE_REFRESH, null, 
-				StatetImages.getDescriptor(StatetImages.TOOL_REFRESH), StatetImages.getDescriptor(StatetImages.TOOLD_REFRESH), null,
-				"&Refresh", null, null,
-				HandlerContributionItem.STYLE_PUSH, null, false),
-				refreshHandler );
-		viewMenu.add(this.fRefreshMenuItem);
+		menuManager.add(autoRefreshItem);
+		this.refreshMenuItem= new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						"Refresh", REFRESH_COMMAND_ID, null, //$NON-NLS-1$
+						StatetImages.getDescriptor(StatetImages.TOOL_REFRESH), StatetImages.getDescriptor(StatetImages.TOOLD_REFRESH), null,
+						"&Refresh", null, null,
+						HandlerContributionItem.STYLE_PUSH, null, false ),
+				handlers );
+		menuManager.add(this.refreshMenuItem);
 		
-		viewMenu.addMenuListener(new IMenuListener() {
+		menuManager.addMenuListener(new IMenuListener() {
 			@Override
 			public void menuAboutToShow(final IMenuManager manager) {
 				autoRefreshItem.update();
 			}
 		});
 		
-		viewToolbar.add(this.fSearchTextItem);
-		viewToolbar.add(new Separator());
-		this.fRefreshToolbarItem = new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
-				"Refresh", IWorkbenchCommandConstants.FILE_REFRESH, null, //$NON-NLS-1$
-				StatetImages.getDescriptor(StatetImages.TOOL_REFRESH), StatetImages.getDescriptor(StatetImages.TOOLD_REFRESH), null,
-				null, null, null,
-				HandlerContributionItem.STYLE_PUSH, null, false),
-				refreshHandler);
-		this.fRefreshToolbarItem.setVisible(false);
-		viewToolbar.add(this.fRefreshToolbarItem);
-		viewToolbar.add(new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
-				"Collapse.All", CollapseAllHandler.COMMAND_ID, null, //$NON-NLS-1$
-				null, null, null,
-				null, null, null,
-				HandlerContributionItem.STYLE_PUSH, null, false),
-				collapseAllHandler));
+		toolbarManager.add(this.searchTextItem);
+		toolbarManager.add(new Separator());
+		this.refreshToolbarItem= new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						"Refresh", REFRESH_COMMAND_ID, null, //$NON-NLS-1$
+						StatetImages.getDescriptor(StatetImages.TOOL_REFRESH), StatetImages.getDescriptor(StatetImages.TOOLD_REFRESH), null,
+						null, null, null,
+						HandlerContributionItem.STYLE_PUSH, null, false ),
+				handlers );
+		this.refreshToolbarItem.setVisible(false);
+		toolbarManager.add(this.refreshToolbarItem);
+		toolbarManager.add(new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						null, NAVIGATE_COLLAPSE_ALL, null,
+						null, null, null,
+						null, null, null,
+						HandlerContributionItem.STYLE_PUSH, null, false ),
+				handlers ));
 	}
 	
 	void updateAutoRefresh(final boolean enabled) {
@@ -626,190 +647,204 @@
 			updateDirty(this.process.getWorkspaceData().isRObjectDBDirty());
 		}
 		
-		if (this.fRefreshToolbarItem.isVisible() != enabled) {
+		if (this.refreshToolbarItem.isVisible() != enabled) {
 			return;
 		}
-		this.fRefreshToolbarItem.setVisible(!enabled);
-		final IContributionManager manager = this.fRefreshToolbarItem.getParent();
+		this.refreshToolbarItem.setVisible(!enabled);
+		final IContributionManager manager= this.refreshToolbarItem.getParent();
 		manager.update(true);
-		this.fSearchTextItem.resize();
+		this.searchTextItem.resize();
 	}
 	
 	void updateDirty(final boolean enabled) {
-		if (this.fRefreshDirtyIndicator == enabled) {
+		if (this.refreshDirtyIndicator == enabled) {
 			return;
 		}
-		final ImageDescriptor icon = (enabled) ?
+		final ImageDescriptor icon= nonNullAssert((enabled) ?
 				RUIPlugin.getInstance().getImageRegistry().getDescriptor(RUIPlugin.IMG_LOCTOOL_REFRESH_RECOMMENDED) :
-				StatetImages.getDescriptor(StatetImages.TOOL_REFRESH);
-		this.fRefreshToolbarItem.setIcon(icon);
-		this.fRefreshMenuItem.setIcon(icon);
-		this.fRefreshDirtyIndicator = enabled;
+				StatetImages.getDescriptor(StatetImages.TOOL_REFRESH) );
+		this.refreshToolbarItem.setIcon(icon);
+		this.refreshMenuItem.setIcon(icon);
+		this.refreshDirtyIndicator= enabled;
 	}
 	
-	
 	private void hookContextMenu() {
-		final MenuManager menuManager = new MenuManager("ContextMenu", //$NON-NLS-1$
+		final MenuManager menuManager= new MenuManager("ContextMenu", //$NON-NLS-1$
 				"org.eclipse.statet.r.menus.RObjectBrowserContextMenu" ); //$NON-NLS-1$
 		menuManager.setRemoveAllWhenShown(true);
-		menuManager.addMenuListener(new IMenuListener() {
-			@Override
-			public void menuAboutToShow(final IMenuManager m) {
-				contextMenuAboutToShow(m);
-			}
-		});
-		final Menu contextMenu = menuManager.createContextMenu(this.fTreeViewer.getTree());
-		this.fTreeViewer.getTree().setMenu(contextMenu);
-		getSite().registerContextMenu(menuManager, this.fTreeViewer);
+		menuManager.addMenuListener(this::fillContextMenu);
+		final Menu contextMenu= menuManager.createContextMenu(this.treeViewer.getTree());
+		this.treeViewer.getTree().setMenu(contextMenu);
+		getSite().registerContextMenu(menuManager, this.treeViewer);
 	}
 	
-	private void contextMenuAboutToShow(final IMenuManager m) {
-		m.add(new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
-				null, OPEN_COMMAND_ID, null,
-				null, null, null,
-				"Open in Data &Viewer", null, null,
-				HandlerContributionItem.STYLE_PUSH, null, true), this.fOpenInEditorHandler));
+	private void fillContextMenu(final IMenuManager m) {
+		final IServiceLocator serviceLocator= getSite();
+		final ContextHandlers handlers= this.handlers;
 		
-		m.add(new Separator("edit"));
-		m.add(new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
-				"Copy.ElementName", ISourceEditorCommandIds.COPY_ELEMENT_NAME, null, //$NON-NLS-1$
-				null, null, null,
-				null, null, null,
-				HandlerContributionItem.STYLE_PUSH, null, false),
-				this.fCopyElementNameHandler));
-		m.add(new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
-				"Delete", IWorkbenchCommandConstants.EDIT_DELETE, null, //$NON-NLS-1$
-				null, null, null,
-				null, null, null,
-				HandlerContributionItem.STYLE_PUSH, null, false),
-				this.fDeleteElementHandler));
+		m.add(new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						null, OPEN_COMMAND_ID, null,
+						null, null, null,
+						"Open in Data &Viewer", null, null,
+						HandlerContributionItem.STYLE_PUSH, null, true ),
+				handlers ));
+		
+		m.add(new Separator(UIActions.EDIT_GROUP_ID));
+		m.add(new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						"Copy", IWorkbenchCommandConstants.EDIT_COPY, null, //$NON-NLS-1$
+						null, null, null,
+						null, null, null,
+						HandlerContributionItem.STYLE_PUSH, null, false ),
+				handlers ));
+		m.add(new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						"Copy.ElementName", ISourceEditorCommandIds.COPY_ELEMENT_NAME, null, //$NON-NLS-1$
+						null, null, null,
+						null, null, null,
+						HandlerContributionItem.STYLE_PUSH, null, false ),
+				handlers ));
+		m.add(new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						"Delete", IWorkbenchCommandConstants.EDIT_DELETE, null, //$NON-NLS-1$
+						null, null, null,
+						null, null, null,
+						HandlerContributionItem.STYLE_PUSH, null, false ),
+				handlers ));
 		m.add(new Separator());
-		m.add(new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
-				null, RunPrintInR.COMMAND_ID, null, 
-				null, null, null,
-				null, null, null,
-				HandlerContributionItem.STYLE_PUSH, null, false),
-				this.fPrintElementHandler));
+		m.add(new HandlerContributionItem(
+				new CommandContributionItemParameter(serviceLocator,
+						null, PRINT_COMMAND_ID, null,
+						null, null, null,
+						null, null, null,
+						HandlerContributionItem.STYLE_PUSH, null, false ),
+				handlers ));
 		
 		m.add(new Separator(ADDITIONS_GROUP_ID));
 	}
 	
-	/** May only be called in UI thread */
-	public void connect(final ToolProcess tool) {
-		if (this.process == tool) {
-			return;
-		}
-		final RProcess oldProcess = this.process;
-		if (oldProcess != null) {
-			oldProcess.getWorkspaceData().removePropertyListener(this.fInputUpdater);
-		}
-		synchronized (this.processLock) {
-			this.process = (RProcess) 
-					((tool != null && tool.isProvidingFeatureSet(RConsoleTool.R_DATA_FEATURESET_ID)) ?
-							tool : null);
-		}
-		if (this.fHoveringController != null) {
-			this.fHoveringController.stop();
-		}
-		if (oldProcess != null) {
-			clearInfo();
-			oldProcess.getQueue().remove(new ToolRunnable[] { this.fManualRefreshRunnable });
-		}
-		if (!UIAccess.isOkToUse(this.fTreeViewer)) {
-			return;
-		}
-		for (final IToolRetargetable listener : this.fToolListenerList.toArray()) {
-			listener.setTool(this.process);
-		}
-		this.fInputUpdater.forceUpdate(this.process);
-		if (this.process != null) {
-			this.process.getWorkspaceData().addPropertyListener(this.fInputUpdater);
-			updateAutoRefresh(this.process.getWorkspaceData().isAutoRefreshEnabled());
-		}
-		this.fInputUpdater.schedule();
+	@Override
+	public void setFocus() {
+		this.treeViewer.getControl().setFocus();
 	}
 	
+	
 	@Override
-	public RProcess getTool() {
+	public @Nullable RProcess getTool() {
 		return this.process;
 	}
 	
-	public ITreeSelection getSelection() {
-		return (ITreeSelection) this.fTreeViewer.getSelection();
-	}
-	
-	
-	public boolean getShowInternal() {
-		return this.fFilterIncludeInternal;
-	}
-	
-	public boolean getShowConsenseUserspace() {
-		return this.fFilterUserspace;
-	}
-	
-	public String getSearchText() {
-		return this.fFilterText;
-	}
-	
-	
 	@Override
-	public void addToolRetargetable(final IToolRetargetable action) {
-		this.fToolListenerList.add(action);
+	public void addToolListener(final ActiveToolListener action) {
+		this.toolListeners.add(action);
 	}
 	
 	@Override
-	public void removeToolRetargetable(final IToolRetargetable action) {
-		this.fToolListenerList.remove(action);
+	public void removeToolListener(final ActiveToolListener action) {
+		this.toolListeners.remove(action);
 	}
 	
-	@Override
-	@SuppressWarnings("unchecked")
-	public <T> T getAdapter(final Class<T> adapterType) {
-		if (adapterType == Tool.class) {
-			return (T) this.process;
+	/** UI thread only */
+	private void setTool(final @Nullable ToolProcess tool, final boolean update) {
+		final RProcess process= (tool != null
+						&& tool.isProvidingFeatureSet(RConsoleTool.R_DATA_FEATURESET_ID)
+						&& !tool.isTerminated() ) ?
+				(RProcess) tool : null;
+		if (this.process == tool) {
+			return;
 		}
-		return super.getAdapter(adapterType);
+		
+		final RProcess oldProcess= this.process;
+		if (oldProcess != null) {
+			oldProcess.getWorkspaceData().removePropertyListener(this.inputUpdater);
+		}
+		synchronized (this.sourceLock) {
+			this.process= process;
+		}
+		if (this.hoveringController != null) {
+			this.hoveringController.stop();
+		}
+		clearActionInfo();
+		if (oldProcess != null) {
+			oldProcess.getQueue().remove(this.manualRefreshRunnable);
+		}
+		
+		final ActiveToolEvent event= new ActiveToolEvent(ActiveToolEvent.TOOL_ACTIVATED, process);
+		for (final ActiveToolListener listener : this.toolListeners) {
+			listener.onToolChanged(event);
+		}
+		this.inputUpdater.forceUpdate(process);
+		if (process != null) {
+			process.getWorkspaceData().addPropertyListener(this.inputUpdater);
+			updateAutoRefresh(process.getWorkspaceData().isAutoRefreshEnabled());
+		}
+		
+		if (update) {
+			this.inputUpdater.schedule();
+		}
 	}
 	
+	public ITreeSelection getSelection() {
+		return (ITreeSelection) this.treeViewer.getSelection();
+	}
+	
+	
+	public boolean getShowCondensedUserspace() {
+		return this.filterUserspace;
+	}
+	
+	public boolean getFilterIncludeInternal() {
+		return this.filterIncludeInternal;
+	}
+	
+	public String getFilterSearchText() {
+		return this.filterText;
+	}
+	
+	
 	private void scheduleUpdateAll() {
 		if (this.process != null) {
-			this.process.getQueue().add(this.fManualRefreshRunnable);
+			this.process.getQueue().add(this.manualRefreshRunnable);
 		}
 	}
 	
-	/** May only be called in UI thread (called by update job) */
-	void updateViewer(final List<RProcessREnvironment> updateEnvirs, final ContentInput input) {
-		if (!UIAccess.isOkToUse(this.fTreeViewer)) {
+	/** UI thread only (called by update job) */
+	void updateView(final @Nullable ContentInput input,
+			final @Nullable List<RProcessREnvironment> updateEnvirs) {
+		if (!UIAccess.isOkToUse(this.treeViewer)) {
 			return;
 		}
-		this.fHoveringController.stop();
+		this.isUpdating= true;
 		
-		this.contentProvider.setInput(input);
+		this.hoveringController.stop();
 		
-		final boolean changed = input.processChanged;
-		ISelection selection = null;
-		if (changed) {
-			input.processChanged = false;
+		final ContentInput prevInput= this.inputContentProvider.getInput();
+		this.inputContentProvider.setInput(input);
+		final boolean processChanged= Objects.equals(
+				(input != null) ? input.getSource() : null,
+				(prevInput != null) ? prevInput.getSource() : null );
+		
+		ISelection selection= null;
+		// If filter is changed, we have to retain the selection manually
+		if (input != null && !processChanged && input.isFilterUserspace() != this.filterUserspaceActivated) {
+			selection= this.treeViewer.getSelection();
 		}
-		/*else*/ if (input.showCondensedUserspace != this.fFilterUserspaceActivated) {
-			// If filter is changed, we have to retain the selection manually
-			selection = this.fTreeViewer.getSelection();
-		}
-		this.fFilterUserspaceActivated = input.showCondensedUserspace;
+		this.filterUserspaceActivated= (input != null) ? input.isFilterUserspace() : false;
 		
-		final Set<RReference> previousReferences = this.contentProvider.resetUsedReferences();
-		if (updateEnvirs != null) {
+		final Set<RReference> previousReferences= this.inputContentProvider.resetUsedReferences();
+		if (input != null && updateEnvirs != null) {
 			for (final RProcessREnvironment entry : updateEnvirs) {
-				this.fTreeViewer.refresh(entry, true);
+				this.treeViewer.refresh(entry, true);
 			}
 			if (!previousReferences.isEmpty()) {
-				final Set<RReference> usedReferences = this.contentProvider.getUsedReferences();
+				final Set<RReference> usedReferences= this.inputContentProvider.getUsedReferences();
 				ITER_REFS: for (final RReference reference : previousReferences) {
 					if (!usedReferences.contains(reference)) {
 						// Update the envir copy in the viewer, if it refers to an updated envir
 						for (final RProcessREnvironment entry : updateEnvirs) {
 							if (entry.getHandle() == reference.getHandle()) {
-								this.fTreeViewer.refresh(reference, true);
+								this.treeViewer.refresh(reference, true);
 								// reference is readded automatically to new set, if necessary
 								continue ITER_REFS;
 							}
@@ -826,177 +861,139 @@
 			}
 		}
 		else {
-			this.fTreeViewer.refresh(true);
+			this.treeViewer.refresh(true);
 		}
 		
 		// Adapt and set selection
-		if (selection != null && !selection.isEmpty()) {
-			final ITreeSelection s = (ITreeSelection) selection;
-			final TreePath[] paths = s.getPaths();
-			int j = 0;
-			for (int i = 0; i < paths.length; i++) {
-				final TreePath oldPath = paths[i];
-				final int count = oldPath.getSegmentCount();
-				final int shift = (input.showCondensedUserspace) ? -1 : +1;
+		if (input != null && selection != null && !selection.isEmpty()) {
+			final ITreeSelection s= (ITreeSelection) selection;
+			final TreePath[] paths= s.getPaths();
+			int j= 0;
+			for (int i= 0; i < paths.length; i++) {
+				final TreePath oldPath= paths[i];
+				final int count= oldPath.getSegmentCount();
+				final int shift= (input.isFilterUserspace()) ? -1 : +1;
 				if (count + shift < 1) {
 					continue;
 				}
-				final Object[] newPath = new Object[count + shift];
-				for (int k = (shift == -1) ? +1 : 0; k < count; k++) {
-					newPath[k + shift] = oldPath.getSegment(k);
+				final Object[] newPath= new @NonNull Object[count + shift];
+				for (int k= (shift == -1) ? +1 : 0; k < count; k++) {
+					newPath[k + shift]= oldPath.getSegment(k);
 				}
 				if (shift == +1) {
-					newPath[0] = ContentProvider.getCombinedRElement(newPath[1]).getModelParent();
+					newPath[0]= RElementInputContentProvider.getCombinedRElement(newPath[1]).getModelParent();
 				}
-				paths[j++] = new TreePath(newPath);
+				paths[j++]= new TreePath(newPath);
 			}
-			this.fTreeViewer.setSelection(new TreeSelection(
+			this.treeViewer.setSelection(new TreeSelection(
 					(j < paths.length) ? Arrays.copyOf(paths, j) : paths),
 					true );
 		}
 		
 		// Expand Global_Env, if it is a new process and has valid input
-		EXPAND_GLOBALENV : if (changed && !input.showCondensedUserspace 
-				&& input.rootElements != null && input.rootElements.length > 0) {
-			for (final Object element : input.rootElements) {
-				if (this.fTreeViewer.getExpandedState(element)) {
-					break EXPAND_GLOBALENV;
+		EXPAND_GLOBALENV: if (input != null && processChanged && !input.isFilterUserspace()) {
+			final CombinedRElement[] rootElements= input.getRootElements();
+			if (rootElements != null && rootElements.length > 0) {
+				for (final Object element : rootElements) {
+					if (this.treeViewer.getExpandedState(element)) {
+						break EXPAND_GLOBALENV;
+					}
 				}
+				this.treeViewer.expandToLevel(new TreePath(new Object[] { rootElements[0] }), 1);
 			}
-			this.fTreeViewer.expandToLevel(new TreePath(new Object[] { input.rootElements[0] }), 1);
 		}
+		
+		this.isUpdating= false;
+		
+		updateSelectionInfo((ITreeSelection) this.actionUtil.getSelectionProvider().getSelection());
 	}
 	
 	private void updateFilter() {
-		this.fInputUpdater.schedule();
+		this.inputUpdater.schedule();
 	}
 	
 	private void updateSorter() {
-		this.fTreeViewer.setComparator(this.fSortByType ? TYPE_COMPARATOR : null);
+		this.treeViewer.setComparator((this.sortByType) ? TYPE_COMPARATOR : null);
+	}
+	
+	private void clearActionInfo() {
+		this.actionUtil.getStatusLine().clearAll();
 	}
 	
 	private void updateSelectionInfo(final ITreeSelection selection) {
-		final Object previousInfoObject = this.fCurrentInfoObject;
-		this.fCurrentInfoObject = null;
-		
-		final RProcess tool = getTool();
-		if (tool == null) {
+		if (this.isUpdating) {
 			return;
 		}
-		if (selection.size() == 1) {
-			this.fCurrentInfoObject = selection.getFirstElement();
-			final TreePath treePath = selection.getPaths()[0];
-			final ElementName elementName = getFQElementName(treePath);
-			final String name = (elementName != null) ? elementName.getDisplayName() : null;
-			if (name != null) {
-				if (this.fCurrentInfoObject.equals(previousInfoObject)) {
-					clearInfo();
+		Object infoObject= null;
+		String message= null;
+		
+		final RProcess tool= getTool();
+		if (tool != null && !selection.isEmpty()) {
+			if (selection.size() == 1) {
+				final TreePath treePath= selection.getPaths()[0];
+				final ElementName elementName= getFQElementName(treePath);
+				final String name= (elementName != null) ? elementName.getDisplayName() : null;
+				if (name != null) {
+					infoObject= selection.getFirstElement();
+					message= name;
 				}
-				this.statusLine.setSelectionMessage(new StatusInfo(IStatus.OK,
-						NLS.bind("{0}  \u2012  {1}", name, tool.getLabel(Tool.DEFAULT_LABEL)) )); //$NON-NLS-1$
-				return;
+			}
+			else {
+				message= NLS.bind("{0} items selected", selection.size());
+			}
+			if (message != null) {
+				message= NLS.bind("{0}  \u2012  {1}", message, tool.getLabel(Tool.DEFAULT_LABEL)); //$NON-NLS-1$
 			}
 		}
-		clearInfo();
-		if (selection.size() > 1) {
-			this.statusLine.setSelectionMessage(new StatusInfo(IStatus.OK,
-					NLS.bind("{0} items selected", selection.size()) ));
-			return;
-		}
-		this.statusLine.setSelectionMessage(null);
-	}
-	
-	private void clearInfo() {
-	}
-	
-	/**
-	 * Returns a shared clipboard resource, which can be used by actions of this view.
-	 * 
-	 * @return a clipboard object.
-	 */
-	Clipboard getClipboard() {
-		if (this.fClipboard == null) {
-			this.fClipboard = new Clipboard(Display.getCurrent());
-		}
-		return this.fClipboard;
-	}
-	
-	StatusLineMessageManager getStatusLine() {
-		return this.statusLine;
-	}
-	
-	@Override
-	public void setFocus() {
-		this.fTreeViewer.getControl().setFocus();
-	}
-	
-	@Override
-	public void dispose() {
-		if (this.fToolRegistryListener != null) {
-			NicoUI.getToolRegistry().removeListener(this.fToolRegistryListener);
-			this.fToolRegistryListener = null;
-		}
-		if (this.process != null) {
-			this.process.getWorkspaceData().removePropertyListener(this.fInputUpdater);
-			this.process.getQueue().remove(new ToolRunnable[] { this.fManualRefreshRunnable });
-		}
-		this.fInputUpdater.cancel();
-		if (this.fHoveringController != null) {
-			this.fHoveringController.dispose();
-			this.fHoveringController = null;
-		}
-		if (this.fSearchStartHandler != null) {
-			this.fSearchStartHandler.dispose();
-			this.fSearchStartHandler = null;
-		}
-		if (this.fCopyElementNameHandler != null) {
-			this.fCopyElementNameHandler.dispose();
-			this.fCopyElementNameHandler = null;
-		}
-		if (this.fPrintElementHandler != null) {
-			this.fPrintElementHandler.dispose();
-			this.fPrintElementHandler = null;
-		}
 		
-		for (final IToolRetargetable listener : this.fToolListenerList.toArray()) {
-			listener.setTool(null);
+		if (infoObject == null || !infoObject.equals(this.currentInfoObject)) {
+			clearActionInfo();
 		}
-		
-		super.dispose();
-		
-		if (this.fClipboard != null) {
-			this.fClipboard.dispose();
-			this.fClipboard = null;
-		}
+		this.currentInfoObject= infoObject;
+		this.actionUtil.getStatusLine().setSelectionMessage(
+				(message != null) ? new StatusInfo(IStatus.OK, message) : null );
 	}
 	
 	
-	public RElementName getFQElementName(final TreePath treePath) {
+	public @Nullable RElementName getFQElementName(final TreePath treePath) {
 		if (treePath.getSegmentCount() == 0) {
 			return null;
 		}
-		int segmentIdx = 0;
-		if (!this.fFilterUserspaceActivated) {
+		int segmentIdx= 0;
+		if (!this.filterUserspaceActivated) {
 			if (treePath.getSegmentCount() == 1) { // search path item
-				return ContentProvider.getCombinedRElement(treePath.getFirstSegment()).getElementName();
+				return RElementInputContentProvider.getCombinedRElement(treePath.getFirstSegment()).getElementName();
 			}
 			else { // main name at 1
-				segmentIdx = 1;
+				segmentIdx= 1;
 			}
 		}
 		final List<RElementName> names= new ArrayList<>(treePath.getSegmentCount() - segmentIdx + 1);
-		final CombinedRElement first= ContentProvider.getCombinedRElement(treePath.getSegment(segmentIdx++));
+		final CombinedRElement first= RElementInputContentProvider.getCombinedRElement(treePath.getSegment(segmentIdx++));
 		names.add(first.getModelParent().getElementName());
 		names.add(first.getElementName());
 		while (segmentIdx < treePath.getSegmentCount()) {
-			final Object object= treePath.getSegment(segmentIdx++);
-			if (object instanceof ElementPartition) {
+			final Object segment= treePath.getSegment(segmentIdx++);
+			if (segment instanceof ElementPartition) {
 				continue;
 			}
-			final CombinedRElement rElement= ContentProvider.getCombinedRElement(object);
+			final CombinedRElement rElement= RElementInputContentProvider.getCombinedRElement(segment);
 			names.add(rElement.getElementName());
 		}
 		return RElementName.create(names);
 	}
 	
+	
+	@Override
+	@SuppressWarnings("unchecked")
+	public <T> T getAdapter(final Class<T> adapterType) {
+		if (adapterType == Control.class) {
+			return (T) this.treeViewer.getControl();
+		}
+		if (adapterType == Tool.class) {
+			return (T) this.process;
+		}
+		return super.getAdapter(adapterType);
+	}
+	
 }
diff --git a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/OpenInEditorHandler.java b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/OpenInEditorHandler.java
index 95aee4d..69d8c8c 100644
--- a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/OpenInEditorHandler.java
+++ b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/OpenInEditorHandler.java
@@ -20,6 +20,9 @@
 import org.eclipse.jface.viewers.ITreeSelection;
 import org.eclipse.ui.IWorkbenchPart;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
 import org.eclipse.statet.ecommons.ui.workbench.WorkbenchUIUtils;
 
 import org.eclipse.statet.nico.core.runtime.ToolProcess;
@@ -27,8 +30,10 @@
 import org.eclipse.statet.r.core.model.RElementName;
 import org.eclipse.statet.r.ui.dataeditor.RDataEditor;
 import org.eclipse.statet.r.ui.dataeditor.RLiveDataEditorInput;
+import org.eclipse.statet.r.ui.util.RElementInputContentProvider;
 
 
+@NonNullByDefault
 public class OpenInEditorHandler extends AbstractHandler {
 	
 	
@@ -37,7 +42,7 @@
 	
 	
 	@Override
-	public void setEnabled(final Object evaluationContext) {
+	public void setEnabled(final @Nullable Object evaluationContext) {
 		final IWorkbenchPart activePart= WorkbenchUIUtils.getActivePart(evaluationContext);
 		if (activePart instanceof ObjectBrowserView) {
 			final ObjectBrowserView browser= (ObjectBrowserView) activePart;
@@ -46,7 +51,7 @@
 			final ITreeSelection selection= browser.getSelection();
 			if (tool != null && !tool.isTerminated()
 					&& selection.size() == 1) {
-				final CombinedRElement rElement= ContentProvider.getCombinedRElement(selection.getFirstElement());
+				final CombinedRElement rElement= RElementInputContentProvider.getCombinedRElement(selection.getFirstElement());
 				setBaseEnabled(rElement != null
 						&& RLiveDataEditorInput.isSupported(rElement) );
 				return;
@@ -56,7 +61,7 @@
 	}
 	
 	@Override
-	public Object execute(final ExecutionEvent event) throws ExecutionException {
+	public @Nullable Object execute(final ExecutionEvent event) throws ExecutionException {
 		final IWorkbenchPart activePart= WorkbenchUIUtils.getActivePart(event.getApplicationContext());
 		if (activePart instanceof ObjectBrowserView) {
 			final ObjectBrowserView browser= (ObjectBrowserView) activePart;
diff --git a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/PrintHandler.java b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/PrintHandler.java
deleted file mode 100644
index bfc0fa6..0000000
--- a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/PrintHandler.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*=============================================================================#
- # Copyright (c) 2009, 2018 Stephan Wahlbrink and others.
- # 
- # This program and the accompanying materials are made available under the
- # terms of the Eclipse Public License 2.0 which is available at
- # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
- # which is available at https://www.apache.org/licenses/LICENSE-2.0.
- # 
- # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
- # 
- # Contributors:
- #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
- #=============================================================================*/
-
-package org.eclipse.statet.internal.r.objectbrowser;
-
-import org.eclipse.core.commands.AbstractHandler;
-import org.eclipse.core.commands.ExecutionEvent;
-import org.eclipse.core.commands.ExecutionException;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.jface.viewers.ITreeSelection;
-import org.eclipse.jface.viewers.TreePath;
-
-import org.eclipse.statet.ecommons.models.core.util.ElementPartition;
-import org.eclipse.statet.ecommons.ui.util.UIAccess;
-
-import org.eclipse.statet.nico.core.runtime.SubmitType;
-import org.eclipse.statet.nico.core.runtime.ToolController;
-import org.eclipse.statet.nico.core.runtime.ToolProcess;
-import org.eclipse.statet.nico.ui.NicoUITools;
-import org.eclipse.statet.r.console.core.RConsoleTool;
-import org.eclipse.statet.r.core.data.CombinedRElement;
-import org.eclipse.statet.r.core.model.RElementName;
-import org.eclipse.statet.rj.data.RObject;
-
-
-class PrintHandler extends AbstractHandler {
-	
-	
-	private final ObjectBrowserView view;
-	
-	
-	public PrintHandler(final ObjectBrowserView objectBrowserView) {
-		this.view = objectBrowserView;
-	}
-	
-	
-	private boolean isValidSelection(final ITreeSelection selection) {
-		if (selection == null || selection.size() != 1) {
-			return false;
-		}
-		final Object element = selection.getFirstElement();
-		if (element instanceof ElementPartition) {
-			final CombinedRElement rElement = ContentProvider.getCombinedRElement(element);
-			if (rElement == null || rElement.getRObjectType() != RObject.TYPE_LIST) {
-				return false;
-			}
-		}
-		return true;
-	}
-	
-	@Override
-	public void setEnabled(final Object evaluationContext) {
-		final ToolProcess process = this.view.getTool();
-		setBaseEnabled(process != null && !process.isTerminated()
-				&& isValidSelection(this.view.getSelection()) );
-	}
-	
-	@Override
-	public Object execute(final ExecutionEvent event) throws ExecutionException {
-		if (!UIAccess.isOkToUse(this.view.getViewer())) {
-			return null;
-		}
-		final ITreeSelection selection = this.view.getSelection();
-		if (!isValidSelection(selection)) {
-			return null;
-		}
-		final TreePath treePath = selection.getPaths()[0];
-		final RElementName elementName = this.view.getFQElementName(treePath);
-		if (elementName != null) {
-			String cmd = RElementName.createDisplayName(elementName, RElementName.DISPLAY_FQN | RElementName.DISPLAY_EXACT);
-			if (treePath.getLastSegment() instanceof ElementPartition) {
-				final ElementPartition partition = (ElementPartition) treePath.getLastSegment();
-				cmd = cmd + '[' + (partition.getPartitionStart() + 1) + ':' + (partition.getPartitionStart() + partition.getPartitionLength()) + ']';
-			}
-			
-			final ToolProcess process = this.view.getTool();
-			try {
-				final ToolController controller = NicoUITools.accessController(RConsoleTool.TYPE, process);
-				controller.submit(cmd, SubmitType.TOOLS);
-			}
-			catch (final CoreException e) {
-			}
-		}
-		
-		return null;
-	}
-	
-}
diff --git a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ToggleAutoRefreshHandler.java b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ToggleAutoRefreshHandler.java
index 78aff7b..315f96e 100644
--- a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ToggleAutoRefreshHandler.java
+++ b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/objectbrowser/ToggleAutoRefreshHandler.java
@@ -20,14 +20,19 @@
 import org.eclipse.ui.commands.IElementUpdater;
 import org.eclipse.ui.menus.UIElement;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
 import org.eclipse.statet.ecommons.ui.workbench.WorkbenchUIUtils;
 
-import org.eclipse.statet.nico.ui.actions.ToolRetargetableHandler;
+import org.eclipse.statet.nico.ui.actions.AbstractToolHandler;
+import org.eclipse.statet.r.console.core.RConsoleTool;
 import org.eclipse.statet.r.console.core.RProcess;
 import org.eclipse.statet.r.console.core.RWorkspace;
 
 
-class ToggleAutoRefreshHandler extends ToolRetargetableHandler implements IElementUpdater {
+@NonNullByDefault
+class ToggleAutoRefreshHandler extends AbstractToolHandler<RProcess> implements IElementUpdater {
 	
 	
 	private final ObjectBrowserView view;
@@ -36,21 +41,21 @@
 	
 	
 	public ToggleAutoRefreshHandler(final ObjectBrowserView view) {
-		super(view, view.getSite());
+		super(RConsoleTool.TYPE, null, view, view.getSite());
 		
-		this.view = view;
+		this.view= view;
 		init();
 	}
 	
 	
 	@Override
 	public void updateElement(final UIElement element, final Map parameters) {
-		this.currentState = false;
-		final RProcess tool = this.view.getTool();
+		this.currentState= false;
+		final RProcess tool= getActiveTool();
 		if (tool != null) {
-			final RWorkspace workspace = tool.getWorkspaceData();
+			final RWorkspace workspace= tool.getWorkspaceData();
 			if (workspace != null) {
-				this.currentState = workspace.isAutoRefreshEnabled();
+				this.currentState= workspace.isAutoRefreshEnabled();
 			}
 		}
 		
@@ -64,10 +69,10 @@
 	}
 	
 	@Override
-	protected Object doExecute(final ExecutionEvent event) {
-		final RWorkspace workspace = (RWorkspace) getCheckedTool().getWorkspaceData();
+	protected @Nullable Object execute(final RProcess tool, final ExecutionEvent event) {
+		final RWorkspace workspace= tool.getWorkspaceData();
 		if (workspace != null) {
-			this.currentState = !this.currentState;
+			this.currentState= !this.currentState;
 			workspace.setAutoRefresh(this.currentState);
 		}
 		return null;
diff --git a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/r/ui/rtool/CopyRElementHandler.java b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/r/ui/rtool/CopyRElementHandler.java
new file mode 100644
index 0000000..95d09b5
--- /dev/null
+++ b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/r/ui/rtool/CopyRElementHandler.java
@@ -0,0 +1,115 @@
+/*=============================================================================#
+ # Copyright (c) 2009, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.r.ui.rtool;
+
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullElse;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+
+import org.eclipse.statet.jcommons.collections.CollectionUtils;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.models.core.util.ElementPartition;
+import org.eclipse.statet.ecommons.ui.util.DNDUtils;
+import org.eclipse.statet.ecommons.ui.util.UIAccess;
+import org.eclipse.statet.ecommons.ui.util.ViewActionUtil;
+
+
+@NonNullByDefault
+public class CopyRElementHandler extends AbstractHandler {
+	
+	
+	private final ViewActionUtil actionUtil;
+	
+	private final ILabelProvider labelProvider;
+	
+	
+	public CopyRElementHandler(final ViewActionUtil actionUtil, final ILabelProvider labelProvider) {
+		this.actionUtil= actionUtil;
+		this.labelProvider= labelProvider;
+	}
+	
+	
+	private IStructuredSelection getSelection() {
+		final ISelectionProvider selectionProvider= this.actionUtil.getSelectionProvider();
+		return (IStructuredSelection) selectionProvider.getSelection();
+	}
+	
+	protected boolean isValidSelection(final IStructuredSelection selection) {
+		if (selection == null || selection.isEmpty()) {
+			return false;
+		}
+		for (final Object element : selection.toList()) {
+			if (element instanceof ElementPartition) {
+				return false;
+			}
+		}
+		return true;
+	}
+	
+	private @Nullable String getText(final Object element) {
+		return this.labelProvider.getText(element);
+	}
+	
+	@Override
+	public void setEnabled(final @Nullable Object evaluationContext) {
+		setBaseEnabled(isValidSelection(getSelection()));
+	}
+	
+	@Override
+	public @Nullable Object execute(final ExecutionEvent event) throws ExecutionException {
+		if (!UIAccess.isOkToUse(this.actionUtil.getControl())) {
+			return null;
+		}
+		final IStructuredSelection selection= getSelection();
+		if (!isValidSelection(selection)) {
+			return null;
+		}
+		
+		final String text= createData(selection);
+		if (text != null) {
+			copy(text);
+		}
+		
+		return null;
+	}
+	
+	protected @Nullable String createData(final IStructuredSelection selection) {
+		final List<String> texts= new ArrayList<>(selection.size());
+		for (final Object element : selection.toList()) {
+			texts.add(nonNullElse(getText(element), "-")); //$NON-NLS-1$
+		}
+		return (texts.size() == 1) ?
+				texts.get(0) : CollectionUtils.toString(texts, ", "); //$NON-NLS-1$
+	}
+	
+	private void copy(final String text) {
+		DNDUtils.setContent(this.actionUtil.getClipboard(),
+				new String[] { text },
+				new Transfer[] { TextTransfer.getInstance() } );
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/r/ui/rtool/PrintRElementHandler.java b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/r/ui/rtool/PrintRElementHandler.java
new file mode 100644
index 0000000..c949746
--- /dev/null
+++ b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/r/ui/rtool/PrintRElementHandler.java
@@ -0,0 +1,113 @@
+/*=============================================================================#
+ # Copyright (c) 2009, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.r.ui.rtool;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.TreePath;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.models.core.util.ElementPartition;
+import org.eclipse.statet.ecommons.ts.core.util.ToolProvider;
+import org.eclipse.statet.ecommons.ui.util.UIAccess;
+import org.eclipse.statet.ecommons.ui.util.ViewActionUtil;
+
+import org.eclipse.statet.nico.core.runtime.SubmitType;
+import org.eclipse.statet.nico.core.runtime.ToolController;
+import org.eclipse.statet.nico.ui.NicoUITools;
+import org.eclipse.statet.nico.ui.actions.AbstractToolHandler;
+import org.eclipse.statet.r.console.core.RConsoleTool;
+import org.eclipse.statet.r.console.core.RProcess;
+import org.eclipse.statet.r.core.data.CombinedRElement;
+import org.eclipse.statet.r.core.model.RElementName;
+import org.eclipse.statet.r.ui.util.RElementInputContentProvider;
+import org.eclipse.statet.r.ui.util.RElementInputUtils;
+import org.eclipse.statet.rj.data.RObject;
+
+
+@NonNullByDefault
+public class PrintRElementHandler extends AbstractToolHandler<RProcess> {
+	
+	
+	private final ViewActionUtil actionUtil;
+	
+	
+	public PrintRElementHandler(final ViewActionUtil actionUtil) {
+		super(RConsoleTool.TYPE, RConsoleTool.R_BASIC_FEATURESET_ID,
+				(ToolProvider) actionUtil.getWorkbenchPart(), actionUtil.getWorkbenchPart().getSite() );
+		this.actionUtil= actionUtil;
+	}
+	
+	
+	private ITreeSelection getSelection() {
+		final ISelectionProvider selectionProvider= this.actionUtil.getSelectionProvider();
+		return (ITreeSelection) selectionProvider.getSelection();
+	}
+	
+	private boolean isValidSelection(final ITreeSelection selection) {
+		if (selection == null || selection.size() != 1) {
+			return false;
+		}
+		final Object element= selection.getFirstElement();
+		if (element instanceof ElementPartition) {
+			final CombinedRElement rElement= RElementInputContentProvider.getCombinedRElement(element);
+			if (rElement == null || rElement.getRObjectType() != RObject.TYPE_LIST) {
+				return false;
+			}
+		}
+		return true;
+	}
+	
+	
+	@Override
+	protected boolean evaluateIsEnabled(final RProcess tool, final @Nullable Object evaluationContext) {
+		return (super.evaluateIsEnabled(tool, evaluationContext)
+				&& isValidSelection(getSelection()) );
+	}
+	
+	@Override
+	protected @Nullable Object execute(final RProcess tool, final ExecutionEvent event) {
+		if (!UIAccess.isOkToUse(this.actionUtil.getControl())) {
+			return null;
+		}
+		final ITreeSelection selection= getSelection();
+		if (!isValidSelection(selection)) {
+			return null;
+		}
+		final TreePath treePath= selection.getPaths()[0];
+		final RElementName elementName= RElementInputUtils.getRElementName(treePath, selection);
+		if (elementName != null) {
+			String cmd= elementName.getDisplayName(RElementName.DISPLAY_FQN | RElementName.DISPLAY_EXACT);
+			if (treePath.getLastSegment() instanceof ElementPartition) {
+				final ElementPartition partition= (ElementPartition) treePath.getLastSegment();
+				cmd= cmd + '[' + (partition.getPartitionStart() + 1) + ':' + (partition.getPartitionStart() + partition.getPartitionLength()) + ']';
+			}
+			
+			try {
+				final ToolController controller= NicoUITools.accessController(RConsoleTool.TYPE, tool);
+				controller.submit(cmd, SubmitType.TOOLS);
+			}
+			catch (final CoreException e) {
+			}
+		}
+		
+		return null;
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/r/ui/rtool/RElementViewerDragSourceListener.java b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/r/ui/rtool/RElementViewerDragSourceListener.java
new file mode 100644
index 0000000..c4971dd
--- /dev/null
+++ b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/r/ui/rtool/RElementViewerDragSourceListener.java
@@ -0,0 +1,82 @@
+/*=============================================================================#
+ # Copyright (c) 2009, 2018 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.r.ui.rtool;
+
+import org.eclipse.jface.util.TransferDragSourceListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.swt.dnd.DragSourceEvent;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+
+@NonNullByDefault
+public class RElementViewerDragSourceListener implements TransferDragSourceListener {
+	
+	
+	private final CopyRElementHandler commandHandler;
+	
+	private final StructuredViewer viewer;
+	
+	private String text= ""; //$NON-NLS-1$
+	
+	
+	public RElementViewerDragSourceListener(final CopyRElementHandler commandHandler, 
+			final StructuredViewer viewer) {
+		this.commandHandler= commandHandler;
+		this.viewer= viewer;
+	}
+	
+	
+	@Override
+	public Transfer getTransfer() {
+		return TextTransfer.getInstance();
+	}
+	
+	private ITreeSelection getSelection() {
+		return (ITreeSelection) this.viewer.getSelection();
+	}
+	
+	@Override
+	public void dragStart(final DragSourceEvent event) {
+		final IStructuredSelection selection= getSelection();
+		if (!this.commandHandler.isValidSelection(selection)) {
+			event.doit= false;
+			return;
+		}
+		
+		final String text= this.commandHandler.createData(selection);
+		if (text != null) {
+			this.text= text;
+		}
+		else {
+			event.doit= false;
+		}
+	}
+	
+	@Override
+	public void dragSetData(final DragSourceEvent event) {
+		event.data= this.text;
+	}
+	
+	@Override
+	public void dragFinished(final DragSourceEvent event) {
+		this.text= ""; //$NON-NLS-1$
+	}
+	
+}
diff --git a/redocs/org.eclipse.statet.redocs.r/META-INF/MANIFEST.MF b/redocs/org.eclipse.statet.redocs.r/META-INF/MANIFEST.MF
index a5ac451..42a7445 100644
--- a/redocs/org.eclipse.statet.redocs.r/META-INF/MANIFEST.MF
+++ b/redocs/org.eclipse.statet.redocs.r/META-INF/MANIFEST.MF
@@ -24,6 +24,7 @@
  org.eclipse.statet.ecommons.io,
  org.eclipse.statet.ecommons.variables.core,
  org.eclipse.statet.jcommons.collections;version="4.0.0",
+ org.eclipse.statet.jcommons.lang;version="4.0.0",
  org.eclipse.statet.jcommons.string;version="4.0.0"
 Export-Package: org.eclipse.statet.redocs.r,
  org.eclipse.statet.redocs.r.core.model,
diff --git a/redocs/org.eclipse.statet.redocs.r/src/org/eclipse/statet/redocs/r/ui/processing/RunRConsoleSnippetOperation.java b/redocs/org.eclipse.statet.redocs.r/src/org/eclipse/statet/redocs/r/ui/processing/RunRConsoleSnippetOperation.java
index 7998844..28b5463 100644
--- a/redocs/org.eclipse.statet.redocs.r/src/org/eclipse/statet/redocs/r/ui/processing/RunRConsoleSnippetOperation.java
+++ b/redocs/org.eclipse.statet.redocs.r/src/org/eclipse/statet/redocs/r/ui/processing/RunRConsoleSnippetOperation.java
@@ -22,7 +22,6 @@
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.SubMonitor;
-import org.eclipse.core.variables.IStringVariable;
 import org.eclipse.osgi.util.NLS;
 
 import org.eclipse.statet.ecommons.io.FileUtil;
@@ -39,6 +38,7 @@
 import org.eclipse.statet.nico.core.runtime.IRequireSynch;
 import org.eclipse.statet.r.console.core.IRBasicAdapter;
 import org.eclipse.statet.r.console.core.RWorkspace;
+import org.eclipse.statet.r.console.core.util.RCodeVariableText;
 import org.eclipse.statet.r.core.RUtil;
 import org.eclipse.statet.redocs.r.RedocsRweave;
 
@@ -115,20 +115,8 @@
 		
 		r.briefAboutToChange();
 		final RWorkspace rWorkspace= r.getWorkspaceData();
-		final VariableText2 variableResolver= new VariableText2(
-				getStepConfig().getVariableResolver().getExtraVariables() ) {
-			@Override
-			protected String checkValue(final IStringVariable variable, String value) throws CoreException {
-				if (variable.getName().endsWith("_loc")) { //$NON-NLS-1$
-					if (rWorkspace.isRemote()) {
-						final IFileStore store= FileUtil.getFileStore(value);
-						value= rWorkspace.toToolPath(store);
-					}
-					return RUtil.escapeBackslash(value);
-				}
-				return value;
-			}
-		};
+		final VariableText2 variableResolver= new RCodeVariableText(rWorkspace,
+				getStepConfig().getVariableResolver().getExtraVariables() );
 		
 		{	// Set wd
 			final IFileStore dir= FileUtil.getFileStore(VariableUtils.getValue(
diff --git a/redocs/org.eclipse.statet.redocs.r/src/org/eclipse/statet/redocs/r/ui/processing/RunRConsoleSnippetOperationSettings.java b/redocs/org.eclipse.statet.redocs.r/src/org/eclipse/statet/redocs/r/ui/processing/RunRConsoleSnippetOperationSettings.java
index 24365bd..16d58f2 100644
--- a/redocs/org.eclipse.statet.redocs.r/src/org/eclipse/statet/redocs/r/ui/processing/RunRConsoleSnippetOperationSettings.java
+++ b/redocs/org.eclipse.statet.redocs.r/src/org/eclipse/statet/redocs/r/ui/processing/RunRConsoleSnippetOperationSettings.java
@@ -107,7 +107,7 @@
 		}
 		{	final TemplateVariableProcessor templateVariableProcessor= new TemplateVariableProcessor();
 			final RSourceViewerConfigurator configurator= new RTemplateSourceViewerConfigurator(
-					RCore.WORKBENCH_ACCESS,
+					RCore.getWorkbenchAccess(),
 					templateVariableProcessor );
 			final SnippetEditor1 editor= new SnippetEditor1(configurator, null,
 					PlatformUI.getWorkbench(), RLaunchingUI.LAUNCH_CONFIG_QUALIFIER, true ) {