Package net.xoetrope.swing

Source Code of net.xoetrope.swing.XMetaContent

package net.xoetrope.swing;

import java.awt.Color;
import java.util.Hashtable;
import java.util.Stack;
import java.util.Vector;
import java.io.StringReader;

import java.awt.Component;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.ScrollPane;
import javax.swing.JPanel;

import net.xoetrope.xml.XmlElement;
import net.xoetrope.xui.build.BuildProperties;
import net.xoetrope.xui.XMetaContentHolder;
import net.xoetrope.xui.style.XStyle;
import net.xoetrope.xui.style.XStyleComponent;
import net.xoetrope.xml.XmlSource;
import net.xoetrope.xui.XProjectManager;
import net.xoetrope.debug.DebugLogger;
import net.xoetrope.xui.XAttributedComponent;
import net.xoetrope.xui.XProject;
import net.xoetrope.xui.helper.XTranslator;


/**
* <p>This component renders XML based content. A simple set of tags
* for layout and formatting are used. Styles can be used to control attributes
* such as fonts and colors</p>
* <p>For example the following taken from the Xui Zoo sample application shows some formatted paragraphs of text:</p>
* <pre>
* &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;XText&gt; &lt;XStyle name=&quot;Heading&quot;&gt;Congratulations!&lt;/XStyle&gt;&lt;BR/&gt;&lt;BR/&gt; &lt;XStyle name=&quot;base&quot;&gt;You have just been appointed general manager of the XUI Zoo.&lt;/XStyle&gt;&lt;BR/&gt;&lt;BR/&gt; &lt;XStyle name=&quot;base&quot;&gt;Unfortunately your predescessor wasted most of his time struggling with his Java code. To keep the zoo in business you must buy some new animals quickly. You need to buy 100  animals, including Monkies, Elephants and to get new visitors to the zoo you need to have at least one amazing Unicorn.&lt;/XStyle&gt;&lt;BR/&gt;&lt;BR/&gt; &lt;XStyle name=&quot;base&quot;&gt;Monkies are common enough and only cost 0.05 $XML, Elephants on the other hand are big animals and cost a full 1.00 $XML. And the most rare Unicorns cost the princely sum of  5.00 $XML&lt;/XStyle&gt;&lt;BR/&gt;&lt;BR/&gt; &lt;XStyle name=&quot;base&quot;&gt;Please fill in your order quantities below.&lt;/XStyle&gt; &lt;/XText&gt;
* </pre>
* <p>The tag supported by this class are listed below and play a similar role to their html equivalents:
* <UL>
* </LI>
* <LI>XSTYLE - sets the style for the text.
* <LI>BR - A break.
* <LI>UL - A unordered list.
* <LI>LI - A list item.
* <LI>TABLE - A table.
* <LI>TD - A table cell declaration.
* <LI>TR - A table record/row declaration.
* </p>
* <p>The tags can be nested as you would do whith HTML.</p>
* <p>Copyright (c) Xoetrope Ltd., 1998-2004<br>
* License:      see license.txt
* @version 1.0
*/
public class XMetaContent extends JPanel implements XMetaContentHolder, XStyleComponent, XAttributedComponent
{
  private Stack styleStack;
  private XmlElement content;
  private String strContent;
  private String source;
  private boolean usesLaf = false;

  private int currentX, currentY;
  private int width, height;

  private int startX, colStart;

  private XStyle currentStyle;
  private String currentStyleName;
  private Font font;
  private FontMetrics fontMetrics;
  private int fontHeight;
  private int contentHeight;
  private int padding;
 
  protected XTranslator translator;
  protected boolean antiAlias;

  /**
   * A table of the method names needed for rendereing each style
   */
  protected static Hashtable methodTable = null;

  private static final int XSTYLE = 1;
  private static final int BR = 2;
  private static final int UL = 3;
  private static final int LI = 4;
  private static final int TABLE = 5;
  private static final int TR = 6;
  private static final int TD = 7;

  protected XProject currentProject = XProjectManager.getCurrentProject();

  /**
   * Constructs anew XMetaContent component
   */
  public XMetaContent()
  {
    styleStack = new Stack();
    currentStyle = new XStyle();
    currentStyleName = "";
    translator = currentProject.getTranslator();

    fillMethodTable();
  }

  /**
   * Initialises the lookup hash table of handlers for the content. The hash table
   * provides a quick way of looking up the tag names for subsequent use in a
   * switch.
   */
  protected void fillMethodTable()
  {
    if ( methodTable == null ) {
      methodTable = new Hashtable();
      methodTable.put( "xstyle", new Integer( XSTYLE ));
      methodTable.put( "br", new Integer( BR ));
      methodTable.put( "ul", new Integer( UL ));
      methodTable.put( "li", new Integer( LI ));
      methodTable.put( "table", new Integer( TABLE ));
      methodTable.put( "tr", new Integer( TR ));
      methodTable.put( "td", new Integer( TD ));
    }
  }

