]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/participant/Notifications.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / participant / Notifications.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.g2d.participant;\r
13 \r
14 import java.awt.geom.Rectangle2D;\r
15 import java.util.ArrayList;\r
16 import java.util.Collection;\r
17 import java.util.HashSet;\r
18 import java.util.Iterator;\r
19 import java.util.Set;\r
20 import java.util.concurrent.CopyOnWriteArrayList;\r
21 \r
22 import org.simantics.g2d.canvas.ICanvasContext;\r
23 import org.simantics.g2d.canvas.SGDesignation;\r
24 import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;\r
25 import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;\r
26 import org.simantics.g2d.canvas.impl.DependencyReflection.Reference;\r
27 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;\r
28 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;\r
29 import org.simantics.g2d.notification.INotification;\r
30 import org.simantics.g2d.notification.INotificationHandle;\r
31 import org.simantics.scenegraph.Node;\r
32 import org.simantics.scenegraph.g2d.G2DParentNode;\r
33 import org.simantics.scenegraph.g2d.IG2DNode;\r
34 import org.simantics.scenegraph.g2d.events.TimeEvent;\r
35 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;\r
36 import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
37 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;\r
38 \r
39 /**\r
40  * @author Tuukka Lehtonen\r
41  */\r
42 public class Notifications extends AbstractCanvasParticipant {\r
43 \r
44     static final double MARGIN = 12;\r
45 \r
46     @Reference RulerPainter ruler;\r
47     @Dependency TransformUtil util;;\r
48     @Dependency CanvasBoundsParticipant bounds;\r
49     @Dependency TimeParticipant time;\r
50 \r
51     CopyOnWriteArrayList<NotificationHandle> notifications = new CopyOnWriteArrayList<NotificationHandle>();\r
52 \r
53     public static final int PAINT_PRIORITY = Integer.MAX_VALUE - 100;\r
54 \r
55     public interface NotificationLayout {\r
56         void rewind();\r
57         void setup(INotification n);\r
58     }\r
59 \r
60     public static final Key KEY_NOTIFICATION_LAYOUT = new KeyOf(NotificationLayout.class);\r
61 \r
62     class DefaultLayout implements NotificationLayout {\r
63         double x = 0;\r
64         double y = 0;\r
65         @Override\r
66         public void rewind() {\r
67             x = 0;\r
68             y = 0;\r
69         }\r
70         @Override\r
71         public void setup(INotification n) {\r
72         }\r
73     }\r
74 \r
75     class NotificationHandle implements INotificationHandle {\r
76         INotification not;\r
77         long duration;\r
78 \r
79         NotificationHandle(INotification not, long duration) {\r
80             this.not = not;\r
81             this.duration = duration;\r
82         }\r
83 \r
84         @Override\r
85         public void discard() {\r
86             clearNotification(this);\r
87         }\r
88 \r
89         public long elapsed(long currentTime) {\r
90             long elapsed = currentTime - not.getCreationTime();\r
91             return elapsed;\r
92         }\r
93 \r
94         @Override\r
95         public boolean hasElapsed() {\r
96             return elapsed(System.currentTimeMillis()) > duration;\r
97         }\r
98     }\r
99 \r
100     @Override\r
101     public void addedToContext(ICanvasContext ctx) {\r
102         super.addedToContext(ctx);\r
103         setHint(KEY_NOTIFICATION_LAYOUT, new DefaultLayout());\r
104     }\r
105 \r
106     @EventHandler(priority = 0)\r
107     public boolean handleTimer(TimeEvent event) {\r
108         //System.out.println("time event: " + event.time + " (" + event.interval + ")");\r
109         if (notifications.isEmpty()) {\r
110             time.unregisterForEvents(getClass());\r
111             return false;\r
112         }\r
113 \r
114         boolean changes = false;\r
115         Collection<NotificationHandle> removed = null;\r
116         for (Iterator<NotificationHandle> it = notifications.iterator(); it.hasNext();) {\r
117             NotificationHandle n = it.next();\r
118             long elapsed = n.elapsed(event.time);\r
119             if (elapsed > n.duration) {\r
120                 changes = true;\r
121                 if (removed == null)\r
122                     removed = new ArrayList<NotificationHandle>();\r
123                 removed.add(n);\r
124             } else {\r
125                 n.not.setProgress(progress(n, event.time));\r
126                 changes = true;\r
127             }\r
128         }\r
129         if (changes) {\r
130             if (removed != null)\r
131                 notifications.removeAll(removed);\r
132             updateNotifications();\r
133             setDirty();\r
134         }\r
135         return false;\r
136     }\r
137 \r
138     /**\r
139      * @param notification the notification to show\r
140      * @param duration duration to show the notification for in milliseconds\r
141      * @return\r
142      */\r
143     public INotificationHandle addNotification(INotification notification, long duration) {\r
144         assert getThread().currentThreadAccess();\r
145         NotificationHandle n = new NotificationHandle(notification, duration);\r
146         notifications.add(n);\r
147         // Make sure that timer events are received for as long as there are notifications.\r
148         time.registerForEvents(getClass());\r
149         setDirty();\r
150         return n;\r
151     }\r
152 \r
153     /**\r
154      * Clear the specified notification from sight immediately.\r
155      * \r
156      * @param handle the notification to remove\r
157      */\r
158     public void clearNotification(INotificationHandle handle) {\r
159         assert getThread().currentThreadAccess();\r
160         notifications.remove(handle);\r
161     }\r
162 \r
163     Rectangle2D r2d = new Rectangle2D.Double();\r
164 \r
165     G2DParentNode notificationNode;\r
166 \r
167     @SGInit(designation = SGDesignation.CONTROL)\r
168     public void initSG(G2DParentNode parent) {\r
169         notificationNode = parent.addNode("notifications", G2DParentNode.class);\r
170         notificationNode.setZIndex(PAINT_PRIORITY);\r
171     }\r
172 \r
173     @SGCleanup\r
174     public void cleanup() {\r
175         if (notificationNode != null) {\r
176             notificationNode.remove();\r
177             notificationNode = null;\r
178         }\r
179     }\r
180 \r
181     private final Set<Node> updated = new HashSet<Node>();\r
182 \r
183     public void updateNotifications() {\r
184         Rectangle2D cb = bounds.getControlBounds();\r
185 \r
186         double x = cb.getCenterX() + MARGIN;\r
187         double y = MARGIN;\r
188         double h = 100;\r
189 \r
190         updated.clear();\r
191 \r
192         for (NotificationHandle n : notifications) {\r
193             G2DParentNode node = notificationNode.getOrCreateNode("" + n.not.hashCode(), G2DParentNode.class);\r
194             updated.add(node);\r
195             r2d.setFrame(x, y, cb.getWidth() / 2 - 2*MARGIN, h);\r
196             //System.out.println("update notification: " + n + " : " + r2d);\r
197             n.not.setBounds(r2d);\r
198             n.not.update(node);\r
199             y += h + MARGIN;\r
200         }\r
201 \r
202         Collection<IG2DNode> nodes = notificationNode.getNodes();\r
203         IG2DNode[] nodesCopy = nodes.toArray(new IG2DNode[nodes.size()]);\r
204         for (IG2DNode node : nodesCopy) {\r
205             if (!updated.contains(node)) {\r
206                 node.remove();\r
207             }\r
208         }\r
209 \r
210         // Don't leave dangling references.\r
211         updated.clear();\r
212     }\r
213 \r
214     static double progress(NotificationHandle h, long currentTime) {\r
215         long elapsed = h.elapsed(currentTime);\r
216         return Math.min((double) elapsed / (double) h.duration, 1.0);\r
217     }\r
218 \r
219 //    @EventHandler(priority = 0)\r
220 //    public boolean handleKey(KeyPressedEvent e) {\r
221 //        if (e.character == 'n') {\r
222 //            //System.out.println("add notification");\r
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);\r
224 //        }\r
225 //        return false;\r
226 //    }\r
227 \r
228 }\r