Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal')
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationAction.java52
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationCategory.java46
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationElement.java80
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationEvent.java116
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationHandler.java41
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationModel.java83
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationService.java131
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationSinkDescriptor.java59
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationsExtensionReader.java142
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/events/EventListener.java33
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/factory/DefaultFormTextFactoryDelegate.java74
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/factory/FactoryDelegateManager.java93
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/AbstractNotificationPopup.java615
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/AnimationUtil.java159
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/GradientColors.java187
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/NotificationPopup.java154
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/PopupNotificationSink.java156
-rw-r--r--target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/ScalingHyperlink.java112
18 files changed, 1639 insertions, 694 deletions
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationAction.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationAction.java
deleted file mode 100644
index 34f46ac55..000000000
--- a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationAction.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2010, 2013 Tasktop Technologies 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:
- * Tasktop Technologies - initial API and implementation
- * Wind River Systems - Extracted from o.e.mylyn.commons and adapted for Target Explorer
- *******************************************************************************/
-
-package org.eclipse.tcf.te.ui.notifications.internal;
-
-import org.eclipse.core.runtime.Assert;
-import org.eclipse.tcf.te.runtime.notifications.NotificationSink;
-
-/**
- * Describes how a {@link NotificationEvent} is handled. {@link NotificationAction}s store enablement and parameters
- * that determine how the {@link NotificationSink} executes the action.
- *
- * @author Steffen Pingel
- */
-public class NotificationAction {
-
- private boolean selected;
-
- private final NotificationSinkDescriptor sinkDescriptor;
-
- public NotificationAction(NotificationSinkDescriptor sinkDescriptor) {
- Assert.isNotNull(sinkDescriptor);
- this.sinkDescriptor = sinkDescriptor;
- }
-
- public NotificationSinkDescriptor getSinkDescriptor() {
- return sinkDescriptor;
- }
-
- public boolean isSelected() {
- return selected;
- }
-
- public void setSelected(boolean selected) {
- this.selected = selected;
- }
-
- @Override
- public String toString() {
- return sinkDescriptor.getLabel();
- }
-
-}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationCategory.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationCategory.java
deleted file mode 100644
index c9a261922..000000000
--- a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationCategory.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2010, 2013 Tasktop Technologies 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:
- * Tasktop Technologies - initial API and implementation
- * Wind River Systems - Extracted from o.e.mylyn.commons and adapted for Target Explorer
- *******************************************************************************/
-
-package org.eclipse.tcf.te.ui.notifications.internal;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.core.runtime.IConfigurationElement;
-
-/**
- * @author Steffen Pingel
- */
-public class NotificationCategory extends NotificationElement {
-
- private final List<NotificationEvent> events;
-
- public NotificationCategory(IConfigurationElement element) {
- super(element);
- this.events = new ArrayList<NotificationEvent>();
- }
-
- public void addEvent(NotificationEvent event) {
- event.setCategory(this);
- events.add(event);
- }
-
- public List<NotificationEvent> getEvents() {
- return events;
- }
-
- public void removeEvent(NotificationEvent event) {
- event.setCategory(null);
- events.remove(event);
- }
-
-}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationElement.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationElement.java
deleted file mode 100644
index 7de3b44ee..000000000
--- a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationElement.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2010, 2013 Tasktop Technologies 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:
- * Tasktop Technologies - initial API and implementation
- * Wind River Systems - Extracted from o.e.mylyn.commons and adapted for Target Explorer
- *******************************************************************************/
-
-package org.eclipse.tcf.te.ui.notifications.internal;
-
-import org.eclipse.core.runtime.Assert;
-import org.eclipse.core.runtime.IConfigurationElement;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.osgi.util.NLS;
-import org.eclipse.tcf.te.ui.notifications.activator.UIPlugin;
-import org.eclipse.ui.plugin.AbstractUIPlugin;
-
-/**
- * @author Steffen Pingel
- */
-public class NotificationElement {
-
- protected final IConfigurationElement element;
-
- private ImageDescriptor iconDescriptor;
-
- private final String id;
-
- private final String label;
-
- public NotificationElement(IConfigurationElement element) {
- Assert.isNotNull(element);
- this.element = element;
- this.id = element.getAttribute("id"); //$NON-NLS-1$
- this.label = element.getAttribute("label"); //$NON-NLS-1$
- }
-
- public String getId() {
- return id;
- }
-
- public ImageDescriptor getImageDescriptor() {
- if (iconDescriptor == null) {
- if (element != null) {
- String iconPath = element.getAttribute("icon"); //$NON-NLS-1$
- if (iconPath != null) {
- iconDescriptor = AbstractUIPlugin.imageDescriptorFromPlugin(element.getContributor().getName(), iconPath);
- }
- }
- }
- return iconDescriptor;
- }
-
- public String getLabel() {
- return label;
- }
-
- public String getPluginId() {
- return element.getContributor().getName();
- }
-
- public IStatus validate() {
- if (id == null) {
- return new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(),
- NLS.bind("Extension {0} contributed by {1} does not specify id attribute", element.getNamespaceIdentifier(), getPluginId())); //$NON-NLS-1$
- }
- else if (label == null) {
- return new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(),
- NLS.bind("Extension {0} contributed by {1} does not specify label attribute", element.getNamespaceIdentifier(), getPluginId())); //$NON-NLS-1$
- }
- return Status.OK_STATUS;
- }
-
-}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationEvent.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationEvent.java
deleted file mode 100644
index 195072533..000000000
--- a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationEvent.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2010, 2013 Tasktop Technologies 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:
- * Tasktop Technologies - initial API and implementation
- * Itema AS - bug 331424 handle default event-sink action associations
- * Wind River Systems - Extracted from o.e.mylyn.commons and adapted for Target Explorer
- *******************************************************************************/
-
-package org.eclipse.tcf.te.ui.notifications.internal;
-
-import java.util.ArrayList;
-
-import org.eclipse.core.runtime.IConfigurationElement;
-import org.eclipse.core.runtime.IExtensionPoint;
-import org.eclipse.core.runtime.IExtensionRegistry;
-import org.eclipse.core.runtime.Platform;
-import org.eclipse.tcf.te.runtime.notifications.NotificationSink;
-
-/**
- * Describes an event that is handled through a notification. The handling of event is stored in
- * {@link NotificationAction} objects that delegate to {@link NotificationSink} objects for the handling of actual
- * events.
- *
- * @author Steffen Pingel
- * @author Torkild U. Resheim
- */
-public class NotificationEvent extends NotificationElement {
-
- private static final String EXTENSION_POINT_ID = "org.eclipse.tcf.te.ui.notifications.notifications"; //$NON-NLS-1$
-
- private NotificationCategory category;
-
- private final ArrayList<String> defaultSinks;
-
- /**
- * Tests whether or not the event should per default be handled by the sink with the specified identifier.
- *
- * @param sinkId
- * the sink identifier
- * @return <code>true</code> if the
- */
- public boolean defaultHandledBySink(String sinkId) {
- if (defaultSinks.isEmpty() || defaultSinks.contains(sinkId)) {
- return true;
- }
- return false;
- }
-
- public NotificationEvent(IConfigurationElement element) {
- super(element);
- defaultSinks = new ArrayList<String>();
- IConfigurationElement[] children = element.getChildren("defaultHandler"); //$NON-NLS-1$
- for (IConfigurationElement child : children) {
- defaultSinks.add(child.getAttribute("sinkId")); //$NON-NLS-1$
- }
- doEventMappings();
- }
-
- private void doEventMappings() {
- IExtensionRegistry registry = Platform.getExtensionRegistry();
- IExtensionPoint point = registry.getExtensionPoint(EXTENSION_POINT_ID);
- if (point != null) {
- IConfigurationElement[] elements = point.getConfigurationElements();
- for (IConfigurationElement mapping : elements) {
- if (mapping.getName().equals("eventMapping")) { //$NON-NLS-1$
- String eventIds = mapping.getAttribute("eventIds"); //$NON-NLS-1$
- String[] list = eventIds.split(","); //$NON-NLS-1$
- for (String item : list) {
- if (wildCardMatch(getId(), item)) {
- defaultSinks.add(mapping.getAttribute("sinkId")); //$NON-NLS-1$
- }
- }
- }
- }
- }
- }
-
- private boolean wildCardMatch(String text, String pattern) {
- String[] cards = pattern.split("\\*"); //$NON-NLS-1$
- for (String card : cards) {
- int idx = text.indexOf(card);
- if (idx == -1) {
- return false;
- }
- text = text.substring(idx + card.length());
- }
-
- return true;
- }
-
- public NotificationCategory getCategory() {
- return category;
- }
-
- public String getCategoryId() {
- return element.getAttribute("categoryId"); //$NON-NLS-1$
- }
-
- public String getDescription() {
- IConfigurationElement[] children = element.getChildren("description"); //$NON-NLS-1$
- if (children.length > 0) {
- return children[0].getValue();
- }
- return ""; //$NON-NLS-1$
- }
-
- public void setCategory(NotificationCategory category) {
- this.category = category;
- }
-
-}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationHandler.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationHandler.java
deleted file mode 100644
index cd15cf446..000000000
--- a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationHandler.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2010, 2013 Tasktop Technologies 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:
- * Tasktop Technologies - initial API and implementation
- * Wind River Systems - Extracted from o.e.mylyn.commons and adapted for Target Explorer
- *******************************************************************************/
-
-package org.eclipse.tcf.te.ui.notifications.internal;
-
-import java.util.List;
-
-/**
- * Manages actions that are triggered when a {@link NotificationEvent} occurs.
- *
- * @author Steffen Pingel
- */
-public class NotificationHandler {
-
- private final List<NotificationAction> actions;
-
- private final NotificationEvent event;
-
- public NotificationHandler(NotificationEvent event, List<NotificationAction> actions) {
- this.event = event;
- this.actions = actions;
- }
-
- public List<NotificationAction> getActions() {
- return actions;
- }
-
- public NotificationEvent getEvent() {
- return event;
- }
-
-}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationModel.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationModel.java
deleted file mode 100644
index 6b4e7763a..000000000
--- a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationModel.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2010, 2013 Tasktop Technologies 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:
- * Tasktop Technologies - initial API and implementation
- * Itema AS - bug 330064 notification filtering and model persistence
- * Itema AS - bug 331424 handle default event-sink action associations
- * Wind River Systems - Extracted from o.e.mylyn.commons and adapted for Target Explorer
- *******************************************************************************/
-
-package org.eclipse.tcf.te.ui.notifications.internal;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @author Steffen Pingel
- * @author Torkild U. Resheim
- */
-public class NotificationModel {
-
- private Map<String, NotificationHandler> handlerByEventId;
-
- /**
- * Constructor
- */
- public NotificationModel() {
- this.handlerByEventId = new HashMap<String, NotificationHandler>();
- // We need the handlerByEventId map to be populated early
- for (NotificationCategory category : getCategories()) {
- for (NotificationEvent event : category.getEvents()) {
- getOrCreateNotificationHandler(event);
- }
- }
- }
-
- public Collection<NotificationCategory> getCategories() {
- return NotificationsExtensionReader.getCategories();
- }
-
- public NotificationHandler getNotificationHandler(String eventId) {
- return handlerByEventId.get(eventId);
- }
-
- public NotificationHandler getOrCreateNotificationHandler(NotificationEvent event) {
- NotificationHandler handler = getNotificationHandler(event.getId());
- if (handler == null) {
- handler = new NotificationHandler(event, getActions(event));
- handlerByEventId.put(event.getId(), handler);
- }
- return handler;
- }
-
- private List<NotificationAction> getActions(NotificationEvent event) {
- List<NotificationSinkDescriptor> descriptors = NotificationsExtensionReader.getSinks();
- List<NotificationAction> actions = new ArrayList<NotificationAction>(descriptors.size());
- for (NotificationSinkDescriptor descriptor : descriptors) {
- NotificationAction action = new NotificationAction(descriptor);
- if (event.defaultHandledBySink(descriptor.getId())) {
- action.setSelected(true);
- }
- actions.add(action);
- }
- return actions;
- }
-
- public boolean isSelected(NotificationEvent event) {
- NotificationHandler handler = getOrCreateNotificationHandler(event);
- for (NotificationAction action : handler.getActions()) {
- if (action.isSelected()) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationService.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationService.java
index 0f95e8f97..dfff769f6 100644
--- a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationService.java
+++ b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationService.java
@@ -1,100 +1,81 @@
/*******************************************************************************
- * Copyright (c) 2010, 2013 Tasktop Technologies 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
+ * Copyright (c) 2013 Wind River Systems, Inc. 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:
- * Tasktop Technologies - initial API and implementation
- * Itema AS - bug 330064 notification filtering and model persistence
- * Wind River Systems - Extracted from o.e.mylyn.commons and adapted for Target Explorer
+ * Wind River Systems - initial API and implementation
*******************************************************************************/
-
package org.eclipse.tcf.te.ui.notifications.internal;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map.Entry;
-
+import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
-import org.eclipse.tcf.te.runtime.notifications.AbstractNotification;
-import org.eclipse.tcf.te.runtime.notifications.NotificationSink;
-import org.eclipse.tcf.te.runtime.notifications.NotificationSinkEvent;
-import org.eclipse.tcf.te.runtime.notifications.interfaces.INotificationService;
-import org.eclipse.tcf.te.runtime.services.AbstractService;
+import org.eclipse.tcf.te.runtime.events.NotifyEvent;
import org.eclipse.tcf.te.ui.notifications.activator.UIPlugin;
-import org.eclipse.tcf.te.ui.notifications.preferences.IPreferenceKeys;
+import org.eclipse.tcf.te.ui.notifications.internal.popup.PopupNotificationSink;
/**
- * @author Steffen Pingel
- * @author Torkild U. Resheim
+ * Notification service implementation.
*/
-public class NotificationService extends AbstractService implements INotificationService {
+public class NotificationService {
+ // Reference to the popup notification sink we use exclusively
+ /* default */ final PopupNotificationSink sink = new PopupNotificationSink();
- /* (non-Javadoc)
- * @see org.eclipse.tcf.te.runtime.notifications.interfaces.INotificationService#notify(org.eclipse.tcf.te.runtime.notifications.AbstractNotification)
+ /*
+ * Thread save singleton instance creation.
*/
- @Override
- public void notify(AbstractNotification notification) {
- notify(new AbstractNotification[] { notification });
+ private static class LazyInstance {
+ public static NotificationService instance = new NotificationService();
}
- /* (non-Javadoc)
- * @see org.eclipse.tcf.te.runtime.notifications.interfaces.INotificationService#notify(org.eclipse.tcf.te.runtime.notifications.AbstractNotification[])
+ /**
+ * Constructor.
*/
- @Override
- public void notify(AbstractNotification[] notifications) {
- // Return if notifications are not globally enabled.
- if (!UIPlugin.getDefault().getPreferenceStore().getBoolean(IPreferenceKeys.PREF_SERVICE_ENABLED)) {
- return;
- }
+ NotificationService() {
+ super();
+ }
+
+ /**
+ * Returns the singleton instance of the notification service.
+ */
+ public static NotificationService getInstance() {
+ return LazyInstance.instance;
+ }
+
+ /**
+ * Shows a single notification.
+ *
+ * @param event The notification event. Must not be <code>null</code>.
+ */
+ public void notify(NotifyEvent event) {
+ Assert.isNotNull(event);
+ notify(new NotifyEvent[] { event });
+ }
+
+ /**
+ * Shows a set of notifications.
+ *
+ * @param events The notification events. Must not be <code>null</code>.
+ */
+ public void notify(final NotifyEvent[] events) {
+ Assert.isNotNull(events);
+
+ SafeRunner.run(new ISafeRunnable() {
+ @Override
+ public void run() throws Exception {
+ sink.notify(events);
+ }
- // For each sink assemble a list of notifications that are not blocked
- // and pass these along.
- HashMap<NotificationSink, ArrayList<AbstractNotification>> filtered = new HashMap<NotificationSink, ArrayList<AbstractNotification>>();
- for (AbstractNotification notification : notifications) {
- String id = notification.getEventId();
- NotificationHandler handler = UIPlugin.getDefault().getModel().getNotificationHandler(id);
- if (handler != null) {
- List<NotificationAction> actions = handler.getActions();
- for (NotificationAction action : actions) {
- if (action.isSelected()) {
- NotificationSink sink = action.getSinkDescriptor().getSink();
- if (sink != null) {
- ArrayList<AbstractNotification> list = filtered.get(sink);
- if (list == null) {
- list = new ArrayList<AbstractNotification>();
- filtered.put(sink, list);
- }
- list.add(notification);
- }
- }
- }
+ @Override
+ public void handleException(Throwable e) {
+ UIPlugin.getDefault().getLog().log(new Status(IStatus.WARNING, UIPlugin.getUniqueIdentifier(), "Sink failed: " + sink.getClass(), e)); //$NON-NLS-1$
}
- }
- // Go through all the sinks that have notifications to display and let
- // them do their job.
- for (Entry<NotificationSink, ArrayList<AbstractNotification>> entry : filtered.entrySet()) {
- final NotificationSink sink = entry.getKey();
- final NotificationSinkEvent event = new NotificationSinkEvent(new ArrayList<AbstractNotification>(
- entry.getValue()));
- SafeRunner.run(new ISafeRunnable() {
- @Override
- public void handleException(Throwable e) {
- UIPlugin.getDefault().getLog().log(new Status(IStatus.WARNING, UIPlugin.getUniqueIdentifier(), "Sink failed: " + sink.getClass(), e)); //$NON-NLS-1$
- }
- @Override
- public void run() throws Exception {
- sink.notify(event);
- }
- });
- }
+ });
}
}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationSinkDescriptor.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationSinkDescriptor.java
deleted file mode 100644
index 55de73fb2..000000000
--- a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationSinkDescriptor.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2010, 2013 Tasktop Technologies 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:
- * Tasktop Technologies - initial API and implementation
- * Wind River Systems - Extracted from o.e.mylyn.commons and adapted for Target Explorer
- *******************************************************************************/
-
-package org.eclipse.tcf.te.ui.notifications.internal;
-
-import org.eclipse.core.runtime.IConfigurationElement;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
-import org.eclipse.osgi.util.NLS;
-import org.eclipse.tcf.te.runtime.notifications.NotificationSink;
-import org.eclipse.tcf.te.ui.notifications.activator.UIPlugin;
-import org.eclipse.ui.statushandlers.StatusManager;
-
-/**
- * @author Steffen Pingel
- */
-public class NotificationSinkDescriptor extends NotificationElement {
-
- private NotificationSink sink;
-
- private Status status;
-
- public NotificationSinkDescriptor(IConfigurationElement element) {
- super(element);
- }
-
- public NotificationSink getSink() {
- if (sink != null || status != null) {
- return sink;
- }
-
- try {
- Object object = element.createExecutableExtension("class"); //$NON-NLS-1$
- if (object instanceof NotificationSink) {
- sink = (NotificationSink) object;
- return sink;
- }
-
- status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(), NLS.bind("Sink ''{0}'' does not extend expected class for extension contributed by {1}", //$NON-NLS-1$
- object.getClass().getCanonicalName(), getPluginId()));
- }
- catch (Throwable e) {
- status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(), NLS.bind("Sink failed to load for extension contributed by {0}", getPluginId()), e); //$NON-NLS-1$
- }
-
- StatusManager.getManager().handle(status);
- return null;
- }
-
-}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationsExtensionReader.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationsExtensionReader.java
deleted file mode 100644
index a5e728c4a..000000000
--- a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/NotificationsExtensionReader.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2010, 2013 Tasktop Technologies 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:
- * Tasktop Technologies - initial API and implementation
- * Itema AS - bug 330064 notification filtering and model persistence
- * Wind River Systems - Extracted from o.e.mylyn.commons and adapted for Target Explorer
- *******************************************************************************/
-
-package org.eclipse.tcf.te.ui.notifications.internal;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-
-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.IStatus;
-import org.eclipse.core.runtime.MultiStatus;
-import org.eclipse.core.runtime.Platform;
-import org.eclipse.core.runtime.Status;
-import org.eclipse.osgi.util.NLS;
-import org.eclipse.tcf.te.ui.notifications.activator.UIPlugin;
-import org.eclipse.ui.statushandlers.StatusManager;
-
-/**
- * @author Steffen Pingel
- * @author Torkild U. Resheim
- */
-public class NotificationsExtensionReader {
-
- private static boolean errorLogged = false;
-
- static List<NotificationSinkDescriptor> sinks;
-
- private static Collection<NotificationCategory> categories;
-
- /**
- * Returns a list of notification categories, each containing their belonging notification
- * events. Once initialised the same list will be returned upon subsequent calls of this method.
- *
- * @return a list of notification categories.
- * @see NotificationModel#save(org.eclipse.ui.IMemento)
- * @see NotificationModel#load(org.eclipse.ui.IMemento)
- */
- public static Collection<NotificationCategory> getCategories() {
- if (categories != null) {
- return categories;
- }
- HashMap<String, NotificationCategory> categoryById = new HashMap<String, NotificationCategory>();
-
- MultiStatus result = new MultiStatus(UIPlugin.getUniqueIdentifier(), 0, "Notifcation extensions failed to load", null); //$NON-NLS-1$
-
- IExtensionRegistry registry = Platform.getExtensionRegistry();
- IExtensionPoint repositoriesExtensionPoint = registry.getExtensionPoint(UIPlugin.getUniqueIdentifier() + ".notifications"); //$NON-NLS-1$
- IExtension[] extensions = repositoriesExtensionPoint.getExtensions();
- for (IExtension extension : extensions) {
- IConfigurationElement[] elements = extension.getConfigurationElements();
- for (IConfigurationElement element : elements) {
- if ("category".equals(element.getName())) { //$NON-NLS-1$
- NotificationCategory category = new NotificationCategory(element);
- IStatus status = category.validate();
- if (status.isOK()) {
- categoryById.put(category.getId(), category);
- }
- else {
- result.add(status);
- }
- }
- }
- for (IConfigurationElement element : elements) {
- if ("event".equals(element.getName())) { //$NON-NLS-1$
- NotificationEvent event = new NotificationEvent(element);
- IStatus status = event.validate();
- if (status.isOK()) {
- NotificationCategory category = categoryById.get(event.getCategoryId());
- if (category != null) {
- category.addEvent(event);
- }
- else {
- result.add(new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(),
- NLS.bind("Extension {0} contributed by {1} specify unknown category ''{2}''", new String[] { element.getNamespaceIdentifier(), element.getContributor().getName(), event.getCategoryId() }))); //NON-NLS-1$ //$NON-NLS-1$
- }
- }
- else {
- result.add(status);
- }
- }
- }
- }
-
- if (!result.isOK() && !errorLogged) {
- StatusManager.getManager().handle(result);
- errorLogged = true;
- }
-
- categories = categoryById.values();
- return categories;
- }
-
- public static List<NotificationSinkDescriptor> getSinks() {
- if (sinks != null) {
- return sinks;
- }
-
- sinks = new ArrayList<NotificationSinkDescriptor>();
-
- MultiStatus result = new MultiStatus(UIPlugin.getUniqueIdentifier(), 0, "Notifcation extensions failed to load", null); //$NON-NLS-1$
-
- IExtensionRegistry registry = Platform.getExtensionRegistry();
- IExtensionPoint repositoriesExtensionPoint = registry.getExtensionPoint(UIPlugin.getUniqueIdentifier() + ".notifications"); //$NON-NLS-1$
- IExtension[] extensions = repositoriesExtensionPoint.getExtensions();
- for (IExtension extension : extensions) {
- IConfigurationElement[] elements = extension.getConfigurationElements();
- for (IConfigurationElement element : elements) {
- if ("sink".equals(element.getName())) { //$NON-NLS-1$
- NotificationSinkDescriptor descriptor = new NotificationSinkDescriptor(element);
- IStatus status = descriptor.validate();
- if (status.isOK()) {
- sinks.add(descriptor);
- }
- else {
- result.add(status);
- }
- }
- }
- }
-
- if (!result.isOK()) {
- StatusManager.getManager().handle(result);
- }
-
- return sinks;
- }
-}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/events/EventListener.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/events/EventListener.java
new file mode 100644
index 000000000..18211fb41
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/events/EventListener.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Wind River Systems, Inc. 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.notifications.internal.events;
+
+import java.util.EventObject;
+
+import org.eclipse.tcf.te.runtime.events.NotifyEvent;
+import org.eclipse.tcf.te.runtime.interfaces.events.IEventListener;
+import org.eclipse.tcf.te.ui.notifications.internal.NotificationService;
+
+/**
+ * Event listener implementation. Handle events of type {@link NotifyEvent}.
+ */
+public class EventListener implements IEventListener {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.interfaces.events.IEventListener#eventFired(java.util.EventObject)
+ */
+ @Override
+ public void eventFired(EventObject event) {
+ if (event instanceof NotifyEvent) {
+ NotificationService.getInstance().notify((NotifyEvent)event);
+ }
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/factory/DefaultFormTextFactoryDelegate.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/factory/DefaultFormTextFactoryDelegate.java
new file mode 100644
index 000000000..f80c6c8b5
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/factory/DefaultFormTextFactoryDelegate.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Wind River Systems, Inc. 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.notifications.internal.factory;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.tcf.te.runtime.events.NotifyEvent;
+import org.eclipse.tcf.te.ui.notifications.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.notifications.interfaces.IFormTextFactoryDelegate;
+import org.eclipse.ui.forms.IFormColors;
+import org.eclipse.ui.forms.widgets.FormText;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+/**
+ * Default notification form text factory delegate implementation.
+ */
+public class DefaultFormTextFactoryDelegate implements IFormTextFactoryDelegate {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.notifications.interfaces.IFormTextFactoryDelegate#populateFormText(org.eclipse.ui.forms.widgets.FormToolkit, org.eclipse.ui.forms.widgets.FormText, org.eclipse.tcf.te.runtime.events.NotifyEvent)
+ */
+ @Override
+ public void populateFormText(FormToolkit toolkit, FormText widget, NotifyEvent event) {
+ Assert.isNotNull(toolkit);
+ Assert.isNotNull(widget);
+ Assert.isNotNull(event);
+
+ // Get properties
+ String titleText = event.getProperties().getStringProperty(NotifyEvent.PROP_TITLE_TEXT);
+ String titleImageId = event.getProperties().getStringProperty(NotifyEvent.PROP_TITLE_IMAGE_ID);
+ String description = event.getProperties().getStringProperty(NotifyEvent.PROP_DESCRIPTION_TEXT);
+
+ // At least the title text and the description must be not null
+ if (titleText != null && description != null) {
+ StringBuilder buffer = new StringBuilder();
+
+ buffer.append("<form>"); //$NON-NLS-1$
+
+ // If the title image id is set, try to load the image
+ if (titleImageId != null) {
+ Image image = UIPlugin.getImage(titleImageId);
+ if (image != null) {
+ buffer.append("<img href=\"titleImage\"> "); //$NON-NLS-1$
+ widget.setImage("titleImage", image); //$NON-NLS-1$
+ }
+ }
+
+ // Set the title using the default header font
+ buffer.append("<span color=\"header\" font=\"header\">"); //$NON-NLS-1$
+ buffer.append(titleText);
+ buffer.append("</span>"); //$NON-NLS-1$
+
+ // Add the description
+ buffer.append("<p>"); //$NON-NLS-1$
+ buffer.append(description);
+ buffer.append("</p>"); //$NON-NLS-1$
+
+ buffer.append("</form>"); //$NON-NLS-1$
+
+ widget.setColor("header", toolkit.getColors().getColor(IFormColors.TITLE)); //$NON-NLS-1$
+ widget.setFont("header", JFaceResources.getHeaderFont()); //$NON-NLS-1$
+
+ widget.setText(buffer.toString(), true, false);
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/factory/FactoryDelegateManager.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/factory/FactoryDelegateManager.java
new file mode 100644
index 000000000..fc832f2a1
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/factory/FactoryDelegateManager.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Wind River Systems, Inc. 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.ui.notifications.internal.factory;
+
+import java.util.Collection;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.tcf.te.runtime.extensions.AbstractExtensionPointManager;
+import org.eclipse.tcf.te.runtime.extensions.ExecutableExtensionProxy;
+import org.eclipse.tcf.te.ui.notifications.interfaces.IFormTextFactoryDelegate;
+
+
+/**
+ * Notification form text factory delegate extension point manager implementation.
+ */
+public class FactoryDelegateManager extends AbstractExtensionPointManager<IFormTextFactoryDelegate> {
+ private final IFormTextFactoryDelegate defaultDelegate = new DefaultFormTextFactoryDelegate();
+
+ /*
+ * Thread save singleton instance creation.
+ */
+ private static class LazyInstance {
+ public static FactoryDelegateManager instance = new FactoryDelegateManager();
+ }
+
+ /**
+ * Constructor.
+ */
+ FactoryDelegateManager() {
+ super();
+ }
+
+ /**
+ * Returns the singleton instance of the notification form text factory delegate manager.
+ */
+ public static FactoryDelegateManager getInstance() {
+ return LazyInstance.instance;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.extensions.AbstractExtensionPointManager#getExtensionPointId()
+ */
+ @Override
+ protected String getExtensionPointId() {
+ return "org.eclipse.tcf.te.ui.notifications.factoryDelegates"; //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.extensions.AbstractExtensionPointManager#getConfigurationElementName()
+ */
+ @Override
+ protected String getConfigurationElementName() {
+ return "delegate"; //$NON-NLS-1$
+ }
+
+ /**
+ * Returns the notification form text factory delegate matching the given id.
+ *
+ * @param id The notification form text factory delegate id. Must not be <code>null</code>.
+ * @return The notification form text factory delegate or <code>null</code>.
+ */
+ public IFormTextFactoryDelegate getFactoryDelegate(String id) {
+ Assert.isNotNull(id);
+
+ IFormTextFactoryDelegate delegate = null;
+
+ Collection<ExecutableExtensionProxy<IFormTextFactoryDelegate>> delegates = getExtensions().values();
+ for (ExecutableExtensionProxy<IFormTextFactoryDelegate> candidate : delegates) {
+ if (id.equals(candidate.getId())) {
+ delegate = candidate.getInstance();
+ break;
+ }
+ }
+
+ return delegate;
+ }
+
+ /**
+ * Returns the default notification form text factory delegate.
+ *
+ * @return The default notification form text factory delegate.
+ */
+ public IFormTextFactoryDelegate getDefaultFactoryDelegate() {
+ return defaultDelegate;
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/AbstractNotificationPopup.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/AbstractNotificationPopup.java
new file mode 100644
index 000000000..1c3596a64
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/AbstractNotificationPopup.java
@@ -0,0 +1,615 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2013 Tasktop Technologies 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:
+ * Benjamin Pasero - initial API and implementation
+ * Tasktop Technologies - initial API and implementation
+ * Wind River Systems - Extracted from o.e.mylyn.commons and adapted for Target Explorer
+ *******************************************************************************/
+
+package org.eclipse.tcf.te.ui.notifications.internal.popup;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.LocalResourceManager;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseTrackAdapter;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.Region;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Monitor;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tcf.te.ui.notifications.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.notifications.interfaces.ImageConsts;
+import org.eclipse.tcf.te.ui.notifications.internal.popup.AnimationUtil.FadeJob;
+import org.eclipse.tcf.te.ui.notifications.internal.popup.AnimationUtil.IFadeListener;
+import org.eclipse.tcf.te.ui.notifications.nls.Messages;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * A popup window with a title bar and message area for displaying notifications.
+ *
+ * @author Benjamin Pasero
+ * @author Mik Kersten
+ * @author Steffen Pingel
+ * @since 3.7
+ */
+public abstract class AbstractNotificationPopup extends Window {
+
+ private static final int TITLE_HEIGHT = 24;
+
+ private static final String LABEL_NOTIFICATION = Messages.AbstractNotificationPopup_Notification;
+
+ private static final String LABEL_JOB_CLOSE = Messages.AbstractNotificationPopup_Close_Notification_Job;
+
+ private static final int MAX_WIDTH = 400;
+
+ private static final int MIN_HEIGHT = 100;
+
+ private static final long DEFAULT_DELAY_CLOSE = 8 * 1000;
+
+ private static final int PADDING_EDGE = 5;
+
+ private long delayClose = DEFAULT_DELAY_CLOSE;
+
+ protected LocalResourceManager resources;
+
+ /* default */ GradientColors color;
+
+ /* default */ final Display display;
+
+ private Shell shell;
+
+ private Region lastUsedRegion;
+
+ /* default */ Image lastUsedBgImage;
+
+ private final Job closeJob = new Job(LABEL_JOB_CLOSE) {
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ if (!display.isDisposed()) {
+ display.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ Shell shell = AbstractNotificationPopup.this.getShell();
+ if (shell == null || shell.isDisposed()) {
+ return;
+ }
+
+ if (isMouseOver(shell)) {
+ scheduleAutoClose();
+ return;
+ }
+
+ AbstractNotificationPopup.this.closeFade();
+ }
+
+ });
+ }
+ if (monitor.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
+
+ return Status.OK_STATUS;
+ }
+ };
+
+ private final boolean respectDisplayBounds = true;
+
+ private final boolean respectMonitorBounds = true;
+
+ /* default */ FadeJob fadeJob;
+
+ private boolean fadingEnabled;
+
+ public AbstractNotificationPopup(Display display) {
+ this(display, SWT.NO_TRIM | SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
+ }
+
+ public AbstractNotificationPopup(Display display, int style) {
+ super(new Shell(display));
+ setShellStyle(style);
+
+ this.display = display;
+ resources = new LocalResourceManager(JFaceResources.getResources());
+ initResources();
+
+ closeJob.setSystem(true);
+ }
+
+ public boolean isFadingEnabled() {
+ return fadingEnabled;
+ }
+
+ public void setFadingEnabled(boolean fadingEnabled) {
+ this.fadingEnabled = fadingEnabled;
+ }
+
+ /**
+ * Override to return a customized name.
+ *
+ * @return The name to be used in the title of the popup.
+ */
+ protected String getPopupShellTitle() {
+ return LABEL_NOTIFICATION;
+ }
+
+ /**
+ * Override to return a customized image. Defaults to the workbench window image.
+ *
+ * @param maximumHeight The maximum height of the image.
+ * @return The image or <code>null</code>.
+ */
+ protected Image getPopupShellImage(int maximumHeight) {
+ // always use the launching workbench window
+ IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
+ if (windows != null && windows.length > 0) {
+ IWorkbenchWindow workbenchWindow = windows[0];
+ if (workbenchWindow != null && !workbenchWindow.getShell().isDisposed()) {
+ Image image = workbenchWindow.getShell().getImage();
+ int diff = Integer.MAX_VALUE;
+ if (image != null && image.getBounds().height <= maximumHeight) {
+ diff = maximumHeight - image.getBounds().height;
+ } else {
+ image = null;
+ }
+
+ Image[] images = workbenchWindow.getShell().getImages();
+ if (images != null && images.length > 0) {
+ // find the icon that is closest in size, but not larger than maximumHeight
+ for (Image image2 : images) {
+ int newDiff = maximumHeight - image2.getBounds().height;
+ if (newDiff >= 0 && newDiff <= diff) {
+ diff = newDiff;
+ image = image2;
+ }
+ }
+ }
+
+ return image;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Override to populate with notifications.
+ *
+ * @param parent
+ */
+ protected void createContentArea(Composite parent) {
+ // empty by default
+ }
+
+ /**
+ * Override to customize the title bar
+ */
+ protected void createTitleArea(Composite parent) {
+ ((GridData) parent.getLayoutData()).heightHint = TITLE_HEIGHT;
+
+ Label titleImageLabel = new Label(parent, SWT.NONE);
+ titleImageLabel.setImage(getPopupShellImage(TITLE_HEIGHT));
+
+ Label titleTextLabel = new Label(parent, SWT.NONE);
+ titleTextLabel.setText(getPopupShellTitle());
+ titleTextLabel.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DEFAULT_FONT));
+ titleTextLabel.setForeground(getTitleForeground());
+ titleTextLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
+ titleTextLabel.setCursor(parent.getDisplay().getSystemCursor(SWT.CURSOR_HAND));
+
+ final Label button = new Label(parent, SWT.NONE);
+ button.setImage(UIPlugin.getImage(ImageConsts.NOTIFICATION_CLOSE));
+ button.addMouseTrackListener(new MouseTrackAdapter() {
+ @Override
+ public void mouseEnter(MouseEvent e) {
+ button.setImage(UIPlugin.getImage(ImageConsts.NOTIFICATION_CLOSE_HOVER));
+ }
+
+ @Override
+ public void mouseExit(MouseEvent e) {
+ button.setImage(UIPlugin.getImage(ImageConsts.NOTIFICATION_CLOSE));
+ }
+ });
+ button.addMouseListener(new MouseAdapter() {
+
+ @SuppressWarnings("synthetic-access")
+ @Override
+ public void mouseUp(MouseEvent e) {
+ close();
+ setReturnCode(CANCEL);
+ }
+
+ });
+ }
+
+ protected Color getTitleForeground() {
+ return color.getTitleText();
+ }
+
+ private void initResources() {
+ color = new GradientColors(display, resources);
+ }
+
+ @Override
+ protected void configureShell(Shell newShell) {
+ super.configureShell(newShell);
+
+ shell = newShell;
+ newShell.setBackground(color.getBorder());
+ }
+
+ @Override
+ public void create() {
+ super.create();
+ addRegion(shell);
+ }
+
+ private void addRegion(Shell shell) {
+ Region region = new Region();
+ Point s = shell.getSize();
+
+ /* Add entire Shell */
+ region.add(0, 0, s.x, s.y);
+
+ /* Subtract Top-Left Corner */
+ region.subtract(0, 0, 5, 1);
+ region.subtract(0, 1, 3, 1);
+ region.subtract(0, 2, 2, 1);
+ region.subtract(0, 3, 1, 1);
+ region.subtract(0, 4, 1, 1);
+
+ /* Subtract Top-Right Corner */
+ region.subtract(s.x - 5, 0, 5, 1);
+ region.subtract(s.x - 3, 1, 3, 1);
+ region.subtract(s.x - 2, 2, 2, 1);
+ region.subtract(s.x - 1, 3, 1, 1);
+ region.subtract(s.x - 1, 4, 1, 1);
+
+ /* Subtract Bottom-Left Corner */
+ region.subtract(0, s.y, 5, 1);
+ region.subtract(0, s.y - 1, 3, 1);
+ region.subtract(0, s.y - 2, 2, 1);
+ region.subtract(0, s.y - 3, 1, 1);
+ region.subtract(0, s.y - 4, 1, 1);
+
+ /* Subtract Bottom-Right Corner */
+ region.subtract(s.x - 5, s.y - 0, 5, 1);
+ region.subtract(s.x - 3, s.y - 1, 3, 1);
+ region.subtract(s.x - 2, s.y - 2, 2, 1);
+ region.subtract(s.x - 1, s.y - 3, 1, 1);
+ region.subtract(s.x - 1, s.y - 4, 1, 1);
+
+ /* Dispose old first */
+ if (shell.getRegion() != null) {
+ shell.getRegion().dispose();
+ }
+
+ /* Apply Region */
+ shell.setRegion(region);
+
+ /* Remember to dispose later */
+ lastUsedRegion = region;
+ }
+
+ /* default */ boolean isMouseOver(Shell shell) {
+ if (display.isDisposed()) {
+ return false;
+ }
+ return shell.getBounds().contains(display.getCursorLocation());
+ }
+
+ @Override
+ public int open() {
+ if (shell == null || shell.isDisposed()) {
+ shell = null;
+ create();
+ }
+
+ constrainShellSize();
+ shell.setLocation(fixupDisplayBounds(shell.getSize(), shell.getLocation()));
+
+ if (isFadingEnabled()) {
+ shell.setAlpha(0);
+ }
+ shell.setVisible(true);
+ fadeJob = AnimationUtil.fadeIn(shell, new IFadeListener() {
+ @Override
+ public void faded(Shell shell, int alpha) {
+ if (shell.isDisposed()) {
+ return;
+ }
+
+ if (alpha == 255) {
+ scheduleAutoClose();
+ }
+ }
+ });
+
+ return Window.OK;
+ }
+
+ protected void scheduleAutoClose() {
+ if (delayClose > 0) {
+ closeJob.schedule(delayClose);
+ }
+ }
+
+ @Override
+ protected Control createContents(Composite parent) {
+ ((GridLayout) parent.getLayout()).marginWidth = 1;
+ ((GridLayout) parent.getLayout()).marginHeight = 1;
+
+ /* Outer Composite holding the controls */
+ final Composite outerCircle = new Composite(parent, SWT.NO_FOCUS);
+ outerCircle.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ outerCircle.setBackgroundMode(SWT.INHERIT_FORCE);
+
+ outerCircle.addControlListener(new ControlAdapter() {
+
+ @Override
+ public void controlResized(ControlEvent e) {
+ Rectangle clArea = outerCircle.getClientArea();
+ lastUsedBgImage = new Image(outerCircle.getDisplay(), clArea.width, clArea.height);
+ GC gc = new GC(lastUsedBgImage);
+
+ /* Gradient */
+ drawGradient(gc, clArea);
+
+ /* Fix Region Shape */
+ fixRegion(gc, clArea);
+
+ gc.dispose();
+
+ Image oldBGImage = outerCircle.getBackgroundImage();
+ outerCircle.setBackgroundImage(lastUsedBgImage);
+
+ if (oldBGImage != null) {
+ oldBGImage.dispose();
+ }
+ }
+
+ private void drawGradient(GC gc, Rectangle clArea) {
+ gc.setForeground(color.getGradientBegin());
+ gc.setBackground(color.getGradientEnd());
+ gc.fillGradientRectangle(clArea.x, clArea.y, clArea.width, clArea.height, true);
+ }
+
+ private void fixRegion(GC gc, Rectangle clArea) {
+ gc.setForeground(color.getBorder());
+
+ /* Fill Top Left */
+ gc.drawPoint(2, 0);
+ gc.drawPoint(3, 0);
+ gc.drawPoint(1, 1);
+ gc.drawPoint(0, 2);
+ gc.drawPoint(0, 3);
+
+ /* Fill Top Right */
+ gc.drawPoint(clArea.width - 4, 0);
+ gc.drawPoint(clArea.width - 3, 0);
+ gc.drawPoint(clArea.width - 2, 1);
+ gc.drawPoint(clArea.width - 1, 2);
+ gc.drawPoint(clArea.width - 1, 3);
+
+ /* Fill Bottom Left */
+ gc.drawPoint(2, clArea.height - 0);
+ gc.drawPoint(3, clArea.height - 0);
+ gc.drawPoint(1, clArea.height - 1);
+ gc.drawPoint(0, clArea.height - 2);
+ gc.drawPoint(0, clArea.height - 3);
+
+ /* Fill Bottom Right */
+ gc.drawPoint(clArea.width - 4, clArea.height - 0);
+ gc.drawPoint(clArea.width - 3, clArea.height - 0);
+ gc.drawPoint(clArea.width - 2, clArea.height - 1);
+ gc.drawPoint(clArea.width - 1, clArea.height - 2);
+ gc.drawPoint(clArea.width - 1, clArea.height - 3);
+ }
+ });
+
+ GridLayout layout = new GridLayout(1, false);
+ layout.marginWidth = 0;
+ layout.marginHeight = 0;
+ layout.verticalSpacing = 0;
+
+ outerCircle.setLayout(layout);
+
+ /* Title area containing label and close button */
+ final Composite titleCircle = new Composite(outerCircle, SWT.NO_FOCUS);
+ titleCircle.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ titleCircle.setBackgroundMode(SWT.INHERIT_FORCE);
+
+ layout = new GridLayout(4, false);
+ layout.marginWidth = 3;
+ layout.marginHeight = 0;
+ layout.verticalSpacing = 5;
+ layout.horizontalSpacing = 3;
+
+ titleCircle.setLayout(layout);
+
+ /* Create Title Area */
+ createTitleArea(titleCircle);
+
+ /* Outer composite to hold content controlls */
+ Composite outerContentCircle = new Composite(outerCircle, SWT.NONE);
+ outerContentCircle.setBackgroundMode(SWT.INHERIT_FORCE);
+
+ layout = new GridLayout(1, false);
+ layout.marginWidth = 0;
+ layout.marginHeight = 0;
+
+ outerContentCircle.setLayout(layout);
+ outerContentCircle.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ outerContentCircle.setBackground(outerCircle.getBackground());
+
+ /* Middle composite to show a 1px black line around the content controls */
+ Composite middleContentCircle = new Composite(outerContentCircle, SWT.NO_FOCUS);
+ middleContentCircle.setBackgroundMode(SWT.INHERIT_FORCE);
+
+ layout = new GridLayout(1, false);
+ layout.marginWidth = 0;
+ layout.marginHeight = 0;
+ layout.marginTop = 1;
+
+ middleContentCircle.setLayout(layout);
+ middleContentCircle.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ middleContentCircle.setBackground(color.getBorder());
+
+ /* Inner composite containing the content controls */
+ Composite innerContent = new Composite(middleContentCircle, SWT.NO_FOCUS);
+ innerContent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ innerContent.setBackgroundMode(SWT.INHERIT_FORCE);
+
+ layout = new GridLayout(1, false);
+ layout.marginWidth = 0;
+ layout.marginHeight = 5;
+ layout.marginLeft = 5;
+ layout.marginRight = 5;
+ innerContent.setLayout(layout);
+
+ innerContent.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_WHITE));
+
+ /* Content Area */
+ createContentArea(innerContent);
+
+ setNullBackground(outerCircle);
+
+ return outerCircle;
+ }
+
+ private void setNullBackground(final Composite outerCircle) {
+ for (Control c : outerCircle.getChildren()) {
+ c.setBackground(null);
+ if (c instanceof Composite) {
+ setNullBackground((Composite) c);
+ }
+ }
+ }
+
+ @Override
+ protected void initializeBounds() {
+ Rectangle clArea = getPrimaryClientArea();
+ Point initialSize = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ int height = Math.max(initialSize.y, MIN_HEIGHT);
+ int width = Math.min(initialSize.x, MAX_WIDTH);
+
+ Point size = new Point(width, height);
+ shell.setLocation(clArea.width + clArea.x - size.x - PADDING_EDGE, clArea.height + clArea.y - size.y
+ - PADDING_EDGE);
+ shell.setSize(size);
+ }
+
+ private Rectangle getPrimaryClientArea() {
+ Monitor primaryMonitor = shell.getDisplay().getPrimaryMonitor();
+ return (primaryMonitor != null) ? primaryMonitor.getClientArea() : shell.getDisplay().getClientArea();
+ }
+
+ public void closeFade() {
+ if (fadeJob != null) {
+ fadeJob.cancelAndWait(false);
+ }
+ fadeJob = AnimationUtil.fadeOut(getShell(), new IFadeListener() {
+ @Override
+ public void faded(Shell shell, int alpha) {
+ if (!shell.isDisposed()) {
+ if (alpha == 0) {
+ shell.close();
+ } else if (isMouseOver(shell)) {
+ if (fadeJob != null) {
+ fadeJob.cancelAndWait(false);
+ }
+ fadeJob = AnimationUtil.fastFadeIn(shell, new IFadeListener() {
+ @Override
+ public void faded(Shell shell, int alpha) {
+ if (shell.isDisposed()) {
+ return;
+ }
+
+ if (alpha == 255) {
+ scheduleAutoClose();
+ }
+ }
+ });
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public boolean close() {
+ resources.dispose();
+ if (lastUsedRegion != null) {
+ lastUsedRegion.dispose();
+ }
+ if (lastUsedBgImage != null && !lastUsedBgImage.isDisposed()) {
+ lastUsedBgImage.dispose();
+ }
+ return super.close();
+ }
+
+ public long getDelayClose() {
+ return delayClose;
+ }
+
+ public void setDelayClose(long delayClose) {
+ this.delayClose = delayClose;
+ }
+
+ private Point fixupDisplayBounds(Point tipSize, Point location) {
+ if (respectDisplayBounds) {
+ Rectangle bounds;
+ Point rightBounds = new Point(tipSize.x + location.x, tipSize.y + location.y);
+
+ if (respectMonitorBounds) {
+ bounds = shell.getDisplay().getPrimaryMonitor().getBounds();
+ } else {
+ bounds = getPrimaryClientArea();
+ }
+
+ if (!(bounds.contains(location) && bounds.contains(rightBounds))) {
+ if (rightBounds.x > bounds.x + bounds.width) {
+ location.x -= rightBounds.x - (bounds.x + bounds.width);
+ }
+
+ if (rightBounds.y > bounds.y + bounds.height) {
+ location.y -= rightBounds.y - (bounds.y + bounds.height);
+ }
+
+ if (location.x < bounds.x) {
+ location.x = bounds.x;
+ }
+
+ if (location.y < bounds.y) {
+ location.y = bounds.y;
+ }
+ }
+ }
+
+ return location;
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/AnimationUtil.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/AnimationUtil.java
new file mode 100644
index 000000000..cffee0f20
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/AnimationUtil.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2013 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ * Wind River Systems - Extracted from o.e.mylyn.commons and adapted for Target Explorer
+ *******************************************************************************/
+
+package org.eclipse.tcf.te.ui.notifications.internal.popup;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tcf.te.ui.notifications.nls.Messages;
+
+/**
+ * @author Mik Kersten
+ * @author Steffen Pingel
+ */
+public class AnimationUtil {
+
+ public static final long FADE_RESCHEDULE_DELAY = 80;
+
+ public static final int FADE_IN_INCREMENT = 15;
+
+ public static final int FADE_OUT_INCREMENT = -20;
+
+ public static FadeJob fastFadeIn(Shell shell, IFadeListener listener) {
+ return new FadeJob(shell, 2 * FADE_IN_INCREMENT, FADE_RESCHEDULE_DELAY, listener);
+ }
+
+ public static FadeJob fadeIn(Shell shell, IFadeListener listener) {
+ return new FadeJob(shell, FADE_IN_INCREMENT, FADE_RESCHEDULE_DELAY, listener);
+ }
+
+ public static FadeJob fadeOut(Shell shell, IFadeListener listener) {
+ return new FadeJob(shell, FADE_OUT_INCREMENT, FADE_RESCHEDULE_DELAY, listener);
+ }
+
+ public static class FadeJob extends Job {
+
+ /* default */ final Shell shell;
+
+ private final int increment;
+
+ /* default */ volatile boolean stopped;
+
+ /* default */ volatile int currentAlpha;
+
+ private final long delay;
+
+ /* default */ final IFadeListener fadeListener;
+
+ public FadeJob(Shell shell, int increment, long delay, IFadeListener fadeListener) {
+ super(Messages.SwtUtil_Fading);
+ if (increment < -255 || increment == 0 || increment > 255) {
+ throw new IllegalArgumentException("-255 <= increment <= 255 && increment != 0"); //$NON-NLS-1$
+ }
+ if (delay < 1) {
+ throw new IllegalArgumentException("delay must be > 0"); //$NON-NLS-1$
+ }
+ this.currentAlpha = shell.getAlpha();
+ this.shell = shell;
+ this.increment = increment;
+ this.delay = delay;
+ this.fadeListener = fadeListener;
+
+ setSystem(true);
+ schedule(delay);
+ }
+
+ @Override
+ protected void canceling() {
+ stopped = true;
+ }
+
+ private void reschedule() {
+ if (stopped) {
+ return;
+ }
+ schedule(delay);
+ }
+
+ public void cancelAndWait(final boolean setAlpha) {
+ if (stopped) {
+ return;
+ }
+ cancel();
+ Display.getDefault().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ if (setAlpha) {
+ shell.setAlpha(getLastAlpha());
+ }
+ }
+ });
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ if (stopped) {
+ return Status.OK_STATUS;
+ }
+
+ currentAlpha += increment;
+ if (currentAlpha <= 0) {
+ currentAlpha = 0;
+ } else if (currentAlpha >= 255) {
+ currentAlpha = 255;
+ }
+
+ Display.getDefault().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ if (stopped) {
+ return;
+ }
+
+ if (shell.isDisposed()) {
+ stopped = true;
+ return;
+ }
+
+ shell.setAlpha(currentAlpha);
+
+ if (fadeListener != null) {
+ fadeListener.faded(shell, currentAlpha);
+ }
+ }
+ });
+
+ if (currentAlpha == 0 || currentAlpha == 255) {
+ stopped = true;
+ }
+
+ reschedule();
+ return Status.OK_STATUS;
+ }
+
+ /* default */ int getLastAlpha() {
+ return (increment < 0) ? 0 : 255;
+ }
+
+ }
+
+ public static interface IFadeListener {
+
+ public void faded(Shell shell, int alpha);
+
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/GradientColors.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/GradientColors.java
new file mode 100644
index 000000000..a50425f87
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/GradientColors.java
@@ -0,0 +1,187 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2013 Tasktop Technologies 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:
+ * Benjamin Pasero - initial API and implementation
+ * Tasktop Technologies - improvements
+ * Wind River Systems - Extracted from o.e.mylyn.commons and adapted for Target Explorer
+ *******************************************************************************/
+
+package org.eclipse.tcf.te.ui.notifications.internal.popup;
+
+import org.eclipse.jface.resource.DeviceResourceException;
+import org.eclipse.jface.resource.ResourceManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Based on FormColors of UI Forms.
+ *
+ * @author Benjamin Pasero (initial contribution from RSSOwl, see bug 177974)
+ * @author Mik Kersten
+ */
+public class GradientColors {
+
+ private final Display display;
+
+ private Color titleText;
+
+ private Color gradientBegin;
+
+ private Color gradientEnd;
+
+ private Color border;
+
+ private final ResourceManager resourceManager;
+
+ public GradientColors(Display display, ResourceManager resourceManager) {
+ this.display = display;
+ this.resourceManager = resourceManager;
+
+ createColors();
+ }
+
+ private void createColors() {
+ createBorderColor();
+ createGradientColors();
+ // previously used SWT.COLOR_TITLE_INACTIVE_FOREGROUND, but too light on Windows XP
+ titleText = getColor(resourceManager, getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW));
+ }
+
+ public Color getGradientBegin() {
+ return gradientBegin;
+ }
+
+ public Color getGradientEnd() {
+ return gradientEnd;
+ }
+
+ public Color getBorder() {
+ return border;
+ }
+
+ public Color getTitleText() {
+ return titleText;
+ }
+
+ private void createBorderColor() {
+ RGB tbBorder = getSystemColor(SWT.COLOR_TITLE_BACKGROUND);
+ RGB bg = getImpliedBackground().getRGB();
+
+ // Group 1
+ // Rule: If at least 2 of the RGB values are equal to or between 180 and
+ // 255, then apply specified opacity for Group 1
+ // Examples: Vista, XP Silver, Wn High Con #2
+ // Keyline = TITLE_BACKGROUND @ 70% Opacity over LIST_BACKGROUND
+ if (testTwoPrimaryColors(tbBorder, 179, 256)) {
+ tbBorder = blend(tbBorder, bg, 70);
+ } else if (testTwoPrimaryColors(tbBorder, 120, 180)) {
+ tbBorder = blend(tbBorder, bg, 50);
+ } else {
+ tbBorder = blend(tbBorder, bg, 30);
+ }
+
+ border = getColor(resourceManager, tbBorder);
+ }
+
+ private void createGradientColors() {
+ RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND);
+ Color bgColor = getImpliedBackground();
+ RGB bg = bgColor.getRGB();
+ RGB bottom, top;
+
+ // Group 1
+ // Rule: If at least 2 of the RGB values are equal to or between 180 and
+ // 255, then apply specified opacity for Group 1
+ // Examples: Vista, XP Silver, Wn High Con #2
+ // Gradient Bottom = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND
+ // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
+ if (testTwoPrimaryColors(titleBg, 179, 256)) {
+ bottom = blend(titleBg, bg, 30);
+ top = bg;
+ }
+
+ // Group 2
+ // Rule: If at least 2 of the RGB values are equal to or between 121 and
+ // 179, then apply specified opacity for Group 2
+ // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black
+ // Gradient Bottom = TITLE_BACKGROUND @ 20% Opacity over LIST_BACKGROUND
+ // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
+ else if (testTwoPrimaryColors(titleBg, 120, 180)) {
+ bottom = blend(titleBg, bg, 20);
+ top = bg;
+ }
+
+ // Group 3
+ // Rule: If at least 2 of the RGB values are equal to or between 0 and
+ // 120, then apply specified opacity for Group 3
+ // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX
+ // Aqua, Wn High Con White, Wn High Con #1
+ // Gradient Bottom = TITLE_BACKGROUND @ 10% Opacity over LIST_BACKGROUND
+ // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
+ else {
+ bottom = blend(titleBg, bg, 10);
+ top = bg;
+ }
+
+ gradientBegin = getColor(resourceManager, top);
+ gradientEnd = getColor(resourceManager, bottom);
+ }
+
+ private RGB blend(RGB c1, RGB c2, int ratio) {
+ int r = blend(c1.red, c2.red, ratio);
+ int g = blend(c1.green, c2.green, ratio);
+ int b = blend(c1.blue, c2.blue, ratio);
+ return new RGB(r, g, b);
+ }
+
+ private int blend(int v1, int v2, int ratio) {
+ int b = (ratio * v1 + (100 - ratio) * v2) / 100;
+ return Math.min(255, b);
+ }
+
+ private boolean testTwoPrimaryColors(RGB rgb, int from, int to) {
+ int total = 0;
+ if (testPrimaryColor(rgb.red, from, to)) {
+ total++;
+ }
+ if (testPrimaryColor(rgb.green, from, to)) {
+ total++;
+ }
+ if (testPrimaryColor(rgb.blue, from, to)) {
+ total++;
+ }
+ return total >= 2;
+ }
+
+ private boolean testPrimaryColor(int value, int from, int to) {
+ return value > from && value < to;
+ }
+
+ private RGB getSystemColor(int code) {
+ return getDisplay().getSystemColor(code).getRGB();
+ }
+
+ private Color getImpliedBackground() {
+ return display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
+ }
+
+ private Display getDisplay() {
+ return display;
+ }
+
+ private Color getColor(ResourceManager manager, RGB rgb) {
+ try {
+ return manager.createColor(rgb);
+ } catch (DeviceResourceException e) {
+ return manager.getDevice().getSystemColor(SWT.COLOR_BLACK);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/NotificationPopup.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/NotificationPopup.java
new file mode 100644
index 000000000..3145dfc43
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/NotificationPopup.java
@@ -0,0 +1,154 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2013 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ * Wind River Systems - Extracted from o.e.mylyn.commons and adapted for Target Explorer
+ *******************************************************************************/
+
+package org.eclipse.tcf.te.ui.notifications.internal.popup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tcf.te.runtime.events.NotifyEvent;
+import org.eclipse.tcf.te.ui.notifications.activator.UIPlugin;
+import org.eclipse.tcf.te.ui.notifications.interfaces.IFormTextFactoryDelegate;
+import org.eclipse.tcf.te.ui.notifications.internal.factory.FactoryDelegateManager;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.forms.IFormColors;
+import org.eclipse.ui.forms.events.HyperlinkAdapter;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.FormText;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+/**
+ * @author Rob Elves
+ * @author Mik Kersten
+ */
+public class NotificationPopup extends AbstractNotificationPopup {
+
+ private static final int NUM_NOTIFICATIONS_TO_DISPLAY = 4;
+
+ /* default */ Color hyperlinkWidget = null;
+
+ private List<NotifyEvent> notifications;
+
+ /**
+ * Constructor
+ *
+ * @param parent The parent shell. Must not be <code>null</code>.
+ */
+ public NotificationPopup(Shell parent) {
+ super(parent.getDisplay());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.notifications.popup.AbstractNotificationPopup#createContentArea(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected void createContentArea(Composite parent) {
+ Assert.isNotNull(parent);
+ hyperlinkWidget = new Color(parent.getDisplay(), 12, 81, 172);
+ parent.addDisposeListener(new DisposeListener() {
+ @Override
+ public void widgetDisposed(DisposeEvent e) {
+ if (hyperlinkWidget != null) {
+ hyperlinkWidget.dispose();
+ hyperlinkWidget = null;
+ }
+ }
+ });
+
+ int count = 0;
+ for (final NotifyEvent notification : notifications) {
+ Composite notificationComposite = new Composite(parent, SWT.NO_FOCUS);
+ GridLayout gridLayout = new GridLayout(1, false);
+ GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(notificationComposite);
+ notificationComposite.setLayout(gridLayout);
+ notificationComposite.setBackground(parent.getBackground());
+
+ if (count < NUM_NOTIFICATIONS_TO_DISPLAY) {
+ // Get the notification form text factory delegate for the current notification
+ IFormTextFactoryDelegate delegate = null;
+ if (notification.getFactoryId() != null) {
+ delegate = FactoryDelegateManager.getInstance().getFactoryDelegate(notification.getFactoryId());
+ }
+ if (delegate == null) delegate = FactoryDelegateManager.getInstance().getDefaultFactoryDelegate();
+ Assert.isNotNull(delegate);
+
+ // Get the form toolkit to use
+ FormToolkit toolkit = UIPlugin.getDefault().getFormToolkit();
+ Assert.isNotNull(toolkit);
+
+ // Create the form text widget.
+ FormText widget = toolkit.createFormText(notificationComposite, true);
+ widget.setBackground(parent.getBackground());
+
+ // Populate the widget content based on the current notification event
+ delegate.populateFormText(toolkit, widget, notification);
+ } else {
+ int numNotificationsRemain = notifications.size() - count;
+ ScalingHyperlink remainingLink = new ScalingHyperlink(notificationComposite, SWT.NO_FOCUS);
+ remainingLink.setForeground(hyperlinkWidget);
+ remainingLink.registerMouseTrackListener();
+ remainingLink.setBackground(parent.getBackground());
+
+ remainingLink.setText(NLS.bind("{0} more", Integer.valueOf(numNotificationsRemain))); //$NON-NLS-1$
+ remainingLink.addHyperlinkListener(new HyperlinkAdapter() {
+ @Override
+ public void linkActivated(HyperlinkEvent e) {
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (window != null) {
+ Shell windowShell = window.getShell();
+ if (windowShell != null) {
+ windowShell.setMaximized(true);
+ windowShell.open();
+ }
+ }
+ }
+ });
+ break;
+ }
+ count++;
+ }
+ }
+
+ public List<NotifyEvent> getNotifications() {
+ return new ArrayList<NotifyEvent>(notifications);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.ui.notifications.popup.AbstractNotificationPopup#getTitleForeground()
+ */
+ @Override
+ protected Color getTitleForeground() {
+ return UIPlugin.getDefault().getFormToolkit().getColors().getColor(IFormColors.TITLE);
+ }
+
+ /**
+ * Sets the content of the notify popup.
+ *
+ * @param notifications The notification events. Must not be <code>null</code>.
+ */
+ public void setContents(List<NotifyEvent> notifications) {
+ Assert.isNotNull(notifications);
+ this.notifications = notifications;
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/PopupNotificationSink.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/PopupNotificationSink.java
new file mode 100644
index 000000000..20357aa25
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/PopupNotificationSink.java
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2013 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ * Wind River Systems - Extracted from o.e.mylyn.commons and adapted for Target Explorer
+ *******************************************************************************/
+
+package org.eclipse.tcf.te.ui.notifications.internal.popup;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tcf.te.runtime.events.NotifyEvent;
+import org.eclipse.tcf.te.ui.notifications.nls.Messages;
+import org.eclipse.ui.IWorkbenchPreferenceConstants;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * @author Rob Elves
+ * @author Steffen Pingel
+ */
+public class PopupNotificationSink {
+
+ private static final long DELAY_OPEN = 1 * 1000;
+
+ private static final boolean runSystem = true;
+
+ /* default */ NotificationPopup popup;
+
+ /* default */ final List<NotifyEvent> cancelledNotifications = new ArrayList<NotifyEvent>();
+
+ private final Set<NotifyEvent> notifications = new HashSet<NotifyEvent>();
+
+ /* default */ final Set<NotifyEvent> currentlyNotifying = Collections.synchronizedSet(notifications);
+
+ private final Job openJob = new Job(Messages.PopupNotificationSink_Popup_Notifier_Job_Label) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ if (Platform.isRunning() && PlatformUI.getWorkbench() != null
+ && PlatformUI.getWorkbench().getDisplay() != null
+ && !PlatformUI.getWorkbench().getDisplay().isDisposed()) {
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ if (popup != null && popup.getReturnCode() == Window.CANCEL) {
+ List<NotifyEvent> notifications = popup.getNotifications();
+ for (NotifyEvent notification : notifications) {
+ if (!cancelledNotifications.contains(notification)) {
+ cancelledNotifications.add(notification);
+ }
+ }
+ }
+
+ for (Iterator<NotifyEvent> it = currentlyNotifying.iterator(); it.hasNext();) {
+ NotifyEvent notification = it.next();
+ if (cancelledNotifications.contains(notification)) {
+ it.remove();
+ }
+ }
+
+ synchronized (PopupNotificationSink.class) {
+ if (currentlyNotifying.size() > 0) {
+ showPopup();
+ }
+ }
+ }
+ });
+ }
+ } finally {
+ if (popup != null) {
+ schedule(popup.getDelayClose() / 2);
+ }
+ }
+
+ if (monitor.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
+
+ return Status.OK_STATUS;
+ }
+
+ };
+
+
+ public PopupNotificationSink() {
+ openJob.setSystem(runSystem);
+ }
+
+ private void cleanNotified() {
+ currentlyNotifying.clear();
+ }
+
+
+ public boolean isAnimationsEnabled() {
+ IPreferenceStore store = PlatformUI.getPreferenceStore();
+ return store.getBoolean(IWorkbenchPreferenceConstants.ENABLE_ANIMATIONS);
+ }
+
+ /**
+ * Notify the given notification events.
+ *
+ * @param events The notification events. Must not be <code>null</code>.
+ */
+ public void notify(NotifyEvent[] events) {
+ Assert.isNotNull(events);
+
+ currentlyNotifying.addAll(Arrays.asList(events));
+
+ if (!openJob.cancel()) {
+ try {
+ openJob.join();
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ openJob.schedule(DELAY_OPEN);
+ }
+
+ public void showPopup() {
+ if (popup != null) {
+ popup.close();
+ }
+
+ Shell shell = new Shell(PlatformUI.getWorkbench().getDisplay());
+ popup = new NotificationPopup(shell);
+ popup.setFadingEnabled(isAnimationsEnabled());
+ List<NotifyEvent> toDisplay = new ArrayList<NotifyEvent>(currentlyNotifying);
+ Collections.sort(toDisplay);
+ popup.setContents(toDisplay);
+ cleanNotified();
+ popup.setBlockOnOpen(false);
+ popup.open();
+ }
+
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/ScalingHyperlink.java b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/ScalingHyperlink.java
new file mode 100644
index 000000000..ab2132c30
--- /dev/null
+++ b/target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal/popup/ScalingHyperlink.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2013 Tasktop Technologies 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:
+ * Tasktop Technologies - initial API and implementation
+ * Wind River Systems - Extracted from o.e.mylyn.commons and adapted for Target Explorer
+ *******************************************************************************/
+
+package org.eclipse.tcf.te.ui.notifications.internal.popup;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.widgets.ImageHyperlink;
+
+/**
+ * Enhanced {@link ImageHyperlink} that truncates the link text at the end rather than the middle if it is wider than
+ * the available space. Also provides default color and underline on hover.
+ *
+ * @author Leo Dos Santos
+ * @author Mik Kersten
+ */
+public class ScalingHyperlink extends ImageHyperlink {
+
+ private boolean strikeThrough;
+
+ protected final MouseTrackListener MOUSE_TRACK_LISTENER = new MouseTrackListener() {
+
+ @Override
+ public void mouseEnter(MouseEvent e) {
+ setUnderlined(true);
+ }
+
+ @Override
+ public void mouseExit(MouseEvent e) {
+ setUnderlined(false);
+ }
+
+ @Override
+ public void mouseHover(MouseEvent e) {
+ }
+ };
+
+ public ScalingHyperlink(Composite parent, int style) {
+ super(parent, style);
+ }
+
+ @Override
+ public void dispose() {
+ removeMouseTrackListener(MOUSE_TRACK_LISTENER);
+ super.dispose();
+ }
+
+ public void registerMouseTrackListener() {
+ addMouseTrackListener(MOUSE_TRACK_LISTENER);
+ }
+
+ public boolean isStrikeThrough() {
+ return strikeThrough;
+ }
+
+ @Override
+ protected void paintText(GC gc, Rectangle bounds) {
+ super.paintText(gc, bounds);
+ if (strikeThrough) {
+ Point totalSize = computeTextSize(SWT.DEFAULT, SWT.DEFAULT);
+ int textWidth = Math.min(bounds.width, totalSize.x);
+ int textHeight = totalSize.y;
+
+ // int descent = gc.getFontMetrics().getDescent();
+ int lineY = bounds.y + (textHeight / 2); // - descent + 1;
+ gc.drawLine(bounds.x, lineY, bounds.x + textWidth, lineY);
+ }
+ }
+
+ public void setStrikeThrough(boolean strikethrough) {
+ this.strikeThrough = strikethrough;
+ }
+
+ @Override
+ protected String shortenText(GC gc, String t, int width) {
+ if (t == null) {
+ return null;
+ }
+
+ if ((getStyle() & SWT.SHORT) != 0) {
+ return t;
+ }
+
+ String returnText = t;
+ if (gc.textExtent(t).x > width) {
+ for (int i = t.length(); i > 0; i--) {
+ String test = t.substring(0, i);
+ test = test + "..."; //$NON-NLS-1$
+ if (gc.textExtent(test).x < width) {
+ returnText = test;
+ break;
+ }
+ }
+ }
+ return returnText;
+ }
+
+} \ No newline at end of file

Back to the top