]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/dnd/DragPainter.java
Take the transform hint of DragItem into account during drag
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / dnd / DragPainter.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.dnd;
13
14 import java.awt.AlphaComposite;
15 import java.awt.geom.AffineTransform;
16 import java.awt.geom.Point2D;
17 import java.awt.geom.Rectangle2D;
18
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.element.ElementHints;
25 import org.simantics.g2d.participant.TransformUtil;
26 import org.simantics.scenegraph.g2d.G2DParentNode;
27 import org.simantics.scenegraph.g2d.IG2DNode;
28 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
29 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
30 import org.simantics.scenegraph.g2d.events.command.Commands;
31 import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
32 import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;
33 import org.simantics.utils.datastructures.context.IContext;
34 import org.simantics.utils.datastructures.context.IContextListener;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 /**
39  * This participant paints an array of icons of a drag operation.
40  *
41  * @author Toni Kalajainen
42  */
43 public class DragPainter extends AbstractCanvasParticipant {
44     private static final Logger LOGGER = LoggerFactory.getLogger(DragPainter.class);
45     // Priority of drag event, eats ecs key
46     public final static int DRAG_EVENT_PRIORITY = Integer.MAX_VALUE - 1500;
47     public final static int DRAG_PAINT_PRIORITY = Integer.MAX_VALUE - 1100;
48     public final static int MARGIN = 5;
49
50     final IDnDContext       dropCtx;
51     Point2D                 mouseControlPos;
52
53     public DragPainter(IDnDContext dropCtx, Point2D mouseControlPos) {
54         super();
55         this.dropCtx = dropCtx;
56         this.mouseControlPos = (Point2D) mouseControlPos.clone();
57     }
58
59     IContextListener<IDragItem> contextListener = new IContextListener<IDragItem>() {
60         @Override
61         public void itemAdded(IContext<IDragItem> sender, IDragItem item) {
62             updateItemPositions();
63             getContext().getContentContext().setDirty();
64         }
65         @Override
66         public void itemRemoved(IContext<IDragItem> sender, IDragItem item) {
67             updateItemPositions();
68             getContext().getContentContext().setDirty();
69         }
70     };
71
72     @Override
73     public void addedToContext(ICanvasContext ctx) {
74         super.addedToContext(ctx);
75         dropCtx.addContextListener(getContext().getThreadAccess(), contextListener);
76     }
77
78     @Override
79     public void removedFromContext(ICanvasContext ctx) {
80         dropCtx.removeContextListener(getContext().getThreadAccess(), contextListener);
81         super.removedFromContext(ctx);
82     }
83
84     public boolean setMousePos(Point2D mouseControlPos) {
85         return setMousePos(mouseControlPos, false);
86     }
87
88     public boolean setMousePos(Point2D mouseControlPos, boolean forceUpdate) {
89         if (!forceUpdate && mouseControlPos.equals(this.mouseControlPos))
90             return false;
91         this.mouseControlPos = (Point2D) mouseControlPos.clone();
92         getContext().getContentContext().setDirty();
93         updateItemPositions();
94         return true;
95     }
96
97     /**
98      * Get the size of the largest drop item
99      * @param items
100      * @return
101      */
102     private Point2D calcItemSize(IDragItem[] items)
103     {
104         double w = Double.MIN_VALUE;
105         double h = Double.MIN_VALUE;
106         for (IDragItem i : items) {
107             Rectangle2D bounds = i.getBounds();
108             if (bounds == null) bounds = new Rectangle2D.Double(0,0,1,1);
109             if (w < bounds.getWidth())
110                 w = bounds.getWidth();
111             if (h < bounds.getHeight())
112                 h = bounds.getHeight();
113         }
114         return new Point2D.Double(w, h);
115     }
116
117     private void updateItemPositions() {
118         // Calculate mouse position on diagram
119         TransformUtil vi = getContext().getSingleItem(TransformUtil.class);
120         AffineTransform controlToDiagram = vi.getInverseTransform();
121         if (controlToDiagram==null) return;
122         Point2D mouseDiagramPos = controlToDiagram.transform(mouseControlPos, new Point2D.Double());
123
124         //System.out.println("updateItemPositions: mousepos: " + mouseControlPos + " - " + mouseDiagramPos);
125
126         IDragItem items[] = dropCtx.toArray();
127         int count = items.length;
128         int columns = (int) Math.ceil( Math.sqrt( count) );
129         Integer columnsHint = dropCtx.getHints().getHint(DnDHints.KEY_DND_GRID_COLUMNS);
130         if (columnsHint != null)
131             columns = columnsHint;
132         int rows = (int) Math.ceil((double)count / (double)columns);
133         int margin = MARGIN;
134         // Size of the largest item
135         Point2D size = calcItemSize(items);
136
137         // Calculate center point of the whole item mass
138         double cx = ((columns-1)*(size.getX()+margin))/2 + size.getX()/2;
139         double cy = ((rows   -1)*(size.getY()+margin))/2 + size.getY()/2;
140         //double cx = ((columns)*(size.getX()+margin)-margin)/2 /*- size.getX()/2*/;
141         //double cy = ((rows   )*(size.getY()+margin)-margin)/2 /*- size.getY()/2*/;
142         //double cx = 0;
143         //double cy = 0;
144
145         int index = 0;
146         for (IDragItem i : items) {
147             Rectangle2D bounds = i.getBounds();
148             double x = (index % columns) * (margin + size.getX());
149             double y = (index / columns) * (margin + size.getY());
150 //            Point2D pos = new Point2D.Double(x, y);
151             index++;
152
153             x -= bounds.getX();
154             y -= bounds.getY();
155             x += mouseDiagramPos.getX();
156             y += mouseDiagramPos.getY();
157
158             x -= cx;
159             y -= cy;
160
161             Point2D p = new Point2D.Double(x, y);
162             ISnapAdvisor snap = getHint(DiagramHints.SNAP_ADVISOR);
163             if (snap != null)
164                 snap.snap(p);
165             dropCtx.setItemPosition(i, p);
166         }
167         updateSG();
168     }
169
170     protected G2DParentNode node = null;
171
172     @SGInit
173     public void initSG(G2DParentNode parent) {
174         node = parent.addNode(G2DParentNode.class);
175         node.setZIndex(DRAG_PAINT_PRIORITY);
176         updateSG();
177     }
178
179     @SGCleanup
180     public void cleanupSG() {
181         node.remove();
182         node = null;
183     }
184
185     private void updateSG() {
186         IDragItem items[] = dropCtx.toArray();
187 //        updateItemPositions();
188         for (IDragItem item : items) {
189             Point2D pos = dropCtx.getItemPosition(item);
190             if(pos != null) { // Position can (or at least seems to be) be null on the first frame
191                 AffineTransform subt = AffineTransform.getTranslateInstance(pos.getX(), pos.getY());
192                 AffineTransform at = item.getHintContext().getHint(ElementHints.KEY_TRANSFORM);
193                 if (at != null) {
194                     subt.concatenate(at);
195                 }
196
197                 IG2DNode itemHolder = node.getNode(""+item.hashCode());
198                 if (itemHolder != null && !(itemHolder instanceof SingleElementNode)) {
199                     LOGGER.error("BUG: item hash codes collide within the dragged item context - found unrecognized item " + itemHolder + " with node id '" + item.hashCode() + "'");
200                     continue;
201                 }
202
203                 SingleElementNode elementHolder = (SingleElementNode) itemHolder;
204                 if (elementHolder == null) {
205                     elementHolder = node.getOrCreateNode(""+item.hashCode(), SingleElementNode.class);
206                     elementHolder.setTransform(subt);
207                     elementHolder.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.75f));
208                     item.paint(elementHolder);
209                 } else {
210                     elementHolder.setTransform(subt);
211                 }
212             }
213         }
214     }
215
216     @EventHandler(priority = DRAG_EVENT_PRIORITY)
217     public boolean handleEvent(CommandEvent e) {
218         if (e.command.equals( Commands.CANCEL) )
219         {
220             remove();
221             return true;
222         }
223         return false;
224     }
225 }