  /**
   * Set the meta content to the content of an xml file
   * @param fileName the new content
   */
  public void setFileName( String fileName )
  {
    try {
      if ( fileName != null ) {
        XmlElement src = XmlSource.read( currentProject.getBufferedReader( fileName, null ) );
        setContent( fileName, src );
      }
    }
    catch ( Exception ex ) {
      DebugLogger.logWarning( "Unable to load content: " + fileName );
      DebugLogger.logWarning( "Please check the case of the file name" );
      ex.printStackTrace();
    }
  }

  /**
   * Set the meta content to a simple string value
   * @param newContent the new content
   */
  public void setContent( String newContent )
  {
    if (( strContent != null ) && ( strContent.indexOf( "<?xml" ) == 0 )) {
      XmlElement src = XmlSource.read( new StringReader( newContent ) );
      setContent( newContent, src );
    }
    else if ( newContent.indexOf ( ".xml" ) > 0
      setContent( newContent, null );
    else {
      strContent = newContent;
      content = null;
      source = null;
      contentHeight = 0;
      padding = 2;
      calcSize();
      repaint();
    }
  }

  /**
   * Set the meta content.
   * @param xmlSrc the input location
   * @param src the input xml element
   */
  public void setContent( String xmlSrc, XmlElement src )
  {
    if ( xmlSrc.indexOf( "<?xml" ) == 0 )
      strContent = xmlSrc;
    else if ( xmlSrc.indexOf( ".xml" ) > 0 ) {
      try
        src = XmlSource.read( currentProject.getBufferedReader( xmlSrc, null ) );
      }
      catch ( Exception ex ) {
        DebugLogger.logWarning( "Unable to load content: " + xmlSrc );
        DebugLogger.logWarning( "Please check the case of the file name" );
        ex.printStackTrace();
      }
    }
    source = xmlSrc;
    content = src;
    contentHeight = 0;
    padding = 2;
    calcSize();
    repaint();
  }

  /**
   * Get the source of the current content
   * @return the file name
   */
  public String getFileName()
  {
    return source;
  }

  /**
   * Get the meta content's name .
   * @return the model name of the content
   */
  public String getContent()
  {
    if (( source != null ) && ( source.indexOf( "<?xml" ) == 0 ))
      return source;
    else if ( content != null )
      return content.getName();
    else
      return strContent;
  }

  /**
   * Renders the component
   * @param g the graphics context
   */
  public void paintComponent( Graphics g )
  {
    Graphics2D g2d = (Graphics2D)g;
    Object oldHint = g2d.getRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING );
    g2d.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, antiAlias ? RenderingHints.VALUE_TEXT_ANTIALIAS_ON : RenderingHints.VALUE_TEXT_ANTIALIAS_OFF );

    currentX = currentY = startX = padding;
    height = getSize().height - 2 * padding;

