This commit was manufactured by cvs2svn to create tag 'Root_e_3_2_m_1_0'.

Sprout from master 2006-12-23 00:17:43 UTC mkersten 'Version update'
Cherrypick from master 2007-02-08 00:52:52 UTC mkersten 'RESOLVED - bug 135668: [activity] support integrating activity monitoring outside of the workbench':
    org.eclipse.mylyn.monitor.ui/plugin.xml
    org.eclipse.mylyn.monitor.ui/schema/user.exsd
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/AbstractCommandMonitor.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/AbstractUserActivityTimer.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/AbstractUserInteractionMonitor.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/ActivityContextManager.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/IActionExecutionListener.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/IMylarMonitorLifecycleListener.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/IUserAttentionListener.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/MylarMonitorUiPlugin.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/ShellLifecycleListener.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/WorkbenchUserActivityTimer.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/AbstractEditorTracker.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/AbstractPartTracker.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/ActionExecutionMonitor.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/ActivityChangeMonitor.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/BrowserMonitor.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/KeybindingCommandMonitor.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/MenuCommandMonitor.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/PerspectiveChangeMonitor.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/PreferenceChangeMonitor.java
    org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/WindowChangeMonitor.java
diff --git a/org.eclipse.mylyn.monitor.ui/plugin.xml b/org.eclipse.mylyn.monitor.ui/plugin.xml
new file mode 100644
index 0000000..68d1b0d
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/plugin.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin>
+   <extension-point id="user" name="User Monitoring" schema="schema/user.exsd"/>
+  
+</plugin>
diff --git a/org.eclipse.mylyn.monitor.ui/schema/user.exsd b/org.eclipse.mylyn.monitor.ui/schema/user.exsd
new file mode 100644
index 0000000..41a7517
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/schema/user.exsd
@@ -0,0 +1,110 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.mylar.monitor.ui">
+<annotation>
+      <appInfo>
+         <meta.schema plugin="org.eclipse.mylar.monitor.ui" id="user" name="User Monitoring"/>
+      </appInfo>
+      <documentation>
+         [Enter description of this extension point.]
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <complexType>
+         <sequence>
+            <element ref="osActivityTimer"/>
+         </sequence>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute translatable="true"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="osActivityTimer">
+      <annotation>
+         <documentation>
+            Only one extension is permitted per workbench instance.  If more than one plug-in specifies this extension point, only one will be used.
+         </documentation>
+      </annotation>
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="java" basedOn="org.eclipse.mylar.monitor.ui.AbstractUserActivityTimer"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="since"/>
+      </appInfo>
+      <documentation>
+         [Enter the first release in which this extension point appears.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="examples"/>
+      </appInfo>
+      <documentation>
+         [Enter extension point usage example here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="apiInfo"/>
+      </appInfo>
+      <documentation>
+         [Enter API information here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="implementation"/>
+      </appInfo>
+      <documentation>
+         [Enter information about supplied implementation of this extension point.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="copyright"/>
+      </appInfo>
+      <documentation>
+         
+      </documentation>
+   </annotation>
+
+</schema>
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/AbstractCommandMonitor.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/AbstractCommandMonitor.java
new file mode 100644
index 0000000..926660d
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/AbstractCommandMonitor.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IExecutionListener;
+import org.eclipse.core.commands.NotHandledException;
+import org.eclipse.mylar.core.MylarStatusHandler;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.commands.ICommandService;
+
+/**
+ * Self-registering on construction.
+ * 
+ * @author Mik Kersten
+ */
+public abstract class AbstractCommandMonitor implements IExecutionListener {
+
+	/**
+	 * Workbench must be active.
+	 */
+	public AbstractCommandMonitor() {
+		try {
+			ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getAdapter(ICommandService.class);
+			commandService.addExecutionListener(this);
+		} catch (NullPointerException npe) {
+			MylarStatusHandler.log("Monitors can not be instantiated until the workbench is active: ", this);
+		}
+	}
+
+	public void dispose() {
+		try {
+			ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getAdapter(ICommandService.class);
+			commandService.removeExecutionListener(this);
+		} catch (NullPointerException npe) {
+			MylarStatusHandler.log(npe, "Could not dispose monitor.");
+		}
+	}
+
+	public void postExecuteFailure(String commandId, ExecutionException exception) {
+		// don't care about this
+	}
+
+	public void notHandled(String commandId, NotHandledException exception) {
+		// don't care about this
+	}
+
+	public void postExecuteSuccess(String commandId, Object returnValue) {
+		// don't care about this
+	}
+
+	public void preExecute(String commandId, ExecutionEvent event) {
+		if (commandId != null)
+			handleCommandExecution(commandId, event);
+	}
+
+	protected abstract void handleCommandExecution(String commandId, ExecutionEvent event);
+}
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/AbstractUserActivityTimer.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/AbstractUserActivityTimer.java
new file mode 100644
index 0000000..fd51720
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/AbstractUserActivityTimer.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui;
+
+import org.eclipse.mylar.monitor.core.IActivityTimerListener;
+
+/**
+ * @author Mik Kersten
+ */
+public abstract class AbstractUserActivityTimer {
+
+	/**
+	 * The listener needs to be notified of timed user activity and inactivity
+	 */
+	public abstract boolean addListener(IActivityTimerListener activityListener);
+
+	public abstract void resetTimer();
+
+	public abstract void kill();
+
+	public abstract void start();
+
+	public abstract void setTimeoutMillis(int millis);
+
+}
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/AbstractUserInteractionMonitor.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/AbstractUserInteractionMonitor.java
new file mode 100644
index 0000000..be934f0
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/AbstractUserInteractionMonitor.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.mylar.context.core.AbstractContextStructureBridge;
+import org.eclipse.mylar.context.core.ContextCorePlugin;
+import org.eclipse.mylar.core.MylarStatusHandler;
+import org.eclipse.mylar.monitor.core.InteractionEvent;
+import org.eclipse.mylar.monitor.core.InteractionEvent.Kind;
+import org.eclipse.ui.ISelectionListener;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * Self-registering on construction. Encapsulates users' interaction with the
+ * context model.
+ * 
+ * @author Mik Kersten
+ */
+public abstract class AbstractUserInteractionMonitor implements ISelectionListener {
+
+	protected Object lastSelectedElement = null;
+
+	/**
+	 * Requires workbench to be active.
+	 */
+	public AbstractUserInteractionMonitor() {
+		try {
+			MylarMonitorUiPlugin.getDefault().addWindowPostSelectionListener(this);
+		} catch (NullPointerException npe) {
+			MylarStatusHandler.log("Monitors can not be instantiated until the workbench is active", this);
+		}
+	}
+
+	public void dispose() {
+		try {
+			MylarMonitorUiPlugin.getDefault().removeWindowPostSelectionListener(this);
+		} catch (NullPointerException npe) {
+			MylarStatusHandler.log(npe, "Could not dispose monitor.");
+		}
+	}
+
+	public void selectionChanged(IWorkbenchPart part, ISelection selection) {
+		if (selection == null || selection.isEmpty())
+			return;
+		if (!ContextCorePlugin.getContextManager().isContextActive()) {
+			handleWorkbenchPartSelection(part, selection, false);
+		} else {
+			handleWorkbenchPartSelection(part, selection, true);
+		}
+	}
+
+	protected abstract void handleWorkbenchPartSelection(IWorkbenchPart part, ISelection selection, boolean contributeToContext);
+
+	/**
+	 * Intended to be called back by subclasses.
+	 */
+	protected InteractionEvent handleElementSelection(IWorkbenchPart part, Object selectedElement, boolean contributeToContext) {
+		if (selectedElement == null || selectedElement.equals(lastSelectedElement))
+			return null;
+		AbstractContextStructureBridge bridge = ContextCorePlugin.getDefault().getStructureBridge(selectedElement);
+		InteractionEvent selectionEvent;
+		if (bridge.getContentType() != null) {
+			selectionEvent = new InteractionEvent(InteractionEvent.Kind.SELECTION,
+					bridge.getContentType(), bridge.getHandleIdentifier(selectedElement), part.getSite().getId());
+		} else {
+			selectionEvent = new InteractionEvent(InteractionEvent.Kind.SELECTION,
+					null, null, part.getSite().getId());			
+		}
+		if (contributeToContext) {
+			ContextCorePlugin.getContextManager().handleInteractionEvent(selectionEvent);
+		}
+		MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(selectionEvent);
+		return selectionEvent;
+	}
+
+	/**
+	 * Intended to be called back by subclasses.
+	 */
+	protected void handleElementEdit(IWorkbenchPart part, Object selectedElement, boolean contributeToContext) {
+		if (selectedElement == null)
+			return;
+		AbstractContextStructureBridge bridge = ContextCorePlugin.getDefault().getStructureBridge(selectedElement);
+		InteractionEvent editEvent = new InteractionEvent(InteractionEvent.Kind.EDIT, bridge.getContentType(),
+				bridge.getHandleIdentifier(selectedElement), part.getSite().getId());
+		if (contributeToContext) {
+			ContextCorePlugin.getContextManager().handleInteractionEvent(editEvent);
+		}
+		MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(editEvent);
+	}
+
+	/**
+	 * Intended to be called back by subclasses.
+	 */
+	protected void handleNavigation(IWorkbenchPart part, Object targetElement, String kind, boolean contributeToContext) {
+		AbstractContextStructureBridge adapter = ContextCorePlugin.getDefault().getStructureBridge(targetElement);
+		if (adapter.getContentType() != null) {
+			InteractionEvent navigationEvent = new InteractionEvent(InteractionEvent.Kind.SELECTION, adapter
+					.getContentType(), adapter.getHandleIdentifier(targetElement), part.getSite().getId(), kind);
+			if (contributeToContext) {
+				ContextCorePlugin.getContextManager().handleInteractionEvent(navigationEvent);
+			}
+			MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(navigationEvent);
+		}
+	}
+
+	public Kind getEventKind() {
+		return InteractionEvent.Kind.SELECTION;
+	}
+}
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/ActivityContextManager.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/ActivityContextManager.java
new file mode 100644
index 0000000..9ced445
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/ActivityContextManager.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui;
+
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.eclipse.mylar.context.core.ContextCorePlugin;
+import org.eclipse.mylar.internal.context.core.MylarContextManager;
+import org.eclipse.mylar.monitor.core.IActivityTimerListener;
+import org.eclipse.mylar.monitor.core.InteractionEvent;
+
+/**
+ * @author Mik Kersten
+ */
+public class ActivityContextManager implements IActivityTimerListener {
+
+	private AbstractUserActivityTimer userActivityTimer;
+
+	private boolean isStalled;
+
+	private Set<IUserAttentionListener> attentionListeners = new CopyOnWriteArraySet<IUserAttentionListener>();
+	
+	public ActivityContextManager(AbstractUserActivityTimer userActivityTimer) {
+		this.userActivityTimer = userActivityTimer;
+		userActivityTimer.addListener(this);
+	}
+	
+	public void fireActive() {
+		userActivityTimer.resetTimer();
+		if (isStalled) {
+			ContextCorePlugin.getContextManager().handleActivityMetaContextEvent(new InteractionEvent(InteractionEvent.Kind.COMMAND,
+					 MylarContextManager.ACTIVITY_STRUCTURE_KIND,  MylarContextManager.ACTIVITY_HANDLE_ATTENTION, MylarContextManager.ACTIVITY_ORIGIN_ID, null,
+					MylarContextManager.ACTIVITY_DELTA_ACTIVATED, 1f));
+			for (IUserAttentionListener attentionListener : attentionListeners) {
+				attentionListener.userAttentionGained();
+			}
+		}
+		isStalled = false;
+	}
+
+	public void fireInactive() {
+		if (!isStalled) {
+			ContextCorePlugin.getContextManager().handleActivityMetaContextEvent(new InteractionEvent(InteractionEvent.Kind.COMMAND,
+					 MylarContextManager.ACTIVITY_STRUCTURE_KIND,  MylarContextManager.ACTIVITY_HANDLE_ATTENTION, MylarContextManager.ACTIVITY_ORIGIN_ID, null,
+					MylarContextManager.ACTIVITY_DELTA_DEACTIVATED, 1f));
+			for (IUserAttentionListener attentionListener : attentionListeners) {
+				attentionListener.userAttentionLost();
+			}
+		}
+		isStalled = true;
+	}
+
+	public void start() {
+		userActivityTimer.start();
+	}
+	
+	public void stop() {
+		userActivityTimer.kill();
+	}
+	
+	public void setTimeoutMillis(int millis) {
+		userActivityTimer.setTimeoutMillis(millis);
+		userActivityTimer.resetTimer();
+	}
+	
+	public void addListener(IUserAttentionListener listener) {
+		attentionListeners.add(listener);
+	}
+
+	public void removeListener(IUserAttentionListener listener) {
+		attentionListeners.remove(listener);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/IActionExecutionListener.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/IActionExecutionListener.java
new file mode 100644
index 0000000..b49440d
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/IActionExecutionListener.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui;
+
+import org.eclipse.jface.action.IAction;
+
+/**
+ * @author Mik Kersten
+ */
+public interface IActionExecutionListener {
+ 
+	public void actionObserved(IAction action);
+
+}
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/IMylarMonitorLifecycleListener.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/IMylarMonitorLifecycleListener.java
new file mode 100644
index 0000000..d9f1751
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/IMylarMonitorLifecycleListener.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui;
+
+/**
+ * @author Brian de Alwis
+ */
+public interface IMylarMonitorLifecycleListener {
+
+	void startMonitoring();
+
+	void stopMonitoring();
+
+}
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/IUserAttentionListener.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/IUserAttentionListener.java
new file mode 100644
index 0000000..20a3574
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/IUserAttentionListener.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui;
+
+/**
+ * @author Mik Kersten
+ */
+public interface IUserAttentionListener {
+
+	public void userAttentionGained();
+	
+	public void userAttentionLost();
+	
+}
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/MylarMonitorUiPlugin.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/MylarMonitorUiPlugin.java
new file mode 100644
index 0000000..551e721
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/MylarMonitorUiPlugin.java
@@ -0,0 +1,320 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.mylar.context.core.ContextCorePlugin;
+import org.eclipse.mylar.core.MylarStatusHandler;
+import org.eclipse.mylar.monitor.core.IInteractionEventListener;
+import org.eclipse.mylar.monitor.core.InteractionEvent;
+import org.eclipse.ui.IPageListener;
+import org.eclipse.ui.IPartListener;
+import org.eclipse.ui.IPerspectiveListener;
+import org.eclipse.ui.ISelectionListener;
+import org.eclipse.ui.ISelectionService;
+import org.eclipse.ui.IWindowListener;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * @author Mik Kersten
+ */
+public class MylarMonitorUiPlugin extends AbstractUIPlugin {
+
+	private static final int TIMEOUT_INACTIVITY_MILLIS = 2 * 60 * 1000;
+
+	private int inactivityTimeout = TIMEOUT_INACTIVITY_MILLIS;
+
+	private static MylarMonitorUiPlugin INSTANCE;
+
+	private ShellLifecycleListener shellLifecycleListener;
+
+	private List<AbstractUserInteractionMonitor> selectionMonitors = new ArrayList<AbstractUserInteractionMonitor>();
+
+	/**
+	 * TODO: this could be merged with context interaction events rather than
+	 * requiring update from the monitor.
+	 */
+	private List<IInteractionEventListener> interactionListeners = new ArrayList<IInteractionEventListener>();
+
+	private ActivityContextManager activityContextManager;
+
+	private AbstractUserActivityTimer osActivityTimer = null;
+
+	protected Set<IPartListener> partListeners = new HashSet<IPartListener>();
+
+	protected Set<IPageListener> pageListeners = new HashSet<IPageListener>();
+
+	protected Set<IPerspectiveListener> perspectiveListeners = new HashSet<IPerspectiveListener>();
+
+	protected Set<ISelectionListener> postSelectionListeners = new HashSet<ISelectionListener>();
+
+	public static final String OBFUSCATED_LABEL = "[obfuscated]";
+
+	protected IWindowListener WINDOW_LISTENER = new IWindowListener() {
+		public void windowActivated(IWorkbenchWindow window) {
+			// ignore
+		}
+
+		public void windowDeactivated(IWorkbenchWindow window) {
+			// ignore
+		}
+
+		public void windowOpened(IWorkbenchWindow window) {
+			if (getWorkbench().isClosing()) {
+				return;
+			}
+			for (IPageListener listener : pageListeners) {
+				window.addPageListener(listener);
+			}
+			for (IPartListener listener : partListeners) {
+				window.getPartService().addPartListener(listener);
+			}
+			for (IPerspectiveListener listener : perspectiveListeners) {
+				window.addPerspectiveListener(listener);
+			}
+			for (ISelectionListener listener : postSelectionListeners) {
+				window.getSelectionService().addPostSelectionListener(listener);
+			}
+
+		}
+
+		public void windowClosed(IWorkbenchWindow window) {
+			for (IPageListener listener : pageListeners) {
+				window.removePageListener(listener);
+			}
+			for (IPartListener listener : partListeners) {
+				window.getPartService().removePartListener(listener);
+			}
+			for (IPerspectiveListener listener : perspectiveListeners) {
+				window.removePerspectiveListener(listener);
+			}
+			for (ISelectionListener listener : postSelectionListeners) {
+				window.getSelectionService().removePostSelectionListener(listener);
+			}
+		}
+	};
+
+	public MylarMonitorUiPlugin() {
+		INSTANCE = this;
+	}
+
+	@Override
+	public void start(BundleContext context) throws Exception {
+		super.start(context);
+		PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
+			public void run() {
+				try {
+					getWorkbench().addWindowListener(WINDOW_LISTENER);
+					shellLifecycleListener = new ShellLifecycleListener(ContextCorePlugin.getContextManager());
+					getWorkbench().getActiveWorkbenchWindow().getShell().addShellListener(shellLifecycleListener);
+
+					new MonitorUiExtensionPointReader().initExtensions();
+
+					AbstractUserActivityTimer activityTimer;
+					if (osActivityTimer != null) {
+						activityTimer = osActivityTimer;
+					} else {
+						activityTimer = new WorkbenchUserActivityTimer(TIMEOUT_INACTIVITY_MILLIS);
+					}
+
+					activityContextManager = new ActivityContextManager(activityTimer);
+					activityContextManager.start();
+				} catch (Exception e) {
+					MylarStatusHandler.fail(e, "Mylar Monitor start failed", false);
+				}
+			}
+		});
+	}
+
+	@Override
+	public void stop(BundleContext context) throws Exception {
+		super.stop(context);
+		try {
+			activityContextManager.stop();
+			if (getWorkbench() != null && !getWorkbench().isClosing()) {
+				getWorkbench().removeWindowListener(WINDOW_LISTENER);
+				getWorkbench().getActiveWorkbenchWindow().getShell().removeShellListener(shellLifecycleListener);
+			}
+		} catch (Exception e) {
+			MylarStatusHandler.fail(e, "Mylar Monitor stop failed", false);
+		}
+		INSTANCE = null;
+	}
+
+	public ShellLifecycleListener getShellLifecycleListener() {
+		return shellLifecycleListener;
+	}
+
+	public void setInactivityTimeout(int millis) {
+		inactivityTimeout = millis;
+		activityContextManager.setTimeoutMillis(millis);
+	}
+
+	/**
+	 * @return timeout in mililiseconds
+	 */
+	public int getInactivityTimeout() {
+		return inactivityTimeout;
+	}
+
+	public void addWindowPartListener(IPartListener listener) {
+		partListeners.add(listener);
+		for (IWorkbenchWindow window : getWorkbench().getWorkbenchWindows()) {
+			window.getPartService().addPartListener(listener);
+		}
+	}
+
+	public void removeWindowPartListener(IPartListener listener) {
+		partListeners.remove(listener);
+		for (IWorkbenchWindow window : getWorkbench().getWorkbenchWindows()) {
+			window.getPartService().removePartListener(listener);
+		}
+	}
+
+	public void addWindowPageListener(IPageListener listener) {
+		pageListeners.add(listener);
+		for (IWorkbenchWindow window : getWorkbench().getWorkbenchWindows()) {
+			window.addPageListener(listener);
+		}
+	}
+
+	public void removeWindowPageListener(IPageListener listener) {
+		pageListeners.remove(listener);
+		for (IWorkbenchWindow window : getWorkbench().getWorkbenchWindows()) {
+			window.removePageListener(listener);
+		}
+	}
+
+	public void addWindowPerspectiveListener(IPerspectiveListener listener) {
+		perspectiveListeners.add(listener);
+		for (IWorkbenchWindow window : getWorkbench().getWorkbenchWindows()) {
+			window.addPerspectiveListener(listener);
+		}
+	}
+
+	public void removeWindowPerspectiveListener(IPerspectiveListener listener) {
+		perspectiveListeners.remove(listener);
+		for (IWorkbenchWindow window : getWorkbench().getWorkbenchWindows()) {
+			window.removePerspectiveListener(listener);
+		}
+	}
+
+	public void addWindowPostSelectionListener(ISelectionListener listener) {
+		postSelectionListeners.add(listener);
+		for (IWorkbenchWindow window : getWorkbench().getWorkbenchWindows()) {
+			ISelectionService service = window.getSelectionService();
+			service.addPostSelectionListener(listener);
+		}
+	}
+
+	public void removeWindowPostSelectionListener(ISelectionListener listener) {
+		getDefault().postSelectionListeners.remove(listener);
+		for (IWorkbenchWindow window : getDefault().getWorkbench().getWorkbenchWindows()) {
+			ISelectionService service = window.getSelectionService();
+			service.removePostSelectionListener(listener);
+		}
+	}
+
+	public static MylarMonitorUiPlugin getDefault() {
+		return INSTANCE;
+	}
+
+	public List<AbstractUserInteractionMonitor> getSelectionMonitors() {
+		return selectionMonitors;
+	}
+
+	public void addInteractionListener(IInteractionEventListener listener) {
+		interactionListeners.add(listener);
+	}
+
+	public void removeInteractionListener(IInteractionEventListener listener) {
+		interactionListeners.remove(listener);
+	}
+
+	/**
+	 * TODO: refactor this, it's awkward
+	 */
+	public void notifyInteractionObserved(InteractionEvent interactionEvent) {
+		for (IInteractionEventListener listener : interactionListeners) {
+			listener.interactionObserved(interactionEvent);
+		}
+	}
+
+	public List<IInteractionEventListener> getInteractionListeners() {
+		return interactionListeners;
+	}
+
+	class MonitorUiExtensionPointReader {
+
+		public static final String EXTENSION_ID_STUDY = "org.eclipse.mylar.monitor.ui";
+
+		public static final String ELEMENT_ACTIVITY_TIMER = "osActivityTimer";
+
+		public static final String ELEMENT_CLASS = "class";
+
+		private boolean extensionsRead = false;
+
+		@SuppressWarnings("deprecation")
+		public void initExtensions() {
+			try {
+				if (!extensionsRead) {
+					IExtensionRegistry registry = Platform.getExtensionRegistry();
+					IExtensionPoint extensionPoint = registry.getExtensionPoint(EXTENSION_ID_STUDY);
+					if (extensionPoint != null) {
+						IExtension[] extensions = extensionPoint.getExtensions();
+						for (int i = 0; i < extensions.length; i++) {
+							IConfigurationElement[] elements = extensions[i].getConfigurationElements();
+							for (int j = 0; j < elements.length; j++) {
+								if (elements[j].getName().compareTo(ELEMENT_ACTIVITY_TIMER) == 0) {
+									readActivityTimer(elements[j]);
+								}
+							}
+						}
+						extensionsRead = true;
+					}
+				}
+			} catch (Throwable t) {
+				MylarStatusHandler.fail(t, "could not read monitor extension", false);
+			}
+		}
+
+		private void readActivityTimer(IConfigurationElement element) throws CoreException {
+			try {
+				if (element.getAttribute(ELEMENT_CLASS) != null) {
+					Object activityTimer = element.createExecutableExtension(ELEMENT_CLASS);
+					if (activityTimer instanceof AbstractUserActivityTimer) {
+						osActivityTimer = (AbstractUserActivityTimer) activityTimer;
+					}
+				}
+			} catch (CoreException throwable) {
+				MylarStatusHandler.log(throwable, "could not load activity timer");
+			}
+		}
+	}
+
+	public ActivityContextManager getActivityContextManager() {
+		return activityContextManager;
+	}
+}
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/ShellLifecycleListener.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/ShellLifecycleListener.java
new file mode 100644
index 0000000..f7143da
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/ShellLifecycleListener.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui;
+
+import org.eclipse.mylar.internal.context.core.MylarContextManager;
+import org.eclipse.mylar.monitor.core.InteractionEvent;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.events.ShellListener;
+
+/**
+ * @author Mik Kersten
+ */
+class ShellLifecycleListener implements ShellListener {
+
+	private final MylarContextManager manager;
+
+	public ShellLifecycleListener(MylarContextManager manager) {
+		this.manager = manager;
+		manager.handleActivityMetaContextEvent(new InteractionEvent(InteractionEvent.Kind.COMMAND, MylarContextManager.ACTIVITY_STRUCTURE_KIND,
+				MylarContextManager.ACTIVITY_HANDLE_LIFECYCLE, MylarContextManager.ACTIVITY_ORIGIN_ID, null, MylarContextManager.ACTIVITY_DELTA_STARTED, 1f));
+	}
+
+	public void shellClosed(ShellEvent shellEvent) {
+		manager.handleActivityMetaContextEvent(new InteractionEvent(InteractionEvent.Kind.COMMAND, MylarContextManager.ACTIVITY_STRUCTURE_KIND,
+				MylarContextManager.ACTIVITY_HANDLE_ATTENTION, MylarContextManager.ACTIVITY_ORIGIN_ID, null, MylarContextManager.ACTIVITY_DELTA_DEACTIVATED, 1f));
+
+		manager.deactivateAllContexts();
+		
+		manager.handleActivityMetaContextEvent(new InteractionEvent(InteractionEvent.Kind.COMMAND, MylarContextManager.ACTIVITY_STRUCTURE_KIND,
+				MylarContextManager.ACTIVITY_HANDLE_LIFECYCLE, MylarContextManager.ACTIVITY_ORIGIN_ID, null, MylarContextManager.ACTIVITY_DELTA_STOPPED, 1f));
+	
+	}
+
+	public void shellDeactivated(ShellEvent arg0) {
+		// ignore
+	}
+
+	public void shellActivated(ShellEvent arg0) {
+		// ignore
+	}
+
+	public void shellDeiconified(ShellEvent arg0) {
+		// ingore
+	}
+
+	public void shellIconified(ShellEvent arg0) {
+		// ignore
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/WorkbenchUserActivityTimer.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/WorkbenchUserActivityTimer.java
new file mode 100644
index 0000000..f49ac90
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/WorkbenchUserActivityTimer.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.mylar.monitor.core.IActivityTimerListener;
+import org.eclipse.mylar.monitor.core.IInteractionEventListener;
+import org.eclipse.mylar.monitor.core.InteractionEvent;
+import org.eclipse.mylar.monitor.core.ActivityTimerThread;
+
+/**
+ * @author Mik Kersten
+ */
+public class WorkbenchUserActivityTimer extends AbstractUserActivityTimer implements IInteractionEventListener {
+	
+	private ActivityTimerThread timerThread;
+	
+	private Set<IActivityTimerListener> listeners = new HashSet<IActivityTimerListener>();
+	
+	public WorkbenchUserActivityTimer(int millis) {
+		timerThread = new ActivityTimerThread(millis);
+	}
+
+	public void interactionObserved(InteractionEvent event) {
+		for (IActivityTimerListener listener : listeners) {
+			listener.fireActive();
+		}
+	}
+	
+	@Override
+	public boolean addListener(IActivityTimerListener activityListener) {
+		listeners.add(activityListener);
+		return timerThread.addListener(activityListener);
+	}
+
+	@Override
+	public void start() {
+		timerThread.start();
+		MylarMonitorUiPlugin.getDefault().addInteractionListener(this);
+	}
+	
+	@Override
+	public void kill() {
+		if (Platform.isRunning() && MylarMonitorUiPlugin.getDefault() != null) {
+			timerThread.kill();
+			MylarMonitorUiPlugin.getDefault().removeInteractionListener(this);
+		}
+	}
+
+	@Override
+	public void resetTimer() {
+		timerThread.resetTimer();
+	}
+
+	@Override
+	public void setTimeoutMillis(int millis) {
+		timerThread.setTimeoutMillis(millis);
+	}
+
+	public void startMonitoring() {
+		// ignore
+	}
+
+	public void stopMonitoring() {
+		// ignore
+	}
+}
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/AbstractEditorTracker.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/AbstractEditorTracker.java
new file mode 100644
index 0000000..70f86d1
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/AbstractEditorTracker.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui.workbench;
+
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * @author Mik Kersten
+ */
+public abstract class AbstractEditorTracker extends AbstractPartTracker {
+
+	@Override
+	public void partClosed(IWorkbenchPart part) {
+		if (part instanceof IEditorPart) {
+			editorClosed((IEditorPart) part);
+		}
+	}
+
+	@Override
+	public void partOpened(IWorkbenchPart part) {
+		if (part instanceof IEditorPart) {
+			editorOpened((IEditorPart) part);
+		}
+	}
+
+	@Override
+	public void partBroughtToTop(IWorkbenchPart part) {
+		if (part instanceof IEditorPart) {
+			editorBroughtToTop((IEditorPart) part);
+		}
+	}
+
+	protected abstract void editorOpened(IEditorPart part);
+
+	protected abstract void editorClosed(IEditorPart part);
+
+	protected abstract void editorBroughtToTop(IEditorPart part);
+	
+	@Override
+	public void partActivated(IWorkbenchPart part) {
+		// ignore
+	}
+
+	@Override
+	public void partDeactivated(IWorkbenchPart part) {
+	}
+
+}
\ No newline at end of file
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/AbstractPartTracker.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/AbstractPartTracker.java
new file mode 100644
index 0000000..5e731e1
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/AbstractPartTracker.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui.workbench;
+
+import org.eclipse.mylar.monitor.ui.MylarMonitorUiPlugin;
+import org.eclipse.ui.IPartListener;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * @author Mik Kersten
+ */
+public abstract class AbstractPartTracker implements IPartListener {
+
+	public void install(IWorkbench workbench) {
+		MylarMonitorUiPlugin.getDefault().addWindowPartListener(this);
+	}
+
+	public void dispose(IWorkbench workbench) {
+		MylarMonitorUiPlugin.getDefault().removeWindowPartListener(this);
+	}
+
+	public abstract void partActivated(IWorkbenchPart part);
+
+	public abstract void partBroughtToTop(IWorkbenchPart part);
+
+	public abstract void partClosed(IWorkbenchPart part);
+
+	public abstract void partDeactivated(IWorkbenchPart part);
+
+	public abstract void partOpened(IWorkbenchPart part);
+
+}
\ No newline at end of file
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/ActionExecutionMonitor.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/ActionExecutionMonitor.java
new file mode 100644
index 0000000..5a14dd7
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/ActionExecutionMonitor.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui.workbench;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.mylar.monitor.core.InteractionEvent;
+import org.eclipse.mylar.monitor.ui.IActionExecutionListener;
+import org.eclipse.mylar.monitor.ui.MylarMonitorUiPlugin;
+
+/**
+ * @author Mik Kersten
+ */
+public class ActionExecutionMonitor implements IActionExecutionListener {
+
+	public void actionObserved(IAction action) {
+		InteractionEvent interactionEvent = InteractionEvent.makeCommand(action.getId(), "");
+		MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(interactionEvent);
+	}
+}
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/ActivityChangeMonitor.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/ActivityChangeMonitor.java
new file mode 100644
index 0000000..e4499c3
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/ActivityChangeMonitor.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui.workbench;
+
+import org.eclipse.mylar.monitor.core.InteractionEvent;
+import org.eclipse.mylar.monitor.ui.MylarMonitorUiPlugin;
+import org.eclipse.ui.activities.ActivityManagerEvent;
+import org.eclipse.ui.activities.IActivityManagerListener;
+
+/**
+ * @author Mik Kersten
+ */
+public class ActivityChangeMonitor implements IActivityManagerListener {
+
+	private static final String ACTIVITIES_CHANGED = "activities changed";
+
+	public void activityManagerChanged(ActivityManagerEvent activityManagerEvent) {
+		if (activityManagerEvent.haveEnabledActivityIdsChanged()) {
+			String source = activityManagerEvent.getActivityManager().toString();
+			String delta = activityManagerEvent.getActivityManager().getEnabledActivityIds().toString();
+			InteractionEvent interactionEvent = InteractionEvent.makePreference(source, ACTIVITIES_CHANGED + ": "
+					+ delta);
+			MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(interactionEvent);
+		}
+	}
+}
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/BrowserMonitor.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/BrowserMonitor.java
new file mode 100644
index 0000000..d8ce4dd
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/BrowserMonitor.java
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui.workbench;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.mylar.core.MylarStatusHandler;
+import org.eclipse.mylar.monitor.core.InteractionEvent;
+import org.eclipse.mylar.monitor.ui.AbstractUserInteractionMonitor;
+import org.eclipse.mylar.monitor.ui.MylarMonitorUiPlugin;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.browser.LocationEvent;
+import org.eclipse.swt.browser.LocationListener;
+import org.eclipse.ui.IPageListener;
+import org.eclipse.ui.IPartListener;
+import org.eclipse.ui.IWindowListener;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.internal.browser.BrowserViewer;
+import org.eclipse.ui.internal.browser.WebBrowserEditor;
+
+/**
+ * @author Mik Kersten
+ */
+public class BrowserMonitor extends AbstractUserInteractionMonitor implements IPartListener, IWindowListener,
+		IPageListener {
+
+	public static final String URL_LIST_DELIM = ",";
+
+	private UrlTrackingListener urlTrackingListener = new UrlTrackingListener();
+
+	private List<String> acceptedUrls = new ArrayList<String>();
+
+	class UrlTrackingListener implements LocationListener {
+
+		public void changing(LocationEvent event) {
+			// ignore
+		}
+
+		public void changed(LocationEvent locationEvent) {
+			String url = locationEvent.location;
+			boolean accept = false;
+			for (String urlMatch : acceptedUrls) {
+				if (url.indexOf(urlMatch) != -1)
+					accept = true;
+			}
+			if (accept) {
+				InteractionEvent interactionEvent = new InteractionEvent(InteractionEvent.Kind.SELECTION, "url", url,
+						WebBrowserEditor.WEB_BROWSER_EDITOR_ID, "null", "", 0);
+				MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(interactionEvent); // TODO:
+																						// move
+			}
+		}
+	}
+
+	@Override
+	protected void handleWorkbenchPartSelection(IWorkbenchPart part, ISelection selection, boolean contributeToContext) {
+		// ignore, this is a special case
+	}
+
+	// ---- Part Listener
+
+	public void partOpened(IWorkbenchPart part) {
+		if (part instanceof WebBrowserEditor) {
+			Browser browser = getBrowser((WebBrowserEditor) part);
+			if (browser != null)
+				browser.addLocationListener(urlTrackingListener);
+		}
+	}
+
+	public void partClosed(IWorkbenchPart part) {
+		if (part instanceof WebBrowserEditor) {
+			Browser browser = getBrowser((WebBrowserEditor) part);
+			if (browser != null)
+				browser.removeLocationListener(urlTrackingListener);
+		}
+	}
+
+	public void partActivated(IWorkbenchPart part) {
+	}
+
+	public void partBroughtToTop(IWorkbenchPart part) {
+	}
+
+	public void partDeactivated(IWorkbenchPart part) {
+	}
+
+	private Browser getBrowser(final WebBrowserEditor browserEditor) {
+		try { // HACK: using reflection to gain accessibility
+			Class<?> browserClass = browserEditor.getClass();
+			Field browserField = browserClass.getDeclaredField("webBrowser");
+			browserField.setAccessible(true);
+			Object browserObject = browserField.get(browserEditor);
+			if (browserObject != null && browserObject instanceof BrowserViewer) {
+				return ((BrowserViewer) browserObject).getBrowser();
+			}
+		} catch (Exception e) {
+			MylarStatusHandler.log(e, "could not add browser listener");
+		}
+		return null;
+	}
+
+	// --- Window listener
+
+	public void windowActivated(IWorkbenchWindow window) {
+	}
+
+	public void windowDeactivated(IWorkbenchWindow window) {
+	}
+
+	public void windowClosed(IWorkbenchWindow window) {
+		window.removePageListener(this);
+	}
+
+	public void windowOpened(IWorkbenchWindow window) {
+		window.addPageListener(this);
+	}
+
+	// ---- IPageListener
+
+	public void pageActivated(IWorkbenchPage page) {
+	}
+
+	public void pageClosed(IWorkbenchPage page) {
+		page.removePartListener(this);
+	}
+
+	public void pageOpened(IWorkbenchPage page) {
+		page.addPartListener(this);
+	}
+
+	public List<String> getAcceptedUrls() {
+		return acceptedUrls;
+	}
+
+	public void setAcceptedUrls(String urlBuffer) {
+		acceptedUrls = new ArrayList<String>();
+
+		if (urlBuffer != null) {
+			StringTokenizer token = new StringTokenizer(urlBuffer, URL_LIST_DELIM);
+			while (token.hasMoreTokens()) {
+				acceptedUrls.add(token.nextToken());
+			}
+		}
+	}
+
+}
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/KeybindingCommandMonitor.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/KeybindingCommandMonitor.java
new file mode 100644
index 0000000..abf4ce1
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/KeybindingCommandMonitor.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui.workbench;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.mylar.monitor.core.InteractionEvent;
+import org.eclipse.mylar.monitor.ui.AbstractCommandMonitor;
+import org.eclipse.mylar.monitor.ui.MylarMonitorUiPlugin;
+
+/**
+ * @author Mik Kersten
+ */
+public class KeybindingCommandMonitor extends AbstractCommandMonitor {
+
+	public static final String COMMAND_INVOKED = "keybinding";
+
+	@Override
+	protected void handleCommandExecution(String commandId, ExecutionEvent event) {
+		InteractionEvent interactionEvent = InteractionEvent.makeCommand(commandId, COMMAND_INVOKED);
+		MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(interactionEvent);
+	}
+
+}
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/MenuCommandMonitor.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/MenuCommandMonitor.java
new file mode 100644
index 0000000..c9282d1
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/MenuCommandMonitor.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui.workbench;
+
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.mylar.core.MylarStatusHandler;
+import org.eclipse.mylar.monitor.core.InteractionEvent;
+import org.eclipse.mylar.monitor.ui.MylarMonitorUiPlugin;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.ToolItem;
+
+/**
+ * @author Leah Findlater and Mik Kersten
+ */
+public class MenuCommandMonitor implements Listener {
+	public static final String MENU_ITEM_ID = "item.label.";
+
+	public static final String MENU_ITEM_SELECTED = "menu";
+
+	public static final String TOOLBAR_ITEM_SELECTED = "toolbar";
+
+	public static final String MENU_PATH_DELIM = "/";
+
+	public void handleEvent(Event event) {
+		try {
+			if (!(event.widget instanceof Item))
+				return;
+			Item item = (Item) event.widget;
+			if (item.getData() == null)
+				return;
+			Object target = event.widget.getData();
+			String id = null;
+			String delta = null;
+			if (target instanceof IContributionItem)
+				id = ((IContributionItem) target).getId();
+
+			if (item instanceof MenuItem) {
+				MenuItem menu = (MenuItem) item;
+				Menu parentMenu = menu.getParent();
+				String location = "";
+				if (parentMenu != null) {
+					while (parentMenu.getParentItem() != null) {
+						location = parentMenu.getParentItem().getText() + MENU_PATH_DELIM + location;
+						parentMenu = parentMenu.getParentMenu();
+					}
+				}
+				String simpleId = "";
+				if (id == null)
+					id = "null";
+				String itemText = obfuscateValueIfContainsPath(item.getText());
+				id = id + "$" + MENU_ITEM_ID + simpleId + location + itemText;
+
+				delta = MENU_ITEM_SELECTED;
+			} else if (item instanceof ToolItem) {
+				ToolItem tool = (ToolItem) item;
+				String simpleId = "";
+				if (id == null)
+					id = "null";
+				id = id + "$" + MENU_ITEM_ID + simpleId + '.' + tool.getToolTipText();
+
+				delta = TOOLBAR_ITEM_SELECTED;
+			}
+			InteractionEvent interactionEvent = InteractionEvent.makeCommand(id, delta);
+			MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(interactionEvent);
+
+		} catch (Throwable t) {
+			MylarStatusHandler.fail(t, "Could not log selection", false);
+		}
+	}
+
+	/**
+	 * TODO: generalize this to other resources whose names are private
+	 */
+	private String obfuscateValueIfContainsPath(String text) {
+		if (text.indexOf(".java") != -1 || text.indexOf(".xml") != -1) {
+			return MylarMonitorUiPlugin.OBFUSCATED_LABEL;
+		} else {
+			return text;
+		}
+	}
+}
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/PerspectiveChangeMonitor.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/PerspectiveChangeMonitor.java
new file mode 100644
index 0000000..7c07b37
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/PerspectiveChangeMonitor.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui.workbench;
+
+import org.eclipse.mylar.monitor.core.InteractionEvent;
+import org.eclipse.mylar.monitor.ui.MylarMonitorUiPlugin;
+import org.eclipse.ui.IPerspectiveDescriptor;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.PerspectiveAdapter;
+import org.eclipse.ui.internal.registry.PerspectiveDescriptor;
+
+/**
+ * @author Leah Findlater and Mik Kersten
+ */
+public class PerspectiveChangeMonitor extends PerspectiveAdapter {
+
+	public static final String PERSPECTIVE_SAVED = "perspective saved";
+
+	public static final String PERSPECTIVE_OPENED = "perspective opened";
+
+	public static final String PERSPECTIVE_CLOSED = "perspective closed";
+
+	public static final String PERSPECTIVE_CHANGED = "perspective changed";
+
+	public static final String PERSPECTIVE_ACTIVATED = "perspective activated";
+
+	@Override
+	public void perspectiveActivated(IWorkbenchPage page, IPerspectiveDescriptor perspective) {
+		String source = this.getPerspectiveId(perspective);
+
+		InteractionEvent interactionEvent = InteractionEvent.makePreference(source, PERSPECTIVE_ACTIVATED);
+		MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(interactionEvent);
+	}
+
+	@Override
+	public void perspectiveChanged(IWorkbenchPage page, IPerspectiveDescriptor perspective,
+			IWorkbenchPartReference partRef, String changeId) {
+		String source = partRef.getId();
+		InteractionEvent interactionEvent = InteractionEvent.makePreference(source, PERSPECTIVE_CHANGED + ": "
+				+ changeId);
+		MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(interactionEvent);
+	}
+
+	@Override
+	public void perspectiveChanged(IWorkbenchPage page, IPerspectiveDescriptor perspective, String changeId) {
+		String source = this.getPerspectiveId(perspective);
+		InteractionEvent interactionEvent = InteractionEvent.makePreference(source, PERSPECTIVE_CHANGED + ": "
+				+ changeId);
+		MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(interactionEvent);
+	}
+
+	@Override
+	public void perspectiveClosed(IWorkbenchPage page, IPerspectiveDescriptor perspective) {
+		String source = this.getPerspectiveId(perspective);
+		InteractionEvent interactionEvent = InteractionEvent.makePreference(source, PERSPECTIVE_CLOSED);
+		MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(interactionEvent);
+	}
+
+	@Override
+	public void perspectiveOpened(IWorkbenchPage page, IPerspectiveDescriptor perspective) {
+		String source = this.getPerspectiveId(perspective);
+		InteractionEvent interactionEvent = InteractionEvent.makePreference(source, PERSPECTIVE_OPENED);
+		MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(interactionEvent);
+	}
+
+	@Override
+	public void perspectiveSavedAs(IWorkbenchPage page, IPerspectiveDescriptor oldPerspective,
+			IPerspectiveDescriptor newPerspective) {
+		String source = this.getPerspectiveId(newPerspective);
+		InteractionEvent interactionEvent = InteractionEvent.makePreference(source, PERSPECTIVE_SAVED);
+		MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(interactionEvent);
+	}
+
+	private String getPerspectiveId(IPerspectiveDescriptor perspective) {
+		String id;
+
+		if (perspective instanceof PerspectiveDescriptor) {
+			String originalId = ((PerspectiveDescriptor) perspective).getOriginalId();
+			if (!originalId.equals(perspective.getId()))
+				id = originalId + "[customized]";
+			else
+				id = perspective.getId();
+		} else
+			id = perspective.getId();
+		return id;
+	}
+
+}
+
+/* Perspective listener methods */
+
+// TODO Should we comment out the more detailed perspective listener methods and
+// just use this one instead? This one logs the open set of views and editors
+// whenever that changes.
+/*
+ * @Override public void perspectiveChanged(IWorkbenchPage page,
+ * IPerspectiveDescriptor perspective, String changeId) {
+ * super.perspectiveChanged(page, perspective, changeId);
+ * 
+ * if(changeId.startsWith("view") || changeId.startsWith("editor")) {
+ * IWorkbenchPage workbenchPage =
+ * PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ * IViewReference[] viewList = workbenchPage.getViewReferences();
+ * IEditorReference[] editorList = workbenchPage.getEditorReferences();
+ * 
+ * String delta = ""; for(int i = 0; i < viewList.length; i++) { delta = delta +
+ * viewList[i].getTitle() + ","; } delta = delta + "Editor (" +
+ * editorList.length + " open)";
+ * 
+ * String source = "perspective." + perspective.getLabel(); InteractionEvent
+ * interactionEvent = new InteractionEvent( source, delta );
+ * logger.interactionObserved(interactionEvent); } }
+ */
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/PreferenceChangeMonitor.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/PreferenceChangeMonitor.java
new file mode 100644
index 0000000..6b28e3f
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/PreferenceChangeMonitor.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui.workbench;
+
+import org.eclipse.core.runtime.Preferences.IPropertyChangeListener;
+import org.eclipse.core.runtime.Preferences.PropertyChangeEvent;
+import org.eclipse.mylar.context.core.ContextCorePlugin;
+import org.eclipse.mylar.monitor.core.InteractionEvent;
+import org.eclipse.mylar.monitor.ui.MylarMonitorUiPlugin;
+
+/**
+ * @author Mik Kersten
+ */
+public class PreferenceChangeMonitor implements IPropertyChangeListener {
+
+	public void propertyChange(PropertyChangeEvent event) {
+		String newValue = obfuscateValueIfContainsPath(event.getNewValue().toString());
+		InteractionEvent interactionEvent = InteractionEvent.makePreference(event.getProperty(), newValue);
+		if (ContextCorePlugin.getDefault() != null) {
+			MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(interactionEvent);
+		}
+	}
+
+	private String obfuscateValueIfContainsPath(String preferenceValue) {
+		if (preferenceValue.indexOf(java.io.File.separator) != -1 || preferenceValue.indexOf('/') != -1) {
+			return MylarMonitorUiPlugin.OBFUSCATED_LABEL;
+		} else {
+			return preferenceValue;
+		}
+	}
+}
diff --git a/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/WindowChangeMonitor.java b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/WindowChangeMonitor.java
new file mode 100644
index 0000000..19adef4
--- /dev/null
+++ b/org.eclipse.mylyn.monitor.ui/src/org/eclipse/mylyn/monitor/ui/workbench/WindowChangeMonitor.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2004 - 2006 University Of British Columbia 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:
+ *     University Of British Columbia - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylar.monitor.ui.workbench;
+
+import org.eclipse.mylar.monitor.core.InteractionEvent;
+import org.eclipse.mylar.monitor.ui.MylarMonitorUiPlugin;
+import org.eclipse.ui.IWindowListener;
+import org.eclipse.ui.IWorkbenchWindow;
+
+/**
+ * Logs all bug root window selections (i.e. the window that the workbench is
+ * launced with).
+ * 
+ * @author Leah Findlater and Mik Kersten
+ */
+public class WindowChangeMonitor implements IWindowListener {
+
+	public static final String WINDOW_CLOSED = "closed";
+
+	public static final String WINDOW_OPENED = "opened";
+
+	public static final String WINDOW_ACTIVATED = "activated";
+
+	public static final String WINDOW_DEACTIVATED = "deactivated";
+
+	public WindowChangeMonitor() {
+		super();
+	}
+
+	// TODO: Should we add the default set of monitors to the new window as
+	// well?
+	public void windowOpened(IWorkbenchWindow window) {
+			InteractionEvent interactionEvent = InteractionEvent.makeCommand(getWindowOrigin(window),
+					WINDOW_OPENED);
+			MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(interactionEvent);
+	}
+
+	public void windowClosed(IWorkbenchWindow window) {
+			InteractionEvent interactionEvent = InteractionEvent.makeCommand(getWindowOrigin(window),
+					WINDOW_CLOSED);
+			MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(interactionEvent);
+	}
+
+	public void windowDeactivated(IWorkbenchWindow window) {
+		InteractionEvent interactionEvent = InteractionEvent.makeCommand(getWindowOrigin(window),
+				WINDOW_DEACTIVATED);
+		MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(interactionEvent);
+	}
+
+	public void windowActivated(IWorkbenchWindow window) {
+		InteractionEvent interactionEvent = InteractionEvent.makeCommand(getWindowOrigin(window),
+				WINDOW_ACTIVATED);
+		MylarMonitorUiPlugin.getDefault().notifyInteractionObserved(interactionEvent);
+	}
+
+	protected String getWindowOrigin(IWorkbenchWindow window) {
+		return window.getClass().getCanonicalName() + "@" + window.hashCode();
+	}
+}