diff options
Diffstat (limited to 'target_explorer/plugins/org.eclipse.tcf.te.ui.notifications/src/org/eclipse/tcf/te/ui/notifications/internal')
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 |