1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.scenegraph.swing;
\r
14 import java.awt.Color;
\r
15 import java.awt.Font;
\r
16 import java.awt.FontMetrics;
\r
17 import java.awt.Graphics2D;
\r
18 import java.awt.Point;
\r
19 import java.awt.event.ActionEvent;
\r
20 import java.awt.event.ActionListener;
\r
21 import java.awt.event.FocusEvent;
\r
22 import java.awt.event.FocusListener;
\r
23 import java.awt.event.KeyEvent;
\r
24 import java.awt.event.KeyListener;
\r
25 import java.awt.event.MouseEvent;
\r
26 import java.awt.geom.Point2D;
\r
27 import java.awt.geom.Rectangle2D;
\r
28 import java.beans.PropertyChangeEvent;
\r
29 import java.beans.PropertyChangeListener;
\r
31 import javax.swing.JTextField;
\r
32 import javax.swing.border.Border;
\r
33 import javax.swing.border.CompoundBorder;
\r
34 import javax.swing.border.EmptyBorder;
\r
35 import javax.swing.border.LineBorder;
\r
37 import org.simantics.scenegraph.ExportableWidget.InputWidget;
\r
38 import org.simantics.scenegraph.ExportableWidget.OutputWidget;
\r
39 import org.simantics.scenegraph.utils.DummyComponent;
\r
41 @OutputWidget("value")
\r
42 @InputWidget("value")
\r
43 public class MonitorNode extends ComponentNode<JTextField> implements ActionListener, FocusListener, PropertyChangeListener, KeyListener {
\r
47 private static final long serialVersionUID = 7073028693751719102L;
\r
49 protected boolean editable = true;
\r
50 protected String value = "";
\r
51 protected String tooltip = "";
\r
52 protected double borderWidth = 0;
\r
54 protected transient ActionListener actionListener = null;
\r
56 private boolean doResize = false;
\r
58 protected Font font = null;
\r
59 protected Color color = null;
\r
60 protected int halign = JTextField.LEFT; // See JTextField for value options
\r
62 static class TextField extends JTextField {
\r
63 private static final int X_INSET = 5;
\r
64 private static final int Y_INSET = -1;
\r
65 private static final long serialVersionUID = -668522226693100386L;
\r
66 private Border lineBorder;
\r
67 private final MonitorNode node;
\r
69 public TextField(double borderWidth, MonitorNode node) {
\r
70 if(borderWidth < 0.1) {
\r
71 lineBorder = new EmptyBorder(Y_INSET, X_INSET, Y_INSET, X_INSET);
\r
73 lineBorder = new CompoundBorder(LineBorder.createGrayLineBorder(), new EmptyBorder(Y_INSET, X_INSET, Y_INSET, X_INSET));
\r
76 setHorizontalAlignment(JTextField.CENTER);
\r
77 setAlignmentY(JTextField.CENTER_ALIGNMENT);
\r
80 // workaround for 4530952
\r
82 public void setText(String s) {
\r
83 if (getText().equals(s)) {
\r
91 public void setBorder(Border border) {
\r
92 super.setBorder(lineBorder);
\r
95 public void setBorder(double borderWidth) {
\r
96 if(borderWidth < 0.1) {
\r
97 lineBorder = new EmptyBorder(Y_INSET, X_INSET, Y_INSET, X_INSET);
\r
99 lineBorder = new CompoundBorder(LineBorder.createGrayLineBorder(), new EmptyBorder(Y_INSET, X_INSET, Y_INSET, X_INSET));
\r
102 super.setBorder(lineBorder);
\r
106 public Point getToolTipLocation(MouseEvent event) {
\r
107 Point2D p2d = node.localToControl(event.getPoint());
\r
108 Point p = new Point((int)p2d.getX(),-20+(int)p2d.getY());
\r
114 public String toString() {
\r
115 return super.toString() + "[editable=" + editable + ", value=" + value + "]";
\r
118 private void markResize() {
\r
119 //System.out.println("MonitorNode.markResize()");
\r
124 public void init() {
\r
125 component = new TextField(borderWidth,this);
\r
126 component.setEditable(editable);
\r
127 component.setEnabled(editable);
\r
128 component.addActionListener(this);
\r
129 component.addFocusListener(this);
\r
130 component.addKeyListener(this);
\r
134 @SyncField("editable")
\r
135 public void setEditable(boolean value) {
\r
136 this.editable = value;
\r
138 if(component != null) {
\r
139 component.setEditable(value);
\r
140 component.setEnabled(value);
\r
144 @PropertySetter("Stroke Width")
\r
145 @SyncField("borderWidth")
\r
146 public void setBorderWidth(Float borderWidth) {
\r
147 this.borderWidth = borderWidth;
\r
148 if(component != null) {
\r
149 ((TextField)component).setBorder(borderWidth);
\r
153 @SyncField("value")
\r
154 public void setText(String value) {
\r
155 this.value = value;
\r
156 // RemoteViewer does not have component initialized
\r
157 if (component != null) {
\r
158 //System.out.println("MonitorNode.setText(" + value + ")");
\r
159 component.setText(value);
\r
161 component.repaint();
\r
165 @SyncField("tooltip")
\r
166 public void setToolTipText(String tooltip) {
\r
167 this.tooltip = tooltip;
\r
168 if (component != null) {
\r
169 component.setToolTipText(tooltip);
\r
174 @PropertySetter("Font")
\r
176 public void setFont(Font font) {
\r
178 if (component != null) {
\r
179 setComponentFont(font);
\r
184 @PropertySetter("Color")
\r
185 @SyncField("color")
\r
186 public void setColor(Color color) {
\r
187 this.color = color;
\r
188 if (component != null) {
\r
189 component.setForeground(color);
\r
194 @SyncField("halign")
\r
195 public void setHorizontalAlign(int halign) {
\r
196 this.halign = halign;
\r
200 public void render(Graphics2D g2d) {
\r
202 recalculateSize(g2d);
\r
204 if (component != null) {
\r
205 synchronized (component) {
\r
206 if (component.getHorizontalAlignment() != halign)
\r
207 component.setHorizontalAlignment(halign);
\r
213 private void recalculateSize(Graphics2D g2d) {
\r
214 //System.out.println("MonitorNode.recalculateSize(" + value + ")");
\r
215 if (component == null || value == null)
\r
218 Font font = getComponentFont();
\r
219 if (font != null) {
\r
220 String measuredValue = value;
\r
221 //String measuredValue = value + "x";
\r
222 // If bounds are NOT set, calculate size..
\r
223 if (bounds == null) {
\r
224 FontMetrics metrics = component.getFontMetrics(font);
\r
225 Rectangle2D size = metrics.getStringBounds(measuredValue, g2d);
\r
228 // setSize(xPadding + (int) Math.ceil(size.getWidth()), yPadding + (int) Math.ceil(size.getHeight()));
\r
229 setBounds(new Rectangle2D.Double(0, 0, xPadding + (int) Math.ceil(size.getWidth()), yPadding + (int) Math.ceil(size.getHeight())));
\r
230 //component.setScrollOffset(0);
\r
232 // ... If bounds are set, change font size to get the text fit
\r
233 // into the bounds. Find the best fit through bisection.
\r
235 // min is assumed to fit within the bounds. It will not be searched
\r
238 // First find an upper bound that no longer fits within the bounds.
\r
239 // First guess is 62.
\r
241 final float upperLimit = 200;
\r
242 while (max < upperLimit) {
\r
243 font = font.deriveFont(max);
\r
244 FontMetrics metrics = component.getFontMetrics(font);
\r
245 Rectangle2D fbounds = metrics.getStringBounds(measuredValue, g2d);
\r
246 if (!fits(bounds, fbounds))
\r
250 if (max < upperLimit) {
\r
251 // Bisect the largest font size in [min,max] that fits the bounds.
\r
253 float half = (max + min) / 2;
\r
254 float interval = max - min;
\r
255 font = font.deriveFont(half);
\r
256 FontMetrics metrics = component.getFontMetrics(font);
\r
257 Rectangle2D fbounds = metrics.getStringBounds(measuredValue, g2d);
\r
258 if (fits(bounds, fbounds)) {
\r
259 // Fits within bounds, bisect [half, max]
\r
260 if (interval <= 1) {
\r
265 // Does not fit within bounds, bisect [min, half]
\r
266 if (interval <= 1) {
\r
267 font = font.deriveFont(min);
\r
275 setComponentFont(font);
\r
276 // setSize((int)bounds.getWidth(), (int)bounds.getHeight());
\r
281 private boolean fits(Rectangle2D parent, Rectangle2D child) {
\r
282 return parent.getWidth() >= child.getWidth() && parent.getHeight() >= child.getHeight();
\r
285 public String getText() {
\r
289 public Font getFont() {
\r
294 public void propertyChange(PropertyChangeEvent evt) {
\r
295 if("value".equals(evt.getPropertyName()) && component != null) {
\r
296 synchronized(component) {
\r
297 component.setText((String)evt.getNewValue());
\r
299 component.repaint();
\r
301 } else if("editable".equals(evt.getPropertyName()) && component != null) {
\r
302 synchronized(component) {
\r
303 component.setEditable((Boolean)evt.getNewValue());
\r
304 component.setEnabled((Boolean)evt.getNewValue());
\r
310 public void setActionListener(ActionListener actionListener) {
\r
311 this.actionListener = actionListener;
\r
315 public void actionPerformed(ActionEvent e) {
\r
316 // performAction(e);
\r
321 if (component != null)
\r
322 if (component.isFocusOwner())
\r
323 if (container.getParent() != null)
\r
324 container.getParent().requestFocusInWindow(); // Lose focus
\r
328 public void focusGained(FocusEvent arg0) {
\r
329 if (component != null) {
\r
330 component.selectAll();
\r
335 public void focusLost(FocusEvent arg0) {
\r
336 if (component != null) {
\r
337 ActionEvent e = new ActionEvent(component, ActionEvent.ACTION_PERFORMED, component.getText());
\r
343 * Wrapper method to send event to serverside
\r
348 public void performAction(ActionEvent e) {
\r
349 if (actionListener != null) {
\r
350 //System.out.println("MonitorNode.performAction(" + e + ")");
\r
351 actionListener.actionPerformed(e);
\r
356 public void keyPressed(KeyEvent e) {
\r
360 public void keyTyped(KeyEvent e) {
\r
364 public void keyReleased(KeyEvent e) {
\r
365 if (e.getModifiers() == 0 && e.getKeyCode() == KeyEvent.VK_ESCAPE) {
\r
366 // ESC without modifiers == CANCEL edit
\r
367 // TODO: signal about cancellation
\r
372 public String widgetGet(String name) {
\r
373 if("value".equals(name)) {
\r
379 public void widgetSet(String name, String value) {
\r
380 if("value".equals(name)) {
\r
381 ActionEvent e = new ActionEvent(new DummyComponent(), ActionEvent.ACTION_PERFORMED, value);
\r