]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/dnd/DragPainter.java
feb712cd31865f107166cb487cd798161e0b6e40
[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.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;
36
37 /**
38  * This participant paints an array of icons of a drag operation.
39  *
40  * @author Toni Kalajainen
41  */
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;
48
49     final IDnDContext       dropCtx;
50     Point2D                 mouseControlPos;
51
52     public DragPainter(IDnDContext dropCtx, Point2D mouseControlPos) {
53         super();
54         this.dropCtx = dropCtx;
55         this.mouseControlPos = (Point2D) mouseControlPos.clone();
56     }
57
58     IContextListener<IDragItem> contextListener = new IContextListener<IDragItem>() {
59         @Override
60         public void itemAdded(IContext<IDragItem> sender, IDragItem item) {
61             updateItemPositions();
62             getContext().getContentContext().setDirty();
63         }
64         @Override
65         public void itemRemoved(IContext<IDragItem> sender, IDragItem item) {
66             updateItemPositions();
67             getContext().getContentContext().setDirty();
68         }
69     };
70
71     @Override
72     public void addedToContext(ICanvasContext ctx) {
73         super.addedToContext(ctx);
74         dropCtx.addContextListener(getContext().getThreadAccess(), contextListener);
75     }
76
77     @Override
78     public void removedFromContext(ICanvasContext ctx) {
79         dropCtx.removeContextListener(getContext().getThreadAccess(), contextListener);
80         super.removedFromContext(ctx);
81     }
82
83     public boolean setMousePos(Point2D mouseControlPos) {
84         return setMousePos(mouseControlPos, false);
85     }
86
87     public boolean setMousePos(Point2D mouseControlPos, boolean forceUpdate) {
88         if (!forceUpdate && mouseControlPos.equals(this.mouseControlPos))
89             return false;
90         this.mouseControlPos = (Point2D) mouseControlPos.clone();
91         getContext().getContentContext().setDirty();
92         updateItemPositions();
93         return true;
94     }
95
96     /**
97      * Get the size of the largest drop item
98      * @param items
99      * @return
100      */
101     private Point2D calcItemSize(IDragItem[] items)
102     {
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();
112         }
113         return new Point2D.Double(w, h);
114     }
115
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());
122
123         //System.out.println("updateItemPositions: mousepos: " + mouseControlPos + " - " + mouseDiagramPos);
124
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);
132         int margin = MARGIN;
133         // Size of the largest item
134         Point2D size = calcItemSize(items);
135
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*/;
141         //double cx = 0;
142         //double cy = 0;
143
144         int index = 0;
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);
150             index++;
151
152             x -= bounds.getX();
153             y -= bounds.getY();
154             x += mouseDiagramPos.getX();
155             y += mouseDiagramPos.getY();
156
157             x -= cx;
158             y -= cy;
159
160             Point2D p = new Point2D.Double(x, y);
161             ISnapAdvisor snap = getHint(DiagramHints.SNAP_ADVISOR);
162             if (snap != null)
163                 snap.snap(p);
164             dropCtx.setItemPosition(i, p);
165         }
166         updateSG();
167     }
168
169     protected G2DParentNode node = null;
170
171     @SGInit
172     public void initSG(G2DParentNode parent) {
173         node = parent.addNode(G2DParentNode.class);
174         node.setZIndex(DRAG_PAINT_PRIORITY);
175         updateSG();
176     }
177
178     @SGCleanup
179     public void cleanupSG() {
180         node.remove();
181         node = null;
182     }
183
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());
191
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() + "'");
195                     continue;
196                 }
197
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);
204                 } else {
205                     elementHolder.setTransform(subt);
206                 }
207             }
208         }
209     }
210
211     @EventHandler(priority = DRAG_EVENT_PRIORITY)
212     public boolean handleEvent(CommandEvent e) {
213         if (e.command.equals( Commands.CANCEL) )
214         {
215             remove();
216             return true;
217         }
218         return false;
219     }
220 }