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