1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 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 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 private 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) {
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);
267 public void render(Graphics2D g2d) {
268 Vec2d delta = getDelta(FACTOR);
270 setTransform(AffineTransform.getTranslateInstance(delta.x, delta.y-2.1*computeRows()*(up ? 1.0 : 0.0)));
272 setTransform(AffineTransform.getTranslateInstance(0, -2.1*computeRows()*(up ? 1.0 : 0.0)));
277 public Rectangle2D getBoundsInLocal(boolean b) {
282 public Rectangle2D getBoundsInLocal() {
287 public Rectangle2D getBounds() {
292 public int getEventMask() {
293 return EventTypes.MouseDragBeginMask | EventTypes.KeyPressedMask;
296 private static boolean isEventDummy(MouseDragBegin e) {
297 if (e.controlPosition.distance(0, 0) == 0
298 && e.screenPosition.distance(0, 0) == 0
306 private boolean dragging = false;
307 private Point2D dragBegin = null;
308 private Point2D currentDrag = null;
309 private Function1<Vec2d, Boolean> translator = null;
311 private static double FACTOR = 1.0;
312 private static double FACTOR2 = 7.0;
314 private Vec2d getDelta(double factor) {
315 if(dragBegin != null && currentDrag != null) {
316 double dx = factor * (currentDrag.getX() - dragBegin.getX());
317 double dy = factor * (currentDrag.getY() - dragBegin.getY());
318 return new Vec2d(dx, dy);
325 protected boolean keyPressed(KeyPressedEvent e) {
326 if (dragging && e.keyCode == java.awt.event.KeyEvent.VK_ESCAPE) {
336 protected boolean mouseMoved(MouseMovedEvent event) {
339 currentDrag = NodeUtil.worldToLocal(this, event.controlPosition, new Point2D.Double());
347 protected boolean hitTest(MouseEvent event, double tolerance) {
349 Rectangle2D bounds = super.getBoundsInLocal(false);
350 if(bounds == null) return false;
351 Point2D localPos = NodeUtil.worldToLocal(this, event.controlPosition, new Point2D.Double());
352 double x = localPos.getX();
353 double y = localPos.getY()+2.1*computeRows()*(up ? 1.0 : 0.0);
354 boolean hit = bounds.contains(x, y);
360 protected boolean mouseDragged(MouseDragBegin e) {
362 // Get rid of dummy events from dragGestureRecognized
363 if (isEventDummy(e)) {
367 G2DSceneGraph sg = NodeUtil.getRootNode(this);
368 Boolean b = sg.getGlobalProperty(G2DSceneGraph.IGNORE_FOCUS, false);
371 if(!e.hasAnyButton(MouseEvent.LEFT_MASK) || e.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK) || !hitTest(e, 0.0)) return false;
373 dragBegin = NodeUtil.worldToLocal(this, e.controlPosition, new Point2D.Double());
379 protected boolean mouseButtonReleased(MouseButtonReleasedEvent e) {
381 if(!dragging) return false;
383 Vec2d delta = getDelta(FACTOR2);
384 if(delta != null && translator != null) {
385 translator.apply(delta);
397 addEventHandler(this);
401 public void cleanup() {
402 removeEventHandler(this);