<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" 
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
    xmlns:admin="http://webns.net/mvcb/"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
	<channel>
<title>Bug&#x27;s Blog</title><link>http://www.blogofbug.com/index.html</link><description>The Blog of Bug</description><dc:language>en</dc:language><dc:creator>bug.face.uk@mac.com</dc:creator><dc:rights>Copyright 2006 Bug</dc:rights><dc:date>2007-03-25T00:45:05+00:00</dc:date><admin:generatorAgent rdf:resource="http://www.realmacsoftware.com/" />
<admin:errorReportsTo rdf:resource="mailto:bug.face.uk@mac.com" /><sy:updatePeriod>hourly</sy:updatePeriod>
<sy:updateFrequency>1</sy:updateFrequency>
<sy:updateBase>2000-01-01T12:00+00:00</sy:updateBase>
<lastBuildDate>Sun, 25 Mar 2007 00:53:03 +0000</lastBuildDate><item><title>Switching Blog tool... Moving Links</title><dc:creator>bug.face.uk@mac.com</dc:creator><dc:subject>Bug&#x27;s Blog</dc:subject><dc:date>2007-03-25T00:45:05+00:00</dc:date><link>http://www.blogofbug.com/files/06a5f6827655bc51a2bfc5c39f862249-33.html#unique-entry-id-33</link><guid isPermaLink="true">http://www.blogofbug.com/files/06a5f6827655bc51a2bfc5c39f862249-33.html#unique-entry-id-33</guid><content:encoded><![CDATA[I've just switched to Word Press to run the blog (Rapid Weaver just makes it too slow to make small changes, and I can't use it while I'm traveling). As a result although I'm fixing up the old pages to re-direct correctly, you will certainly need to update your RSS feeds to point at: <br /><br />http://www.blogofbug.com/feed/<br /><br />Otherwise, just use the http:www.blogofbug.com URL and you'll find everything pretty much as it was. <br /><br />Thanks for your patience and making the site busy enough to need it!]]></content:encoded></item><item><title>Introducing JMultiButton</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2007-03-23T21:18:26+00:00</dc:date><link>http://www.blogofbug.com/files/Introducting_JMultiButton.html#unique-entry-id-32</link><guid isPermaLink="true">http://www.blogofbug.com/files/Introducting_JMultiButton.html#unique-entry-id-32</guid><content:encoded><![CDATA[So this isn't a very "Rich" component yet, but I've been working on a little helper app for myself and I've always needed one of these. It's basically a button that allows you to add multiple actions which can be selected from or the left side clicked to fire the current action. It saves space on a tool-bar. Although I've uploaded it to the cvs repository there is much more work to do, but it is useful right now, which is good!<br /><p style="text-align:center;" ><br /><img class="imageStyle" alt="multi_button" src="http://www.blogofbug.com/files//page0_blog_entry32_1.png"width="635" height="479"/><br /><br />Basically all the component does is extend JButton, over-riding a few key methods. The first thing it does is add something to hold the multiple actions, and identify the selected action. <br /><br />It then adds a certain amount on to the returned minimum size etc to ensure it has enough space to draw the down arrow on the right (which allows the multiple selection), and of course overrides the paint method to draw the triangle (this needs some real work and cross L&F testing right now) and a line between the triangle and the action when the mouse is hovering. <br /><br />That line tells the user that clicking on the left will have a different effect to clicking on the right. In order to deliver that we simple check to see if they should fire the current action (on the left) or show a menu of all of the actions (on the right). <br /><br />    /**<br />     * Overridden so that a check can be made to see if the current action should be fired, or the <br />     * list of available actions should be shown.<br />     * @param actionEvent The action event<br />     */<br />    protected void fireActionPerformed(ActionEvent actionEvent) {<br />        if (this.getMousePosition().x>boundary){<br />            JPopupMenu popup = new JPopupMenu();<br />            for (Action action : actions){<br />                popup.add(new ActionOptionWrapper(action));<br />            }<br />            popup.show(this,0,getHeight());<br />        } else {<br />            super.fireActionPerformed(actionEvent);<br />        }<br />    } <code></code><br />And that's it. The ActionOptionWrapper just sets the selected action as the current action, and then fires it. Simple really! Any how, I'll develop this one and I do have "Rich" plans for it, but given this one can be useful out the box, and I haven't had a chance to do cross platform testing (hint hint). <br /></p>]]></content:encoded></item><item><title>Updated Demo&#x2c; JavaDEV Project </title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2007-03-21T07:35:57+00:00</dc:date><link>http://www.blogofbug.com/files/1c0e3ddc61ddad80bbd84a183ef6eb0d-31.html#unique-entry-id-31</link><guid isPermaLink="true">http://www.blogofbug.com/files/1c0e3ddc61ddad80bbd84a183ef6eb0d-31.html#unique-entry-id-31</guid><content:encoded><![CDATA[I've broken out the examples from the library, because they really don't have any place in the library. I'm looking into how I can make the demo source neatly available in an easy to keep up to date way (anyone know of a good "convert source tree to html" tool, please let me know). <br /><br />For those of you keeping track of the CVS, I've added a couple of new effects, including a neat one to get at a ListCell which you can now see updated in the <a href="(null)/(null)" rel="self" title="Demonstrations">demo</a>. You'll also notice I've taken some of the older links out, they were just cluttering up the demo now, so I don't think they are needed anymore.<br /><br />I've also taken out the need for the demo to use signed jars which simplifies things nicely. So, just a little house work and an update to the demo.<br /><br /><strong>Update<br /></strong>OK, that was easier than I thought! You can <a href="(null)/(null)" rel="self" title="Demo Source Code">view the demo source here</a>.<br /><br /><br /><br />]]></content:encoded></item><item><title>Sun&#x2c; are you paying attention?</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2007-03-20T15:17:05+00:00</dc:date><link>http://www.blogofbug.com/files/cf4dee155ff6154828c31eb96b84c645-30.html#unique-entry-id-30</link><guid isPermaLink="true">http://www.blogofbug.com/files/cf4dee155ff6154828c31eb96b84c645-30.html#unique-entry-id-30</guid><content:encoded><![CDATA[JSmooth has just released what I consider to be the "important" missing part of Java on windows.... a way to launch the a jar, and if Java isn't installed to go get it and install it. That's all that's needed. If the download could be made smaller and modularized... fine... but the ground zero has to be a simple exe that can fire up the JRE whether it's there or not. <br /><br />Well done to the JSmooth developers. <br /><br /><a href="http://jsmooth.sourceforge.net/index.php" rel="self">http://jsmooth.sourceforge.net/index.php</a><br /><br />]]></content:encoded></item><item><title>Introducing... the EffectsPanel class&#x21;</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2007-03-18T13:39:22+00:00</dc:date><link>http://www.blogofbug.com/files/Introducing_the_EffectsPanel_class.html#unique-entry-id-29</link><guid isPermaLink="true">http://www.blogofbug.com/files/Introducing_the_EffectsPanel_class.html#unique-entry-id-29</guid><content:encoded><![CDATA[Now that I've documented and fixed up what was there, I've started moving a few of the new things I've been building into the library, and I wanted to start with the EffectPanel. <br /><p style="text-align:center;" ><br /><img class="imageStyle" alt="EffectsPanel" src="http://www.blogofbug.com/files//page0_blog_entry29_1.png"width="599" height="400"/><br />Effects Panel<br /><br /></p><p><strong>Update<br /></strong>Fixed a bug on Windows that made the effects demo, well, dog ugly! Also updated the acknowledgments to use some of the new effects (subtle but not un-purty). Also updated the src.zip, web-start demo, and the CVS is of course up-to-date. <br /><br />Quite often you want to overlay (or indeed... underlay) effects over the top (or underneath!) of a normal swing window. You've probably seen examples of these before, from shadowing you can find in the SwingX library (I think that's where I saw it) to icon "blooms" as the mouse moves over a button (something Krill's added to substance) to transitions. They are all great, but I wanted a way to build a re-usable library of effects so that they could be easily added to any application without too much re-jigging. <br /><br />This is where the EffectsPanel comes in, it is a Component that can be set as the glass pane (as it is in my example) and you can add as many effects to it as you like. It manages the timing and repainting of, well Effects. The EffectsPanel class itself is really not very exciting, create on, set it as the GlassPane and then addEffects. Here's the code and then we'll move onto the Effects themselves.<br /><br />        EffectsPanel effectsPanel = new EffectsPanel();<br />        setGlassPane(effectsPanel);<br />        effectsPanel.setVisible(true);<br />        ...<br />        effectsPanel.addEffect(new SomeEffect());<br /><br />That's it. What's much more interesting is the Effect interface.<br /><br /><strong>Special Effects<br /></strong>The Effect interface has three methods of interest, let's start with the most simple.<br /><br />    public void paintEffect(Graphics2D graphics);<br /><br />The implementation of this method is given a graphics object and the effect paints itself on. That's it, of course we would like our effects to change over time so there is an update method that needs implementing as well. <br /><br />    public long  update();<br /><br />That looks pretty simple too huh? Well it is, when called by an EffectPanel it's a signal that any animation, movement, pre-calcs should be done. The return value is important as well, if it's a number above zero it will be called again after that number of intervals (how often update is polled is controlled by the update panel). There are also a couple of special values you can return specified in the interface. <br /><br />EFFECT_FINISHED<br /><br />Return this, and the effect will be removed from the EffectsPanel, effectively finishing it, and then rather interestingly...<br /><br />EFFECT_INACTIVE<br /><br />Return this and the EffectsPanel will stop calling update, which means that the effect will still be painted, but no further calls to update will be made. <br /><br />Now we can animate, paint our animations, really that's all that is needed, except there is one more method that you'll see used in a later example. <br /><br />public boolean  isLocalEffect();<br /><br />Basically a local effect is supplied it's own Graphics object meaning that you can go crazy with transforms, composites etc, and the next effect along will be un-affected, a non-local effect will impact every other that is drawn. <br /><br />So all you have to do to add an effect is create an EffectsPanel and implement some effects. It's that easy. <br /><br />In the example I put in the web-start demo has three effects, one is a does the Krill "Alpha Burst" thing when a mouse moves over a component (using a mouseListener to check when that happens). Here's the class, you'll love how short it is!<br /><br />public class AlphaBurst extends AbstractComponentEffect{<br />    protected int   width = 0;<br />    protected int   height = 0;<br />    protected float alpha   = 1.0f;<br />    <br />    /** Creates a new instance of AlphaBurst */<br />    public AlphaBurst(Component c) {<br />        super(c);<br />        width = c.getWidth();<br />        height = c.getHeight();<br />    }<br /><br /><br />    public void paintEffect(Graphics2D graphics) {<br />        graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,alpha));<br />        graphics.drawImage(componentImage,centre.x-width/2,centre.y-height/2,width,height,null);<br />    }<br /><br />    public long update() {<br />        alpha=alpha-(alpha/8.0f);<br />        width=(int) ((float)width*1.2f);<br />        height=(int) ((float)height*1.2f);<br />        <br />        if (alpha>0.1){<br />            return 20;<br />        } else {<br />            return Effect.EFFECT_FINISHED;<br />        }<br />    }<br />    <br />}<br /><br />That's it, it renders the component into an image, and then increases the width and height it's drawn at reducing the alpha. when the alpha goes below 10% it ends the animation. That's it. Cute. <br /><br />If you tick the "Mouse Trails" button it will also add some particle effects, each time a mouse moved or mouse dragged event is received it randomly adds a particle. The particle effect is also pretty simple. It's just a dot that updates (with a minor nod towards physics) and bounces if it goes below the bottom third of the screen. Here it is<br /><br />public class ParticleEffect implements Effect{<br />    protected BufferedImage   particle    = null;<br />    protected double          x = 0.0;<br />    protected double          y = 0.0;<br />    protected double          vx = 0.0;<br />    protected double          vy = 0.0;<br />    protected int             life = 0;<br />    protected float           alpha = 1.0f;<br />    protected double          floor = 1000000.0f;<br />    <br />    /** <br />     * Creates a new instance of ParticleEffect <br />     *<br />     */<br />    public ParticleEffect(BufferedImage particle, int x, int y,int age, int floor) {<br />        this.particle = particle;<br />        this.x = (double) x;<br />        this.y = (double) y;<br />        this.vx = 4.0-Math.random()*8.0;<br />        this.vy = 6.0-Math.random()*8.0;<br />        this.life = age;<br />        this.floor = (double) floor;<br />    }<br /><br />    public boolean isLocalEffect() {<br />        return true;<br />    }<br /><br />    public void paintEffect(Graphics2D graphics) {<br />        if (alpha<1.0f){<br />            float finalAlpha = alpha;<br />            <br />            if (graphics.getComposite() instanceof AlphaComposite){<br />                AlphaComposite  alphaComp = (AlphaComposite) graphics.getComposite();<br />                finalAlpha = alphaComp.getAlpha() * alpha;<br />            }<br />            <br />            graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,finalAlpha));<br />        }<br />        graphics.drawImage(particle,(int)x, (int)y,null);<br />    }<br /><br />    public long update() {<br />        life--;<br />        if (life<=0){<br />            return Effect.EFFECT_FINISHED;<br />        }<br />        if ((y>floor) && (vy>0)) {<br />            vy=0.0-vy*0.5;<br />        }<br />        x+=vx;<br />        y+=vy;<br />        vy+=0.1;<br />        if (life<10){<br />            alpha = (float) life / 10.0f;<br />        }<br />        return 20;<br />    }<br />    <br />}<br /><br />Not rocket science as you can see. But we can do some really cute things, what if we want our particles to be reflected into that bottom third of the screen? Well how about we create an Effect, that reflects other Effects? Tick the Reflect check box to make it happen, but here's the Reflect Effect class....<br /><br />public class ReflectEffect implements Effect{<br />    protected   Effect          reflectThis;<br />    protected   EffectsPanel    thePanel;<br />    <br />    /** Creates a new instance of ReflectEffect */<br />    public ReflectEffect(Effect reflectThis, EffectsPanel panel) {<br />        this.reflectThis = reflectThis;<br />        this.thePanel = panel;<br />    }<br /><br />    public boolean isLocalEffect() {<br />        return true;<br />    }<br /><br />    public void paintEffect(Graphics2D graphics) {<br />        reflectThis.paintEffect(graphics);<br />        graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.5f));<br />        graphics.translate(0,thePanel.getHeight());<br />        graphics.scale(1.0,-0.3);<br />        reflectThis.paintEffect(graphics);<br />    }<br /><br />    public long update() {<br />        return reflectThis.update();<br />    }<br />    <br />}<br /><br />This effect takes another effect as a parameter, passes on it's update calls to the sub-summed effect and then in paint, does something clever. First it asks the effect to paint itself normally before applying a couple of transforms to the graphics context, and calling it again making it paint itself upside and squashed. Easy-peasy, why not have a play with the web-start demo or check out (no pun) the code from CVS in the <a href="https://swing-bug.dev.java.net/" rel="self">JavaDEV</a> project.<br /><br />To see the new Effects run "Effects - Alpha Burst" <br /></p><p style="text-align:center;" ><br /><a href="assets/blogofbug.jnlp" rel="self"><img class="imageStyle" alt="webstart-small2" src="http://www.blogofbug.com/files//page0_blog_entry29_2.gif"width="88" height="23"/></a><br /></p>]]></content:encoded></item><item><title>Changes to Carousel API commited to JavaDEV Project</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2007-03-16T21:32:10+00:00</dc:date><link>http://www.blogofbug.com/files/Changes_to_Carousel_API_commited_to_JavaDEV_Project.html#unique-entry-id-28</link><guid isPermaLink="true">http://www.blogofbug.com/files/Changes_to_Carousel_API_commited_to_JavaDEV_Project.html#unique-entry-id-28</guid><content:encoded><![CDATA[Just a note to those who are keeping up to date, I've started delivering API changes (and some improved <a href="http://www.blogofbug.com/swing-bug/javadoc/index.html" rel="self">documentation</a>) to the java dev project. I'm not going to dilly-dally with the methods I have deprecated so if you are using the library, please quickly update your code before you get a nasty shock! On the plus side, it's already getting easier to use the carousel as a result. More changes to come.<br /><br /><strong>Update</strong><br />JavaDocs are now complete (see link to documentation above). Interestingly after writing them there are a few "TODO"s that I've spotted, but I think we have a reasonable base to start adding in some of the new things I've been working on over the last month or so. ]]></content:encoded></item><item><title>What&#x27;s Happened to Paul Thurrott...?</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Tech</category><dc:date>2007-03-13T20:19:32+00:00</dc:date><link>http://www.blogofbug.com/files/Whats_happened_to_paul_thurrott.html#unique-entry-id-27</link><guid isPermaLink="true">http://www.blogofbug.com/files/Whats_happened_to_paul_thurrott.html#unique-entry-id-27</guid><content:encoded><![CDATA[As most of you will have figured out, I'm a Mac user. I love my Macs. They Rock, Go Mac! However, I'm not blind to the allure of Windows and its benefits or the developments that have been achieved with Vista. I've always used Paul Thurrott as my "Windows Guide" letting me know what's hot, and what's not, with an objective perspective on Windows development in the broader market place. <br /><br />However, just <a href="http://www.internet-nexus.com/" rel="self">recently</a> it seems to have turned into a crusade against Mac's and Linux. For me whether or not he has a point is lost in the "oh oh I have something negative to report above non Windows platforms". Perhaps he's trying to provide some balance (us Mac users love our rumours, and as the under dogs, we're always striving to see the good news), but for me he's ceased to be useful as a source of information and valuable perspective. <br /><br />I've no interest in putting the man down, his blog, his right to say what he wants. I guess I just feel it's a shame. Come on Paul, you're better than this. ]]></content:encoded></item><item><title>Java Image Filters</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2007-03-03T23:28:31+00:00</dc:date><link>http://www.blogofbug.com/files/Java_Image_Filters.html#unique-entry-id-26</link><guid isPermaLink="true">http://www.blogofbug.com/files/Java_Image_Filters.html#unique-entry-id-26</guid><content:encoded><![CDATA[Every so often you come across a site where someone has done loads of the things you are always meaning to get around to, and today was one of those days. Whilst writing an application to manipulate multiple images in a batch mode (I'll make it available, it's certainly been useful for me!). I wanted to add a couple more image operations to it before sharing it with you guys, and I stumbled across this site <a href="http://www.jhlabs.com/ip/filters/index.html" rel="self">jhlabs</a>. <br /><br /><img class="imageStyle" alt="java_image_filters" src="http://www.blogofbug.com/files//page0_blog_entry26_1.png"width="725" height="547"/><br /><br />Believe it or not, the list goes on! The filters are available as a jar and the source under the Apache 2.0 license, and are a real resource. <br /><br />For now I'll get back to documenting!]]></content:encoded></item><item><title>Java Dev Project up and running</title><dc:creator>bug.face.uk@mac.com</dc:creator><dc:subject>Bug&#x27;s Blog</dc:subject><dc:date>2007-03-03T07:48:40+00:00</dc:date><link>http://www.blogofbug.com/files/Java_Dev_Project_up_and_running.html#unique-entry-id-25</link><guid isPermaLink="true">http://www.blogofbug.com/files/Java_Dev_Project_up_and_running.html#unique-entry-id-25</guid><content:encoded><![CDATA[Want to keep up-to-date with the blog code, or perhaps even contribute? The java dev project has been approved and you can now get cvs updates and even register as a contributor. As I've mentioned in earlier blogs I'm going to be doing some tidying work first before adding "more" stuff on top of the code base and I'm starting work this weekend. I have a few ideas for what's coming next as well, but I'll keep those to myself for the moment. <br /><br />For now, here's the link: <a href="https://swing-bug.dev.java.net/" rel="self">https://swing-bug.dev.java.net/</a><br /><br />]]></content:encoded></item><item><title>What happens next...?</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2007-02-18T22:53:31+00:00</dc:date><link>http://www.blogofbug.com/files/58335ddf4222838d749804fa28d498ac-24.html#unique-entry-id-24</link><guid isPermaLink="true">http://www.blogofbug.com/files/58335ddf4222838d749804fa28d498ac-24.html#unique-entry-id-24</guid><content:encoded><![CDATA[As I've hinted in one of the comments on a recent blog, I've been setting up a dev.java.net project for the blog code. It's proved more work than I expected, largely because I've had to set up one thing or another (CVS repositories, last time I did anything like this by had was writing sccs bash scripts to make it easy to manage a group project!). All that aside, once the project has been approved I'll post the link here. <br /><br />I'm also going on a bit of a document and re-factor effort to try and get things in shape as I am getting more requests to use the components (open-source dudes, do what you like!) I feel I should at least make them more usable and, well, better documented. In the meantime I'm (as usual) fiddling with one or two ideas that will hopefully mature enough to be added into the library. ]]></content:encoded></item><item><title>First real application of java carousel</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2007-02-14T21:13:15+00:00</dc:date><link>http://www.blogofbug.com/files/First_real_application_of_java_carousel.html#unique-entry-id-23</link><guid isPermaLink="true">http://www.blogofbug.com/files/First_real_application_of_java_carousel.html#unique-entry-id-23</guid><content:encoded><![CDATA[I've mentioned  a couple of times that I've  spent a few days working with Krill on some bits and bobs for substance, and I have to say it's great to see the carousel (and for me the carousel menu) having a real (and useful) application. So head on over to <a href="http://weblogs.java.net/blog/kirillcool/archive/2007/02/spicing_up_your_8.html" rel="self">Krill's blog</a> and check out what he has done... <br /><br /><p style="text-align:center;" ><img class="imageStyle" alt="KrillPreview" src="http://www.blogofbug.com/files//page0_blog_entry23_1.png"width="565" height="450"/><br />Sweet :-)</p>]]></content:encoded></item><item><title>Improved Dock Panel and Coder Stupidity</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2007-02-11T21:22:03+00:00</dc:date><link>http://www.blogofbug.com/files/Improved_Dock_Panel_and_Coder_Stupidity.html#unique-entry-id-22</link><guid isPermaLink="true">http://www.blogofbug.com/files/Improved_Dock_Panel_and_Coder_Stupidity.html#unique-entry-id-22</guid><content:encoded><![CDATA[When I first publish my dock example, it was implemented in a glass pane. Which is fine. I also went through some hoops with AWT listeners to stop it sucking all the events away from the component below. I wrote some event forwarders. I added listeners. I did a lot of stuff. The whole time something was niggling away at the back of my mind. It just shouldn't be this hard. It wasn't. <br /><br />There is a magical method for each container, that can cause it to NOT hog mouse events. It simply dictates whether or not there is a component at a particular x,y co-ordinate. If there's not, Swing doesn't pass the mouse event on. After all, there's nothing there. Here it is (you can over-ride it)<br /><br />public boolean contains(int x, int y);<br /><br />That's it. If it returns false, then no events are passed on and the clicks fall through. I had started fiddling with this, but then stopped hitting a recursive dead end when I called getComponentAt(int x,int y) inside it. Of course, all I should have done is a little bit of work to implement my own getComponentAt() and I would have had my solution. Find out if there is something in the dock panel that isn't a spacer that's at that co-ordinate... if there's not return false and pretend it's got nothing to do with me.<br /><br />Here's the implementation:<br /><br />    public boolean contains(int x, int y) {<br />        Rectangle rect=new Rectangle();<br />        //Have to implement our own componentAt here<br />        for (Component comp : getComponents()){<br />            rect=comp.getBounds(rect);<br />            if (rect.contains(x,y)){<br />                if (comp instanceof JSpacer){<br />                    mouseMoved(new Point(-1,-1));<br />                    return false;<br />                }<br />                return true;<br />            }<br />        }<br />        //A little non-intuitive. If it's not over a normal component or a spacer, it's in the dock area<br />        return true;<br />    }    <br /><br />The final return true is a little bit tricky (it's after all like saying, I didn't find anything but yes send me the message). This stops the mouse going over little gaps causing a problem, in general, if it's over a spacer pretend I'm not there, otherwise consume the event. Doing this enabled me to remove the GlassPaneDock class entirely, and a WHOLE load of complexity went with it. It's also enabled me to do something else I wanted to, add the dock into a Frame's layered pane. This is useful because it means I can make things like combo-box drop downs and pop-up menus appear over it (play with the demo to make it happen if you wish). Which might be more appropriate in some cases. In the screen shot below see how the combo-box appears OVER the dock.<br /><br /><p style="text-align:center;" ><img class="imageStyle" alt="Layered Dock Pane" src="http://www.blogofbug.com/files//page0_blog_entry22_1.png"width="399" height="400"/><br /><br />This "perfect" message handling has one problem, the mouse can still fall between the cracks of dock icons from time to time, but that's part of another tweak I'm working towards. For now, the usual web-start and source code update has been done!<br /><br /><a href="assets/blogofbug.jnlp" rel="self"><img class="imageStyle" alt="webstart-small2" src="http://www.blogofbug.com/files//page0_blog_entry22_2.gif"width="88" height="23"/></a></p>]]></content:encoded></item><item><title>Updates to JCarouselMenu and some bug fixes...</title><dc:creator>bug.face.uk@mac.com</dc:creator><dc:subject>Bug&#x27;s Blog</dc:subject><dc:date>2007-02-11T02:35:49+00:00</dc:date><link>http://www.blogofbug.com/files/Updates_to_JCarouselMenu_and_some_bug_fixes.html#unique-entry-id-21</link><guid isPermaLink="true">http://www.blogofbug.com/files/Updates_to_JCarouselMenu_and_some_bug_fixes.html#unique-entry-id-21</guid><content:encoded><![CDATA[It's been an interesting 48 hours, with Krill using the Java Carousel, and then starting to use the Carousel Menu he spotted quite a few more items that needed tidying. With those changes applied, I went back to my inbox to find someone else spotting the same problem (Sebastien) with the carousel menu needing to be able to handle more items than could fit on the screen. <br /><br /><p style="text-align:center;" ><img class="imageStyle" alt="scrolling menu" src="http://www.blogofbug.com/files//page0_blog_entry21_1.png"width="600" height="400"/><br /><br />I've also added Mouse Wheel support to the carousel, which is pretty funky (well I think so!). Another big thanks to Krill and Sebastien, it's all looking better than it was.<br /><br /><a href="assets/blogofbug.jnlp" rel="self"><img class="imageStyle" alt="webstart-small2" src="http://www.blogofbug.com/files//page0_blog_entry21_2.gif"width="88" height="23"/></a></p>]]></content:encoded></item><item><title>Updating Blog Jar and New Features to Carousel</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2007-02-09T22:35:42+00:00</dc:date><link>http://www.blogofbug.com/files/Updating_Blog_Jar_and_New_Features_to_Carousel.html#unique-entry-id-20</link><guid isPermaLink="true">http://www.blogofbug.com/files/Updating_Blog_Jar_and_New_Features_to_Carousel.html#unique-entry-id-20</guid><content:encoded><![CDATA[What happens when bloggers team up? Interesting things!!! <a href="http://weblogs.java.net/blog/kirillcool/" rel="self" title="Krill's Blog">Kirill Grouchnikov</a> recently asked if he could use JCarousel in one of his projects, and of course I was delighted to agree. During the day Krill would send me some updates, suggest improvements, and the end result is subtly but substantially improved.<br /><br />Quite a few changes have been made here and there, one of my favourites was Krill's idea to add mouse wheel functionality, now why didn't I think of that? All very simple in the code as well....<br /><br />public void mouseWheelMoved(MouseWheelEvent mouseWheelEvent) {<br />    <br />    if (mouseWheelEvent.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {<br />        int amount = mouseWheelEvent.getWheelRotation();<br />        if (lastWheeledTo==null){<br />            lastWheeledTo=getFrontmost();<br />        }<br />        int lastPosition = layout.getComponentIndex(lastWheeledTo);<br />        int frontMostPosition = layout.getComponentIndex(getComponent(0));<br />        //Don't over spin<br />        if (Math.abs(lastPosition-frontMostPosition)>layout.getComponentCount()/4){<br />            return;<br />        }<br />        if (amount > 0) {<br />            lastWheeledTo=layout.getPreviousComponent(lastWheeledTo);<br />        } else {<br />            lastWheeledTo=layout.getNextComponent(lastWheeledTo);<br />        }<br />        bringToFront(lastWheeledTo,true);<br />    }        <br />}<br /><br />Basically all the code does is look for the mouse wheel to be rolled and try and bring the next component along to the front. I bet your are wondering about the over-spin bit? Well, the carousel always looks for the shortest route to be bringing a component to the front, so racing too far ahead suddenly spins it backwards, not what I wanted, but if we never let it go further than a quarter of the way around, it's always quickest to carry on moving in the same direction.<br /><br />The next item, and I think you'll see it to great effect in Krill's application, is to improve how text is rendered and labels generated. Nice alpha blended backdrop in a rounded rectangle. Once again, there were a few e-mails backwards and forwards before we hit on the right solution. Fun!<br /><br /><p style="text-align:center;" ><img class="imageStyle" alt="Updated Dock zoomed" src="http://www.blogofbug.com/files//page0_blog_entry20_1.png"width="171" height="240"/><br /></p><p><br />Finally (and what started it all) items further back are now more transparent (something I know a few of you had raised) not to mention a couple of bad practice items Krill had highlighted (laziness on my part, but when you realize someone else is doing things with your code!)<br /><br /></p><p style="text-align:center;" ><img class="imageStyle" alt="Updated Dock" src="http://www.blogofbug.com/files//page0_blog_entry20_2.png"width="600" height="400"/><br /><br /></p><p>I have updated the source, as well the web-start demo....<br /><br /></p><p style="text-align:center;" ><a href="assets/blogofbug.jnlp" rel="self"><img class="imageStyle" alt="webstart-small2" src="http://www.blogofbug.com/files//page0_blog_entry20_3.gif"width="88" height="23"/></a><br /></p>]]></content:encoded></item><item><title>Updating source code and licensing</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2007-01-20T13:37:47+00:00</dc:date><link>http://www.blogofbug.com/files/Updating_Source_Code_and_Licensing.html#unique-entry-id-19</link><guid isPermaLink="true">http://www.blogofbug.com/files/Updating_Source_Code_and_Licensing.html#unique-entry-id-19</guid><content:encoded><![CDATA[Well I'm back, and I'm very jet-lagged! As part of staying awake I've decided to release the source code under the Apache 2.0 license meaning you can all get it into which-ever projects you like pretty easily. Look in the downloads section for the updated Netbeans project including all of the latest content. You'll also need to download and link against the Cascade.jar or delete the references to the Demo file if you get linking errors, it's up to you!<br /><br />I've also tweaked the carousel example following a suggestion from Romain Guy (his site has moved btw, follow updated link) that the jittering some people experienced is due to single pixel increments in size, so I've made it only size to even pixels and we'll see if that helps (Webstart updated). <br /><br />Other bits of house keeping have also been done to help people find their way around the site, so I hope it's a little bit clearer now. <br /><br />Finally, thanks so much for all of the messages (and comments) with encouragement and ideas. Every single one has been valuable and greatly appreciated. ]]></content:encoded></item><item><title>Carousel Menu (a la Apple TV)</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2007-01-16T13:22:32-08:00</dc:date><link>http://www.blogofbug.com/files/Carousel_Menu_a_la_Apple_TV.html#unique-entry-id-18</link><guid isPermaLink="true">http://www.blogofbug.com/files/Carousel_Menu_a_la_Apple_TV.html#unique-entry-id-18</guid><content:encoded><![CDATA[While everyone was wrapped up in Steve's reality distortion field during his recent keynote, I was ogling the Apple TV interface. What a fantastic product (I'm getting one of those, just what I need). Anyhow, one of the interface features that I noticed was that they had re-used a carousel to produce a funky menu. Well, I have a carousel component how hard can it be to re-use it to create.... a carousel menu? <br /><br /><p style="text-align:center;" ><img class="imageStyle" alt="CarouselMenu" src="http://www.blogofbug.com/files//page0_blog_entry18_1.png"width="300" height="200"/><br /><br />Starting off, let's list out the tasks ahead of us:<br /><br />1. We need to do a small amount of re-factoring on the carousel layout to allow us to sub-class it and over-ride a couple of pieces of functionality. <br />2. We need a new component that will contain a carousel, and a menu (I'm going to use a JList, it does the job!)<br />3. The menu needs a custom cell renderer to make the selected cell look pretty<br /><br />The re-factoring was pretty minimal, but I did add a couple of methods, just before I start that, I need to mention one of the existing methods which needed to be over-ridden:<br /><br /><strong>public void setFrontMostComponent(Component component)<br /></strong>Previously this would spin the specified component to the FRONT of the carousel. This time, I wanted it to be on the right of the carousel, so the new method (in the rather imaginatively named OffsetCarouselLayout class). This function just determines the target angle differently. <br /><br /><strong>protected boolean shouldHide(Component comp, double angle)<br /></strong>One of the problems with the over-riding of the above function is that you can have component in front of the currently focused one. This new function is used to determine if a component should be hidden (based on where it is on the carousel). It lets me stop things appearing in front of the current component. <br /><br />The next two are pretty obvious, they let me manipulate the center of the carousel (in the component) and the radius of the carousel (I didn't want it quite as squashed). <br /><br />That's it, not bad, gotta-love OO programming!<br /><br />So, let's summarize what we have; a single component with a JCarousel component (with a new layout manager), and a JList in it, pretty straight forward....<br /><br /><img class="imageStyle" alt="JCarouselMenu Structure" src="http://www.blogofbug.com/files//page0_blog_entry18_2.png"width="621" height="530"/><br /></p><p>As you can imagine, Java makes most of this pretty easy. But there are a couple more steps that you'll find in the source code. <br /><br />It turns out, JLists don't like wrapping the selected object when the user is using the keys to cycle through (it gets stuck at the top or the bottom). So you'll see in the code of the Carousel Menu that it has a keyboard listener to move the cursor to the top or the bottom when it hits one of the ends. Also we need to update which component is at the "front" of the carousel based on which item is selected, and a simple implementation of the list selection listener achieves that. <br /><br />And of course, we need a nice border around the selected item. I've cheated a little and created a simple border called "ImageBorder" which takes a bounded image (like the one shown below) and a set of insets, and slices and dices it to make it resize-able. <br /><br /></p><p style="text-align:center;" ><img class="imageStyle" alt="menu_highlight" src="http://www.blogofbug.com/files//page0_blog_entry18_3.png"width="220" height="54"/><br /><br />I've added the code for this below, and of course, once I'm back from my trip I'll update my project file so that you can all download the source. And rather wonderfully, that's it. Once again, Java can really deliver on a modern UI and I think you'll agree it makes my blog examples a little more usable! <br /><br /><a href="assets/blogofbug.jnlp" rel="self"><img class="imageStyle" alt="webstart-small2" src="http://www.blogofbug.com/files//page0_blog_entry18_4.gif"width="88" height="23"/></a></p>]]></content:encoded></item><item><title>Java Dock Component</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2007-01-11T19:42:28-08:00</dc:date><link>http://www.blogofbug.com/files/Java_Dock_Component.html#unique-entry-id-17</link><guid isPermaLink="true">http://www.blogofbug.com/files/Java_Dock_Component.html#unique-entry-id-17</guid><content:encoded><![CDATA[As promised I've done some work on an OS X a-like Dock component for Swing. I've implemented a couple of different uses for it, a Glass Pane version that displays the dock over a window, but also kept it in a separate component (which could be useful for a funky tab-like-pane). After spending several evenings tweaking it, I threw it all away for a much simpler solution using GridBagLayout, for more details, read on.<br /><br /><p style="text-align:center;" ><img class="imageStyle" alt="Picture 1" src="http://www.blogofbug.com/files//page0_blog_entry17_1.png"width="465" height="323"/><br /></p><p><br />The final implementation essentially uses a GridBagLayout to control the dock, with spacers "pushing" the icons in the dock into the center. The mouse is tracked, and when it moves over one of the Components (you can just add any Java component into there) its preferred size is changed (therefore increasing the size). Of course, like everything it's not QUITE that simple. The diagram below shows the basic strategy. <br /></p><p style="text-align:center;" ><br /><img class="imageStyle" alt="dock panel" src="http://www.blogofbug.com/files//page0_blog_entry17_2.png"width="350" height="351"/><br /><br /></p><p>I'll update the source code for the Blog project when I get back home after my trip, but I'd like to focus on a couple of the interesting problems that needed solving. <br /><br /><strong>Scaling The Icons Based on Distance From Pointer<br /></strong>The DockPanel sets the preferred size of the icons to control their "zooming" in the dock. It's implemented in a method called. This is implemented in the getComponentPreferedSize method. First it checks to see if the mouse is actually hovering over the given icon, if it is, it returns the enlarged size, otherwise it looks for the distance from the mouse pointer to the center of the icon, and linearly scales. The method is protected so it could be over-ridden by a subsequent implementation. I point this out because I'm pretty sure Apple are using a Sigma curve or the like scale theirs as the distance increases. I'll leave it to you to try it yourself!<br /><br /><strong>Watching for Change</strong><br />Another over-used class of mine that I've added to the Blog project is a MouseTracker which does a couple of things, including tracking a mouse inside a container, filtering out a lot of the events, and just passing interesting ones across (when I publish the source you'll see). One of the MouseTrackerListener call-backs is mouseMoved, and it's here that I update the parameters on the GridBagLayout to make any changes. There's one other place that the same thing is done. As usual we don't want everything flicking from small to giant etc so I have a timer that updates a tweened animation between the target size for the icons in the dock, and the size they currently are. Consequently actionPerformed also updates the layout.<br /><br /><strong>Easy Animation</strong><br />I've been pretty lazy here. If you look at the updateLayout method (which updates all the grid bag constraints) you'll see that the result of getComponentPreferredSize is passed into the tweenValue method. This is where the animation is done. The tweenValue takes a current size, and a target size and calculates a value between the two sizes. If the two aren't already the same it lets the animation timer know that the size being passed on isn't the final size, and then returns an intermediate size. Finally, it does a little bit of "clipping" to make sure that the icons can always fit in the space available. <br /><br /><strong>Docking to the Different Sides<br /></strong>The code looks pretty lengthy, but a lot is taken up by dealing with the various layout wrinkles that come along with aligning the dock to different sides, so as you read through, just pick a compass point and worry about that. <br /><br />Now speaking of the source code here it is, you can launch the web-start demo (it creates an AWT has been updated with a dock tab too to keep you going until all the source is available. <br /><br /><strong>Note: </strong>I have fixed the problem that stopped the web-start working (it uses an AWTEventListener which requires ALL permissions, so the jar needed to be signed). <br /><br /></p><p style="text-align:center;" ><a href="assets/blogofbug.jnlp" rel="self"><img class="imageStyle" alt="webstart-small2" src="http://www.blogofbug.com/files//page0_blog_entry17_3.gif"width="88" height="23"/></a><br /><br /></p><p><strong>Dock Panel Source Code<br /><br />/*<br /> * DockPanel.java<br /> *<br /> * Created on January 9, 2007, 6:22 AM<br /> *<br /> * Re-implementation of the dock to use a grid-bag-laid-out panel instead<br /> * of the very complicated code I was developing.<br /> *<br /> */<br /><br />package com.blogofbug.swing.components;<br /><br />import com.blogofbug.swing.delegates.MouseTracker;<br />import com.blogofbug.swing.delegates.MouseTrackerListener;<br />import java.awt.Color;<br />import java.awt.Component;<br />import java.awt.Container;<br />import java.awt.Dimension;<br />import java.awt.Graphics;<br />import java.awt.GridBagConstraints;<br />import java.awt.GridBagLayout;<br />import java.awt.Point;<br />import java.awt.Rectangle;<br />import java.awt.event.ActionEvent;<br />import java.awt.event.ActionListener;<br />import java.awt.event.MouseEvent;<br />import java.awt.event.MouseListener;<br />import java.awt.event.MouseMotionListener;<br />import java.util.Hashtable;<br />import javax.swing.JComponent;<br />import javax.swing.JLabel;<br />import javax.swing.JPanel;<br />import javax.swing.SwingUtilities;<br />import javax.swing.Timer;<br />import java.awt.Insets;<br /><br />/**<br /> *<br /> * @author nigel<br /> */<br />public class DockPanel extends JPanel implements MouseTrackerListener, ActionListener{<br />    /**<br />     * Enumeration object to contain the side of the screen/window the dock is against<br />     *<br />     */<br />    public enum       Side {NORTH, SOUTH, EAST, WEST};<br />    <br />    //The enlarged size of the icons<br />    private int       enlargedSize = 96;<br />    <br />    //The normal size of the icons<br />    private int       normalSize   = 32;<br />    <br />    //Insets around each dock icon<br />    private int       insets       = 2;<br />    <br />    //The side the dock is docked to<br />    private Side      dockedTo     = Side.SOUTH;<br />    <br />    //Autohide?<br />    private boolean   autoHide     = false;<br />    <br />    //Auto-hiding!<br />    private boolean   hiding       = false;<br />    <br />    //Layout and standard constraints<br />    private GridBagLayout       layout = new GridBagLayout();<br />    private GridBagConstraints  filler = new GridBagConstraints();<br />    private GridBagConstraints  title  = new GridBagConstraints();<br />    private GridBagConstraints  icon   = new GridBagConstraints();<br />    <br />    //Spacer Panels<br />    protected JSpacer              spacer; ;<br />    protected JSpacer              firstSpacer; ;<br />    protected JSpacer              lastSpacer;<br />    <br />    //Hold index of components and their titles<br />    private Hashtable<Component,DockLabel>   iconLabels = new Hashtable<Component,DockLabel>();<br />    <br />    //Track mouse movements (will be over the entire component)<br />    private MouseTracker        mouseTracker;<br />    <br />    //Animation timer<br />    private Timer               timer = new Timer(0,this);<br />    private boolean             animating = false;<br />    <br />    //Mouse location (last known)<br />    private Point            lastKnownMouse = new Point(0,0);<br />    <br />    /** Creates a new instance of DockPanel */<br />    public DockPanel(int normalSize, int enlargedSize, Side dockedTo) {<br />        //Set up the basics<br />        this.dockedTo = dockedTo;<br />        this.enlargedSize = enlargedSize;<br />        this.normalSize = normalSize;<br />        <br />        //Prepare the timer<br />        timer.setCoalesce(true);<br />        timer.setDelay(20);<br />        timer.setRepeats(true);<br />        timer.start();<br />        <br />        //make me transparent<br />        setBackground(null);<br />        setOpaque(false);<br />        <br />        //Set up the mouse tracker<br />        mouseTracker = new MouseTracker(this,this);<br />        <br />        //Set up the spacers<br />        spacer = new JSpacer();<br />        firstSpacer = new JSpacer();<br />        lastSpacer = new JSpacer();<br />        <br />        //Initial setup<br />        setLayout(layout);<br />        initializeLayout();<br />        add(spacer,filler);<br />        add(firstSpacer,icon);<br />        add(lastSpacer,icon);<br />        <br />        //Perform initial layout<br />        updateLayout(null);<br />    }<br />    <br />    public boolean isAutoHiding(){<br />        return autoHide;<br />    }<br />    <br />    public void setAutoHiding(boolean autoHide){<br />        this.autoHide = autoHide;<br />        mouseMoved(lastKnownMouse);<br />    }<br />    <br />    public DockPanel(int normalSize, int enlargedSize){<br />        this(normalSize,enlargedSize,Side.SOUTH);<br />    }<br />    <br />    public void setNormalSize(int normalSize){<br />        this.normalSize = normalSize;<br />        updateLayout(null);<br />        layout.layoutContainer(this);<br />    }<br />    <br />    public void setEnlargedSize(int enlargedSize){<br />        this.enlargedSize = enlargedSize;<br />        updateLayout(null);<br />        layout.layoutContainer(this);<br />    }<br />    <br />    /**<br />     * Sets up the basic layout and then calls updateLayout to finalize the<br />     * individual dock elements<br />     */<br />    private void initializeLayout(){<br />        //Standard settings for the filler panel<br />        filler.fill = GridBagConstraints.BOTH;<br />        filler.weightx = 1.0;<br />        filler.weighty = 1.0;<br />        <br />        //Standard settings for the title components<br />        title.anchor = GridBagConstraints.CENTER;<br />        <br />        //Standard settings for the icon components<br />        icon.anchor = GridBagConstraints.CENTER;<br />        icon.weightx = 0.0;<br />        icon.weighty = 0.0;<br />        icon.insets = new Insets(insets/2,insets/2,insets/2,insets/2);<br />        <br />        //Do various bits of specific constraints layout<br />        switch (dockedTo){<br />            case SOUTH:<br />                filler.gridx = 0;<br />                filler.gridy = 0;<br />                filler.gridheight =1;<br />                filler.gridwidth = GridBagConstraints.REMAINDER;<br />                title.gridx  = 0;<br />                title.gridy = 1;<br />                icon.gridx = 0;<br />                icon.gridy = 2;<br />                icon.anchor = GridBagConstraints.SOUTH;<br />                break;<br />            case NORTH:<br />                filler.gridx = 0;<br />                filler.gridy = 2;<br />                filler.gridheight =1;<br />                filler.gridwidth = GridBagConstraints.REMAINDER;<br />                title.gridx  = 0;<br />                title.gridy = 1;<br />                icon.gridx = 0;<br />                icon.gridy = 0;<br />                icon.anchor = GridBagConstraints.NORTH;<br />                break;<br />            case WEST:<br />                filler.gridx = 2;<br />                filler.gridy = 0;<br />                filler.gridheight =GridBagConstraints.REMAINDER;<br />                filler.gridwidth = 1;<br />                title.gridx  = 1;<br />                title.gridy = 0;              <br />                icon.gridx = 0;<br />                icon.gridy = 0;<br />                icon.anchor = GridBagConstraints.WEST;<br />                break;<br />            case EAST:<br />                filler.gridx = 0;<br />                filler.gridy = 0;<br />                filler.gridheight =GridBagConstraints.REMAINDER;<br />                filler.gridwidth = 1;<br />                title.gridx  = 1;<br />                title.gridy = 0;<br />                icon.gridx = 2;<br />                icon.gridy = 0;<br />                icon.anchor = GridBagConstraints.EAST;<br />                break;<br />        }<br />    }<br />    <br />    /**<br />     * Changes the side of the panel the dock is attached to<br />     */<br />    public void setDockedTo(DockPanel.Side dockedTo) {<br />        this.dockedTo = dockedTo;<br />        initializeLayout();<br />        layout.setConstraints(spacer,filler);<br />        updateLayout(null);<br />        layout.layoutContainer(this);<br />    }<br />    <br />    /**<br />     * Determines the appropriate gridbag weight for the "book-end" spacers<br />     * at either end of the dock<br />     *<br />     * @return The weight<br />     */<br />    private double spacerWeightX(){<br />        switch (dockedTo){<br />            case NORTH:<br />            case SOUTH:<br />                return 1.0;<br />            case EAST:<br />            case WEST:<br />                return 0.0;<br />        }<br />        return 0.0;<br />    }<br />    <br />    /**<br />     * Determines the appropriate gridbag weight for the "book-end" spacers<br />     * at either end of the dock<br />     *<br />     * @return The weight<br />     */<br />    private double spacerWeightY(){<br />        return 1.0-spacerWeightX();<br />    }<br />    <br />    /**<br />     * Determines the appropriate size for the component based on the current "mouse over"<br />     * component, the location in the dock area relative to the overall dock panel, and a given component<br />     * This is protected so it can be over-ridden to apply a better alogrithm .<br />     *<br />     * @param p The location of the mouse relative to the overall dock<br />     * @param compoment The component to be sized<br />     * @param highlightedComponent The current component with the mouse over it<br />     * @return A Dimension object with the recommened size of the component<br />     */<br />    protected Dimension getComponentPreferedSize(Point p, Component component, Component highlightedComponent){<br />        //If we are hiding it should be small<br />        if (hiding){<br />            return new Dimension(insets*3,insets*3);<br />        }<br />        <br />        //determine it's distance based on half the size of the biggest component<br />        double compSize = component.getSize().height;<br />        double distanceFromCenter = 0;<br />        double delta = (double) (enlargedSize-normalSize);<br />        <br />        if ((highlightedComponent!=null)){<br />            if (highlightedComponent==component){<br />                return new Dimension(enlargedSize,enlargedSize);<br />            }<br />            switch (dockedTo){<br />                case NORTH:<br />                case SOUTH:<br />                    distanceFromCenter = Math.abs((double) ((component.getLocation().x + compSize/2 )- p.x));<br />                    break;<br />                case EAST:<br />                case WEST:<br />                    distanceFromCenter = Math.abs((double) ((component.getLocation().y + compSize/2 )- p.y));<br />                    break;<br />            }<br />            double cdy = 1.0 - distanceFromCenter/(double) (enlargedSize*2);<br />            double tSize = Math.max((double) normalSize + cdy * delta,(double) normalSize);<br />            return new Dimension((int)tSize,(int) tSize);<br />        } else {<br />            return new Dimension(normalSize,normalSize);<br />        }<br />    }<br />    <br />    /**<br />     * Essentially removes the spacer that forces the dock to the bottom of the<br />     * panel, neat if you would like to use the panel as something like a<br />     * special tab top.<br />     *<br />     * @param fillSpace true if the panel should fill up any empty space above the dock,<br />     * false if it should not<br />     */<br />    public void fillSpace(boolean fillSpace){<br />        spacer.setVisible(fillSpace);<br />    }<br />    <br />    /**<br />     * Determines a value between the current and target including easing. Can<br />     * be over-ridden if desired to have different sizing behavior. Implementers <br />     * should note that if the size is not clipped to ensure that the icons will<br />     * all fit in the dock, gridbag does Bad Things (tm). <br />     *<br />     * @param current The current value<br />     * @param target  The target value<br />     * @return A value equal to or between target and current<br />     */<br />    protected double tweenValue(double current, double target){<br />        //Just make sure we do nothing if they are the same<br />        if (current==target){<br />            return target;<br />        }<br />        <br />        //Determine the difference<br />        double delta = (target-current)/4.0;<br />        <br />        //Tween in the right direction, but always by at least one pixel<br />        if (delta>0){<br />            delta = Math.max(1.0,delta);<br />        } else if (delta<0){<br />            delta = Math.min(-1.0,delta);<br />        }<br />        <br />        switch (dockedTo){<br />            case SOUTH:<br />            case NORTH:<br />                return Math.min(current+delta,( getWidth()-insets*iconLabels.size()*2)/iconLabels.size()-10);<br />            default:<br />                return Math.min(current+delta,(getHeight()-insets*iconLabels.size()*2)/iconLabels.size());<br />        }<br />    }<br />    <br />    /**<br />     * Determines if a component is in the dock<br />     *<br />     */<br />    public boolean dockContains(Component component){<br />        if (iconLabels.get(component)!=null){<br />            return true;<br />        }<br />        return false;<br />    }<br />    <br />    /**<br />     * Takes a current size, and determines a new size setting up an animation<br />     * if needed.<br />     *<br />     * @param currentSize The current size of the component<br />     * @param newSize     The new size of the component<br />     * @return The in-between size<br />     */<br />    private Dimension tweenSize(Dimension currentSize, Dimension newSize){<br />        if ((newSize.width != currentSize.width) || (newSize.height != currentSize.height)){<br />            animating = true;<br />            newSize.width = (int) tweenValue(currentSize.width,newSize.width);<br />            newSize.height = (int) tweenValue(currentSize.height,newSize.height);<br />        }<br />        return newSize;<br />    }<br />    <br />    /**<br />     * Determines the new settings for the gridbag layout, and then applies them<br />     * to all components<br />     *<br />     */<br />    private void updateLayout(Point p){<br />        Component highlightedComponent = null;<br />        animating = false;<br />        if (p!=null){<br />            highlightedComponent = getComponentAt(p);<br />            //Make sure it's at least in the dock zone<br />            switch (dockedTo){<br />                case NORTH:<br />                    if (p.y>spacer.getY()){<br />                        highlightedComponent=null;<br />                    }<br />                    break;<br />                case SOUTH:<br />                    if (p.y<spacer.getHeight()){<br />                        highlightedComponent=null;<br />                    }<br />                    break;<br />                case WEST:<br />                    if (p.x>spacer.getX()){<br />                        highlightedComponent=null;<br />                    }<br />                    break;<br />                case EAST:<br />                    if (p.x<spacer.getWidth()){<br />                        highlightedComponent=null;<br />                    }<br />                    break;<br />            }<br />            if ((highlightedComponent==firstSpacer) || (highlightedComponent==lastSpacer)){<br />                highlightedComponent = null;<br />            }<br />        }<br />        //Variables used for incrementing grid-x's and grid-y's<br />        int dx=0,dy=0;<br />        switch (dockedTo){<br />            case NORTH:<br />            case SOUTH:<br />                dx=1;<br />                break;<br />            case EAST:<br />            case WEST:<br />                dy=1;<br />                break;<br />        }<br />        <br />        //Set everything up as it should be<br />        initializeLayout();<br />        <br />        //Add first spacer and move to next position<br />        icon.weightx = spacerWeightX();<br />        icon.weighty = spacerWeightY();<br />        icon.fill = GridBagConstraints.BOTH;<br />        layout.setConstraints(firstSpacer,icon);<br />        icon.fill = GridBagConstraints.NONE;<br />        icon.gridx += dx;<br />        icon.gridy += dy;<br />        title.gridx += dx;<br />        title.gridy += dy;<br />        <br />        //Iterate through the components updating everything<br />        Component[] components = getComponents();<br />        for (Component component : components){<br />            //Make sure it's not a filler or a title<br />            if (!((component == spacer) || (component instanceof DockLabel) || (component == firstSpacer) || (component == lastSpacer))){<br />                //Set the weight<br />                icon.weightx = 0.0;<br />                icon.weighty = 0.0;<br />                //Icon first<br />                layout.setConstraints(component, icon);<br />                <br />                //Set the prefered size of the component<br />                component.setPreferredSize(tweenSize(component.getSize(),getComponentPreferedSize(p,component,highlightedComponent)));<br />                <br />                //Set the visibility of its label<br />                if (highlightedComponent==component){<br />                    iconLabels.get(component).setVisible(true);<br />                } else {<br />                    iconLabels.get(component).setVisible(false);<br />                }<br />                <br />                //Title next<br />                layout.setConstraints(iconLabels.get(component),title);<br />                <br />                //Move to next position<br />                icon.gridx += dx;<br />                icon.gridy += dy;<br />                title.gridx += dx;<br />                title.gridy += dy;<br />            }<br />        }<br />        <br />        //Add the last spacer<br />        icon.weightx = spacerWeightX();<br />        icon.weighty = spacerWeightY();<br />        icon.fill = GridBagConstraints.BOTH;<br />        layout.setConstraints(lastSpacer,icon);<br />    }<br />    <br />    /**<br />     * Adds a new item to the dock<br />     *<br />     */<br />    public void addDockElement(Component component, String label){<br />        if (iconLabels.get(component)!=null){<br />            return;<br />        }<br />        <br />        DockLabel newLabel = new DockLabel(label);<br />        <br />        //We would like it to do something funcky as it adds them and make them grow<br />        component.setPreferredSize(new Dimension(0,0));<br />        iconLabels.put(component,newLabel);<br />        add(component);<br />        add(newLabel);<br />        updateLayout(mouseTracker.getPosition());<br />    }<br />    <br />    /**<br />     * Not interested<br />     */<br />    public void mouseCrossThreshold(boolean mouseEntered) {<br />    }<br />    <br />    /**<br />     * When the mouse moves, update the layout. Should be optimized when the<br />     * highlighted component is the spacer to not do anything<br />     */<br />    public void mouseMoved(Point position) {<br />        if (autoHide){<br />            Component mouseOver = getComponentAt(position);<br />            if ((mouseOver == firstSpacer) || (mouseOver == lastSpacer) || (spacer==mouseOver)){<br />                hiding = true;<br />            } else {<br />                hiding = false;<br />            }<br />        } else {<br />            hiding = false;<br />        }<br />        lastKnownMouse = position;<br />        updateLayout(position);<br />        layout.layoutContainer(this);<br />    }<br />    <br />    public void paint(Graphics graphics) {<br />        graphics.setColor(new Color(200,200,220,127));<br />        Rectangle dockSize = null;<br />        int oldNormalSize=normalSize;<br />        if (hiding){<br />            normalSize = Math.min(normalSize, ((dockedTo==Side.NORTH) || (dockedTo==Side.SOUTH)) ? firstSpacer.getHeight(): firstSpacer.getWidth());<br />        }<br />        //Draw the main dock background<br />        switch (dockedTo){<br />            case NORTH:<br />                dockSize = new Rectangle(firstSpacer.getX()+firstSpacer.getWidth(),0,getWidth()-(lastSpacer.getWidth()+firstSpacer.getWidth()),normalSize);<br />                break;<br />            case SOUTH:<br />                dockSize = new Rectangle(firstSpacer.getX()+firstSpacer.getWidth(),getHeight()-normalSize,getWidth()-(lastSpacer.getWidth()+firstSpacer.getWidth()),normalSize);<br />                break;<br />            case WEST:<br />                dockSize = new Rectangle(0,firstSpacer.getHeight(),normalSize,getHeight()-(lastSpacer.getHeight()+firstSpacer.getHeight()));               <br />                break;<br />            case EAST:<br />                dockSize = new Rectangle(getWidth()-normalSize,firstSpacer.getHeight(),normalSize,getHeight()-(lastSpacer.getHeight()+firstSpacer.getHeight()));<br />                break;<br />        }<br />        if (hiding){<br />            normalSize = oldNormalSize;<br />        }<br />        dockSize.x-=insets;<br />        dockSize.y-=insets;<br />        dockSize.width+=insets*2;<br />        dockSize.height+=insets*2;<br />        graphics.fillRect(dockSize.x,dockSize.y,dockSize.width,dockSize.height);<br />        <br />        //Draw the outline<br />        graphics.setColor(new Color(255,255,255,127));<br />        switch (dockedTo){<br />            case SOUTH:<br />                graphics.drawRect(dockSize.x,dockSize.y,dockSize.width-1,dockSize.height);<br />                break;<br />        }<br />        super.paint(graphics);<br />    }<br />    <br />    public void actionPerformed(ActionEvent actionEvent) {<br />        if (animating){<br />            updateLayout(lastKnownMouse);<br />            layout.layoutContainer(this);<br />        }<br />    }<br />    <br />    //For convenience<br />    public class JSpacer extends JComponent {<br />        public JSpacer(){<br />            setBackground(null);<br />            setOpaque(false);<br />        }<br /><br />        <br />        <br />    }<br />    <br />    //Temporary until I do something else<br />    public class DockLabel extends StrokedLabel {<br />        public DockLabel(String title){<br />            super(title);<br />        }<br />        <br />    };<br />}<br /><br /></strong></p>]]></content:encoded></item><item><title>Recreating the Apple Dock in Java</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2007-01-07T21:18:33+00:00</dc:date><link>http://www.blogofbug.com/files/recreating_apple_dock_in_java.html#unique-entry-id-15</link><guid isPermaLink="true">http://www.blogofbug.com/files/recreating_apple_dock_in_java.html#unique-entry-id-15</guid><content:encoded><![CDATA[I had hoped to keep up the momentum with a few more of my "rough hacks" being tweaked enough to be published, but the start of the business year has as always deflected me. To top it all I'm off on a couple of weeks of business trips so I'm unlikely to be in a position to publish. However, a couple of international flights can be great for giving you time to code in the flight. To pique your interest while I'm off, I thought you might like to see the next component I've been working up, the Dock. It's actually pretty close but I haven't done things like allowing it to be attached to the various compass points. <br /><br />Anyhow, there is something coming along, and here's a little teaser....<br /><p style="text-align:center;" ><br /><img class="imageStyle" alt="Picture 2" src="http://www.blogofbug.com/files//page0_blog_entry15_1.png"width="398" height="320"/></p>]]></content:encoded></item><item><title>Java Self-Leveling Fluid Simulation - Help Needed&#x21;</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2007-01-03T20:46:36+00:00</dc:date><link>http://www.blogofbug.com/files/java_self_leveling_fluid_simulation.html#unique-entry-id-14</link><guid isPermaLink="true">http://www.blogofbug.com/files/java_self_leveling_fluid_simulation.html#unique-entry-id-14</guid><content:encoded><![CDATA[I blogged in the new year about a little hack-up of an old algorithm. Well I've been fiddling with it in the evenings and I've managed to optimize the self-leveling code so that it barely impacts performance. Great, I hear you say, that's wonderful now post a Web-Start  demo. I'm going to, but first I would like you to read a bit, and then help!<br /><br /><p style="text-align:center;" ><img class="imageStyle" alt="Picture 1" src="http://www.blogofbug.com/files//page0_blog_entry14_1.png"width="600" height="483"/><br /><br />Sleep patterns have been knocked all over the place with the return to work and the bustle of the new year. Oh, and there's a matter of the Ashes which is on during the middle of the night giving me an excuse to get out of bed rather than trying to go to sleep. So I decided to use the early hours to optimize. <br /><br />A few notes first:<br /><br />	1. This has been optimized for dual-or-more-core processors (more later)<br />	2. I'm not claiming it's the fastest or best or anything. This is intellectual mastication (ahem) and hopefully a hint of "oh shiny!"<br /><br />About 1. As I've said, once upon a time I used to write games, back in the days when 20Mhz was pretty exciting and SVGA was bleeding edge. I've built up some opinions about how I would go about making use of dual cores, and this demo implements some of them. The simulation runs in one thread, and is considered more important than drawing. The second thread does the painting (javax.swing.Timer anyone) and run's every 20th of a second (here in the UK on the Amiga, 50Hz was the frame-rate we were aiming for, so that's what I was going for). <br /><br />I have a MacBook Pro and an almost-exactly-the-same-spec Dell 620 to test on so they are both Dual-Core, I'd love to read your comments on performance on single core (in fact whatever machine you have) machines. But that brings me to something else I really need your help with. <br /><br />I'm running on Java SE 6 on both the Mac and Windows. Windows has always been a bit faster (since I've been writing this), but when I split up the main thread, Windows suddenly started to claim it was A LOT faster. As in it's claiming 0ms on the Dell for the simulation, and often 0ms for the re-draw. Hum. Can't see why the instrumentation would suddenly change, but hot-damn it does seem to be running without slow down (on the dell at least) barely chewing any (or either) of the cores. So again, any comments on your experiences would be great. <br /><br />The web-startable app below needs Java 5, but please try on Java 6. Please post your results... and don't forget to click on "Tap On" when you are ready to start the water!<br /><br /><a href="http://www.blogofbug.com/assets/cascade.jnlp" rel="self"></p><p><img class="imageStyle" alt="webstart-small2" src="http://www.blogofbug.com/files//page0_blog_entry14_2.gif"width="88" height="23"/></a></p>]]></content:encoded></item><item><title>Re-writing history implementing old game in Java</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2007-01-01T09:09:34+00:00</dc:date><link>http://www.blogofbug.com/files/rewriting_history_implementing_old_game_in_java.html#unique-entry-id-13</link><guid isPermaLink="true">http://www.blogofbug.com/files/rewriting_history_implementing_old_game_in_java.html#unique-entry-id-13</guid><content:encoded><![CDATA[I have a confession, I was an Amiga fan-boy. More than that, I used to develop games for the platform. One game we were never able to find a publisher for was called Cascade. It used a really simple algorithm to simulate water a particle at a time, and by manipulating the environment a player would attempt to collect enough water before the time ran out. Well, with a really tightly engineered loop the Amiga version on a 1200 used to manage about 400 drops + the rest of the game engine every 50th of a second. I recently wondered how Java would handle the algorithm and whether a few of the limitations could be worked around. I have to admit, it's looking good!<br /><br /><img class="imageStyle" alt="Picture 2" src="http://www.blogofbug.com/files//page0_blog_entry13_1.png"width="600" height="400"/><br /><br /><br />As you can see, 400 drops is no longer a problem inside the "20ms" limit required to meet the Amiga's performance, in-fact you can crank it up to over 100,000 on my MBP before it starts to sweat. No great surprise there, the processor is massively faster. What has surprised me is how easy it is to implement in Java, how well Java2D works. In addition, the Java implementation is doing a whole lot more than the original version. I'm not sure if I'm going to post the full source or not, but here are some of the problems that have been solved with the old algorithm. <br /><br /><strong>Vertical Columns<br /></strong>The old routine didn't have any idea of the inertia a particle of water had as it flowed over the scene, so when the water could go down it would, creating very artificial looking columns of falling water. <br /><br /><img class="imageStyle" alt="Picture 6" src="http://www.blogofbug.com/files//page0_blog_entry13_2.png"width="600" height="400"/><br /><br />Easy to address, give the water some inertia, immediately much nicer diffusing water falls. <br /><br /><img class="imageStyle" alt="Picture 7" src="http://www.blogofbug.com/files//page0_blog_entry13_3.png"width="600" height="400"/><br /><br /><strong>Self-Leveling<br /></strong>This was a big one, the levels of the game had to be very carefully designed to ensure that no part of a level would highlight the fact that the water would not self-level (pressure equalizing the water level). It was a real pain, and subtly caused other minor problems. Have a look at the U-bends in the screen shot below. The water should be pushing down equalizing the pressure in the u-bend. <br /><br /><img class="imageStyle" alt="Picture 10" src="http://www.blogofbug.com/files//page0_blog_entry13_4.png"width="600" height="400"/><br /><br />Well in the spirit of the original code (that is that it looks as much like water as possible, without ever really simulating what water does), I've managed to solve this problem too, although it is currently impacting performance (3-4 times slower in regions where water self-levels) but there are lots of ways that can be improved it just the raw solution at the moment, plugged into the inner loop. As you can see, it's working perfectly...<br /><br /><img class="imageStyle" alt="Picture 12" src="http://www.blogofbug.com/files//page0_blog_entry13_5.png"width="600" height="400"/><br /><br />So what's the point of the teasing post? Well I'm not sure what I'm going to do with this code yet, it's partly an exercise for walking down memory lane for me, in which case I'll just post the code so people can do a better job than me! However, it's also an interesting look at writing dynamic graphics algorithms in Java, in which case I'm going to publish my findings so that they can be exploited by others. Anyhow, let me know if you find it interesting, I'll make it web-startable soon so you can all play with the routine (it's kind of hypnotic). <br /><br />Update: The web-start demo has been implemented, and I would really like your help with getting some results from a range of machines: <a href="http://www.blogofbug.com/files/java_self_leveling_fluid_simulation.html" rel="self" title="Web-Start Water Demo">Click Here</a>]]></content:encoded></item><item><title>Improving the performance of the Carousel</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2006-12-30T19:19:51+00:00</dc:date><link>http://www.blogofbug.com/files/Improving_performance_of_java_carousel.html#unique-entry-id-12</link><guid isPermaLink="true">http://www.blogofbug.com/files/Improving_performance_of_java_carousel.html#unique-entry-id-12</guid><content:encoded><![CDATA[I sent a quick e-mail to Romain guy to let him know how I was re-using his reflection code, and because I thought he might get a kick out of it. I was delighted to get a reply along with some suggestions for improving performance. The main points he raised were as follows: <br /><br />1. Gradient painting was being done very inefficiently (and he suggested looking at <a href="http://weblogs.java.net/blog/gfx/archive/2006/09/java2d_gradient.html" rel="self">this thread</a>) <br />2. I should stop using ImageIcon to do the loading, and use ImageIO instead. I've been putting off looking at ImageIO for a long time, seems like a good excuse to have a fiddle<br />3. The BICUBIC rendering hint gives little improvement over BILINEAR but costs a LOT more<br />4. Check out <a href="http://swingx.dev.java.net/" rel="self">SwingX</a> and use their compatible BufferedImage loader for best possible performance<br /><br />As this is the first time I've fiddled with ImageIO, I'm not going to use SwingX I like to figure things out myself the first time so that I fully understand why the right solution is better. I thought it would be good to perform some performance metrics and see just how much we could speed things up, here are the results. If you missed the original posts on the carousel then just look back in the blog (hum, it was only yesterday!)<br /><br />The first step is to put some performance metrics in. This required a little bit of thinking as we have a couple of "painters" to see. My first thought was to look at the paint method and time that, it seemed to work well so I dug out an old metric recording class I wrote a long time ago to measure server request performance. It's a useful little class. <br /><br /><span style="font:11px Courier, mono; ">/*<br /> * PerformanceMonitor.java<br /> *<br /> * Created on June 12, 2003, 7:36 PM<br /> *<br /> */<br /><br />package com.blogofbug.utility;<br /><br />/**<br /> *<br /> * @author buggles<br /> */<br />public class PerformanceMonitor {<br />    private long    operationTimes[];<br />    private int     nextSlot=-1;<br />    private String  monitorName = "Unset";<br />    private long    operationStartTime = -1;<br />    <br />    /** Creates a new instance of PerformanceMonitor */<br />    public PerformanceMonitor(String monitorName, int samples) {<br />        operationTimes = new long[samples];<br />        nextSlot = 0;<br />        this.monitorName = monitorName;<br />        for (int i = 0;i<operationTimes.length;i++){<br />            operationTimes[i]=-1;<br />        }<br />    }<br />    <br />    /**<br />     * Call when an operation started<br />     */<br />    public void operationStarted(){<br />        operationStartTime = System.currentTimeMillis();<br />    }<br />    <br />    /**<br />     * Call when an operation is stopped<br />     */<br />    public void operationStopped(){<br />        operationTimes[nextSlot++]=System.currentTimeMillis() - operationStartTime;<br />        if (nextSlot == operationTimes.length){<br />            nextSlot = 0;<br />        }<br />    }<br />    <br />    /** <br />     * Produces a string containing the key metrics for the operation<br />     */<br />    public String generateMetrics(){<br />       long totalTime = 0;<br />       long tally = 0;<br />       long fastest = 1000000;<br />       long slowest = 0;<br />       for (int i=0;i<operationTimes.length;i++){<br />           if (operationTimes[i]>-1){<br />               totalTime+=operationTimes[i];<br />               tally++;<br />               fastest = Math.min(fastest, operationTimes[i]);<br />               slowest = Math.max(slowest, operationTimes[i]);<br />           } else{<br />               break;<br />           }<br />       }<br />       int lastSlot = nextSlot - 1;<br />       if (lastSlot == -1){<br />           lastSlot=operationTimes.length-1;<br />       }<br />       return monitorName+" "+tally+" operations - Range: "+fastest+"ms to "+slowest+"ms Total: "+totalTime+"ms Average: "+(totalTime/tally)+"ms Last: "+operationTimes[lastSlot]+"ms";<br />    }<br />}<br /></span><br />It's been trimmed down a little here, I just don't need some of the complexity for this. I plugged it into the paint method of JCarosel <br /><br /><span style="font:11px Courier, mono; ">    public void paint(Graphics g){<br />        performanceMonitor.operationStarted();<br />        super.paint(g);<br />        performanceMonitor.operationStopped();<br />        System.out.println(performanceMonitor.generateMetrics());<br />    }</span><br /><br />and got the following results:<br /><br /><em>JCarousel.paint() 78 operations - Range: 21ms to 86ms Total: 2442ms Average: 31ms Last: 24ms<br /></em><br />So we have a peak performance of 21ms (i.e. slower than 60 fps) and a criminaly slow 86ms performance. Average still very bad. Romain Guy was right, something is badly wrong! <br /><br />Let's do some easy stuff first, changing the rendering hint from BICUBIC to BILINEAR...<br /><br /><em>JCarousel.paint() 78 operations - Range: 20ms to 86ms Total: 2348ms Average: 30ms Last: 23ms<br /></em><br />Hum, doesn't look like a huge change, but remember that a millisecond we've trimmed off of peak and average performance. Certainly a start and not bad for changing one constant. Let's tackle the gradient next. There's a couple of inefficiencies here, one is I am creating a new gradient paint each time, and the second is how I'm rendering the gradient itself. I'm going to change the code to create and store the gradient painter first. <br /><br />JCarousel.paint() 78 operations - Range: 20ms to 86ms Total: 2357ms Average: 30ms Last: 23ms<br /><br />Hum, that looks slightly slower! Given that I'm already not using cyclic gradients (read the blog of Romains... All the way down) it would seem like a good idea to try the other recommended methodology using ImagePaint and stretching the image. <br /><br /><span style="font:11px Courier, mono; ">        if (cache == null || cache.getHeight() != getHeight()) {<br />            cache = new BufferedImage(2, getHeight(),<br />                BufferedImage.TYPE_INT_RGB);<br />            Graphics2D g2d = cache.createGraphics();<br />            <br />            GradientPaint paint = new GradientPaint(0, 0, start,<br />                0, getHeight(), end);<br />            g2d.setPaint(paint);<br />            g2d.fillRect(0, 0, 2, getHeight());<br />            g2d.dispose();<br />        }<br />        g2.drawImage(cache, 0, 0, getWidth(), getHeight(), null); <br /></span><br />The results came as a bit of a shock<br /><br /><em>JCarousel.paint() 78 operations - Range: 26ms to 89ms Total: 3081ms Average: 39ms Last: 35ms<br /></em><br />Almost a second slower for just 78 draws, big negative impact. OK, let's try another technique listed on the site using a texture paint.... <br /><br /><em>JCarousel.paint() 78 operations - Range: 20ms to 85ms Total: 2705ms Average: 34ms Last: 28ms<br /></em><br />No great improvement, and certainly not faster for this size gradient (and that's probably key). Rather than change the size to suit the results, I'm going to stick with the standard gradient paint, but leave the texture code there, what I would really like to do in the future is some decent instrumentation to see the cross-over point between the two techniques and make it auto-switch. For now, a little disappointed, onto looking at ImageIO and my use of ImageIcon etc. <br /><br />One of the other points Romain had made was that there was really no need to specify an ImageObserver, so I took that out, not gain, but no pain either<br /><br /><em>JCarousel.paint() 78 operations - Range: 20ms to 86ms Total: 2357ms Average: 30ms Last: 24ms<br /></em><br />So onto ImageIO and creating an appropriately compatible image (with the actual graphics device being used by the OS). Well the ImageIO part turned out to be very very easy, the real trick was digging through the various device and graphics configurations to get that compatible image. To encapsulate this for the various constructors I created a setImage() method that takes a string URL and performs the various steps. <br /><br /><span style="font:11px Courier, mono; ">    private void setupImage(String imageURL){<br />        Image image = null;<br />        try {<br />            image = ImageIO.read(new URL(imageURL));<br />        } catch (MalformedURLException ex) {<br />            ex.printStackTrace();<br />        } catch (IOException ex) {<br />            ex.printStackTrace();<br />        }<br />        if (image==null){<br />            return;<br />        }<br />        <br />        GraphicsConfiguration configuration = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();<br /><br />        //Create a buffered image which is the right (translucent) format for the current graphics device, this <br />        //should ensure the fastest possible performance. <br />        BufferedImage originalImage = configuration.createCompatibleImage(image.getWidth(null),image.getHeight(null), Transparency.TRANSLUCENT);<br /><br />        //Blit the loaded image onto the optimized surface by creating a graphics context for the new image<br />        Graphics g = originalImage.getGraphics();<br />        g.drawImage(image, 0, 0, null);<br />        g.dispose();<br />        <br />        //First hack, just sets the bufferedIMage to the one loaded, don't cache any rendering<br />        bufferedImage = originalImage;<br />        setPreferredSize(new Dimension(originalImage.getWidth(null), originalImage.getHeight(null)));<br />    }<br /></span><br />One quick tweak to remove the dependance on ImageIcon in the paint method, make sure the image created to render the reflection is compatible with the graphics device also and we fire off our metrics again... <br /><br /><em>JCarousel.paint() 78 operations - Range: 16ms to 55ms Total: 1965ms Average: 25ms Last: 18ms<br /></em><br />And we have our first big result, big improvement on both peak and trough performance, and consummate improvement to average render times. There's one more thing we can do, remembering the mantra of graphics programmers everywhere... to make the rendering pipeline as fast as possible, get any and all operations OUT of it. So let's use setImage to correctly create the reflected image and text so that the paint is just a straight scaled blit and the rendering of the text. Resulting in...<br /><br /><em>JCarousel.paint() 78 operations - Range: 8ms to 35ms Total: 974ms Average: 12ms Last: 11ms<br /></em><br />There we have it, I've no doubt that the rendering hints and gradient rendering would have a bigger effect on a larger area, but on a small area you just don't see the benefit. However, creating an image that is already compatible with the final rendering surface brought a real performance bonus, and I just had no idea. I'm pretty sure I can give several of my applications a real boost now using this technique (6ms is significant). However, the biggest hit came by moving things out of the rendering loop altogether. <br /><br />I guess you still want to know that it looks right? Honestly some people just aren't trusting enough!<br /><br /><img class="imageStyle" alt="Picture 9" src="http://www.blogofbug.com/files//page0_blog_entry12_1.png"width="600" height="400"/><br />]]></content:encoded></item><item><title>Nintendo Wii: Gimmic or Gaming Platform?</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Tech</category><dc:date>2006-12-30T09:14:47+00:00</dc:date><link>http://www.blogofbug.com/files/Wii_Gimmic_or_Gaming_Platform.html#unique-entry-id-11</link><guid isPermaLink="true">http://www.blogofbug.com/files/Wii_Gimmic_or_Gaming_Platform.html#unique-entry-id-11</guid><content:encoded><![CDATA[Our kids unwrapped their Wii on Christmas day, and there's no doubt it was an instant hit with them and the family. I thought I would wait a couple of days to see how usage changed over the following 5 days before posting a review.<br /><br />The Wii was unwrapped with great excitement, with the kids clamoring to get their hands on it. We had bought them the main console (includes Wii Sports here in Europe) as well as Wii Play (with the extra controller), Zelda, and Monster 4x4 World Circuit (each targeted at the normal game type liked by one of the kids). The initial plug-in was easy, wireless setup to our home router easy, and although I've read some complaints, it was a 5 minute job to download the two system updates, and we were in business. <br /><br /><img class="imageStyle" alt="1" src="http://www.blogofbug.com/files//page0_blog_entry11_1.png"width="412" height="301"/><br /><br />Wii Sports went in first, and I have to say from the first moment, to just now when I finished my Wii Fitness Program for the day it is one of the classic pieces of gaming software developed, and the Wii will stand or fall based on how close to this level of perfection they get. One of the first things we noticed was that we didn't have our own Mii's. Mii's are characters you enter (give them names, define their faces and physical proportions etc) to tie your game saves to. This means Wii Sports (and Wii Play) can track your progress, and of course adds an extra edge when playing two player games; the characters even look like you! So with our newly defined Mii's we huffed and puffed our way to Tennis greatness. Since then we've pushed out further and are big fans of Tennis, Baseball, and Bowling. Golf is great, but it's not a game we take much interest in in the family so I guess it's hard to get as "into" it as we might if we played golf. Boxing... hasn't pushed our buttons at all, again we don't do it or watch it as a family, but I find the controls the least instinctive of all. That said, Boxing does excel as one of the "you'll be exhausted at the end of the game" games. This leads me back to the Wii Fitness and Training. These are mini-master strokes, each day the Wii will set you 3 challenges and measure your sports "Age" with 20 being the optimum (eek, I'm 15 years off my peak!). I've dragged my age down from 64 through the 30's to 27 today! All of the challenges can be practiced in Wii training, and I'm making sure I spend 30 minutes a day doing the various mini-games. I only just get to the stage where you might be considered to be breaking a sweat, but I certainly am breathing harder than usual, and I have muscles that ache. This is a good thing, and leads to my final good thing. The kids stop playing with the console when they are physically tired! Their sessions seem naturally limited to around an hour on Wii Sports before they feel like a bit of a rest and doing something else (they're not bored!). You can bet they'll be fighting over which game they will play first in a couple of hours, but I really like that the console seems to limit how much they play without taking a break. <br /><br /><img class="imageStyle" alt="wii-remote-controller" src="http://www.blogofbug.com/files//page0_blog_entry11_2.jpg"width="247" height="185"/><br /><br />Wii-Play comes with a spare Wii-Remote (needed for the two player shenanigans) and is intended as a training ground for the Wii remote. That it may be, but few of the games are that engaging. Duck-Shooting deserves a mention, great two player fun, fishing has been popular with the kids, and the pool game has gone down well with the older members of our family. Overall for me, this is a no-score draw of a game, the tedious out-weighs the great (and both are present) and I have been left wanting to play more "levels" of say duck-shoot rather than being subjected to the "Find the matching Mii's" game. Yawn. <br /><br /><img class="imageStyle" alt="notizia.shot1.T150" src="http://www.blogofbug.com/files//page0_blog_entry11_3.jpg"width="150" height="105"/><br /><br />Monster 4x4 World Circuit does what it says on the tin. It comes with a steering wheel that has to be assembled (not hard) and then has the wiimote plugged into it and away you go, it's like being a kid again pretending to hold a steering wheel and throwing it left and right as you drive along. The game itself is pretty basic, not bringing anything new to the "not-very-real-racing" genre. Stunts can be performed, Mario Kart-esque upgrades etc, but as I hoped, the control system lends itself to a 4 year old being able to play it with ease. It may not be setting the older kids world alight, but it's great for the very young.<br /><br />Finally, onto the big launch title, Zelda: Twighlight Princess. I've never been into Zelda, and there are very few of these types of game that I enjoy. Zelda doesn't really do anything to change that for me. The graphics look like a very good PS2 title (although smoother) with lots of High Dynamic Range lighting and they are fast an fluid with attractive spot effects. The music is what you would expect, but all eyes turn to the control system, and the game play. <br /><br /><img class="imageStyle" alt="zelda_twilight_princess_wii_demo_e3" src="http://www.blogofbug.com/files//page0_blog_entry11_4.jpg"width="144" height="107"/><br /><br />Sure enough the Wii's unique control system is put to good use. The combat system works very well, more instinctively than I expected. On screen your character's sword is strapped to his back... so how do you equip it ready for a fight? Well you reach over your shoulder with the Wii-mote of course. Very nice, and leads to one of many Wii moments when the kids were trying to work out which button to press, before one of them says "just do what you'd do" and hey presto it works (another was serving in Wii Sports tennis. Just literally toss up and smash... almost too obvious!). The game... oh the game. Kids are enjoying it, although it bores me to tears, gushing emotion, cut scenes you can't quickly skip through. Our eldest daughter laps it up though, and is clearly having a lot of fun. For me, the control system remains the highlight (fishing very good, oh yes). <br /><img class="imageStyle" alt="wii_internet" src="http://www.blogofbug.com/files//page0_blog_entry11_5.jpg"width="200" height="151"/><br /><br /><br />That's some of the key launch titles covered, what about the other Wii features? The beta internet channel works very very well (better than it has any right to) and I find myself looking forward to being able to surf on the Wii with Google Reader to keep up on my blogs. Very nicely done Nintendo/Opera. The weather channel is a very nice gimmick, filling up space on what, at launch, is a very empty menu system. The photo-channel is cute, slot in a SD card with some photos on, draw all over them and post them on the Wii message board for the other members of your family to see. Cute. which leads neatly onto the Wii Message board. Your Wii sends you messages, letting you know how long which games have been played, new personal bests in the Wii Sports/Wii Play games, and it can be synced with an actual email address. You can even send e-mail to others from it which is a nice touch. Which brings us to how keyboard entry works, no great surprise there is an onscreen keyboard you point and click at. It works very well, and even sports predictive typing which can really speed things up. One can't help but feel Nintendo are deploying lessons learned from the DS which has an almost identical system. <br /><br /><img class="imageStyle" alt="images" src="http://www.blogofbug.com/files//page0_blog_entry11_6.jpeg"width="130" height="130"/><br /><br />Finally, the hardware. Nintendo have taken a risk, they've not packed the console full of more graphics power than an SGI christmas party. What they have packed it full of is standard interfaces. The Wiimotes are Bluetooth (wireless keyboards and mice to come then), the memory cards are standard SD cards (meaning we didn't bother buying one for the Wii, just used an old 1Gb one lying around from a smart phone), and of course there are USB ports and the 802.11 wireless network. What this means is that Nintendo have made it very easy for companies to build add-ons. As we've seen with the iPod this can be critically important to have a healthy eco-system of supporting products. You can also use all of your GameCube controllers with a "flip-flap" opening to reveal for controller ports and two memory slots. That pleased the kids too. But what of the graphics and sound? Absence of a hard-drive? To be honest, I don't care. I'm sure we will buy a 360 or PS3 at some point when the price has come down, but right now the Wii offers the first novel gaming experience since the PS1 launched. To be blunt, screw the graphics they are good enough. Sounds is what sound is, although hard-core gamers may be searching for 5.1 surround sound, our kids don't care and they are rather keen on the full 3D control system. <br /><br />So what's the summary? The Wii itself is genius, and provides a unique platform for future gaming and media fun. It's going to initially capture imaginations and sell like hot cakes. However, the next 12 months are critical. Nintendo need to build a solid base of 4 or 5 killer Wii only games that make use of the unique control system and ensure that the number of consoles out there keep growing. I can see them leaning on their DS experience to do that (and some synergy there would be great), but it's not an easy shot. The PS3 will gain market share, and the XBox 360 is another master piece for the older gamer. For me, the weakest machine at this point is the PS3, but if Nintendo play it right, they'll ensure the 360 and PS3 fight it out between them while it races ahead. Get it wrong, and in 18 months the Wii will be an interesting milestone is game console history, and little more. <br /><br />Right, I'm off to see if I can get to Pro level at Wii Tennis. ]]></content:encoded></item><item><title>Java Carousel Part 3: Reflected Images Love Carousels</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2006-12-29T14:40:09+00:00</dc:date><link>http://www.blogofbug.com/files/Java_Carousel_Component_Sexy.html#unique-entry-id-10</link><guid isPermaLink="true">http://www.blogofbug.com/files/Java_Carousel_Component_Sexy.html#unique-entry-id-10</guid><content:encoded><![CDATA[Although our carosel can take any Java component, what we really need is a dedicated component that looks perfect in the carosel? For example, the image shouldn't be clipped as in our previous examples, it should scale to fit the size the layout manager sets for it. We also need labels and of course that trade mark "wet floor" reflection (original algorithm came from an article by Romain Guy).<br /><br />As some of you may have picked up by now, I like to build complexity up gradually. So I'm going to start with a new Component called ImageLabel. What's special? It's basically a standard JLabel, but its constructor takes an image, and the component's preferred size is set to the dimensions of that image. Alternatively, if that's not desired (i.e. you want to specify a higher resolution image and ensure it's always scaled down) you can supply a target preferred size. OK, none of that is very different, so the real difference comes in the paint method. It scales the image to fill the set size, meaning as our CaroselLayout sizes the components as they spin around, it will fill the space available to it. <br /><br /><span style="font:11px Courier, mono; ">public class ImageLabel extends JLabel{<br />    protected ImageIcon imageIcon = null;<br /><br />    /**<br />     * Creates a new instance of ImageLabel. The prefered width and height will <br />     * be set to the dimensions of the image<br />     *<br />     * @param icon The image to display<br />     */<br />    public ImageLabel(ImageIcon icon) {<br />        super(icon);<br />        this.imageIcon = icon;<br />        setPreferredSize(new Dimension(icon.getIconWidth(), icon.getIconHeight()));<br />    }<br />    <br />    /**<br />     * Creates a new instance of ImageLabel, setting the preferred rendering size to <br />     * the supplied dimensions<br />     *<br />     * @param icon The image to place on the label<br />     * @param width The prefered width<br />     * @parem height The prefered height<br />     */<br />    public ImageLabel(ImageIcon icon, int width, int height){<br />        this(icon);<br />        setPreferredSize(new Dimension(width,height));<br />    }<br />    <br />    /**<br />     * Paints the label scaling the image to the appropriate size<br />     *<br />     */<br />    public void paint(Graphics graphics) {<br />        Image image = this.imageIcon.getImage();<br />        ImageObserver observer = imageIcon.getImageObserver();<br />        ((Graphics2D)graphics).setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);<br />        ((Graphics2D)graphics).setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);<br />        graphics.drawImage(image, 0,0,getWidth(),getHeight(),0,0,image.getWidth(observer),image.getHeight(observer),observer);<br />    }<br />}<br /></span><br /><br /><br /><img class="imageStyle" alt="Pasted Graphic 4" src="http://www.blogofbug.com/files//page0_blog_entry10_1.jpg"width="600" height="400"/><br /><br />Here we are with a Carosel with 4 of the normal ImageLabels added. It's looking a lot more like our front Row component already, but we need to add reflections and labels. Let's do that by extending our ImageLabel class with ReflectedImageLabel.<br /><br /><span style="font:11px Courier, mono; ">public class ReflectedImageLabel extends ImageLabel{<br />    //The text title to display<br />    private String  text = "";<br />    private static final Font reference = new Font("Arial",Font.BOLD,14);<br />    <br />    <br />    /**<br />     * See constructor for image label<br />     *<br />     */<br />    public ReflectedImageLabel(ImageIcon image){<br />       super(image); <br />    }<br />    <br />    public ReflectedImageLabel(ImageIcon image, int width, int height){<br />        super(image,width,height);<br />    }<br />    <br />    public ReflectedImageLabel(ImageIcon image, String text, int width, int height){<br />        this(image,width,height);<br />        this.text=text;<br />    }<br />    <br />    public Dimension getPreferredSize() {<br />        Dimension d = super.getPreferredSize();<br />        d.height = (int) ((double) d.height * 1.5);<br />        <br />        return d;<br />    }<br /><br />    public void paint(Graphics graphics) {<br />        Graphics2D g = (Graphics2D) graphics;<br />        Image image = this.imageIcon.getImage();<br />        ImageObserver observer = imageIcon.getImageObserver();<br />        <br />        int drawHeight = (int) ((double) getHeight() / 1.5);<br />        <br />        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);<br />        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);<br />        <br />        g.drawImage(image, 0,0,getWidth(),drawHeight,0,0,image.getWidth(observer),image.getHeight(observer),observer);<br />    <br /><br />        <br />        BufferedImage reflection;<br />        try{<br />            reflection= new BufferedImage(getWidth(), drawHeight, BufferedImage.TYPE_INT_ARGB);<br />        } catch (Exception e){<br />            return;<br />        }<br />        Graphics2D reflectionGraphics = reflection.createGraphics();<br />        <br />        AffineTransform tranform = AffineTransform.getScaleInstance(1.0, -1.0);<br />        tranform.translate(0, -drawHeight);<br />        <br />        // draw the flipped image<br />        AffineTransform oldTransform = reflectionGraphics.getTransform();<br />        reflectionGraphics.setTransform(tranform);<br />        //Should be drawn upside down<br />        //could also try<br />        //super.paint(reflectionGraphics)<br />        reflectionGraphics.drawImage(image, 0,0,getWidth(),drawHeight,0,0,image.getWidth(observer),image.getHeight(observer),observer);<br /><br />        <br />        reflectionGraphics.setTransform(oldTransform);<br /><br />        <br />        GradientPaint painter = new GradientPaint(0.0f, 0.0f,<br />        new Color(0.0f, 0.0f, 0.0f, 0.5f),<br />        0.0f, drawHeight / 2.0f,<br />        new Color(0.0f, 0.0f, 0.0f, 1.0f));<br /><br />        // this use : Ar = Ad*(1-As) and Cr = Cd*(1-As)<br />        reflectionGraphics.setComposite(AlphaComposite.DstOut);<br />        reflectionGraphics.setPaint(painter);<br />        // this will make our image transluent ...<br />        reflectionGraphics.fill(new Rectangle2D.Double(0, 0, getWidth(), drawHeight));<br />        <br />        //now draw on the main graphics layer<br />        graphics.drawImage(reflection, 0,drawHeight,observer);<br />        <br />        //Debugging<br />        /*<br />        String text = this.getText();<br />        if (text==null)<br />            text="Null";<br />        char chars[] = new char[text.length()];<br />        text.getChars(0,text.length(),chars,0);<br />        graphics.drawChars(chars,0,chars.length,10,getHeight()/2);*/<br />        <br />        reflectionGraphics.dispose();     <br />        <br />        //Draw text if there is any...<br />        if (text.length()>0){<br />            Graphics2D g2d = (Graphics2D) graphics;<br />            Rectangle2D bounds = reference.getStringBounds(text,g2d.getFontRenderContext());<br />            double scaleFactor =  (double) getWidth()/ bounds.getWidth();<br />            Font font = new Font("Arial",Font.BOLD,(int) (14.0 * scaleFactor));<br />            g2d.setFont(font);<br />            graphics.setColor(Color.WHITE);<br />            graphics.drawString(text,0,drawHeight+(int)(bounds.getHeight()*scaleFactor));<br />        }<br />    }<br />}</span><br /><br />Once again, nothing very complex here. The constructors basically match those for ImageLabel, with additional provision for text to show in the label (not reflected), and then re-use a piece of code I must have abused a hundred times by now, Romain Guy's reflection code. <br /><br /><span style="font:11px Courier, mono; ">public class ReflectedImageLabel extends ImageLabel{<br />    //The text title to display<br />    private String  text = "";<br />    <br />    private static final Font reference = new Font("Arial",Font.BOLD,14);<br />    <br />    <br />    /**<br />     * See constructor for image label<br />     *<br />     */<br />    public ReflectedImageLabel(ImageIcon image){<br />       super(image); <br />    }<br />    <br />    public ReflectedImageLabel(ImageIcon image, int width, int height){<br />        super(image,width,height);<br />    }<br />    <br />    public ReflectedImageLabel(ImageIcon image, String text, int width, int height){<br />        this(image,width,height);<br />        this.text=text;<br />    }<br />    <br />    public Dimension getPreferredSize() {<br />        Dimension d = super.getPreferredSize();<br />        d.height = (int) ((double) d.height * 1.5);<br />        <br />        return d;<br />    }<br /><br />    public void paint(Graphics graphics) {<br />        Graphics2D g = (Graphics2D) graphics;<br />        Image image = this.imageIcon.getImage();<br />        ImageObserver observer = imageIcon.getImageObserver();<br />        <br />        int drawHeight = (int) ((double) getHeight() / 1.5);<br />        <br />        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);<br />        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);<br />        <br />        g.drawImage(image, 0,0,getWidth(),drawHeight,0,0,image.getWidth(observer),image.getHeight(observer),observer);<br />        <br />        BufferedImage reflection;<br />        try{<br />            reflection= new BufferedImage(getWidth(), drawHeight, BufferedImage.TYPE_INT_ARGB);<br />        } catch (Exception e){<br />            return;<br />        }<br />        Graphics2D reflectionGraphics = reflection.createGraphics();<br />        <br />        AffineTransform tranform = AffineTransform.getScaleInstance(1.0, -1.0);<br />        tranform.translate(0, -drawHeight);<br />        <br />        // draw the flipped image<br />        AffineTransform oldTransform = reflectionGraphics.getTransform();<br />        reflectionGraphics.setTransform(tranform);<br />        //Should be drawn upside down<br />        //could also try<br />        //super.paint(reflectionGraphics)<br />        reflectionGraphics.drawImage(image, 0,0,getWidth(),drawHeight,0,0,image.getWidth(observer),image.getHeight(observer),observer);<br /><br />        <br />        reflectionGraphics.setTransform(oldTransform);<br /><br />        <br />        GradientPaint painter = new GradientPaint(0.0f, 0.0f,<br />        new Color(0.0f, 0.0f, 0.0f, 0.5f),<br />        0.0f, drawHeight / 2.0f,<br />        new Color(0.0f, 0.0f, 0.0f, 1.0f));<br /><br />        // this use : Ar = Ad*(1-As) and Cr = Cd*(1-As)<br />        reflectionGraphics.setComposite(AlphaComposite.DstOut);<br />        reflectionGraphics.setPaint(painter);<br />        // this will make our image transluent ...<br />        reflectionGraphics.fill(new Rectangle2D.Double(0, 0, getWidth(), drawHeight));<br />        <br />        //now draw on the main graphics layer<br />        graphics.drawImage(reflection, 0,drawHeight,observer);<br />        reflectionGraphics.dispose();     <br />        <br />        //Draw text if there is any...<br />        if (text.length()>0){<br />            Graphics2D g2d = (Graphics2D) graphics;<br />            Rectangle2D bounds = reference.getStringBounds(text,g2d.getFontRenderContext());<br />            double scaleFactor =  (double) getWidth()/ bounds.getWidth();<br />            Font font = new Font("Arial",Font.BOLD,(int) (14.0 * scaleFactor));<br />            g2d.setFont(font);<br />            graphics.setColor(Color.WHITE);<br />            graphics.drawString(text,0,drawHeight+(int)(bounds.getHeight()*scaleFactor));<br />        }<br />    }  <br />}<br /></span><br />And this brings us all the way back to our first screen shot and example... <br /><br /><img class="imageStyle" alt="Picture 2" src="http://www.blogofbug.com/files//page0_blog_entry10_2.png"width="600" height="400"/><br /><br />Looks good doesn't it? There's lot's of optimization that can be done (caching the reflected image rather than redrawing it each time for example) but I shall leave that to the reader. If you use this code please drop in a link to the blog (much appreciated), and enjoy. <br /><br /><a href="assets/BlogOfBug.jar" rel="self">Download the demo</a> or <a href="assets/BlogOfBug.zip" rel="self">The Whole Project</a><br /><br /><strong>Note: The iTunes CD Images used in this example are reproduced here by kind permission of  </strong><strong><a href="http://www.pixelnetdesign.com/" rel="self">Brian Zeitler from PixelNet Design</a></strong><strong> and please note that all copyright remains with Brian and PixelNet Design.</strong>]]></content:encoded></item><item><title>Java Carousel Part 2: Creating a Carousel Component</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2006-12-29T13:35:18+00:00</dc:date><link>http://www.blogofbug.com/files/Java_Carousel_Component.html#unique-entry-id-9</link><guid isPermaLink="true">http://www.blogofbug.com/files/Java_Carousel_Component.html#unique-entry-id-9</guid><content:encoded><![CDATA[We can now layout and animate our components using a layout manager, but we need something that can cause the animations to occur, and perhaps looks a little better. Queue our Gradient Panel and introducing JCarosel. <br /><br />We want our component to be derived from a JPanel (it contains all of the container functionality we need, no pun intended), but we also know we want it to look pretty. So the first thing I'm going to do use a GrandientPanel class I've written for this purpose several times, here it is for completeness. <br /><br /><span style="font:11px Courier, mono; ">public class GradientPanel extends JPanel{<br />    protected Color start;<br />    protected Color end;<br /><br />    <br />    public void setBackground(Color color){<br />        this.start = color;<br />        this.end = color;<br />        super.setBackground(color);<br />    }<br />    <br />    public void setBackground(Color start, Color end){<br />        this.start = start;<br />        this.end = end;<br />    }<br />    <br />    public void paint(Graphics graphics) {<br />        if (start == end){<br />            super.paint(graphics);<br />            return;<br />        }<br />        <br />        Graphics2D g = (Graphics2D) graphics;<br />        <br />        Rectangle2D.Double rectangle = new Rectangle2D.Double();<br />        rectangle.setRect(0, 0, getWidth(), getHeight());        <br />        GradientPaint gradient = new GradientPaint((float)(getWidth()/2), (float) getY(), start, (float) (getWidth()/2), (float) getHeight(), end, false);<br /><br />        g.setPaint(gradient);<br />        g.fill(rectangle);<br />        super.paintChildren(graphics);<br />    }    <br /><br />}</span><br /><br />It's not very complicated, just using a custom paint method to draw a gradient instead of a single color background. But it's already starting to look a little better!<br /><br /><img class="imageStyle" alt="Picture 5" src="http://www.blogofbug.com/files//page0_blog_entry9_1.png"width="600" height="400"/><br /><br />So our carosel component will extend this gradient panel class, let's take a look at it in full... <br /><br /><span style="font:11px Courier, mono; ">public class JCarosel extends GradientPanel implements MouseListener{<br />    public static final String  FRONT_COMPONENT_CHANGE  = "frontComponentChanged";<br />    protected   CaroselLayout   layout;<br />    <br />    /**<br />     * Creates a new instance of JCarosel<br />     */<br />    public JCarosel() {<br />        layout = new CaroselLayout(this);<br />        this.setLayout(layout);<br />    }<br />    <br />    public Component add(Component component){<br />        add("",component);<br />        bringToFront(getComponent(0));<br />        validate();<br />        return component;<br />    }<br />        <br />    public void remove(Component component) {<br />        super.remove(component);<br />        if (getComponentCount()>0){<br />            bringToFront(getComponent(0));<br />        }<br />        invalidate();<br />        validate();<br />    }<br />    <br />    public Component add(ImageIcon icon, int width, int height){<br />        ReflectedImageLabel component = new ReflectedImageLabel(icon,width,height);<br />        component.addMouseListener(this);<br />        return add(component);<br />    }<br />    <br />    public Component add(ImageIcon icon, String text, int width, int height){<br />        ReflectedImageLabel component = new ReflectedImageLabel(icon,text,width,height);<br />        component.addMouseListener(this);<br />        return add(component);<br />    }    <br />    <br />    public void bringToFront(Component component){<br />        firePropertyChange(FRONT_COMPONENT_CHANGE,getComponent(0),component);<br />        layout.setFrontMostComponent(component);<br />    }<br />    <br />    /**<br />     * Which component is at the front<br />     */<br />    public Component getFrontmost(){<br />        return getComponent(0);<br />    }<br />    <br />    /** <br />     * Bring the "clicked" component to the front<br />     */<br />    public void mouseClicked(MouseEvent mouseEvent) {<br />        if (mouseEvent.getClickCount()==1){<br />            bringToFront((Component) mouseEvent.getSource());<br />        }<br />    }<br /><br />    /** <br />     * Moves everything to their final positions<br />     *<br />     */<br />    public void finalizeLayoutImmediately(){<br />        layout.layoutContainer(this);<br />        layout.finalizeLayoutImmediately();<br />        repaint();<br />    }<br />    <br />    /** Not interested */<br />    public void mousePressed(MouseEvent mouseEvent) {}<br />    /** Not interested */<br />    public void mouseReleased(MouseEvent mouseEvent) {}<br />    /** Not interested */<br />    public void mouseEntered(MouseEvent mouseEvent) {}<br />    /** Not interested */<br />    public void mouseExited(MouseEvent mouseEvent){}<br />}</span><br /><br />Not too much to try to understand is it? Well, hopefully not. Once again, let's break it down. As already discussed it extends GradientPanel, but also implements MouseLIstener. Why? Well what we want to make our carosel spin when the user clicks on an component on the carosel to bring that component to the front (remember setFrontmostComponent?) and we'll use a mouse listener to do that. First things first, let's look at the constructor...<br /><br /><span style="font:11px Courier, mono; ">    /**<br />     * Creates a new instance of JCarosel<br />     */<br />    public JCarosel() {<br />        layout = new CaroselLayout(this);<br />        this.setLayout(layout);<br />    }</span><br /><br />Simple really, we construct and use a CaroselLayout object. We need to keep a reference to the layout manager so we can call that setFrontmostComponent method later. <br /><br /><span style="font:11px Courier, mono; ">    public Component add(Component component){<br />        component.addMouseListener(this);<br />        add("",component);<br />        bringToFront(getComponent(0));<br />        validate();<br />        return component;<br />    }<br />        <br />    public void remove(Component component) {<br />        super.remove(component);<br />        component.removeMouseListener(this);<br />        if (getComponentCount()>0){<br />            bringToFront(getComponent(0));<br />        }<br />        invalidate();<br />        validate();<br />    }</span><br /><br />I also override the add and remove methods inherited from Container. We want to animate if that happens, bringing the newly added component to the front. <br /><br />The next two methods we'll look at in more detail in the next installment, but for now you just need to know that we are going to have some methods for easily adding nice image buttons to look similar to the FrontRow buttons without a lot of over-head. <br /><br /><span style="font:11px Courier, mono; ">    public Component add(ImageIcon icon, int width, int height){<br />        ReflectedImageLabel component = new ReflectedImageLabel(icon,width,height);<br />        component.addMouseListener(this);<br />        return add(component);<br />    }<br />    <br />    public Component add(ImageIcon icon, String text, int width, int height){<br />        ReflectedImageLabel component = new ReflectedImageLabel(icon,text,width,height);<br />        component.addMouseListener(this);<br />        return add(component);<br />    }    </span><br /><br />So finally, and oh-so-Java wonderfully simply... the code to spin around our carosel when they click on a component...<br /><br /><span style="font:11px Courier, mono; ">    /** <br />     * Bring the "clicked" component to the front<br />     */<br />    public void mouseClicked(MouseEvent mouseEvent) {<br />        if (mouseEvent.getClickCount()==1){<br />            bringToFront((Component) mouseEvent.getSource());<br />        }<br />    }<br /><br /></span>That's it. In the final installment I'll introduce the ReflectedImage component (and you'll know why give the citation to Romain Guy too!) and make the jar and source code available. <br /><br /><img class="imageStyle" alt="Pasted Graphic 3" src="http://www.blogofbug.com/files//page0_blog_entry9_2.jpg"width="600" height="400"/><br /><br /><strong>Note: The iTunes CD Images used in this example are reproduced here by kind permission of  </strong><strong><a href="http://www.pixelnetdesign.com/" rel="self">Brian Zeitler from PixelNet Design</a></strong><strong> and please note that all copyright remains with Brian and PixelNet Design.</strong>]]></content:encoded></item><item><title>Java Carousel Part 1: Layout and Animation</title><dc:creator>bug.face.uk@mac.com</dc:creator><category>Java</category><dc:date>2006-12-29T11:34:15+00:00</dc:date><link>http://www.blogofbug.com/files/Java_Carousel_Layout_Manager.html#unique-entry-id-8</link><guid isPermaLink="true">http://www.blogofbug.com/files/Java_Carousel_Layout_Manager.html#unique-entry-id-8</guid><content:encoded><![CDATA[In the first installment we looked at what we needed to do to recreate the FrontRow-esque carosel as a standard Swing component. The first step is the LayoutManager, followed by the animation to rotate the carosel. <br /><br />So without any more waffle, let's start with our Layout Manager<br /><br />The first thing we need is a class to test our layout manager and a couple of components to it, here's that code (you'll need it to test!)<br /><br /><span style="font:11px Courier, mono; ">public class CaroselLayoutManager extends JFrame{<br />    <br />    /** Creates a new instance of CaroselLayoutManager */<br />    public CaroselLayoutManager() {<br />        super("Carosel Layout Manager");<br />        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br />        setSize(600,400);<br />        getContentPane().setLayout(new CaroselLayout(getContentPane()));<br />        getContentPane().add("Label Example",new JLabel("Example with text"));<br />        getContentPane().add("Button Example", new JButton("Oh, and a button too!"));<br />        getContentPane().add("Text Field", new JTextField("Edit me!"));<br />        getContentPane().add("Image example",new JLabel(new ImageIcon(CaroselLayoutManager.class.getResource("/com/blogofbug/examples/carosel/itunes.png"))));<br />    }<br />    <br />    /**<br />     * @param args the command line arguments<br />     */<br />    public static void main(String args[]) {<br />        java.awt.EventQueue.invokeLater(new Runnable() {<br />            public void run() {<br />                new CaroselLayoutManager().setVisible(true);<br />            }<br />        });<br />    }    <br />}</span><br /><br />Inheriting from a JFrame, the constructor sets the layout manager to our CaroselLayout (coming soon!) and then adds a couple of standard components to it, that should be enough to test (thanks again to PixelNet Design for permission to use the images). Nothing too exciting here, so what does the layout manager class look like? <br /><br /><span style="font:11px Courier, mono; ">public class CaroselLayout implements LayoutManager,ActionListener{<br />    protected int                      numberOfItems = 0;<br />    <br />    protected LinkedList<Component>    components = new LinkedList<Component>();<br />    <br />    protected Hashtable     additionalData = new Hashtable();<br />    <br />    protected double        rotationalOffset = 0.0;<br />    <br />    protected double        targetOffset = 0.0;<br />    <br />    private   Timer         animationTimer = null;<br />    <br />    private   Container     container = null;<br />    <br />    public CaroselLayout(Container forContainer){<br />        animationTimer = new Timer(0,this);<br />        container = forContainer;<br />    }<br />    <br />    /**<br />     * Name is ignored<br />     *<br />     */<br />    public void addLayoutComponent(String name, Component comp) {<br />        components.addLast(comp);<br />        recalculateCarosel();<br />    }<br />    <br />    /**<br />     * Remove the component<br />     *<br />     */<br />    public void removeLayoutComponent(Component comp) {<br />        components.remove(comp);<br />        recalculateCarosel();<br />    }<br />    <br />    /**<br />     * Gets the additional data stored by the layout manager for a given component<br />     *<br />     * @param comp The component you wish retreive the data for<br />     * @return A position, which is added if it does not already exist. Never null unless<br />     * you run out of memory!<br />     */<br />    protected CaroselPosition getPosition(Component comp){<br />        CaroselPosition cpos = (CaroselPosition) additionalData.get(comp);<br />        <br />        if (cpos==null){<br />            cpos = new CaroselPosition(comp);<br />            additionalData.put(comp,cpos);<br />        }<br />        <br />        return cpos;<br />    }<br />    <br />    protected int recalculateVisibleItems(){<br />        int visibleItems=0;<br />        try{<br />            for (Component comp : components){<br />                if (comp.isVisible()){<br />                    visibleItems++;<br />                }<br />            }<br />        } catch (ConcurrentModificationException ex){<br />            return recalculateVisibleItems();<br />        }<br />        return visibleItems;<br />    }<br />    <br />    protected void recalculateCarosel(){<br />        //Need to count visible, not just how many in the list<br />        //Again dealing with out-of-EDT modification<br />        numberOfItems = recalculateVisibleItems();<br /><br />        //Trap and re-calc on concurrent modification (might as well be up-to-date)<br />        <br />        try{<br />            boolean animate=false;<br />            double itemCount = 0;<br />            for (Component comp : components){<br />                CaroselPosition position = getPosition(comp);<br />                if (comp.isVisible()){<br />                    double localAngle = itemCount * (Math.PI * 2.0 / (double) numberOfItems);<br />                    position.setAngle(localAngle);<br />                }<br />                if (position.isAnimating()){<br />                    animate=true;<br />                }<br />                itemCount+=1.0;<br />            }<br /><br />            //If we do need to animate, get it started<br />            if (animate){<br />                animationTimer.start();<br />            }<br />        } catch (ConcurrentModificationException ex){<br />            recalculateCarosel();<br />            return;<br />        }<br />        <br />    }<br />    <br />    /**<br />     * Cheats and bases it's size on the prefered sizes of each component<br />     *<br />     */<br />    public Dimension minimumLayoutSize(Container parent) {<br />        return preferredLayoutSize(parent);<br />    }<br />    <br />    /**<br />     * Determine the widest and tallest dimensions, then return the height as 1.5 * the highest, and 3 * the widest<br />     *<br />     * @param parent The container for the layout<br />     */<br />    public Dimension preferredLayoutSize(Container parent) {<br />        Dimension dim = new Dimension(0, 0);<br />        // get widest preferred width for left && right<br />        // get highest preferred height for left && right<br />        // add preferred width of middle<br />        int widestWidth = 0;<br />        int highestHeight = 0;<br />        <br />        Iterator i = components.iterator();<br />        while (i.hasNext()){<br />            Component comp = (Component) i.next();<br />            <br />            if (comp.isVisible()){<br />                widestWidth = Math.max(widestWidth, comp.getPreferredSize().width);<br />                highestHeight = Math.max(highestHeight, comp.getPreferredSize().height);<br />            }<br />        }<br />        <br />        dim.width = widestWidth * 3;<br />        dim.height = highestHeight * 2;<br />        <br />        Insets insets = parent.getInsets();<br />        dim.width += insets.left + insets.right;<br />        dim.height += insets.top + insets.bottom;<br />        <br />        return dim;<br />    }<br />    <br />    </span><br /><span style="font:11px Courier, mono; ">    /**<br />     * Lays out all of the components on the carosel. Using the preferred width and height to base<br />     * scaling on<br />     */<br />    public void layoutContainer(Container target) {<br />        //Local copy of components to avoid concurrent modification<br />        //which could happen if someone adds something to the layout outside<br />        //of the EDT. This is faster than do any synchronization or brute force<br />        //exception catching<br />        LinkedList<Component> components = (LinkedList) this.components.clone();<br />        int numberOfItems = this.numberOfItems;<br />        <br />        recalculateCarosel();<br />        // these variables hold the position where we can draw components<br />        // taking into account insets<br />        Insets insets = target.getInsets();<br />        int    width  = target.getSize().width - (insets.left + insets.right);<br />        int    height = target.getSize().height - (insets.top + insets.bottom);<br />        <br />        int widestWidth = 0;<br />        int highestHeight = 0;<br />        <br />        <br />        Iterator i = components.iterator();<br />        while (i.hasNext()){<br />            Component comp = (Component) i.next();<br />   