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();
+ }
+}