//    int w = getSize().width;
    //int h = getSize().height;
    Color bkColor = getBackground();
    if ( usesLaf )
      bkColor = bkColor.brighter();
    else
      bkColor = bkColor.darker();

    if ( isOpaque() ) {
      if ( usesLaf )
        super.paintComponent( g );
      else {
        g.setColor( getBackground() );
        g.fillRect( 0, 0, getSize().width, getSize().height );
      }
    }


    try {
      if ( ( content != null ) && ( content.getName().compareTo( "XText" ) == 0 ) ) {
        font = g.getFont();
        if ( font.getSize() == 0 )
          font = new Font( null, 0, 12 );
        g.setFont( font );

        if ( contentHeight == 0 )
          calcSize();
        else
          render( g, content );
      }
      else if ( ( content == null ) && ( strContent != null ) ) {
        applyStyle( g );
        renderText( g, strContent );
      }
    }
    catch ( Exception e )
    {
      if ( BuildProperties.DEBUG )
        DebugLogger.logError( "Exception while rendering metacontent: " + getContent());
    }

    int renderHeight = currentY + fontHeight + 2 * padding;
    if ( renderHeight != contentHeight ) {
      Component parent = getParent();
      contentHeight = renderHeight;
      if ( parent instanceof ScrollPane ) {
        setSize( getSize().width - 40, contentHeight );
        parent.doLayout();
      }
      else
        repaint();
    }
   
    g2d.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, oldHint );
}

  /**
   *  Sets the padding or indent for the content
   * @param pad the amount of padding in pixels
   */

  public void setPadding( int pad )
  {
    padding = pad;
  }

  /**
   * Calcualte the size of the content. This method is called from within the
   * paint method and recalculates the required size for display of the content.
   * If a scrollpane is the parent then this control is resized so that all the
   * content  will be visible. The scrollpane may initially have no scrollbar so
   * to avoid flicker and multiple repaints as the control is sized and offscreen
   * graphics context is used for the sizing.
   */
  public void calcSize()
  {
    width = getSize().width;
    if ( contentHeight == 0 )
      width -= 20;
    contentHeight = -1;
    java.awt.Image offscreen = createImage( 1, 1 );

    if ( offscreen != null ) {
      Graphics og = offscreen.getGraphics();
      og.setClip( 0, 0, width, height );
      paint( og );
      og.dispose();
    }
  }

  /**
   * Renders the content, recursively descending the XML
   * @param g the graphics context
   * @param element the root element
   */
  private void render( Graphics g, XmlElement element )
  {
    applyStyle( g );

    Vector elements = element.getChildren();
    int numElements = elements.size();

    for ( int i = 0; i < numElements; i++ ) {
      XmlElement child = (XmlElement)elements.elementAt( i );

      int method = 0;
      String methodName = child.getName();
      method = ((Integer)methodTable.get( methodName.toLowerCase())).intValue();

      if ( !renderItem( g, child, method ))
        renderText( g, child.getContent() );

      pushStyle();
      render( g, child );
      popStyle();
    }
  }

  /**
   * Renders an individual item/tag
   * @param g the graphics context
   * @param child the xml element being rendered
   * @param method the element name
   * @return true if the item was rendered
   */
  protected boolean renderItem( Graphics g, XmlElement child, int method )
  {
    switch ( method ) {
      case XSTYLE:
        setStyle( g, child.getContent(), ( String )child.getAttribute( "name" ) );
        break;
      case BR:
        lineBreak( g );
        break;
      case UL:
        renderList( g );
        break;
      case LI:
        renderListItem( g, child.getContent() );
        break;
      case TABLE:
        renderTable( g );
        break;
      case TR:
        renderTableRecord( g );
        break;
      case TD:
        renderTableCell( g, child.getContent(), child.getAttribute( "width" ).toString() );
        break;
      default:
        return false;
    }

    return true;
  }

  /**
   * Set the current style.
   * @param style the style to use when renderer
   */
  public void setStyle( String style )
  {
    currentStyle = XProjectManager.getStyleManager().getStyle( style );
  }

  /**
   * Changes the current style. The style element may have content and it is
   * assumed to be text when rendering.
   * @param g the graphics context
   * @param content the current xml node
   * @param styleName the style name
   */
  private void setStyle( Graphics g, String content, String styleName )
  {
    if ( styleName.compareTo( currentStyleName ) != 0 ) {
      currentStyle = XProjectManager.getStyleManager().getStyle( styleName );
      currentStyleName = styleName;

      applyStyle( g );
    }

    renderText( g, content );
  }

  /**
   * Applies the current style to the graphics context
   * @param g teh graphics context
   */
  private void applyStyle( Graphics g )
  {
    g.setColor( currentStyle.getStyleAsColor( XStyle.COLOR_FORE ));

    try {
      font = XProjectManager.getStyleManager().getFont( currentStyle );
    }
    catch ( Exception ex ) {
      font = new Font("Arial", 0, 12);
    }
    if ( font!=null )
      g.setFont( font );
    fontMetrics = g.getFontMetrics();
    fontHeight = fontMetrics.getHeight();
  }

  /**
   * Pushes the style onto the style stack. Called prior to descending a level
   * in the xml
   */
  private void pushStyle()
  {
    styleStack.push( currentStyle.clone());
  }

  /**
   * Pops the style from the style stack. Called on return from a recursive call
   * to render a lower level of the xml hierarchy
   */
  private void popStyle()
  {
    currentStyle = (XStyle)styleStack.pop();
  }

  /**
   * Renders text content
   * @param g the graphcis context
   * @param text the text content
   */
  private void renderText( Graphics g, String text )
  {
    // Draw the question text over multiple lines
    g.setColor( currentStyle.getStyleAsColor( XStyle.COLOR_BACK ));
    if ( text == null )
      return;

    int iStart = text.indexOf( "${" );
    int iEnd = text.indexOf( "}", iStart );
    while ( iStart > -1 )
    {
      String key = text.substring( iStart + 2, iEnd );
      String temp = text.substring( 0, iStart );
      temp += translator.translate( key );
      temp += text.substring( iEnd + 1, text.length() );
      text = temp;
      iStart = text.indexOf( "${" );
      iEnd = text.indexOf( "}", iStart );
    }
   
    int start = 0;
    int end = 0;
    int lineNum = 1;

    boolean newLine = false;
    do {
      boolean drawLine = false;

      // Extend the string by a word (to the next space, if any)
      end = text.indexOf( ' ', end + 1 );
      String ss;
      if ( end > 0 )
        ss = text.substring( start, end );
      else {
        ss = text.substring( start );
        drawLine = true;
      }

      // Does the next word fit in?
      if (( currentX + fontMetrics.stringWidth( ss )) > width ) {
        int inc = ss.lastIndexOf( ' ' );
        end = start + inc;
        if ( inc < 0 ) {
          end += ss.length();
        }
        else if ( end >= 0 )
          ss = text.substring( start, end );
        else
          ss = text.substring( start );
       
        drawLine = true;
        newLine = true;
      }

      if ( drawLine ) {
        // Erase the background
        g.setColor( getBackground() );
        g.fillRect( currentX, currentY, (int)fontMetrics.stringWidth( ss ) + 1, fontHeight + 1 );

        // Draw the text
        g.setColor( getForeground());
        g.drawString( ss, currentX, currentY + fontMetrics.getAscent());

        // Update the current position.
        currentX += (int)fontMetrics.stringWidth( ss );
        start = end + 1;
        lineNum++;
        if ( newLine ) {
          currentY +=  fontHeight;
          currentX = startX;
          newLine = false;
        }
      }
    }
    while ( end >= 0 );

    // If no text drawn, then do it now
    if (( start == 0 ) && ( lineNum == 1 ))
      g.drawString( text, currentX, fontMetrics.getAscent());
  }

  /**
   * Render a list
   * @param g
   */
  private void renderList( Graphics g )
  {
    lineBreak( g );
  }

  /**
   * Render a list item
   * @param g
   * @param text
   */
  private void renderListItem( Graphics g, String text )
  {
    int oldStart = startX;
    int bulletSize = fontHeight / 4;

    currentY += fontHeight / 2;

    startX += 2 * fontHeight;
    lineBreak( g );

    g.fillOval( startX - fontHeight / 2, currentY + fontHeight / 2, bulletSize, bulletSize );

    renderText( g, text );
    startX = oldStart;
  }

  /**
   * Prepare to render a table element
   * @param g
   */
  private void renderTable( Graphics g )
  {
    startX += 2 * fontHeight;
    colStart = startX;
    lineBreak( g );
  }

  /**
   * Render a record of a table.
   * @param g
   */
  private void renderTableRecord( Graphics g )
  {
    lineBreak( g );
    startX = colStart;
    currentX = startX;
  }

  /**
   * Render an individual table cell
   * @param g
   * @param text
   * @param width
   */
  private void renderTableCell( Graphics g, String text, String width )
  {
//    int bulletSize = fontHeight / 4;

    renderText( g, text );

    startX += Integer.parseInt( width );
    currentX = startX;
  }

  /**
   * Add a line break
   * @param g
   */
  private void lineBreak( Graphics g )
  {
    currentY += fontHeight;
    currentX = startX;
  }

  /**
   * Sets the bounds for this component
   * @param x the left coordinate
   * @param y the top/y coordinate
   * @param w the width
   * @param h the height
   */
  public void setBounds( int x, int y, int w, int h )
  {
    super.setBounds( x, y, w, h );
    doLayout();
    if (( w != width ) || ( height != h ))
      calcSize();
  }

  /**
   * Set the painting of the background
   * @param value true to have the LAF paint the background or false for the
   * component to paint it itself using the colours specified in the styles (if any)
   */
  public  void setUsesLaf( boolean value )
  {
    usesLaf = value;
  }

  /**
   * Set one or more attributes of the component.
   * <OL>
   * <LI>laf value=(true|false) rely on the look and feel to paint the background</LI>
   * <LI>opaque value=(true|false) false to rely on the parent component to paint the background</LI>
   * <LI>antialias, value=(true|false) override the page antialiasing setting</LI>
   * </OL>
   * @param attribName the attribute name
   * @param attribValue the attribute value
   * @return 0 for success, non zero for failure or to require some further action
   */
  public int setAttribute( String attribName, Object attribValue )
  {
    String attribNameLwr = attribName.toLowerCase();
    String attribValueLwr = ((String)attribValue).toLowerCase();
    if ( attribNameLwr.equals( "laf" )) {
      if ( attribValueLwr != null )
        setUsesLaf( attribValueLwr.equals( "true" ) );
    }
    else if ( attribNameLwr.equals( "opaque" )) {
      if ( attribValueLwr != null )
        setOpaque( attribValueLwr.equals( "true" ) );
      else if ( attribNameLwr.equals( "antialias" ))
        antiAlias = attribValue.equals( "true" );
    }
    else
      return -1;
   
    return 0;
  }
}
TOP

Related Classes of net.xoetrope.swing.XMetaContent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.