001    /*
002     * Copyright 2002 - 2007 JEuclid, http://jeuclid.sf.net
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    /* $Id: JMathComponent.java 433 2007-08-20 11:59:25Z maxberger $ */
018    
019    package net.sourceforge.jeuclid.swing;
020    
021    import java.awt.Color;
022    import java.awt.Dimension;
023    import java.awt.Font;
024    import java.io.IOException;
025    import java.util.Map;
026    
027    import javax.swing.JComponent;
028    import javax.swing.SwingConstants;
029    import javax.swing.UIManager;
030    import javax.xml.parsers.ParserConfigurationException;
031    
032    import net.sourceforge.jeuclid.MathBase;
033    import net.sourceforge.jeuclid.MathMLParserSupport;
034    import net.sourceforge.jeuclid.MathMLSerializer;
035    import net.sourceforge.jeuclid.ParameterKey;
036    import net.sourceforge.jeuclid.elements.support.attributes.AttributesHelper;
037    
038    import org.w3c.dom.Document;
039    import org.xml.sax.SAXException;
040    
041    /**
042     * Displays MathML content in a Swing Component.
043     * <p>
044     * There are two properties which expose the actual content, accessible though
045     * {@link #getDocument()} / {@link #setDocument(Document)} for content already
046     * available as a DOM model, and {@link #getContent()} and
047     * {@link #setContent(String)} for content available as a String.
048     * <p>
049     * This class exposes most of the rendering parameters as standard bean
050     * attributes. If you need to set additional attributes, you may use the
051     * {@link #setParameter(ParameterKey, String)} function.
052     * <p>
053     * Please use only the attributes exposed through the attached
054     * {@link JMathComponentBeanInfo} class. Additional attributes, such as
055     * {@link #getFont()} and {@link #setFont(Font)} are provided for Swing
056     * compatibility, but they may not work exactly as expected.
057     * 
058     * @see net.sourceforge.jeuclid.awt.MathComponent
059     * @author Unknown
060     * @author Max Berger
061     * @version $Revision: 433 $
062     */
063    public class JMathComponent extends JComponent implements SwingConstants {
064    
065        private static final String DEFAULT_DOCUMENT = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
066                + "<math mode=\"display\">\n"
067                + "    <mrow>\n"
068                + "        <munderover>\n"
069                + "            <mo>&#x0222B;</mo>\n"
070                + "            <mn>1</mn>\n"
071                + "            <mi>x</mi>\n"
072                + "        </munderover>\n"
073                + "        <mfrac>\n"
074                + "            <mi>dt</mi>\n"
075                + "            <mi>t</mi>\n"
076                + "        </mfrac>\n" + "    </mrow>\n" + "</math>";
077    
078        private static final String FONT_SEPARATOR = ",";
079    
080        /**
081         * Logger for this class
082         */
083        // currently unused.
084        // private static final Log LOGGER =
085        // LogFactory.getLog(JMathComponent.class);
086        /** */
087        private static final long serialVersionUID = 1L;
088    
089        private static final String UI_CLASS_ID = "MathComponentUI";
090    
091        private Document document;
092    
093        private int horizontalAlignment = SwingConstants.CENTER;
094    
095        private final Map<ParameterKey, String> parameters = MathBase
096                .getDefaultParameters();
097    
098        private int verticalAlignment = SwingConstants.CENTER;
099    
100        /**
101         * Default constructor.
102         */
103        public JMathComponent() {
104            this.updateUI();
105            this.fontCompat();
106            this.setContent(JMathComponent.DEFAULT_DOCUMENT);
107        }
108    
109        /**
110         * Provide compatibility for standard get/setFont() operations.
111         */
112        private void fontCompat() {
113            final String fontName = this.getFontsSerif().split(
114                    JMathComponent.FONT_SEPARATOR)[0];
115            final float fontSize = this.getFontSize();
116            super.setFont(new Font(fontName, 0, (int) fontSize));
117        }
118    
119        /**
120         * Tries to return the content as a String.
121         * <p>
122         * This transforms the internal DOM tree back into a string, which may is
123         * not guaranteed to be the literally same as the original content.
124         * However, it will represent the same XML document.
125         * 
126         * @return the content string.
127         */
128        public String getContent() {
129            return MathMLSerializer.serializeDocument(this.getDocument(), false,
130                    false);
131        }
132    
133        /**
134         * @return the document
135         */
136        public Document getDocument() {
137            return this.document;
138        }
139    
140        /**
141         * Font list for Doublestruck. Please see
142         * {@link ParameterKey#FontsDoublestruck} for an explanation of this
143         * parameter.
144         * 
145         * @return The list for Doublestruck.
146         * @see ParameterKey#FontsDoublestruck
147         */
148        public String getFontsDoublestruck() {
149            return this.parameters.get(ParameterKey.FontsDoublestruck);
150        }
151    
152        /**
153         * Font list for Fraktur. Please see {@link ParameterKey#FontsFraktur} for
154         * an explanation of this parameter.
155         * 
156         * @return The list for Fraktur.
157         * @see ParameterKey#FontsFraktur
158         */
159        public String getFontsFraktur() {
160            return this.parameters.get(ParameterKey.FontsFraktur);
161        }
162    
163        /**
164         * @return the fontSize
165         */
166        public float getFontSize() {
167            return Float.parseFloat(this.parameters.get(ParameterKey.FontSize));
168        }
169    
170        /**
171         * Font list for Monospaced. Please see
172         * {@link ParameterKey#FontsMonospaced} for an explanation of this
173         * parameter.
174         * 
175         * @return The list for monospaced.
176         * @see ParameterKey#FontsMonospaced
177         */
178        public String getFontsMonospaced() {
179            return this.parameters.get(ParameterKey.FontsMonospaced);
180        }
181    
182        /**
183         * Font list for Sans-Serif. Please see {@link ParameterKey#FontsSanserif}
184         * for an explanation of this parameter.
185         * 
186         * @return The list for sansserif.
187         * @see ParameterKey#FontsSanserif
188         */
189        public String getFontsSanserif() {
190            return this.parameters.get(ParameterKey.FontsSanserif);
191        }
192    
193        /**
194         * Font list for Script. Please see {@link ParameterKey#FontsScript} for
195         * an explanation of this parameter.
196         * 
197         * @return The list for Script.
198         * @see ParameterKey#FontsScript
199         */
200        public String getFontsScript() {
201            return this.parameters.get(ParameterKey.FontsScript);
202        }
203    
204        /**
205         * Font list for Serif (the default MathML font). Please see
206         * {@link ParameterKey#FontsSerif} for an explanation of this parameter.
207         * 
208         * @return The list for serif.
209         * @see ParameterKey#FontsSerif
210         */
211        public String getFontsSerif() {
212            return this.parameters.get(ParameterKey.FontsSerif);
213        }
214    
215        /** {@inheritDoc} */
216        @Override
217        public Color getForeground() {
218            return AttributesHelper.stringToColor(this.parameters
219                    .get(ParameterKey.ForegroundColor), Color.BLACK);
220        }
221    
222        /**
223         * Horizontal alignment, as defined by
224         * {@link javax.swing.JLabel#getHorizontalAlignment()}.
225         * <p>
226         * Supported are: {@link SwingConstants#LEADING},
227         * {@link SwingConstants#LEFT}, {@link SwingConstants#CENTER},
228         * {@link SwingConstants#TRAILING}, {@link SwingConstants#RIGHT}.
229         * 
230         * @return the horizontalAlignment
231         * @see javax.swing.JLabel#getHorizontalAlignment()
232         */
233        public int getHorizontalAlignment() {
234            return this.horizontalAlignment;
235        }
236    
237        /**
238         * Gets the preferred size of this component.
239         * 
240         * @return A dimension object indicating this component's preferred size.
241         */
242        @Override
243        public Dimension getPreferredSize() {
244            return this.getMinimumSize();
245        }
246    
247        /**
248         * @return the UI implementation.
249         */
250        public MathComponentUI getUI() {
251            return (MathComponentUI) this.ui;
252        }
253    
254        /**
255         * @return The default UI class
256         */
257        @Override
258        public String getUIClassID() {
259            return JMathComponent.UI_CLASS_ID;
260        }
261    
262        /**
263         * Vertical alignment, as defined by
264         * {@link javax.swing.JLabel#getVerticalAlignment()}.
265         * <p>
266         * Supported are: {@link SwingConstants#TOP},
267         * {@link SwingConstants#CENTER}, {@link SwingConstants#BOTTOM}.
268         * 
269         * @return the verticalAlignment
270         * @see javax.swing.JLabel#getVerticalAlignment()
271         */
272        public int getVerticalAlignment() {
273            return this.verticalAlignment;
274        }
275    
276        private void reval() {
277            this.repaint();
278            this.revalidate();
279        }
280    
281        /** {@inheritDoc} */
282        @Override
283        public void setBackground(final Color c) {
284            super.setBackground(c);
285            this.reval();
286        }
287    
288        /**
289         * Set the content from a String containing the MathML content.
290         * 
291         * @param contentString
292         *            the content to set.
293         */
294        public void setContent(final String contentString) {
295            try {
296                this.setDocument(MathMLParserSupport.parseString(contentString));
297            } catch (final SAXException e) {
298                throw new RuntimeException(e);
299            } catch (final ParserConfigurationException e) {
300                throw new RuntimeException(e);
301            } catch (final IOException e) {
302                throw new RuntimeException(e);
303            }
304        }
305    
306        /**
307         * Enables, or disables the debug mode.
308         * 
309         * @param dbg
310         *            Debug mode.
311         */
312        public void setDebug(final boolean dbg) {
313            this.parameterChange(ParameterKey.DebugMode, Boolean.toString(dbg));
314        }
315    
316        /**
317         * @param doc
318         *            the document to set
319         */
320        public void setDocument(final Document doc) {
321            final Document oldValue = this.document;
322            this.firePropertyChange("document", oldValue, doc);
323            this.document = doc;
324            if (doc != oldValue) {
325                this.revalidate();
326                this.repaint();
327            }
328        }
329    
330        /**
331         * Font emulator for standard component behaviour.
332         * <p>
333         * Emulates the standard setFont function by setting the font Size and
334         * adding the font to the front of the serif font list.
335         * <p>
336         * Please use the separate setters if possible.
337         * 
338         * @param f
339         *            font to set.
340         * @see #setFontSize(float)
341         * @see #setFontsSerif(String)
342         * @deprecated
343         */
344        @Deprecated
345        @Override
346        public void setFont(final Font f) {
347            super.setFont(f);
348            this.setFontSize(f.getSize2D());
349            this.setFontsSerif(f.getFamily() + JMathComponent.FONT_SEPARATOR
350                    + this.getFontsSerif());
351        }
352    
353        /**
354         * Font list for Doublestruck. Please see
355         * {@link ParameterKey#FontsDoublestruck} for an explanation of this
356         * parameter.
357         * 
358         * @param newFonts
359         *            new list for Doublestruck (comma seraparated).
360         * @see ParameterKey#FontsDoublestruck
361         */
362        public void setFontsDoublestruck(final String newFonts) {
363            this.parameterChange(ParameterKey.FontsDoublestruck, newFonts);
364        }
365    
366        /**
367         * Font list for Fraktur. Please see {@link ParameterKey#FontsFraktur} for
368         * an explanation of this parameter.
369         * 
370         * @param newFonts
371         *            new list for Fraktur (comma seraparated).
372         * @see ParameterKey#FontsFraktur
373         */
374        public void setFontsFraktur(final String newFonts) {
375            this.parameterChange(ParameterKey.FontsFraktur, newFonts);
376        }
377    
378        private void parameterChange(final ParameterKey key, final String newValue) {
379            final String oldValue = this.parameters.get(key);
380            this.parameters.put(key, newValue);
381            this.firePropertyChange(key.name(), oldValue, this.parameters
382                    .get(key));
383            this.revalidate();
384            this.repaint();
385        }
386    
387        /**
388         * sets the font size used.
389         * 
390         * @param fontSize
391         *            the font size.
392         */
393        public void setFontSize(final float fontSize) {
394            this.parameterChange(ParameterKey.FontSize, Float.toString(fontSize));
395        }
396    
397        /**
398         * Font list for Monospaced. Please see
399         * {@link ParameterKey#FontsMonospaced} for an explanation of this
400         * parameter.
401         * 
402         * @param newFonts
403         *            new list for Monospaced (comma seraparated).
404         * @see ParameterKey#FontsMonospaced
405         */
406        public void setFontsMonospaced(final String newFonts) {
407            this.parameterChange(ParameterKey.FontsMonospaced, newFonts);
408        }
409    
410        /**
411         * Font list for Sans-Serif. Please see {@link ParameterKey#FontsSanserif}
412         * for an explanation of this parameter.
413         * 
414         * @param newFonts
415         *            new list for sansserif (comma seraparated).
416         * @see ParameterKey#FontsSanserif
417         */
418        public void setFontsSanserif(final String newFonts) {
419            this.parameterChange(ParameterKey.FontsSanserif, newFonts);
420        }
421    
422        /**
423         * Font list for Script. Please see {@link ParameterKey#FontsScript} for
424         * an explanation of this parameter.
425         * 
426         * @param newFonts
427         *            new list for Script (comma seraparated).
428         * @see ParameterKey#FontsScript
429         */
430        public void setFontsScript(final String newFonts) {
431            this.parameterChange(ParameterKey.FontsScript, newFonts);
432        }
433    
434        /**
435         * Font list for Serif (the default MathML font). Please see
436         * {@link ParameterKey#FontsSerif} for an explanation of this parameter.
437         * 
438         * @param newFonts
439         *            new list for serif (comma seraparated).
440         * @see ParameterKey#FontsSerif
441         */
442        public void setFontsSerif(final String newFonts) {
443            this.parameterChange(ParameterKey.FontsSerif, newFonts);
444            this.fontCompat();
445        }
446    
447        /** {@inheritDoc} */
448        @Override
449        public void setForeground(final Color fg) {
450            super.setForeground(fg);
451            final String colorAsHex = AttributesHelper.colorTOsRGBString(fg);
452            this.parameterChange(ParameterKey.ForegroundColor, colorAsHex);
453        }
454    
455        /**
456         * Horizontal alignment, as defined by
457         * {@link javax.swing.JLabel#setHorizontalAlignment(int)}.
458         * <p>
459         * Supported are: {@link SwingConstants#LEADING},
460         * {@link SwingConstants#LEFT}, {@link SwingConstants#CENTER},
461         * {@link SwingConstants#TRAILING}, {@link SwingConstants#RIGHT}.
462         * 
463         * @param hAlignment
464         *            the horizontalAlignment to set
465         * @see javax.swing.JLabel#setHorizontalAlignment(int)
466         */
467        public void setHorizontalAlignment(final int hAlignment) {
468            this.horizontalAlignment = hAlignment;
469        }
470    
471        /** {@inheritDoc} */
472        @Override
473        public void setOpaque(final boolean opaque) {
474            super.setOpaque(opaque);
475            this.reval();
476        }
477    
478        /**
479         * Sets a generic JEuclid rendering parameter. Please see
480         * {@link ParameterKey} for a list of possible values.
481         * 
482         * @param key
483         *            the parameter to set.
484         * @param value
485         *            the value to set it to.
486         */
487        public final void setParameter(final ParameterKey key, final String value) {
488            this.parameterChange(key, value);
489        }
490    
491        /**
492         * Vertical alignment, as defined by
493         * {@link javax.swing.JLabel#setVerticalAlignment(int)}.
494         * <p>
495         * Supported are: {@link SwingConstants#TOP},
496         * {@link SwingConstants#CENTER}, {@link SwingConstants#BOTTOM}.
497         * 
498         * @param vAlignment
499         *            the verticalAlignment to set
500         * @see javax.swing.JLabel#setVerticalAlignment(int)
501         */
502        public void setVerticalAlignment(final int vAlignment) {
503            this.verticalAlignment = vAlignment;
504        }
505    
506        /** {@inheritDoc} */
507        @Override
508        public void updateUI() {
509            if (UIManager.get(this.getUIClassID()) != null) {
510                this.setUI(UIManager.getUI(this));
511            } else {
512                this.setUI(new MathComponentUI());
513            }
514        }
515    
516        /**
517         * @return the parameters
518         */
519        public Map<ParameterKey, String> getParameters() {
520            return this.parameters;
521        }
522    
523    }