diff options
Diffstat (limited to 'stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui')
18 files changed, 1780 insertions, 0 deletions
diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/Messages.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/Messages.java new file mode 100644 index 00000000..7a5f257e --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/Messages.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2011 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 + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.mylyn.internal.commons.ui.notifications.messages"; //$NON-NLS-1$ + + public static String NotificationsPreferencesPage_Descriptions_Label; + + public static String NotificationsPreferencesPage_Enable_Notifications_Text; + + public static String NotificationsPreferencesPage_Events_Label; + + public static String NotificationsPreferencesPage_Notifiers_Label; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationAction.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationAction.java new file mode 100644 index 00000000..9876076d --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationAction.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2010 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 + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.mylyn.commons.ui.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/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationCategory.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationCategory.java new file mode 100644 index 00000000..a6c57978 --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationCategory.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2010 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 + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +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/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationElement.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationElement.java new file mode 100644 index 00000000..795e32ae --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationElement.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2010 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 + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +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.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, NotificationsPlugin.ID_PLUGIN, NLS.bind( + "Extension {0} contributed by {1} does not specify id attribute", element.getNamespaceIdentifier(), //$NON-NLS-1$ + getPluginId())); //NON-NLS-1$ + } else if (label == null) { + return new Status(IStatus.ERROR, NotificationsPlugin.ID_PLUGIN, NLS.bind( + "Extension {0} contributed by {1} does not specify label attribute", //$NON-NLS-1$ + element.getNamespaceIdentifier(), getPluginId())); //NON-NLS-1$ + } + return Status.OK_STATUS; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationEvent.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationEvent.java new file mode 100644 index 00000000..92527ed1 --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationEvent.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2010 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 + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +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.mylyn.commons.ui.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.mylyn.commons.notifications.notifications"; //$NON-NLS-1$ + + private NotificationCategory category; + + private boolean selected; + + 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; + } + + @Deprecated + public boolean isSelected() { + return selected; + } + + @Deprecated + public void setSelected(boolean selected) { + this.selected = selected; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationHandler.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationHandler.java new file mode 100644 index 00000000..7aa94a77 --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationHandler.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2010 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 + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +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/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationModel.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationModel.java new file mode 100644 index 00000000..4d10e90f --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationModel.java @@ -0,0 +1,172 @@ +/******************************************************************************* + * Copyright (c) 2010 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 + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.ui.IMemento; + +/** + * @author Steffen Pingel + * @author Torkild U. Resheim + */ +public class NotificationModel { + + private boolean dirty; + + private Map<String, NotificationHandler> handlerByEventId; + + public NotificationModel(IMemento memento) { + initialize(memento); + } + + void initialize(IMemento memento) { + 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); + } + } + if (memento != null) { + load(memento); + } + } + + 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 isDirty() { + return dirty; + } + + public boolean isSelected(NotificationEvent event) { + NotificationHandler handler = getOrCreateNotificationHandler(event); + for (NotificationAction action : handler.getActions()) { + if (action.isSelected()) { + return true; + } + } + return false; + } + + /** + * Stores the selected state of events and sinks. + * + * @param memento + * the memento to store in. + */ + public void save(IMemento memento) { + for (Entry<String, NotificationHandler> entry : handlerByEventId.entrySet()) { + IMemento event = memento.createChild("event"); //$NON-NLS-1$ + event.putString("id", entry.getKey()); //$NON-NLS-1$ + List<NotificationAction> actions = entry.getValue().getActions(); + for (NotificationAction notificationAction : actions) { + IMemento action = event.createChild("action"); //$NON-NLS-1$ + action.putBoolean("selected", notificationAction.isSelected()); //$NON-NLS-1$ + action.putString("sink", notificationAction.getSinkDescriptor().getId()); //$NON-NLS-1$ + } + } + setDirty(false); + } + + /** + * Updates the notification model with selected states from the memento instance. + * + * @param memento + */ + private void load(IMemento memento) { + Assert.isNotNull(memento); + for (IMemento mEvent : memento.getChildren("event")) { //$NON-NLS-1$ + for (NotificationCategory category : getCategories()) { + for (NotificationEvent event : category.getEvents()) { + if (event.getId().equals(mEvent.getString("id"))) { //$NON-NLS-1$ + NotificationHandler handler = getOrCreateNotificationHandler(event); + List<NotificationAction> actions = handler.getActions(); + for (NotificationAction notificationAction : actions) { + IMemento[] mActions = mEvent.getChildren("action"); //$NON-NLS-1$ + for (IMemento mAction : mActions) { + if (notificationAction.getSinkDescriptor().getId().equals(mAction.getString("sink"))) { //$NON-NLS-1$ + notificationAction.setSelected(mAction.getBoolean("selected")); //$NON-NLS-1$ + } + } + } + } + } + } + } + } + + public void setDirty(boolean dirty) { + this.dirty = dirty; + } + + /** + * Updates the state of the notification handlers depending on their child notification action states. + */ + @Deprecated + public void updateStates() { + Collection<NotificationHandler> handlers = handlerByEventId.values(); + for (NotificationHandler notificationHandler : handlers) { + List<NotificationAction> actions = notificationHandler.getActions(); + boolean selected = false; + for (NotificationAction notificationAction : actions) { + if (notificationAction.isSelected()) { + selected = true; + break; + } + } + notificationHandler.getEvent().setSelected(selected); + } + } + + public void setNotificationHandler(String eventId, NotificationHandler handler) { + handlerByEventId.put(eventId, handler); + setDirty(true); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationService.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationService.java new file mode 100644 index 00000000..3e57558b --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationService.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2010 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 + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; + +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.mylyn.commons.core.StatusHandler; +import org.eclipse.mylyn.commons.ui.notifications.AbstractNotification; +import org.eclipse.mylyn.commons.ui.notifications.INotificationService; +import org.eclipse.mylyn.commons.ui.notifications.NotificationSink; +import org.eclipse.mylyn.commons.ui.notifications.NotificationSinkEvent; + +/** + * @author Steffen Pingel + * @author Torkild U. Resheim + */ +public class NotificationService implements INotificationService { + + public NotificationService() { + } + + /** + * Notify sinks about the. + */ + public void notify(List<? extends AbstractNotification> notifications) { + // Return if notifications are not globally enabled. + if (!NotificationsPlugin.getDefault() + .getPreferenceStore() + .getBoolean(NotificationsPlugin.PREF_NOTICATIONS_ENABLED)) { + return; + } + // 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 = NotificationsPlugin.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); + } + } + } + } + } + // 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() { + public void handleException(Throwable e) { + StatusHandler.log(new Status(IStatus.WARNING, NotificationsPlugin.ID_PLUGIN, "Sink failed: " //$NON-NLS-1$ + + sink.getClass(), e)); + } + + public void run() throws Exception { + sink.notify(event); + } + }); + } + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationSinkDescriptor.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationSinkDescriptor.java new file mode 100644 index 00000000..06784243 --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationSinkDescriptor.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2010 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 + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.mylyn.commons.ui.notifications.NotificationSink; +import org.eclipse.osgi.util.NLS; +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; + } else { + status = new Status(IStatus.ERROR, NotificationsPlugin.ID_PLUGIN, 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, NotificationsPlugin.ID_PLUGIN, 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/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsExtensionReader.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsExtensionReader.java new file mode 100644 index 00000000..b0b1a0a6 --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsExtensionReader.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (c) 2010 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 + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +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.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(NotificationsPlugin.ID_PLUGIN, 0, + "Notifcation extensions failed to load", null); //$NON-NLS-1$ + + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint repositoriesExtensionPoint = registry.getExtensionPoint(NotificationsPlugin.ID_PLUGIN + + ".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, + NotificationsPlugin.ID_PLUGIN, + 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(NotificationsPlugin.ID_PLUGIN, 0, + "Notifcation extensions failed to load", null); //$NON-NLS-1$ + + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint repositoriesExtensionPoint = registry.getExtensionPoint(NotificationsPlugin.ID_PLUGIN + + ".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/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPlugin.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPlugin.java new file mode 100644 index 00000000..43253468 --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPlugin.java @@ -0,0 +1,131 @@ +/******************************************************************************* + * Copyright (c) 2010 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 + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.ui.IMemento; +import org.eclipse.ui.WorkbenchException; +import org.eclipse.ui.XMLMemento; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * @author Steffen Pingel + */ +public class NotificationsPlugin extends AbstractUIPlugin { + + public static final String ID_PLUGIN = "org.eclipse.mylyn.commons.notifications"; //$NON-NLS-1$ + + public static final String PREF_NOTICATIONS_ENABLED = "notifications.enabled"; //$NON-NLS-1$ + + private static NotificationsPlugin instance; + + public static NotificationsPlugin getDefault() { + return instance; + } + + private NotificationModel model; + + private NotificationService service; + + public NotificationModel createModelWorkingCopy() { + IMemento memento = null; + File file = getModelFile().toFile(); + if (file.exists()) { + try { + FileReader reader = new FileReader(file); + try { + memento = XMLMemento.createReadRoot(reader); + } finally { + reader.close(); + } + } catch (IOException e) { + getLog().log(new Status(IStatus.ERROR, ID_PLUGIN, "Unexpected error restoring notification state", e)); //$NON-NLS-1$ + } catch (WorkbenchException e) { + getLog().log(new Status(IStatus.ERROR, ID_PLUGIN, "Unexpected error restoring notification state", e)); //$NON-NLS-1$ + } + } + return new NotificationModel(memento); + } + + public NotificationModel getModel() { + if (model == null) { + model = createModelWorkingCopy(); + } + return model; + } + + public NotificationService getService() { + if (service == null) { + service = new NotificationService(); + } + return service; + } + + public void saveModel() { + if (model != null && model.isDirty()) { + save(model); + } + } + + public void saveWorkingCopy(NotificationModel workingCopy) { + XMLMemento memento = save(workingCopy); + if (this.model != null) { + // reload model + this.model.initialize(memento); + } + } + + @Override + public void start(BundleContext context) throws Exception { + instance = this; + super.start(context); + } + + @Override + public void stop(BundleContext context) throws Exception { + super.stop(context); + instance = null; + } + + private XMLMemento save(NotificationModel model) { + XMLMemento memento = XMLMemento.createWriteRoot("notifications"); //$NON-NLS-1$ + model.save(memento); + FileWriter writer; + try { + writer = new FileWriter(getModelFile().toFile()); + try { + memento.save(writer); + } finally { + writer.close(); + } + } catch (IOException e) { + getLog().log(new Status(IStatus.ERROR, ID_PLUGIN, "Unexpected error saving notification state", e)); //$NON-NLS-1$ + } + return memento; + } + + protected IPath getModelFile() { + IPath stateLocation = Platform.getStateLocation(getBundle()); + IPath cacheFile = stateLocation.append("notifications.xml"); //$NON-NLS-1$ + return cacheFile; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPreferencesInitializer.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPreferencesInitializer.java new file mode 100644 index 00000000..299a2024 --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPreferencesInitializer.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2010 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 + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.jface.preference.IPreferenceStore; + +/** + * @author Steffen Pingel + */ +public class NotificationsPreferencesInitializer extends AbstractPreferenceInitializer { + + public NotificationsPreferencesInitializer() { + // ignore + } + + @Override + public void initializeDefaultPreferences() { + IPreferenceStore preferencesStore = NotificationsPlugin.getDefault().getPreferenceStore(); + preferencesStore.setDefault(NotificationsPlugin.PREF_NOTICATIONS_ENABLED, true); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPreferencesPage.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPreferencesPage.java new file mode 100644 index 00000000..82d4e54e --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPreferencesPage.java @@ -0,0 +1,406 @@ +/******************************************************************************* + * Copyright (c) 2010 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 329897 select event type on open if available + * Itema AS - bug 330064 notification filtering and model persistence + * Itema AS - bug 331424 handle default event-sink action associations + *******************************************************************************/ +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.ICheckStateProvider; +import org.eclipse.jface.viewers.IElementComparer; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.mylyn.internal.provisional.commons.ui.CommonImages; +import org.eclipse.mylyn.internal.provisional.commons.ui.SubstringPatternFilter; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.dialogs.FilteredTree; + +/** + * @author Steffen Pingel + * @author Torkild Ulvøy Resheim + */ +public class NotificationsPreferencesPage extends PreferencePage implements IWorkbenchPreferencePage { + + /** + * We need this in order to make sure that the correct element is selected in the {@link TreeViewer} when the + * selection is set. + * + * @author Torkild Ulvøy Resheim + */ + public class NotificationEventComparer implements IElementComparer { + + public boolean equals(Object a, Object b) { + if (a instanceof NotificationEvent && b instanceof NotificationEvent) { + String idA = ((NotificationEvent) a).getId(); + String idB = ((NotificationEvent) b).getId(); + return (idA.equals(idB)); + } + return a.equals(b); + } + + public int hashCode(Object element) { + return element.hashCode(); + } + + } + + private static final Object[] EMPTY = new Object[0]; + + private final class EventContentProvider implements ITreeContentProvider { + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // ignore + } + + public void dispose() { + // ignore + } + + public boolean hasChildren(Object element) { + if (element instanceof NotificationCategory) { + return ((NotificationCategory) element).getEvents().size() > 0; + } + return false; + } + + public Object getParent(Object element) { + if (element instanceof NotificationEvent) { + return ((NotificationEvent) element).getCategory(); + } + return null; + } + + public Object[] getElements(Object inputElement) { + if (inputElement instanceof Object[]) { + return (Object[]) inputElement; + } else { + return EMPTY; + } + } + + public Object[] getChildren(Object parentElement) { + if (parentElement instanceof NotificationCategory) { + return ((NotificationCategory) parentElement).getEvents().toArray(); + } + return EMPTY; + } + + } + + private final class NotifiersContentProvider implements IStructuredContentProvider { + + private NotificationHandler handler; + + public void dispose() { + // ignore + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + if (newInput instanceof NotificationHandler) { + handler = (NotificationHandler) newInput; + } else { + handler = null; + } + } + + public Object[] getElements(Object inputElement) { + if (handler != null) { + return handler.getActions().toArray(); + } else { + return EMPTY; + } + } + + } + + public final class EventLabelProvider extends LabelProvider { + + @Override + public String getText(Object element) { + if (element instanceof NotificationElement) { + NotificationElement item = (NotificationElement) element; + return item.getLabel(); + } + return super.getText(element); + } + + @Override + public Image getImage(Object element) { + if (element instanceof NotificationEvent) { + NotificationEvent item = (NotificationEvent) element; + if (model.isSelected(item)) { + return CommonImages.getImage(CommonImages.CHECKED); + } else { + return null; + } + } + if (element instanceof NotificationElement) { + NotificationElement item = (NotificationElement) element; + ImageDescriptor imageDescriptor = item.getImageDescriptor(); + if (imageDescriptor != null) { + return CommonImages.getImage(imageDescriptor); + } + } + return super.getImage(element); + } + } + + private TreeViewer eventsViewer; + + private CheckboxTableViewer notifiersViewer; + + private Button enableNotificationsButton; + + private NotificationModel model; + + private Text descriptionText; + + public NotificationsPreferencesPage() { + } + + @Override + public IPreferenceStore getPreferenceStore() { + return NotificationsPlugin.getDefault().getPreferenceStore(); + } + + @Override + protected Control createContents(Composite parent) { + model = NotificationsPlugin.getDefault().createModelWorkingCopy(); + + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + + enableNotificationsButton = new Button(composite, SWT.CHECK); + GridDataFactory.fillDefaults().span(2, 1).applyTo(enableNotificationsButton); + enableNotificationsButton.setText(Messages.NotificationsPreferencesPage_Enable_Notifications_Text); + enableNotificationsButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + updateEnablement(); + } + }); + + Label label = new Label(composite, SWT.NONE); + label.setText(" "); //$NON-NLS-1$ + GridDataFactory.fillDefaults().span(2, 1).applyTo(label); + + label = new Label(composite, SWT.NONE); + label.setText(Messages.NotificationsPreferencesPage_Events_Label); + + label = new Label(composite, SWT.NONE); + label.setText(Messages.NotificationsPreferencesPage_Notifiers_Label); + // Create the tree showing all the various notification types + FilteredTree tree = new FilteredTree(composite, SWT.BORDER, new SubstringPatternFilter(), true); + eventsViewer = tree.getViewer(); + GridDataFactory.fillDefaults().span(1, 2).grab(false, true).applyTo(tree); + eventsViewer.setComparer(new NotificationEventComparer()); + eventsViewer.setContentProvider(new EventContentProvider()); + eventsViewer.setLabelProvider(new EventLabelProvider()); + eventsViewer.setInput(model.getCategories().toArray()); + eventsViewer.expandAll(); + eventsViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + Object input = getDetailsInput((IStructuredSelection) event.getSelection()); + notifiersViewer.setInput(input); + + Object item = ((IStructuredSelection) event.getSelection()).getFirstElement(); + if (item instanceof NotificationEvent) { + descriptionText.setText(((NotificationEvent) item).getDescription()); + notifiersViewer.getControl().setEnabled(true); + } else { + descriptionText.setText(" "); //$NON-NLS-1$ + notifiersViewer.getControl().setEnabled(false); + } + } + + private Object getDetailsInput(IStructuredSelection selection) { + Object item = selection.getFirstElement(); + if (item instanceof NotificationEvent) { + return model.getOrCreateNotificationHandler((NotificationEvent) item); + } + return null; + } + }); + // Create the table listing all notification sinks available for the selected event type. + notifiersViewer = CheckboxTableViewer.newCheckList(composite, SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, true).applyTo(notifiersViewer.getControl()); + notifiersViewer.setContentProvider(new NotifiersContentProvider()); + notifiersViewer.setLabelProvider(new EventLabelProvider()); + notifiersViewer.addCheckStateListener(new ICheckStateListener() { + public void checkStateChanged(CheckStateChangedEvent event) { + NotificationAction action = (NotificationAction) event.getElement(); + action.setSelected(event.getChecked()); + model.setDirty(true); + eventsViewer.refresh(); + } + }); + notifiersViewer.setCheckStateProvider(new ICheckStateProvider() { + public boolean isChecked(Object element) { + return ((NotificationAction) element).isSelected(); + } + + public boolean isGrayed(Object element) { + return false; + } + }); + notifiersViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + Object item = ((IStructuredSelection) event.getSelection()).getFirstElement(); + if (item instanceof NotificationAction) { + // TODO show configuration pane + } + } + }); + + Group group = new Group(composite, SWT.NONE); + GridDataFactory.fillDefaults().hint(150, SWT.DEFAULT).grab(true, true).applyTo(group); + group.setText(Messages.NotificationsPreferencesPage_Descriptions_Label); + FillLayout layout = new FillLayout(); + layout.marginHeight = 5; + layout.marginWidth = 5; + group.setLayout(layout); + + descriptionText = new Text(group, SWT.WRAP); + descriptionText.setBackground(group.getBackground()); + +// Button testButton = new Button(composite, SWT.NONE); +// testButton.setText("Test"); +// testButton.addSelectionListener(new SelectionAdapter() { +// @Override +// public void widgetSelected(SelectionEvent e) { +// ISelection selection = eventsViewer.getSelection(); +// if (selection instanceof IStructuredSelection) { +// Object object = ((IStructuredSelection) selection).getFirstElement(); +// if (object instanceof NotificationEvent) { +// final NotificationEvent event = (NotificationEvent) object; +// getControl().getDisplay().asyncExec(new Runnable() { +// public void run() { +// Notifications.getService().notify( +// Collections.singletonList(new TestNotification(event))); +// } +// }); +// } +// } +// } +// +// }); + + reset(); + Dialog.applyDialogFont(composite); + return composite; + } + + @Override + public void applyData(Object data) { + // We may or may not have a NotificationEvent supplied when this + // preference dialog is opened. If we do have this data we want to + // highlight the appropriate instance. + if (data instanceof String) { + String selectedEventId = (String) data; + Collection<NotificationCategory> items = model.getCategories(); + NotificationEvent selectedEvent = null; + for (NotificationCategory notificationCategory : items) { + List<NotificationEvent> event = notificationCategory.getEvents(); + for (NotificationEvent notificationEvent : event) { + if (notificationEvent.getId().equals(selectedEventId)) { + selectedEvent = notificationEvent; + break; + } + } + } + if (selectedEvent != null) { + eventsViewer.setSelection(new StructuredSelection(selectedEvent), true); + } + } + } + + private void updateEnablement() { + boolean enabled = enableNotificationsButton.getSelection(); + eventsViewer.getControl().setEnabled(enabled); + notifiersViewer.getControl().setEnabled(enabled);// FIXME enabled && notifiersViewer.getInput() != null); + descriptionText.setEnabled(enabled); + if (!enabled) { + eventsViewer.setSelection(StructuredSelection.EMPTY); + } + } + + public void init(IWorkbench workbench) { + // ignore + } + + public void reset() { + enableNotificationsButton.setSelection(getPreferenceStore().getBoolean( + NotificationsPlugin.PREF_NOTICATIONS_ENABLED)); + updateEnablement(); + } + + @Override + public boolean performOk() { + getPreferenceStore().setValue(NotificationsPlugin.PREF_NOTICATIONS_ENABLED, + enableNotificationsButton.getSelection()); + if (model.isDirty()) { + NotificationsPlugin.getDefault().saveWorkingCopy(model); + model.setDirty(false); + } + return super.performOk(); + } + + @Override + protected void performDefaults() { + enableNotificationsButton.setSelection(getPreferenceStore().getDefaultBoolean( + NotificationsPlugin.PREF_NOTICATIONS_ENABLED)); + for (NotificationCategory category : model.getCategories()) { + for (NotificationEvent event : category.getEvents()) { + NotificationHandler handler = model.getOrCreateNotificationHandler(event); + for (NotificationAction action : handler.getActions()) { + action.setSelected(event.defaultHandledBySink(action.getSinkDescriptor().getId())); + } + } + } + // assume that the model has become dirty + model.setDirty(true); + // refresh UI + eventsViewer.refresh(); + notifiersViewer.refresh(); + updateEnablement(); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/messages.properties b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/messages.properties new file mode 100644 index 00000000..1b85c3c2 --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/messages.properties @@ -0,0 +1,4 @@ +NotificationsPreferencesPage_Descriptions_Label=Description +NotificationsPreferencesPage_Enable_Notifications_Text=&Enable notifications +NotificationsPreferencesPage_Events_Label=Events: +NotificationsPreferencesPage_Notifiers_Label=Notifiers: diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/Messages.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/Messages.java new file mode 100644 index 00000000..f82bf60f --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/Messages.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2011 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 + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications.popup; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.mylyn.internal.commons.ui.notifications.popup.messages"; //$NON-NLS-1$ + + public static String PopupNotificationSink_Popup_Noifier_Job_Label; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/NotificationPopup.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/NotificationPopup.java new file mode 100644 index 00000000..b16d44bd --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/NotificationPopup.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2004, 2010 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 + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications.popup; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.mylyn.commons.ui.notifications.AbstractNotification; +import org.eclipse.mylyn.internal.commons.ui.CommonsUiPlugin; +import org.eclipse.mylyn.internal.provisional.commons.ui.AbstractNotificationPopup; +import org.eclipse.mylyn.internal.provisional.commons.ui.CommonColors; +import org.eclipse.mylyn.internal.provisional.commons.ui.CommonUiUtil; +import org.eclipse.mylyn.internal.provisional.commons.ui.ScalingHyperlink; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +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; + +/** + * @author Rob Elves + * @author Mik Kersten + */ +public class NotificationPopup extends AbstractNotificationPopup { + + private static final int NUM_NOTIFICATIONS_TO_DISPLAY = 4; + + private List<AbstractNotification> notifications; + + public NotificationPopup(Shell parent) { + super(parent.getDisplay()); + } + + @Override + protected void createContentArea(Composite parent) { + int count = 0; + for (final AbstractNotification notification : notifications) { + Composite notificationComposite = new Composite(parent, SWT.NO_FOCUS); + GridLayout gridLayout = new GridLayout(2, false); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(notificationComposite); + notificationComposite.setLayout(gridLayout); + notificationComposite.setBackground(parent.getBackground()); + + if (count < NUM_NOTIFICATIONS_TO_DISPLAY) { + final Label notificationLabelIcon = new Label(notificationComposite, SWT.NO_FOCUS); + notificationLabelIcon.setBackground(parent.getBackground()); + notificationLabelIcon.setImage(notification.getNotificationKindImage()); + // FIXME +// if (!(notification instanceof TaskListNotificationReminder)) { +// final AbstractTask task = (AbstractTask) notification.getAdapter(AbstractTask.class); +// if (task != null) { +// notificationLabelIcon.addMouseListener(new MouseAdapter() { +// @Override +// public void mouseUp(MouseEvent e) { +// TasksUiPlugin.getTaskDataManager().setTaskRead(task, true); +// notificationLabelIcon.setImage(null); +// notificationLabelIcon.setToolTipText(null); +// } +// }); +// notificationLabelIcon.setToolTipText(Messages.TaskListNotificationPopup_Mark_Task_Read); +// } +// } + + // FIXME +// final TaskScalingHyperlink itemLink = new TaskScalingHyperlink(notificationComposite, SWT.BEGINNING +// | SWT.NO_FOCUS); + + final ScalingHyperlink itemLink = new ScalingHyperlink(notificationComposite, SWT.BEGINNING + | SWT.NO_FOCUS); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(itemLink); + itemLink.setForeground(CommonColors.HYPERLINK_WIDGET); + itemLink.registerMouseTrackListener(); + itemLink.setText(CommonUiUtil.toLabel(notification.getLabel())); + itemLink.setImage(notification.getNotificationImage()); + itemLink.setBackground(parent.getBackground()); + itemLink.addHyperlinkListener(new HyperlinkAdapter() { + @Override + public void linkActivated(HyperlinkEvent e) { + notification.open(); + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window != null) { + Shell windowShell = window.getShell(); + if (windowShell != null) { + if (windowShell.getMinimized()) { + windowShell.setMinimized(false); + } + + windowShell.open(); + windowShell.forceActive(); + } + } + } + }); + + String descriptionText = null; + if (notification.getDescription() != null) { + descriptionText = notification.getDescription(); + } + if (descriptionText != null && !descriptionText.trim().equals("")) { //$NON-NLS-1$ + Label descriptionLabel = new Label(notificationComposite, SWT.NO_FOCUS); + descriptionLabel.setText(CommonUiUtil.toLabel(descriptionText)); + descriptionLabel.setBackground(parent.getBackground()); + GridDataFactory.fillDefaults() + .span(2, SWT.DEFAULT) + .grab(true, false) + .align(SWT.FILL, SWT.TOP) + .applyTo(descriptionLabel); + } + } else { + int numNotificationsRemain = notifications.size() - count; + ScalingHyperlink remainingLink = new ScalingHyperlink(notificationComposite, SWT.NO_FOCUS); + remainingLink.setForeground(CommonColors.HYPERLINK_WIDGET); + remainingLink.registerMouseTrackListener(); + remainingLink.setBackground(parent.getBackground()); + + remainingLink.setText(NLS.bind("{0} more", numNotificationsRemain)); //$NON-NLS-1$ + GridDataFactory.fillDefaults().span(2, SWT.DEFAULT).applyTo(remainingLink); + remainingLink.addHyperlinkListener(new HyperlinkAdapter() { + @Override + public void linkActivated(HyperlinkEvent e) { + // FIXME + // TasksUiUtil.openTasksViewInActivePerspective().setFocus(); + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window != null) { + Shell windowShell = window.getShell(); + if (windowShell != null) { + windowShell.setMaximized(true); + windowShell.open(); + } + } + } + }); + break; + } + count++; + } + } + + @Override + protected void createTitleArea(Composite parent) { + super.createTitleArea(parent); + } + + public List<AbstractNotification> getNotifications() { + return new ArrayList<AbstractNotification>(notifications); + } + + @Override + protected Color getTitleForeground() { + return CommonsUiPlugin.getDefault().getFormColors(Display.getDefault()).getColor(IFormColors.TITLE); + + } + + public void setContents(List<AbstractNotification> notifications) { + this.notifications = notifications; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/PopupNotificationSink.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/PopupNotificationSink.java new file mode 100644 index 00000000..785ba85d --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/PopupNotificationSink.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (c) 2004, 2010 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 + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications.popup; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.WeakHashMap; + +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.mylyn.commons.ui.notifications.AbstractNotification; +import org.eclipse.mylyn.commons.ui.notifications.NotificationSink; +import org.eclipse.mylyn.commons.ui.notifications.NotificationSinkEvent; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IWorkbenchPreferenceConstants; +import org.eclipse.ui.PlatformUI; + +/** + * @author Rob Elves + * @author Steffen Pingel + */ +public class PopupNotificationSink extends NotificationSink { + + private static final long DELAY_OPEN = 1 * 1000; + + private static final boolean runSystem = true; + + private final WeakHashMap<Object, Object> cancelledTokens = new WeakHashMap<Object, Object>(); + + private final Set<AbstractNotification> notifications = new HashSet<AbstractNotification>(); + + private final Set<AbstractNotification> currentlyNotifying = Collections.synchronizedSet(notifications); + + private final Job openJob = new Job(Messages.PopupNotificationSink_Popup_Noifier_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() { + + public void run() { + collectNotifications(); + + if (popup != null && popup.getReturnCode() == Window.CANCEL) { + List<AbstractNotification> notifications = popup.getNotifications(); + for (AbstractNotification notification : notifications) { + if (notification.getToken() != null) { + cancelledTokens.put(notification.getToken(), null); + } + } + } + + for (Iterator<AbstractNotification> it = currentlyNotifying.iterator(); it.hasNext();) { + AbstractNotification notification = it.next(); + if (notification.getToken() != null + && cancelledTokens.containsKey(notification.getToken())) { + it.remove(); + } + } + + synchronized (PopupNotificationSink.class) { + if (currentlyNotifying.size() > 0) { +// popup.close(); + showPopup(); + } + } + } + }); + } + } finally { + if (popup != null) { + schedule(popup.getDelayClose() / 2); + } + } + + if (monitor.isCanceled()) { + return Status.CANCEL_STATUS; + } + + return Status.OK_STATUS; + } + + }; + + private NotificationPopup popup; + + public PopupNotificationSink() { + } + + private void cleanNotified() { + currentlyNotifying.clear(); + } + + /** public for testing */ + public void collectNotifications() { + } + + /** + * public for testing purposes + */ + public Set<AbstractNotification> getNotifications() { + synchronized (PopupNotificationSink.class) { + return currentlyNotifying; + } + } + + public boolean isAnimationsEnabled() { + IPreferenceStore store = PlatformUI.getPreferenceStore(); + return store.getBoolean(IWorkbenchPreferenceConstants.ENABLE_ANIMATIONS); + } + + @Override + public void notify(NotificationSinkEvent event) { + currentlyNotifying.addAll(event.getNotifications()); + + if (!openJob.cancel()) { + try { + openJob.join(); + } catch (InterruptedException e) { + // ignore + } + } + openJob.setSystem(runSystem); + 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<AbstractNotification> toDisplay = new ArrayList<AbstractNotification>(currentlyNotifying); + Collections.sort(toDisplay); + popup.setContents(toDisplay); + cleanNotified(); + popup.setBlockOnOpen(false); + popup.open(); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/messages.properties b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/messages.properties new file mode 100644 index 00000000..5ac2c060 --- /dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/messages.properties @@ -0,0 +1 @@ +PopupNotificationSink_Popup_Noifier_Job_Label=Popup Notifier |