1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.g2d.participant;
14 import java.awt.geom.Rectangle2D;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.HashSet;
18 import java.util.Iterator;
20 import java.util.concurrent.CopyOnWriteArrayList;
22 import org.simantics.g2d.canvas.ICanvasContext;
23 import org.simantics.g2d.canvas.SGDesignation;
24 import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;
25 import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;
26 import org.simantics.g2d.canvas.impl.DependencyReflection.Reference;
27 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;
28 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;
29 import org.simantics.g2d.notification.INotification;
30 import org.simantics.g2d.notification.INotificationHandle;
31 import org.simantics.scenegraph.Node;
32 import org.simantics.scenegraph.g2d.G2DParentNode;
33 import org.simantics.scenegraph.g2d.IG2DNode;
34 import org.simantics.scenegraph.g2d.events.TimeEvent;
35 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
36 import org.simantics.utils.datastructures.hints.IHintContext.Key;
37 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
40 * @author Tuukka Lehtonen
42 public class Notifications extends AbstractCanvasParticipant {
44 static final double MARGIN = 12;
46 @Reference RulerPainter ruler;
47 @Dependency TransformUtil util;;
48 @Dependency CanvasBoundsParticipant bounds;
49 @Dependency TimeParticipant time;
51 CopyOnWriteArrayList<NotificationHandle> notifications = new CopyOnWriteArrayList<NotificationHandle>();
53 public static final int PAINT_PRIORITY = Integer.MAX_VALUE - 100;
55 public interface NotificationLayout {
57 void setup(INotification n);
60 public static final Key KEY_NOTIFICATION_LAYOUT = new KeyOf(NotificationLayout.class);
62 class DefaultLayout implements NotificationLayout {
66 public void rewind() {
71 public void setup(INotification n) {
75 class NotificationHandle implements INotificationHandle {
79 NotificationHandle(INotification not, long duration) {
81 this.duration = duration;
85 public void discard() {
86 clearNotification(this);
89 public long elapsed(long currentTime) {
90 long elapsed = currentTime - not.getCreationTime();
95 public boolean hasElapsed() {
96 return elapsed(System.currentTimeMillis()) > duration;
101 public void addedToContext(ICanvasContext ctx) {
102 super.addedToContext(ctx);
103 setHint(KEY_NOTIFICATION_LAYOUT, new DefaultLayout());
106 @EventHandler(priority = 0)
107 public boolean handleTimer(TimeEvent event) {
108 //System.out.println("time event: " + event.time + " (" + event.interval + ")");
109 if (notifications.isEmpty()) {
110 time.unregisterForEvents(getClass());
114 boolean changes = false;
115 Collection<NotificationHandle> removed = null;
116 for (Iterator<NotificationHandle> it = notifications.iterator(); it.hasNext();) {
117 NotificationHandle n = it.next();
118 long elapsed = n.elapsed(event.time);
119 if (elapsed > n.duration) {
122 removed = new ArrayList<NotificationHandle>();
125 n.not.setProgress(progress(n, event.time));
131 notifications.removeAll(removed);
132 updateNotifications();
139 * @param notification the notification to show
140 * @param duration duration to show the notification for in milliseconds
143 public INotificationHandle addNotification(INotification notification, long duration) {
144 assert getThread().currentThreadAccess();
145 NotificationHandle n = new NotificationHandle(notification, duration);
146 notifications.add(n);
147 // Make sure that timer events are received for as long as there are notifications.
148 time.registerForEvents(getClass());
154 * Clear the specified notification from sight immediately.
156 * @param handle the notification to remove
158 public void clearNotification(INotificationHandle handle) {
159 assert getThread().currentThreadAccess();
160 notifications.remove(handle);
163 Rectangle2D r2d = new Rectangle2D.Double();
165 G2DParentNode notificationNode;
167 @SGInit(designation = SGDesignation.CONTROL)
168 public void initSG(G2DParentNode parent) {
169 notificationNode = parent.addNode("notifications", G2DParentNode.class);
170 notificationNode.setZIndex(PAINT_PRIORITY);
174 public void cleanup() {
175 if (notificationNode != null) {
176 notificationNode.remove();
177 notificationNode = null;
181 private final Set<Node> updated = new HashSet<Node>();
183 public void updateNotifications() {
184 Rectangle2D cb = bounds.getControlBounds();
186 double x = cb.getCenterX() + MARGIN;
192 for (NotificationHandle n : notifications) {
193 G2DParentNode node = notificationNode.getOrCreateNode("" + n.not.hashCode(), G2DParentNode.class);
195 r2d.setFrame(x, y, cb.getWidth() / 2 - 2*MARGIN, h);
196 //System.out.println("update notification: " + n + " : " + r2d);
197 n.not.setBounds(r2d);
202 Collection<IG2DNode> nodes = notificationNode.getNodes();
203 IG2DNode[] nodesCopy = nodes.toArray(new IG2DNode[nodes.size()]);
204 for (IG2DNode node : nodesCopy) {
205 if (!updated.contains(node)) {
210 // Don't leave dangling references.
214 static double progress(NotificationHandle h, long currentTime) {
215 long elapsed = h.elapsed(currentTime);
216 return Math.min((double) elapsed / (double) h.duration, 1.0);
219 // @EventHandler(priority = 0)
220 // public boolean handleKey(KeyPressedEvent e) {
221 // if (e.character == 'n') {
222 // //System.out.println("add notification");
223 // addNotification(new MessageNotification("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam justo odio, vehicula non, elementum vel, condimentum ut, odio. Integer quis massa nec risus consectetur euismod. Duis venenatis adipiscing ligula. Pellentesque pellentesque nunc vulputate metus. Curabitur laoreet libero eu nisl ornare molestie. Cras non tellus. Vivamus vestibulum tincidunt mi. Morbi accumsan. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam erat volutpat. Aliquam odio erat, dictum aliquet, placerat a, sollicitudin eget, leo."), 5000);