Bug 306598 - option to hide running threads in Debug view

Change-Id: Iabd832ffc4835086e4a5f27ec0489f89575ca53d
Signed-off-by: Igor Fedorenko <igor@ifedorenko.com>
Signed-off-by: Andrey Loskutov <loskutov@gmx.de>
diff --git a/org.eclipse.jdt.debug.ui/META-INF/MANIFEST.MF b/org.eclipse.jdt.debug.ui/META-INF/MANIFEST.MF
index 61bd3ee..7b4676f 100644
--- a/org.eclipse.jdt.debug.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.debug.ui/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.jdt.debug.ui; singleton:=true
-Bundle-Version: 3.9.100.qualifier
+Bundle-Version: 3.10.0.qualifier
 Bundle-Activator: org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
diff --git a/org.eclipse.jdt.debug.ui/plugin.properties b/org.eclipse.jdt.debug.ui/plugin.properties
index abe533a..82aa951 100644
--- a/org.eclipse.jdt.debug.ui/plugin.properties
+++ b/org.eclipse.jdt.debug.ui/plugin.properties
@@ -158,6 +158,9 @@
 showSystemThreads.label=Show &System Threads
 showSystemThreads.tooltip=Show System Threads
 
+showRunningThreads.label=Show &Running Threads
+showRunningThreads.tooltip=Show Running Threads
+
 showMonitorThreadInfo.label=Show &Monitors
 showMonitorThreadInfo.tooltip=Show the Thread & Monitor Information
 
diff --git a/org.eclipse.jdt.debug.ui/plugin.xml b/org.eclipse.jdt.debug.ui/plugin.xml
index 9cdacd2..6dc3cdb 100644
--- a/org.eclipse.jdt.debug.ui/plugin.xml
+++ b/org.eclipse.jdt.debug.ui/plugin.xml
@@ -2076,6 +2076,16 @@
                menubarPath="org.eclipse.jdt.debug.ui.LaunchView.javaSubmenu/javaPart"
                tooltip="%showSystemThreads.tooltip">
          </action>
+         <action
+               class="org.eclipse.jdt.internal.debug.ui.actions.ShowRunningThreadsAction"
+               helpContextId="show_running_threads_action_context"
+               icon="$nl$/icons/full/obj16/thread_obj.png"
+               id="org.eclipse.jdt.debug.ui.launchViewActions.ShowRunningThreads"
+               label="%showRunningThreads.label"
+               menubarPath="org.eclipse.jdt.debug.ui.LaunchView.javaSubmenu/javaPart"
+               style="toggle"
+               tooltip="%showRunningThreads.tooltip">
+         </action>
   <action
         helpContextId="show_monitors_action_context"
         label="%showMonitorThreadInfo.label"
diff --git a/org.eclipse.jdt.debug.ui/pom.xml b/org.eclipse.jdt.debug.ui/pom.xml
index f04dd11..6a176c9 100644
--- a/org.eclipse.jdt.debug.ui/pom.xml
+++ b/org.eclipse.jdt.debug.ui/pom.xml
@@ -18,7 +18,7 @@
   </parent>
   <groupId>org.eclipse.jdt</groupId>
   <artifactId>org.eclipse.jdt.debug.ui</artifactId>
