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 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;
20 import java.awt.Color;
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;
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;
46 * TODO: does not work remotely, e.g. rowIds
48 @OutputWidget("textGrid")
49 public class TextGridNode extends G2DParentNode implements Decoration {
51 public static class A extends TextNode {
53 transient MonitorTextGridResult cache = null;
55 private static final long serialVersionUID = -4519849713591842241L;
57 public MonitorTextGridResult getCache() {
61 public void setCache(MonitorTextGridResult cache) {
67 private static final int CACHED_COLUMNS = 4;
68 private static final int CACHED_ROWS = 10;
70 private static Cell[][] cache;
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);
82 private static String makeId(int x, int y) {
91 public Cell(int x, int y) {
97 public boolean equals(Object object) {
100 else if (object == null)
102 else if (Cell.class != object.getClass())
104 Cell p = (Cell)object;
105 return x == p.x && y == p.y;
109 public int hashCode() {
114 public String toString() {
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);
125 private static final long serialVersionUID = 7015425802228571055L;
127 private TMap<Cell, A> nodes = new THashMap<Cell, A>();
128 private TIntObjectMap<String> rowIds = new TIntObjectHashMap<>();
129 private boolean up = true;
131 static class MaxY implements TObjectProcedure<Cell> {
134 public boolean execute(Cell key) {
141 public int computeRows() {
142 MaxY maxy = new MaxY();
143 nodes.forEachKey(maxy);
147 private List<Cell> peekRowCells(int y) {
148 ArrayList<Cell> row = new ArrayList<Cell>(4);
149 for (Cell key : nodes.keySet())
155 public void setUp(boolean up) {
159 public A get(int x, int y) {
160 Cell p = Cell.make(x, y);
161 A node = nodes.get(p);
163 //System.out.println(" create(" + x + "," + y + ")");
164 node = getOrCreateNode(p.toString(), A.class);
165 node.setZIndex(x + (y-1)*100);
168 //System.out.println(" get(" + x + "," + y + ")");
173 public MonitorTextGridResult getCache(int x, int y) {
174 return get(x,y).getCache();
177 public void setCache(int x, int y, MonitorTextGridResult cache) {
178 get(x,y).setCache(cache);
181 public void setTransform(int x, int y, AffineTransform transform) {
182 get(x,y).setTransform(transform);
187 public void setHorizontalAlignment(int x, int y, byte horizontalAlignment) {
188 get(x,y).setHorizontalAlignment(horizontalAlignment);
191 public void setVerticalAlignment(int x, int y, byte verticalAlignment) {
192 get(x,y).setVerticalAlignment(verticalAlignment);
195 public void setForceEventListening(int x, int y, boolean force) {
196 get(x,y).setForceEventListening(force);
199 public void setEditable(int x, int y, boolean editable) {
200 get(x,y).setEditable(editable);
203 public void setTextListener(int x, int y, ITextListener listener) {
204 get(x,y).setTextListener(listener);
207 public void setInputValidator(int x, int y, Function1<String, String> validator) {
208 get(x,y).setValidator(validator);
211 public void setTranslator(Function1<Vec2d, Boolean> translator) {
212 this.translator = translator;
215 public void setContentFilter(int x, int y, ITextContentFilter filter) {
216 get(x,y).setContentFilter(filter);
219 public void setRVI(int x, int y, RVI rvi) {
220 get(x,y).setRVI(rvi);
223 public void setBackgroundColor(int x, int y, Color color) {
224 get(x,y).setBackgroundColor(color);
228 public void setFont(int x, int y, Font font) {
229 get(x,y).setFont(font);
233 public void setText(int x, int y, String text) {
234 get(x,y).setText(text);
238 public void setPending(int x, int y, boolean pending) {
239 get(x,y).setPending(pending);
243 public void setColor(int x, int y, Color color) {
244 get(x,y).setColor(color);
247 public void removeRow(int y) {
249 List<Cell> row = peekRowCells(y);
252 //System.out.println("removeRow(" + y + "): removing " + row.size() + " cells");
253 for (Cell cell : row) {
255 removeNode(cell.toString());
259 public void setRowId(int y, String id) {
263 public String getRowId(int y) {
264 return rowIds.get(y);
268 public void refresh() {
269 Vec2d delta = getDelta(FACTOR);
271 setTransform(AffineTransform.getTranslateInstance(delta.x, delta.y-2.1*computeRows()*(up ? 1.0 : 0.0)));
273 setTransform(AffineTransform.getTranslateInstance(0, -2.1*computeRows()*(up ? 1.0 : 0.0)));
278 public Rectangle2D getBoundsInLocal(boolean b) {
283 public Rectangle2D getBoundsInLocal() {
288 public Rectangle2D getBounds() {
293 public int getEventMask() {
294 return EventTypes.MouseDragBeginMask | EventTypes.MouseMovedMask | EventTypes.MouseButtonReleasedMask | EventTypes.KeyPressedMask;
297 private static boolean isEventDummy(MouseDragBegin e) {
298 if (e.controlPosition.distance(0, 0) == 0
299 && e.screenPosition.distance(0, 0) == 0
307 private boolean dragging = false;
308 private Point2D dragBegin = null;
309 private Point2D currentDrag = null;
310 private Function1<Vec2d, Boolean> translator = null;
312 private static double FACTOR = 1.0;
313 private static double FACTOR2 = 7.0;
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);
326 protected boolean keyPressed(KeyPressedEvent e) {
327 if (dragging && e.keyCode == java.awt.event.KeyEvent.VK_ESCAPE) {
337 protected boolean mouseMoved(MouseMovedEvent event) {
340 currentDrag = NodeUtil.worldToLocal(this, event.controlPosition, new Point2D.Double());
348 protected boolean hitTest(MouseEvent event, double tolerance) {
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);
361 protected boolean mouseDragged(MouseDragBegin e) {
363 // Get rid of dummy events from dragGestureRecognized
364 if (isEventDummy(e)) {
368 G2DSceneGraph sg = NodeUtil.getRootNode(this);
369 Boolean b = sg.getGlobalProperty(G2DSceneGraph.IGNORE_FOCUS, false);
372 if(!e.hasAnyButton(MouseEvent.LEFT_MASK) || e.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK) || !hitTest(e, 0.0)) return false;
374 dragBegin = NodeUtil.worldToLocal(this, e.controlPosition, new Point2D.Double());
380 protected boolean mouseButtonReleased(MouseButtonReleasedEvent e) {
382 if(!dragging) return false;
384 Vec2d delta = getDelta(FACTOR2);
385 if(delta != null && translator != null) {
386 translator.apply(delta);
398 addEventHandler(this);
402 public void cleanup() {
403 removeEventHandler(this);