1 /*******************************************************************************
2 * Copyright (c) 2007, 2017 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.diagram.elements;
14 import java.awt.Color;
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;
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;
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;
45 * TODO: does not work remotely, e.g. rowIds
47 @OutputWidget("textGrid")
48 public class TextGridNode extends G2DParentNode implements Decoration {
50 public static class A extends TextNode {
52 transient MonitorTextGridResult cache = null;
54 private static final long serialVersionUID = -4519849713591842241L;
56 public MonitorTextGridResult getCache() {
60 public void setCache(MonitorTextGridResult cache) {
66 private static final int CACHED_COLUMNS = 4;
67 private static final int CACHED_ROWS = 10;
69 private static Cell[][] cache;
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);
81 private static String makeId(int x, int y) {
90 public Cell(int x, int y) {
96 public boolean equals(Object object) {
99 else if (object == null)
101 else if (Cell.class != object.getClass())
103 Cell p = (Cell)object;
104 return x == p.x && y == p.y;
108 public int hashCode() {
113 public String toString() {
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);
124 private static final long serialVersionUID = 7015425802228571055L;
126 private TMap<Cell, A> nodes = new THashMap<Cell, A>();
127 private TIntObjectMap<String> rowIds = new TIntObjectHashMap<>();
128 private boolean up = true;
130 static class MaxY implements TObjectProcedure<Cell> {
133 public boolean execute(Cell key) {
140 public int computeRows() {
141 MaxY maxy = new MaxY();
142 nodes.forEachKey(maxy);
146 private List<Cell> peekRowCells(int y) {
147 ArrayList<Cell> row = new ArrayList<Cell>(4);
148 for (Cell key : nodes.keySet())
154 public void setUp(boolean up) {
158 public A get(int x, int y) {
159 Cell p = Cell.make(x, y);
160 A node = nodes.get(p);
162 //System.out.println(" create(" + x + "," + y + ")");
163 node = getOrCreateNode(p.toString(), A.class);
164 node.setZIndex(x + (y-1)*100);
167 //System.out.println(" get(" + x + "," + y + ")");
172 public MonitorTextGridResult getCache(int x, int y) {
173 return get(x,y).getCache();
176 public void setCache(int x, int y, MonitorTextGridResult cache) {
177 get(x,y).setCache(cache);
180 public void setTransform(int x, int y, AffineTransform transform) {
181 get(x,y).setTransform(transform);
186 public void setHorizontalAlignment(int x, int y, byte horizontalAlignment) {
187 get(x,y).setHorizontalAlignment(horizontalAlignment);
190 public void setVerticalAlignment(int x, int y, byte verticalAlignment) {
191 get(x,y).setVerticalAlignment(verticalAlignment);
194 public void setForceEventListening(int x, int y, boolean force) {
195 get(x,y).setForceEventListening(force);
198 public void setEditable(int x, int y, boolean editable) {
199 get(x,y).setEditable(editable);
202 public void setTextListener(int x, int y, ITextListener listener) {
203 get(x,y).setTextListener(listener);
206 public void setInputValidator(int x, int y, Function1<String, String> validator) {
207 get(x,y).setValidator(validator);
210 public void setTranslator(Function1<Vec2d, Boolean> translator) {
211 this.translator = translator;
214 public void setContentFilter(int x, int y, ITextContentFilter filter) {
215 get(x,y).setContentFilter(filter);
218 public void setRVI(int x, int y, RVI rvi) {
219 get(x,y).setRVI(rvi);
222 public void setBackgroundColor(int x, int y, Color color) {
223 get(x,y).setBackgroundColor(color);
227 public void setFont(int x, int y, Font font) {
228 get(x,y).setFont(font);
232 public void setText(int x, int y, String text) {
233 get(x,y).setText(text);
237 public void setPending(int x, int y, boolean pending) {
238 get(x,y).setPending(pending);
242 public void setColor(int x, int y, Color color) {
243 get(x,y).setColor(color);
246 public void removeRow(int y) {
248 List<Cell> row = peekRowCells(y);
251 //System.out.println("removeRow(" + y + "): removing " + row.size() + " cells");
252 for (Cell cell : row) {
254 removeNode(cell.toString());
258 public void setRowId(int y, String id) {
262 public String getRowId(int y) {
263 return rowIds.get(y);
266 class FontSizeSum implements TObjectProcedure<Cell> {
270 FontSizeSum(int maxY) {
271 hs = new double[maxY+1];
275 public boolean execute(Cell key) {
276 A a = nodes.get(key);
277 int h = a.getFont().getSize();
283 public double getH() {
285 for(int i=0;i<hs.length;i++)
293 public void refresh() {
294 FontSizeSum fss = new FontSizeSum(computeRows());
295 nodes.forEachKey(fss);
296 Vec2d delta = getDelta(FACTOR);
298 setTransform(AffineTransform.getTranslateInstance(delta.x, delta.y-0.15*fss.getH()*(up ? 1.0 : 0.0)));
300 setTransform(AffineTransform.getTranslateInstance(0, -0.15*fss.getH()*(up ? 1.0 : 0.0)));
305 public Rectangle2D getBoundsInLocal(boolean b) {
310 public Rectangle2D getBoundsInLocal() {
315 public Rectangle2D getBounds() {
320 public int getEventMask() {
321 return EventTypes.MouseDragBeginMask | EventTypes.MouseMovedMask | EventTypes.MouseButtonReleasedMask | EventTypes.KeyPressedMask;
324 protected static boolean isEventDummy(MouseDragBegin e) {
325 if (e.controlPosition.distance(0, 0) == 0
326 && e.screenPosition.distance(0, 0) == 0
334 protected boolean dragging = false;
335 protected Point2D dragBegin = null;
336 protected Point2D currentDrag = null;
337 private Function1<Vec2d, Boolean> translator = null;
339 private static double FACTOR = 1.0;
340 private static double FACTOR2 = 7.0;
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);
353 protected boolean keyPressed(KeyPressedEvent e) {
354 if (dragging && e.keyCode == java.awt.event.KeyEvent.VK_ESCAPE) {
364 protected boolean mouseMoved(MouseMovedEvent event) {
367 currentDrag = NodeUtil.worldToLocal(this, event.controlPosition, new Point2D.Double());
375 protected boolean hitTest(MouseEvent event, double tolerance) {
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);
388 protected boolean mouseDragged(MouseDragBegin e) {
390 // Get rid of dummy events from dragGestureRecognized
391 if (isEventDummy(e)) {
395 G2DSceneGraph sg = NodeUtil.getRootNode(this);
396 Boolean b = sg.getGlobalProperty(G2DSceneGraph.IGNORE_FOCUS, false);
399 if(!e.hasAnyButton(MouseEvent.LEFT_MASK) || e.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK) || !hitTest(e, 0.0)) return false;
401 dragBegin = NodeUtil.worldToLocal(this, e.controlPosition, new Point2D.Double());
407 protected boolean mouseButtonReleased(MouseButtonReleasedEvent e) {
409 if(!dragging) return false;
411 Vec2d delta = getDelta(FACTOR2);
412 if(delta != null && translator != null) {
413 translator.apply(delta);
425 addEventHandler(this);
429 public void cleanup() {
430 removeEventHandler(this);