]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph.swing/src/org/simantics/scenegraph/swing/MonitorNode.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.scenegraph.swing / src / org / simantics / scenegraph / swing / MonitorNode.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 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.scenegraph.swing;
13
14 import java.awt.Color;
15 import java.awt.Font;
16 import java.awt.FontMetrics;
17 import java.awt.Graphics2D;
18 import java.awt.Point;
19 import java.awt.event.ActionEvent;
20 import java.awt.event.ActionListener;
21 import java.awt.event.FocusEvent;
22 import java.awt.event.FocusListener;
23 import java.awt.event.KeyEvent;
24 import java.awt.event.KeyListener;
25 import java.awt.event.MouseEvent;
26 import java.awt.geom.Point2D;
27 import java.awt.geom.Rectangle2D;
28 import java.beans.PropertyChangeEvent;
29 import java.beans.PropertyChangeListener;
30
31 import javax.swing.JTextField;
32 import javax.swing.border.Border;
33 import javax.swing.border.CompoundBorder;
34 import javax.swing.border.EmptyBorder;
35 import javax.swing.border.LineBorder;
36
37 import org.simantics.scenegraph.ExportableWidget.InputWidget;
38 import org.simantics.scenegraph.ExportableWidget.OutputWidget;
39 import org.simantics.scenegraph.utils.DummyComponent;
40
41 @OutputWidget("value")
42 @InputWidget("value")
43 public class MonitorNode extends ComponentNode<JTextField> implements ActionListener, FocusListener, PropertyChangeListener, KeyListener {
44     /**
45      * 
46      */
47     private static final long serialVersionUID = 7073028693751719102L;
48
49     protected boolean editable = true;
50     protected String value = "";
51     protected String tooltip = "";
52     protected double borderWidth = 0;
53
54     protected transient ActionListener actionListener = null;
55
56     private boolean doResize = false;
57
58     protected Font font = null;
59     protected Color color = null;
60     protected int halign = JTextField.LEFT;  // See JTextField for value options
61
62     static class TextField extends JTextField {
63         private static final int X_INSET = 5;
64         private static final int Y_INSET = -1;
65         private static final long serialVersionUID = -668522226693100386L;
66         private Border lineBorder;
67         private final MonitorNode node;
68
69         public TextField(double borderWidth, MonitorNode node) {
70             if(borderWidth < 0.1) {
71                 lineBorder = new EmptyBorder(Y_INSET, X_INSET, Y_INSET, X_INSET);
72             } else {
73                 lineBorder = new CompoundBorder(LineBorder.createGrayLineBorder(), new EmptyBorder(Y_INSET, X_INSET, Y_INSET, X_INSET));
74             }
75             this.node = node;
76             setHorizontalAlignment(JTextField.CENTER);
77             setAlignmentY(JTextField.CENTER_ALIGNMENT);
78         }
79
80         // workaround for 4530952
81         @Override
82         public void setText(String s) {
83             if (getText().equals(s)) {
84                 return;
85             }
86             super.setText(s);
87
88         }
89
90         @Override
91         public void setBorder(Border border) {
92             super.setBorder(lineBorder);
93         }
94
95         public void setBorder(double borderWidth) {
96             if(borderWidth < 0.1) {
97                 lineBorder = new EmptyBorder(Y_INSET, X_INSET, Y_INSET, X_INSET);
98             } else {
99                 lineBorder = new CompoundBorder(LineBorder.createGrayLineBorder(), new EmptyBorder(Y_INSET, X_INSET, Y_INSET, X_INSET));
100             }
101
102             super.setBorder(lineBorder);
103         }
104
105         @Override
106         public Point getToolTipLocation(MouseEvent event) {
107             Point2D p2d = node.localToControl(event.getPoint());
108             Point p = new Point((int)p2d.getX(),-20+(int)p2d.getY());
109             return p;
110         }
111     }
112
113     @Override
114     public String toString() {
115         return super.toString() + "[editable=" + editable + ", value=" + value + "]";
116     }
117
118     private void markResize() {
119         //System.out.println("MonitorNode.markResize()");
120         doResize = true;
121     }
122
123     @Override
124     public void init() {
125         component = new TextField(borderWidth,this);
126         component.setEditable(editable);
127         component.setEnabled(editable);
128         component.addActionListener(this);
129         component.addFocusListener(this);
130         component.addKeyListener(this);
131         super.init();
132     }
133
134     @SyncField("editable")
135     public void setEditable(boolean value) {
136         this.editable = value;
137
138         if(component != null) {
139             component.setEditable(value);
140             component.setEnabled(value);
141         }
142     }
143
144     @PropertySetter("Stroke Width")
145     @SyncField("borderWidth")
146     public void setBorderWidth(Float borderWidth) {
147         this.borderWidth = borderWidth;
148         if(component != null) {
149             ((TextField)component).setBorder(borderWidth);
150         }
151     }
152
153     @SyncField("value")
154     public void setText(String value) {
155         this.value = value;
156         // RemoteViewer does not have component initialized
157         if (component != null) {
158             //System.out.println("MonitorNode.setText(" + value + ")");
159             component.setText(value);
160             markResize();
161             component.repaint();
162         }
163     }
164
165     @SyncField("tooltip")
166     public void setToolTipText(String tooltip) {
167         this.tooltip = tooltip;
168         if (component != null) {
169             component.setToolTipText(tooltip);
170         }
171     }
172
173
174     @PropertySetter("Font")
175     @SyncField("font")
176     public void setFont(Font font) {
177         this.font = font;
178         if (component != null) {
179             setComponentFont(font);
180             markResize();
181         }
182     }
183
184     @PropertySetter("Color")
185     @SyncField("color")
186     public void setColor(Color color) {
187         this.color = color;
188         if (component != null) {
189             component.setForeground(color);
190             markResize();
191         }
192     }
193
194     @SyncField("halign")
195     public void setHorizontalAlign(int halign) {
196         this.halign = halign;
197     }
198
199     @Override
200     public void render(Graphics2D g2d) {
201         if (doResize)
202             recalculateSize(g2d);
203         doResize = false;
204                 if (component != null) {
205                         synchronized (component) {
206                                 if (component.getHorizontalAlignment() != halign)
207                                         component.setHorizontalAlignment(halign);
208                                 super.render(g2d);
209                         }
210                 }
211     }
212
213     private void recalculateSize(Graphics2D g2d) {
214         //System.out.println("MonitorNode.recalculateSize(" + value + ")");
215         if (component == null || value == null)
216             return;
217
218         Font font = getComponentFont();
219         if (font != null) {
220             String measuredValue = value;
221             //String measuredValue = value + "x";
222             // If bounds are NOT set, calculate size..
223             if (bounds == null) {
224                 FontMetrics metrics = component.getFontMetrics(font);
225                 Rectangle2D size = metrics.getStringBounds(measuredValue, g2d);
226                 int xPadding = 15;
227                 int yPadding = 2;
228 //                setSize(xPadding + (int) Math.ceil(size.getWidth()), yPadding + (int) Math.ceil(size.getHeight()));
229                 setBounds(new Rectangle2D.Double(0, 0, xPadding + (int) Math.ceil(size.getWidth()), yPadding + (int) Math.ceil(size.getHeight())));
230                 //component.setScrollOffset(0);
231             } else {
232                 // ... If bounds are set, change font size to get the text fit
233                 // into the bounds. Find the best fit through bisection.
234
235                 // min is assumed to fit within the bounds. It will not be searched
236                 float min = 6;
237
238                 // First find an upper bound that no longer fits within the bounds.
239                 // First guess is 62.
240                 float max = 62;
241                 final float upperLimit = 200;
242                 while (max < upperLimit) {
243                     font = font.deriveFont(max);
244                     FontMetrics metrics = component.getFontMetrics(font);
245                     Rectangle2D fbounds = metrics.getStringBounds(measuredValue, g2d);
246                     if (!fits(bounds, fbounds))
247                         break;
248                     max += 20;
249                 }
250                 if (max < upperLimit) {
251                     // Bisect the largest font size in [min,max] that fits the bounds.
252                     while (true) {
253                         float half = (max + min) / 2;
254                         float interval = max - min;
255                         font = font.deriveFont(half);
256                         FontMetrics metrics = component.getFontMetrics(font);
257                         Rectangle2D fbounds = metrics.getStringBounds(measuredValue, g2d);
258                         if (fits(bounds, fbounds)) {
259                             // Fits within bounds, bisect [half, max]
260                             if (interval <= 1) {
261                                 break;
262                             }
263                             min = half;
264                         } else {
265                             // Does not fit within bounds, bisect [min, half]
266                             if (interval <= 1) {
267                                 font = font.deriveFont(min);
268                                 break;
269                             }
270                             max = half;
271                         }
272                     }
273                 }
274
275                 setComponentFont(font);
276 //                setSize((int)bounds.getWidth(), (int)bounds.getHeight());
277             }
278         }
279     }
280
281     private boolean fits(Rectangle2D parent, Rectangle2D child) {
282         return parent.getWidth() >= child.getWidth() && parent.getHeight() >= child.getHeight();
283     }
284
285     public String getText() {
286         return value;
287     }
288
289     public Font getFont() {
290         return font;
291     }
292
293     @Override
294     public void propertyChange(PropertyChangeEvent evt) {
295         if("value".equals(evt.getPropertyName()) && component != null) {
296             synchronized(component) {
297                     component.setText((String)evt.getNewValue());
298                     markResize();
299                     component.repaint();
300             }
301         } else if("editable".equals(evt.getPropertyName()) && component != null) {
302             synchronized(component) {
303                 component.setEditable((Boolean)evt.getNewValue());
304                 component.setEnabled((Boolean)evt.getNewValue());
305             }
306         }
307     }
308
309
310     public void setActionListener(ActionListener actionListener) {
311         this.actionListener = actionListener;
312     }
313
314     @Override
315     public void actionPerformed(ActionEvent e) {
316 //        performAction(e);
317         loseFocus();
318     }
319
320     void loseFocus() {
321         if (component != null)
322             if (component.isFocusOwner())
323                 if (container.getParent() != null)
324                     container.getParent().requestFocusInWindow(); // Lose focus
325     }
326
327     @Override
328     public void focusGained(FocusEvent arg0) {
329         if (component != null) {
330                 component.selectAll();
331         }
332     }
333
334     @Override
335     public void focusLost(FocusEvent arg0) {
336         if (component != null) {
337             ActionEvent e = new ActionEvent(component, ActionEvent.ACTION_PERFORMED, component.getText());
338             performAction(e);
339         }
340     }
341
342     /**
343      * Wrapper method to send event to serverside
344      * 
345      * @param e
346      */
347     @ServerSide
348     public void performAction(ActionEvent e) {
349         if (actionListener != null) {
350             //System.out.println("MonitorNode.performAction(" + e + ")");
351             actionListener.actionPerformed(e);
352         }
353     }
354
355     @Override
356     public void keyPressed(KeyEvent e) {
357     }
358
359     @Override
360     public void keyTyped(KeyEvent e) {
361     }
362
363     @Override
364     public void keyReleased(KeyEvent e) {
365         if (e.getModifiers() == 0 && e.getKeyCode() == KeyEvent.VK_ESCAPE) {
366             // ESC without modifiers == CANCEL edit
367             // TODO: signal about cancellation
368             loseFocus();
369         }
370     }
371     
372     public String widgetGet(String name) {
373         if("value".equals(name)) {
374             return ""+value;
375         }
376         return null;
377     }
378     
379     public void widgetSet(String name, String value) {
380         if("value".equals(name)) {
381             ActionEvent e = new ActionEvent(new DummyComponent(), ActionEvent.ACTION_PERFORMED, value);
382             performAction(e);
383         }
384     }
385 }