/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * 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: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.g2d.participant; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.canvas.SGDesignation; import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant; import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency; import org.simantics.g2d.canvas.impl.DependencyReflection.Reference; import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup; import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit; import org.simantics.g2d.notification.INotification; import org.simantics.g2d.notification.INotificationHandle; import org.simantics.scenegraph.Node; import org.simantics.scenegraph.g2d.G2DParentNode; import org.simantics.scenegraph.g2d.IG2DNode; import org.simantics.scenegraph.g2d.events.TimeEvent; import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; /** * @author Tuukka Lehtonen */ public class Notifications extends AbstractCanvasParticipant { static final double MARGIN = 12; @Reference RulerPainter ruler; @Dependency TransformUtil util;; @Dependency CanvasBoundsParticipant bounds; @Dependency TimeParticipant time; CopyOnWriteArrayList notifications = new CopyOnWriteArrayList(); public static final int PAINT_PRIORITY = Integer.MAX_VALUE - 100; public interface NotificationLayout { void rewind(); void setup(INotification n); } public static final Key KEY_NOTIFICATION_LAYOUT = new KeyOf(NotificationLayout.class); class DefaultLayout implements NotificationLayout { double x = 0; double y = 0; @Override public void rewind() { x = 0; y = 0; } @Override public void setup(INotification n) { } } class NotificationHandle implements INotificationHandle { INotification not; long duration; NotificationHandle(INotification not, long duration) { this.not = not; this.duration = duration; } @Override public void discard() { clearNotification(this); } public long elapsed(long currentTime) { long elapsed = currentTime - not.getCreationTime(); return elapsed; } @Override public boolean hasElapsed() { return elapsed(System.currentTimeMillis()) > duration; } } @Override public void addedToContext(ICanvasContext ctx) { super.addedToContext(ctx); setHint(KEY_NOTIFICATION_LAYOUT, new DefaultLayout()); } @EventHandler(priority = 0) public boolean handleTimer(TimeEvent event) { //System.out.println("time event: " + event.time + " (" + event.interval + ")"); if (notifications.isEmpty()) { time.unregisterForEvents(getClass()); return false; } boolean changes = false; Collection removed = null; for (Iterator it = notifications.iterator(); it.hasNext();) { NotificationHandle n = it.next(); long elapsed = n.elapsed(event.time); if (elapsed > n.duration) { changes = true; if (removed == null) removed = new ArrayList(); removed.add(n); } else { n.not.setProgress(progress(n, event.time)); changes = true; } } if (changes) { if (removed != null) notifications.removeAll(removed); updateNotifications(); setDirty(); } return false; } /** * @param notification the notification to show * @param duration duration to show the notification for in milliseconds * @return */ public INotificationHandle addNotification(INotification notification, long duration) { assert getThread().currentThreadAccess(); NotificationHandle n = new NotificationHandle(notification, duration); notifications.add(n); // Make sure that timer events are received for as long as there are notifications. time.registerForEvents(getClass()); setDirty(); return n; } /** * Clear the specified notification from sight immediately. * * @param handle the notification to remove */ public void clearNotification(INotificationHandle handle) { assert getThread().currentThreadAccess(); notifications.remove(handle); } Rectangle2D r2d = new Rectangle2D.Double(); G2DParentNode notificationNode; @SGInit(designation = SGDesignation.CONTROL) public void initSG(G2DParentNode parent) { notificationNode = parent.addNode("notifications", G2DParentNode.class); notificationNode.setZIndex(PAINT_PRIORITY); } @SGCleanup public void cleanup() { if (notificationNode != null) { notificationNode.remove(); notificationNode = null; } } private final Set updated = new HashSet(); public void updateNotifications() { Rectangle2D cb = bounds.getControlBounds(); double x = cb.getCenterX() + MARGIN; double y = MARGIN; double h = 100; updated.clear(); for (NotificationHandle n : notifications) { G2DParentNode node = notificationNode.getOrCreateNode("" + n.not.hashCode(), G2DParentNode.class); updated.add(node); r2d.setFrame(x, y, cb.getWidth() / 2 - 2*MARGIN, h); //System.out.println("update notification: " + n + " : " + r2d); n.not.setBounds(r2d); n.not.update(node); y += h + MARGIN; } Collection nodes = notificationNode.getNodes(); IG2DNode[] nodesCopy = nodes.toArray(new IG2DNode[nodes.size()]); for (IG2DNode node : nodesCopy) { if (!updated.contains(node)) { node.remove(); } } // Don't leave dangling references. updated.clear(); } static double progress(NotificationHandle h, long currentTime) { long elapsed = h.elapsed(currentTime); return Math.min((double) elapsed / (double) h.duration, 1.0); } // @EventHandler(priority = 0) // public boolean handleKey(KeyPressedEvent e) { // if (e.character == 'n') { // //System.out.println("add notification"); // 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); // } // return false; // } }