]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/participant/Notifications.java
Let the request processor handle the exceptions
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / participant / Notifications.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.g2d.participant;
13
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;
19 import java.util.Set;
20 import java.util.concurrent.CopyOnWriteArrayList;
21
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;
38
39 /**
40  * @author Tuukka Lehtonen
41  */
42 public class Notifications extends AbstractCanvasParticipant {
43
44     static final double MARGIN = 12;
45
46     @Reference RulerPainter ruler;
47     @Dependency TransformUtil util;;
48     @Dependency CanvasBoundsParticipant bounds;
49     @Dependency TimeParticipant time;
50
51     CopyOnWriteArrayList<NotificationHandle> notifications = new CopyOnWriteArrayList<NotificationHandle>();
52
53     public static final int PAINT_PRIORITY = Integer.MAX_VALUE - 100;
54
55     public interface NotificationLayout {
56         void rewind();
57         void setup(INotification n);
58     }
59
60     public static final Key KEY_NOTIFICATION_LAYOUT = new KeyOf(NotificationLayout.class);
61
62     class DefaultLayout implements NotificationLayout {
63         double x = 0;
64         double y = 0;
65         @Override
66         public void rewind() {
67             x = 0;
68             y = 0;
69         }
70         @Override
71         public void setup(INotification n) {
72         }
73     }
74
75     class NotificationHandle implements INotificationHandle {
76         INotification not;
77         long duration;
78
79         NotificationHandle(INotification not, long duration) {
80             this.not = not;
81             this.duration = duration;
82         }
83
84         @Override
85         public void discard() {
86             clearNotification(this);
87         }
88
89         public long elapsed(long currentTime) {
90             long elapsed = currentTime - not.getCreationTime();
91             return elapsed;
92         }
93
94         @Override
95         public boolean hasElapsed() {
96             return elapsed(System.currentTimeMillis()) > duration;
97         }
98     }
99
100     @Override
101     public void addedToContext(ICanvasContext ctx) {
102         super.addedToContext(ctx);
103         setHint(KEY_NOTIFICATION_LAYOUT, new DefaultLayout());
104     }
105
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());
111             return false;
112         }
113
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) {
120                 changes = true;
121                 if (removed == null)
122                     removed = new ArrayList<NotificationHandle>();
123                 removed.add(n);
124             } else {
125                 n.not.setProgress(progress(n, event.time));
126                 changes = true;
127             }
128         }
129         if (changes) {
130             if (removed != null)
131                 notifications.removeAll(removed);
132             updateNotifications();
133             setDirty();
134         }
135         return false;
136     }
137
138     /**
139      * @param notification the notification to show
140      * @param duration duration to show the notification for in milliseconds
141      * @return
142      */
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());
149         setDirty();
150         return n;
151     }
152
153     /**
154      * Clear the specified notification from sight immediately.
155      * 
156      * @param handle the notification to remove
157      */
158     public void clearNotification(INotificationHandle handle) {
159         assert getThread().currentThreadAccess();
160         notifications.remove(handle);
161     }
162
163     Rectangle2D r2d = new Rectangle2D.Double();
164
165     G2DParentNode notificationNode;
166
167     @SGInit(designation = SGDesignation.CONTROL)
168     public void initSG(G2DParentNode parent) {
169         notificationNode = parent.addNode("notifications", G2DParentNode.class);
170         notificationNode.setZIndex(PAINT_PRIORITY);
171     }
172
173     @SGCleanup
174     public void cleanup() {
175         if (notificationNode != null) {
176             notificationNode.remove();
177             notificationNode = null;
178         }
179     }
180
181     private final Set<Node> updated = new HashSet<Node>();
182
183     public void updateNotifications() {
184         Rectangle2D cb = bounds.getControlBounds();
185
186         double x = cb.getCenterX() + MARGIN;
187         double y = MARGIN;
188         double h = 100;
189
190         updated.clear();
191
192         for (NotificationHandle n : notifications) {
193             G2DParentNode node = notificationNode.getOrCreateNode("" + n.not.hashCode(), G2DParentNode.class);
194             updated.add(node);
195             r2d.setFrame(x, y, cb.getWidth() / 2 - 2*MARGIN, h);
196             //System.out.println("update notification: " + n + " : " + r2d);
197             n.not.setBounds(r2d);
198             n.not.update(node);
199             y += h + MARGIN;
200         }
201
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)) {
206                 node.remove();
207             }
208         }
209
210         // Don't leave dangling references.
211         updated.clear();
212     }
213
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);
217     }
218
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);
224 //        }
225 //        return false;
226 //    }
227
228 }