View Javadoc

1   /*
2    * Copyright 2002 - 2008 JEuclid, http://jeuclid.sf.net
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  /* $Id: JMathComponent.java,v f1695c1926a6 2010/08/09 21:09:55 max $ */
18  
19  package net.sourceforge.jeuclid.swing;
20  
21  import java.awt.Color;
22  import java.awt.Font;
23  import java.io.IOException;
24  import java.util.Arrays;
25  import java.util.Collections;
26  import java.util.List;
27  import java.util.Map;
28  
29  import javax.swing.JComponent;
30  import javax.swing.SwingConstants;
31  import javax.swing.UIManager;
32  import javax.xml.parsers.ParserConfigurationException;
33  
34  import net.sourceforge.jeuclid.DOMBuilder;
35  import net.sourceforge.jeuclid.MathMLParserSupport;
36  import net.sourceforge.jeuclid.MathMLSerializer;
37  import net.sourceforge.jeuclid.MutableLayoutContext;
38  import net.sourceforge.jeuclid.context.LayoutContextImpl;
39  import net.sourceforge.jeuclid.context.Parameter;
40  import net.sourceforge.jeuclid.elements.generic.DocumentElement;
41  import net.sourceforge.jeuclid.elements.support.ClassLoaderSupport;
42  
43  import org.apache.commons.logging.Log;
44  import org.apache.commons.logging.LogFactory;
45  import org.w3c.dom.Document;
46  import org.w3c.dom.Node;
47  import org.xml.sax.SAXException;
48  
49  /**
50   * Displays MathML content in a Swing Component.
51   * <p>
52   * There are two properties which expose the actual content, accessible though
53   * {@link #getDocument()} / {@link #setDocument(org.w3c.dom.Node)} for content
54   * already available as a DOM model, and {@link #getContent()} and
55   * {@link #setContent(String)} for content available as a String.
56   * <p>
57   * This class exposes most of the rendering parameters as standard bean
58   * attributes. If you need to set additional attributes, you may use the
59   * {@link #setParameter(Parameter, Object)} function.
60   * <p>
61   * Please use only the attributes exposed through the attached
62   * {@link JMathComponentBeanInfo} class. Additional attributes, such as
63   * {@link #getFont()} and {@link #setFont(Font)} are provided for Swing
64   * compatibility, but they may not work exactly as expected.
65   * 
66   * @see net.sourceforge.jeuclid.awt.MathComponent
67   * @version $Revision: f1695c1926a6 $
68   */
69  public final class JMathComponent extends JComponent implements
70          SwingConstants {
71  
72      private static final String FONT_SEPARATOR = ",";
73  
74      /**
75       * Logger for this class
76       */
77      private static final Log LOGGER = LogFactory.getLog(JMathComponent.class);
78  
79      /** */
80      private static final long serialVersionUID = 1L;
81  
82      private static String uiClassId;
83  
84      private static Class<?> mathComponentUIClass;
85  
86      private Node document;
87  
88      private int horizontalAlignment = SwingConstants.CENTER;
89  
90      private final MutableLayoutContext parameters = new LayoutContextImpl(
91              LayoutContextImpl.getDefaultLayoutContext());
92  
93      private int verticalAlignment = SwingConstants.CENTER;
94  
95      /**
96       * cursor listener instance.
97       */
98      private final CursorListener cursorListener;
99  
100     /**
101      * Default constructor.
102      */
103     public JMathComponent() {
104         this(null);
105     }
106 
107     /**
108      * Default constructor with cursor listener.
109      * 
110      * @param listener
111      *            cursor listener instance
112      */
113     public JMathComponent(final CursorListener listener) {
114         this.cursorListener = listener;
115 
116         final JMathComponentMouseListener mouseListener = new JMathComponentMouseListener(
117                 this);
118         this.addMouseListener(mouseListener);
119 
120         this.updateUI();
121         this.fontCompat();
122         this.setDocument(new DocumentElement());
123     }
124 
125     /**
126      * gets cursor listener instance.
127      * 
128      * @return cursor listener instance
129      */
130     public CursorListener getCursorListener() {
131         return this.cursorListener;
132     }
133 
134     /**
135      * Provide compatibility for standard get/setFont() operations.
136      */
137     private void fontCompat() {
138         final String fontName = this.getFontsSerif().split(
139                 JMathComponent.FONT_SEPARATOR)[0];
140         final float fontSize = this.getFontSize();
141         super.setFont(new Font(fontName, 0, (int) fontSize));
142     }
143 
144     /**
145      * Tries to return the content as a String.
146      * <p>
147      * This transforms the internal DOM tree back into a string, which may is
148      * not guaranteed to be the literally same as the original content.
149      * However, it will represent the same XML document.
150      * 
151      * @return the content string.
152      */
153     public String getContent() {
154         return MathMLSerializer.serializeDocument(this.getDocument(), false,
155                 false);
156     }
157 
158     /**
159      * @return the document
160      */
161     public Node getDocument() {
162         return this.document;
163     }
164 
165     private static String join(final List<String> list) {
166         boolean first = true;
167         final StringBuilder b = new StringBuilder();
168         for (final String s : list) {
169             if (first) {
170                 first = false;
171             } else {
172                 b.append(JMathComponent.FONT_SEPARATOR);
173             }
174             b.append(s);
175         }
176         return b.toString();
177     }
178 
179     /**
180      * Font list for Doublestruck. Please see
181      * {@link Parameter#FontsDoublestruck} for an explanation of this
182      * parameter.
183      * 
184      * @return The list for Doublestruck.
185      * @see Parameter#FontsDoublestruck
186      */
187     @SuppressWarnings("unchecked")
188     public String getFontsDoublestruck() {
189         return JMathComponent.join((List<String>) this.parameters
190                 .getParameter(Parameter.FONTS_DOUBLESTRUCK));
191     }
192 
193     /**
194      * Font list for Fraktur. Please see {@link Parameter#FontsFraktur} for an
195      * explanation of this parameter.
196      * 
197      * @return The list for Fraktur.
198      * @see Parameter#FontsFraktur
199      */
200     @SuppressWarnings("unchecked")
201     public String getFontsFraktur() {
202         return JMathComponent.join((List<String>) this.parameters
203                 .getParameter(Parameter.FONTS_FRAKTUR));
204     }
205 
206     /**
207      * @return the fontSize
208      */
209     public float getFontSize() {
210         return (Float) this.parameters.getParameter(Parameter.MATHSIZE);
211     }
212 
213     /**
214      * Font list for Monospaced. Please see {@link Parameter#FontsMonospaced}
215      * for an explanation of this parameter.
216      * 
217      * @return The list for monospaced.
218      * @see Parameter#FontsMonospaced
219      */
220     @SuppressWarnings("unchecked")
221     public String getFontsMonospaced() {
222         return JMathComponent.join((List<String>) this.parameters
223                 .getParameter(Parameter.FONTS_MONOSPACED));
224     }
225 
226     /**
227      * Font list for Sans-Serif. Please see {@link Parameter#FontsSanserif}
228      * for an explanation of this parameter.
229      * 
230      * @return The list for sansserif.
231      * @see Parameter#FontsSanserif
232      */
233     @SuppressWarnings("unchecked")
234     public String getFontsSanserif() {
235         return JMathComponent.join((List<String>) this.parameters
236                 .getParameter(Parameter.FONTS_SANSSERIF));
237     }
238 
239     /**
240      * Font list for Script. Please see {@link Parameter#FontsScript} for an
241      * explanation of this parameter.
242      * 
243      * @return The list for Script.
244      * @see Parameter#FontsScript
245      */
246     @SuppressWarnings("unchecked")
247     public String getFontsScript() {
248         return JMathComponent.join((List<String>) this.parameters
249                 .getParameter(Parameter.FONTS_SCRIPT));
250     }
251 
252     /**
253      * Font list for Serif (the default MathML font). Please see
254      * {@link Parameter#FontsSerif} for an explanation of this parameter.
255      * 
256      * @return The list for serif.
257      * @see Parameter#FontsSerif
258      */
259     @SuppressWarnings("unchecked")
260     public String getFontsSerif() {
261         return JMathComponent.join((List<String>) this.parameters
262                 .getParameter(Parameter.FONTS_SERIF));
263     }
264 
265     /** {@inheritDoc} */
266     @Override
267     public Color getForeground() {
268         return (Color) this.parameters.getParameter(Parameter.MATHCOLOR);
269     }
270 
271     /**
272      * Horizontal alignment, as defined by
273      * {@link javax.swing.JLabel#getHorizontalAlignment()}.
274      * <p>
275      * Supported are: {@link SwingConstants#LEADING},
276      * {@link SwingConstants#LEFT}, {@link SwingConstants#CENTER},
277      * {@link SwingConstants#TRAILING}, {@link SwingConstants#RIGHT}.
278      * 
279      * @return the horizontalAlignment
280      * @see javax.swing.JLabel#getHorizontalAlignment()
281      */
282     public int getHorizontalAlignment() {
283         return this.horizontalAlignment;
284     }
285 
286     /**
287      * @return the UI implementation.
288      */
289     public MathComponentUI getUI() {
290         return (MathComponentUI) this.ui;
291     }
292 
293     /**
294      * @return The default UI class
295      */
296     @Override
297     public String getUIClassID() {
298         return JMathComponent.uiClassId;
299     }
300 
301     /**
302      * Vertical alignment, as defined by
303      * {@link javax.swing.JLabel#getVerticalAlignment()}.
304      * <p>
305      * Supported are: {@link SwingConstants#TOP},
306      * {@link SwingConstants#CENTER}, {@link SwingConstants#BOTTOM}.
307      * 
308      * @return the verticalAlignment
309      * @see javax.swing.JLabel#getVerticalAlignment()
310      */
311     public int getVerticalAlignment() {
312         return this.verticalAlignment;
313     }
314 
315     private void reval() {
316         this.repaint();
317         this.revalidate();
318     }
319 
320     /** {@inheritDoc} */
321     @Override
322     public void setBackground(final Color c) {
323         super.setBackground(c);
324         this.reval();
325     }
326 
327     /**
328      * Set the content from a String containing the MathML content.
329      * 
330      * @param contentString
331      *            the content to set.
332      */
333     public void setContent(final String contentString) {
334         try {
335             final Document stdDomNode = MathMLParserSupport.parseString(contentString); 
336             final DocumentElement jEuclidDom = DOMBuilder.getInstance().createJeuclidDom(stdDomNode,
337                     true, true);
338             this.setDocument(jEuclidDom);
339         } catch (final SAXException e) {
340             throw new IllegalArgumentException(e);
341         } catch (final ParserConfigurationException e) {
342             throw new IllegalArgumentException(e);
343         } catch (final IOException e) {
344             throw new IllegalArgumentException(e);
345         }
346 
347     }
348 
349     /**
350      * Enables, or disables the debug mode.
351      * 
352      * @param dbg
353      *            Debug mode.
354      */
355     public void setDebug(final boolean dbg) {
356         this.setParameter(Parameter.DEBUG, dbg);
357     }
358 
359     /**
360      * @param doc
361      *            the document to set
362      */
363     public void setDocument(final Node doc) {
364         final Node oldValue = this.document;
365         this.firePropertyChange("document", oldValue, doc);
366         this.document = doc;
367         if (doc != oldValue) {
368             this.revalidate();
369             this.repaint();
370         }
371     }
372 
373     /**
374      * Font emulator for standard component behavior.
375      * <p>
376      * Emulates the standard setFont function by setting the font Size and
377      * adding the font to the front of the serif font list.
378      * <p>
379      * Please use the separate setters if possible.
380      * 
381      * @param f
382      *            font to set.
383      * @see #setFontSize(float)
384      * @see #setFontsSerif(String)
385      * @deprecated use separate setters.
386      */
387     @Deprecated
388     @Override
389     public void setFont(final Font f) {
390         super.setFont(f);
391         this.setFontSize(f.getSize2D());
392         this.setFontsSerif(f.getFamily() + JMathComponent.FONT_SEPARATOR
393                 + this.getFontsSerif());
394     }
395 
396     private List<String> splitFonts(final String list) {
397         return Arrays.asList(list.split(JMathComponent.FONT_SEPARATOR));
398     }
399 
400     /**
401      * Font list for Doublestruck. Please see
402      * {@link Parameter#FONTS_DOUBLESTRUCK} for an explanation of this
403      * parameter.
404      * 
405      * @param newFonts
406      *            new list for Doublestruck (comma seraparated).
407      * @see Parameter#FONTS_DOUBLESTRUCK
408      */
409     public void setFontsDoublestruck(final String newFonts) {
410         this.setParameter(Parameter.FONTS_DOUBLESTRUCK, this
411                 .splitFonts(newFonts));
412     }
413 
414     /**
415      * Font list for Fraktur. Please see {@link Parameter#FONTS_FRAKTUR} for
416      * an explanation of this parameter.
417      * 
418      * @param newFonts
419      *            new list for Fraktur (comma seraparated).
420      * @see Parameter#FONTS_FRAKTUR
421      */
422     public void setFontsFraktur(final String newFonts) {
423         this.setParameter(Parameter.FONTS_FRAKTUR, this.splitFonts(newFonts));
424     }
425 
426     /**
427      * Sets a generic rendering parameter.
428      * 
429      * @param key
430      *            Key for the parameter
431      * @param newValue
432      *            newValue
433      */
434     public void setParameter(final Parameter key, final Object newValue) {
435         this.setParameters(Collections.singletonMap(key, newValue));
436     }
437 
438     /**
439      * Sets generic rendering parameters.
440      * 
441      * @param newValues
442      *            map of parameter keys to new values
443      */
444     public void setParameters(final Map<Parameter, Object> newValues) {
445         for (final Map.Entry<Parameter, Object> entry : newValues.entrySet()) {
446             final Parameter key = entry.getKey();
447             final Object oldValue = this.parameters.getParameter(key);
448             this.parameters.setParameter(key, entry.getValue());
449             this.firePropertyChange(key.name(), oldValue, this.parameters
450                     .getParameter(key));
451         }
452         this.revalidate();
453         this.repaint();
454     }
455 
456     /**
457      * sets the font size used.
458      * 
459      * @param fontSize
460      *            the font size.
461      */
462     public void setFontSize(final float fontSize) {
463         this.setParameter(Parameter.MATHSIZE, fontSize);
464     }
465 
466     /**
467      * Font list for Monospaced. Please see {@link Parameter#FONTS_MONOSPACED}
468      * for an explanation of this parameter.
469      * 
470      * @param newFonts
471      *            new list for Monospaced (comma seraparated).
472      * @see Parameter#FONTS_MONOSPACED
473      */
474     public void setFontsMonospaced(final String newFonts) {
475         this.setParameter(Parameter.FONTS_MONOSPACED, this
476                 .splitFonts(newFonts));
477     }
478 
479     /**
480      * Font list for Sans-Serif. Please see {@link Parameter#FONTS_SANSSERIF}
481      * for an explanation of this parameter.
482      * 
483      * @param newFonts
484      *            new list for sansserif (comma seraparated).
485      * @see Parameter#FONTS_SANSSERIF
486      */
487     public void setFontsSanserif(final String newFonts) {
488         this.setParameter(Parameter.FONTS_SANSSERIF, this
489                 .splitFonts(newFonts));
490     }
491 
492     /**
493      * Font list for Script. Please see {@link Parameter#FONTS_SCRIPT} for an
494      * explanation of this parameter.
495      * 
496      * @param newFonts
497      *            new list for Script (comma seraparated).
498      * @see Parameter#FONTS_SCRIPT
499      */
500     public void setFontsScript(final String newFonts) {
501         this.setParameter(Parameter.FONTS_SCRIPT, this.splitFonts(newFonts));
502     }
503 
504     /**
505      * Font list for Serif (the default MathML font). Please see
506      * {@link Parameter#FONTS_SERIF} for an explanation of this parameter.
507      * 
508      * @param newFonts
509      *            new list for serif (comma seraparated).
510      * @see Parameter#FONTS_SERIF
511      */
512     public void setFontsSerif(final String newFonts) {
513         this.setParameter(Parameter.FONTS_SERIF, this.splitFonts(newFonts));
514         this.fontCompat();
515     }
516 
517     /** {@inheritDoc} */
518     @Override
519     public void setForeground(final Color fg) {
520         super.setForeground(fg);
521         this.setParameter(Parameter.MATHCOLOR, fg);
522     }
523 
524     /**
525      * Horizontal alignment, as defined by
526      * {@link javax.swing.JLabel#setHorizontalAlignment(int)}.
527      * <p>
528      * Supported are: {@link SwingConstants#LEADING},
529      * {@link SwingConstants#LEFT}, {@link SwingConstants#CENTER},
530      * {@link SwingConstants#TRAILING}, {@link SwingConstants#RIGHT}.
531      * 
532      * @param hAlignment
533      *            the horizontalAlignment to set
534      * @see javax.swing.JLabel#setHorizontalAlignment(int)
535      */
536     public void setHorizontalAlignment(final int hAlignment) {
537         this.horizontalAlignment = hAlignment;
538     }
539 
540     /** {@inheritDoc} */
541     @Override
542     public void setOpaque(final boolean opaque) {
543         super.setOpaque(opaque);
544         this.reval();
545     }
546 
547     /**
548      * Vertical alignment, as defined by
549      * {@link javax.swing.JLabel#setVerticalAlignment(int)}.
550      * <p>
551      * Supported are: {@link SwingConstants#TOP},
552      * {@link SwingConstants#CENTER}, {@link SwingConstants#BOTTOM}.
553      * 
554      * @param vAlignment
555      *            the verticalAlignment to set
556      * @see javax.swing.JLabel#setVerticalAlignment(int)
557      */
558     public void setVerticalAlignment(final int vAlignment) {
559         this.verticalAlignment = vAlignment;
560     }
561 
562     /** {@inheritDoc} */
563     @Override
564     public void updateUI() {
565         if (UIManager.get(this.getUIClassID()) == null) {
566             try {
567                 this
568                         .setUI((MathComponentUI) JMathComponent.mathComponentUIClass
569                                 .newInstance());
570             } catch (final InstantiationException e) {
571                 JMathComponent.LOGGER.warn(e.getMessage());
572             } catch (final IllegalAccessException e) {
573                 JMathComponent.LOGGER.warn(e.getMessage());
574             }
575         } else {
576             this.setUI(UIManager.getUI(this));
577         }
578     }
579 
580     /**
581      * @return the parameters
582      */
583     public MutableLayoutContext getParameters() {
584         return this.parameters;
585     }
586 
587     /** {@inheritDoc} */
588     @Override
589     public void setSize(final int width, final int height) {
590         // TODO Auto-generated method stub
591         super.setSize(width, height);
592     }
593 
594     static {
595         Class<?> uiClass;
596         String id;
597         try {
598             uiClass = ClassLoaderSupport.getInstance().loadClass(
599                     "net.sourceforge.jeuclid.swing.MathComponentUI16");
600             id = "MathComponentUI16";
601         } catch (final ClassNotFoundException t) {
602             uiClass = MathComponentUI.class;
603             id = "MathComponentUI";
604         }
605         JMathComponent.uiClassId = id;
606         JMathComponent.mathComponentUIClass = uiClass;
607     }
608 
609 }