1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sourceforge.jeuclid.swing;
20
21 import java.awt.Color;
22 import java.awt.Dimension;
23 import java.awt.Graphics;
24 import java.awt.Graphics2D;
25 import java.awt.Insets;
26 import java.awt.Point;
27 import java.awt.geom.Point2D;
28 import java.beans.PropertyChangeEvent;
29 import java.beans.PropertyChangeListener;
30 import java.lang.ref.Reference;
31 import java.lang.ref.SoftReference;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35
36 import javax.swing.JComponent;
37 import javax.swing.LookAndFeel;
38 import javax.swing.SwingConstants;
39 import javax.swing.border.Border;
40 import javax.swing.plaf.ComponentUI;
41
42 import net.sourceforge.jeuclid.LayoutContext;
43 import net.sourceforge.jeuclid.layout.JEuclidView;
44
45 import org.w3c.dom.Node;
46
47
48
49
50
51
52
53
54 public class MathComponentUI extends ComponentUI implements
55 PropertyChangeListener {
56
57
58
59
60
61
62
63
64 private final Map<JMathComponent, Reference<ViewContext>> contextCache = new HashMap<JMathComponent, Reference<ViewContext>>();
65
66
67
68
69 public MathComponentUI() {
70 super();
71
72 }
73
74 private JEuclidView getJeuclidView(final Graphics g, final JComponent c) {
75 JMathComponent jc = (JMathComponent) c;
76 ViewContext cache = null;
77 Reference<ViewContext> ref = contextCache.get(jc);
78 if (ref != null) {
79 cache = ref.get();
80 }
81
82 if (cache == null) {
83 cache = new ViewContext(jc);
84 contextCache.put(jc, new SoftReference<ViewContext>(cache));
85 }
86
87 return cache.getJeculidView((Graphics2D) g);
88 }
89
90
91 @Override
92 public void paint(final Graphics g, final JComponent c) {
93 JEuclidView jEuclidView = this.getJeuclidView(g, c);
94 final Dimension dim = this.calculatePreferredSize(c, jEuclidView);
95 final Point start = this.getStartPointWithBordersAndAdjustDimension(c,
96 dim);
97 this.paintBackground(g, c, dim, start);
98
99 final Point2D alignOffset = this.calculateAlignmentOffset(
100 (JMathComponent) c, jEuclidView, dim);
101 jEuclidView.draw((Graphics2D) g, (float) alignOffset.getX() + start.x,
102 (float) alignOffset.getY() + start.y);
103
104 }
105
106
107 @Override
108 public void update(final Graphics g, final JComponent c) {
109 if (c.isOpaque()) {
110 g.setColor(c.getBackground());
111 g.fillRect(0, 0, c.getWidth(), c.getHeight());
112 }
113 this.paint(g, c);
114 }
115
116 private Point2D calculateAlignmentOffset(JMathComponent jc,
117 JEuclidView jEuclidView, final Dimension dim) {
118 final float xo;
119 if ((jc.getHorizontalAlignment() == SwingConstants.LEADING)
120 || (jc.getHorizontalAlignment() == SwingConstants.LEFT)) {
121 xo = 0.0f;
122 } else if ((jc.getHorizontalAlignment() == SwingConstants.TRAILING)
123 || (jc.getHorizontalAlignment() == SwingConstants.RIGHT)) {
124 xo = dim.width - jEuclidView.getWidth();
125 } else {
126 xo = (dim.width - jEuclidView.getWidth()) / 2.0f;
127 }
128 final float yo;
129 if (jc.getVerticalAlignment() == SwingConstants.TOP) {
130 yo = jEuclidView.getAscentHeight();
131 } else if (jc.getVerticalAlignment() == SwingConstants.BOTTOM) {
132 yo = dim.height - jEuclidView.getDescentHeight();
133 } else {
134 yo = (dim.height + jEuclidView.getAscentHeight() - jEuclidView
135 .getDescentHeight()) / 2.0f;
136 }
137 return new Point2D.Float(xo, yo);
138 }
139
140 private void paintBackground(final Graphics g, JComponent c,
141 final Dimension dim, final Point start) {
142 final Color back = this.getRealBackgroundColor(c);
143 if (back != null) {
144 g.setColor(back);
145 g.fillRect(start.x, start.y, dim.width, dim.height);
146 }
147 }
148
149 private Point getStartPointWithBordersAndAdjustDimension(JComponent c,
150 final Dimension dim) {
151 Point start = new Point(0, 0);
152 final Border border = c.getBorder();
153 if (border != null) {
154 final Insets insets = border.getBorderInsets(c);
155 if (insets != null) {
156 dim.width -= insets.left + insets.right;
157 dim.height -= insets.top + insets.bottom;
158 start = new Point(insets.left, insets.top);
159 }
160 }
161 return start;
162 }
163
164 private Color getRealBackgroundColor(JComponent c) {
165 Color back = c.getBackground();
166 if (c.isOpaque()) {
167 if (back == null) {
168 back = Color.WHITE;
169 }
170
171 back = new Color(back.getRGB());
172 }
173 return back;
174 }
175
176
177 @Override
178 public void installUI(final JComponent c) {
179 if (c instanceof JMathComponent) {
180 c.addPropertyChangeListener(this);
181 this.installDefaults(c);
182 } else {
183 throw new IllegalArgumentException(
184 "This UI can only be installed on a JMathComponent");
185 }
186 }
187
188
189
190
191
192
193
194 protected void installDefaults(final JComponent c) {
195
196
197 LookAndFeel.installProperty(c, "opaque", Boolean.FALSE);
198 }
199
200
201 @Override
202 public void uninstallUI(final JComponent c) {
203 c.removePropertyChangeListener(this);
204 this.contextCache.remove(c);
205 }
206
207
208 public void propertyChange(final PropertyChangeEvent evt) {
209 this.contextCache.remove(evt.getSource());
210 }
211
212
213 @Override
214 public Dimension getPreferredSize(final JComponent c) {
215 return this.getMathComponentSize(c);
216 }
217
218
219
220
221
222
223
224
225 private Dimension getMathComponentSize(final JComponent c) {
226 JEuclidView jEuclidView = this.getJeuclidView(c.getGraphics(), c);
227 return this.calculatePreferredSize(c, jEuclidView);
228 }
229
230 private Dimension calculatePreferredSize(final JComponent c,
231 JEuclidView jEuclidView) {
232 Dimension retVal;
233 retVal = new Dimension((int) Math.ceil(jEuclidView.getWidth()),
234 (int) Math.ceil(jEuclidView.getAscentHeight()
235 + jEuclidView.getDescentHeight()));
236
237 final Border border = c.getBorder();
238 if (border != null) {
239 final Insets insets = border.getBorderInsets(c);
240 if (insets != null) {
241 retVal.width += insets.left + insets.right;
242 retVal.height += insets.top + insets.bottom;
243 }
244 }
245 return retVal;
246 }
247
248
249 @Override
250 public Dimension getMaximumSize(final JComponent c) {
251 return this.getMathComponentSize(c);
252 }
253
254
255 @Override
256 public Dimension getMinimumSize(final JComponent c) {
257 return this.getMathComponentSize(c);
258 }
259
260
261
262
263
264
265
266
267
268
269
270
271
272 public List<JEuclidView.NodeRect> getNodesAt(JMathComponent mathComponent,
273 final float x, final float y) {
274 JEuclidView jEuclidView = getJeuclidView(mathComponent.getGraphics(),
275 mathComponent);
276 final Point2D point = this.calculateAlignmentOffset(mathComponent,
277 jEuclidView, mathComponent.getSize());
278 return jEuclidView.getNodesAt(x, y, (float) point.getX(),
279 (float) point.getY());
280 }
281
282 private static class ViewContext {
283 final Node document;
284 final LayoutContext layoutContext;
285 final Map<Graphics2D, JEuclidView> jeuclidViews = new HashMap<Graphics2D, JEuclidView>();
286
287 public ViewContext(JMathComponent jMathComponent) {
288 this.document = jMathComponent.getDocument();
289 this.layoutContext = jMathComponent.getParameters();
290 }
291
292 public JEuclidView getJeculidView(Graphics2D g2d) {
293 JEuclidView jeuclidView = jeuclidViews.get(g2d);
294 if (jeuclidView == null) {
295 jeuclidView = new JEuclidView(document, layoutContext, g2d);
296 jeuclidViews.put(g2d, jeuclidView);
297 }
298 return jeuclidView;
299 }
300 }
301
302 }