-  <version>3.9.100-SNAPSHOT</version>
+  <version>3.10.0-SNAPSHOT</version>
   <packaging>eclipse-plugin</packaging>
   <properties>
     <code.ignoredWarnings>-warn:+resource,-deprecation,unavoidableGenericProblems</code.ignoredWarnings>
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/debug/ui/IJavaDebugUIConstants.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/debug/ui/IJavaDebugUIConstants.java
index 56bb695..1dc8069 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/debug/ui/IJavaDebugUIConstants.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/debug/ui/IJavaDebugUIConstants.java
@@ -87,6 +87,13 @@
 	public static final String PREF_SHOW_SYSTEM_THREADS = PLUGIN_ID + ".show_system_threads"; //$NON-NLS-1$
 
 	/**
+	 * Boolean preference indicating whether running threads should appear visible in the debug view.
+	 *
+	 * @since 3.10
+	 */
+	public static final String PREF_SHOW_RUNNING_THREADS = PLUGIN_ID + ".show_running_threads"; //$NON-NLS-1$
+
+	/**
 	 * Boolean preference indicating whether thread groups should be displayed in the debug view.
 	 *
 	 * @since 3.2
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPreferenceInitializer.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPreferenceInitializer.java
index 859781c..49f77d1 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPreferenceInitializer.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JDIDebugUIPreferenceInitializer.java
@@ -54,6 +54,7 @@
 		store.setDefault(IJDIPreferencesConstants.PREF_SHOW_DETAILS, IJDIPreferencesConstants.DETAIL_PANE);
 
 		store.setDefault(IJavaDebugUIConstants.PREF_SHOW_SYSTEM_THREADS, false);
+		store.setDefault(IJavaDebugUIConstants.PREF_SHOW_RUNNING_THREADS, true);
 		store.setDefault(IJavaDebugUIConstants.PREF_SHOW_MONITOR_THREAD_INFO, true);
 		store.setDefault(IJavaDebugUIConstants.PREF_SHOW_THREAD_GROUPS, false);
 		store.setDefault(IJDIPreferencesConstants.PREF_OPEN_INSPECT_POPUP_ON_EXCEPTION, false);
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/AbstractThreadsViewFilterAction.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/AbstractThreadsViewFilterAction.java
new file mode 100644
index 0000000..d57deb8
--- /dev/null
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/AbstractThreadsViewFilterAction.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2015 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.debug.ui.actions;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IDebugEventSetListener;
+import org.eclipse.jdt.debug.core.IJavaThread;
+import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.ui.IViewPart;
+
+/**
+ * An action delegate that toggles the state of its viewer to show/hide certain java threads.
+ */
+abstract class AbstractThreadsViewFilterAction extends ViewFilterAction implements IDebugEventSetListener {
+
+	@Override
+	public boolean select(Viewer viewer, Object parentElement, Object element) {
+		if (!getPreferenceValue()) {
+
+			IJavaThread thread = getJavaThread(element);
+
+			if (thread != null) {
+				try {
+					return selectThread(thread);
+				}
+				catch (DebugException e) {
+				}
+			}
+		}
+		return true;
+	}
+
+	private void refresh(Object source) {
+		final IJavaThread thread = getJavaThread(source);
+		if (thread != null) {
+			try {
+				if (isCandidateThread(thread)) {
+					Runnable r = new Runnable() {
+						@Override
+						public void run() {
+							StructuredViewer viewer = getStructuredViewer();
+							if (viewer != null) {
+								viewer.refresh();
+							}
+						}
+					};
+					JDIDebugUIPlugin.getStandardDisplay().asyncExec(r);
+					return;
+				}
+			}
+			catch (DebugException e) {
+			}
+		}
+	}
+
+	protected abstract boolean isCandidateThread(final IJavaThread thread) throws DebugException;
+
+	protected abstract boolean selectThread(IJavaThread thread) throws DebugException;
+
+	private IJavaThread getJavaThread(Object element) {
+		IJavaThread thread = null;
+
+		if (element instanceof IJavaThread) {
+			thread = (IJavaThread) element;
+		} else if (element instanceof IAdaptable) {
+			thread = ((IAdaptable) element).getAdapter(IJavaThread.class);
+		}
+
+		return thread;
+	}
+
+	@Override
+	public void init(IViewPart view) {
+		super.init(view);
+		DebugPlugin.getDefault().addDebugEventListener(this);
+	}
+
+	@Override
+	public void dispose() {
+		DebugPlugin.getDefault().removeDebugEventListener(this);
+		super.dispose();
+	}
+
+	@Override
+	public void handleDebugEvents(DebugEvent[] events) {
+		if (getValue()) {
+			// if showing all threads, no need to worry about displaying/hiding
+			return;
+		}
+		for (int i = 0; i < events.length; i++) {
+			DebugEvent event = events[i];
+			switch (event.getKind()) {
+				case DebugEvent.RESUME:
+					if (event.getDetail() == DebugEvent.CLIENT_REQUEST) {
+						// when a thread resumes we need to refresh the viewer to re-filter it
+						refresh(event.getSource());
+					}
+					break;
+			}
+		}
+	}
+}
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ShowRunningThreadsAction.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ShowRunningThreadsAction.java
new file mode 100644
index 0000000..3937449
--- /dev/null
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ShowRunningThreadsAction.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Igor Fedorenko.
+ * 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:
+ *     Igor Fedorenko - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.debug.ui.actions;
+
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.jdt.debug.core.IJavaThread;
+import org.eclipse.jdt.debug.ui.IJavaDebugUIConstants;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.ui.IViewPart;
+
+/**
+ * An action delegate that toggles the state of its viewer to show/hide running threads.
+ */
+public class ShowRunningThreadsAction extends AbstractThreadsViewFilterAction {
+
+	@Override
+	public void init(IViewPart view) {
+		// compensate for a bug in how ViewFilterAction stores compositeKey value
+		// which does not allow default==true and explicit==false
+		IPreferenceStore store = getPreferenceStore();
+		String preferenceKey = getPreferenceKey();
+		if (store.contains(preferenceKey)) {
+			String compositeKey = view.getSite().getId() + "." + preferenceKey; //$NON-NLS-1$
+			store.setDefault(compositeKey, store.getBoolean(preferenceKey));
+		}
+
+		super.init(view);
+	}
+
+	@Override
+	protected String getPreferenceKey() {
+		return IJavaDebugUIConstants.PREF_SHOW_RUNNING_THREADS;
+	}
+
+	@Override
+	protected boolean selectThread(IJavaThread thread) throws DebugException {
+		return thread.isSuspended();
+	}
+
+	@Override
+	protected boolean isCandidateThread(IJavaThread thread) throws DebugException {
+		return true;
+	}
+}
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ShowSystemThreadsAction.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ShowSystemThreadsAction.java
index 7abe841..2cfea81 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ShowSystemThreadsAction.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ShowSystemThreadsAction.java
@@ -10,122 +10,29 @@
  *******************************************************************************/
 package org.eclipse.jdt.internal.debug.ui.actions;
 
