001 /* 002 * Copyright 2002 - 2008 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,v f1695c1926a6 2010/08/09 21:09:55 max $ */ 018 019 package net.sourceforge.jeuclid.swing; 020 021 import java.awt.Color; 022 import java.awt.Font; 023 import java.io.IOException; 024 import java.util.Arrays; 025 import java.util.Collections; 026 import java.util.List; 027 import java.util.Map; 028 029 import javax.swing.JComponent; 030 import javax.swing.SwingConstants; 031 import javax.swing.UIManager; 032 import javax.xml.parsers.ParserConfigurationException; 033 034 import net.sourceforge.jeuclid.DOMBuilder; 035 import net.sourceforge.jeuclid.MathMLParserSupport; 036 import net.sourceforge.jeuclid.MathMLSerializer; 037 import net.sourceforge.jeuclid.MutableLayoutContext; 038 import net.sourceforge.jeuclid.context.LayoutContextImpl; 039 import net.sourceforge.jeuclid.context.Parameter; 040 import net.sourceforge.jeuclid.elements.generic.DocumentElement; 041 import net.sourceforge.jeuclid.elements.support.ClassLoaderSupport; 042 043 import org.apache.commons.logging.Log; 044 import org.apache.commons.logging.LogFactory; 045 import org.w3c.dom.Document; 046 import org.w3c.dom.Node; 047 import org.xml.sax.SAXException; 048 049 /** 050 * Displays MathML content in a Swing Component. 051 * <p> 052 * There are two properties which expose the actual content, accessible though 053 * {@link #getDocument()} / {@link #setDocument(org.w3c.dom.Node)} for content 054 * already available as a DOM model, and {@link #getContent()} and 055 * {@link #setContent(String)} for content available as a String. 056 * <p> 057 * This class exposes most of the rendering parameters as standard bean 058 * attributes. If you need to set additional attributes, you may use the 059 * {@link #setParameter(Parameter, Object)} function. 060 * <p> 061 * Please use only the attributes exposed through the attached 062 * {@link JMathComponentBeanInfo} class. Additional attributes, such as 063 * {@link #getFont()} and {@link #setFont(Font)} are provided for Swing 064 * compatibility, but they may not work exactly as expected. 065 * 066 * @see net.sourceforge.jeuclid.awt.MathComponent 067 * @version $Revision: f1695c1926a6 $ 068 */ 069 public final class JMathComponent extends JComponent implements 070 SwingConstants { 071 072 private static final String FONT_SEPARATOR = ","; 073 074 /** 075 * Logger for this class 076 */ 077 private static final Log LOGGER = LogFactory.getLog(JMathComponent.class); 078 079 /** */ 080 private static final long serialVersionUID = 1L; 081 082 private static String uiClassId; 083 084 private static Class<?> mathComponentUIClass; 085 086 private Node document; 087 088 private int horizontalAlignment = SwingConstants.CENTER; 089 090 private final MutableLayoutContext parameters = new LayoutContextImpl( 091 LayoutContextImpl.getDefaultLayoutContext()); 092 093 private int verticalAlignment = SwingConstants.CENTER; 094 095 /** 096 * cursor listener instance. 097 */ 098 private final CursorListener cursorListener; 099 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 }