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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.g2d.dnd;
\r
14 import java.awt.AlphaComposite;
\r
15 import java.awt.geom.AffineTransform;
\r
16 import java.awt.geom.Point2D;
\r
17 import java.awt.geom.Rectangle2D;
\r
19 import org.simantics.g2d.canvas.ICanvasContext;
\r
20 import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;
\r
21 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;
\r
22 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;
\r
23 import org.simantics.g2d.diagram.DiagramHints;
\r
24 import org.simantics.g2d.participant.TransformUtil;
\r
25 import org.simantics.scenegraph.g2d.G2DParentNode;
\r
26 import org.simantics.scenegraph.g2d.IG2DNode;
\r
27 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
\r
28 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
\r
29 import org.simantics.scenegraph.g2d.events.command.Commands;
\r
30 import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
\r
31 import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;
\r
32 import org.simantics.utils.datastructures.context.IContext;
\r
33 import org.simantics.utils.datastructures.context.IContextListener;
\r
36 * This participant paints an array of icons of a drag operation.
\r
38 * @author Toni Kalajainen
\r
40 public class DragPainter extends AbstractCanvasParticipant {
\r
41 // Priority of drag event, eats ecs key
\r
42 public final static int DRAG_EVENT_PRIORITY = Integer.MAX_VALUE - 1500;
\r
43 public final static int DRAG_PAINT_PRIORITY = Integer.MAX_VALUE - 1100;
\r
44 public final static int MARGIN = 5;
\r
46 final IDnDContext dropCtx;
\r
47 Point2D mouseControlPos;
\r
49 public DragPainter(IDnDContext dropCtx, Point2D mouseControlPos) {
\r
51 this.dropCtx = dropCtx;
\r
52 this.mouseControlPos = (Point2D) mouseControlPos.clone();
\r
55 IContextListener<IDragItem> contextListener = new IContextListener<IDragItem>() {
\r
57 public void itemAdded(IContext<IDragItem> sender, IDragItem item) {
\r
58 updateItemPositions();
\r
59 getContext().getContentContext().setDirty();
\r
62 public void itemRemoved(IContext<IDragItem> sender, IDragItem item) {
\r
63 updateItemPositions();
\r
64 getContext().getContentContext().setDirty();
\r
69 public void addedToContext(ICanvasContext ctx) {
\r
70 super.addedToContext(ctx);
\r
71 dropCtx.addContextListener(getContext().getThreadAccess(), contextListener);
\r
75 public void removedFromContext(ICanvasContext ctx) {
\r
76 dropCtx.removeContextListener(getContext().getThreadAccess(), contextListener);
\r
77 super.removedFromContext(ctx);
\r
80 public boolean setMousePos(Point2D mouseControlPos) {
\r
81 return setMousePos(mouseControlPos, false);
\r
84 public boolean setMousePos(Point2D mouseControlPos, boolean forceUpdate) {
\r
85 if (!forceUpdate && mouseControlPos.equals(this.mouseControlPos))
\r
87 this.mouseControlPos = (Point2D) mouseControlPos.clone();
\r
88 getContext().getContentContext().setDirty();
\r
89 updateItemPositions();
\r
94 * Get the size of the largest drop item
\r
98 private Point2D calcItemSize(IDragItem[] items)
\r
100 double w = Double.MIN_VALUE;
\r
101 double h = Double.MIN_VALUE;
\r
102 for (IDragItem i : items) {
\r
103 Rectangle2D bounds = i.getBounds();
\r
104 if (bounds == null) bounds = new Rectangle2D.Double(0,0,1,1);
\r
105 if (w < bounds.getWidth())
\r
106 w = bounds.getWidth();
\r
107 if (h < bounds.getHeight())
\r
108 h = bounds.getHeight();
\r
110 return new Point2D.Double(w, h);
\r
113 private void updateItemPositions() {
\r
114 // Calculate mouse position on diagram
\r
115 TransformUtil vi = getContext().getSingleItem(TransformUtil.class);
\r
116 AffineTransform controlToDiagram = vi.getInverseTransform();
\r
117 if (controlToDiagram==null) return;
\r
118 Point2D mouseDiagramPos = controlToDiagram.transform(mouseControlPos, new Point2D.Double());
\r
120 //System.out.println("updateItemPositions: mousepos: " + mouseControlPos + " - " + mouseDiagramPos);
\r
122 IDragItem items[] = dropCtx.toArray();
\r
123 int count = items.length;
\r
124 int columns = (int) Math.ceil( Math.sqrt( count) );
\r
125 Integer columnsHint = dropCtx.getHints().getHint(DnDHints.KEY_DND_GRID_COLUMNS);
\r
126 if (columnsHint != null)
\r
127 columns = columnsHint;
\r
128 int rows = (int) Math.ceil((double)count / (double)columns);
\r
129 int margin = MARGIN;
\r
130 // Size of the largest item
\r
131 Point2D size = calcItemSize(items);
\r
133 // Calculate center point of the whole item mass
\r
134 double cx = ((columns-1)*(size.getX()+margin))/2 + size.getX()/2;
\r
135 double cy = ((rows -1)*(size.getY()+margin))/2 + size.getY()/2;
\r
136 //double cx = ((columns)*(size.getX()+margin)-margin)/2 /*- size.getX()/2*/;
\r
137 //double cy = ((rows )*(size.getY()+margin)-margin)/2 /*- size.getY()/2*/;
\r
142 for (IDragItem i : items) {
\r
143 Rectangle2D bounds = i.getBounds();
\r
144 double x = (index % columns) * (margin + size.getX());
\r
145 double y = (index / columns) * (margin + size.getY());
\r
146 // Point2D pos = new Point2D.Double(x, y);
\r
149 x -= bounds.getX();
\r
150 y -= bounds.getY();
\r
151 x += mouseDiagramPos.getX();
\r
152 y += mouseDiagramPos.getY();
\r
157 Point2D p = new Point2D.Double(x, y);
\r
158 ISnapAdvisor snap = getHint(DiagramHints.SNAP_ADVISOR);
\r
161 dropCtx.setItemPosition(i, p);
\r
166 protected G2DParentNode node = null;
\r
169 public void initSG(G2DParentNode parent) {
\r
170 node = parent.addNode(G2DParentNode.class);
\r
171 node.setZIndex(DRAG_PAINT_PRIORITY);
\r
176 public void cleanupSG() {
\r
181 private void updateSG() {
\r
182 IDragItem items[] = dropCtx.toArray();
\r
183 // updateItemPositions();
\r
184 for (IDragItem item : items) {
\r
185 Point2D pos = dropCtx.getItemPosition(item);
\r
186 if(pos != null) { // Position can (or at least seems to be) be null on the first frame
\r
187 AffineTransform subt = AffineTransform.getTranslateInstance(pos.getX(), pos.getY());
\r
189 IG2DNode itemHolder = node.getNode(""+item.hashCode());
\r
190 if (itemHolder != null && !(itemHolder instanceof SingleElementNode)) {
\r
191 System.err.println("BUG: item hash codes collide within the dragged item context - found unrecognized item " + itemHolder + " with node id '" + item.hashCode() + "'");
\r
195 SingleElementNode elementHolder = (SingleElementNode) itemHolder;
\r
196 if (elementHolder == null) {
\r
197 elementHolder = node.getOrCreateNode(""+item.hashCode(), SingleElementNode.class);
\r
198 elementHolder.setTransform(subt);
\r
199 elementHolder.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.75f));
\r
200 item.paint(elementHolder);
\r
202 elementHolder.setTransform(subt);
\r
208 @EventHandler(priority = DRAG_EVENT_PRIORITY)
\r
209 public boolean handleEvent(CommandEvent e) {
\r
210 if (e.command.equals( Commands.CANCEL) )
\r