]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextGridNode.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / elements / TextGridNode.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2017 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.diagram.elements;
13
14 import gnu.trove.map.TIntObjectMap;
15 import gnu.trove.map.TMap;
16 import gnu.trove.map.hash.THashMap;
17 import gnu.trove.map.hash.TIntObjectHashMap;
18 import gnu.trove.procedure.TObjectProcedure;
19
20 import java.awt.Color;
21 import java.awt.Font;
22 import java.awt.Graphics2D;
23 import java.awt.geom.AffineTransform;
24 import java.awt.geom.Point2D;
25 import java.awt.geom.Rectangle2D;
26 import java.util.ArrayList;
27 import java.util.List;
28
29 import org.simantics.datatypes.literal.Vec2d;
30 import org.simantics.db.layer0.variable.RVI;
31 import org.simantics.diagram.profile.MonitorTextGridResult;
32 import org.simantics.scenegraph.ExportableWidget.OutputWidget;
33 import org.simantics.scenegraph.g2d.G2DParentNode;
34 import org.simantics.scenegraph.g2d.G2DSceneGraph;
35 import org.simantics.scenegraph.g2d.events.EventTypes;
36 import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
37 import org.simantics.scenegraph.g2d.events.MouseEvent;
38 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;
39 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;
40 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;
41 import org.simantics.scenegraph.g2d.nodes.Decoration;
42 import org.simantics.scenegraph.utils.NodeUtil;
43 import org.simantics.scl.runtime.function.Function1;
44
45 /**
46  * TODO: does not work remotely, e.g. rowIds 
47  */
48 @OutputWidget("textGrid")
49 public class TextGridNode extends G2DParentNode implements Decoration {
50
51     public static class A extends TextNode {
52
53         transient MonitorTextGridResult cache = null;
54         
55         private static final long serialVersionUID = -4519849713591842241L;
56
57         public MonitorTextGridResult getCache() {
58                 return cache;
59         }
60         
61         public void setCache(MonitorTextGridResult cache) {
62                 this.cache = cache;
63         }
64
65     }
66
67     private static final int CACHED_COLUMNS = 4;
68     private static final int CACHED_ROWS = 10;
69
70     private static Cell[][] cache;
71
72     static {
73         cache = new Cell[CACHED_ROWS][];
74         for (int row = 0; row < CACHED_ROWS; ++row) {
75             cache[row] = new Cell[CACHED_COLUMNS];
76             for (int col = 0; col < CACHED_COLUMNS; ++col) {
77                 cache[row][col] = new Cell(col+1, row+1);
78             }
79         }
80     }
81
82     private static String makeId(int x, int y) {
83         return x + "#" + y;
84     }
85
86     static class Cell {
87
88         public final int x;
89         public final int y;
90
91         public Cell(int x, int y) {
92             this.x = x;
93             this.y = y;
94         }
95
96         @Override
97         public boolean equals(Object object) {
98             if (this == object)
99                 return true;
100             else if (object == null)
101                 return false;
102             else if (Cell.class != object.getClass())
103                 return false;
104             Cell p = (Cell)object;
105             return x == p.x && y == p.y;
106         }
107
108         @Override
109         public int hashCode() {
110             return x *31 + y;
111         }
112
113         @Override
114         public String toString() {
115             return makeId(x, y);
116         }
117
118         public static Cell make(int x, int y) {
119             if (x >= 1 && x <= CACHED_COLUMNS && y >= 1 && y <= CACHED_ROWS)
120                 return cache[y-1][x-1];
121             return new Cell(x,y);
122         }
123     }
124
125     private static final long serialVersionUID = 7015425802228571055L;
126
127     private TMap<Cell, A> nodes = new THashMap<Cell, A>();
128     private TIntObjectMap<String> rowIds = new TIntObjectHashMap<>();
129     private boolean up = true;
130
131     static class MaxY implements TObjectProcedure<Cell> {
132         int max = 0;
133         @Override
134         public boolean execute(Cell key) {
135             if (key.y > max)
136                 max = key.y;
137             return true;
138         }
139     }
140
141     public int computeRows() {
142         MaxY maxy = new MaxY();
143         nodes.forEachKey(maxy);
144         return maxy.max;
145     }
146
147     private List<Cell> peekRowCells(int y) {
148         ArrayList<Cell> row = new ArrayList<Cell>(4);
149         for (Cell key : nodes.keySet())
150             if (key.y == y)
151                 row.add(key);
152         return row;
153     }
154     
155     public void setUp(boolean up) {
156         this.up = up;
157     }
158
159     public A get(int x, int y) {
160         Cell p = Cell.make(x, y);
161         A node = nodes.get(p);
162         if(node == null) {
163             //System.out.println(" create(" + x + "," + y + ")");
164             node = getOrCreateNode(p.toString(), A.class);
165             node.setZIndex(x + (y-1)*100);
166             nodes.put(p, node);
167         } else {
168             //System.out.println(" get(" + x + "," + y + ")");
169         }
170         return node;
171     }
172
173     public MonitorTextGridResult getCache(int x, int y) {
174         return get(x,y).getCache();
175     }
176     
177     public void setCache(int x, int y, MonitorTextGridResult cache) {
178         get(x,y).setCache(cache);
179     }
180
181     public void setTransform(int x, int y, AffineTransform transform) {
182         get(x,y).setTransform(transform);
183         dragBegin = null;
184         currentDrag = null;
185     }
186
187     public void setHorizontalAlignment(int x, int y, byte horizontalAlignment) {
188         get(x,y).setHorizontalAlignment(horizontalAlignment);
189     }
190
191     public void setVerticalAlignment(int x, int y, byte verticalAlignment) {
192         get(x,y).setVerticalAlignment(verticalAlignment);
193     }
194
195     public void setForceEventListening(int x, int y, boolean force) {
196         get(x,y).setForceEventListening(force);
197     }
198     
199     public void setEditable(int x, int y, boolean editable) {
200         get(x,y).setEditable(editable);
201     }
202     
203     public void setTextListener(int x, int y, ITextListener listener) {
204         get(x,y).setTextListener(listener);
205     }
206
207     public void setInputValidator(int x, int y, Function1<String, String> validator) {
208         get(x,y).setValidator(validator);
209     }
210     
211     public void setTranslator(Function1<Vec2d, Boolean> translator) {
212         this.translator = translator;
213     }
214
215     public void setContentFilter(int x, int y, ITextContentFilter filter) {
216         get(x,y).setContentFilter(filter);
217     }
218
219     public void setRVI(int x, int y, RVI rvi) {
220         get(x,y).setRVI(rvi);
221     }
222
223     public void setBackgroundColor(int x, int y, Color color) {
224         get(x,y).setBackgroundColor(color);
225     }
226
227     @SyncField("font")
228     public void setFont(int x, int y, Font font) {
229         get(x,y).setFont(font);
230     }
231
232     @SyncField("text")
233     public void setText(int x, int y, String text) {
234         get(x,y).setText(text);
235     }
236
237     @SyncField("text")
238     public void setPending(int x, int y, boolean pending) {
239         get(x,y).setPending(pending);
240     }
241
242     @SyncField("color")
243     public void setColor(int x, int y, Color color) {
244         get(x,y).setColor(color);
245     }
246
247     public void removeRow(int y) {
248         rowIds.remove(y);
249         List<Cell> row = peekRowCells(y);
250         if (row.isEmpty())
251             return;
252         //System.out.println("removeRow(" + y + "): removing " + row.size() + " cells");
253         for (Cell cell : row) {
254             nodes.remove(cell);
255             removeNode(cell.toString());
256         }
257     }
258
259     public void setRowId(int y, String id) {
260         rowIds.put(y, id);
261     }
262
263     public String getRowId(int y) {
264         return rowIds.get(y);
265     }
266     
267     @Override
268     public void refresh() {
269         Vec2d delta = getDelta(FACTOR);
270         if(delta != null)
271             setTransform(AffineTransform.getTranslateInstance(delta.x, delta.y-2.1*computeRows()*(up ? 1.0 : 0.0)));
272         else
273             setTransform(AffineTransform.getTranslateInstance(0, -2.1*computeRows()*(up ? 1.0 : 0.0)));
274         super.refresh();
275     }
276     
277     @Override
278     public Rectangle2D getBoundsInLocal(boolean b) {
279         return null;
280     }
281     
282     @Override
283     public Rectangle2D getBoundsInLocal() {
284         return null;
285     }
286     
287     @Override
288     public Rectangle2D getBounds() {
289         return null;
290     }
291     
292     @Override
293     public int getEventMask() {
294         return EventTypes.MouseDragBeginMask | EventTypes.MouseMovedMask | EventTypes.MouseButtonReleasedMask | EventTypes.KeyPressedMask;
295     }
296     
297     private static boolean isEventDummy(MouseDragBegin e) {
298         if (e.controlPosition.distance(0, 0) == 0 
299                         && e.screenPosition.distance(0, 0) == 0
300                         && e.buttons == 0) {
301                 return true;
302         } else {
303                 return false;
304         }
305     }
306     
307     private boolean dragging = false;
308     private Point2D dragBegin = null;
309     private Point2D currentDrag = null;
310     private Function1<Vec2d, Boolean> translator = null;
311     
312     private static double FACTOR = 1.0; 
313     private static double FACTOR2 = 7.0;
314     
315     private Vec2d getDelta(double factor) {
316             if(dragBegin != null && currentDrag != null) {
317                 double dx =  factor * (currentDrag.getX() - dragBegin.getX());
318                 double dy = factor * (currentDrag.getY() - dragBegin.getY());
319                 return new Vec2d(dx, dy);
320             } else {
321                 return null;
322             }
323     }
324     
325     @Override
326     protected boolean keyPressed(KeyPressedEvent e) {
327         if (dragging && e.keyCode == java.awt.event.KeyEvent.VK_ESCAPE) {
328                 dragBegin = null;
329                 currentDrag = null;
330                 dragging = false;
331                 repaint();
332                 return true;
333         }
334         return false;
335     }
336
337     protected boolean mouseMoved(MouseMovedEvent event) {
338         
339         if (dragging) {
340                 currentDrag = NodeUtil.worldToLocal(this, event.controlPosition, new Point2D.Double());
341                 repaint();
342         }
343         
344         return false;
345         
346     }
347     
348     protected boolean hitTest(MouseEvent event, double tolerance) {
349         
350         Rectangle2D bounds = super.getBoundsInLocal(false);
351         if(bounds == null) return false;
352         Point2D localPos = NodeUtil.worldToLocal(this, event.controlPosition, new Point2D.Double());
353         double x = localPos.getX();
354         double y = localPos.getY()+2.1*computeRows()*(up ? 1.0 : 0.0);
355         boolean hit = bounds.contains(x, y);
356         return hit;
357         
358     }
359     
360     @Override
361     protected boolean mouseDragged(MouseDragBegin e) {
362         
363         // Get rid of dummy events from dragGestureRecognized
364         if (isEventDummy(e)) {
365                 return false;
366         }
367         
368         G2DSceneGraph sg = NodeUtil.getRootNode(this);
369         Boolean b = sg.getGlobalProperty(G2DSceneGraph.IGNORE_FOCUS, false);
370         if(!b) return false;
371         
372         if(!e.hasAnyButton(MouseEvent.LEFT_MASK) || e.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK) || !hitTest(e, 0.0)) return false;
373         
374         dragBegin = NodeUtil.worldToLocal(this, e.controlPosition, new Point2D.Double());
375         dragging = true;
376         return true;
377         
378     }
379     
380     protected boolean mouseButtonReleased(MouseButtonReleasedEvent e) {
381         
382         if(!dragging) return false;
383         
384         Vec2d delta = getDelta(FACTOR2);
385         if(delta != null && translator != null) {
386                 translator.apply(delta); 
387         } else {
388                 dragBegin = null;
389                 currentDrag = null;
390         }
391         dragging = false;
392         return false;
393     }
394     
395     @Override
396     public void init() {
397         super.init();
398         addEventHandler(this);
399     }
400
401     @Override
402     public void cleanup() {
403         removeEventHandler(this);
404         super.cleanup();
405     }
406     
407 }