-import org.eclipse.core.runtime.IAdaptable;
-import org.eclipse.debug.core.DebugEvent;
 import org.eclipse.debug.core.DebugException;
-import org.eclipse.debug.core.DebugPlugin;
-import org.eclipse.debug.core.IDebugEventSetListener;
 import org.eclipse.jdt.debug.core.IJavaThread;
 import org.eclipse.jdt.debug.ui.IJavaDebugUIConstants;
-import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
-import org.eclipse.jface.viewers.StructuredViewer;
-import org.eclipse.jface.viewers.Viewer;
-import org.eclipse.ui.IViewPart;
 
 /**
  * An action delegate that toggles the state of its viewer to
  * show/hide System Threads.
  */
-public class ShowSystemThreadsAction extends ViewFilterAction implements IDebugEventSetListener {
+public class ShowSystemThreadsAction extends AbstractThreadsViewFilterAction {
 
-	/* (non-Javadoc)
-	 * @see org.eclipse.jdt.internal.debug.ui.actions.ThreadFilterAction#getPreferenceKey()
-	 */
 	@Override
 	protected String getPreferenceKey() {
 		return IJavaDebugUIConstants.PREF_SHOW_SYSTEM_THREADS;
 	}
 
-	/* (non-Javadoc)
-	 * @see org.eclipse.jface.viewers.ViewerFilter#select(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
-	 */
 	@Override
-	public boolean select(Viewer viewer, Object parentElement, Object element) {
-	    if (!getValue()) {
-
-            IJavaThread thread = getJavaThread(element);
-
-	        if (thread != null) {
-	            try {
-	                // Show only non-system threads and suspended threads.
-	                return !thread.isSystemThread() || thread.isSuspended();
-	            } catch (DebugException e) {
-	            }
-	        }
-	    }
-	    return true;
+	protected boolean selectThread(IJavaThread thread) throws DebugException {
+		// Show only non-system threads and suspended threads.
+		return !thread.isSystemThread() || thread.isSuspended();
 	}
 
-	private IJavaThread getJavaThread(Object element) {
-	    IJavaThread thread = null;
-
-	    if (element instanceof IJavaThread) {
-			thread = (IJavaThread) element;
-		} else if (element instanceof IAdaptable) {
-			thread = ((IAdaptable)element).getAdapter(IJavaThread.class);
-		}
-
-	    return thread;
-	}
-
-    /* (non-Javadoc)
-	 * @see org.eclipse.ui.IViewActionDelegate#init(org.eclipse.ui.IViewPart)
-	 */
 	@Override
-	public void init(IViewPart view) {
-		super.init(view);
-		DebugPlugin.getDefault().addDebugEventListener(this);
-	}
-	/* (non-Javadoc)
-	 * @see org.eclipse.ui.IActionDelegate2#dispose()
-	 */
-	@Override
-	public void dispose() {
-		super.dispose();
-		DebugPlugin.getDefault().removeDebugEventListener(this);
-	}
-
-	/* (non-Javadoc)
-	 * @see org.eclipse.debug.core.IDebugEventSetListener#handleDebugEvents(org.eclipse.debug.core.DebugEvent[])
-	 */
-	@Override
-	public void handleDebugEvents(DebugEvent[] events) {
-		if (getValue()) {
-			// if showing system threads, no need to worry about displaying/hiding
-			return;
-		}
-		for (int i = 0; i < events.length; i++) {
-			DebugEvent event = events[i];
-			switch (event.getKind()) {
-				case DebugEvent.RESUME:
-					if (event.getDetail() == DebugEvent.CLIENT_REQUEST) {
-						// when a system thread resumes we need to refresh the viewer to re-filter it
-						refresh(event.getSource());
-					}
-					break;
-			}
-		}
-	}
-
-	private void refresh(Object source) {
-        final IJavaThread thread = getJavaThread(source);
-        if (thread != null) {
-			try {
-				if (thread.isSystemThread()) {
-					Runnable r = new Runnable() {
-						@Override
-						public void run() {
-							StructuredViewer viewer = getStructuredViewer();
-							if (viewer != null) {
-								viewer.refresh();
-							}
-						}
-					};
-					JDIDebugUIPlugin.getStandardDisplay().asyncExec(r);
-					return;
-				}
-			} catch (DebugException e) {
-			}
-		}
+	protected boolean isCandidateThread(IJavaThread thread) throws DebugException {
+		return thread.isSystemThread();
 	}
 }