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.dnd;
14 import java.awt.AlphaComposite;
15 import java.awt.geom.AffineTransform;
16 import java.awt.geom.Point2D;
17 import java.awt.geom.Rectangle2D;
19 import org.simantics.g2d.canvas.ICanvasContext;
20 import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;
21 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;
22 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;
23 import org.simantics.g2d.diagram.DiagramHints;
24 import org.simantics.g2d.participant.TransformUtil;
25 import org.simantics.scenegraph.g2d.G2DParentNode;
26 import org.simantics.scenegraph.g2d.IG2DNode;
27 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
28 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
29 import org.simantics.scenegraph.g2d.events.command.Commands;
30 import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
31 import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;
32 import org.simantics.utils.datastructures.context.IContext;
33 import org.simantics.utils.datastructures.context.IContextListener;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
38 * This participant paints an array of icons of a drag operation.
40 * @author Toni Kalajainen
42 public class DragPainter extends AbstractCanvasParticipant {
43 private static final Logger LOGGER = LoggerFactory.getLogger(DragPainter.class);
44 // Priority of drag event, eats ecs key
45 public final static int DRAG_EVENT_PRIORITY = Integer.MAX_VALUE - 1500;
46 public final static int DRAG_PAINT_PRIORITY = Integer.MAX_VALUE - 1100;
47 public final static int MARGIN = 5;
49 final IDnDContext dropCtx;
50 Point2D mouseControlPos;
52 public DragPainter(IDnDContext dropCtx, Point2D mouseControlPos) {
54 this.dropCtx = dropCtx;
55 this.mouseControlPos = (Point2D) mouseControlPos.clone();
58 IContextListener<IDragItem> contextListener = new IContextListener<IDragItem>() {
60 public void itemAdded(IContext<IDragItem> sender, IDragItem item) {
61 updateItemPositions();
62 getContext().getContentContext().setDirty();
65 public void itemRemoved(IContext<IDragItem> sender, IDragItem item) {
66 updateItemPositions();
67 getContext().getContentContext().setDirty();
72 public void addedToContext(ICanvasContext ctx) {
73 super.addedToContext(ctx);
74 dropCtx.addContextListener(getContext().getThreadAccess(), contextListener);
78 public void removedFromContext(ICanvasContext ctx) {
79 dropCtx.removeContextListener(getContext().getThreadAccess(), contextListener);
80 super.removedFromContext(ctx);
83 public boolean setMousePos(Point2D mouseControlPos) {
84 return setMousePos(mouseControlPos, false);
87 public boolean setMousePos(Point2D mouseControlPos, boolean forceUpdate) {
88 if (!forceUpdate && mouseControlPos.equals(this.mouseControlPos))
90 this.mouseControlPos = (Point2D) mouseControlPos.clone();
91 getContext().getContentContext().setDirty();
92 updateItemPositions();
97 * Get the size of the largest drop item
101 private Point2D calcItemSize(IDragItem[] items)
103 double w = Double.MIN_VALUE;
104 double h = Double.MIN_VALUE;
105 for (IDragItem i : items) {
106 Rectangle2D bounds = i.getBounds();
107 if (bounds == null) bounds = new Rectangle2D.Double(0,0,1,1);
108 if (w < bounds.getWidth())
109 w = bounds.getWidth();
110 if (h < bounds.getHeight())
111 h = bounds.getHeight();
113 return new Point2D.Double(w, h);
116 private void updateItemPositions() {
117 // Calculate mouse position on diagram
118 TransformUtil vi = getContext().getSingleItem(TransformUtil.class);
119 AffineTransform controlToDiagram = vi.getInverseTransform();
120 if (controlToDiagram==null) return;
121 Point2D mouseDiagramPos = controlToDiagram.transform(mouseControlPos, new Point2D.Double());
123 //System.out.println("updateItemPositions: mousepos: " + mouseControlPos + " - " + mouseDiagramPos);
125 IDragItem items[] = dropCtx.toArray();
126 int count = items.length;
127 int columns = (int) Math.ceil( Math.sqrt( count) );
128 Integer columnsHint = dropCtx.getHints().getHint(DnDHints.KEY_DND_GRID_COLUMNS);
129 if (columnsHint != null)
130 columns = columnsHint;
131 int rows = (int) Math.ceil((double)count / (double)columns);
133 // Size of the largest item
134 Point2D size = calcItemSize(items);
136 // Calculate center point of the whole item mass
137 double cx = ((columns-1)*(size.getX()+margin))/2 + size.getX()/2;
138 double cy = ((rows -1)*(size.getY()+margin))/2 + size.getY()/2;
139 //double cx = ((columns)*(size.getX()+margin)-margin)/2 /*- size.getX()/2*/;
140 //double cy = ((rows )*(size.getY()+margin)-margin)/2 /*- size.getY()/2*/;
145 for (IDragItem i : items) {
146 Rectangle2D bounds = i.getBounds();
147 double x = (index % columns) * (margin + size.getX());
148 double y = (index / columns) * (margin + size.getY());
149 // Point2D pos = new Point2D.Double(x, y);
154 x += mouseDiagramPos.getX();
155 y += mouseDiagramPos.getY();
160 Point2D p = new Point2D.Double(x, y);
161 ISnapAdvisor snap = getHint(DiagramHints.SNAP_ADVISOR);
164 dropCtx.setItemPosition(i, p);
169 protected G2DParentNode node = null;
172 public void initSG(G2DParentNode parent) {
173 node = parent.addNode(G2DParentNode.class);
174 node.setZIndex(DRAG_PAINT_PRIORITY);
179 public void cleanupSG() {
184 private void updateSG() {
185 IDragItem items[] = dropCtx.toArray();
186 // updateItemPositions();
187 for (IDragItem item : items) {
188 Point2D pos = dropCtx.getItemPosition(item);
189 if(pos != null) { // Position can (or at least seems to be) be null on the first frame
190 AffineTransform subt = AffineTransform.getTranslateInstance(pos.getX(), pos.getY());
192 IG2DNode itemHolder = node.getNode(""+item.hashCode());
193 if (itemHolder != null && !(itemHolder instanceof SingleElementNode)) {
194 LOGGER.error("BUG: item hash codes collide within the dragged item context - found unrecognized item " + itemHolder + " with node id '" + item.hashCode() + "'");
198 SingleElementNode elementHolder = (SingleElementNode) itemHolder;
199 if (elementHolder == null) {
200 elementHolder = node.getOrCreateNode(""+item.hashCode(), SingleElementNode.class);
201 elementHolder.setTransform(subt);
202 elementHolder.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.75f));
203 item.paint(elementHolder);
205 elementHolder.setTransform(subt);
211 @EventHandler(priority = DRAG_EVENT_PRIORITY)
212 public boolean handleEvent(CommandEvent e) {
213 if (e.command.equals( Commands.CANCEL) )