<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>javajeff's blog</title>
  <link rel="alternate" type="text/html" href="http://www.javaworld.com/community/blog/14572"/>
  <link rel="self" type="application/atom+xml" href="http://www.javaworld.com/community/blog/14572/atom/feed"/>
  <id>http://www.javaworld.com/community/blog/14572/atom/feed</id>
  <updated>2009-03-03T17:35:53-05:00</updated>
  <entry>
    <title>Introducing an emboss effect to JavaFX</title>
    <link rel="alternate" type="text/html" href="http://www.javaworld.com/community/node/2854" />
    <id>http://www.javaworld.com/community/node/2854</id>
    <published>2009-04-28T15:01:04-04:00</published>
    <updated>2009-04-28T15:01:04-04:00</updated>
    <author>
      <name>javajeff</name>
    </author>
    <category term="effect" />
    <category term="effects" />
    <category term="emboss" />
    <category term="embossing" />
    <category term="glow" />
    <category term="javafx" />
    <category term="media" />
    <category term="node" />
    <category term="nodes" />
    <category term="player" />
    <category term="reflection" />
    <category term="reflections" />
    <summary type="html"><![CDATA[<!--paging_filter--><p>Wikipedia's <a href="http://en.wikipedia.org/wiki/Embossing">Embossing</a> entry defines <em>embossing</em> as "the process of creating a three-dimensional image or design in paper and other ductile materials." A few years back, I presented an algorithm for embossing images in my <a href="http://today.java.net/pub/a/today/2005/12/08/image-embossing.html">Java Tech: Image Embossing</a> article.</p>    ]]></summary>
    <content type="html"><![CDATA[<!--paging_filter--><p>Wikipedia's <a href="http://en.wikipedia.org/wiki/Embossing">Embossing</a> entry defines <em>embossing</em> as "the process of creating a three-dimensional image or design in paper and other ductile materials." A few years back, I presented an algorithm for embossing images in my <a href="http://today.java.net/pub/a/today/2005/12/08/image-embossing.html">Java Tech: Image Embossing</a> article.</p>

<p>Subsequent to its revelation in the aforementioned article, I presented an improved version of this algorithm in my <a href="http://today.java.net/pub/a/today/2006/02/07/process-images-with-imagician.html">Java Tech: Process Images with Imagician</a> article. Figure 1 excerpts from that article an image showing the metallic-looking, three-dimensional result of applying the algorithm to a non-embossed image.</p>

<p><a href="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42809_1.jpg"><img src="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42809_1_thumb.jpg" width="300" height="200" alt="" /></a></p>

<p><strong>Figure 1:</strong> An embossed flower. (Click to enlarge.)</p>

<p>JavaFX offers a nice assortment of effects, along with the ability to create new effects by chaining some of them together, but omits an emboss effect. I recently corrected this oversight by making my emboss algorithm available to JavaFX 1.1 and 1.1.1 nodes via a new <code>javafx.scene.effect.Effect</code> subclass (<code>Emboss</code>) and supporting Java code. I present <code>Emboss</code> and its Java infrastructure in this blog post.</p>

<div class="textbox">
<h4 class="textboxhead">The price of progress</h4>
<p>Because <code>Emboss</code>'s implementation relies on JavaFX's undocumented Java runtime infrastructure, it's highly unlikely that <code>Emboss</code> will work with future JavaFX releases. Although I'll try to update the implementation as needed, I'd prefer that Sun's next JavaFX release include an emboss effect (or at least some kind of documented plugin-based architecture that lets Java developers easily introduce their own effects).</p>
</div>

<h2>Coding the emboss effect</h2>

<p>I found it easier to first code the JavaFX side of the emboss effect, and then code the Java side. After decompiling JavaFX's <code>Reflection.class</code> and some of its other effects classfiles, to discover how their underlying Java code connects to the rest of JavaFX's effects infrastructure, I wrote Listing 1's <code>Emboss.fx</code> source code.</p>

<p><strong>Listing 1:</strong> <code>Emboss.fx</code></p>

<pre><div class="codeblock"><code>/*<br /> * Emboss.fx<br /> */<br /><br />package embosseffectdemo;<br /><br />import javafx.scene.effect.Effect;<br /><br />public class Emboss extends Effect<br />{<br />&nbsp;&nbsp;&nbsp; var embosser = new Embosser ();<br /><br />&nbsp;&nbsp;&nbsp; override function impl_getImpl ()<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; embosser<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; public var input: Effect on replace<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; embosser.setInput (input.impl_getImpl ())<br />&nbsp;&nbsp;&nbsp; }<br />}</code></div></pre>

<p>The <code>Emboss</code> class relies on <code>javafx.scene.effect.Effect</code>'s <code>public abstract impl_getImpl(): com.sun.scenario.effect.Effect</code> function to connect the emboss effect's JavaFX side to its Java implementation. Because this function can be called multiple times, it must return a reference to a pre-created instance of the Java implementation class, which Listing 2 describes.</p>

<p><strong>Listing 2:</strong> <code>Embosser.java</code></p>

<pre><div class="codeblock"><code>/*<br /> * Embosser.java<br /> */<br /><br />package embosseffectdemo;<br /><br />import java.awt.*;<br />import java.awt.geom.*;<br />import java.awt.image.*;<br /><br />import com.sun.scenario.effect.*;<br /><br />public class Embosser extends FilterEffect<br />{<br />&nbsp;&nbsp;&nbsp; public Embosser ()<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this (DefaultInput);<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; public Embosser (Effect input)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super (input);<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; public final Effect getInput ()<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ((Effect) getInputs ().get (0));<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; public void setInput (Effect input)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setInput (0, input);<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; @Override<br />&nbsp;&nbsp;&nbsp; public ImageData filterImageDatas (FilterContext fctx,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AffineTransform transform,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ImageData [] inputDatas)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Rectangle inputbounds = inputDatas [0].getBounds ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int srcW = inputbounds.width;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int srcH = inputbounds.height;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Image imDst = getCompatibleImage (fctx, srcW, srcH);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ((!(inputDatas [0].validate (fctx))) || (imDst == null))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new ImageData (fctx, null, inputbounds);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Image imSrc = inputDatas [0].getImage ();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BufferedImage biTmp = new BufferedImage (srcW, srcH,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BufferedImage.TYPE_INT_ARGB);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Graphics g = biTmp.getGraphics ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.drawImage (imSrc, 0, 0, null);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.dispose ();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int [] srcPixels = biTmp.getRGB (0, 0, srcW, srcH, null, 0, srcW);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0, offset = 0; i &lt; srcH; i++)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int j = 0; j &lt; srcW; j++, offset++)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int current = srcPixels [offset];<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int upperLeft = 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (i &gt; 0 &amp;&amp; j &gt; 0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; upperLeft = srcPixels [offset-srcW-1];<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int opacity = (current &gt;&gt; 24)&amp;255;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int rDiff = ((current &gt;&gt; 16)&amp;255)-((upperLeft &gt;&gt; 16)&amp;255);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int gDiff = ((current &gt;&gt; 8)&amp;255)-((upperLeft &gt;&gt; 8)&amp;255);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int bDiff = (current &amp; 255)-(upperLeft &amp; 255);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int diff = rDiff;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (Math.abs (gDiff) &gt; Math.abs (diff)) diff = gDiff;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (Math.abs (bDiff) &gt; Math.abs (diff)) diff = bDiff;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int grayLevel = Math.max (Math.min (128+diff, 255), 0);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; biTmp.setRGB (j, i, (opacity &lt;&lt; 24)+(grayLevel &lt;&lt; 16)+<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (grayLevel &lt;&lt; 8)+grayLevel);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; srcPixels = null;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g = imDst.getGraphics ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.drawImage (biTmp, 0, 0, null);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.dispose ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Rectangle newbounds = new Rectangle (inputbounds.x, inputbounds.y, srcW,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; srcH);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new ImageData (fctx, imDst, newbounds);<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; @Override<br />&nbsp;&nbsp;&nbsp; public boolean operatesInUserSpace ()<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; @Override<br />&nbsp;&nbsp;&nbsp; public AccelType getAccelType (FilterContext paramFilterContext)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return AccelType.INTRINSIC;<br />&nbsp;&nbsp;&nbsp; }<br />}</code></div></pre>

<p><code>Embosser.java</code> is a good example of "monkey see, monkey do." I arrived at this code after studying decompiled output from JavaFX's <code>Reflection</code> class (the version derived from <code>Reflection.java</code>, not from <code>Reflection.fx</code>), and decided to mimic this class as much as possible.</p>

<p>The <code>filterImageDatas()</code> method is key to <code>Embosser</code>. After obtaining source and destination <code>Image</code>s, it creates a <code>BufferedImage</code> intermediary, copies the source <code>Image</code> to the buffer, embosses the buffer's content, and copies the buffer to the destination <code>Image</code>. A <code>BufferedImage</code> is needed because the source and destination <code>Image</code>s are <code>sun.java2d.pipe.hw.AccelTypedVolatileImage</code> instances.</p>

<h2>Testing the emboss effect</h2>

<p>After creating <code>Emboss.fx</code> and <code>Embosser.java</code>, I needed a JavaFX application to test the emboss effect. I created Listing 3's <code>Main.fx</code> source code, as part of an <code>EmbossEffectDemo</code> project, to test unscaled and scaled versions of an <code>Emboss</code>ed image, and to test <code>Emboss</code>'s effect-chaining support (via its <code>input</code> attribute).</p>

<p><strong>Listing 3:</strong> <code>Main.fx</code> (from an <code>EmbossEffectDemo</code> project)</p>

<pre><div class="codeblock"><code>/*<br /> * Main.fx<br /> */<br /><br />package embosseffectdemo;<br /><br />import javafx.scene.Scene;<br /><br />import javafx.scene.effect.Reflection;<br />import javafx.scene.effect.SepiaTone;<br /><br />import javafx.scene.image.Image;<br />import javafx.scene.image.ImageView;<br /><br />import javafx.scene.input.MouseEvent;<br /><br />import javafx.scene.layout.HBox;<br /><br />import javafx.scene.paint.Color;<br /><br />import javafx.scene.transform.Scale;<br /><br />import javafx.stage.Stage;<br /><br />Stage<br />{<br />&nbsp;&nbsp;&nbsp; title: &quot;Emboss Effect Demo&quot;<br />&nbsp;&nbsp;&nbsp; width: 823<br />&nbsp;&nbsp;&nbsp; height: 430<br /><br />&nbsp;&nbsp;&nbsp; scene: Scene<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fill: Color.BLACK<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content: HBox<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spacing: 5.0<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var imageViewRef1: ImageView<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var imageViewRef2: ImageView<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ImageView<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; image: Image<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; url: &quot;{__DIR__}bunny.jpg&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; effect: Reflection<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input: SepiaTone<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; level: 1.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fraction: 1.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; topOffset: 5.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ImageView<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; image: Image<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; url: &quot;{__DIR__}bunny.jpg&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; effect: SepiaTone<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; level: 1.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input: Reflection<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fraction: 1.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; topOffset: 5.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; imageViewRef1 = ImageView<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; image: Image<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; url: &quot;{__DIR__}bunny.jpg&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; effect: Reflection<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input: Emboss {}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fraction: 1.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; topOffset: 5.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onMouseEntered: function (me: MouseEvent): Void<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; imageViewRef2.scaleX = 0.0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; imageViewRef2.scaleY = 0.0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; imageViewRef1.transforms = Scale { x: 2.0 y: 2.0 }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onMouseExited: function (me: MouseEvent): Void<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; imageViewRef1.transforms = null;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; imageViewRef2.scaleX = 1.0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; imageViewRef2.scaleY = 1.0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; imageViewRef2 = ImageView<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; image: Image<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; url: &quot;{__DIR__}bunny.jpg&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; effect: Emboss<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input: Reflection<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fraction: 1.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; topOffset: 5.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />}</code></div></pre>

<p>Listing 3 renders a row of four instances of the same underlying image. The first two renderings show this image by first applying <code>SepiaTone</code> followed by <code>Reflection</code>, and then by applying <code>Reflection</code> followed by <code>SepiaTone</code>. The last two renderings, where <code>Emboss</code> replaces <code>SepiaTone</code>, employ the same approach.</p>

<p><a href="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42809_2.jpg"><img src="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42809_2_thumb.jpg" width="300" height="158" alt="" /></a></p>

<p><strong>Figure 2:</strong> Sepia toned and embossed reflected images. (Click to enlarge.)</p>

<p>Although each pair of renderings looks identical, look closer at the reflections and you'll notice subtle differences. Order matters! Interestingly, if you replace <code>SepiaTone</code> with <code>Glow</code>, you'll notice a big difference: Applying <code>Glow</code> before <code>Reflection</code> causes the reflection to also glow. However, the reflection doesn't glow when you reverse the order of these effects.</p>

<p>It's possible to transform an embossed node without screwing up the embossed appearance. For example, you can scale the third-from-the-left bunny image by a factor of two by moving the mouse over this image. (The image reverts to its original size when you move the mouse off of the image.) Figure 3 shows the resulting scaled-up image.</p>

<p><a href="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42809_3.jpg"><img src="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42809_3_thumb.jpg" width="300" height="158" alt="" /></a></p>

<p><strong>Figure 3:</strong> A giant bunny. (Click to enlarge.)</p>

<p>Before you can build and run this application, you need to add <code>Scenario.jar</code> (which contains <code>com.sun.scenario.effect.Effect</code>, <code>com.sun.scenario.effect.FilterEffect</code>, and other effects infrastructure classes) to the <code>EmbossEffectDemo</code> script's classpath. Assuming that you're using NetBeans IDE 6.5, complete the following steps to accomplish this task:</p>

<ul>
<li>
Activate the <strong><code>Project Properties</code></strong> dialog box via the <strong><code>File</code></strong> menu, or by right-clicking the project name and selecting <strong><code>Properties</code></strong> from the popup menu.
</li>

<li>
Select this dialog box's <strong><code>Libraries</code></strong> category and click the resulting pane's <strong><code>Add JAR/Folder</code></strong> button.
</li>

<li>
Use the resulting file chooser to navigate to <code>Scenario.jar</code>. On my platform, this file locates in the <code>C:\Program Files\NetBeans 6.5\javafx2\javafx-sdk\lib\desktop\</code> directory.
</li>
</ul>

<h2>Embossing a movie</h2>

<p>As with everything user interface-related, you'll want to be sensible about using the emboss effect. However, I couldn't resist trying out this effect with the <code>javafx.scene.media.MediaView</code> class to emboss a movie -- I wanted to visually inspect the effect's performance, and I've never seen an embossed movie. Listing 4 presents the source code to my embossed media player.</p>

<p><strong>Listing 4:</strong> <code>Main.fx</code> (from an <code>EmbossedMediaPlayer</code> project)</p>

<pre><div class="codeblock"><code>/*<br /> * Main.fx<br /> */<br /><br />package embossedmediaplayer;<br /><br />import javafx.scene.Scene;<br /><br />import javafx.scene.input.MouseEvent;<br /><br />import javafx.scene.media.Media;<br />import javafx.scene.media.MediaPlayer;<br />import javafx.scene.media.MediaView;<br /><br />import javafx.scene.paint.Color;<br /><br />import javafx.stage.Stage;<br /><br />Stage<br />{<br />&nbsp;&nbsp;&nbsp; title: &quot;Embossed Media Player&quot;<br /><br />&nbsp;&nbsp;&nbsp; width: 400<br />&nbsp;&nbsp;&nbsp; height: 300<br /><br />&nbsp;&nbsp;&nbsp; var mediaURI = &quot;https://swinghelper.dev.java.net/bin/blog/sam/jfx/movie.avi&quot;<br /><br />&nbsp;&nbsp;&nbsp; var mediaPlayerRef: MediaPlayer<br /><br />&nbsp;&nbsp;&nbsp; var sceneRef: Scene<br />&nbsp;&nbsp;&nbsp; scene: sceneRef = Scene<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fill: Color.BLACK<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var mediaViewRef: MediaView<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content: mediaViewRef = MediaView<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; effect: Emboss {}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mediaPlayer: mediaPlayerRef = MediaPlayer<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; media: Media<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; source: mediaURI<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onMouseClicked: function (me: MouseEvent): Void<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (mediaPlayerRef.paused)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mediaPlayerRef.play ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mediaPlayerRef.pause ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translateX: bind (sceneRef.width-mediaViewRef.boundsInLocal.width)/2<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translateY: bind (sceneRef.height-mediaViewRef.boundsInLocal.height)/2<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; onClose: function ()<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mediaPlayerRef.pause ();<br />&nbsp;&nbsp;&nbsp; }<br />}</code></div></pre>

<p>This simple media player requires that you click the scene to start playing the movie, click again to pause the movie, click yet again to resume the movie, and so on. For convenience, I've hardwired the player to always play the same short space shuttle launch movie located at <code>https://swinghelper.dev.java.net/bin/blog/sam/jfx/movie.avi</code>. Figure 4 reveals one of its frames.</p>

<p><a href="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42809_4.jpg"><img src="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42809_4_thumb.jpg" width="300" height="226" alt="" /></a></p>

<p><strong>Figure 4:</strong> A strange-looking space shuttle launch. (Click to enlarge.)</p>

<h2>Conclusion</h2>

<p>I've taken liberties with JavaFX's runtime to make my emboss algorithm available as a JavaFX effect. However, I don't advise you to do the same for your own effects because the runtime is undocumented, subject to change, and is more complex than I've shown. It's simply too easy to create buggy effect implementation classes -- I think <code>Embosser</code> is okay. Hopefully, a future JavaFX will facilitate introducing custom effects.</p>

<p>Download a source file: <a
href="http://www.javaworld.com/javaworld/jw-04-2009/csj42809-src.zip">csj42809-src.zip</a></p>

<p>Like this blog? <a
href="http://www.javaworld.com/community/blog/14572/feed">Subscribe to
the CSJ Explorer RSS feed</a></p>    ]]></content>
  </entry>
  <entry>
    <title>Deploying Swing and JavaFX applications to the masses</title>
    <link rel="alternate" type="text/html" href="http://www.javaworld.com/community/node/2811" />
    <id>http://www.javaworld.com/community/node/2811</id>
    <published>2009-04-21T19:12:14-04:00</published>
    <updated>2009-04-21T19:12:15-04:00</updated>
    <author>
      <name>javajeff</name>
    </author>
    <category term="applet" />
    <category term="application" />
    <category term="deploy" />
    <category term="deployment" />
    <category term="java" />
    <category term="Java Web Start" />
    <category term="javafx" />
    <category term="JNLP" />
    <category term="jws" />
    <category term="swing" />
    <category term="toolkit" />
    <summary type="html"><![CDATA[<!--paging_filter--><p>Sun's (and now Oracle's) desires for Java/Swing's and JavaFX's success on the client depend upon how easy it is for the average user to install the appropriate version of Java/JavaFX on their platform. Furthermore, these desires depend upon browsers being able to launch Swing and JavaFX applications without problems.</p>    ]]></summary>
    <content type="html"><![CDATA[<!--paging_filter--><p>Sun's (and now Oracle's) desires for Java/Swing's and JavaFX's success on the client depend upon how easy it is for the average user to install the appropriate version of Java/JavaFX on their platform. Furthermore, these desires depend upon browsers being able to launch Swing and JavaFX applications without problems.</p>

<p>To somewhat gauge a user's experience with installation and launch reliability, I recently used Sun's Deployment Toolkit to deploy several Swing applications to my website, and also deployed a JavaFX application as an applet via the JavaFX extension to this toolkit. I then used four browsers in a Windows XP (my only operating system) context to launch these applications.</p>

<p>This post discusses these deployment exercises, and presents my ease-of-installation and reliability findings for Mozilla Firefox 3.0.8, Internet Explorer 7.0.5730.11, Google Chrome 1.0.154.53, and Opera 9.62. It also reveals something strange about the JavaFX 1.1 compiler that I discovered shortly after working on the JavaFX application introduced later.</p>

<h2>Swing application deployment</h2>

<p>The first exercise deploys a Swing application that converts (via a Web service) between Roman numerals and integers. The application is the centerpiece of my <a href="http://javajeff.mb.ca/cgi-bin/makepage.cgi?articles/usaajtcwws/usaajtcwws">Use SAAJ to Communicate with Web Services</a> article, and is packaged in a <code>RomanNumerals.jar</code> file that consists of several classfiles and an empty manifest.</p>

<p>After configuring my Web server so that all files with the <code>.jnlp</code> file extension associate with the <code>application/x-java-jnlp-file</code> MIME type, I created a <code>RomanNumerals.jnlp</code> Java Network Launch Protocol (JNLP) file. Listing 1 reveals this file's contents.</p>

<p><strong>Listing 1:</strong> <code>RomanNumerals.jnlp</code></p>

<pre><div class="codeblock"><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;<br />&lt;!-- JNLP File for RomanNumerals --&gt;<br /><br />&lt;jnlp codebase=&quot;http://javajeff.mb.ca/articles/usaajtcwws/&quot; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; href=&quot;RomanNumerals.jnlp&quot;&gt;<br />&nbsp; &lt;information&gt;<br />&nbsp;&nbsp;&nbsp; &lt;title&gt;Roman Numerals&lt;/title&gt;<br />&nbsp;&nbsp;&nbsp; &lt;vendor&gt;Jeff Friesen&lt;/vendor&gt;<br />&nbsp;&nbsp;&nbsp; &lt;description&gt;Roman Numerals&lt;/description&gt;<br />&nbsp;&nbsp;&nbsp; &lt;homepage href=&quot;http://javajeff.mb.ca&quot;/&gt;<br />&nbsp;&nbsp;&nbsp; &lt;offline-allowed/&gt;<br />&nbsp; &lt;/information&gt;<br /><br />&nbsp; &lt;resources&gt;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &lt;jar href=&quot;RomanNumerals.jar&quot;/&gt;&nbsp;&nbsp; <br />&nbsp; &lt;/resources&gt;<br /><br />&nbsp; &lt;application-desc main-class=&quot;RomanNumerals&quot;/&gt;<br />&lt;/jnlp&gt;</code></div></pre>

<p>You'll notice that I've omitted the <code>spec=&quot;1.0+&quot;</code> attribute that is often present in a <code>&lt;jnlp&gt;</code> tag -- this attribute defaults to <code>&quot;1.0+&quot;</code>, according to the JNLP spec. I've also omitted a <code>j2se</code> (or <code>java</code>) element from within the <code>resources</code> element, because I'm not relying on the auto-downloader to download a Java Runtime Environment (JRE).</p>

<p>After copying <code>RomanNumerals.jar</code> and <code>RomanNumerals.jnlp</code> to a suitable location on my website (<code>http://javajeff.mb.ca/articles/usaajtcwws/</code>), I created the script that's shown in Listing 2.</p>

<p><strong>Listing 2:</strong> Script for deploying the Roman numerals application</p>

<pre><div class="codeblock"><code>&lt;script type=&quot;text/javascript&quot; src=&quot;http://java.com/js/deployJava.js&quot;&gt;<br />&lt;/script&gt;<br /><br />&lt;script type=&quot;text/javascript&quot;&gt;<br />&nbsp; var url = &quot;http://javajeff.mb.ca/articles/usaajtcwws/RomanNumerals.jnlp&quot;;<br />&nbsp; deployJava.createWebStartLaunchButton (url, &#039;1.6.0&#039;);<br />&nbsp; document.writeln (&quot; (RomanNumerals)&quot;);<br />&lt;/script&gt;</code></div></pre>

<p>This script uses Sun's Java Deployment Toolkit (<code>deployJava.js</code>) and its <code>createWebStartLaunchButton()</code> function to output a launch button for the specified URL. When clicked, associated code ensures that at least the official release of JRE 6 is installed (installing the most recent JRE 6 if needed), and launches the application via JNLP/Java Web Start.</p>

<p>With JRE 6u13 already installed on my platform, I pointed each of my browsers to <code>http://javajeff.mb.ca/cgi-bin/makepage.cgi?articles/usaajtcwws/usaajtcwws</code>, and proceeded to launch this application by clicking the <strong><code>Launch</code></strong> button -- Figure 1 shows the launch in the context of Firefox. Unsurprisingly, I experienced no program crashes or otherwise strange behaviors.</p>

<p><a href="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42109_1.jpg"
><img src="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42109_1_thumb.jpg
" width="300" height="109" alt="" /></a></p>

<p><strong>Figure 1:</strong> The <code>RomanNumerals</code> application user interface features a gradient background. (Click to enlarge.)</p>

<p>To see what would happen with no JRE present, I uninstalled JRE 6u13 and clicked the <strong><code>Launch</code></strong> button for each browser. Firefox and Internet Explorer responded by presented the same sequence of dialog boxes for installing the JRE. Figure 2 reveals the initial <strong><code>Downloading Java</code></strong> dialog box.</p>

<p><a href="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42109_2.jpg"
><img src="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42109_2_thumb.jpg
" width="300" height="167" alt="" /></a></p>

<p><strong>Figure 2:</strong> The initial Downloading Java dialog box (shown here for Internet Explorer) appears when the <strong><code>Launch</code></strong> button is clicked. (Click to enlarge.)</p>

<p>This dialog box was succeeded by the following sequence of dialog boxes:</p>

<ul>
<li>
<strong><code>Downloading Java Installer</code></strong>
</li>

<li>
<strong><code>Java Setup - Welcome</code></strong> (click the <strong><code>Accept</code></strong> button)
</li>

<li>
<strong><code>Java Setup - Yahoo! Toolbar</code></strong> (click the <strong><code>Next</code></strong> button)
</li>

<li>
<strong><code>Java Setup - Progress</code></strong>
</li>

<li>
<strong><code>Java Setup - Complete</code></strong> (click the <strong><code>Finish</code></strong> button)
</li>
</ul>

<p>After clicking <strong><code>Finish</code></strong>, the application started running with no problems.</p>

<p>Although some users might not appreciate having to navigate through several dialog boxes before the application starts to run (I wonder if there's a way to make this process more transparent, especially for users who know nothing about Java, and who don't want to know anything about this technology), the situation is worse for Chrome and Opera users.</p>

<p>With Chrome, I was taken to a page (see Figure 3 for an excerpt) where I was told to click a link to download the JRE executable. After clicking this link, responding to Chrome prompts, and responding to a sequence of dialogs similar to those shown earlier, I found that I had to close and restart Chrome, navigate back to the page, and click <strong><code>Launch</code></strong> to launch the application (which launched and ran without problems).</p>

<p><a href="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42109_3.jpg"
><img src="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42109_3_thumb.jpg
" width="300" height="119" alt="" /></a></p>

<p><strong>Figure 3:</strong> Click the link to download the JRE executable. (Click to enlarge.)</p>

<p>With Opera, I found myself confronted with a <strong><code>Downloading file RomanNumerals.jnlp</code></strong> dialog box presenting only an <strong><code>Other application...</code></strong> category for opening this JNLP file. Basically, I'm required to click this dialog box's <strong><code>Open</code></strong> button (see Figure 4) to associate my <code>C:\Program Files\java\jre6\bin\javaws.exe</code> file with the <code>.jnlp</code> file extension.</p>

<p><a href="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42109_4.jpg"
><img src="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42109_4_thumb.jpg
" width="300" height="175" alt="" /></a></p>

<p><strong>Figure 4:</strong> Clicking the <strong><code>Launch</code></strong> button leads to an unexpected result. (Click to enlarge.)</p>

<p>Because there was no JRE (and certainly no <code>javaws.exe</code> file) installed on my Windows platform, I couldn't automatically install the necessary JRE to launch the application. (For more information on Opera and Java Web Start, check out the <a href="http://mindprod.com/jgloss/javawebstart.html">Java Web Start : Java Glossary</a> page from Canadian Mind Products.)</p>

<p>I'm not sure about market penetration for Chrome or Opera, but some users will probably prefer these browsers, and many of them (especially in Opera's case) probably won't bother (or have the savvy) to install Java. Perhaps Sun will work with Chrome and Opera vendors to improve their browsers' interaction with the Deployment Toolkit. Doing so can only help to expand the market for your Swing applications.</p>

<h2>JavaFX application deployment</h2>

<p>If you've visited the <a href="http://javafx.com">javafx.com</a> website, you've probably encountered pages that embed JavaFX applications as applets. This website also provides helpful information on deploying these applications as applets or Java Web Start-launchable applications. Over the weekend, I used this information to deploy a JavaFX application as an applet on my website.</p>

<p>Specifically, I deployed the application, which presents a slideshow of various objects in the solar system, on my <a href="http://javajeff.mb.ca/cgi-bin/makepage.cgi?articles/dajfxa/dajfxa">Deploying a JavaFX Application</a> article page. Figure 5 reveals this application's applet running in the context of the Google Chrome browser.</p>

<p><a href="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42109_5.jpg"
><img src="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42109_5_thumb.jpg
" width="300" height="257" alt="" /></a></p>

<p><strong>Figure 5:</strong> Transitioning from the Sun to Mercury. (Click to enlarge.)</p>

<p>The slideshow application -- its article page introduces this application, along with the steps I took to deploy it -- also ran without problems in the context of the Firefox and Internet Explorer browsers. However, Opera didn't fail to disappoint. After telling Opera that it was okay to run the applet, this browser presented a blank applet area on the Web page, and Listing 3's console-based exception information.</p>

<p><strong>Listing 3:</strong> Opera's Java console reveals various problems when attempting to launch the JavaFX applet</p>

<pre><div class="codeblock"><code>&lt;pre&gt;-- Opera - Java Console --<br /><br />Java vendor: Sun Microsystems Inc.<br />Java version: 1.6.0_13<br /><br />type &#039;h&#039; for help<br /><br />--<br />java.security.PrivilegedActionException: java.lang.ClassNotFoundException: java.lang<br />	at java.security.AccessController.doPrivileged(Native Method)<br />	at com.opera.PrivilegedThread.run(LiveConnectPrivilegedActions.java:290)<br />	at java.lang.Thread.run(Unknown Source)<br />Caused by: java.lang.ClassNotFoundException: java.lang<br />	at com.opera.AppletClassLoader.findClass(AppletClassLoader.java:369)<br />	at java.lang.ClassLoader.loadClass(Unknown Source)<br />	at com.opera.AppletClassLoader.loadClass(AppletClassLoader.java:433)<br />	at java.lang.ClassLoader.loadClass(Unknown Source)<br />	at com.opera.PrivilegedFindClass.run(LiveConnectPrivilegedActions.java:266)<br />	... 3 more<br />Caused by: java.io.IOException: HTTP Connection failed.<br />	at com.opera.AppletClassLoader.readClassFile(AppletClassLoader.java:378)<br />	at com.opera.AppletClassLoader.access$100(AppletClassLoader.java:34)<br />	at com.opera.AppletClassLoader$4.run(AppletClassLoader.java:360)<br />	at java.security.AccessController.doPrivileged(Native Method)<br />	at com.opera.AppletClassLoader.findClass(AppletClassLoader.java:358)<br />	... 7 more<br />JNLPAppletLauncher: static initializer<br />Exception in thread &quot;AWT-EventQueue-2&quot; java.lang.ClassNotFoundException: slideshow.Main<br />	at com.opera.AppletClassLoader.findClass(AppletClassLoader.java:369)<br />	at java.lang.ClassLoader.loadClass(Unknown Source)<br />	at com.opera.AppletClassLoader.loadClass(AppletClassLoader.java:433)<br />	at java.lang.ClassLoader.loadClass(Unknown Source)<br />	at java.lang.ClassLoader.loadClassInternal(Unknown Source)<br />	at java.lang.Class.forName0(Native Method)<br />	at java.lang.Class.forName(Unknown Source)<br />	at com.sun.javafx.runtime.adapter.Applet.launchStage(Unknown Source)<br />	at com.sun.javafx.runtime.adapter.Applet$1.lambda(Unknown Source)<br />	at com.sun.javafx.runtime.adapter.Applet$1.invoke(Unknown Source)<br />	at com.sun.javafx.runtime.adapter.Applet$1.invoke(Unknown Source)<br />	at com.sun.javafx.runtime.Entry$2.run(Unknown Source)<br />	at java.awt.event.InvocationEvent.dispatch(Unknown Source)<br />	at java.awt.EventQueue.dispatchEvent(Unknown Source)<br />	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)<br />	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)<br />	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)<br />	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)<br />	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)<br />	at java.awt.EventDispatchThread.run(Unknown Source)<br />Caused by: java.io.IOException: HTTP Connection failed.<br />	at com.opera.AppletClassLoader.readClassFile(AppletClassLoader.java:378)<br />	at com.opera.AppletClassLoader.access$100(AppletClassLoader.java:34)<br />	at com.opera.AppletClassLoader$4.run(AppletClassLoader.java:360)<br />	at java.security.AccessController.doPrivileged(Native Method)<br />	at com.opera.AppletClassLoader.findClass(AppletClassLoader.java:358)<br />	... 19 more</code></div></pre>

<p>Once again, I decided to see what would happen when I tried to launch an application without JRE 6 installed on my platform. After removing the previously installed JRE 6u13, I surfed to this Web page, and was greeted by the blank applet window and "Java needed. Click for details." link that are shown in Figure 6.</p>

<p><a href="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42109_6.jpg"
><img src="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42109_6_thumb.jpg
" width="300" height="228" alt="" /></a></p>

<p><strong>Figure 6:</strong> Encountering the blank applet window and "Java needed..." link on Internet Explorer. (Click to enlarge.)</p>

<p>For each of Firefox, Internet Explorer, and Chrome (there's no point in trying Opera), clicking the link resulted in a dialog box presenting the message <em>The current version of Java on this system (0 - unknown) does not support JavaFX. Install Java now?</em> (This message appeared even though there was no Java on my system), along with <strong><code>OK</code></strong> and <strong><code>CANCEL</code></strong> buttons. I clicked <strong><code>OK</code></strong>.</p>

<p>In response, I discovered an identical experience to when I installed the JRE for the Swing application. After completing the install, I decided to try dragging the JavaFX applet onto the desktop. This task is possible for Firefox and Chrome, by pressing <strong><code>Alt</code></strong> and the left arrow keys, and then positioning the mouse cursor over the applet and dragging the mouse. Figure 7 demonstrates applet dragging in a Chrome context.</p>

<p><a href="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42109_7.jpg"
><img src="http://www.javaworld.com/javaworld/jw-04-2009/images/csj42109_7_thumb.jpg
" width="300" height="203" alt="" /></a></p>

<p><strong>Figure 7:</strong> Click the small x (slightly above and to the right of the applet's window) to close the applet and cause it to reappear in the Web page. (Click to enlarge.)</p>

<p>After completing both exercises, I see improvement (from a user perspective) in getting Java onto the platform for the Firefox and Explorer browsers. However, Chrome and (definitely) Opera need work to improve this experience. Also, except for Opera exceptions, I experienced no program crashes or other anomalies while running the Swing application and JavaFX applet, but then I only tested under Windows XP.</p>

<h2>A JavaFX compiler curiosity</h2>

<p>My <a href="http://www.javaworld.com/community/node/2551">initial post</a> to this blog pointed out an oddity in the JavaFX 1.0 and 1.1 compilers: It's possible to invoke functions that don't exist. I've recently discovered another peculiarity, specifically in the JavaFX 1.1 compiler (and probably in JavaFX 1.0 and 1.1.1 as well). The script in Listing 4 demonstrates this peculiarity.</p>

<p><strong>Listing 4:</strong> A peculiar script</p>

<pre><div class="codeblock"><code>function foo1 (): Void<br />{<br />&nbsp;&nbsp;&nbsp; println (&quot;foo1() called&quot;);<br />&nbsp;&nbsp;&nbsp; return 10.0<br />}<br /><br />function foo2 (): Void<br />{<br />&nbsp;&nbsp;&nbsp; println (&quot;foo2() called&quot;);<br />&nbsp;&nbsp;&nbsp; 23<br />}<br /><br />foo1 ();<br />foo2 ()</code></div></pre>

<p>It shouldn't be possible to return a value from a function whose return type is <code>Void</code>, and yet the compiler allows this syntax. When you run the script under JavaFX 1.1, you'll notice <code>foo1() called</code> followed by <code>foo2() called</code> messages in the standard output. Fortunately, the compiler is smart enough to flag an error when you try to assign the function to a variable, as in <code>var x = foo1 ();</code>.</p>

<h2>Conclusion</h2>

<p>Because I could only deploy in a Windows XP context, I'm curious about your experiences when you try to install Java/JavaFX and run the Swing application and JavaFX implementation applet on your Linux, Mac OS X, Solaris, or Vista operating system. I'd also like to know about any JavaFX 1.1 compiler oddities that you've encountered.</p>

<p>Like this blog? <a
href="http://www.javaworld.com/community/blog/14572/feed">Subscribe to
the CSJ Explorer RSS feed</a></p>    ]]></content>
  </entry>
  <entry>
    <title>Enriching your Swing-based user interfaces</title>
    <link rel="alternate" type="text/html" href="http://www.javaworld.com/community/node/2784" />
    <id>http://www.javaworld.com/community/node/2784</id>
    <published>2009-04-14T15:34:29-04:00</published>
    <updated>2009-04-16T03:11:58-04:00</updated>
    <author>
      <name>javajeff</name>
    </author>
    <category term="border" />
    <category term="enrich" />
    <category term="gradient" />
    <category term="login" />
    <category term="logout" />
    <category term="reflection" />
    <category term="rich" />
    <category term="swing" />
    <category term="UI" />
    <category term="user interface" />
    <summary type="html"><![CDATA[<!--paging_filter--><p>You're developing a Swing application that will be delivered to your company's clients. The application's user interface consists of several screens, starting with a login screen in which the user must enter a user ID and a password. After clicking the screen's <strong><code>LOGIN</code></strong> button, the application validates the entered credentials in some fashion, and (assuming they are valid) takes the user to the second screen.</p>

<p>Listing 1 codifies a simplistic version of this application.</p>

<p><strong>Listing 1:</strong> <code>PoorUIApp.java</code></p>    ]]></summary>
    <content type="html"><![CDATA[<!--paging_filter--><p>You're developing a Swing application that will be delivered to your company's clients. The application's user interface consists of several screens, starting with a login screen in which the user must enter a user ID and a password. After clicking the screen's <strong><code>LOGIN</code></strong> button, the application validates the entered credentials in some fashion, and (assuming they are valid) takes the user to the second screen.</p>

<p>Listing 1 codifies a simplistic version of this application.</p>

<p><strong>Listing 1:</strong> <code>PoorUIApp.java</code></p>

<pre><div class="codeblock"><code>// PoorUIApp.java<br /><br />package pooruiapp;<br /><br />import java.awt.*;<br />import java.awt.event.*;<br /><br />import javax.swing.*;<br /><br />public class PoorUIApp extends JFrame<br />{<br />&nbsp;&nbsp;&nbsp; PoorUIApp ()<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setDefaultCloseOperation (EXIT_ON_CLOSE);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setContentPane (new LoginScreen (this));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pack ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setResizable (false);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setVisible (true);<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; public static void main (String [] args)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Runnable r = new Runnable ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void run ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new PoorUIApp ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EventQueue.invokeLater (r);<br />&nbsp;&nbsp;&nbsp; }<br />}<br /><br />class LoginScreen extends JPanel<br />{<br />&nbsp;&nbsp;&nbsp; private JFrame frame;<br /><br />&nbsp;&nbsp;&nbsp; LoginScreen (JFrame frame)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.frame = frame;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createUI ();<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; private void createUI ()<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setLayout (new GridLayout (3, 1));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JPanel panelRow = new JPanel ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JLabel lblUserID;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow.add (lblUserID = new JLabel (&quot;UserID: &quot;));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JTextField txtUserID = new JTextField (10);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow.add (txtUserID);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add (panelRow);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow = new JPanel ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JLabel lblPassword;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow.add (lblPassword = new JLabel (&quot;Password: &quot;));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JPasswordField txtPassword = new JPasswordField (10);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow.add (txtPassword);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add (panelRow);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lblUserID.setPreferredSize (lblPassword.getPreferredSize ());<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow = new JPanel ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JButton btnLogin = new JButton (&quot;LOGIN&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ActionListener alLogin;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alLogin = new ActionListener ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void actionPerformed (ActionEvent ae)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Validate txtUserID&#039;s entered user ID and txtPassword&#039;s<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // entered password, perhaps by connecting to a database and<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // scanning an authentication table for the presence of the<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // entered ID and password.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Assuming successful validation, move to the next screen,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // which happens to be the logout screen (for simplicity).<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frame.setContentPane (new LogoutScreen (frame));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frame.pack ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frame.setDefaultCloseOperation (JFrame.DO_NOTHING_ON_CLOSE);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; btnLogin.addActionListener (alLogin);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow.add (btnLogin);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add (panelRow);<br />&nbsp;&nbsp;&nbsp; }<br />}<br /><br />class LogoutScreen extends JPanel<br />{<br />&nbsp;&nbsp;&nbsp; private JFrame frame;<br /><br />&nbsp;&nbsp;&nbsp; LogoutScreen (JFrame frame)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.frame = frame;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createUI ();<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; private void createUI ()<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setPreferredSize (new Dimension (400, 400));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JButton btnLogout = new JButton (&quot;LOGOUT&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ActionListener alLogout;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alLogout = new ActionListener ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void actionPerformed (ActionEvent ae)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frame.setContentPane (new LoginScreen (frame));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frame.pack ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; btnLogout.addActionListener (alLogout);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add (btnLogout);<br />&nbsp;&nbsp;&nbsp; }<br />}</code></div></pre>

<p>This application's user interface consists exclusively of login and logout screens. (More screens could be added later.) Each screen is implemented as a panel that has its own size, and gets plugged into the user interface as the new content pane when appropriate to do so. The login screen (shown in Figure 1) is presented first, and is the only screen from where the user can exit the application.</p>

<p><img src="http://www.javaworld.com/javaworld/jw-04-2009/images/csj41409.jpg" width="207" height="148" alt="" /></p>

<p><strong>Figure 1:</strong> Click the login screen's <strong><code>X</code></strong> button (on the titlebar) to exit the application.</p>

<p>Figure 1's login screen is problematic. For example, the edges of the user ID and password text fields are not vertically aligned. According to Formulate Information Design's <a href="http://formulate.com.au/articles/alignment/">Alignment</a> article, forms with vertically aligned edges are more aesthetically pleasing and easier to use. Also, this screen's bland appearance will not impress users who expect richer user interfaces.</p>

<h2>User interface makeover</h2>

<p>The first problem is easy to fix. Assuming that <code>lblUserID</code> and <code>lblPassword</code> reference the labels created in Listing 1, invoking <code>lblUserID.setPreferredSize (lblPassword.getPreferredSize ());</code> (after creating both labels) gives the user ID label the same width (and height) as the password label, and results in Figure 2's vertically aligned text fields.</p>

<p><img src="http://www.javaworld.com/javaworld/jw-04-2009/images/csj41409_2.jpg" width="205" height="146" alt="" /></p>

<p><strong>Figure 2:</strong> The labels are also vertically aligned.</p>

<p>The bland appearance problem takes more effort to address. We'll need to enrich the login screen by (hopefully) making clever use of colors, borders, fonts, and gradients; by accessing the <code>UIManager</code> class; and by introducing an image with its own reflection visual effect. Figure 3 shows what a little bit of effort can accomplish.</p>

<p><a href="http://www.javaworld.com/javaworld/jw-04-2009/images/csj41409_3.jpg"
><img src="http://www.javaworld.com/javaworld/jw-04-2009/images/csj41409_3_thumb.jpg
" width="200" height="128" alt="" /></a></p>

<p><strong>Figure 3:</strong> A more compelling login screen. (Click to enlarge.)</p>

<p>Listing 2 presents the source code to the application that's responsible for this enriched screen.</p>

<p><strong>Listing 2:</strong> <code>RichUIApp.java</code></p>

<pre><div class="codeblock"><code>// RichUIApp.java<br /><br />package richuiapp;<br /><br />import java.awt.*;<br />import java.awt.event.*;<br />import java.awt.image.*;<br /><br />import java.util.*;<br /><br />import javax.swing.*;<br />import javax.swing.border.*;<br /><br />public class RichUIApp extends JFrame<br />{<br />&nbsp;&nbsp;&nbsp; RichUIApp ()<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setDefaultCloseOperation (EXIT_ON_CLOSE);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setContentPane (new LoginScreen (this));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pack ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setResizable (false);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setLocationRelativeTo (null);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setVisible (true);<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; public static void main (String [] args)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Runnable r = new Runnable ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void run ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new RichUIApp ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EventQueue.invokeLater (r);<br />&nbsp;&nbsp;&nbsp; }<br />}<br /><br />class LoginScreen extends JPanel<br />{<br />&nbsp;&nbsp;&nbsp; final static Font FNTLBL = new Font (&quot;Arial&quot;, Font.BOLD, 14);<br />&nbsp;&nbsp;&nbsp; final static Font FNTTXT = new Font (&quot;Tahoma&quot;, Font.BOLD, 12);<br /><br />&nbsp;&nbsp;&nbsp; private JFrame frame;<br />&nbsp;&nbsp;&nbsp; private JComponent initComp;<br />&nbsp;&nbsp;&nbsp; private GradientPaint gp;<br /><br />&nbsp;&nbsp;&nbsp; LoginScreen (JFrame frame)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.frame = frame;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UIManager.put (&quot;Button.focus&quot;, Color.BLACK);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; java.util.List gradients = new ArrayList (5);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gradients.add (0.63f);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gradients.add (0.12f);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gradients.add (new Color (0xFF6600));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gradients.add (new Color (0xFFCC33));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gradients.add (new Color (0xFF9933));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UIManager.put (&quot;Button.gradient&quot;, gradients);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UIManager.put (&quot;Button.select&quot;, new Color (0xff, 0xcc, 0x33));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createUI ();<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; void initFocus ()<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; initComp.requestFocusInWindow ();<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; private void createUI ()<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setBorder (new EmptyBorder (10, 10, 10, 10));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setLayout (new GridLayout (3, 1));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JPanel panelRow = new JPanel ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow.setOpaque (false);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JLabel lblUserID = new JLabel (&quot;UserID: &quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lblUserID.setFont (FNTLBL);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lblUserID.setForeground (Color.WHITE);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow.add (lblUserID);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JTextField txtUserID = new JTextField (10);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; txtUserID.setFont (FNTTXT);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Border bdText = new BevelBorder (BevelBorder.LOWERED, Color.LIGHT_GRAY,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Color.GRAY);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Border bdMargin = new EmptyBorder (0, 1, 0, 1);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Border bdCompound = new CompoundBorder (bdText, bdMargin);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; txtUserID.setBorder (bdCompound);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow.add (txtUserID);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add (panelRow);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow = new JPanel ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow.setOpaque (false);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JLabel lblPassword = new JLabel (&quot;Password: &quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lblPassword.setFont (FNTLBL);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lblPassword.setForeground (Color.WHITE);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow.add (lblPassword);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JPasswordField txtPassword = new JPasswordField (10);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; txtPassword.setFont (FNTTXT);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; txtPassword.setBorder (bdCompound);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow.add (txtPassword);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add (panelRow);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lblUserID.setPreferredSize (lblPassword.getPreferredSize ());<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow = new JPanel ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow.setLayout (new FlowLayout (FlowLayout.CENTER, 30, 0));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow.setOpaque (false);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ImageIcon ii = new ImageIcon (&quot;key1.gif&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ii.setImage (ImageUtils.makeReflectedImage (ii.getImage (), 5, 0, 0.5f));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow.add (new JLabel (ii));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JButton btnLogin = new JButton (&quot;LOGIN&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Border bdButton = new BevelBorder (BevelBorder.RAISED, Color.WHITE,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Color.BLACK);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Insets insets = btnLogin.getMargin ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bdMargin = new EmptyBorder (insets.top+1, insets.left+1,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; insets.bottom+1, insets.right+1);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; btnLogin.setBorder (new CompoundBorder (bdButton, bdMargin));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ActionListener alLogin;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alLogin = new ActionListener ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void actionPerformed (ActionEvent ae)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Validate txtUserID&#039;s entered user ID and txtPassword&#039;s<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // entered password, perhaps by connecting to a database and<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // scanning an authentication table for the presence of the<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // entered ID and password.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Assuming successful validation, move to the next screen,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // which happens to be the logout screen (for simplicity).<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LogoutScreen los = new LogoutScreen (frame);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frame.setContentPane (los);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frame.pack ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; los.initFocus ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frame.setLocationRelativeTo (null);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frame.setDefaultCloseOperation (JFrame.DO_NOTHING_ON_CLOSE);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; btnLogin.addActionListener (alLogin);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow.add (btnLogin);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ii = new ImageIcon (&quot;key2.gif&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ii.setImage (ImageUtils.makeReflectedImage (ii.getImage (), 5, 0, 0.5f));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; panelRow.add (new JLabel (ii));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add (panelRow);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; initComp = txtUserID;<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; @Override<br />&nbsp;&nbsp;&nbsp; protected void paintComponent (Graphics g)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (gp == null)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gp = new GradientPaint (0, 0, Color.BLACK, 0, getHeight (),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new Color (96, 96, 96));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ((Graphics2D) g).setPaint (gp);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.fillRect (0, 0, getWidth (), getHeight ());<br />&nbsp;&nbsp;&nbsp; }<br />}<br /><br />class LogoutScreen extends JPanel<br />{<br />&nbsp;&nbsp;&nbsp; private JFrame frame;<br />&nbsp;&nbsp;&nbsp; private JComponent initComp;<br />&nbsp;&nbsp;&nbsp; private GradientPaint gp;<br /><br />&nbsp;&nbsp;&nbsp; LogoutScreen (JFrame frame)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.frame = frame;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createUI ();<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; void initFocus ()<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; initComp.requestFocusInWindow ();<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; private void createUI ()<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setPreferredSize (new Dimension (400, 400));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JButton btnLogout = new JButton (&quot;LOGOUT&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Border bdButton = new BevelBorder (BevelBorder.RAISED, Color.WHITE,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Color.BLACK);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Insets insets = btnLogout.getMargin ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Border bdMargin = new EmptyBorder (insets.top+1, insets.left+1,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; insets.bottom+1, insets.right+1);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; btnLogout.setBorder (new CompoundBorder (bdButton, bdMargin));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ActionListener alLogout;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alLogout = new ActionListener ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void actionPerformed (ActionEvent ae)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LoginScreen lis = new LoginScreen (frame);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frame.setContentPane (lis);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frame.pack ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lis.initFocus ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frame.setLocationRelativeTo (null);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; btnLogout.addActionListener (alLogout);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add (btnLogout);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; initComp = btnLogout;<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; @Override<br />&nbsp;&nbsp;&nbsp; protected void paintComponent (Graphics g)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (gp == null)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gp = new GradientPaint (0, 0, Color.BLACK, 0, getHeight (),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new Color (96, 96, 96));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ((Graphics2D) g).setPaint (gp);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.fillRect (0, 0, getWidth (), getHeight ());<br />&nbsp;&nbsp;&nbsp; }<br />}</code></div></pre>

<p>Listing 2 differs from Listing 1 in terms of various appearance-and-behavioral-oriented features, which I've itemized below:</p>

<ul>
<li>
The <code>RichUIApp()</code> constructor and each of the action listeners for the <strong><code>LOGIN</code></strong> and <strong><code>LOGOUT</code></strong> buttons contains a <code>setLocationRelativeTo(null)</code> method call. This invocation is used to center the frame window and its current login or logout screen on the display.
</li>

<li>
The <code>LoginScreen()</code> constructor customizes the default Metal Look and Feel's/Ocean theme's Swing button by invoking <code>UIManager</code>'s <code>public static Object put(Object key, Object value)</code> method. Specifically, it overrides the system values for <code>Button.focus</code> (the button's focus rectangle color), <code>Button.gradient</code> (the gradient information for filling the button), and <code>Button.select</code> (the fill color when the button is selected, typically by pressing the left mouse button). These <code>UIManager</code> changes affect all buttons on all screens. Also, Sun developer Scott Violet presents the format of the object assigned to <code>Button.gradient</code> in his <a href="http://weblogs.java.net/blog/zixle/archive/2005/09/customizing_oce_1.html">Customizing Ocean Gradients</a> blog post.
</li>

<li>
<code>LoginScreen</code>'s <code>createUI()</code> method first invokes <code>setBorder(new EmptyBorder(10, 10, 10, 10))</code> to surround its screen panel with a 10-pixel empty border. I think that this extra space helps to make the screen look more pleasing because the components are somewhat further away from the frame window borders.
</li>

<li>
Each screen panel's background is painted with a gradient, via the overridden <code>paintComponent()</code> method. Additional panels that organize various components are placed over this background. To ensure that the gradient background shows through, I invoke <code>setOpaque(false)</code> on each of these additional panels.
</li>

<li>
The gradient background ranges from black at the top to (what I call) charcoal gray at the bottom. To allow the user ID and password labels to stand out against this background, I've set each label's foreground color to white. I've also changed each label's font from its default 12-point size to a 14-point size, to help those with less vision better read the label.
</li>

<li>
Metal and its themes are inherently two-dimensional. Because I think a three-dimensional appearance looks nicer, I've employed the <code>BevelBorder</code>, <code>EmptyBorder</code>, and <code>CompoundBorder</code> classes to create a sunken border effect for the text fields. The empty border is placed inside the bevel border to ensure a 1-pixel separation between the text field's caret and the left and right sides of the text field. Later on, I use a similar technique for the button, to give it a raised effect. (Rather than duplicate the border-oriented code for each text field and button, it would be better to introduce factory methods that create these three-dimensional-looking components. I'll leave this task with you.)
</li>

<li>
<code>BevelBorder</code> lets you specify the colors of its highlight and shadow. I've chosen two shades of gray for the text field, and black and white for the button. If you need inspiration on choosing colors, check out Taylored Marketing's <a href="http://www.tayloredmktg.com/rgb/">The Other RGB Color Chart</a> page.
</li>

<li>
Each of the <strong><code>LOGIN</code></strong> and <strong><code>LOGOUT</code></strong> button's action listeners sets the keyboard focus to the first navigable component on the target screen so that the user can tab through that screen's components. Focus is specified via the <code>requestFocusInWindow()</code> method, which is preferable to <code>requestFocus()</code> because code reliant on <code>requestFocus()</code> might exhibit different focus behavior on different platforms.
</li>
</ul>

<p>The login screen's <code>createUI()</code> method also loads two images of keys and creates a reflection for each image. Each reflected image is subsequently assigned to a label, and appears to the left or right of the <strong><code>LOGIN</code></strong> button. The reflection effect depends upon an external <code>ImageUtils</code> class with its <code>makeReflectedImage()</code> method. You'll find this source code in Listing 3.</p>

<p><strong>Listing 3:</strong> <code>ImageUtils.java</code></p>

<pre><div class="codeblock"><code>// ImageUtils.java<br /><br />package richuiapp;<br /><br />import java.awt.AlphaComposite;<br />import java.awt.Color;<br />import java.awt.GradientPaint;<br />import java.awt.Graphics2D;<br />import java.awt.Image;<br /><br />import java.awt.image.BufferedImage;<br /><br />public class ImageUtils<br />{<br />&nbsp;&nbsp;&nbsp; public static BufferedImage makeReflectedImage (Image image, int gap,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int fadeHeight,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float topOpacity)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int width = image.getWidth (null);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int height = image.getHeight (null);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BufferedImage biReflectedImage;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; biReflectedImage = new BufferedImage (width, height*2+gap,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BufferedImage.TYPE_INT_ARGB);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Graphics2D g2dRI = biReflectedImage.createGraphics ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g2dRI.drawImage (image, 0, 0, null);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g2dRI.translate (0, 2*height+gap);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g2dRI.scale (1, -1);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BufferedImage biReflection;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; biReflection = new BufferedImage (width, height,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BufferedImage.TYPE_INT_ARGB );<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Graphics2D g2dR = biReflection.createGraphics ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g2dR.drawImage (image, 0, 0, null);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g2dR.setComposite (AlphaComposite.getInstance (AlphaComposite.DST_IN));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GradientPaint gp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gp = new GradientPaint (0, height*fadeHeight, new Color (0.0f, 0.0f,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.0f, 0.0f), 0, height, new Color (0.0f, 0.0f,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.0f, topOpacity));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g2dR.setPaint (gp);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g2dR.fillRect (0, 0, width, height);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g2dR.dispose ();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g2dRI.drawImage (biReflection, 0, 0, null);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g2dRI.dispose ();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return biReflectedImage;<br />&nbsp;&nbsp;&nbsp; }<br />}</code></div></pre>

<p>The <code>makeReflectedImage()</code> method reflects an input image and returns a <code>BufferedImage</code> that stores this image plus its reflection. The result is configured by arguments <code>gap</code> (the number of pixels between the bottom of the original image and the top of its reflection), <code>fadeHeight</code> (the distance down the reflection at which it fades to nothing), and <code>topOpacity</code> (the opacity of the top line in the reflection).</p>

<p>This method's code is based on public domain code that originated with developer Jerry Huxtable in his <a href="http://www.jhlabs.com/java/java2d/reflections/index.html">Fun with Java2D - Java Reflections</a> article. I recommend reading Huxtable's article to learn how the reflection technique works. (Hint: The technique requires a translucent gradient.)</p>

<h2>Conclusion</h2>

<p>It's easy to throw together unimpressive Swing-based user interfaces, but the extra time and effort spent on enriching these UIs will probably make them more appealing to your users (and easier to work with). This post's example demonstrated a variety of Swing and Java 2D features for enriching its user interface. What other features do you find useful for enriching your Swing-based user interfaces?</p>

<div class="textbox">
<h4 class="textboxhead">After the fact</h4>
<p>After posting this topic, I became aware of two problematic items that I should have mentioned earlier. (I plead tiredness from "burning too much midnight oil" as my excuse.)</p>

<p>First, my choice in fonts is hardly cross-platform. One needs to be careful when choosing fonts to ensure that they are hosted on all platforms where the application will be made available.</p>

<p>Second, this topic originated as a collection of tips for improving the Metal Look and Feel by using simple things such as gradients, borders, and so on. Metal has been the default look and feel for quite some time, but (unless I'm mistaken) probably will be replaced by Nimbus as the default look and feel in JDK 7. As a result, <code>UIManager</code> keys such as <code>Button.gradient</code> probably won't be applicable to Nimbus. Assuming that they are applicable, invoking <code>UIManager.put()</code> to associate one kind of object with a key could result in the application breaking when another kind of object is required. The implication for <code>RichUIApp</code> is that it should test the current look and feel to see if it's Metal, and install Metal if this look and feel is not current.</p>

<p>If you'd like to read more tips on improving your Swing-based user interfaces, check out Shannon Hickey's <a href="http://java.sun.com/products/jfc/tsc/articles/swing2d/">Unleash Your Creativity with Swing and the Java 2D API!</a> article.</p>
                     </div>

<p>Download a source file: <a
href="http://www.javaworld.com/javaworld/jw-04-2009/csj41409-src.zip">csj41409-src.zip</a></p>

<p>Like this blog? <a
href="http://www.javaworld.com/community/blog/14572/feed">Subscribe to
the CSJ Explorer RSS feed</a>    ]]></content>
  </entry>
  <entry>
    <title>Reducing JavaFX&#039;s memory footprint via a CustomNode alternative</title>
    <link rel="alternate" type="text/html" href="http://www.javaworld.com/community/node/2754" />
    <id>http://www.javaworld.com/community/node/2754</id>
    <published>2009-04-07T15:11:48-04:00</published>
    <updated>2009-04-14T16:27:01-04:00</updated>
    <author>
      <name>javajeff</name>
    </author>
    <category term="Canvas" />
    <category term="CustomNode" />
    <category term="javafx" />
    <category term="memory" />
    <category term="performance" />
    <summary type="html"><![CDATA[<!--paging_filter--><p>Much has been written about JavaFX's performance from the perspective of execution speed. Because less has been written about performance in terms of memory footprint, I was interested to discover developer Markus Kohler's JavaFX <a href="http://kohlerm.blogspot.com/2009/03/javafx-memory-overhead-some-high.html">memory overhead blog post</a>.</p>    ]]></summary>
    <content type="html"><![CDATA[<!--paging_filter--><p>Much has been written about JavaFX's performance from the perspective of execution speed. Because less has been written about performance in terms of memory footprint, I was interested to discover developer Markus Kohler's JavaFX <a href="http://kohlerm.blogspot.com/2009/03/javafx-memory-overhead-some-high.html">memory overhead blog post</a>.</p>

<p>Kohler's post focuses on the amount of memory occupied by JavaFX variables of various types (such as <code>Boolean</code>s), which looks to be considerable. I suspect that Sun will manage to significantly reduce this memory usage as JavaFX matures. However, I wonder if Sun will address another area where memory usage could become considerable: custom nodes.</p>

<p>JavaFX's <code>CustomNode</code> class inherits more than 30 attributes (or variables, if you prefer) from its <code>Node</code> superclass. Combine this count with the number of attributes you might add to your own <code>CustomNode</code> subclass, take Kohler's findings into account, and you can see that a single instance of your subclass will probably occupy at least a few hundred bytes.</p>

<p>Imagine a scenario where you create a scene that renders hundreds of custom node instances. For example, perhaps the scene is a complex schematic diagram of an electronic circuit that consists of many instances of various custom node classes representing resistors, diodes, capacitors, and other electronics components. Collectively, these instances could occupy a considerable amount of memory.</p>

<p>I regard <code>CustomNode</code> as JavaFX's analogue of a heavyweight component. For complex scenes that are comprised of many symbols (such as resistors) and other items (such as lines representing connecting wires), I believe that something lightweight is needed, and present a lightweight alternative to <code>CustomNode</code> in this post. Ironically, this alternative depends on a single heavyweight <code>Node</code> instance.</p>

<h2>The <code>SGCanvas</code> and <code>Canvas</code> classes</h2>

<p>My <code>CustomNode</code> alternative combines an <code>SGCanvas</code> Java class with a <code>Canvas</code> JavaFX class. These classes implement a canvas node onto which a string of drawing instructions (specified via a <code>Canvas</code> attribute, and implemented via <code>SGCanvas</code>'s <code>paint()</code> method) renders content whenever the node is rendered. Listing 1 presents <code>SGCanvas</code>'s source code.</p>

<p><strong>Listing 1:</strong> <code>SGCanvas.java</code> (version 1)</p>

<pre><div class="codeblock"><code>/*<br /> * SGCanvas.java<br /> */<br /><br />package canvasdemo1;<br /><br />import java.awt.Color;<br />import java.awt.Graphics2D;<br /><br />import java.awt.geom.AffineTransform;<br />import java.awt.geom.Rectangle2D;<br /><br />import java.util.StringTokenizer;<br /><br />import com.sun.scenario.scenegraph.SGLeaf;<br /><br />public class SGCanvas extends SGLeaf<br />{<br />&nbsp;&nbsp;&nbsp; private Color fill;<br /><br />&nbsp;&nbsp;&nbsp; private int height, width;<br /><br />&nbsp;&nbsp;&nbsp; private String content;<br /><br />&nbsp;&nbsp;&nbsp; @Override<br />&nbsp;&nbsp;&nbsp; public final Rectangle2D getBounds (AffineTransform transform)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;getBounds&quot;);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float x = 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float y = 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float w = width;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float h = height;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (transform != null &amp;&amp; !transform.isIdentity ())<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (transform.getShearX () == 0 &amp;&amp; transform.getShearY () == 0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // No rotations...<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (transform.getScaleX () == 1 &amp;&amp; transform.getScaleY () == 1)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // just a translation...<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x += transform.getTranslateX ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; y += transform.getTranslateY ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float coords [] = { x, y, x+w, y+h };<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; transform.transform (coords, 0, coords, 0, 2);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x = Math.min (coords [0], coords [2]);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; y = Math.min (coords [1], coords [3]);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; w = Math.max (coords [0], coords [2])-x;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; h = Math.max (coords [1], coords [3])-y;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float coords [] = { x, y, x+w, y, x, y+h, x+w, y+h };<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; transform.transform (coords, 0, coords, 0, 4);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x = w = coords [0];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; y = h = coords [1];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 2; i &lt; coords.length; i += 2)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (x &gt; coords [i]) x = coords [i];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (w &lt; coords [i]) w = coords [i];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (y &gt; coords [i+1]) y = coords [i+1];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (h &lt; coords [i+1]) h = coords [i+1];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; w -= x;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; h -= y;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new Rectangle2D.Float (0, 0, width, height);<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; @Override<br />&nbsp;&nbsp;&nbsp; public void paint (Graphics2D g)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.setColor (fill);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.fillRect (0, 0, width, height);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int ox = 0, oy = 0;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StringTokenizer st = new StringTokenizer (content);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (st.hasMoreTokens ())<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String token = st.nextToken ();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (token.equals (&quot;C&quot;))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int red = Integer.parseInt (st.nextToken ());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int grn = Integer.parseInt (st.nextToken ());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int blu = Integer.parseInt (st.nextToken ());<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.setColor (new Color (red, grn, blu));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (token.equals (&quot;L&quot;))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int x = Integer.parseInt (st.nextToken ());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int y = Integer.parseInt (st.nextToken ());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.drawLine (ox, oy, x, y);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ox = x;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oy = y;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (token.equals (&quot;M&quot;))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int x = Integer.parseInt (st.nextToken ());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int y = Integer.parseInt (st.nextToken ());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ox = x;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oy = y;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; public final void setContent (String content)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;setContent&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.content = content;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; repaint (false);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public final void setFill (Color fill)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;setFill&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.fill = fill;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; repaint (false);<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; public final void setHeight (int height)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;setHeight&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.height = height;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; repaint (true);<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; public final void setWidth (int width)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;setWidth&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.width = width;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; repaint (true);<br />&nbsp;&nbsp;&nbsp; }<br />}</code></div></pre>

<p><code>SGCanvas</code> subclasses Project Scene Graph's (PSG's) <code>com.sun.scenario.scenegraph.SGLeaf</code> class in order to tell the JavaFX runtime that it knows how to render its appearance. If you're unfamiliar with PSG (also known as Scenario), check out my InformIT article, <a href="http://www.informit.com/articles/article.aspx?p=1323245">Creating Java User Interfaces with Project Scene Graph</a>.</p>

<p><code>SGCanvas</code> next declares four private write-only properties that specify the canvas's background color, height and width, and content (a string of rendering instructions); and overrides the <code>public final Rectangle2D getBounds(AffineTransform transform)</code> method to return bounds information when needed by the JavaFX runtime. (I copied much of this method's code from PSG's <code>SGImage</code> class.)</p>

<p>Moving on, <code>SGCanvas</code> overrides the <code>public void paint(Graphics2D g)</code> method to render its content whenever the JavaFX runtime requires this node to be rendered. After filling the background with the color specified by the <code>fill</code> property, <code>paint()</code> uses a tokenizer to extract each instruction from the <code>content</code> property, and then performs the instruction's graphics operation:</p>

<ul>
<li>
<code>C red grn blu</code>: Specify the current drawing color. Capital letter C is followed by three integer values that specify the color's red, green, and blue components. Each value must range from 0 through 255.
</li>

<li>
<code>L x y</code>: Draw a line from the current position to the new position. Capital letter L is followed by two integer values that specify the X coordinate followed by the Y coordinate. Each value must be greater than or equal to 0.
</li>

<li>
<code>M x y</code>: Set the current position from where a line is drawn. Capital letter M is followed by two integer values that specify the X coordinate followed by the Y coordinate. Each value must be greater than or equal to 0.
</li>
</ul>

<p>For simplicity, I chose to parse the <code>content</code> string via <code>java.util.StringTokenizer</code>. Because a new <code>StringTokenizer</code> must be created each time the <code>paint()</code> method is invoked (canvas content might have changed), and because at least one space character must appear between each token (usually to prevent a runtime exception), I present a better alternative in the next version of this class.</p> 

<p>Finally, <code>SGCanvas</code> provides four methods for setting its four properties. Each method outputs a message to standard output (letting you know when it's called). It then updates the property and invokes an inherited <code>repaint()</code> method to repaint the canvas: <code>false</code> is passed if only the visual state has changed; <code>true</code> is passed if node bounds have also been changed and must be recalculated.</p>

<p>Listing 2 presents <code>Canvas</code>'s source code.</p>

<p><strong>Listing 2:</strong> <code>Canvas.fx</code> (version 1)</p>

<pre><div class="codeblock"><code>/*<br /> * Canvas.fx<br /> */<br /><br />package canvasdemo1;<br /><br />import javafx.scene.Node;<br /><br />import javafx.scene.paint.Color;<br /><br />import com.sun.scenario.scenegraph.SGNode;<br /><br />public class Canvas extends Node<br />{<br />&nbsp;&nbsp; override function impl_createSGNode (): SGNode<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new SGCanvas ()<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; function getSGCanvas (): SGCanvas<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; impl_getSGNode () as SGCanvas<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; public var content: String on replace<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getSGCanvas ().setContent (content)<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; public var fill: Color = Color.WHITE on replace<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getSGCanvas ().setFill (new java.awt.Color (fill.red, fill.green,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fill.blue))<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; public var height = 0 on replace<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getSGCanvas ().setHeight (height)<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; public var width = 0 on replace<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getSGCanvas ().setWidth (width)<br />&nbsp;&nbsp; }<br />}</code></div></pre>

<p><code>Canvas</code> relies on <code>Node</code>'s undocumented <code>impl_createSGNode()</code> function to instantiate <code>SGCanvas</code>. It also relies on the undocumented <code>impl_getSGNode()</code> function to return this reference whenever it needs to invoke an <code>SGCanvas</code> method, which takes place exclusively in the context of a replace trigger.</p>

<div class="textbox">
<h4 class="textboxhead">Undocumented danger!</h4>
<p>Although both versions of <code>SGCanvas</code> and <code>Canvas</code> work properly under JavaFX 1.1 (and probably under 1.1.1 as well), it's possible that these classes might not work under a future JavaFX release because they rely on JavaFX runtime features (which aren't officially documented).</p>
</div>

<p>I've chosen what I think are reasonable defaults for <code>Canvas</code>'s four attributes: no content to render, a white fill color, and zero dimensions (how big should the node default to when you don't know where it will be placed?). You will need to assign meaningful values to <code>content</code>, <code>height</code>, and <code>width</code>, and possibly to <code>fill</code>, as Listing 3 demonstrates.</p>

<p><strong>Listing 3:</strong> <code>Main.fx</code> (version 1)</p>

<pre><div class="codeblock"><code>/*<br /> * Main.fx<br /> */<br /><br />package canvasdemo1;<br /><br />import javafx.scene.Scene;<br /><br />import javafx.scene.paint.Color;<br /><br />import javafx.stage.Stage;<br /><br />Stage<br />{<br />&nbsp;&nbsp;&nbsp; title: &quot;Canvas Demo #1&quot;<br />&nbsp;&nbsp;&nbsp; width: 300<br />&nbsp;&nbsp;&nbsp; height: 300<br /><br />&nbsp;&nbsp;&nbsp; var sceneRef: Scene<br />&nbsp;&nbsp;&nbsp; scene: sceneRef = Scene<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var canvasRef: Canvas<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Canvas<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content: &quot;C 255 0 0 M 10 10 L 10 90 L 90 90 L 90 10 L 10 10 &quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;C 0 255 0 L 90 90 C 0 0 255 M 90 10 L 10 90&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fill: Color.BLACK<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: 100<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; height: 100<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; canvasRef = Canvas<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content: &quot;C 255 0 0 M 10 10 L 10 90 L 90 90 L 90 10 L 10 10 &quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;C 0 255 0 L 90 90 C 0 0 255 M 90 10 L 10 90&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fill: Color.BLACK<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: 100<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; height: 100<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translateX: bind (sceneRef.width-<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; canvasRef.boundsInParent.width)/2.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translateY: bind (sceneRef.height-<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; canvasRef.boundsInParent.height)/2.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rotate: 30<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scaleX: 1.5<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scaleY: 1.5<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br />&nbsp;&nbsp;&nbsp; }<br />}</code></div></pre>

<p>Listing 3 reveals two occurrences of <code>Canvas</code> being integrated into a scene. You can scale, rotate, and translate the canvas. (You could even assign a key or mouse listener to this node.) The most important aspect of the canvas node, however, is the compactness achieved by specifying its content via a string of instructions. Figure 1 shows the resulting untransformed and transformed canvases with their content.</p>

<p><a href="http://www.javaworld.com/javaworld/jw-04-2009/images/csj40709.jpg"
><img src="http://www.javaworld.com/javaworld/jw-04-2009/images/csj40709_thumb.jpg" width="200" height="200" alt="" /></a></p>

<p><strong>Figure 1:</strong> Untransformed and transformed versions of the canvas reveal a need for antialiasing. (Click to enlarge.)</p>

<p>You need to add <code>Scenario.jar</code> (which contains <code>SGLeaf</code>, <code>SGNode</code>, and other PSG classes) to the <code>CanvasDemo1</code> script's classpath before you can build and run this script -- you also need to perform this task before you can build and run the next section's <code>CanvasDemo2</code> script. Assuming that you're using NetBeans IDE 6.5, complete the following steps to accomplish this task:</p>

<ul>
<li>
Activate the <strong><code>Project Properties</code></strong> dialog box via the <strong><code>File</code></strong> menu, or by right-clicking the project name and selecting <strong><code>Properties</code></strong> from the popup menu.
</li>

<li>
Select this dialog box's <strong><code>Libraries</code></strong> category and click the resulting pane's <strong><code>Add JAR/Folder</code></strong> button.
</li>

<li>
Use the resulting file chooser to navigate to <code>Scenario.jar</code>. On my platform, this file locates in the <code>C:\Program Files\NetBeans 6.5\javafx2\javafx-sdk\lib\desktop\</code> directory.
</li>
</ul>

<h2>Improving the canvas node</h2>

<p>The canvas node needs many improvements. For example, its lines are not antialiased. Also, this node suffers from the string tokenizer issues mentioned previously. And then there is the problem of being restricted to a very limited language in which to specify rendering instructions. This section addresses these deficiencies by revealing an improved canvas node, starting with Listing 4's updated <code>SGCanvas</code> class.</p>

<p><strong>Listing 4:</strong> <code>SGCanvas.java</code> (version 2)</p>

<pre><div class="codeblock"><code>/*<br /> * SGCanvas.java<br /> */<br /><br />package canvasdemo2;<br /><br />import java.awt.Color;<br />import java.awt.Graphics2D;<br />import java.awt.RenderingHints;<br /><br />import java.awt.geom.AffineTransform;<br />import java.awt.geom.Rectangle2D;<br /><br />import java.util.Stack;<br /><br />import com.sun.javafx.runtime.sequence.Sequence;<br /><br />import com.sun.scenario.scenegraph.SGLeaf;<br /><br />public class SGCanvas extends SGLeaf<br />{<br />&nbsp;&nbsp;&nbsp; private boolean smooth;<br /><br />&nbsp;&nbsp;&nbsp; private Color fill;<br /><br />&nbsp;&nbsp;&nbsp; private int height, width;<br /><br />&nbsp;&nbsp;&nbsp; private Sequence&lt;? extends String&gt; content;<br /><br />&nbsp;&nbsp;&nbsp; private Tokenizer tok = new Tokenizer (&quot;&quot;);<br /><br />&nbsp;&nbsp;&nbsp; private Stack&lt;AffineTransform&gt; stack = new Stack&lt;AffineTransform&gt; ();<br /><br />&nbsp;&nbsp;&nbsp; private int ox, oy;<br /><br />&nbsp;&nbsp;&nbsp; @Override<br />&nbsp;&nbsp;&nbsp; public final Rectangle2D getBounds (AffineTransform transform)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;getBounds&quot;);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float x = 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float y = 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float w = width;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float h = height;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (transform != null &amp;&amp; !transform.isIdentity ())<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (transform.getShearX () == 0 &amp;&amp; transform.getShearY () == 0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // No rotations...<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (transform.getScaleX () == 1 &amp;&amp; transform.getScaleY () == 1)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // just a translation...<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x += transform.getTranslateX ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; y += transform.getTranslateY ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float coords [] = { x, y, x+w, y+h };<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; transform.transform (coords, 0, coords, 0, 2);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x = Math.min (coords [0], coords [2]);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; y = Math.min (coords [1], coords [3]);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; w = Math.max (coords [0], coords [2])-x;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; h = Math.max (coords [1], coords [3])-y;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float coords [] = { x, y, x+w, y, x, y+h, x+w, y+h };<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; transform.transform (coords, 0, coords, 0, 4);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x = w = coords [0];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; y = h = coords [1];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 2; i &lt; coords.length; i += 2)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (x &gt; coords [i]) x = coords [i];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (w &lt; coords [i]) w = coords [i];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (y &gt; coords [i+1]) y = coords [i+1];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (h &lt; coords [i+1]) h = coords [i+1];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; w -= x;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; h -= y;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new Rectangle2D.Float (0, 0, width, height);<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; @Override<br />&nbsp;&nbsp;&nbsp; public void paint (Graphics2D g)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (smooth)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.setRenderingHint (RenderingHints.KEY_ANTIALIASING,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RenderingHints.VALUE_ANTIALIAS_ON);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.setColor (fill);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.fillRect (0, 0, width, height);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ox = oy = 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parseContent (content, 0, g);<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; private void parseContent (Sequence&lt;? extends String&gt; content, int index,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Graphics2D g)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.reset (content.get (index));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (tok.type != Tokenizer.TOK_EOS)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.type != Tokenizer.TOK_LETTER)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;instruction letter expected&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.letter == &#039;C&#039;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.type != Tokenizer.TOK_NUMBER)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;number expected&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.number &gt; 255)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;red component &gt; 255: &quot;+tok.number);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int red = tok.number;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.type != Tokenizer.TOK_NUMBER)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;number expected&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.number &gt; 255)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;green component &gt; 255: &quot;+tok.number);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int grn = tok.number;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.type != Tokenizer.TOK_NUMBER)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;number expected&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.number &gt; 255)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;blue component &gt; 255: &quot;+tok.number);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int blu = tok.number;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.setColor (new Color (red, grn, blu));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.letter == &#039;L&#039;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.type != Tokenizer.TOK_NUMBER)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;number expected&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int x = tok.number;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.type != Tokenizer.TOK_NUMBER)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;number expected&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int y = tok.number;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.drawLine (ox, oy, x, y);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ox = x;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oy = y;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.letter == &#039;M&#039;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.type != Tokenizer.TOK_NUMBER)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;number expected&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int x = tok.number;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.type != Tokenizer.TOK_NUMBER)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;number expected&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int y = tok.number;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ox = x;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oy = y;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.letter == &#039;P&#039;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.setTransform (stack.pop ());<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.letter == &#039;R&#039;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.type != Tokenizer.TOK_NUMBER)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;number expected&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int degrees = tok.number;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.rotate (degrees);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.letter == &#039;S&#039;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.type != Tokenizer.TOK_NUMBER)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;number expected&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int sx = tok.number;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.type != Tokenizer.TOK_NUMBER)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;number expected&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int sy = tok.number;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.scale (sx, sy);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.letter == &#039;T&#039;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.type != Tokenizer.TOK_NUMBER)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;number expected&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int tx = tok.number;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.type != Tokenizer.TOK_NUMBER)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;number expected&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int ty = tok.number;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.translate (tx, ty);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.letter == &#039;X&#039;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stack.push (g.getTransform ());<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.letter == &#039;s&#039;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (tok.type != Tokenizer.TOK_NUMBER)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;number expected&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; index = tok.number;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (index &lt; 1 || index &gt;= content.size ())<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;index out of range&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Color c = g.getColor ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.push ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parseContent (content, index, g);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.pop ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.setColor (c);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tok.nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;invalid instruction letter&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; public final void setContent (Sequence&lt;? extends String&gt; content)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;setContent: &quot;+content.size ());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.content = content;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; repaint (false);<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; public final void setFill (Color fill)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;setFill&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.fill = fill;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; repaint (false);<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; public final void setHeight (int height)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;setHeight&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.height = height;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; repaint (true);<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; public final void setSmooth (boolean smooth)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;setSmooth&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.smooth = smooth;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; repaint (false);<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; public final void setWidth (int width)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println (&quot;setWidth&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.width = width;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; repaint (true);<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; class Tokenizer<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final static int TOK_LETTER = 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final static int TOK_NUMBER = 1;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final static int TOK_CHAR = 2;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final static int TOK_EOS = 3;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int type;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char letter;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int number;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private String s;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private int len, pos;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private Stack&lt;State&gt; stack = new Stack&lt;State&gt; ();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Tokenizer (String s)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reset (s);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void nextToken ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (pos &lt; len &amp;&amp; s.charAt (pos) == &#039; &#039;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos++;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (pos == len)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type = TOK_EOS;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (s.charAt (pos) &gt;= &#039;A&#039; &amp;&amp; s.charAt (pos) &lt;= &#039;Z&#039; ||<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.charAt (pos) &gt;= &#039;a&#039; &amp;&amp; s.charAt (pos) &lt;= &#039;z&#039;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; letter = s.charAt (pos++);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type = TOK_LETTER;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (s.charAt (pos) == &#039;-&#039; || s.charAt (pos) &gt;= &#039;0&#039; &amp;&amp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.charAt (pos) &lt;= &#039;9&#039;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int sign = 1;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (s.charAt (pos) == &#039;-&#039;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sign = -1;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (++pos == len || s.charAt (pos) &lt; &#039;0&#039; ||<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.charAt (pos) &gt; &#039;9&#039;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; letter = &#039;-&#039;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type = TOK_CHAR;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; number = 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; do<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; number *= 10;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; number += (s.charAt (pos++)-&#039;0&#039;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (pos &lt; len &amp;&amp; s.charAt (pos) &gt;= &#039;0&#039; &amp;&amp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.charAt (pos) &lt;= &#039;9&#039;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; number *= sign;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type = TOK_NUMBER;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; letter = s.charAt (pos++);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type = TOK_CHAR;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void pop ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; State state = stack.pop ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s = state.s;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; len = state.len;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos = state.pos;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type = state.type;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; number = state.number;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; letter = state.letter;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void push ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; State state = new State ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; state.s = s;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; state.len = len;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; state.pos = pos;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; state.type = type;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; state.number = number;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; state.letter = letter;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stack.push (state);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void reset (String s)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.s = s;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; len = s.length ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos = 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nextToken ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; class State<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String s;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int len, pos, type, number;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char letter;<br />&nbsp;&nbsp;&nbsp; }<br />}</code></div></pre>

<p>This version of <code>SGCanvas</code> supports antialiased lines by providing a write-only Boolean property named <code>smooth</code>, and a setter method named <code>setSmooth()</code>. Furthermore, the <code>paint()</code> method executes <code>g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)</code> whenever <code>smooth</code> is set to <code>true</code>.</p>

<p>I've introduced a new <code>Tokenizer</code> inner class as an alternative to <code>StringTokenizer</code>. This class is instantiated only once, permits zero or more space characters between letters and numbers, and offers push/pop capabilities that are needed to support an expanded language where symbols (such as resistors, diodes, and other electronic component symbols) can be defined and accessed multiple times.</p>

<p>This language expansion is also supported by modifying the <code>content</code> property so that it can store a sequence of <code>Strings</code>. The first string contains instructions for rendering a drawing, and additional strings contain instructions for rendering various symbols. This modification takes advantage of JavaFX's <code>com.sun.javafx.runtime.sequence.Sequence</code> class.</p>

<p>The following instructions have been added to the language:</p>

<ul>
<li>
<code>P</code>: Pop the most recently pushed transformation matrix from the stack and make it current. Capital letter P is specified by itself.
</li>

<li>
<code>R deg</code>: Append a rotation to the current transformation matrix. Capital letter R is followed by an integer value that specifies the number of degrees to rotate.
</li>

<li>
<code>S sx sy</code>: Append a scaling transformation to the current transformation matrix. Capital letter S is followed by two integer values that specify the amount of scaling horizontally followed by the amount of scaling vertically. For example, <code>S 1 1</code> means no scaling, and <code>S 2 2</code> means multiply every coordinate by 2 to double the size of the drawing.
</li>

<li>
<code>T tx ty</code>: Append a translation to the current transformation matrix. Capital letter T is followed by two integer values that specify the amount of translation horizontally followed by the amount of translation vertically.
</li>

<li>
<code>X</code>: Push the current transformation matrix onto the stack. Capital letter X is specified by itself.
</li>

<li>
<code>s index</code>: Execute a symbol's instructions. Lowercase letter s is followed by a one-based integer value that indexes into <code>content</code>. This index value must range from 1 through one less than the number of entries in the <code>content</code> sequence.
</li>
</ul>

<p>Listing 5 presents the updated <code>Canvas</code> class's source code. Apart from a new <code>smooth</code> attribute that provides access to <code>SGCanvas</code>'s <code>smooth</code> property, this <code>Canvas</code> version is identical to the original version.</p>

<p><strong>Listing 5:</strong> <code>Canvas.fx</code> (version 2)</p>

<pre><div class="codeblock"><code>/*<br /> * Canvas.fx<br /> */<br /><br />package canvasdemo2;<br /><br />import javafx.scene.Node;<br /><br />import javafx.scene.paint.Color;<br /><br />import com.sun.scenario.scenegraph.SGNode;<br /><br />public class Canvas extends Node<br />{<br />&nbsp;&nbsp; override function impl_createSGNode (): SGNode<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new SGCanvas ()<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; function getSGCanvas (): SGCanvas<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; impl_getSGNode () as SGCanvas<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; public var content: String[] on replace<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getSGCanvas ().setContent (content)<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; public var fill: Color = Color.WHITE on replace<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getSGCanvas ().setFill (new java.awt.Color (fill.red, fill.green,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fill.blue))<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; public var height = 0 on replace<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getSGCanvas ().setHeight (height)<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; public var smooth: Boolean on replace<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getSGCanvas ().setSmooth (smooth)<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; public var width = 0 on replace<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getSGCanvas ().setWidth (width)<br />&nbsp;&nbsp; }<br />}</code></div></pre>

<p>I've created a new script that demonstrates the updated canvas node. Listing 6 presents its source code.</p>

<p><strong>Listing 6:</strong> <code>Main.fx</code> (version 2)</p>

<pre><div class="codeblock"><code>/*<br /> * Main.fx<br /> */<br /><br />package canvasdemo2;<br /><br />import javafx.scene.Scene;<br /><br />import javafx.scene.paint.Color;<br /><br />import javafx.stage.Stage;<br /><br />Stage<br />{<br />&nbsp;&nbsp;&nbsp; title: &quot;Canvas Demo #2&quot;<br />&nbsp;&nbsp;&nbsp; width: 300<br />&nbsp;&nbsp;&nbsp; height: 300<br /><br />&nbsp;&nbsp;&nbsp; var sceneRef: Scene<br />&nbsp;&nbsp;&nbsp; scene: sceneRef = Scene<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var canvasRef: Canvas<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Canvas<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content: &quot;C255 0 0M10 10L10 90L90 90L90 10L10 10&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;C0 255 0L90 90C0 0 255M90 10L10 90&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fill: Color.BLACK<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: 100<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; height: 100<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; smooth: true<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; canvasRef = Canvas<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;C255 255 255 S 2 2 T -14 -5 M 19 20 L 25 20 X T 25 18 s1 P&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;M 37 20 L 42 20 X T 42 18 s1 P M 54 20 L 59 20&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;L 59 35 L 42 35 X T 35 32 s2 P M 35 35 L 19 35 L 19 20&quot;,<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;M 0 2 L 2 0 L 4 2 L 6 0 L 8 2 L 10 0 L 12 2&quot;,<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;M0 0L0 6M2 2L2 5M4 0L4 6M6 2 L6 4 M-2 -4 L2 -4 M0 -6 L0 -2&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fill: Color.BLACK<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: 100<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; height: 100<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; smooth: true<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translateX: bind (sceneRef.width-<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; canvasRef.boundsInParent.width)/2.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translateY: bind (sceneRef.height-<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; canvasRef.boundsInParent.height)/2.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rotate: 30<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scaleX: 1.5<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scaleY: 1.5<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br />&nbsp;&nbsp;&nbsp; }<br />}</code></div></pre>

<p>Listing 6 is similar to Listing 3 except for a new <code>smooth</code> attribute in both <code>Canvas</code> literals, and a new <code>content</code> sequence in the second literal. This sequence contains three entries, with the first entry specifying instructions for an overall drawing, and the second and third entries each specifying instructions for rendering a symbol.</p>

<p>Each of the second and third entries define a symbol in its own coordinate system, with (0, 0) as the upper-left corner. The first entry uses the translation instruction to position two instances of the first symbol and one instance of the second symbol into the drawing. Ultimately, as shown in Figure 2, a simplified electronic circuit schematic diagram of two resistors connected in series to a battery is revealed.</p>

<p><a href="http://www.javaworld.com/javaworld/jw-04-2009/images/csj40709_2.jpg"
><img src="http://www.javaworld.com/javaworld/jw-04-2009/images/csj40709_2_thumb.jpg" width="200" height="200" alt="" /></a></p>

<p><strong>Figure 2</strong> Untransformed and transformed versions of the improved canvas reveal antialiased versions of the previous script's content and an electronic circuit schematic diagram. (Click to enlarge.)</p>

<h2>
Conclusion
</h2>

<p>Although the canvas node alternative to <code>CustomNode</code> is helpful for reducing JavaFX memory overhead, it could be improved. For example, this node would benefit from dynamically updating part of a drawing whenever mouse activity occurs over that part. Also, its language would benefit from floating-point values instead of integers (especially for scaling). What other improvements would your recommend?</p>

<p>Download a source file: <a
href="http://www.javaworld.com/javaworld/jw-04-2009/csj40709-src.zip">csj40709-src.zip</a></p>

<p>Like this blog? <a
href="http://www.javaworld.com/community/blog/14572/feed">Subscribe to
the CSJ Explorer RSS feed</a></p>    ]]></content>
  </entry>
  <entry>
    <title>A skinnable button component for JavaFX</title>
    <link rel="alternate" type="text/html" href="http://www.javaworld.com/community/node/2720" />
    <id>http://www.javaworld.com/community/node/2720</id>
    <published>2009-03-31T15:58:33-04:00</published>
    <updated>2009-04-14T16:29:48-04:00</updated>
    <author>
      <name>javajeff</name>
    </author>
    <category term="button" />
    <category term="component" />
    <category term="control" />
    <category term="javafx" />
    <category term="skin" />
    <category term="skinnable" />
    <summary type="html"><![CDATA[<!--paging_filter--><p>JavaFX's <code>CustomNode</code> class lets you create reusable node-based components for your user interfaces. Because these components aren't <em>skinnable</em>, you can't change a component's appearance and/or behavior without rewriting the component. Fortunately, Sun has addressed this limitation by providing the <code>javafx.scene.control</code> package with its two core classes:</p>    ]]></summary>
    <content type="html"><![CDATA[<!--paging_filter--><p>JavaFX's <code>CustomNode</code> class lets you create reusable node-based components for your user interfaces. Because these components aren't <em>skinnable</em>, you can't change a component's appearance and/or behavior without rewriting the component. Fortunately, Sun has addressed this limitation by providing the <code>javafx.scene.control</code> package with its two core classes:</p>

<ul>
<li>
The <code>Control</code> class, which subclasses <code>CustomNode</code>, provides the component's model.
</li>

<li>
The <code>Skin</code> class provides the component's look and feel.
</li>
</ul>

<p>Because JavaFX 1.1's documentation on <code>Control</code> and <code>Skin</code> is somewhat obtuse, I created this blog post to share some insight into using these classes to create components beyond <code>javafx.scene.control</code>'s <code>TextBox</code> class. I'll specifically reveal a button component in terms of its <code>Button</code> model and <code>ButtonSkin</code> look and feel classes, and demonstrate using this component with the textbox.</p>

<h2>
Designing a skinnable button component
</h2>

<p><code>Control</code> is the base class for user-interface components that support skins. You extend this class to define the component's model, supplying appropriate model-oriented attributes and an <code>init</code> block to initialize the component's default skin. For example, Listing 1 presents a <code>Button</code> class that subclasses <code>Control</code> to specify the model for a simple push button.</p>

<p><strong>Listing 1:</strong> The <code>Button</code> model class</p>

<pre><div class="codeblock"><code>public class Button extends Control<br />{<br />&nbsp;&nbsp;&nbsp; public var text: String;<br />&nbsp;&nbsp;&nbsp; public var action: function (): Void;<br /><br />&nbsp;&nbsp;&nbsp; override protected var focused on replace<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; println (&quot;focused = {focused}&quot;)<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; override protected var hover on replace<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; println (&quot;hover = {hover}&quot;)<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; override protected var pressed on replace<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; println (&quot;pressed = {pressed}&quot;)<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; init<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; skin = ButtonSkin {}<br />&nbsp;&nbsp;&nbsp; }<br />}</code></div></pre>

<p>This model requires two attributes: text to display, and a function to run whenever the button is activated (by a mouse click, or by pressing a special key while the button is focused). Additionally, the <code>init</code> block creates the button's skin as an instance of the <code>ButtonSkin</code> class, assigning this skin to <code>Button</code>'s <code>skin</code> attribute, which this class inherits from <code>Control</code>.</p>

<p>You'll notice that I've overridden <code>Node</code>'s <code>focused</code>, <code>hover</code>, and <code>pressed</code> attributes. Whenever the values of the skin's corresponding <code>focused</code>, <code>hover</code>, and <code>pressed</code> attributes change, the values of these attributes also change (thanks to behind-the-scenes binding). The replace triggers let you observe these changes.</p>

<p><code>Skin</code> is the base class for defining a component's look and feel. Along with <code>focused</code>, <code>hover</code>, and <code>pressed</code>, <code>Skin</code> provides a <code>control</code> attribute for referencing the component's model's attributes, and a <code>scene</code> attribute for specifying the look and feel's scene graph. Listing 2's <code>ButtonSkin</code> class subclasses <code>Skin</code> to supply this look and feel.</p>

<p><strong>Listing 2:</strong> The <code>ButtonSkin</code> look and feel class</p>

<pre><div class="codeblock"><code>class ButtonSkin extends Skin<br />{<br />&nbsp;&nbsp;&nbsp; def buttonControl = bind control as Button;<br /><br />&nbsp;&nbsp;&nbsp; override protected def focused = bind scene.focused;<br /><br />&nbsp;&nbsp;&nbsp; override protected def hover = bind scene.hover;<br /><br />&nbsp;&nbsp;&nbsp; override protected def pressed = bind scene.pressed on replace<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (pressed)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scene.requestFocus ()<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; init<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scene = Group<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var rectRef: Rectangle<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var textRef: Text<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rectRef = Rectangle<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arcHeight: 12<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arcWidth: 12<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; height: bind buttonControl.height<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: bind buttonControl.width<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stroke: bind if (focused) Color.STEELBLUE else Color.GRAY<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strokeWidth: bind if (focused) 2.0 else 1.5<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; opacity: 0.7<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fill: LinearGradient<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; startX: 0.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; startY: 0.0<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; endX: 0.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; endY: 1.0<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stops:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Stop<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; offset: 0.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color: Color.web (&quot;#dceaff&quot;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Stop<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; offset: 0.49<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color: Color.web (&quot;#6885b2&quot;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Stop<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; offset: 0.5<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color: Color.web (&quot;#2c599c&quot;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Stop<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; offset: 1.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color: Color.web (&quot;#bed3f4&quot;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; textRef = Text<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var w: Number<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var h: Number<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var _focused = false<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def x = bind rectRef.boundsInLocal on replace<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (focused)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _focused = true<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (not _focused)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; w = x.width;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; h = x.height;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translateX: bind (w-textRef.boundsInLocal.width)/2<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translateY: bind h/2<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x: bind if (pressed) 1 else 0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; y: bind if (pressed) 1 else 0<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content: bind buttonControl.text<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font: Font<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name: &quot;Arial BOLD&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size: 12<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fill: Color.WHITE<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onMouseClicked: function (e: MouseEvent): Void<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buttonControl.action ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onKeyPressed: function (e: KeyEvent): Void<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (e.code != KeyCode.VK_SPACE)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def pause = PauseTransition<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; duration: 0.15s<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; action: function (): Void<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scene.pressed = false<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scene.pressed = true;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pause.play ();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buttonControl.action ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />}</code></div></pre>

<p><code>ButtonSkin</code> binds <code>Skin</code>'s <code>control</code> attribute to <code>buttonControl</code> so that it can access the model's <code>action</code> and <code>text</code> attributes, not to mention inherited attributes such as <code>height</code> and <code>width</code>. (I probably should have specified <code>buttonModel</code> or <code>model</code> instead of <code>buttonControl</code>, to more accurately indicate this attribute's purpose.)</p>

<p><code>ButtonSkin</code> next overrides <code>focused</code>, <code>hover</code>, and <code>pressed</code> (which are implicitly bound to <code>Button</code>'s <code>focused</code>, <code>hover</code>, and <code>pressed</code> attributes), binding them to <code>scene</code>'s versions of these attributes -- when <code>scene</code>'s attributes change, binding causes <code>ButtonSkin</code>'s same-named attributes to change, which implicitly causes <code>Button</code>'s same-named attributes to change.</p>

<p>When <code>ButtonSkin</code>'s <code>pressed</code> attribute changes its value (in response to the user pressing the mouse button while the mouse is over the component's scene graph, or releasing this button, whether or not the mouse is still over the scene graph), the replace trigger executes. If <code>pressed</code> is true, the trigger tells the JavaFX runtime to give focus to the scene graph (via <code>requestFocus()</code>).</p>

<p>When the scene graph receives focus (via <code>requestFocus()</code> or the <strong><code>Tab</code></strong> key), <code>ButtonSkin</code>'s <code>focused</code> attribute is set to true. This attribute is used (via binding) to enlarge the button's outline and change its color (to indicate focus), and to solve a small problem (discussed later). (The scene graph receives focus when its root node is assigned a key handler, such as the function assigned to <code>onKeyPressed</code>.) 

<p><code>ButtonSkin</code>'s <code>init</code> block creates the scene graph, assigning its root node to the <code>scene</code> attribute. This node is of type <code>Group</code> because the scene graph consists of a <code>Text</code> node overlaying a <code>Rectangle</code> node. To ensure that the button receives focus, <code>Group</code>'s <code>onKeyPressed</code> attribute is assigned a key handler. (Assigning a key handler to <code>Rectangle</code>'s or <code>Text</code>'s <code>onKeyPressed</code> attribute wouldn't work.)</p>

<p>A mouse handler is similarly assigned to <code>Group</code>'s <code>onMouseClicked</code> attribute. Unlike the key handler, you could place the mouse handler in the <code>Rectangle</code> node and achieve the same effect. Alternatively, you could place this handler in the <code>Text</code> node. In this case, however, the handler would only activate (in response to a button click) whenever the mouse was exactly over the text -- not over the rectangle.</p>

<p>The <code>Rectangle</code> node's look is governed by its <code>arcWidth</code> and <code>arcHeight</code> attributes (which should probably be a percentage of this node's size rather than being specific values), its <code>width</code> and <code>height</code> attributes (which are bound to the model), its <code>stroke</code> and <code>strokeWidth</code> attributes (whose values depend on the scene graph's focused state), and its <code>opacity</code> and <code>fill</code> attributes (which specify its background).</p>
 
<p>The number of stops and their values for the <code>LinearGradient</code> assigned to the <code>fill</code> attribute originated in Amer Sohail's informative <a href="http://javainnovations.blogspot.com/2008/10/creating-custom-button-in-javafx.html">Creating Custom Button in JavaFX</a> blog post -- I like the glassy appearance achieved by this gradient.</p>

<p>Moving on, we discover the <code>Text</code> node. Its object literal begins in a strange way, by specifying <code>w</code>, <code>h</code>, <code>_focused</code>, and <code>x</code> variables. Variable <code>x</code> is bound to the rectangle's <code>boundsInLocal</code> attribute, and provides a replace trigger to keep track of this attribute's <code>width</code> and <code>height</code> attribute values (in variables <code>w</code> and <code>h</code>) until the scene graph is first focused.</p>              

<p>The <code>w</code> and <code>h</code> values are used with <code>translateX</code> and <code>translateY</code> to center the text horizontally over the rectangle background, and to give the text a nice vertical alignment according to its baseline. I bind to <code>w</code> and <code>h</code> instead of <code>boundsInLocal.width</code> and <code>boundsInLocal.height</code> because these dimensions change when the scene graph is focused/unfocused, which causes the text to move around disconcertingly.</p>

<p>In addition to binding <code>x</code> and <code>y</code> to expressions that use <code>pressed</code> to allow the text to shift slightly when the mouse is pressed, the <code>Text</code> literal also binds the <code>content</code> attribute to the model's <code>text</code> attribute. However, it supplies a specific font and a specific text color. (I elected to hardcode these values because they can be changed via stylesheet settings, but only in the desktop profile.)</p>

<p>Finally, <code>Group</code>'s <code>onMouseClicked</code> and <code>onKeyPressed</code> handler functions invoke the model's <code>action</code> function. However, <code>onKeyPressed</code>'s function has more work to do than <code>onMouseClicked</code>'s function. Specifically, it detects an appropriate key to serve as the trigger for invoking the <code>action</code> function -- I've chosen the spacebar key. It also simulates a button click with the help of <code>scene.pressed</code> and a pause transition.</p>

<h2>
Using the skinnable button component
</h2>

<p>I've written a script that employs the button and textbox components in the user interface of a utility that converts between degrees and radians. Enter a numeric degrees/radians value into the textbox, and click a button to convert this value to radians or degrees. The result appears in the textbox, whose contents are erased by clicking a third button. Figure 1 shows this user interface from the desktop perspective.</p>

<p><a href="http://www.javaworld.com/javaworld/jw-03-2009/images/csj33109.jpg"
><img src="http://www.javaworld.com/javaworld/jw-03-2009/images/csj33109_thumb.jpg" width="200" height="98" alt="" /></a></p>

<p><strong>Figure 1:</strong> The textbox displays the result of converting 180 degrees to radians via the <strong><code>Deg2Rad</code></strong> button, which currently has the focus. (Click to enlarge.)</p>

<p>Listing 3 excerpts the script's <code>Stage</code> literal, which is located in the <code>Main.fx</code> file of a NetBeans project named <code>DRC</code>. This literal specifies the user interface shown in Figure 1.</p>

<p><strong>Listing 3:</strong> The <code>DRC</code> script's stage</p>

<pre><div class="codeblock"><code>Stage<br />{<br />&nbsp;&nbsp;&nbsp; var model = Model {}<br /><br />&nbsp;&nbsp;&nbsp; title: &quot;DRC&quot;<br /><br />&nbsp;&nbsp;&nbsp; width: 390<br />&nbsp;&nbsp;&nbsp; height: 190<br /><br />&nbsp;&nbsp;&nbsp; var sceneRef: Scene<br />&nbsp;&nbsp;&nbsp; scene: sceneRef = Scene<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fill: LinearGradient<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; startX: 0.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; startY: 0.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; endX: 0.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; endY: 1.0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stops:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Stop { offset: 0.0 color: Color.YELLOW },<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Stop { offset: 1.0 color: Color.CYAN }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var input: TextBox<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content: VBox<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translateY: 20<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spacing: 20<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var titleRef: Text<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; titleRef = Text<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content: &quot;Degrees/Radians Converter&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font: Font<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name: &quot;Arial BOLD&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size: 18<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; textOrigin: TextOrigin.TOP<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translateX: bind (sceneRef.width-<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; titleRef.boundsInLocal.width)/2<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input = TextBox<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value: bind model.input with inverse<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translateX: bind (sceneRef.width-<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input.boundsInLocal.width)/2<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HBox<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def BTNWIDTH = 80<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def SPACING = 10<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spacing: SPACING<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translateX: bind (sceneRef.width-3*(BTNWIDTH+SPACING))/2<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Button<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text: &quot;Deg2Rad&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: BTNWIDTH<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; height: 28<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; action: function (): Void<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; model.deg2rad ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Button<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text: &quot;Rad2Deg&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: BTNWIDTH<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; height: 28<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; action: function (): Void<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; model.rad2deg ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Button<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text: &quot;Clear&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: BTNWIDTH<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; height: 28<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; action: function (): Void<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input.value = &quot;&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />}</code></div></pre>

<p>You might be wondering why I assign, to <code>HBox</code>'s <code>translateX</code> attribute, the <code>bind (sceneRef.width-3*(BTNWIDTH+SPACING))/2</code> expression rather than the equivalent <code>bind (sceneRef.width-panel.boundsInLocal.width)/2</code> expression (where <code>panel</code> references the <code>HBox</code> literal), to horizontally center the three buttons.</p>

<p>If I assign <code>bind (sceneRef.width-panel.boundsInLocal.width)/2</code> to <code>translateX</code> and, in the <code>ButtonSkin</code> class, increase the font size to a certain value such as 14 or 18, I've found that the leftmost button's text shifts horizontally as focus moves from one component to another. I'm not sure why this anomaly occurs, but I can overcome it via <code>(sceneRef.width-3*(BTNWIDTH+SPACING))/2</code>.</p>

<p>Listing 4 presents the script's <code>Model</code> class, which the stage instantiates and the button handlers use to perform the conversions. (The conversion code is unecessarily complicated, but interesting.)</p>

<p><strong>Listing 4:</strong> The <code>DRC</code> script's <code>Model</code> class</p>

<pre><div class="codeblock"><code>class Model<br />{<br />&nbsp;&nbsp;&nbsp; def base = &quot;http://www.webservicex.net/ConvertAngle.asmx/ChangeAngleUnit?&quot;;<br /><br />&nbsp;&nbsp;&nbsp; var input: String;<br /><br />&nbsp;&nbsp;&nbsp; function deg2rad (): Void<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def request = HttpRequest<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; location: &quot;{base}AngleValue={input}&amp;fromAngleUnit=degrees&amp;&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;toAngleUnit=radians&quot;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onException: function (ex: Exception): Void<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input = ex.getMessage ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onInput: function (is: InputStream)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def parser = PullParser<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; documentType: PullParser.XML<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input: is<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onEvent: function (event: Event)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (event.type == PullParser.END_ELEMENT)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (event.qname.name == &quot;double&quot;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input = event.text<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parser.parse ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parser.input.close ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.enqueue ()<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; function rad2deg (): Void<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def request = HttpRequest<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; location: &quot;{base}AngleValue={input}&amp;fromAngleUnit=radians&amp;&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;toAngleUnit=degrees&quot;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onException: function (ex: Exception): Void<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input = ex.getMessage ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onInput: function (is: InputStream)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def parser = PullParser<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; documentType: PullParser.XML<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input: is<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onEvent: function (event: Event)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (event.type == PullParser.END_ELEMENT)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (event.qname.name == &quot;double&quot;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input = event.text<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parser.parse ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parser.input.close ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.enqueue ()<br />&nbsp;&nbsp;&nbsp; }<br />}</code></div></pre>

<p>The <code>Model</code> class specifies an <code>input</code> attribute that stores the value to be converted, and also stores the result of the conversion. Look closely at Listing 3 and you'll discover the <code>bind model.input with inverse</code> expression, which is assigned to the <code>TextBox</code>'s <code>value</code> attribute. This binding allows the model to easily access the input value, and store the result in the textbox.</p>

<p><code>Model</code> also specifies functions <code>deg2rad()</code> and <code>rad2deg()</code> for performing the degrees-to-radians and radians-to-degrees conversions. Although I could have easily hardcoded these calculations in these functions, I found a Web service that performs this task, and wanted to play with the <code>HttpRequest</code> and <code>PullParser</code> classes.</p>

<p>Whenever <code>deg2rad()</code> or <code>rad2deg()</code> is called, the function creates an <code>HttpRequest</code> object that specifies the location of the Web service, the angle value to pass to this service, and the conversion direction (degrees to radians or radians to degrees). The function then enqueues the request to run in the background, which keeps the user interface responsive.</p>

<p>If a non-numeric value (a letter or the empty string, for example) or otherwise illegal value (such as a mixture of digits and letters) is assigned to <code>AngleValue</code> and the Web service is invoked, the Web service returns an error value that results in the handler function assigned to <code>onException</code> being invoked. For simplicity, I assign this value to the model's <code>input</code> attribute, allowing it to appear in the textbox.</p>

<p>The <code>onInput</code> handler function is invoked whenever the Web service successfully returns the converted value, via a small XML document. This function creates a <code>PullParser</code> to parse out the converted value from the document's <code>double</code> tag, and stores this value in the model's <code>input</code> attribute, which subsequently appears in the user interface's textbox.</p>

<p>That's pretty much it for how the <code>DRC</code> script works. Because this script and the button component access only classes belonging to the common profile (unfortunately, I can't take advantage of the desktop-profile-oriented <code>javafx.scene.effect</code> classes), the script will run on the mobile emulator. Check out Figure 2 for the proof.</p>

<p><a href="http://www.javaworld.com/javaworld/jw-03-2009/images/csj33109_2.jpg"
><img src="http://www.javaworld.com/javaworld/jw-03-2009/images/csj33109_2_thumb.jpg" width="200" height="90" alt="" /></a></p>

<p><strong>Figure 2:</strong> The user interface must be rotated 90 degrees to see it in its entirety. In a future blog post, I'll discuss creating user interfaces that automatically adapt to different screen dimensions. (Click to enlarge.)</p>

<p>While running the script on the mobile emulator, an interesting thing happens whenever you specify an invalid value (leave the textbox empty, for example), and then click a conversion button. Instead of the <code>onException</code> handler function being invoked, allowing the Web service's error message to appear in the textbox, the following <code>NullPointerException</code> is thrown:</p>

<pre><div class="codeblock"><code>java.lang.NullPointerException:&nbsp;&nbsp; 0<br /> - com.sun.javafx.data.pull.ukit.xml.ParserStAX.panic(), bci=25<br /> - com.sun.javafx.data.pull.ukit.xml.Parser.step(), bci=1048<br /> - com.sun.javafx.data.pull.ukit.xml.ParserStAX.next(), bci=204<br /> - javafx.data.pull.PullParser.next$impl(), bci=40<br /> - javafx.data.pull.PullParser.next(), bci=1<br /> - javafx.data.pull.PullParser.parse$impl(), bci=24<br /> - javafx.data.pull.PullParser.parse(), bci=1<br /> - drc.Main$Model$2.lambda(), bci=66<br /> - drc.Main$Model$2.invoke(), bci=2<br /> - drc.Main$Model$2.invoke(), bci=5<br /> - javafx.io.http.HttpRequest.setInput$impl(), bci=48<br /> - javafx.io.http.HttpRequest.setInput(), bci=2<br /> - com.sun.javafx.io.http.impl.BaseTask$ReadNotifier.run(), bci=15<br /> - com.sun.javafx.io.http.impl.msa.MsaProfile$1.invoke(), bci=4<br /> - javafx.lang.FX$1.run(), bci=4<br /> - com.sun.fxme.runtime.RunnableQueue$Worker.run(), bci=222</code></div></pre>

<p>This exception appears to be thrown because of a bug in the JavaFX mobile runtime, and will be reported. Although somewhat annoying, the bug could be avoided by validating the textbox's input prior to invoking the Web service. Alternatively, I could avoid the Web service and just hardcode the calculations (but that wouldn't be as much fun).</pre>

<h2>
Conclusion
</h2>

<p>Although it can be challenging to create skinnable components, I intend to present additional examples that complement the button component in future blog posts. Let me know if you have a favorite component that you'd like to see implemented next. In the meantime, perhaps Sun will provide additional skinnable components (and standardized support for <em>themes</em>, groups of logically related skins) in JavaFX 1.5.</p>

<p>Download a source file: <a
href="http://www.javaworld.com/javaworld/jw-03-2009/csj33109-src.zip">cs
j33109-src.zip</a></p>

<p>Like this blog? <a
href="http://www.javaworld.com/community/blog/14572/feed">Subscribe to
the CSJ Explorer RSS feed</a></p>    ]]></content>
  </entry>
  <entry>
    <title>A SwingSpinnerList component for JavaFX</title>
    <link rel="alternate" type="text/html" href="http://www.javaworld.com/community/node/2670" />
    <id>http://www.javaworld.com/community/node/2670</id>
    <published>2009-03-24T16:14:58-04:00</published>
    <updated>2009-04-14T16:30:46-04:00</updated>
    <author>
      <name>javajeff</name>
    </author>
    <category term="component" />
    <category term="javafx" />
    <category term="JSpinner" />
    <category term="SwingSpinnerList" />
    <summary type="html"><![CDATA[<!--paging_filter--><p>JavaFX 1.0 and 1.1 provide a <code>javafx.ext.swing</code> package of Swing-based component classes that JavaFX scripts can access to build desktop-oriented user interfaces. Although this package contains many useful component classes, it lacks classes for advanced Swing components such as progress bars, trees, and tables. However, it's easy to create classes for these overlooked components.</p>    ]]></summary>
    <content type="html"><![CDATA[<!--paging_filter--><p>JavaFX 1.0 and 1.1 provide a <code>javafx.ext.swing</code> package of Swing-based component classes that JavaFX scripts can access to build desktop-oriented user interfaces. Although this package contains many useful component classes, it lacks classes for advanced Swing components such as progress bars, trees, and tables. However, it's easy to create classes for these overlooked components.</p>

<p>The information for making an arbitrary Swing component accessible to JavaFX code is found in the <a href="http://javafx.com/docs/techtips/swing.jsp">Using Custom Swing Components</a> technical tip from Sun Microsystems. According to this tip, you need to perform the following tasks (the first two tasks are mandatory):</p>

<ul>
<li>
Create a wrapper class for the Swing component. The wrapper class must extend <code>javafx.ext.swing.SwingComponent</code>.
</li>

<li>
Override <code>SwingComponent</code>'s <code>protected abstract createJComponent(): javax.swing.JComponent</code> function to return an instance of the Swing component.
</li>

<li>
If it's necessary to handle events from the Swing component, define appropriate JavaFX variables of function types, and create/register Swing event listeners that delegate event handling to the functions assigned to these variables.
</li>
</ul>

<p>The <a href="http://jfx.wikia.com/wiki/SwingComponents">Swing components section</a> of the <a href="http://jfx.wikia.com/wiki/Planet_JFX_Wiki">Planet JFX Wiki website</a> demonstrates (mostly the first two of) these tasks as it makes Swing's password field, progress bar, tabbed pane, tree, table, menu, and color chooser components accessible to JavaFX. However, it ignores the spinner component.</p>

<p>Introduced by Java 1.4, a <em>spinner</em> is a single-line input field that lets users select number/object values from an ordered sequence. This field is accompanied by a pair of small arrow buttons for stepping through the sequence's values. The spinner's editor can let the user type any value into the input field, but ignores the value if not in the sequence. Unlike combo boxes, spinners don't present drop-down lists.</p>

<p>Swing implements the spinner component via <code>javax.swing.JSpinner</code> and related model and editor classes. To construct a spinner with a specific model, invoke <code>JSpinner</code>'s <code>public JSpinner(SpinnerModel model)</code> constructor. For example, invoke this constructor with an instance of the <code>javax.swing.SpinnerListModel</code> class to create a spinner for stepping through a list of objects.</p>

<p>Listing 1 presents the source code to a <code>SwingSpinnerList</code> class that makes the combination of <code>JSpinner</code> and <code>SpinnerListModel</code> accessible to JavaFX.</p>

<p><strong>Listing 1:</strong> The <code>SwingSpinnerList</code> class</p>

<pre><div class="codeblock"><code>class SwingSpinnerList extends SwingComponent<br />{<br />&nbsp;&nbsp;&nbsp; var spinner: JSpinner;<br /><br />&nbsp;&nbsp;&nbsp; public var value: String = spinner.getValue () as String;<br /><br />&nbsp;&nbsp;&nbsp; public var items: Object[] on replace<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var model = new SpinnerListModel (items);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var listener = ChangeListener<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public override function stateChanged (ce: ChangeEvent)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value = model.getValue ().toString ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; model.addChangeListener (listener);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spinner.setModel (model);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (not (items [0] instanceof String))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var editor = spinner.getEditor () as JSpinner.DefaultEditor;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; editor.getTextField ().setEditable (false)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value = items [0].toString ()<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; public override function createJComponent ()<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spinner = new JSpinner (new SpinnerListModel ())<br />&nbsp;&nbsp;&nbsp; }<br />}</code></div></pre>

<p><code>SwingSpinnerList</code>'s <code>items</code> attribute identifies the objects managed by the spinner's model. Whenever you assign a new sequence of objects to this attribute, its replace trigger creates a new <code>SpinnerListModel</code> that stores these objects. Because the spinner's editor ignores all entered values for models that don't store <code>String</code>s, the trigger disables the editor unless <code>items</code>' element type is <code>String</code>.</p>

<p>The <code>value</code> attribute stores the selected item's string representation, and defaults to the single <code>&quot;empty&quot;</code> string literal that a spinner assigns to its model whenever a <code>SwingSpinnerList</code> is created without assigning a sequence to <code>items</code>. When this assignment occurs, however, <code>value</code> is initialized to the string representation of the first item in the sequence. It's also updated whenever the user selects a new item.</p>

<p>Listing 2 presents an excerpt from a script that creates five instances of <code>SwingSpinnerList</code>, and proves that you can assign different types of objects to its <code>items</code> attribute.</p>

<p><strong>Listing 2:</strong> <code>SwingSpinnerList</code> demo script excerpt</p>

<pre><div class="codeblock"><code>Stage<br />{<br />&nbsp;&nbsp;&nbsp; title: &quot;SwingSpinnerList Demo&quot;<br /><br />&nbsp;&nbsp;&nbsp; width: 250<br />&nbsp;&nbsp;&nbsp; height: 250<br /><br />&nbsp;&nbsp;&nbsp; scene: Scene<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content: VBox<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spacing: 5<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translateX: 30<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; translateY: 30<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HBox<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spacing: 5<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var spinnerRef: SwingSpinnerList<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SwingLabel<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text: &quot;Default:&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spinnerRef = SwingSpinnerList<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: 100<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SwingLabel<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text: bind spinnerRef.value<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: 50<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HBox<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spacing: 5<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var spinnerRef: SwingSpinnerList<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SwingLabel<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text: &quot;String:&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spinnerRef = SwingSpinnerList<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; items: [ &quot;first&quot;, &quot;second&quot;, &quot;third&quot; ]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: 100<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SwingLabel<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text: bind spinnerRef.value<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: 50<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HBox<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spacing: 5<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var spinnerRef: SwingSpinnerList<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SwingLabel<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text: &quot;Boolean:&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spinnerRef = SwingSpinnerList<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; items: [ true, false ]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: 100<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SwingLabel<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text: bind spinnerRef.value<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: 50<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HBox<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spacing: 5<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var spinnerRef: SwingSpinnerList<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SwingLabel<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text: &quot;Integer:&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spinnerRef = SwingSpinnerList<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; items: [ 1, 2, 3, 4 ]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: 50<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SwingLabel<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text: bind spinnerRef.value<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: 50<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HBox<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spacing: 5<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var spinnerRef: SwingSpinnerList<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; content:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SwingLabel<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text: &quot;Number:&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spinnerRef = SwingSpinnerList<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; items: [ 1.5, 2.5, 3.5 ]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: 100<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SwingLabel<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text: bind spinnerRef.value<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; width: 50<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp; }<br />}</code></div></pre>

<p>After compiling and running the demo script (see this post's code archive for the script's NetBeans project's <code>Main.fx</code> file), you should see a user interface similar to that shown in Figure 1.</p>

<p><a
href="http://www.javaworld.com/javaworld/jw-03-2009/images/csj32409.jpg"
><img
src="http://www.javaworld.com/javaworld/jw-03-2009/images/csj32409_thumb
.jpg" width="200" height="200" alt="" /></a></p>

<p><strong>Figure 1:</strong> Five instances of <code>SwingSpinnerList</code>. (Click to enlarge.)</p>

<p>As you've just seen, it's not hard to support a limited version of the spinner component. If you're up for a more challenging task, create a <code>SwingSpinner</code> class that generalizes <code>SwingSpinnerList</code> by supporting all of the spinner models. Furthermore, introduce support for specifying custom editors as JavaFX classes and associating a custom editor with a <code>SwingSpinner</code> instance.</p>

<p>Download a source file: <a
href="http://www.javaworld.com/javaworld/jw-03-2009/csj32409-src.zip">cs
j32409-src.zip</a></p>

<p>Like this blog? <a
href="http://www.javaworld.com/community/blog/14572/feed">Subscribe to
the CSJ Explorer RSS feed</a></p>    ]]></content>
  </entry>
  <entry>
    <title>Java SE 6&#039;s little AWT changes mean a lot</title>
    <link rel="alternate" type="text/html" href="http://www.javaworld.com/community/node/2627" />
    <id>http://www.javaworld.com/community/node/2627</id>
    <published>2009-03-17T21:12:09-04:00</published>
    <updated>2009-04-14T16:32:21-04:00</updated>
    <author>
      <name>javajeff</name>
    </author>
    <category term="AffineTransform" />
    <category term="AWT" />
    <category term="Java SE 6" />
    <summary type="html"><![CDATA[<!--paging_filter--><p>Java SE 6's official December 11, 2006 release and subsequent updates introduced many changes that impact client-side Java. Along with significant new APIs such as those for working with the desktop, splash screens, translucent and shaped windows, multiple gradient paints, and the system tray, Java SE 6 introduced many smaller client-oriented changes.</p>    ]]></summary>
    <content type="html"><![CDATA[<!--paging_filter--><p>Java SE 6's official December 11, 2006 release and subsequent updates introduced many changes that impact client-side Java. Along with significant new APIs such as those for working with the desktop, splash screens, translucent and shaped windows, multiple gradient paints, and the system tray, Java SE 6 introduced many smaller client-oriented changes.</p>

<p>After Java SE 6 debuted, I compiled a list of all the minor client-side Java changes that impact the various Abstract Window Toolkit and Swing packages. I present the AWT portion of that list in this post. (I'll present the Swing portion in a future post.) While exploring the following list of AWT changes, perhaps you'll come across a change that you haven't previously encountered:</p>

<ul>
<li><code>java.awt.AlphaComposite</code>: New <code>public AlphaComposite derive(float alpha)</code> and <code>public AlphaComposite derive(int rule)</code> methods.
</li>

<li>
<code>java.awt.Component</code>: New <code>public int getBaseline(int width, int height)</code> and <code>public Component.BaselineResizeBehavior getBaselineResizeBehavior()</code> methods, and a new <code>BaselineResizeBehavior</code> inner enumeration.
</li>

<li>
<code>java.awt.Dialog</code>: New <code>public static final Dialog.ModalityType DEFAULT_MODALITY_TYPE</code> constant, new <code>public Dialog(Window owner)</code>, <code>public Dialog(Window owner, Dialog.ModalityType modalityType)</code>, <code>public Dialog(Window owner, String title)</code>, <code>public Dialog(Window owner, String title, Dialog.ModalityType modalityType)</code>, and <code>public Dialog(Window owner, String title, Dialog.ModalityType modalityType, GraphicsConfiguration gc)</code> constructors, new <code>public Dialog.ModalityType getModalityType()</code> and <code>public void setModalityType(Dialog.ModalityType type)</code> methods, and new <code>ModalExclusionType</code> and <code>ModalityType</code> inner enumerations.
</li>

<li>
<code>java.awt.FlowLayout</code>: New <code>public boolean getAlignOnBaseline()</code> and <code>public void setAlignOnBaseline(boolean alignOnBaseline)</code> methods.
</li>

<li>
<code>java.awt.Font</code>: New <code>public static final String DIALOG</code>, <code>public static final String DIALOG_INPUT</code>, <code>public static final String MONOSPACED</code>, <code>public static final String SANS_SERIF</code>, and <code>public static final String SERIF</code> constants, a new <code>protected Font(Font font)</code> constructor, and a new <code>public boolean hasLayoutAttributes()</code> method.
</li>

<li>
<code>java.awt.FontMetrics</code>: New <code>public FontRenderContext getFontRenderContext()</code> method.
</li>

<li>
<code>java.awt.GraphicsEnvironment</code>: New <code>public boolean registerFont(Font font)</code> method.
</li>

<li>
<code>java.awt.GridBagConstraints</code>: New <code>public static final int ABOVE_BASELINE</code>, <code>public static final int ABOVE_BASELINE_LEADING</code>, <code>public static final int ABOVE_BASELINE_TRAILING</code>, <code>public static final int BASELINE</code>, <code>public static final int BASELINE_LEADING</code>, <code>public static final int BASELINE_TRAILING</code>, <code>public static final int BELOW_BASELINE</code>, <code>public static final int BELOW_BASELINE_LEADING</code>, and <code>public static final int BELOW_BASELINE_TRAILING</code> constants.
</li>

<li>
<code>java.awt.GridBagLayoutInfo</code>: New class.
</li>

<li>
<code>java.awt.RenderingHints</code>: New <code>public static final RenderingHints.Key KEY_TEXT_LCD_CONTRAST</code>, <code>public static final Object VALUE_TEXT_ANTIALIAS_GASP</code>, <code>public static final Object VALUE_TEXT_ANTIALIAS_LCD_HBGR</code>, <code>public static final Object VALUE_TEXT_ANTIALIAS_LCD_HRGB</code>, <code>public static final Object VALUE_TEXT_ANTIALIAS_LCD_VBGR</code>, and <code>public static final Object VALUE_TEXT_ANTIALIAS_LCD_VRGB</code> constants.
</li>

<li>
<code>java.awt.Toolkit</code>: New <code>protected abstract java.awt.peer.DesktopPeer createDesktopPeer(Desktop target)</code>, <code>public boolean isAlwaysOnTopSupported()</code>, <code>public abstract boolean isModalExclusionTypeSupported(Dialog.ModalExclusionType modalExclusionType)</code>, and <code>public abstract boolean isModalityTypeSupported(Dialog.ModalityType modalityType)</code> methods.
</li>

<li>
<code>java.awt.Window</code>: New <code>public List&lt;Image&gt; getIconImages()</code>, <code>public Dialog.ModalExclusionType getModalExclusionType()</code>, <code>public static Window[] getOwnerlessWindows()</code>, <code>public static Window[] getWindows()</code>, <code>public boolean isAlwaysOnTopSupported()</code>, <code>public void setBounds(int x, int y, int width, int height)</code>, <code>public void setBounds(Rectangle r)</code>, <code>public void setIconImage(Image image)</code>, <code>public void setIconImages(List&lt;? extends Image&gt; icons)</code>, <code>public void setMinimumSize(Dimension minimumSize)</code>, <code>public void setModalExclusionType(Dialog.ModalExclusionType exclusionType)</code>, <code>public void setSize(Dimension d)</code>, and <code>public void setSize(int width, int height)</code> methods.
</li>

<li>
<code>java.awt.event.MouseAdapter</code>: New <code>public void mouseDragged(MouseEvent e)</code>, <code>public void mouseMoved(MouseEvent e)</code>, and <code>public void mouseWheelMoved(MouseWheelEvent e)</code> methods.
</li>

<li>
<code>java.awt.event.MouseEvent</code>: New <code>public MouseEvent(Component source, int id, long when, int modifiers, int x, int y, int xAbs, int yAbs, int clickCount, boolean popupTrigger, int button)</code> constructor, and new <code>public Point getLocationOnScreen()</code>, <code>public int getXOnScreen()</code>, and <code>public int getYOnScreen()</code> methods.
</li>

<li>
<code>java.awt.event.MouseWheelEvent</code>: New <code>public MouseWheelEvent(Component source, int id, long when, int modifiers, int x, int y, int xAbs, int yAbs, int clickCount, boolean popupTrigger, int scrollType, int scrollAmount, int wheelRotation)</code> constructor.
</li>

<li>
<code>java.awt.font.FontRenderContext</code>: New <code>public FontRenderContext(AffineTransform tx, Object aaHint, Object fmHint)</code> constructor, and new <code>public Object getAntiAliasingHint()</code>, <code>public Object getFractionalMetricsHint()</code>, <code>public int getTransformType()</code>, and <code>public boolean isTransformed()</code> methods.
</li>

<li>
<code>java.awt.font.GraphicAttribute</code>: New <code>public Shape getOutline(AffineTransform tx)</code> method.
</li>

<li>
<code>java.awt.font.LayoutPath</code>: New class.
</li>

<li>
<code>java.awt.font.ShapeGraphicAttribute</code>: New <code>public Shape getOutline(AffineTransform tx)</code> method.
</li>

<li>
<code>java.awt.font.TextAttribute</code>: New <code>public static final TextAttribute KERNING</code>, <code>public static final Integer KERNING_ON</code>, <code>public static final TextAttribute LIGATURES</code>, <code>public static final Integer LIGATURES_ON</code>, <code>public static final TextAttribute TRACKING</code>, <code>public static final Float TRACKING_LOOSE</code>, and <code>public static final Float TRACKING_TIGHT</code> constants.
</li>

<li>
<code>java.awt.font.TextLayout</code>: New <code>public LayoutPath getLayoutPath()</code>, <code>public Rectangle getPixelBounds(FontRenderContext frc, float x, float y)</code>, and <code>public void hitToPoint(TextHitInfo hit, Point2D point)</code> methods.
</li>

<li>
<code>java.awt.font.TransformAttribute</code>: New <code>public static final TransformAttribute IDENTITY</code> constant, and newly overridden <code>public boolean equals(Object rhs)</code> and <code>public int hashCode()</code> methods.
</li>

<li>
<code>java.awt.geom.AffineTransform</code>: New <code>public static AffineTransform getQuadrantRotateInstance(int numquadrants)</code>, <code>public static AffineTransform getQuadrantRotateInstance(int numquadrants, double anchorx, double anchory)</code>, <code>public static AffineTransform getRotateInstance(double vecx, double vecy)</code>, <code>public static AffineTransform getRotateInstance(double vecx, double vecy, double anchorx, double anchory)</code>, <code>public void invert()</code>, <code>public void quadrantRotate(int numquadrants)</code>, <code>public void quadrantRotate(int numquadrants, double anchorx, double anchory)</code>, <code>public void rotate(double vecx, double vecy)</code>, <code>public void rotate(double vecx, double vecy, double anchorx, double anchory)</code>, <code>public void setToQuadrantRotation(int numquadrants)</code>, <code>public void setToQuadrantRotation(int numquadrants, double anchorx, double anchory)</code>, <code>public void setToRotation(double vecx, double vecy)</code>, and <code>public void setToRotation(double vecx, double vecy, double anchorx, double anchory)</code> methods.
</li>

<li>
<code>java.awt.geom.Arc2D</code>: Newly overridden <code>public boolean equals(Object obj)</code> and <code>public int hashCode()</code> methods.
</li>

<li>
<code>java.awt.geom.Ellipse2D</code>: Newly overridden <code>public boolean equals(Object obj)</code> and <code>public int hashCode()</code> methods.
</li>

<li>
<code>java.awt.geom.Path2D</code>: New class, with <code>Double</code> and <code>Float</code> inner classes.
</li>

<li>
<code>java.awt.geom.RoundRectangle2D</code>: Newly overridden <code>public boolean equals(Object obj)</code> and <code>public int hashCode()</code> methods.
</li>

<li>
<code>java.awt.image.BufferStrategy</code>: New <code>public void dispose()</code> method.
</li>

<li>
<code>java.awt.print.PrinterJob</code>: New <code>public PageFormat getPageFormat(PrintRequestAttributeSet attributes)</code> method.
</li>
</ul>

<p>Although these changes are documented in the JDK's Javadoc, you might wonder why they were added. For example, why did Sun Microsystems introduce a <code>setIconImage()</code> method to the <code>Window</code> class when this method (along with its companion <code>public Image getIconImage()</code> method) was already present in <code>Window</code>'s <code>Frame</code> subclass?</p>

<p>The <code>setIconImage()</code> method was added to <code>Window</code> so that the same icon image can be specified for an application's AWT-based/Swing-based frame and dialog windows -- on Windows platforms, the icon appears on the left side of the title bar. Prior to Java SE 6, you couldn't change a dialog's title bar icon, giving the impression that the dialog didn't belong to the application.</p>

<p>You can find reasons for various changes by exploring Sun's Bug database. For example, Bug 4980035 <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4980035">new methods on AffineTransform for rotation by a vector and by quadrants</a> discusses the need for <code>AffineTransform</code>'s new rotation methods.</p>

<p>When confronted with API changes, I like to create small demonstration programs that reinforce my understanding of these changes. For example, I created a <code>QuadRotate</code> applet that demonstrates <code>AffineTransform</code>'s <code>getQuadrantRotateInstance(int numquadrants, double anchorx, double anchory)</code> method. Check out Listing 1.</p>

<p><strong>Listing 1:</strong> <code>QuadRotate.java</code></p>

<pre><div class="codeblock"><code>// QuadRotate.java<br /><br />import java.awt.*;<br />import java.awt.event.*;<br />import java.awt.geom.*;<br /><br />import static java.lang.Math.*;<br /><br />public class QuadRotate extends java.applet.Applet<br />{<br />&nbsp;&nbsp; private Drawing d;<br /><br />&nbsp;&nbsp; public void init ()<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; d = new Drawing (getWidth ()-50, getHeight ()-50);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add (d);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Button b = new Button (&quot;Rotate&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b.addActionListener (new ActionListener ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void actionPerformed (ActionEvent ae)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; d.rotate ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add (b);<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; public void paint (Graphics g)<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (d != null)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; d.repaint ();<br />&nbsp;&nbsp; }<br />}<br /><br />class Drawing extends Panel<br />{<br />&nbsp;&nbsp; private AffineTransform at = new AffineTransform ();<br />&nbsp;&nbsp; private Dimension d;<br />&nbsp;&nbsp; private int numQuadsToRotate = 0;<br /><br />&nbsp;&nbsp; Drawing (int width, int height)<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; d = new Dimension (width, height);<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; public Dimension getPreferredSize ()<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return d;<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; public void paint (Graphics g)<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ((Graphics2D) g).setTransform (at);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Allocate storage for polygon coordinate arrays.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int [] x = new int [629];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int [] y = new int [629];<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Initialize arrays index.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int i = 0;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Obtain polar coordinates for cardioid curve. Convert these polar<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // coordinates to rectangular coordinates. Store the rectangular<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // coordinates in the polygon coordinate arrays.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (double angle = 0.0; angle &lt; 2*PI; angle += 0.01)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Evaluate cardioid curve equation. This produces radius for<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // given angle. Note: [r, angle] are the polar coordinates.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; double r = 30.0+30.0*cos (angle);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Convert polar coordinates to rectangular coordinates. Add half<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // the drawing&#039;s width and height to these coordinates, to move the<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // curve&#039;s origin to the center of the drawing panel. (The origin<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // defaults to the panel&#039;s upper-left corner.)<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x [i] = (int) (r*cos (angle))+d.width/2;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; y [i++] = (int) (r*sin (angle))+d.height/2;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Make sure the polygon is properly closed by assigning initial<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // rectangular coordinates as final rectangular coordinates.<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // CAUTION: Although it&#039;s okay to use == to compare the angle with<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 0.0 (because 0.0 can be finitely stored), it&#039;s generally not<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // okay to use == (and !=) to compare floating-point values.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (angle == 0.0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x [628] = x [0];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; y [628] = y [0];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Draw a red-filled polygon depicting the cardioid.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.setColor (Color.red);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.fillPolygon (x, y, x.length);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Draw the X and Y axes.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.setColor (Color.black);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.drawLine (0, d.height/2, d.width, d.height/2);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.drawLine (d.width/2, 0, d.width/2, d.height);<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; void rotate ()<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; at = AffineTransform.getQuadrantRotateInstance (++numQuadsToRotate,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; d.width/2, d.height/2);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; numQuadsToRotate %= 4;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; repaint ();<br />&nbsp;&nbsp; }<br />}</code></div></pre>

<p><code>QuadRotate.java</code> (which avoids using Swing classes because I'm only concentrating on AWT classes in this post) describes a user interface consisting of a drawing panel (which presents x-y axes and a mathematical curve known as a <em>cardioid</em>) and a <strong><code>Rotate</code></strong> button. Click the button to rotate the panel contents in 90-degree increments.</p>

<p>Each button click results in <code>Drawing</code>'s <code>rotate()</code> method invoking <code>getQuadrantRotateInstance(int numquadrants, double anchorx, double anchory)</code> -- a convenience method for <code>AffineTransform.getRotateInstance(numquadrants*Math.PI/2.0, anchorx, anchory)</code> -- to rotate the drawing around its center point by one, two, three, or four 90-degree arcs (quadrants).</p>

<p>Listing 2 presents a simple HTML file for describing this applet to <code>appletviewer</code> and Web browsers.</p>

<p><strong>Listing 2:</strong> <code>QuadRotate.html</code></p>

<pre><div class="codeblock"><code>&lt;applet code=QuadRotate.class width=300 height=300&gt;<br />&lt;/applet&gt;</code></div></pre>

<p>At the command line, execute the applet via <code>appletviewer quadrotate.html</code>. Figure 1 shows the unrotated drawing.</p>

<p><a
href="http://www.javaworld.com/javaworld/jw-03-2009/images/csj31709.jpg"
><img
src="http://www.javaworld.com/javaworld/jw-03-2009/images/csj31709_thumb.jpg" width="163" height="200" alt="" /></a></p>

<p><strong>Figure 1:</strong> The cardioid defaults to the right side of the drawing area. (Click to enlarge.)</p>

<p>After clicking the button once, the cardioid now appears at the bottom of the drawing area (see Figure 2).</p>

<p><a
href="http://www.javaworld.com/javaworld/jw-03-2009/images/csj31709_2.jpg"
><img
src="http://www.javaworld.com/javaworld/jw-03-2009/images/csj31709_2_thumb.jpg" width="163" height="200" alt="" /></a></p>

<p><strong>Figure 2:</strong> The rotation transform rotates user space in a clockwise direction. (Click to enlarge.)</p>

<p>The <code>getQuadrantRotateInstance()</code> method is one of the many conveniences offered by smaller AWT changes. Other little changes offer consistency (<code>Arc2D</code>'s, <code>Ellipse2D</code>'s, and <code>RoundRectange2D</code>'s overridden <code>equals()</code> and <code>hashCode()</code> methods, for example) and reliability (<code>BufferStrategy</code>'s <code>dispose()</code> method comes to mind). Which of the little AWT changes mean a lot to you?</p>

<p>Download a source file: <a href="http://www.javaworld.com/javaworld/jw-03-2009/csj31709-src.zip">csj31709-src.zip</a></p>

<p>Like this blog? <a
href="http://www.javaworld.com/community/blog/14572/feed">Subscribe to
the CSJ Explorer RSS feed</a></p>    ]]></content>
  </entry>
  <entry>
    <title>The Swing Cookbook</title>
    <link rel="alternate" type="text/html" href="http://www.javaworld.com/community/node/2589" />
    <id>http://www.javaworld.com/community/node/2589</id>
    <published>2009-03-10T15:26:28-04:00</published>
    <updated>2009-04-14T16:32:56-04:00</updated>
    <author>
      <name>javajeff</name>
    </author>
    <category term="cell" />
    <category term="combobox" />
    <category term="cookbook" />
    <category term="list" />
    <category term="rendering" />
    <category term="swing" />
    <summary type="html"><![CDATA[<!--paging_filter--><p>Ian Darwin's <a href="http://oreilly.com/catalog/9780596007010/index.html"><em>Java Cookbook</em></a> presents a collection of recipes for solving hundreds of problems that Java developers encounter. For example, in the first edition, one of Chapter 2's recipes discusses how to use the classpath effectively. To the best of my knowledge, O'Reilly hasn't published a similar cookbook that deals exclusively with Swing.</p>    ]]></summary>
    <content type="html"><![CDATA[<!--paging_filter--><p>Ian Darwin's <a href="http://oreilly.com/catalog/9780596007010/index.html"><em>Java Cookbook</em></a> presents a collection of recipes for solving hundreds of problems that Java developers encounter. For example, in the first edition, one of Chapter 2's recipes discusses how to use the classpath effectively. To the best of my knowledge, O'Reilly hasn't published a similar cookbook that deals exclusively with Swing.</p>

<p>A Swing-only cookbook might make Swing more accessible to those inexperienced with this toolkit. Perhaps it would help for Chapter 1's recipes to target the <a href="http://en.wikipedia.org/wiki/Swing_Application_Framework">Swing Application Framework</a>, which is intended to make Swing applications easier to write, and which should debut in Java SE 7. Later chapters could each target one or more Swing components.</p>

<p>For example, one chapter might present recipes for both the <code>JList</code> and <code>JComboBox</code> components. (Because of their similarity, grouping <code>JList</code> and <code>JComboBox</code> recipes in the same chapter seems logical.) Although some recipes would be <code>JList</code>-specific, and other recipes would target only <code>JComboBox</code>, still other recipes would focus on both components. Custom cell rendering is one example.</p>

<p><code>JList</code> and <code>JComboBox</code> each manage a list of items. They rely on a <em>cell renderer</em> component to render each item in a <em>cell</em>. Although the default cell renderer is adequate for most rendering tasks, scenarios exist where you'll want to install a custom cell renderer to handle a specific kind of rendering. For example, Figure 1 shows a custom cell renderer presenting a flag icon beside a country's name.</p>

<p><a href="http://www.javaworld.com/javaworld/jw-03-2009/images/csj31009.jpg"><img src="http://www.javaworld.com/javaworld/jw-03-2009/images/csj31009_thumb.jpg" width="350" height="97" alt="" /></a></p>

<p><strong>Figure 1:</strong> The flag icons are courtesy of British developer <a href="http://www.famfamfam.com/lab/icons/flags/">Mark James</a>. (Click to enlarge.)</p>

<p>Prior to rendering an item in a cell, <code>JList</code> and <code>JComboBox</code> obtain an item-specific rendering component by invoking the <code>ListCellRenderer</code> interface's <code>Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)</code> method. The returned component performs the rendering, taking into account the method's arguments:</p>

<ul>
<li>
<code>list</code> references the <code>JList</code> whose cell is to be rendered. (For a <code>JComboBox</code>, <code>list</code> references the <code>JList</code> part of this component.)
</li>

<li>
<code>value</code> references the object that is to be rendered in the cell.
</li>

<li>
<code>index</code> identifies <code>value</code>'s location in the list. In some cases, such as when focus shifts away from the list, <code>-1</code> is passed to <code>index</code>.
</li>

<li>
<code>isSelected</code> indicates whether the cell being rendered is currently selected (<code>true</code>) or not (<code>false</code>).
</li>

<li>
<code>cellHasFocus</code> indicates whether the cell being rendered has the input focus (<code>true</code>) or not (<code>false</code>). For <code>JComboBox</code>, this argument is always <code>false</code> because a combobox's text field, instead of the list, gets the focus.
</li>
</ul>

<p><code>JList</code> and <code>JComboBox</code> default their cell renderers to instances of the <code>DefaultListCellRenderer</code> class, which implements <code>ListCellRenderer</code>. These defaults can be replaced by invoking <code>JList</code>'s <code>void setCellRenderer(ListCellRenderer cellRenderer)</code> and <code>JComboBox</code>'s <code>void setRenderer(ListCellRenderer aRenderer)</code> methods.</p>

<p>Before invoking <code>setCellRenderer()</code> or <code>setRenderer()</code>, you need to declare and instantiate a class that implements <code>ListCellRenderer</code> and provides the rendering logic. The easiest way to handle the rendering in a look-and-feel independent manner is to subclass an existing component such as a <code>JLabel</code>. This is precisely what the custom cell renderer illustrated in Figure 1 and specified in Listing 1 accomplishes.</p>

<p><strong>Listing 1:</strong> <code>Countries.java</code></p>

<pre>
<div class="codeblock"><code>// Countries.java<br /><br />import java.awt.*;<br /><br />import java.net.URL;<br /><br />import java.util.*;<br /><br />import javax.swing.*;<br />import javax.swing.border.Border;<br /><br />public class Countries extends JFrame<br />{<br />&nbsp;&nbsp; Countries (String title)<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super (title);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setDefaultCloseOperation (EXIT_ON_CLOSE);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FlowLayout layout = new FlowLayout (FlowLayout.CENTER, 5, 5);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; layout.setAlignOnBaseline (true);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getContentPane ().setLayout (layout);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Country [] carray;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JList list = new JList (carray = createCountriesArray ());<br />//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; list.setEnabled (false);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; list.setCellRenderer (new CountryCellRenderer (null));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; list.setVisibleRowCount (8);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; list.setSelectedIndex (0);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getContentPane ().add (new JScrollPane (list));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JComboBox cb = new JComboBox (carray);<br />//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cb.setEnabled (false);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cb.setRenderer (new CountryCellRenderer (cb));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getContentPane ().add (cb);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pack ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setVisible (true);<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; Country [] createCountriesArray ()<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String [] citems =<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;AD,Andorra&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;AE,United Arab Emirates&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;AF,Afghanistan&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;AG,Antigua and Barbuda&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;AI,Anguilla&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;AL,Albania&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;AM,Armenia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;AN,Netherlands Antilles&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;AO,Angola&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;AR,Argentina&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;AS,American Samoa&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;AT,Austria&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;AU,Australia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;AW,Aruba&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;AX,Åland Islands&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;AZ,Azerbaijan&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BA,Bosnia and Herzegovina&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BB,Barbados&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BD,Bangladesh&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BE,Belgium&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BF,Burkina Faso&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BG,Bulgaria&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BH,Bahrain&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BI,Burundi&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BJ,Benin&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BM,Bermuda&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BN,Brunei Darussalam&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BO,Bolivia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BR,Brazil&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BS,Bahamas&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BT,Bhutan&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BV,Bouvet Island&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BW,Botswana&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BY,Belarus&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;BZ,Belize&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CA,Canada&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CC,Cocos (Keeling) Islands&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CD,Congo, the Democratic Republic of the&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CF,Central African Republic&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CG,Congo&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CH,Switzerland&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CI,Cote d&#039;Ivoire Côte d&#039;Ivoire&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CK,Cook Islands&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CL,Chile&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CM,Cameroon&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CN,China&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CO,Colombia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CR,Costa Rica&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CU,Cuba&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CV,Cape Verde&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CX,Christmas Island&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CY,Cyprus&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;CZ,Czech Republic&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;DE,Germany&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;DJ,Djibouti&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;DK,Denmark&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;DM,Dominica&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;DO,Dominican Republic&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;DZ,Algeria&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;EC,Ecuador&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;EE,Estonia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;EG,Egypt&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;EH,Western Sahara&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;ER,Eritrea&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;ES,Spain&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;ET,Ethiopia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;FI,Finland&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;FJ,Fiji&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;FK,Falkland Islands (Malvinas)&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;FM,Micronesia, Federated States of&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;FO,Faroe Islands&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;FR,France&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GA,Gabon&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GB,United Kingdom&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GD,Grenada&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GE,Georgia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GF,French Guiana&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GH,Ghana&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GI,Gibraltar&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GL,Greenland&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GM,Gambia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GN,Guinea&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GP,Guadeloupe&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GQ,Equatorial Guinea&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GR,Greece&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GS,South Georgia and the South Sandwich Islands&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GT,Guatemala&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GU,Guam&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GW,Guinea-Bissau&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;GY,Guyana&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;HK,Hong Kong&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;HM,Heard Island and McDonald Islands&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;HN,Honduras&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;HR,Croatia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;HT,Haiti&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;HU,Hungary&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;ID,Indonesia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;IE,Ireland&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;IL,Israel&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;IN,India&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;IO,British Indian Ocean Territory&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;IQ,Iraq&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;IR,Iran, Islamic Republic of&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;IS,Iceland&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;IT,Italy&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;JM,Jamaica&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;JO,Jordan&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;JP,Japan&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;KE,Kenya&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;KG,Kyrgyzstan&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;KH,Cambodia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;KI,Kiribati&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;KM,Comoros&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;KN,Saint Kitts and Nevis&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;KP,Korea, Democratic People&#039;s Republic of&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;KR,Korea, Republic of&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;KW,Kuwait&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;KY,Cayman Islands&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;KZ,Kazakhstan&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;LA,Lao People&#039;s Democratic Republic&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;LB,Lebanon&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;LC,Saint Lucia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;LI,Liechtenstein&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;LK,Sri Lanka&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;LR,Liberia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;LS,Lesotho&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;LT,Lithuania&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;LU,Luxembourg&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;LV,Latvia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;LY,Libyan Arab Jamahiriya&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MA,Morocco&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MC,Monaco&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MD,Moldova, Republic of&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;ME,Montenegro&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MG,Madagascar&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MH,Marshall Islands&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MK,Macedonia, the former Yugoslav Republic of&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;ML,Mali&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MM,Myanmar&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MN,Mongolia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MO,Macao&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MP,Northern Mariana Islands&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MQ,Martinique&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MR,Mauritania&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MS,Montserrat&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MT,Malta&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MU,Mauritius&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MV,Maldives&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MW,Malawi&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MX,Mexico&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MY,Malaysia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;MZ,Mozambique&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;NA,Namibia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;NC,New Caledonia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;NE,Niger&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;NF,Norfolk Island&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;NG,Nigeria&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;NI,Nicaragua&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;NL,Netherlands&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;NO,Norway&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;NP,Nepal&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;NR,Nauru&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;NU,Niue&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;NZ,New Zealand&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;OM,Oman&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;PA,Panama&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;PE,Peru&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;PF,French Polynesia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;PG,Papua New Guinea&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;PH,Philippines&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;PK,Pakistan&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;PL,Poland&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;PM,Saint Pierre and Miquelon&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;PN,Pitcairn&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;PR,Puerto Rico&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;PS,Palestinian Territory, Occupied&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;PT,Portugal&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;PW,Palau&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;PY,Paraguay&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;QA,Qatar&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;RE,Reunion Réunion&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;RO,Romania&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;RS,Serbia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;RU,Russian Federation&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;RW,Rwanda&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SA,Saudi Arabia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SB,Solomon Islands&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SC,Seychelles&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SD,Sudan&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SE,Sweden&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SG,Singapore&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SH,Saint Helena&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SI,Slovenia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SJ,Svalbard and Jan Mayen&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SK,Slovakia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SL,Sierra Leone&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SM,San Marino&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SN,Senegal&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SO,Somalia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SR,Suriname&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;ST,Sao Tome and Principe&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SV,El Salvador&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SY,Syrian Arab Republic&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;SZ,Swaziland&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;TC,Turks and Caicos Islands&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;TD,Chad&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;TF,French Southern Territories&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;TG,Togo&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;TH,Thailand&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;TJ,Tajikistan&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;TK,Tokelau&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;TL,Timor-Leste&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;TM,Turkmenistan&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;TN,Tunisia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;TO,Tonga&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;TR,Turkey&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;TT,Trinidad and Tobago&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;TV,Tuvalu&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;TW,Taiwan, Province of China&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;TZ,Tanzania, United Republic of&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;UA,Ukraine&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;UG,Uganda&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;UM,United States Minor Outlying Islands&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;US,United States&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;UY,Uruguay&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;UZ,Uzbekistan&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;VA,Holy See (Vatican City State)&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;VC,Saint Vincent and the Grenadines&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;VE,Venezuela&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;VG,Virgin Islands, British&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;VI,Virgin Islands, U.S.&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;VN,Viet Nam&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;VU,Vanuatu&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;WF,Wallis and Futuna&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;WS,Samoa&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;YE,Yemen&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;YT,Mayotte&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;ZA,South Africa&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;ZM,Zambia&quot;,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;ZW,Zimbabwe&quot;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ArrayList&lt;Country&gt; clist = new ArrayList&lt;Country&gt; ();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (String citem: citems)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String [] cdata = citem.split (&quot;,&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; clist.add (new Country (cdata [1],<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getClass ().getResource (&quot;icons/&quot;+<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cdata [0].toLowerCase ()+<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;.png&quot;)));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Country [] carray = clist.toArray (new Country [0]);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Arrays.sort (carray);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return carray;<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; public static void main (String [] args)<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Runnable r = new Runnable ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void run ()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Always create Swing UIs on event-dispatching<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // thread.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new Countries (&quot;Countries&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EventQueue.invokeLater (r);<br />&nbsp;&nbsp; }<br />}<br /><br />class Country implements Comparable&lt;Country&gt;<br />{<br />&nbsp;&nbsp; private String name;<br />&nbsp;&nbsp; private ImageIcon flagIcon;<br /><br />&nbsp;&nbsp; private URL path;<br /><br />&nbsp;&nbsp; Country (String name, URL path)<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.name = name;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.path = path;<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; String getName ()<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return name;<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; ImageIcon getFlagIcon ()<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Lazily load flag icon. Make sure that each country&#039;s flag icon is <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // loaded only once.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (flagIcon == null)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; flagIcon = new ImageIcon (path);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return flagIcon;<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; public int compareTo (Country o)<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return name.compareTo (o.name);<br />&nbsp;&nbsp; }<br />}<br /><br />class CountryCellRenderer extends JLabel implements ListCellRenderer<br />{<br />&nbsp;&nbsp; private Border border;<br /><br />&nbsp;&nbsp; private JComboBox cb;<br /><br />&nbsp;&nbsp; CountryCellRenderer (JComboBox cb)<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.cb = cb;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Leave a 10-pixel separator between the flag icon and country name.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setIconTextGap (10);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Swing labels default to being transparent; the container&#039;s color<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // shows through. To change a Swing label&#039;s background color, you must<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // first make the label opaque (by passing true to setOpaque()). Later,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // you invoke setBackground(), passing the new color as the argument.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setOpaque (true);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // This border is placed around a cell that is selected and has focus.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; border = BorderFactory.createLineBorder (Color.RED, 1);<br />&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp; public Component getListCellRendererComponent (JList list,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object value,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int index,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; boolean isSelected,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; boolean cellHasFocus)<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Country c = (Country) value;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setText (c.getName ());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setIcon (c.getFlagIcon ());<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (isSelected)<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setBackground (list.getSelectionBackground ());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setForeground (list.getSelectionForeground ());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setBackground (list.getBackground ());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setForeground (list.getForeground ());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setFont (list.getFont ());<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // list.isEnabled() always returns true for a combobox&#039;s list, even if<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // the combobox is disabled.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setEnabled (cb != null ? cb.isEnabled () : list.isEnabled ());<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (isSelected &amp;&amp; cellHasFocus) // cellHasFocus ignored for JComboBox<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setBorder (border);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setBorder (null);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this;<br />&nbsp;&nbsp; }<br />}</code></div>
</pre>

<p>Listing 1 is organized into <code>Countries</code>, <code>Country</code>, and <code>CountryCellRenderer</code> classes. The main <code>Countries</code> class creates the user interface and stores the URLs of various flag icons along with country names into an array of <code>Country</code> objects. Each of these objects is passed to <code>CountryCellRenderer</code>'s <code>getListCellRendererComponent()</code> method to obtain a <code>JLabel</code> that can render the object.</p>

<p>These classes offer a variety of techniques that are worth remembering for your own Swing applications and custom cell renderers:</p>

<ul>
<li>
<code>JFrame</code>'s content pane defaults to a <code>JPanel</code> managed by a <code>BorderLayout</code> layout manager. Rather than wrap a component in a <code>JPanel</code> and add that panel to the content pane's panel, to display the component at its preferred size, simply set the content pane's panel's layout manager to a <code>FlowLayout</code>, which makes every effort to display components at their preferred sizes. Wrapping a component in a <code>JPanel</code>, which is then added to the content pane's panel, also works (but results in unnecessary <code>JPanel</code> creation) because <code>JPanel</code>'s default layout manager is <code>FlowLayout</code>.
</li>

<li>
Java SE 6 introduced the <code>void setAlignOnBaseline(boolean alignOnBaseline)</code> method to <code>FlowLayout</code> to allow components to be vertically aligned according to their baselines (when             <code>true</code> is passed to <code>alignOnBaseline</code>). This method makes it possible to present the combobox near the top of its container instead of in the middle.
</li>

<li>
An instance of a custom cell renderer class can be shared among multiple <code>JList</code>s and <code>JComboBox</code>s provided that the class provides no instance-specific fields. For example, Listing 1's <code>CountryCellRenderer</code> class contains a <code>cb</code> field. This field is set to <code>null</code> if the custom cell renderer is assigned to a <code>JList</code>, or the <code>JComboBox</code> instance on which the custom cell renderer is being assigned. The <code>JComboBox</code> is needed to determine if this component is disabled, whereas the <code>list</code> reference passed to <code>getListCellRendererComponent()</code> is sufficient for determining whether a <code>JList</code> is disabled. Because the <code>cb</code> field has a dual role, Listing 1 requires two instances of <code>CountryCellRenderer</code>.
</li>

<li>
The <code>Country</code> class's <code>getFlagIcon()</code> method uses <code>ImageIcon</code> to load the flag icon. It lazily loads this icon when actually required, and also to ensure that the flag icon is loaded only once -- the custom cell renderer invokes <code>getFlagIcon()</code> multiple times for each country.
</li>

<li>
The <code>Country</code> class implements <code>Comparable&lt;T&gt;</code> to allow the array of <code>Country</code> objects (created in the <code>Countries</code> class's <code>createCountriesArray()</code> method) to be sorted (via <code>Arrays.sort()</code>) according to country name.
</li>

<li>
<code>DefaultListCellRenderer</code> supports setting a border for a focused <code>JList</code>'s selected item. <code>CountryCellRenderer</code>'s <code>getListCellRendererComponent()</code> method also sets a red border for the selected item of a focused <code>JList</code>, to show that this component is focused. Rather than create a border object each time <code>getListCellRendererComponent()</code> is invoked, which is inefficient, I create the border exactly once, in <code>CountryCellRenderer</code>'s constructor.
</li>

<li>
Although it's convenient for <code>CountryCellRenderer</code> to offload the rendering task to <code>JLabel</code>, which handles icon and text rendering, there is a problem with <code>JLabel</code> that must be overcome before it can be used to show a selected item. Specifically, <code>JLabel</code>'s opacity defaults to transparent, which allows the underlying container's background color to show through. As a result, a selection bar never appears. You can fix this problem by making <code>JLabel</code> opaque, which is accomplished in <code>CountryCellRenderer</code>'s constructor.
</li>

<li>
Finally, it's important to create as few objects as possible in <code>getListCellRendererComponent()</code> because this method is frequently called. Creating too many objects on the heap can lead to garbage collections pauses that can disrupt the smooth flow of program execution. Fortunately, <code>getListCellRendererComponent()</code> creates no objects (unless I'm mistaken). Furthermore, it reuses the same <code>JLabel</code> object.
</li>
</ul>

<p>After compiling <code>Countries.java</code>, it's easy to package this application into an executable JAR file by first creating a manifest file that contains <code>Main-Class: Countries</code> and (assuming the manifest file is named <code>manifest.mf</code>) executing <code>jar cfm Countries.jar manifest.mf *.class icons</code>. You then specify <code>java -jar Countries.jar</code> to execute this application.</p>

<p>With a bit of work, the previous cell-rendering material could be turned into a recipe in the hypothetical Swing cookbook's chapter on <code>JList</code> and <code>JComboBox</code>. (Recipes on rendering table and tree cells could appear in chapters that deal with <code>JTable</code> and <code>JTree</code>.) However, is creating a Swing cookbook a good idea, especially in light of JavaFX, which simplifies user interface development?</p>

<p>Download a source file: <a href="http://www.javaworld.com/javaworld/jw-03-2009/csj31009-src.zip">csj31009-src.zip</a></p>

<p>Like this blog? <a
href="http://www.javaworld.com/community/blog/14572/feed">Subscribe to
the CSJ Explorer RSS feed</a></p>    ]]></content>
  </entry>
  <entry>
    <title>Invoking JavaFX functions that don&#039;t exist</title>
    <link rel="alternate" type="text/html" href="http://www.javaworld.com/community/node/2551" />
    <id>http://www.javaworld.com/community/node/2551</id>
    <published>2009-03-03T14:25:57-05:00</published>
    <updated>2009-03-03T17:35:53-05:00</updated>
    <author>
      <name>javajeff</name>
    </author>
    <category term="functions" />
    <category term="javafx" />
    <summary type="html"><![CDATA[<p>What happens when you try to execute the following Java code fragment?</p>
<pre>
<div class="codeblock"><code>String s = null;<br />System.out.println (s.length ());</code></div>
</pre><p>
It's not hard to figure out that a <code>NullPointerException</code> object is thrown. You cannot invoke a method via the null reference.</p>
<p>However, you can get away with something similar in JavaFX Script, which the following code fragment demonstrates:</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>What happens when you try to execute the following Java code fragment?</p>
<pre>
<div class="codeblock"><code>String s = null;<br />System.out.println (s.length ());</code></div>
</pre><p>
It's not hard to figure out that a <code>NullPointerException</code> object is thrown. You cannot invoke a method via the null reference.</p>
<p>However, you can get away with something similar in JavaFX Script, which the following code fragment demonstrates:</p>
<pre>
<div class="codeblock"><code>var x: function (): Void;<br />println (x); // Output: null<br />x ();<br />x = null;<br />x ();<br />x = function (): Void { println (&quot;x() invoked&quot;) }<br />x () // Output: x() invoked</code></div>
</pre><p>
The code fragment initially creates variable <code>x</code> of type <code>function (): Void</code>. Furthermore, this variable is initialized to null, which the subsequent <code>println (x);</code> expression proves.</p>
<p>Notice the first <code>x ();</code> invocation. You might expect that the JavaFX runtime would throw a <code>NullPointerException</code>, but this doesn't happen. In fact, the invocation is ignored. </p>
<p>To make this more explicit, the code fragment assigns <code>null</code> to <code>x</code> and once more executes <code>x ();</code>. Same result.</p>
<p>Finally, the code fragment assigns an anonymous function to <code>x</code> and executes <code>x ()</code>, which results in the function being invoked and outputting <code>x() invoked</code>.</p>
<p>This "ignore a function invocation when the function variable contains null" behavior is present in JavaFX 1.0 and 1.1, and appears to be a useful feature for making code more robust. However, it can also mask bugs when a function must be executed but never is because its function variable contains null.</p>
<p>Do you think that a <code>NullPointerException</code> should be thrown in this situation, or is the ignore behavior okay with you?</p>
    ]]></content>
  </entry>
</feed>
