<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7571541</id><updated>2012-01-12T10:00:07.666-05:00</updated><category term='win64'/><category term='nt service'/><category term='decoration'/><category term='transparent window'/><category term='input blocking'/><category term='jni'/><category term='navigation'/><category term='jna'/><category term='web start'/><category term='drag image'/><category term='unit test'/><category term='java'/><category term='swing'/><category term='x11'/><category term='icon'/><category term='cell renderers'/><category term='drag n drop'/><category term='shaped window'/><category term='jnlp'/><category term='wait indicator'/><category term='overlay'/><category term='testing'/><category term='animated icon'/><category term='validation'/><title type='text'>The Rabbit Hole</title><subtitle type='html'>Things I've found on the way to Getting There</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>48</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7571541.post-2079169961885507542</id><published>2009-10-10T08:32:00.006-04:00</published><updated>2010-01-22T08:28:37.550-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jnlp'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='web start'/><category scheme='http://www.blogger.com/atom/ns#' term='unit test'/><title type='text'>WebStart unit tests</title><content type='html'>I had a few bits of &lt;a href="http://jna.dev.java.net"&gt;JNA&lt;/a&gt; functionality that were only active when in a web start environment, which made it a bit tricky to add tests for them that could be run at the same time as all my other tests.  The functionality also showed up as a big splot of missing code coverage under clover, so I decided to tinker just a bit to see if I could somehow simulate a web start environment for the unit tests.&lt;br /&gt;&lt;br /&gt;I managed to come up with a JUnit-based test fixture which ensures its test methods are all run in a web start environment.  This works well under Windows and OSX; unfortunately *nix variations use NetX which needs some extra hand-holding when first run.&lt;br /&gt;&lt;br /&gt;Finding the right hooks involved standard hacking to find whether the most configurability was offered by invoking the javaws executable or adding the javaws classes to my classpath and hooking directly into the Java.  I ended up doing a Runtime.exec to ensure the running environment wouldn't interfere with that of Web Start.&lt;br /&gt;&lt;br /&gt;The main obstacle to be overcome was signed code.  You can get web start to run unsigned code by tweaking the local policy file to allow the code we're testing, but when you include any native code via the &lt;code&gt;&amp;lt;nativelib&amp;gt;&lt;/code&gt; tag, JNLP requires the &lt;code&gt;&amp;lt;all-permission&amp;gt;&lt;/code&gt; tag, which seems to re-trigger the requirement for signed code.  Self-signing the code is no big deal, but that triggers web-starts authorization dialogs to allow the unknown CA to be used.  Fortunately, the &lt;a href="http://java.sun.com/j2se/1.5.0/docs/guide/deployment/deployment-guide/properties.html"&gt;somewhat obscure&lt;/a&gt; deployment properties file can be used to temporarily allow the self-signed certs and bypass the dialogs.  The tests can be run entirely without user input (except in the case of *nix, where NetX is not sufficiently configurable -- you have to dismiss the dialog on the first test run).&lt;br /&gt;&lt;br /&gt;Unfortunately the exit codes for javaws don't correspond to the codes passed to System.exit by Java code, so I had to have the test fixture communicate to the running javaws instance via socket.  Failures are transmitted by the fixture in such a way that you can't tell that the test is running in a separate process.&lt;br /&gt;&lt;br /&gt;I encapsulated the deployment config, JNLP file creation, javaws launch, and test case execution into a single class, WebStartTest, which runs a few self tests and can be extended to add whatever other tests you need to run under web start.  The JNLP is partly hard-coded to include files set up sepecifically for JNA testing, but should be trivial to change to accommodate a different project. &lt;br /&gt;&lt;br /&gt;There are probably other ways to test WebStart code, but this fit nicely with the project's existing tests, allowing its WebStart features to be tested by test methods identical to those for the rest of the project.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://jna.dev.java.net/source/browse/jna/trunk/jnalib/test/com/sun/jna/WebStartTest.java?view=markup"&gt;Full source is here&lt;/a&gt;.  This has been tested under Sun and IBM JDKs, on Windows, OSX, Solaris/sparc, and GNU/Linux.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-2079169961885507542?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='https://jna.dev.java.net/source/browse/jna/trunk/jnalib/test/com/sun/jna/WebStartTest.java?view=markup' title='WebStart unit tests'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/2079169961885507542/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=2079169961885507542' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/2079169961885507542'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/2079169961885507542'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2009/10/webstart-unit-tests.html' title='WebStart unit tests'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-4706045554381296984</id><published>2008-09-11T21:24:00.003-04:00</published><updated>2008-09-11T21:38:13.913-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jna'/><title type='text'>JNA: increasing performance with large Structures</title><content type='html'>If you're using very large structures and using them often, here's a tip that can boost performance by several orders of magnitude.  Note that you should follow this tip *only* if you really need the performance boost; otherwise you may wind up obfuscating your code.&lt;br /&gt;&lt;br /&gt;By default, when &lt;a href="http://jna.dev.java.net"&gt;JNA&lt;/a&gt; makes a native call it will copy the full contents of a Java Structure to native memory prior to the call and read it all back after the call.  If your Structure is very large, this can result in significant overhead reflecting all the fields of the Structure.  The reflection dwarfs the actual native communication time.&lt;br /&gt;&lt;br /&gt;If you're only reading or writing a single field, it's much faster (although somewhat less elegant) to use the &lt;a href="http://jna.dev.java.net/nonav/javadoc/com/sun/jna/Structure.html#readField(java.lang.String)"&gt;readField(String)&lt;/a&gt; and &lt;a href="http://jna.dev.java.net/nonav/javadoc/com/sun/jna/Structure.html#writeField(java.lang.String)"&gt;writeField(String)&lt;/a&gt; methods to access the data, while disabling the normal read and write.    Depending on the size of your structure, you may see two orders of magnitude or more improvement in the native function call time.&lt;br /&gt;&lt;br /&gt;Here's an example of performing the same operation two different ways:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;class Big extends Structure {&lt;br /&gt;    public int toNative;&lt;br /&gt;    public int fromNative;&lt;br /&gt;    &lt;font color=brown&gt;// plus lots more fields&lt;br /&gt;    // the more, the bigger the difference in performance&lt;/font&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class FastBig extends Big {&lt;br /&gt;    public void read() { }&lt;br /&gt;    public void write() { }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Big big = new Big();&lt;br /&gt;big.toNative = 42;&lt;br /&gt;lib.callMyNativeFunction(big);&lt;br /&gt;System.out.println("Got " + big.fromNative);&lt;br /&gt;&lt;br /&gt;Big fast = new FastBig();&lt;br /&gt;fast.toNative = 42;&lt;br /&gt;fast.writeField("toNative");&lt;br /&gt;lib.callMyNativeFunction(fast);&lt;br /&gt;System.out.println("Got " + fast.readField("fromNative"));&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If you wrap a loop and time these, you'll see what kind of difference it makes.  On a test structure with 25 "int" fields, the fast version reduces time by a factor of 10.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;&lt;span style="font-style:italic;"&gt;Trivia&lt;/span&gt;&lt;/span&gt;: some other "struct" implementations (e.g. &lt;a href="http://javolution.org"&gt;Javolution&lt;/a&gt;) use objects for all fields and require an explicit "write" or "set" on each.  This reduces data transfer and/or reflection overhead, at the expense of simplicity of assignment and initialization.&lt;br /&gt;&lt;br /&gt;JNA:&lt;br /&gt;s.field = 1;&lt;br /&gt;call(s.field);&lt;br /&gt;&lt;br /&gt;Javolution:&lt;br /&gt;s.field.set(1);&lt;br /&gt;call(s.field.get());&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-4706045554381296984?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://jna.dev.java.net' title='JNA: increasing performance with large Structures'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/4706045554381296984/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=4706045554381296984' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/4706045554381296984'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/4706045554381296984'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2008/09/jna-increasing-performance-with-large.html' title='JNA: increasing performance with large Structures'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-5863701983705371796</id><published>2008-09-04T11:12:00.003-04:00</published><updated>2008-09-04T11:19:49.343-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jna'/><category scheme='http://www.blogger.com/atom/ns#' term='win64'/><title type='text'>JNA win64 port</title><content type='html'>JNA now supports 64-bit windows.  The the build is a bit nonstandard, but it's automatic and reproducible.  I was hoping to use mingw-64, but that project is not yet sufficient for JNA's purposes.  Fortunately, the MS has a free 64-bit cross compiler which does work. &lt;br /&gt;&lt;br /&gt;Thanks to the fine folks at &lt;a href="http://aquafold.com"&gt;Aquafold&lt;/a&gt; (actually mostly Niels G. the CTO) for making it possible.&lt;br /&gt;&lt;br /&gt;Thanks also to T. Heller of python's ctypes for the initial glue bits.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-5863701983705371796?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='https://jna.dev.java.net' title='JNA win64 port'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/5863701983705371796/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=5863701983705371796' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/5863701983705371796'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/5863701983705371796'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2008/09/jna-win64-port.html' title='JNA win64 port'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-9055051556397732097</id><published>2008-08-10T20:26:00.006-04:00</published><updated>2008-09-25T20:53:48.312-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='overlay'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='decoration'/><category scheme='http://www.blogger.com/atom/ns#' term='validation'/><title type='text'>Decorator Update</title><content type='html'>Thanks to some input from a few users, the AbstractComponentDecorator has been updated to fix a few visual and behavior artifacts.&lt;br /&gt;&lt;br /&gt;Get the latest from &lt;a href="http://furbelow.svn.sourceforge.net/viewvc/furbelow/trunk/src/furbelow/AbstractComponentDecorator.java?view=log"&gt;Sourceforge SVN&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The decorator component allows you to apply arbitrary decorations to a component without affecting its layout or (necessarily) response to events.&lt;br /&gt;&lt;br /&gt;This blog has many different examples of usage beyond simple decorations, including drag images and per-component input blocking.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-9055051556397732097?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://abbot.sf.net/demo/DecoratorDemo.jnlp' title='Decorator Update'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/9055051556397732097/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=9055051556397732097' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/9055051556397732097'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/9055051556397732097'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2008/08/decorator-update.html' title='Decorator Update'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-1137836814801425071</id><published>2008-01-19T17:57:00.000-05:00</published><updated>2008-01-19T18:29:16.011-05:00</updated><title type='text'>Embedding DLL File Version Information with mingw32/cygwin</title><content type='html'>If you look at the properties window for a DLL file in Windows, you'll see a few bits about the file version and copyright info.  I couldn't find any information on the web about how to embed that information into a DLL.  Not terribly surprising, since it's probably something MSVC inserts automatically, so why would any one need to know how it's done?&lt;br /&gt;&lt;br /&gt;Well, I'm building a DLL with cygwin/mingw32, so I &lt;span style="font-style: italic;"&gt;do&lt;/span&gt; need to know how it's done.  Fortunately, Mumit Khan, author of mingw32, wrote a utility called "windres", which compiles w32 resources for use in an executable or DLL.   It'll compile directly to object format, which you can simply add to your final link.&lt;br /&gt;&lt;br /&gt;For example:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt; % windres -i version.rc -o version.o&lt;/span&gt;&lt;br /&gt;So the only thing I needed to figure out was the version resource format (I really didn't want to go grok the resource spec and write it from scratch).  Luckily, Java's main DLL (java.dll, what else) has version information in it, and windres can also conveniently dump an exe or dll back into text format.&lt;br /&gt;&lt;br /&gt;The java.dll resource info looks like this:&lt;br /&gt;&lt;pre&gt;&lt;span style="font-family:courier new;"&gt;&lt;br /&gt;% windres -i java.dll -O rc&lt;br /&gt;&lt;br /&gt;// Type: version&lt;br /&gt;// Name: 1&lt;br /&gt;LANGUAGE 0, 0&lt;br /&gt;1 VERSIONINFO&lt;br /&gt; FILEVERSION 6, 0, 30, 5&lt;br /&gt; PRODUCTVERSION 6, 0, 30, 5&lt;br /&gt; FILEFLAGSMASK 0x3f&lt;br /&gt; FILEOS 0x4&lt;br /&gt; FILETYPE 0x2&lt;br /&gt;BEGIN&lt;br /&gt;  BLOCK "StringFileInfo"&lt;br /&gt;  BEGIN&lt;br /&gt;    BLOCK "000004b0"&lt;br /&gt;    BEGIN&lt;br /&gt;      VALUE "CompanyName", "Sun Microsystems, Inc."&lt;br /&gt;      VALUE "FileDescription", "Java(TM) Platform SE binary"&lt;br /&gt;      VALUE "FileVersion", "6.0.30.5"&lt;br /&gt;      VALUE "Full Version", "1.6.0_03-b05"&lt;br /&gt;      VALUE "InternalName", "java"&lt;br /&gt;      VALUE "LegalCopyright", "Copyright \251 2004"&lt;br /&gt;      VALUE "OriginalFilename", "java.dll"&lt;br /&gt;      VALUE "ProductName", "Java(TM) Platform SE 6 U3"&lt;br /&gt;      VALUE "ProductVersion", "6.0.30.5" &lt;br /&gt;  END &lt;br /&gt;END&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;With just a few tweaks to do a regexp replace on the new template and include the new obj in my build, my released dll now has visible version information (and in this case I'm perfectly happy having absolutely no clue what any of the resource file text actually means :).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_ACEoHFt4yoQ/R5KFh5OJODI/AAAAAAAAABo/hpDbVyPNvCY/s1600-h/snap.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://1.bp.blogspot.com/_ACEoHFt4yoQ/R5KFh5OJODI/AAAAAAAAABo/hpDbVyPNvCY/s320/snap.png" alt="" id="BLOGGER_PHOTO_ID_5157331340634961970" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-1137836814801425071?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/1137836814801425071/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=1137836814801425071' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/1137836814801425071'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/1137836814801425071'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2008/01/embedding-dll-file-version-information.html' title='Embedding DLL File Version Information with mingw32/cygwin'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_ACEoHFt4yoQ/R5KFh5OJODI/AAAAAAAAABo/hpDbVyPNvCY/s72-c/snap.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-8266707828064521486</id><published>2007-09-12T11:52:00.000-04:00</published><updated>2007-10-13T11:46:52.435-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='nt service'/><category scheme='http://www.blogger.com/atom/ns#' term='jna'/><title type='text'>Run your Java program as an NT service</title><content type='html'>Thomas Boerkel has contributed some mappings and code to the &lt;a href=http://jna.dev.java.net&gt;JNA project&lt;/a&gt; to facilitate access to Windows' user accounts, registry, and NT services.  Included is an abstract class which is capable of installing, uninstalling, and running itself as an NT service.  What sets this little gem apart from most available wrappers is that you don't need a Windows executable stub; the complete Windows service API is available to your Java program, including all system events and callbacks.&lt;br /&gt;&lt;br /&gt;You can &lt;a href="http://jna.dev.java.net/source/browse/jna/trunk/jnalib/contrib/ntservice/src/jnacontrib/win32"&gt;browse the source here&lt;/a&gt;.  A NetBeans project is included.&lt;br /&gt;&lt;br /&gt;I was working on an abstract service class as well but Thomas beat me to completion.  Great work, Thomas!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt;: Make sure your classpath and classes are accessible by the local system account, or the service won't be able to start.  To see if this is the issue, you can temporarily change the service to start using &lt;i&gt;your&lt;/i&gt; user identity; if it works, then the problem is that the local system account doesn't have access.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-8266707828064521486?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://jna.dev.java.net/source/browse/jna/trunk/jnalib/contrib/ntservice/' title='Run your Java program as an NT service'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/8266707828064521486/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=8266707828064521486' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/8266707828064521486'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/8266707828064521486'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2007/09/run-your-java-program-as-nt-service.html' title='Run your Java program as an NT service'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-4897177243179380781</id><published>2007-09-06T14:33:00.000-04:00</published><updated>2007-09-07T20:41:30.557-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jni'/><title type='text'>Is final not final, or just not the final I wanted?</title><content type='html'>While adding some VM crash protection to &lt;a href="http://jna.dev.java.net/"&gt;JNA&lt;/a&gt;, I ran into this situation:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;public class Pointer {&lt;br /&gt;    public static final int SIZE = Native.POINTER_SIZE;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Native {&lt;br /&gt;    public static final int POINTER_SIZE;&lt;br /&gt;    static {&lt;br /&gt;        initIDs();&lt;br /&gt;        POINTER_SIZE = pointerSize();&lt;br /&gt;    }&lt;br /&gt;    private static native int pointerSize();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;I had just moved the native initialization code from Pointer to Native.  All the native methods were private, so the only changes were to update those two classes and rename the native methods.  But when I reran the test suite, I started getting a host of errors.&lt;br /&gt;&lt;p&gt;&lt;br /&gt;There errors were all caused by Pointer.SIZE having a zero value.  I figured that since Pointer.SIZE was final, the value it was assigned from must also have been zero.  A quick inspection on Native.POINTER_SIZE, however, showed that it was propertly initialized and non-zero.  How, then, could Pointer.SIZE be zero if Native.POINTER_SIZE was not?&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;Anyone who has done any JNI is probably aware that Java access modifiers don't mean anything to native code.  My first thought was that perhaps "final" modifiers don't mean anything either.  Only problem is that I was never touching that Pointer.SIZE field in native code.  Never even accessed it.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;Scanning though the native initialization code, I noticed that, among a host of standard JRE classes, a reference to the Pointer class was being created.  This turns out to be the cause of my problem: during Native class initialization, the Pointer class was loaded from JNI.  When the pointer final field was initialized, it got the value from a not-yet-initialized Native class.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;Turns out, this can be duplicated with pure Java code:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;public class LoadTest {&lt;br /&gt;    public static final int VALUE;&lt;br /&gt;    static {&lt;br /&gt;        System.out.println("value=" + Secondary.VALUE);&lt;br /&gt;        VALUE = 1;&lt;br /&gt;        System.out.println("value=" + Secondary.VALUE);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class Secondary {&lt;br /&gt;    public static final int VALUE = LoadTest.VALUE;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;When loaded, the class prints "value=0" twice, so final &lt;em&gt;is&lt;/em&gt; final, just not the value I intended.  Here, the problem is more apparent because the class dependency is clearly visible, which wasn't the case with the JNI initialization.&lt;br /&gt;&lt;p&gt;&lt;br /&gt;Maybe I'll call this antipattern something curiously obscure, like "Law of the Polygamist's Second Wife".&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-4897177243179380781?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/4897177243179380781/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=4897177243179380781' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/4897177243179380781'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/4897177243179380781'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2007/09/is-final-not-final-or-just-not-final-i.html' title='Is final not final, or just not the final I wanted?'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-8153498721118518720</id><published>2007-07-23T15:14:00.001-04:00</published><updated>2007-07-23T15:16:22.103-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Demo sources</title><content type='html'>All source for demos in previous blog entries is now available via SVN on SourceForge.  The project page is &lt;a href=http://sf.net/projects/furbelow&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;All sources are provided under the LGPL.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-8153498721118518720?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://sf.net/projects/furbelow' title='Demo sources'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/8153498721118518720/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=8153498721118518720' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/8153498721118518720'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/8153498721118518720'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2007/07/demo-sources.html' title='Demo sources'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-7016110015218002181</id><published>2007-06-27T14:05:00.001-04:00</published><updated>2009-01-11T08:54:36.850-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='transparent window'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='jna'/><category scheme='http://www.blogger.com/atom/ns#' term='shaped window'/><title type='text'>Improved Window Shaping</title><content type='html'>Thanks to contributions from Olivier Chafik and Chris Deckers, the overhead for setting a window shape mask has been reduced by about two orders of magnitude.  In this case, the balloon tip windows no longer have such a noticeable delay before showing.&lt;br /&gt;&lt;br /&gt;Linux users should also have fewer issues with this version; some of the library setup previously required is now taken care of automatically by the JNA library.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;a href="http://jna.dev.java.net/demo/BalloonManagerDemo.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-7016110015218002181?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='https://jna.dev.java.net/demo/BalloonManagerDemo.jnlp' title='Improved Window Shaping'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/7016110015218002181/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=7016110015218002181' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/7016110015218002181'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/7016110015218002181'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2007/06/improved-window-shaping.html' title='Improved Window Shaping'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-1637499618515282496</id><published>2007-05-12T00:10:00.001-04:00</published><updated>2009-01-11T08:54:52.953-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='transparent window'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='jna'/><category scheme='http://www.blogger.com/atom/ns#' term='shaped window'/><title type='text'>Easier Alpha Masks</title><content type='html'>The per-pixel alpha masking in &lt;a href="http://rabbit-hole.blogspot.com/2007/04/alpha-mask-transparency.html"&gt;this post&lt;/a&gt; has been codified into a simple API.&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;br /&gt;WindowUtils.setWindowTransparent(Window w, boolean transparent);&lt;br /&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;This effectively gives the window a transparent background.  The alpha levels of the window's contents are preserved.&lt;br /&gt;&lt;br /&gt;The &lt;a href="https://jna.dev.java.net/svn/jna/trunk/src/com/sun/jna/examples/AlphaMaskDemo2.java"&gt;demo&lt;/a&gt; is similar to the previous one but adds a few standard components to the mix.&lt;br /&gt;&lt;br /&gt;&lt;a href=http://abbot.sf.net/demo/AlphaMaskDemo2.jnlp&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;UPDATE&lt;/b&gt; If you have a linux system and this for some reason doesn't work, please post a comment to that effect, or post a message to users@jna.dev.java.net so we can ensure this works reliably across all linux systems (64-bit is in the works).&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-1637499618515282496?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='https://jna.dev.java.net' title='Easier Alpha Masks'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/1637499618515282496/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=1637499618515282496' title='26 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/1637499618515282496'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/1637499618515282496'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2007/05/easier-alpha-masks.html' title='Easier Alpha Masks'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>26</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-2592411937817019029</id><published>2007-04-25T17:19:00.001-04:00</published><updated>2009-01-11T08:56:46.201-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='drag n drop'/><category scheme='http://www.blogger.com/atom/ns#' term='drag image'/><title type='text'>Improved Drag Images</title><content type='html'>A while back I posted some &lt;a href=http://rabbit-hole.blogspot.com/2006/08/swing-drag-images-improved.html&gt;drag/drop handling code&lt;/a&gt; that included drag images that were closer to real drag image support, but were restricted to only showing up on windows belonging to the same VM.&lt;br /&gt;&lt;br /&gt;Now, thanks to the window masking and transparency utilities provided by &lt;a href=http://jna.dev.java.net&gt;JNA&lt;/a&gt;, the drag images can escape the bounds of java windows.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_ACEoHFt4yoQ/Ri_IbO2u6-I/AAAAAAAAABg/_3lDWf5R2to/s1600-h/snap.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_ACEoHFt4yoQ/Ri_IbO2u6-I/AAAAAAAAABg/_3lDWf5R2to/s200/snap.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5057481276730043362" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href=http://abbot.sf.net/demo/DnDDemo.jnlp&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This wasn't quite as easy as expected, even with the shaped, transparent windows already taken care of.  &lt;br /&gt;&lt;br /&gt;First, I had intended to use Swing's built-in drag support, given that Shannon Hickey has put so much work into improving it.  Unfortunately, that built-in support, while making some operations very easy, makes others impossible.  Namely, drag image support requires updating the image's location in response to cursor motion.  While it's technically possible to listen for motion outside the drag operation using a global &lt;code&gt;DragSource&lt;/code&gt; listener, that's a hacky workaround.  So instead, I fall back to raw DnD with &lt;code&gt;DragGestureListener&lt;/code&gt;, &lt;code&gt;DragSourceListener&lt;/code&gt;, et al.  These provide the necessary hooks to move the drag image, and I already have some abstract base classes that provide a basic, customizable implementation.&lt;br /&gt;&lt;br /&gt;Next, I uncovered a bug in the JNA example code converting an Icon into a region mask.  It turns out that a &lt;code&gt;BufferedImage&lt;/code&gt; of type &lt;code&gt;TYPE_BYTE_BINARY&lt;/code&gt; applies something like a 50% luminance threshold, when what I really wanted was a zero/non-zero threshold based on the alpha component of the icon's pixels.  I tried writing my own binary &lt;code&gt;Composite&lt;/code&gt; to do just that, but wound up with errors downstream of the actual composite operation.  Fortunately, &lt;code&gt;BufferedImage.getAlphaRaster&lt;/code&gt; on a standard ARGB image gives me pretty much what I want.&lt;br /&gt;&lt;br /&gt;Finally, once I instantiated a window &lt;em&gt;under&lt;/em&gt; the current drag operation, I could no longer drop anywhere.  Which makes sense when you consider there is now a window in between the cursor and any place you'd like to drop.  I almost gave up thinking this was an intractable problem given that DnD is so tightly wound up with native implementations, but I'm always game for one more variation.  Remember that a window region can have holes in it, where events pass through to whatever lies beneath.  Well, what if we put a hole right where the cursor is?  Since it's directly under the cursor, it's not really apparent, but it allows events to pass through to the underlying drop target.  We only need a single pixel, because that's all the DnD system cares about (I initially made it much bigger just so I could be sure I put it in the right place).  Voila!  It works.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-2592411937817019029?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://abbot.sf.net/demo/DnDDemo.jnlp' title='Improved Drag Images'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/2592411937817019029/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=2592411937817019029' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/2592411937817019029'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/2592411937817019029'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2007/04/improved-drag-images.html' title='Improved Drag Images'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_ACEoHFt4yoQ/Ri_IbO2u6-I/AAAAAAAAABg/_3lDWf5R2to/s72-c/snap.png' height='72' width='72'/><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-1627888912081409554</id><published>2007-04-18T11:45:00.001-04:00</published><updated>2009-01-11T08:56:07.530-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='transparent window'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='jna'/><category scheme='http://www.blogger.com/atom/ns#' term='shaped window'/><title type='text'>Alpha-mask Transparency for a Window</title><content type='html'>Finally got per-pixel alpha mask working on w32.  The included image isn't the greatest for showing an antialiased edge (it does have one), but if you have something with a drop shadow lying about, just drop it on the demo window and that'll be used instead.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt; This demo works on Windows, OSX and Linux (Linux requires JRE1.5+). &lt;br /&gt;&lt;br /&gt;&lt;a href=http://abbot.sf.net/demo/AlphaMaskDemo.jnlp&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt; Here's another image to drop on the demo window, which better demonstrates the alpha blending, in case you don't have any handy&lt;br /&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_ACEoHFt4yoQ/RiZ3oGV6YRI/AAAAAAAAAAw/dOzUVDxS2O8/s320/toucan.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5054859162550952210" /&gt;&lt;br /&gt;&lt;br /&gt;I've got some working X11 code, too, but I need to figure out a decent API to make it happen.  OSX lets you just set the window background pixel transparent and then everything you paint in the window is automatically composited with whatever alpha mask you paint with.  That could probably work with Swing as well, but you have to magically drop anything with a default background.  OSX does this by checking for a magic UIResource color; anything painted with that color is fully transparent.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt; Source is available on BRANCH_V2 from &lt;a href=http://jna.dev.java.net&gt;JNA&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;I realize I may be an utter dolt, but I find it really hard to follow Microsoft APIs.  I've worked with Qt, X11/Xt, and Mac, each of which has its peculiarities of architecture, but those have a consistency (might I say &lt;i&gt;design&lt;/i&gt;) that spans more than two or three functions.&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-1627888912081409554?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://abbot.sf.net/demo/AlphaMaskDemo.jnlp' title='Alpha-mask Transparency for a Window'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/1627888912081409554/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=1627888912081409554' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/1627888912081409554'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/1627888912081409554'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2007/04/alpha-mask-transparency.html' title='Alpha-mask Transparency for a Window'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_ACEoHFt4yoQ/RiZ3oGV6YRI/AAAAAAAAAAw/dOzUVDxS2O8/s72-c/toucan.png' height='72' width='72'/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-8299407432599570369</id><published>2007-04-13T22:36:00.000-04:00</published><updated>2007-04-20T17:05:45.177-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='x11'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='jna'/><title type='text'>Java Transparent Windows (X11 update)</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_ACEoHFt4yoQ/RiA--mV6YQI/AAAAAAAAAAo/OEhrO8KKcig/s1600-h/snap.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_ACEoHFt4yoQ/RiA--mV6YQI/AAAAAAAAAAo/OEhrO8KKcig/s320/snap.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5053108027074961666" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href=https://jna.dev.java.net/demo/ShapedWindowDemo.jnlp&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update:&lt;/b&gt; If your X11 setup supports it (XFCE, metacity + xcompmgr, compiz, etc), the JNA shaped window demo now has window transparency.&lt;br /&gt;&lt;br /&gt;Thanks to Romain Guy for some initial testing and VMWare for making experimental linux installations easier.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update:&lt;/b&gt; BTW, the linux setup I used was ubuntu (after installing xubuntu packages to get xfce; should have started with xubuntu).  Note that the transparency is similar to that on OS X and w32, i.e. a single alpha level for the entire window.  Next up is to apply a per-pixel alpha mask, if I can scrounge up some samples of how to do it on each platform.&lt;br /&gt;&lt;br /&gt;I added some features to JNA to facilitate working with the X11 visual lookups (it returns an array of structures), so a few more files got changed than just the examples jar.  If you want the latest and greatest, you'll need to check out from CVS and do 'ant examples-jar'.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-8299407432599570369?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='https://jna.dev.java.net' title='Java Transparent Windows (X11 update)'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/8299407432599570369/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=8299407432599570369' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/8299407432599570369'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/8299407432599570369'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2007/04/java-transparent-windows-x11-update.html' title='Java Transparent Windows (X11 update)'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_ACEoHFt4yoQ/RiA--mV6YQI/AAAAAAAAAAo/OEhrO8KKcig/s72-c/snap.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-615126663446675489</id><published>2007-04-05T12:40:00.000-04:00</published><updated>2007-04-05T12:47:30.178-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><title type='text'>Another thing that sucks about applets...</title><content type='html'>You can't easily make the background color match the web page.  You'd think that Applet.setBackground() would just Do the Right Thing.&lt;br /&gt;&lt;br /&gt;You don't necessarily want to hard-code a value into the applet, but even if you set a value via javascript/livescript, you've got to do extra work to make it propagate down the hierarchy.&lt;br /&gt;&lt;br /&gt;This is also applicable to Swing; you can't set a background color on a parent component and have it propagate to descendants.  The first non-opaque component you run into is going to get its background from the UIManager.  I really don't want to have to figure out which UIManager colors I have to override just to set the bloody background color of my app.&lt;br /&gt;&lt;br /&gt;Maybe the new JSR 296 will do something about this, maybe not, but it'd be nice to have something akin to the old X toolkit's resource specifiction:&lt;br /&gt;&lt;br /&gt;*.background: red&lt;br /&gt;*.JCheckBox.background: red&lt;br /&gt;JApplet.*.background: red&lt;br /&gt;named-applet.*.background: red&lt;br /&gt;&lt;br /&gt;The idea is that you can isolate or aggregate just about anything in the hierarchy by name or by class, and apply a resource setting to it (I guess they're calling it "injection" these days).&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-615126663446675489?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/615126663446675489/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=615126663446675489' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/615126663446675489'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/615126663446675489'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2007/04/another-thing-that-sucks-about-applets.html' title='Another thing that sucks about applets...'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-2692913248109551533</id><published>2007-04-05T11:26:00.000-04:00</published><updated>2007-04-06T08:33:33.566-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><title type='text'>Animated Per-panel Options Pane</title><content type='html'>Here's another example in the search for an unobtrusive property editor.  This one looks a little like the Google Maps thumbnail navigator (at least with respect to its activator).&lt;p&gt;&lt;br /&gt;&lt;b&gt;Contents&lt;/b&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="#toggle"&gt;Toggle Button Positioning&lt;/a&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="#animation"&gt;Layout Animation&lt;/a&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;applet code="com.oculustech.widgets.OptionsPanel$Demo" codebase="http://abbot.sf.net/demo" archive="OptionsPanelDemo.jar" width=300 height=200&gt;&lt;br /&gt;&lt;/applet&gt;&lt;p&gt;&lt;br /&gt;The API is simple:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;// This component's preferred size will normally be that of its content&lt;br /&gt;container.add(new OptionsPanel(content, options));&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The purpose of this component is to unobtrusively associate a nontrivial set of options with some content whose display is typically larger than the options themselves.  The options display is kept close the the content it modifies, which avoids the user having to go search through menus or play dialog positioning games.  Since the options are hidden by default, you also don't have them always using up screen real estate.  This sort of thing is often wedged into a docking solution, which is inappropriate if the information doesn't need to be visible at all times.  A docking solution also insists on allocating the whole edge of the dock, so if you don't have other junk that has to be in there, you're wasting a lot of space.&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;OptionsPane&lt;/code&gt; component wraps the content in the center of a &lt;code&gt;BorderLayout&lt;/code&gt; and keeps the &lt;code&gt;PAGE_END&lt;/code&gt; slot available for the options themselves.  &lt;br /&gt;&lt;br /&gt;&lt;a name="toggle"&gt;&lt;h2&gt;Toggle Button Positioning&lt;/h2&gt;&lt;/a&gt;&lt;br /&gt;Notice that the toggle button isn't affected by the border layout (or any other content, for that matter).  The toggle button has been placed in a layer above the rest of the content, so it's always visible and doesn't contribute to any layout complexity.&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;JRootPane root = SwingUtilities.getRootPane(this);&lt;br /&gt;if (root != null) {&lt;br /&gt;    layered = root.getLayeredPane();&lt;br /&gt;    if (layered != null) {&lt;br /&gt;        int layer = JLayeredPane.DEFAULT_LAYER.intValue();&lt;br /&gt;        layered.add(toggle, new Integer(layer + 1));&lt;br /&gt;        updateButtonLocation();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;  We also want to update the button location whenever the &lt;code&gt;OptionsPane&lt;/code&gt; moves.  While we could use a &lt;code&gt;ComponentListener&lt;/code&gt;, that would update the button's location &lt;em&gt;after&lt;/em&gt; the &lt;code&gt;OptionsPane&lt;/code&gt; was moved/resized, resulting in some jerkiness to the display.  So instead, the button gets moved at the same time as the parent component:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;public void setBounds(int x, int y, int w, int h) {&lt;br /&gt;    super.setBounds(x, y, w, h);&lt;br /&gt;    updateButtonLocation();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;a name="animation"&gt;&lt;h2&gt;Layout Animation&lt;/h2&gt;&lt;/a&gt;&lt;br /&gt;Rather than instantly making the options visible and doing a single relayout, we use a &lt;code&gt;javax.swing.Timer&lt;/code&gt; to gradually grow the options from the lower right corner into its final position.  The bottom slot of &lt;code&gt;BorderLayout&lt;/code&gt; will respect a component's height, but stretch or squash the component to the pane's current width.  So changing the height gradually will work, but changing the width will have no effect (the component simply appears to rise from the bottom of the panel, rather than from the lower right).&lt;br /&gt;&lt;br /&gt;Rather than changing the width, we use an empty border in a JPanel wrapped around the options component.  By changing the width of the left border, the options appear to grow from the left to the right.&lt;br /&gt;&lt;br /&gt;The animation increment is simple (move half the remaining distance), which is pretty good for most purposes.  This is the &lt;code&gt;Timer&lt;/code&gt; configuration in response to the expansion button click:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;final int INTERVAL = 50;&lt;br /&gt;final int FRACTION = 2;&lt;br /&gt;final boolean expanded = isExpanded;&lt;br /&gt;Timer timer = new Timer(INTERVAL, new ActionListener() {&lt;br /&gt;    public void actionPerformed(ActionEvent e) {&lt;br /&gt;        Insets insets = optionsBox.getInsets();&lt;br /&gt;        Insets delta = targetInsets(expanded);&lt;br /&gt;        Dimension targetSize = targetSize(expanded);&lt;br /&gt;        int dx = (delta.left - insets.left)/FRACTION;&lt;br /&gt;        if (dx != 0) {&lt;br /&gt;            insets.left += dx;&lt;br /&gt;            optionsBox.setBorder(new EmptyBorder(insets));&lt;br /&gt;            optionsBox.revalidate();&lt;br /&gt;            repaint();&lt;br /&gt;        }&lt;br /&gt;        int dy = (targetSize.height - optionsBox.getHeight())/FRACTION;&lt;br /&gt;        if (dy != 0) {&lt;br /&gt;            Dimension size = optionsBox.getSize();&lt;br /&gt;            size.height += dy;&lt;br /&gt;            optionsBox.setPreferredSize(size);&lt;br /&gt;            optionsBox.revalidate();&lt;br /&gt;            revalidate();&lt;br /&gt;            repaint();&lt;br /&gt;        }&lt;br /&gt;        if (Math.abs(dx) &lt;= 1 &amp;&amp; Math.abs(dy) &lt;= 1) {&lt;br /&gt;            ((Timer)e.getSource()).stop();&lt;br /&gt;            toggle.setEnabled(true);&lt;br /&gt;            if (!expanded) {&lt;br /&gt;                remove(optionsBox);&lt;br /&gt;            }&lt;br /&gt;            revalidate();&lt;br /&gt;            repaint();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;});&lt;br /&gt;timer.setRepeats(true);&lt;br /&gt;timer.start();&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;You can play around with the FRACTION and INTERVAL constants to see the effect it has on the animation.&lt;br /&gt;&lt;br /&gt;Source is available in the applet jar file in the article link.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-2692913248109551533?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://abbot.sf.net/demo/OptionsPanelDemo.jar' title='Animated Per-panel Options Pane'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/2692913248109551533/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=2692913248109551533' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/2692913248109551533'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/2692913248109551533'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2007/04/animated-per-panel-options-pane.html' title='Animated Per-panel Options Pane'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-4051616568447784626</id><published>2007-04-02T12:19:00.000-04:00</published><updated>2007-04-21T09:19:25.969-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='jna'/><title type='text'>Speech Bubble Update (with Drop Shadow)</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_ACEoHFt4yoQ/RhEuWdRim7I/AAAAAAAAAAg/AObssNPc5Rg/s1600-h/snap.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_ACEoHFt4yoQ/RhEuWdRim7I/AAAAAAAAAAg/AObssNPc5Rg/s320/snap.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5048867620609235890" /&gt;&lt;/a&gt;&lt;br /&gt;I added a drop shadow to the speech bubble.  You don't need it on OSX (OSX provides one automatically), and I don't have compiz or similar compositing window manager installed on linux at the moment (although if anyone is willing to submit/test the corresponding code, I'm happy to include it).&lt;br /&gt;&lt;br /&gt;Nothing fancy, just black with 50% alpha.  The shadow mask is just the original mask, sheared, scaled, and offset.  The drop shadow would probably look nicer with a blurred edge, but that likely requires a variable alpha setting for the window (although you might be able to fake it by varying the edge color).  Applying per-pixel alpha masks prior to Windows Vista seems to be non-trivial (this example uses a single alpha value for the entire window).&lt;br /&gt;&lt;br /&gt;The JNA function definitions are trivial:&lt;br&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;    int GetWindowLongA(Pointer hWnd, int nIndex);&lt;br /&gt;    int SetWindowLongA(Pointer hWnd, int nIndex, int dwNewLong);&lt;br /&gt;    boolean SetLayeredWindowAttributes(Pointer hwnd, int crKey, &lt;br /&gt;                                       byte bAlpha, int dwFlags);&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;As is the actual usage:&lt;br&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;    Pointer hWnd = getHWnd(w);&lt;br /&gt;    User32 user = User32.INSTANCE;&lt;br /&gt;    int flags = user.GetWindowLongA(hWnd, User32.GWL_EXSTYLE) | User32.WS_EX_LAYERED;&lt;br /&gt;    user.SetWindowLongA(hWnd, User32.GWL_EXSTYLE, flags);&lt;br /&gt;    user.SetLayeredWindowAttributes(hWnd, 0, (byte)(255*alpha), User32.LWA_ALPHA);&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The only real trick is that DirectDraw must be disabled in order to get the transparency effect and avoid leaving behind painting artifacts.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;java -Dsun.java2d.noddraw=true ...&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;NOTE: thanks to l2fprod for the hint and the original round, transparent clock!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update:&lt;/b&gt; Demo is now available from the JNA homepage.&lt;br /&gt;&lt;center&gt;&lt;a href=http://jna.dev.java.net/demo/BalloonManagerDemo.jnlp&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-4051616568447784626?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://jna.dev.java.net/demo/BalloonManagerDemo.jnlp' title='Speech Bubble Update (with Drop Shadow)'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/4051616568447784626/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=4051616568447784626' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/4051616568447784626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/4051616568447784626'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2007/04/speech-bubble-update-drop-shadow.html' title='Speech Bubble Update (with Drop Shadow)'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_ACEoHFt4yoQ/RhEuWdRim7I/AAAAAAAAAAg/AObssNPc5Rg/s72-c/snap.png' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-2732325499952649565</id><published>2007-03-28T18:45:00.000-04:00</published><updated>2007-04-05T12:03:14.743-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='jna'/><title type='text'>Give your application a speech bubble</title><content type='html'>As a result of an argument about the best way to present meta-information about complex application objects presented to the user, I had to come up with some concrete alternatives.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_ACEoHFt4yoQ/RgrxJ9Rim6I/AAAAAAAAAAU/henGlrlByuI/s1600-h/snap.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://2.bp.blogspot.com/_ACEoHFt4yoQ/RgrxJ9Rim6I/AAAAAAAAAAU/henGlrlByuI/s320/snap.png" alt="" id="BLOGGER_PHOTO_ID_5047111485791312802" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The problem is that for a given item in the application (in this case an SVN-based revision control client that aggregates and presents large amounts of meta-information about the repository), there may exist information that just doesn't fit in the display.  You want to provide the user with a route to the desired information with as few inputs as possible.&lt;br /&gt;&lt;br /&gt;Tooltips is one alternative, but aren't always appropriate.  While you can set (globally) the delay before appearance and the time before dismissal, dismissing them is implicit.  In my application, I need the meta information to be displayed or hidden explicitly, so that rules out tooltips (at least without substantial modification of the tooltip manager).&lt;br /&gt;&lt;br /&gt;A dedicated "inspector" window is another alternative, but kind of heavyweight.  We definitely don't want a component that can be activated, since that introduces focus issues.  I also didn't want to make it appear to the user that there was a generic "container" that kept resizing and moving, as if it were a tool palette that couldn't decide on its purpose.  At a minimum the window has to be undecorated and pretty much behave like a tooltip.&lt;br /&gt;&lt;br /&gt;What I really wanted was something in between the two.  A simple Popup with a unique appearance which could be shown or hidden at will.  Apple introduced something like this as Balloon Help years ago, although I think the main problem was that they didn't get the trigger quite right.  One reason a lot of expert users don't like tooltips (or balloon help) is that they show up when you don't expect it and you have to wave the mouse to make them go away (usually causing another one to be triggered).&lt;br /&gt;&lt;br /&gt;Google maps does a nice job with their balloon tips.  The trigger is obvious, and the tip itself provides a close button (and now I notice a maximize button as well).&lt;br /&gt;&lt;br /&gt;So I started out with Popups via PopupFactory and wound up with another demonstration of non-rectangular clipping, brought to you by the JNA project.  The API is pretty simple:&lt;br&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;Component myComponent;&lt;br /&gt;Component myBubble;&lt;br /&gt;Point where; // location relative to myComponent&lt;br /&gt;final Popup popup = BalloonManager.getBalloon(myComponent, myBubble, where.x, where.y);&lt;br /&gt;popup.show();&lt;br /&gt;// Hide the popup on mouse clicks&lt;br /&gt;myBubble.addMouseListener(new MouseAdapter() {&lt;br /&gt;    public void mouseClicked(MouseEvent e) {&lt;br /&gt;        popup.hide();&lt;br /&gt;    }&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;The balloon is a simple rounded rectangle with a triangle stuck to the bottom, with the corresponding &lt;code&gt;Area&lt;/code&gt; used as the window mask.&lt;br /&gt;&lt;br /&gt;I originally tried using PopupFactory directly, but it doesn't provide enough control over the intermediate components (the ancestors of the content that you don't really see; there's a pesky medium-weight popup that uses java.awt.Panel, which can't be easily masked).  This implementation uses a window for any popup, regardless of whether its fully contained within the parent component.&lt;br /&gt;&lt;br /&gt;Drop shadows would be nice (maybe someone wants to contribute some compositing code?).&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/BalloonManagerDemo.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-2732325499952649565?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://abbot.sf.net/demo/BalloonManagerDemo.jnlp' title='Give your application a speech bubble'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/2732325499952649565/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=2732325499952649565' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/2732325499952649565'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/2732325499952649565'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2007/03/give-your-application-speech-bubble.html' title='Give your application a speech bubble'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_ACEoHFt4yoQ/RgrxJ9Rim6I/AAAAAAAAAAU/henGlrlByuI/s72-c/snap.png' height='72' width='72'/><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-8091152284081931370</id><published>2007-03-13T12:41:00.000-04:00</published><updated>2007-04-05T12:04:08.590-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jni'/><title type='text'>Building w32 JNI DLLs with gcc</title><content type='html'>After a bit of trial and error, I got JNA's native shared library to build (and run, and run tests) using gcc.   Unfortunately the available documentation didn't quite work for me (might be a problem with my cygwin installation, but I wasn't the only one with an issue).&lt;br /&gt;&lt;br /&gt;gcc-mingw is a version of cygwin gcc that can compile directly to the w32 api, without any of the cygwin emulation layer.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;% gcc -dumpspecs | sed s%dllcrt2%/lib/mingw/dllcrt2%g &gt; specs.new&lt;br /&gt;% gcc -mno-cygwin -D_REENTRANT -D_GNU_SOURCE \&lt;br /&gt;      -D__int64="long long" -D_JNI_IMPLEMENTATION \&lt;br /&gt;      &lt;jni-sources&gt; -o &lt;jni-library-name&gt;&lt;br /&gt;      -shared -Wl,--kill-at -specs specs.new -L /lib/mingw&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;-mno-cygwin&lt;/code&gt; flag tells the compiler to run in mingw mode, i.e. omit all the cygwin unix compatibility layer and compile directly against the w32 api.  &lt;br /&gt;&lt;br /&gt;GCC's built-in 64-bit type is &lt;code&gt;long long&lt;/code&gt;, so we map the type used in the JNI headers, &lt;code&gt;__int64&lt;/code&gt;, to that type.  Defining &lt;code&gt;_JNI_IMPLEMENTATION&lt;/code&gt; ensures the JNI implementation exports its native method declarations.&lt;br /&gt;&lt;br /&gt;Defining _REENTRANT and _GNU_SOURCE cause certain additional declarations of C library functions to be included.  If you've never heard of them, most likely you don't need them. &lt;br /&gt;&lt;br /&gt;The &lt;code&gt;--kill-at&lt;/code&gt; flag to the linker ensures all symbols are exported undecorated, i.e. &lt;code&gt;_my_func&lt;/code&gt; instead of &lt;code&gt;_my_func@NN&lt;/code&gt;, which is the default when a function is declared as &lt;code&gt;__stdcall&lt;/code&gt;.  You could also use &lt;code&gt;--add-stdcall-alias&lt;/code&gt;, which includes both versions of the symbol.&lt;br /&gt;&lt;br /&gt;The GCC specs need tweaking or gcc-mingw doesn't find its initialization code for dlls, &lt;code&gt;dllcrt2.o&lt;/code&gt;.  We also have to nudge gcc-mingw to find the mingw libraries.  Normally this is taken care of automatically by gcc, but for some reason my installation of cygwin gcc wouldn't find them, and no amount of -L or explicit object linkage would fix it.&lt;br /&gt;&lt;br /&gt;Anyhow, with that out of the way, you don't have to have Microsoft's tools to build JNA or any other JNI library on w32.&lt;br /&gt;&lt;br /&gt;I have noted that GCC apparently handles floating point a little differently, since the JNA tests that use FP return values/arguments are failing with the GCC-build dll.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-8091152284081931370?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/8091152284081931370/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=8091152284081931370' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/8091152284081931370'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/8091152284081931370'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2007/03/building-w32-jni-dlls-with-gcc.html' title='Building w32 JNI DLLs with gcc'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-1557396615234505221</id><published>2007-02-27T17:30:00.002-05:00</published><updated>2009-01-11T08:55:28.953-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='jna'/><category scheme='http://www.blogger.com/atom/ns#' term='shaped window'/><title type='text'>Non-rectangular Windows Update</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_ACEoHFt4yoQ/Re86rMtigNI/AAAAAAAAAAM/huMmeFsawwQ/s1600-h/snap.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_ACEoHFt4yoQ/Re86rMtigNI/AAAAAAAAAAM/huMmeFsawwQ/s320/snap.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5039311021871890642" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Updated to include support for OSX and linux.&lt;br /&gt;&lt;br /&gt;While OSX doesn't actually use any native code to do the window mask, JNA support is there if it needed to.  Source (and per-platform binaries) is available at http://jna.dev.java.net.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt;&lt;br /&gt;Transparent version of the demo is available &lt;a href=https://jna.dev.java.net/demo/ShapedWindowDemo.jnlp&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-1557396615234505221?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://jna.dev.java.net/demo/ShapedWindowDemo.jnlp' title='Non-rectangular Windows Update'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/1557396615234505221/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=1557396615234505221' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/1557396615234505221'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/1557396615234505221'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2007/02/non-rectangular-windows-update.html' title='Non-rectangular Windows Update'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_ACEoHFt4yoQ/Re86rMtigNI/AAAAAAAAAAM/huMmeFsawwQ/s72-c/snap.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-117228843927393690</id><published>2007-02-23T22:22:00.003-05:00</published><updated>2009-01-11T08:55:46.691-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='jna'/><category scheme='http://www.blogger.com/atom/ns#' term='shaped window'/><title type='text'>Non-rectangular Windows</title><content type='html'>I've been meaning to play around with shaped windows for a while, but didn't relish the thought of walking through the tedium of JNI configurations and builds.  Doing it on one platform is bad enough, but on several?  No thanks.&lt;br /&gt;&lt;br /&gt;So I thought I'd write a little scriptable shared library access stub once and be done with JNI entirely.  Well, turns out it's already been done.  Several times.&lt;br&gt;&lt;br /&gt;&lt;a href=http://jnative.sf.net&gt;JNative&lt;/a&gt;&lt;br&gt;&lt;a href=http://nlink.dev.java.net&gt;NLink&lt;/a&gt;&lt;br&gt;&lt;a href=http://jna.dev.java.net&gt;JNA&lt;/a&gt;&lt;br&gt;&lt;br /&gt;JNative has some interesting features not found in the others, but actually using it is only slightly better than JNI.  NLink is currently w32-only, and has a bit of a COM bent.  JNA fit most closely with my objectives, already had implementations for w32 and solaris, and was already platform-agnostic.  So I took a couple days to hack in some more features (mostly to get an understanding of the codebase), and here is what I got.  The following code is what it takes to make a frame take an arbitrary shape.&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;    User32 user32 = User32.INSTANCE;&lt;br /&gt;    GDI32 gdi32 = GDI32.INSTANCE;&lt;br /&gt;    JFrame frame = new JFrame(getName());&lt;br /&gt;    Pointer p = gdi32.CreateRoundRectRgn(0, -150, 300, 300, 300, 300);&lt;br /&gt;    int hWnd = user32.FindWindowA(null, getName());&lt;br /&gt;    user32.SetWindowRgn(hWnd, p, true);&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;Looking up the appropriate w32 calls probably took the most time.  How are the w32 libraries defined?  How is this for trivial:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;    public interface User32 extends StdCallLibarary {&lt;br /&gt;        User32 INSTANCE = (User32)Native.loadLibary("user32", User32.class);&lt;br /&gt;        int FindWindowA(String winClass, String title);&lt;br /&gt;        void setWindowRgn(int hWnd, Pointer p, boolean redraw);&lt;br /&gt;    }&lt;br /&gt;    public interface GDI32 extends StdCallLibrary {&lt;br /&gt;        GDI32 INSTANCE = (GDI32)Native.loadLibrary("gdi32", GDI32.class);&lt;br /&gt;        Pointer CreateRoundRectRgn(int left, int top, int right, int bottom, int w, int h);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;Now, somebody's probably going to point me to how SWT has had this functionality for years (does it?), but this is nicely abstracted and based on a very small number of classes.  I'll be updating the code at jna.dev.java.net (or maybe opening a new location if I can't get the existing project moved to subversion), but for now, check out the demo by clicking on everyone's favorite orange launch button.&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;a href="http://jna.dev.java.net/demo/ShapedWindowDemo.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;br&gt;&lt;br /&gt;&lt;/center&gt;&lt;br /&gt;Permissions required, because this runs some custom native code.&lt;br /&gt;&lt;br /&gt;Oh, BTW, this is windows-only for the moment.  I'll do X11 next and anyone's free to send me some code snippets for setting window masks on other platforms.  I could also use some help porting to PPC and other platforms (a very small amount of native ASM to push arguments to the stack, not too hard).&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-117228843927393690?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://jna.dev.java.net/demo/ShapedWindowDemo.jnlp' title='Non-rectangular Windows'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/117228843927393690/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=117228843927393690' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/117228843927393690'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/117228843927393690'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2007/02/non-rectangular-windows.html' title='Non-rectangular Windows'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-117165287074163846</id><published>2007-02-16T14:04:00.000-05:00</published><updated>2007-04-05T12:14:44.024-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>JDK 1.6 Applet Madness</title><content type='html'>I had a few applets on the main page showing some interesting eye candy.  Then I upgraded my JDK to 1.6 and they lock up the java plugin (IE or firefox).  If anyone has encountered a similar issue, please let me know, I'd really like to fix it.&lt;br /&gt;&lt;br /&gt;I've gotten a variety of errors, but I can't seem to come up with an effective workaround or consistent analysis (JDK 1.6 doesn't let you pick a different VM to use for applets).&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-117165287074163846?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/117165287074163846/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=117165287074163846' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/117165287074163846'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/117165287074163846'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2007/02/jdk-16-applet-madness.html' title='JDK 1.6 Applet Madness'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-116466886103023444</id><published>2006-11-27T17:52:00.001-05:00</published><updated>2009-01-11T08:53:16.429-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='wait indicator'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='input blocking'/><title type='text'>Waiting and Blocking Input</title><content type='html'>Those pesky users are always trying to submit forms more than once, or creating new files willy nilly while they're waiting for feedback.&lt;br /&gt;&lt;br /&gt;This article addresses two strategies for avoiding Impatient User Syndrome.  First, provide some feedback to show that the program is busy, and second, don't allow any additional input.&lt;br /&gt;&lt;br /&gt;Now, Java provides a JProgressBar, which you can throw up in a Dialog, but those aren't always appropriate.  Sure if you want to block all application input (or in the case of Java 1.6, all frame input), that'll work, and you can always disable one component at a time.  But suppose, for instance, that you've got a single frame, with a couple of different sections or docked panels.  If their operation is independent of one another, you certainly don't want to block them all with a dialog.&lt;br /&gt;&lt;br /&gt;The following demo shows how to block input on individual components, groups of components, or an entire frame, as well as providing "busy" feedback.  The class &lt;code&gt;WaitIndicator&lt;/code&gt; installs a component in the &lt;code&gt;JLayeredPane&lt;/code&gt; &lt;em&gt;above&lt;/em&gt; the target component, which prevents any mouse input from reaching the target component, and displays an hourglass cursor over the target component.  It also installs a key event handler to ensure that no key events reach the target component.  &lt;br /&gt;&lt;br /&gt;The &lt;code&gt;WaitIndicator&lt;/code&gt; also provides a &lt;code&gt;paint&lt;/code&gt; method, which you can use to provide whatever visual feedback is appropriate to your program.  The default implementation fades to the target component's background color.  The demo itself uses a &lt;code&gt;SpinningDialWaitIndicator&lt;/code&gt;, which paints an Apple-like spinner (which you can also see in the animated icon demo applet a few entries back).&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/WaitIndicatorDemo.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;br&gt;&lt;br /&gt;&lt;/center&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/WaitIndicatorDemo.jar"&gt;Source&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-116466886103023444?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://abbot.sf.net/demo/WaitIndicatorDemo.jar' title='Waiting and Blocking Input'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/116466886103023444/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=116466886103023444' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/116466886103023444'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/116466886103023444'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/11/waiting-and-blocking-input.html' title='Waiting and Blocking Input'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-116465511881479482</id><published>2006-11-27T13:39:00.000-05:00</published><updated>2007-04-05T12:15:18.774-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><title type='text'>Tree (and tree table) with checkboxes</title><content type='html'>When the user needs to select a non-trivial number of items from a hierarchy, normal tree item selection is too prone to losing the selection through mistaken clicks.  The standard solution to provide a more robust selection method is to include check boxes next to each selectable item in the tree.&lt;br /&gt;&lt;br /&gt;Hat tip to some previous online implementations (each has a different implementation):&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;&lt;a href=http://www.codeguru.com/java/articles/185.shtml&gt;Bare bones&lt;/a&gt;&lt;br&gt;&lt;br /&gt;&lt;a href=http://www.java2s.com/ExampleCode/Swing-JFC/CheckBoxNodeTreeSample.htm&gt;With a TreeCellEditor&lt;/a&gt;&lt;br&gt;&lt;br /&gt;&lt;a href=http://www.jroller.com/page/santhosh/20050610#jtree_with_checkboxes&gt;More recent variations&lt;/a&gt;&lt;br&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;After a couple of iterations of checkbox trees (and tree tables), I rolled this up into a standalone &lt;code&gt;TreeCellRenderer&lt;/code&gt; so that it can be applied more easily to any tree-oriented component.  This implementation also properly handles rollover effects on the checkbox, which is lacking in the above implementations (most visible under w32; Java's Metal LAF has no checkbox rollover effects).&lt;br /&gt;&lt;br /&gt;The checkbox painting is handled by using a real checkbox and painting its contents into an icon.  The state of the checkbox is set just prior to painting to ensure the proper rollover state.  The original cell renderer's icon is replaced with a composite icon of the original plus the checkbox (hit ^-shift-O to switch component orientation).&lt;br /&gt;&lt;br /&gt;The renderer provides the current checked selection as an array of &lt;code&gt;TreePath&lt;/code&gt; or an array of &lt;code&gt;int&lt;/code&gt; representing the rows. &lt;br /&gt;&lt;br /&gt;I also applied this to my own tree table implementation (similar to SwingX &lt;code&gt;JXTreeTable&lt;/code&gt;) by just overriding a few protected methods which translate mouse coordinates into rows/paths.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/CheckBoxTreeCellRenderer.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;br&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/CheckBoxTreeCellRenderer.jar"&gt;Source&lt;/a&gt;&lt;br /&gt;&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-116465511881479482?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://abbot.sf.net/demo/CheckBoxTreeCellRenderer.jar' title='Tree (and tree table) with checkboxes'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/116465511881479482/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=116465511881479482' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/116465511881479482'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/116465511881479482'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/11/tree-and-tree-table-with-checkboxes.html' title='Tree (and tree table) with checkboxes'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-116053133386620232</id><published>2006-10-10T21:46:00.001-04:00</published><updated>2009-01-11T08:53:44.657-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='icon'/><title type='text'>Iconic Inversion</title><content type='html'>Here's a problem and solution described in an applet.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;applet code="SelectionIconDemo" width="200" height="100" archive="SelectionIconDemo.jar" codebase="http://abbot.sf.net/demo"&gt;&lt;br /&gt;&lt;/applet&gt;&lt;br /&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;The goal here is to make the icon look consistent with the selected text, which means using the &lt;code&gt;JList&lt;/code&gt;'s selection fg/bg colors where the icon uses the component's normal fg/bg colors.&lt;br /&gt;&lt;br /&gt;This applet uses a &lt;code&gt;SelectionIcon&lt;/code&gt;, which applies a custom composite to the original icon.  The custom composite looks for the list fg/bg colors in the original icon (and some colors "nearby"), and swaps them out for the selection fg/bg.  It also applies a slight tint based on the selection background. &lt;br /&gt;&lt;br /&gt;The &lt;code&gt;SelectionIcon&lt;/code&gt; can be applied to any existing icon.  It's useful for selections because you don't know &lt;i&gt;a priori&lt;/i&gt; what your selection colors are, so it's hard to make a dedicated selection version of your original icons that will look good in all cases. Assuming that the selection fg/bg are set reasonably, your icon will look at least as good as the selection text.  The high contrast colors in the above example are from Win2k, but you never know what the user may have set.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/SelectionIconDemo.jar"&gt;Source&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-116053133386620232?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://abbot.sf.net/demo/SelectionIconDemo.jar' title='Iconic Inversion'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/116053133386620232/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=116053133386620232' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/116053133386620232'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/116053133386620232'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/10/iconic-inversion.html' title='Iconic Inversion'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-115836469512864725</id><published>2006-09-15T19:55:00.002-04:00</published><updated>2010-01-28T15:45:37.143-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='wait indicator'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='animated icon'/><title type='text'>Animated Icon Redux</title><content type='html'>Lately we've been putting animated icons all over the UI to provide feedback about actions that are in progress.  The spinning arrows are an animated GIF, while the spinning dial is dynamically generated with Java2D.  Both icons are used just like any other icon (set and forget).  No special coding is needed to get them to animate.  This is how animated icons &lt;em&gt;should&lt;/em&gt; behave.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;applet code="AnimatedIconDemo" width="200" height="300" archive="AnimatedIconDemo.jar" codebase="http://abbot.sf.net/demo"&gt;&lt;br /&gt;&lt;/applet&gt;&lt;br /&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;Icon&lt;/code&gt; interface's &lt;code&gt;paintIcon&lt;/code&gt; method carries enough information for the icon to trigger its own repaint when it needs to display the next frame.  Internally, the spinning arrows icon tracks the component on which it was painted as well as the location.  Using this information, each time we want to display a new animation frame, we just walk the list of repaint locations and request a repaint on each of the stored items.&lt;br /&gt;&lt;br /&gt;After testing this method out on a variety of components, I realized that this would be a superior method of animating icons in trees, tables, and lists (or any other component that makes use of &lt;code&gt;CellRendererPane&lt;/code&gt;.  With just a few tweaks, the logic was refactored to wrap animated GIF icons so that when they are used within a tree, table, or list, they are automatically refreshed as necessary.  This is a much cleaner solution than the one I posted earlier.&lt;br /&gt;&lt;br /&gt;I have a generic icon loader for my apps, so if the loader detects an animated GIF, it automatically wraps it in an &lt;code&gt;AnimatedIcon&lt;/code&gt; and I don't have to worry about manually tracking animations.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://furbelow.svn.sourceforge.net/viewvc/furbelow/trunk/src/furbelow"&gt;Source&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-115836469512864725?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://abbot.sf.net/demo/AnimatedIconDemo.jar' title='Animated Icon Redux'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/115836469512864725/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=115836469512864725' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115836469512864725'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115836469512864725'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/09/animated-icon-redux.html' title='Animated Icon Redux'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-115694981280281316</id><published>2006-08-30T10:55:00.000-04:00</published><updated>2007-05-27T17:01:47.839-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='drag n drop'/><title type='text'>Swing Drag Images, Improved</title><content type='html'>Performance is improved, generating the drag image once instead of repeatedly.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/SwingDragImages.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;br&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/SwingDragImages.jar"&gt;Source&lt;/a&gt;&lt;br /&gt;&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-115694981280281316?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://abbot.sf.net/demo/SwingDragImages.jnlp' title='Swing Drag Images, Improved'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/115694981280281316/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=115694981280281316' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115694981280281316'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115694981280281316'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/08/swing-drag-images-improved.html' title='Swing Drag Images, Improved'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-115538859563775288</id><published>2006-08-12T09:16:00.001-04:00</published><updated>2009-01-11T08:52:22.977-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='drag n drop'/><category scheme='http://www.blogger.com/atom/ns#' term='navigation'/><title type='text'>Drop Target Navigation, or You Drag Your Bags, Let the Doorman Get the Door</title><content type='html'>When I'm dragging a 75lb bag and a five year old, it's rather nice to have someone open the door.  When I'm dragging things around in my application, it's also nice not to have to open all the doors on the way to my destination before I start dragging.&lt;br /&gt;&lt;br /&gt;Specifically, I've got a set of forms in an instance of JTabbedPane.  I know one of the tabs holds a field that defines my preferred download directory, which I happen to have open in my file browser.  Drag from the file browser onto the JTabbedPane, and -- oops, I'm not on the right pane.  It'd be nice to have the tabs change as I drag across them so that I can find the right destination.&lt;br /&gt;&lt;br /&gt;Since this is system-wide behavior, I'd like to enable the behavior once at application startup and forget about it, rather than having to add listeners or subclass every instance of JTabbedPane and JTree that's going to accept a drop.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;DropTargetNavigator.enableDropTargetNavigation();&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;In order to make this happen, we install a global listener to the default DragSource, which lets us respond to &lt;em&gt;any&lt;/em&gt; drags initiated within the same VM.  Since that provides us with the current drag location, we can scan through the currently available components using &lt;code&gt;Frame.getFrames()&lt;/code&gt; and &lt;code&gt;SwingUtilities.getDeepestComponentAt()&lt;/code&gt; to see if what's under the cursor needs to be navigated.  If we do make a modification, a &lt;code&gt;Runnable&lt;/code&gt; which will undo the modification is added to a queue so that we can restore the component to its original state if the drop ends up going somewhere else or being canceled.&lt;br /&gt;&lt;br /&gt;To handle drags originating from outside the VM requires a little hacking, since there isn't any sort of global drop target listener.  During a native drag, you won't see any &lt;code&gt;MouseEvent&lt;/code&gt;s.  As of 1.4+, there is a &lt;code&gt;MouseEvent&lt;/code&gt; (actually a &lt;code&gt;SunDropTargetEvent&lt;/code&gt;) that gets processed by &lt;code&gt;Component.dispatchEventImpl()&lt;/code&gt;, but it gets swallowed before the component passes events off to &lt;code&gt;AWTEventListener&lt;/code&gt;s.  You can't override &lt;code&gt;dispatchEvent&lt;/code&gt;, and wouldn't want to anyway because you'd have to do it on &lt;em&gt;every&lt;/em&gt; component.  Instead, we can install a custom &lt;code&gt;EventQueue&lt;/code&gt; which can watch for the drop target events and forward them to our drop target listener.  &lt;br /&gt;&lt;br /&gt;The navigator class has built-in support for JTabbedPanes and JTrees.  Custom classes that wish to support navigation can register themselves with a simple interface that performs custom navigation and rollback.   This way you can enable auto navigation on whatever tree table implementation you happen to be using.&lt;br /&gt;&lt;br /&gt;In order to avoid unintended navigation when the mouse is dragged rapidly across a component, I've added a configurable delay to when the navigation starts.  You need to hover over the same spot for roughly half a second to activate the navigation.  &lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/DropTargetNavigatorDemo.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-115538859563775288?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://abbot.sf.net/demo/DropTargetNavigatorDemo.jnlp' title='Drop Target Navigation, or You Drag Your Bags, Let the Doorman Get the Door'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/115538859563775288/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=115538859563775288' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115538859563775288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115538859563775288'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/08/drop-target-navigation-or-you-drag.html' title='Drop Target Navigation, or You Drag Your Bags, Let the Doorman Get the Door'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-115506538240429054</id><published>2006-08-08T13:52:00.000-04:00</published><updated>2007-05-27T17:01:15.266-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='cell renderers'/><title type='text'>Mighty Mitochondria! Them Cells Are Animated!</title><content type='html'>Java provides effortless loading and display of animated GIFs.  Just tell ImageIcon where it can find the file, then plug the ImageIcon into a JLabel or JButton.  Couldn't be simpler.&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;applet code="CellAnimatorDemo" width="200" height="30" archive="CellAnimatorDemo.jar" codebase="http://abbot.sf.net/demo"&gt;&lt;br /&gt;&lt;/applet&gt;&lt;br /&gt;&lt;/center&gt;&lt;br /&gt;Now how about putting that animated icon into a JTree as feedback for loading status.&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;applet code="CellAnimatorDemo" width="200" height="200" archive="CellAnimatorDemo.jar" codebase="http://abbot.sf.net/demo"&gt;&lt;br /&gt;  &lt;param name="showTree" value="true"/&gt;&lt;br /&gt;&lt;/applet&gt;&lt;br /&gt;&lt;/center&gt;&lt;br /&gt;Oops.  What happened?  Did we get the right icon?  Why isn't it animated?  Try selecting one of the tree nodes, then rapidly move the selection up and down.  Watch what happens.&lt;br /&gt;&lt;br /&gt;What happened is that no one told the &lt;code&gt;JTree&lt;/code&gt; it needed to repaint itself after it painted the initial image.  Selection changes trigger repaint messages, which is why the icons move a little bit when you move the selection around.  Any animation is driven by regular repaints of the component where it is drawn.  &lt;code&gt;JLabel&lt;/code&gt;s and &lt;code&gt;JButton&lt;/code&gt;s are no exception.  Take a look at the source for &lt;code&gt;ImageIcon.paintIcon&lt;/code&gt;:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;public synchronized void paintIcon(Component c, Graphics g, int x, int y) {&lt;br /&gt;    if(imageObserver == null) {&lt;br /&gt;       g.drawImage(image, x, y, c);&lt;br /&gt;    } else {&lt;br /&gt;       g.drawImage(image, x, y, imageObserver);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;When you create your &lt;code&gt;ImageIcon&lt;/code&gt;, it doesn't have an image observer, so the component where it's being drawn is used instead (note that &lt;code&gt;java.awt.Component&lt;/code&gt; implements &lt;code&gt;ImageObserver&lt;/code&gt;):&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;public boolean imageUpdate(Image img, int infoflags,&lt;br /&gt;                           int x, int y, int w, int h) {&lt;br /&gt;    int rate = -1;&lt;br /&gt;    if ((infoflags &amp; (FRAMEBITS|ALLBITS)) != 0) {&lt;br /&gt;        rate = 0;&lt;br /&gt;    }&lt;br /&gt;    // ...&lt;br /&gt;    if (rate &gt;= 0) {&lt;br /&gt;        repaint(rate, 0, 0, width, height);&lt;br /&gt;    }&lt;br /&gt;    return (infoflags &amp; (ALLBITS|ABORT)) == 0;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;So whenever the image reports that it has another frame ready (&lt;code&gt;infoflags&amp;FRAMEBITS != 0&lt;/code&gt;), it calls this method.&lt;br /&gt;&lt;br /&gt;That's just fine for a label or button which only displays a single icon, but what about a tree, which might display this icon in &lt;em&gt;every&lt;/em&gt; row?  We need an image observer which can call &lt;code&gt;repaint&lt;/code&gt; with the cell bounds of every cell that contains the animated icon.  Using the tree itself as the observer could work, but we'd end up redrawing the entire tree on every frame.  It's preferable to use a decorator which keeps track of which individual cells need updating.&lt;p&gt;&lt;br /&gt;&lt;br /&gt;In this case, let's use the tree cell renderer as the entry point or trigger to start or stop animation, since that's normally where a tree's icons get customized.  &lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;JTree tree = ...;&lt;br /&gt;TreeCellRenderer r = new DefaultTreeCellRenderer() {&lt;br /&gt;    public Component getTreeCellRendererComponent(JTree tree, Object value, &lt;br /&gt;                                                  boolean selected, boolean expanded,&lt;br /&gt;                                                  boolean leaf, int row, boolean focused) {&lt;br /&gt;        Component c = &lt;br /&gt;            super.getTreeCellRendererComponent(tree, value, selected, expanded, &lt;br /&gt;                                               leaf, row, focused);&lt;br /&gt;        TreePath path = tree.getPathForRow(row);&lt;br /&gt;        if (isLoading(path.getLastPathComponent()))&lt;br /&gt;            setIcon(LOADING_ICON);&lt;br /&gt;        // The following line is what you'd normally use; the rest is just illustrative&lt;br /&gt;        //CellAnimator.animate(tree, c, path); &lt;br /&gt;        // Let the CellAnimator utility figure out if there's an animated icon here&lt;br /&gt;        ImageIcon icon = (ImageIcon)getIcon();&lt;br /&gt;        if (CellAnimator.isAnimated(icon)) {&lt;br /&gt;            CellAnimator.animate(tree, new CellAnimator.TreeUpdater(tree, row), icon);&lt;br /&gt;        }&lt;br /&gt;        else {&lt;br /&gt;            CellAnimator.stop(tree, row);&lt;br /&gt;        }&lt;br /&gt;        return c; &lt;br /&gt;    }&lt;br /&gt;};&lt;br /&gt;tree.setCellRenderer(r);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;CellAnimator&lt;/code&gt; needs to do several things.  At any point if it is determined that no animation is required, the animation support is disposed for the current location.&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Obtain the icon in use, if any&lt;br /&gt;&lt;li&gt;If the icon is animated, install an &lt;code&gt;ImageObserver&lt;/code&gt;&lt;br /&gt;&lt;li&gt;Identify the location to receive repaint notifications, and add it to the observer's list&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Whenever the animated icon posts an update, the &lt;code&gt;ImageObserver&lt;/code&gt; for that icon will walk its list of repaint locations and trigger repaints.  The actual implementation takes care of a number of other details, like using weak references for UI components and remove locations if the location or component is no longer valid.&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;CellAnimator&lt;/code&gt; provides one-line methods to enable animation on standard Swing components and is easily extensible to enable animation on custom components that use the cell renderer technique.&lt;br /&gt;&lt;br /&gt;Here is a demo of the cell animator applied to a lazy-loading tree.&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/CellAnimatorDemo.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://abbot.sf.net/demo/CellAnimationDemo.jar"&gt;source&lt;/a&gt; includes JUnit-based unit tests which use the &lt;a href=http://abbot.sf.net&gt;Abbot library&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-115506538240429054?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://abbot.sf.net/demo/CellAnimatorDemo.jnlp' title='Mighty Mitochondria! Them Cells Are Animated!'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/115506538240429054/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=115506538240429054' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115506538240429054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115506538240429054'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/08/mighty-mitochondria-them-cells-are.html' title='Mighty Mitochondria! Them Cells Are Animated!'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-115470758970832948</id><published>2006-08-04T11:33:00.000-04:00</published><updated>2007-04-05T12:16:57.798-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>UI Testing on the Sly</title><content type='html'>One of the drawbacks of testing your UI components (you are testing them, right?) is that to properly run a test, it needs to run on a display.  Unfortunately, if you want to use that display for something else (like writing code and fixing bugs), you run into problems with either the tests writing your code, or the code you're writing supplying input to your UI tests.&lt;br /&gt;&lt;br /&gt;One good solution is to have a dedicated machine for the tests, which you can continually monitor and can easily be updated with changes from your dev machine.  Since that isn't always an option, I'll present a few alternatives for several different platforms which allow you to run UI tests without having a separate machine to run them on.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;OS X&lt;/h3&gt;&lt;br /&gt;VNC is a great tool for controlling remote computers of any platform.  If you have fast user switching enabled, you can actually use &lt;a href="http://www.redstonesoftware.com/vnc.html"&gt;OSXvnc&lt;/a&gt; to display the desktop for a user other than the one currently using the display.  If you configure OSXvnc to &lt;em&gt;not&lt;/em&gt; quit when switching to another user, you will always be able to connect to the desktop where OSXvnc was first launched.&lt;br /&gt;&lt;br /&gt;Set up a "test" user, and set up OSXvnc to launch when that user logs in.  Set up the "Startup" tab like this:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/6337/472/1600/vnc.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/6337/472/320/vnc.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;Once OSXvnc is running, you can access the "test" desktop while using your normal desktop.  Run your tests in the background, peek in on them to check the results.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Linux&lt;/h3&gt;&lt;br /&gt;Run vncserver, either manually or as a daemon.  This will give you an additional X display which is totally separate from your physical one.  Connect to the new display with vncviewer, and you have a window onto your "test machine".&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Windows&lt;/h3&gt;&lt;br /&gt;Windows isn't quite as functional when it comes to sharing a display, but you can still get some mileage out of UI testing on the same machine.  There's a peculiar little mode in windows, I'll call it "service mode", where you think you have a display but you don't.  This prevents java.awt.Robot from working properly, but you can still get some mileage from UI tests that don't depend on screen captures or native drag and drop.  &lt;a href="http://abbot.sf.net"&gt;Abbot&lt;/a&gt; (a UI testing library) will work just fine in this mode, and does so by artificially generating events directly into the AWT event queue in order to drive a UI.&lt;br /&gt;&lt;br /&gt;The easiest way to run tests in this manner is to install cygwin sshd.  It runs as a service, so by default it doesn't have access to the desktop (there is a checkbox in the services admin control panel that indicates whether a service is allowed desktop access).  Once that is installed and configured (google "cygwin sshd install"), you can use ssh to get a shell prompt within the service mode "sandbox".  If you've got an ant script to run your tests, just invoke it.  Abbot automatically detects whether this mode is in effect, so if you're using that library, everything works out of the box.  If you only run your tests interactively through an IDE, you're out of luck, since you won't be able to interact with the IDE if you launch it in service mode.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-115470758970832948?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/115470758970832948/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=115470758970832948' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115470758970832948'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115470758970832948'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/08/ui-testing-on-sly.html' title='UI Testing on the Sly'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-115318437597662917</id><published>2006-07-17T20:40:00.000-04:00</published><updated>2007-04-05T12:17:08.333-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><title type='text'>Drag Images for Everyone...and we do mean Everyone</title><content type='html'>You may have seen&lt;a href="http://java.sun.com/docs/books/tutorial/uiswing/misc/dnd.html"&gt; Sun's tutorial&lt;/a&gt; on using Swing's built-in drag and drop support.  One of the demos from the tutorial looks like this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://java.sun.com/docs/books/tutorial/figures/uiswing/misc/BasicDND.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; curs&lt;br /&gt;or:hand;width: 320px;" src="http://java.sun.com/docs/books/tutorial/figures/uiswing/misc/BasicDND.gif" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Today's demo puts a drag image on each of the displayed drag-enabled components, and does so without modifying the original jar file.  It also does so without any special knowledge of the components that are drag enabled.  (Web start doesn't let you reference a file on another host, so you'll have to trust me when I say the original jar is used unmodified).&lt;br /&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/SwingDragImages.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;A few sights to take in after you've checked that "Turn on Drag and Drop" checkbox.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Try a drag from each component.&lt;br /&gt;&lt;li&gt;Try dragging a multiple, discontiguous selection from each component.&lt;br /&gt;&lt;li&gt;Watch what happens when your drop is rejected.&lt;br /&gt;&lt;li&gt;Move the main window, and drag some more.  Notice the nice images across &lt;em&gt;both&lt;/em&gt; windows (the original &lt;code&gt;main&lt;/code&gt; has simply been called twice).&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/SwingDragImages-src.jar"&gt;Source Here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The technique depends on all drag sources to use the default DragSource as the drag initiator.  To date, all drags implemented within the JRE use the default, as do all the drag &amp; drop examples I've seen on the web, so it always works in practice.&lt;br /&gt;&lt;br /&gt;The global drag source listener technique used here could also be used to automatically switch the visible tab in a JTabbedPane, expand a node in a JTree, or even autoscroll components that don't implement Autoscroll.  Watch for an upcoming article to address those options.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-115318437597662917?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/115318437597662917/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=115318437597662917' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115318437597662917'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115318437597662917'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/07/drag-images-for-everyoneand-we-do-mean.html' title='Drag Images for Everyone...and we do mean Everyone'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-115266481215791057</id><published>2006-07-11T18:20:00.000-04:00</published><updated>2007-04-05T12:17:31.454-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Bugs in the Library</title><content type='html'>One of the great benefits of Java is the availability of a wide variety of libraries providing all sorts of functionality.  However, managing bugs, workarounds, and fixes becomes more complicated when tracking multiple third-party libraries.&lt;br /&gt;&lt;br /&gt;With a simple source code base or a single application, it's relatively simple to take library updates to get bug fixes, or even to apply patches to a local copy of a library.  There are many situations where this isn't possible and you need to determine for a given section of client code whether a bug is present.  One codebase may need to support several VM releases and what's a workaround in one may introduce a bug in another.&lt;br /&gt;&lt;br /&gt;I keep track of library-based bugs with a class &lt;code&gt;Bugs&lt;/code&gt; which comprises static methods which check the environment for the presence of a given bug.  For instance, Mac OSX between Java releases 1.4 and 1.5 incorrectly mapped mouse button events generated by &lt;code&gt;java.awt.Robot&lt;/code&gt;.  So &lt;code&gt;Bugs&lt;/code&gt; would have a corresponding method,&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// This bug has been submitted to Apple, bug #XXXXXXX&lt;br /&gt;// It was officially fixed in version 1.5&lt;br /&gt;public static boolean hasRobotButtonSwapBug() {&lt;br /&gt;    return Platform.isMac() &amp;&amp; Platform.JAVA_VERSION &gt;= 0x1400&lt;br /&gt;        &amp;&amp; Platform.JAVA_VERSION &lt; 0x1500;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The method contains checks which describe as precisely as possible the environment where the bug is present.  In this case, several java releases were tested; the bug first appeared in the Java 1.4 release and persisted until 1.5.  The method should narrowly identify only those environments shown to produce the bug (see below).&lt;br /&gt;&lt;br /&gt;In the corresponding &lt;code&gt;BugsTest&lt;/code&gt;, we implement a method which actually checks for the bug.  This serves several purposes.  First, it provides a test case to submit along with a bug report to whoever is responsible for the bug.  Second, it provides a standard check when used with other automated tests to flag changes in released libraries.  Checking the "bug presence" method at the start of the test avoids test failures where we already know the bug exists, and tests for its presence in any environment we haven't yet tested.  A system property may be set to force running the test.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public void testRobotButtonSwapBug() throws Exception {&lt;br /&gt;    // skip the test if we already know the bug exists, unless forcing a bug check&lt;br /&gt;    if (!Boolean.getBoolean("check_all_bugs") || hasRobotButtonSwapBug()) return;&lt;br /&gt;    // perform tests to reproduce the bug&lt;br /&gt;    final List events = new ArrayList();&lt;br /&gt;    Robot robot = new java.awt.Robot();&lt;br /&gt;    Window w = new Window();&lt;br /&gt;    w.add(new JLabel("Swap test"));&lt;br /&gt;    w.addMouseListener(new MouseAdapter() {&lt;br /&gt;        public void mouseClicked(MouseEvent e) {&lt;br /&gt;            events.add(e);&lt;br /&gt;        }&lt;br /&gt;    });&lt;br /&gt;    showWindow(w);&lt;br /&gt;    Point where = w.getLocationOnScreen();&lt;br /&gt;    where.x += w.getWidth()/2;&lt;br /&gt;    where.y += h.getWidth()/2;&lt;br /&gt;    robot.mouseMove(where.x, where.y);&lt;br /&gt;    robot.mousePress(InputEvent.BUTTON2_MASK);&lt;br /&gt;    robot.mouseRelease(InputEvent.BUTTON2_MASK);&lt;br /&gt;    robot.waitForIdle();&lt;br /&gt;    assertEquals("Robot generated incorrect button event for button 2",&lt;br /&gt;                 InputEvent.BUTTON2_MASK, &lt;br /&gt;                 ((MouseEvent)events.get(0)).getModifiers());&lt;br /&gt;    // and so on...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;&lt;br /&gt;With each new library release, the test is run.  If it fails with a new version or a new platform, extra checks can be added to the method in &lt;code&gt;Bugs&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;Bugs&lt;/code&gt; method test can now be used in client code to determine whether workarounds need to be applied.  Such checking allows the client code to be used consistently regardless of the environment in use on the project where it is used.  If you release an application that runs on Java 1.2 and above, you may need to selectively apply workarounds depending on the actual JRE in use by a user.  In the example above, a workaround to swap button definitions was applied (and still is) if the user happens to be using the affected Java release on OSX.&lt;br /&gt;&lt;br /&gt;This pattern makes bug checks and workarounds very explicit.  Compare these two implementations.  First, the ad hoc workaround:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;JTree tree;&lt;br /&gt;...&lt;br /&gt;// tree row size isn't updated automatically, so hack it&lt;br /&gt;tree.setRowSize(tree.getRowSize());&lt;br /&gt;tree.revalidate();&lt;br /&gt;tree.repaint();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Or this, which is only slightly more structured, but entirely clear in intent.  You can also tell exactly what code can go away when the bug does:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;JTree tree;&lt;br /&gt;...&lt;br /&gt;if (Bugs.hasTreeRowSizeUpdateBug()) {&lt;br /&gt;    tree.setRowSize(tree.getRowSize());&lt;br /&gt;    tree.revalidate();&lt;br /&gt;    tree.repaint();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The code explicitly identifies a bug that has been at least partially analyzed, and indicates workaround code.  If a programmer needs further information on the bug, she can just look up the corresponding information in Bugs or BugsTest&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-115266481215791057?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/115266481215791057/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=115266481215791057' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115266481215791057'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115266481215791057'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/07/bugs-in-library.html' title='Bugs in the Library'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-115150326769007793</id><published>2006-06-28T09:59:00.000-04:00</published><updated>2007-05-27T17:02:09.523-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='drag n drop'/><title type='text'>Fancy Drops</title><content type='html'>Just run the demo.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/FancyDrop.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Again, a very simple method override to enable drop target functionality.  Paint a marquee on the full list if it's empty, otherwise mark a space at the end of the list.  Most of the code is just futzing with the data model on a drop or moving/sizing the decoration.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;JList list = ...;&lt;br /&gt;DataFlavor[] acceptableFlavors = { DataFlavor.stringFlavor };&lt;br /&gt;new DropHandler(list, DnDConstants.ACTION_COPY, acceptableFlavors) {&lt;br /&gt;    private Marquee marquee;&lt;br /&gt;    &lt;span style="color: rgb(153, 51, 0)" &gt;/** Always drop at the end of the list. */&lt;/span&gt;&lt;br /&gt;    protected void drop(DropTargetDropEvent e, int action) throws UnsupportedFlavorException, IOException {&lt;br /&gt;        final List data = new ArrayList();&lt;br /&gt;        for (int i=0;i &amp;lt; list.getModel().getSize();i++) {&lt;br /&gt;            data.add(list.getModel().getElementAt(i));&lt;br /&gt;        }&lt;br /&gt;        data.add(e.getTransferable().getTransferData(DataFlavor.stringFlavor));&lt;br /&gt;        list.setModel(new AbstractListModel() {&lt;br /&gt;            public int getSize() {&lt;br /&gt;                return data.size();&lt;br /&gt;            }&lt;br /&gt;            public Object getElementAt(int index) {&lt;br /&gt;                return data.get(index);&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;    }&lt;br /&gt;    protected void paintDropTarget(DropTargetEvent e, int action, Point location) {&lt;br /&gt;        if (action != DnDConstants.ACTION_NONE &amp;&amp;amp; location != null) {&lt;br /&gt;            if (marquee == null) {&lt;br /&gt;                marquee = new Marquee(list);&lt;br /&gt;            }&lt;br /&gt;            int count = list.getModel().getSize();&lt;br /&gt;            if (count == 0) {&lt;br /&gt;                Dimension size = list.getSize();&lt;br /&gt;                marquee.setDecorationBounds(new Rectangle(0, 0, size.width, size.height));&lt;br /&gt;            }&lt;br /&gt;            else {&lt;br /&gt;                Rectangle r = list.getCellBounds(count-1, count-1);&lt;br /&gt;                r.y += r.height;&lt;br /&gt;                marquee.setDecorationBounds(r);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        else if (marquee != null) {&lt;br /&gt;            marquee.dispose();&lt;br /&gt;            marquee = null;&lt;br /&gt;        }&lt;br /&gt;    }    &lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;Compare that API with the raw D&amp;D APIs (which are still available to override in &lt;code&gt;DropHandler&lt;/code&gt;, by the way):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(153, 51, 0);"&gt;// DropHandler&lt;br /&gt;&lt;/span&gt;void drop(DropTargetDropEvent e, int action);&lt;br /&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span style="color: rgb(153, 51, 0);"&gt;// DropTargetListener&lt;/span&gt;&lt;br /&gt;void dragEnter(DropTargetDragEvent);&lt;br /&gt;void dragOver(DropTargetDragEvent);&lt;br /&gt;void dropActionChanged(DropTargetDragEvent);&lt;br /&gt;void dropExit(DropTargetEvent);&lt;br /&gt;void drop(DropTargetDropEvent);&lt;br /&gt;&lt;/pre&gt;Most of the complexity comes in figuring out the appropriate sequence of calling back methods on the DropTargetEvents, which communicates whether a drag is acceptable and if so, which action is actually to be accepted.   This could be clarified a great deal in the Javadoc APIs, or by a thorough example, but ultimately it's boilerplate code that rarely, if ever, needs changes to its behavior.&lt;br /&gt;&lt;br /&gt;Back to the example.  The JTree drop handler code is more complex, but only to determine what should happen when dropping on any given location.  Non-leaf nodes accept drops, but leaf nodes refuse drops, and the spaces between leaf nodes accept drops.&lt;br /&gt;&lt;br /&gt;Some &lt;code&gt;DropHandler&lt;/code&gt; features:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Automatically get the copy action when it's the only one allowed&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Automatically disable user actions which are not allowed&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Some parts of the drop target allow drops, some don't&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Paint any drop target indication you please, regardless of the target component (no subclassing of components)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Usually, in order to get non-trivial drop target painting, you have to override one of the target component's paint methods and invoke &lt;code&gt;paintImmediately&lt;/code&gt; within the drop handler.  That's avoided by use of a decorator, which uses the existing component hierarchy and layout to paint &lt;em&gt;over&lt;/em&gt; the target component.  The &lt;code&gt;Marquee&lt;/code&gt; class uses a dashed stroke to draw a rectangle.  A timer regularly increases the phase and triggers a repaint, which gives the "marching ants" effect.&lt;br /&gt;&lt;br /&gt;Unfortunately, you still have to make the component implement &lt;code&gt;Autoscrolls&lt;/code&gt;, although you could probably call the methods yourself from within the drop handler.  It'd be nice if the D&amp;amp;D code which calls into the &lt;code&gt;Autoscrolls&lt;/code&gt; interface instead looked for a client property.  That functionality could probably be implemented in the &lt;code&gt;DropHandler&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Next step: hook this up to the list and tree animators.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-115150326769007793?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/115150326769007793/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=115150326769007793' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115150326769007793'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115150326769007793'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/06/fancy-drops.html' title='Fancy Drops'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-115143556145691471</id><published>2006-06-27T14:30:00.000-04:00</published><updated>2007-05-27T17:02:23.281-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='drag n drop'/><title type='text'>Dead Simple Drags</title><content type='html'>Here is a demo of usage of the Drag &amp; Drop Library, which provides D&amp;amp;D support on top of Java's basic D&amp;D in much the way Xt provided toolkit functionality on top of X11.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/6337/472/1600/dead-simple.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://photos1.blogger.com/blogger/6337/472/320/dead-simple.gif" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://abbot.sf.net/demo/DeadSimpleDrag.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Here is the code to add drag handling to a JLabel:&lt;br /&gt;&lt;pre&gt;final JLabel label = new JLabel("Drag Me");&lt;br /&gt;label.setBorder(BorderFactory.createEmptyBorder(4,4,4,4));&lt;br /&gt;new DragHandler(label, DnDConstants.ACTION_COPY) {&lt;br /&gt;  protected Transferable getTransferable(DragGestureEvent e) {&lt;br /&gt;      return new StringSelection(label.getText());&lt;br /&gt;  }&lt;br /&gt;  protected Icon getDragIcon(DragGestureEvent e, Point offset) {&lt;br /&gt;      return new ComponentIcon(label, true);&lt;br /&gt;  }&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The only thing absolutely required by the API is &lt;code&gt;getTransferable&lt;/code&gt;.  However, overloading &lt;code&gt;getDragIcon&lt;/code&gt; will automatically give you a nice ghosted drag image, regardless of whether your platform supports one natively (the &lt;code&gt;ComponentIcon&lt;/code&gt; used to render the &lt;code&gt;JLabel&lt;/code&gt; is just a simple class which renders its given component as an icon).&lt;br /&gt;&lt;br /&gt;Drag and Drop has been in Java since version 1.2 when a functional but very complex set of interfaces was introduced.  Compare the previous &lt;code&gt;DragHandler&lt;/code&gt; interface for dragging with the following interfaces, which must be implemented carefully to get consistent results (note also the conspicuous absence of any methods which explicitly indicate the data to be dragged):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color: rgb(153, 51, 0);"&gt;// DragGestureListener&lt;/span&gt;&lt;br /&gt;void dragGestureRecognized(DragGestureEvent);&lt;br /&gt;&lt;span style="color: rgb(153, 51, 0);"&gt;// DragSourceListener&lt;/span&gt;&lt;br /&gt;void dragEnter(DragSourceDragEvent);&lt;br /&gt;void dragOver(DragSourceDragEvent);&lt;br /&gt;void dropActionChanged(DragSourceDragEvent);&lt;br /&gt;void dragExit(DragSourceEvent);&lt;br /&gt;void dragDropEnd(DragSourceDropEvent);&lt;br /&gt;&lt;span style="color: rgb(153, 51, 0);"&gt;//DragSourceMotionListener&lt;/span&gt;&lt;br /&gt;void dragMouseMoved(DragSourceDragEvent);&lt;br /&gt;&lt;/pre&gt;Not a trivial thing to get right, especially without an in-depth understanding of how the system works.  Not something I'd expect of every developer.    Unfortunately, there is no abstract implementation to provide a base level of functionality or useful example (I'm sure everyone has at least looked at &lt;a href="http://www.rockhoppertech.com/java-drag-and-drop-faq.html"&gt;Rockhopper&lt;/a&gt; or &lt;a href="http://www.javaworld.com/javaworld/jw-03-1999/jw-03-dragndrop.html"&gt;JavaWorld&lt;/a&gt; for functional examples).  These examples provide working implementations, but not one that makes extension or reuse clear and simple.  Sun has tried to make D&amp;D simpler by introducing the TransferHandler and setting up default drag and drop handlers on Swing components.  I consider the TransferHandler a mistaken attempt to merge two operations that just happened to have a little functionality in common.  Copy and drag both need to produce a &lt;code&gt;Transferable&lt;/code&gt;, and Paste and drop both need to absorb a &lt;code&gt;Transferable&lt;/code&gt;, but the edit actions operate in a sufficiently different manner that merging the two doesn't really buy you much in practice.&lt;br /&gt;&lt;br /&gt;Copy/paste actions are fairly trivial to construct in the first place, and the clipboard transfer is a few lines of boilerplate.&lt;br /&gt;&lt;br /&gt;Drag and drop, on the other hand, is a &lt;em&gt;lot&lt;/em&gt; more lines of boilerplate, some of which can introduce subtle differences in behavior if you don't get it just right.&lt;br /&gt;&lt;br /&gt;Back to the library.  An example on a JTree is a bit more involved, but only because you now need to choose which part of the JTree is going to be dragged.  For illustrative purposes, I've disabled dragging of non-leaf nodes, and enabled dragging of the entire tree if you drag outside of any rows.  I futz with the ComponentIcon to draw either the whole tree or just one row, as needed.  You could conceivably construct a drag from a multiple selection by painting the whole tree but adding an appropriate clipping mask when painting the icon.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;final JTree tree = new JTree();&lt;br /&gt;&lt;span style="color: rgb(153, 51, 0);"&gt;// Turn off selection of rows by dragging&lt;/span&gt;&lt;br /&gt;tree.setDragEnabled(true);&lt;br /&gt;&lt;span style="color: rgb(153, 51, 0);"&gt;// Turn off built-in swing drag handling&lt;/span&gt;&lt;br /&gt;tree.setTransferHandler(null);&lt;br /&gt;new DragHandler(tree, DnDConstants.ACTION_COPY) {&lt;br /&gt;  protected boolean canDrag(DragGestureEvent e) {&lt;br /&gt;      Point where = e.getDragOrigin();&lt;br /&gt;      int row = tree.getRowForLocation(where.x, where.y);&lt;br /&gt;      if (row != -1) {&lt;br /&gt;          TreePath path = tree.getPathForRow(row);&lt;br /&gt;          return tree.getModel().isLeaf(path.getLastPathComponent());&lt;br /&gt;      }&lt;br /&gt;      return true;&lt;br /&gt;  }&lt;br /&gt;  protected Transferable getTransferable(DragGestureEvent e) {&lt;br /&gt;      Point where = e.getDragOrigin();&lt;br /&gt;      final int row = tree.getRowForLocation(where.x, where.y);&lt;br /&gt;      if (row == -1) {&lt;br /&gt;          return new StringSelection("full tree");&lt;br /&gt;      }&lt;br /&gt;      Object value = tree.getPathForRow(row).getLastPathComponent();&lt;br /&gt;      return new StringSelection(String.valueOf(value));&lt;br /&gt;  }&lt;br /&gt;  protected Icon getDragIcon(DragGestureEvent e, Point offset) {&lt;br /&gt;      Point where = e.getDragOrigin();&lt;br /&gt;      final int row = tree.getRowForLocation(where.x, where.y);&lt;br /&gt;      if (row != -1) {&lt;br /&gt;          Rectangle r = tree.getRowBounds(row);&lt;br /&gt;          offset.setLocation(r.x, r.y);&lt;br /&gt;      }&lt;br /&gt;      return new ComponentIcon(tree, true) {&lt;br /&gt;          public void paintIcon(Component c, Graphics g, int x, int y) {&lt;br /&gt;              g = g.create();&lt;br /&gt;              if (row != -1) {&lt;br /&gt;                  Rectangle r = tree.getRowBounds(row);&lt;br /&gt;                  g.translate(-r.x, -r.y);&lt;br /&gt;                  g.setClip(new Rectangle(x+r.x, y+r.y, r.width, r.height));&lt;br /&gt;                  super.paintIcon(c, g, x, y);&lt;br /&gt;              }&lt;br /&gt;              else {&lt;br /&gt;                  super.paintIcon(c, g, x, y);&lt;br /&gt;              }&lt;br /&gt;              g.dispose();&lt;br /&gt;          }&lt;br /&gt;      };&lt;br /&gt;  }&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Again, this implementation doesn't have to worry about any of the mechanics of drag and drop.  The implementation decides whether an item can be dragged, what the dragged item looks like, and what is the appropriate &lt;code&gt;Transferable&lt;/code&gt;.  This API hides all the boilerplate, but you can still get access to the basic Java D&amp;amp;D API if you need to, if for some reason you need to augment or override &lt;code&gt;dragOver&lt;/code&gt; or &lt;code&gt;dragGestureRecognized&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Next installment will demonstrate the &lt;code&gt;DropHandler&lt;/code&gt;, which facilitates decorating your drop target as well as accepting or rejecting incoming data.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/DeadSimpleDrag.jar"&gt;Source&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-115143556145691471?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/115143556145691471/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=115143556145691471' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115143556145691471'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115143556145691471'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/06/dead-simple-drags.html' title='Dead Simple Drags'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-115134269396378286</id><published>2006-06-26T13:21:00.000-04:00</published><updated>2007-10-16T14:41:38.258-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><title type='text'>Decorator Update</title><content type='html'>This fixes some display glitches that showed up on X11-based systems.  The decorators are also properly clipped (or not) if they extend beyond the target component bounds.  This showed up with the marquee, which would get clipped by a scroll pane that was not an ancestor.&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://abbot.sf.net/demo/DecoratorDemo.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://furbelow.svn.sourceforge.net/viewvc/furbelow/trunk/src/furbelow"&gt;Source&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-115134269396378286?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/115134269396378286/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=115134269396378286' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115134269396378286'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115134269396378286'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/06/decorator-update.html' title='Decorator Update'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-115043022116470958</id><published>2006-06-15T23:49:00.000-04:00</published><updated>2007-05-31T08:01:29.380-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><title type='text'>Navigating Large Spaces</title><content type='html'>This example demonstrates one method of providing a navigational element for traversing a large, scrolled component. Like Apple's Expose, it's entirely out of the way until you need it.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://photos1.blogger.com/blogger/6337/472/1600/panner.gif" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img alt="" border="0" src="http://photos1.blogger.com/blogger/6337/472/320/panner.gif" style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://abbot.sf.net/demo/panner.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;This implementation combines several effects. First, a ComponentIcon class renders any given target component as an icon. Second, a ScaledIcon is used to resize the ComponentIcon to fit within the desired space. The Panner composes the two icons and paints a border, indicates the visible sub-rectangle, and responds to navigational input (mouse events). Finally the PannerHandler handles showing, hiding, and resizing the panner in response to user input.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;ComponentIcon&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;This is pretty simple. The main thing to watch out for is that you need to turn off double buffering on the target component, or the rendering ends up having odd side effects on the rest of the display. The underlying component's "isShowing" method must return true, otherwise Swing will short-circuit its painting (there are other methods for getting a component to paint which is not currently in the visible hierarchy).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;ScaledIcon&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;This class takes any other icon, scales and translates the Graphics object so that the delegate icon fits within its allotted size, then delegates to the other Icon to paint. Aspect ratio is preserved by default, but that can be turned off if you want to stretch to fit the available space.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Panner&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;This is a lightweight component with optional transparency (so that you can optionally position it over the panned component and have the underlying component show through). It passes through the "preserveAspectRatio" property to the ScaledIcon.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;PannerHandler&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;This class takes care of the details of deciding where and when to show the panner. It also sets the size appropriately (the example uses a percent value against the size of the panned component, and a fixed offset from the UL corner). Essentially this is the class responsible for configuring all the other classes and hooking them together. It installs a ComponentListener to update the panner size and position whenever the panned component is resized or moves (i.e. is scrolled).&lt;br /&gt;&lt;br /&gt;It's possible to attach the panner itself to the panned component (so you get a thumbnail in a corner of the pane) or have it appear in its own window. The panner size can span the entire visible viewport or some fraction thereof. You can also indicate which corner of the visible viewport to use as anchor if the panner is smaller than the visible viewport.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-115043022116470958?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/115043022116470958/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=115043022116470958' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115043022116470958'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/115043022116470958'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/06/navigating-large-spaces.html' title='Navigating Large Spaces'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-114869920955548689</id><published>2006-05-26T23:01:00.000-04:00</published><updated>2006-05-27T00:52:34.830-04:00</updated><title type='text'>Context-sensitive Edit Menu</title><content type='html'>Most applications have an "Edit" menu where our friends "Cut", "Copy", and "Paste" hange out.  In any non-trivial application, there are certainly more than one thing to which those operations might apply.  How can we hook up these friendly edit operations without having to know beforehand everything that might be Cut or every component that might accept a Paste?&lt;br /&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/DelegatingActions.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png"/&gt;&lt;/a&gt;&lt;br /&gt;Permission is required in order to access the system clipboard.&lt;br /&gt;&lt;br /&gt;Let's take a simple example, which displays two &lt;code&gt;JLists&lt;/code&gt;.  The &lt;code&gt;JList&lt;/code&gt; supports a copy action (via its LAF &lt;code&gt;TransferHandler&lt;/code&gt;, if you're curious).  You can retrieve the action from its &lt;code&gt;ActionMap&lt;/code&gt;.  The &lt;code&gt;ActionMap&lt;/code&gt; is supported by all &lt;code&gt;JComponent&lt;/code&gt;s, and is basically a pool of all &lt;code&gt;Action&lt;/code&gt;s that the component supports.  How best to make Edit-&gt;Copy (or any other menu item, for that matter) invoke the corresponding action from the appropriate &lt;code&gt;JList&lt;/code&gt;?  Note that handling a keyboard shortcut is already taken care of by the &lt;code&gt;JList&lt;/code&gt;'s &lt;code&gt;InputMap&lt;/code&gt;, which maps keystrokes to actions in the &lt;code&gt;ActionMap&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;First, let's define context to be what component has focus.  This may not always be true, but given that keyboard input goes where the focus is, it's reasonable to assume that focus indicates the proper current context.  &lt;br /&gt;&lt;br /&gt;If all we wanted to do was support mapping a keyboard shortcut to an action, we'd have nothing to do, but we'd like to actually make Edit-&gt;Copy do the same thing as that keyboard shortcut.  A quick and dirty hack might be to have the menu item generate a fake keystroke sent to the currently focused component, but that only works if the target action has a keystroke associated with it.  But that's kind of indirect and doesn't smell very good.&lt;br /&gt;&lt;br /&gt;What if the Edit-&gt;Copy menu item reflected a sort of delegating action, whose &lt;code&gt;actionPerformed&lt;/code&gt; logic simply called some other action.  Whenever the context (i.e. focus) changes, we check the focused component's &lt;code&gt;ActionMap&lt;/code&gt; for a copy action.  Whatever we find, we set as the delegate of the delegating action.  If there is no copy action, then the delegating action sets itself disabled. &lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;public class DelegatingAction {&lt;br /&gt;    public DelegatingAction(String name) { super(name); }&lt;br /&gt;    public void setDelegate(Action a) { &lt;br /&gt;        delegate = a; &lt;br /&gt;        setEnabled(delegate != null);&lt;br /&gt;    }&lt;br /&gt;    public void actionPerformed(ActionEvent e) {&lt;br /&gt;        if (delegate != null) { delegate.actionPerformed(e); }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To actually look up the delegates, we can store one or more keys which can be used to identify the actions in the target components.  For custom-defined actions, you can set your action keys to whatever is appropriate.  In order to handle Swing built-in edit actions, we have to account for a few variations.  &lt;code&gt;TransferHandler&lt;/code&gt; uses hard-coded strings "cut", "copy" and "paste", while text components use a few string constants defined in &lt;code&gt;DefaultEditorKit&lt;/code&gt;, which are different from the &lt;code&gt;TransferHandler&lt;/code&gt; strings (e.g. "copy-to-clipboard").  Since these variants exist, we need to check for all of them, since they all correspond to the same Edit menu items.&lt;br /&gt;&lt;br /&gt;To know when to update the delegate's mapping, we can install a property change listener to the keyboard focus manager (available in 1.4+ JREs).  We only want to look at permanent focus changes, so listen to "permanentFocusOwner".&lt;br /&gt;&lt;br /&gt;There's an unexpected glitch caused by the fact that the &lt;code&gt;TransferHandler&lt;/code&gt; edit actions are singletons shared across all transfer handlers.  As such, they expect to deduce the target of the edit operatoin from the &lt;code&gt;ActionEvent&lt;/code&gt; source.  In order to work around this, the delegating action must also keep track of the current component context so that it can reformat the &lt;code&gt;ActionEvent&lt;/code&gt; to have the proper source.&lt;br /&gt;&lt;br /&gt;The singleton implementation also means that the actions' enabled state doesn't reflect whether the target component can actually process the action.  Copy should be disabled if nothing is selected, and paste disabled if the component is not editable.  Leave that to a future article...&lt;br /&gt;&lt;br /&gt;In practice I use a system that's a little more general (it can auto-populate dynamic menus and submenus, and listens for changes on a per-menubar basis instead of per-action, which makes GC a little easier).&lt;br /&gt;&lt;br /&gt;&lt;a href=http://abbot.sf.net/demo/edit-menu.jar&gt;Source is here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-114869920955548689?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/114869920955548689/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=114869920955548689' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114869920955548689'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114869920955548689'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/05/context-sensitive-edit-menu.html' title='Context-sensitive Edit Menu'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-114859259501721934</id><published>2006-05-25T17:29:00.000-04:00</published><updated>2006-05-25T17:34:23.583-04:00</updated><title type='text'>Animated Tree Update</title><content type='html'>I've fixed a few bugs and enabled horizontal drags to change the depth in the hierarchy where the drop target is ambiguous.  Children now slide out from under the parent on expansion, rather than over the parent.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/TreeAnimator.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png"/&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Other things that might be nice to do:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Handle drags from outside the component&lt;br /&gt;&lt;li&gt;Auto-expand collapsed folders&lt;br /&gt;&lt;li&gt;Animate canceled drops&lt;br /&gt;&lt;li&gt;Smooth horizontal transitions&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-114859259501721934?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/114859259501721934/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=114859259501721934' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114859259501721934'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114859259501721934'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/05/animated-tree-update.html' title='Animated Tree Update'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-114841849118972879</id><published>2006-05-23T16:43:00.000-04:00</published><updated>2007-05-27T19:52:43.289-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='drag n drop'/><title type='text'>My Drag Image is Better than Yours</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/6337/472/1600/dragdrop.gif"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/6337/472/320/dragdrop.gif" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;While tinkering with getting suitable ghosted drag images to show up on w32 and linux, I thought I'd whip out the old component decorator yet again, not just to display the drag image, but to display it over &lt;span style="font-style:italic;"&gt;any&lt;/span&gt; java window that might be receiving a drop.  The effect isn't required on OSX (since it already knows how to display a drag image anywhere on the display), but in all cases the same drag handler method is used for generating the actual drag image.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/GhostedDrag.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png"/&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I got started in this drag/drop business because I needed to display different animations and/or decorations under the cursor on a tree table.  You know, highlight the folder if you can drop in the folder, show an insertion line to insert between two things.  The AbstractComponentDecorator was born to handle the highlighting/decoration without having to subclass or otherwise modify the underlying drop target.&lt;br /&gt;&lt;br /&gt;The built-in swing drag and drop makes some things easier, but also hides some of the power of raw DnD.  Its drop target highlighting (temporarily changing the selection) is just a programming shortcut, not a real design for marking a drop target area.  the TransferHandler system used for Swing DnD solves a particular problem in a specific way, but for practical purposes, is not extensible.  It does a nice job of consolidating some drag/drop and clipboard support, but it is &lt;em&gt;really&lt;/em&gt; hard to change its behavior.&lt;br /&gt;&lt;br /&gt;Fundamentally, you don't really need to specify much to define a drag and drop operation:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Indicate the types of actions supported (based on a drag gesture and drag location)&lt;br /&gt;&lt;li&gt;Identify a Transferable (data to be dragged) based on a drag gesture and drag location&lt;br /&gt;&lt;li&gt;Provide a drag image/image offset for feedback while dragging&lt;br /&gt;&lt;li&gt;Indicate the types of actions and data (DataFlavor) supported for drop, possibly refined by actual drop target location&lt;br /&gt;&lt;li&gt;Provide drop target feedback based on current drop target location and effective action&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;The base implementation should handle the following, allowing modification of the default behavior if necessary:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Detect drag gesture and user-requested action, if any&lt;br /&gt;&lt;li&gt;Provide a drag image under the cursor for feedback&lt;br /&gt;&lt;li&gt;Determine the effective action based on the user, source, and target&lt;br /&gt;&lt;li&gt;Set/update cursor to standard platform cursors based on the effective action&lt;br /&gt;&lt;li&gt;Invoke methods to update the state as the effective action changes, handle drop, etc.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Basic DnD leaves you to perform &lt;em&gt;all&lt;/em&gt; of these tasks, while Swing DnD performs  most of them but doesn't allow you to override the behavior.  I wrote a pair of classes to hide the complexity but leave the base DnD functionality available if needed.  The simplicity is that based on a drag location, you just provide a Transferable and optionally an Icon to represent the item being dragged (which can be the whole component or some small part of it).  The supported drag actions are indicated in the constructor.  The primary methods to be defined on the DragHandler follow:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;    protected boolean canDrag(DragGestureEvent e);&lt;br /&gt;&lt;br /&gt;    /** Override to provide an appropriate {@link Transferable}.&lt;br /&gt;     */&lt;br /&gt;    protected abstract Transferable getTransferable(DragGestureEvent e);&lt;br /&gt;&lt;br /&gt;    /** Override this to provide a custom image. &lt;br /&gt;     * @param offset set this to be the offset from the drag source origin &lt;br /&gt;     * to the image origin.  If unchanged, the image is assumed to have the&lt;br /&gt;     * same origin as the drag source.&lt;br /&gt;     */&lt;br /&gt;    protected Icon getDragIcon(DragGestureEvent e, Point offset);&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;For the DropHandler, just a few methods are required.  Painting the drop target is optional and does nothing if not overridden, but that allows you to paint insertion lines, tint the target, or anything else that might be appropriate (decorators are useful for this purpose):&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;    protected abstract boolean isSupported(List flavors); &lt;br /&gt;    /** Update the appearance of the target component.  Normally the decoration&lt;br /&gt;     * should be painted if the event is an instance of &lt;br /&gt;     * {@link DropTargetDragEvent} and cleared otherwise.&lt;br /&gt;     */&lt;br /&gt;    protected void paintDropTarget(DropTargetEvent e, int action); &lt;br /&gt;    /** Indicate whether the drop is acceptable. */ &lt;br /&gt;    protected boolean canDrop(DropTargetDropEvent e, int action);&lt;br /&gt;    /** Handle an incoming drop.  */&lt;br /&gt;    protected void drop(DropTargetDropEvent e, int action);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I also needed more control and feedback over user modifiers.  Basic DnD simply performs a logical AND of the source, user, and drop target actions.  In our app, we support moves, copies, and links.  If the user is requesting a link by holding a modifier, you most definitely &lt;span style="font-style:italic;"&gt;don't&lt;/span&gt; want to move or copy just because link is unsupported but move or copy is (which is the default drag/drop behavior, BTW).  In addition, if the user doesn't use any modifiers, the application should be free to select the most appropriate drop operation based on the context.  The cursors are automatically set to whichever appropriate for the platform, although you can still easily change that behavior if necessary.&lt;br /&gt;&lt;br /&gt;Finally, drag image ghosting has been done to death (see &lt;a href="http://www.javaworld.com/javaworld/javatips/jw-javatip114.html"&gt;Java Tip 114&lt;/a&gt;), but I wanted to have the drag image appear not just in the drag source, but every possible (java) drop target.  I managed to get pretty close, probably as close as you can without resorting to native code (see the demo).&lt;br /&gt;&lt;br /&gt;Source &lt;a href=http://abbot.sf.net/demo/DragDrop-src.jar&gt;here&lt;/a&gt;.  The main class was derived from the JUnit tests for these classes, so it looks a bit crufty.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-114841849118972879?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/114841849118972879/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=114841849118972879' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114841849118972879'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114841849118972879'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/05/my-drag-image-is-better-than-yours.html' title='My Drag Image is Better than Yours'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-114705962766931017</id><published>2006-05-07T23:21:00.000-04:00</published><updated>2007-05-27T19:52:32.777-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='drag n drop'/><title type='text'>Smooth Tree Drop Animation</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/6337/472/1600/tree1.gif"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://photos1.blogger.com/blogger/6337/472/320/tree1.gif" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;Here's an updated tree drop animation demo. The base class can be applied to any tree, and if you're using a DefaultTreeModel with MutableTreeNodes, you don't even have to subclass. &lt;a href="http://abbot.sf.net/demo/SmoothTreeDrop2.jnlp"&gt;Web Start demo: &lt;img src="http://swinglabs.org/images/demobutton.png"/&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This starts to get complicated, what with having multiple possible targets and behavior for a dragged node, so I started a JUnit test case around the main class. Note that none of the tests actually requires displaying the UI. I usually keep around a testReveal() method though, since often it's nice to do exploratory testing, or simply to get a feel for the component's response.&lt;br /&gt;&lt;br /&gt;The horizontal drop location now reflects the appropriate indentation level based on the target parent node. Still needs to track horizontal drag motion, since currently if you're at the end of a few nested nodes, you can't insert at the end of the parent node of your choice.&lt;br /&gt;&lt;br /&gt;I started to implement spring-loaded non-leaf nodes (so when you drag over a non-leaf it auto-expands), but the actual implementation has some details to consider, such as whether to collapse auto-expanded nodes and if so, non-disruptively. If you collapse anything above where the user is dragging, the whole tree shifts upward, effectively dragging the tree under the cursor.&lt;br /&gt;&lt;br /&gt;A nice side effect of handling expansions is that now when you expand a tree node, the children sort of slide out of the parent in a pleasing manner.&lt;br /&gt;&lt;br /&gt;Source for &lt;a href="http://abbot.sf.net/demo/TreeAnimator.java"&gt;smooth tree drop animation&lt;/a&gt; and &lt;a href="http://abbot.sf.net/demo/TreeAnimatorTest.java"&gt;tests&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-114705962766931017?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/114705962766931017/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=114705962766931017' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114705962766931017'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114705962766931017'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/05/smooth-tree-drop-animation.html' title='Smooth Tree Drop Animation'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-114684638530513471</id><published>2006-05-05T12:18:00.000-04:00</published><updated>2006-05-05T13:16:17.170-04:00</updated><title type='text'>Of Swing Frameworks and threaded Actions</title><content type='html'>I've been trying to come up with an implementation that properly encapsulates the most common cases of triggering, tracking, and responding to the results of a long-running operation from a Swing UI. If the JSR for a Swing app framework produces nothing else, it should address this issue, given its ubiquitousness with AWT's single-thread model.&lt;br /&gt;&lt;br /&gt;Basically, you have this:&lt;br /&gt;&lt;ul&gt;   &lt;li&gt;User trigger (button, menu, drop)&lt;/li&gt;   &lt;li&gt;Collect information from the UI&lt;/li&gt;   &lt;li style="color: rgb(204, 0, 0);"&gt;Switch to another thread&lt;/li&gt;   &lt;li style="color: rgb(204, 0, 0);"&gt;Perform processing&lt;/li&gt;   &lt;li&gt;Update the UI (success or failure)&lt;br /&gt;&lt;/li&gt; &lt;/ul&gt; Ideally, I'd like to just write straight code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;public void actionPerformed(ActionEvent e) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    Object[] o = list.getSelectedItems();&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    try {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        Object result = processObjects(o);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;         list.setSelectedItem(result);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    catch(CanceledException e) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;         &lt;/span&gt;&lt;span style="color: rgb(153, 51, 0);font-family:courier new;"&gt;// ignore the results&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    catch(Exception e) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        list.setSelectedItem(null);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        status.setText("Error: " + e.getMessage());&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Which makes it very clear what's going on. However, as anyone who has used Swing for more than five minutes is aware, this makes your UI freeze for the duration of the operation.&lt;br /&gt;&lt;br /&gt;I'd prefer an idiom that looks as close as possible to the original code, perhaps an "executor" block:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;public void actionPerformed(ActionEvent e) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    Object[] o = list.getSelectedItems();&lt;/span&gt;&lt;br /&gt;   &lt;span style="font-family:courier new;"&gt;try {&lt;br /&gt;       Object result = null;&lt;br /&gt;       Handle handle = execute-off-edt {&lt;br /&gt;           result = processObjects(o);&lt;br /&gt;       }&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt; &lt;span style="font-family:courier new;"&gt;       list.setSelectedItem(result);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    catch(CanceledException e) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;       &lt;/span&gt;&lt;span style="color: rgb(153, 51, 0);font-family:courier new;"&gt;// ignore the results&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    catch(Exception e) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;       list.setSelectedItem(null);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;       status.setText("Error: " + e.getMessage());&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt; This idiom would let me transparently collect inputs and pass them to a threaded invocation without a lot of messy assignments (which do absolutely nothing to communicate what the code is trying to do). The handle would give me some sort of control over the spawned thread. And when the thing is done or fails, I don't have to do messy assignments to get its results. It's the sort of thing that Java's anonymous inner classes overcome; it's syntactic sugar, but it sure as hell simplifies how the code looks.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://spin.sf.net"&gt;Spin&lt;/a&gt;plays some neat tricks with interfaces and proxies to let you use something close to this idiom (as do FoxTrot and SwingWorker, albeit less elegantly), but it has some unfortunate restrictions . For instance, if you start a Spin invocation from a mouse press event handler, the mouse release event is likely to be processed during your long-running method, and the rest of the mouse press handling will happen &lt;span style="font-style: italic;"&gt;after&lt;/span&gt; the mouse release handling.&lt;br /&gt;&lt;br /&gt;The reason the idiom above &lt;span style="font-style: italic;"&gt;can't&lt;/span&gt;be used as is, is that in order for the event dispatch processing to continue, the current method needs to return immediately. This is the Spin issue. If you don't return, then you may have event handlers that haven't run yet, and end up running out of turn.  I doubt that part of AWT is going to change, so I have to look at variations of the idiom instead.&lt;br /&gt;&lt;br /&gt;So I've been playing around with a code pattern that handles the before/during/after pattern, including thread-hopping, cancelation and failure. It's probably not universal, since there are some situations it fits much better than others. I've put it in the form of a javax.swing.Action, since that's a pretty common method of triggering actions in Swing.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;abstract class AsynchronousAction {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    public void actionPerformed(final ActionEvent e) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        preInvoke(e);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        &lt;span style="color: rgb(153, 51, 0);"&gt;// could use Executor here; implement the before/during/after pattern&lt;br /&gt;      // so you don't have to reimplement it on every action&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;        new Thread() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;            public void run() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                Throwable failure = null;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                try {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                    invoke(e);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                catch(Throwable e) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                     failure = e;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                   SwingUtilities.invokeLater(new Runnable() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                         public void run() {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                        postInvoke(failure);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                         }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                   });&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;            }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        }.start();&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;span style="color: rgb(153, 51, 0);"&gt;/** Change/update UI state here, collect input */&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    protected abstract void preInvoke(ActionEvent e);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;span style="color: rgb(153, 51, 0);"&gt;/** Invoked from a non-UI thread. */&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    protected abstract void invoke(ActionEvent e);&lt;br /&gt;  &lt;span style="color: rgb(153, 51, 0);"&gt;/** Invoked when everything happens normally. */&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;    protected abstract void postInvoke();&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    protected void handleFailure(Throwable failure) { }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    protected abstract void postInvoke(Throwable failure) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        if (failure == null) postInvoke();&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        else {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;           handleFailure(failure);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;     }&lt;br /&gt;  }&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I've left out the "cancel" functionality (and automatic disabling while the action is active) for clarity (using thread local variables, the spawned thread can check whether it should bother continuing or reporting its results).&lt;br /&gt;&lt;br /&gt;So this works pretty well for, say, a "navigate" method which loads some data from a remote source and displays it. I can cancel the current one and invoke it with a different target.  Any canceled action never reaches the postInvoke() method, so I don't have to explicitly add and remove listeners to yet another listener interface.  It gets a bit clunky when there's a lot of data to pass to the invoke() method. Where you'd like to just pass parameters, you have to store in member data, which introduces synchronization problems when dealing with multiple invocations.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-114684638530513471?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/114684638530513471/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=114684638530513471' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114684638530513471'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114684638530513471'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/05/of-swing-frameworks-and-threaded.html' title='Of Swing Frameworks and threaded Actions'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-114648266380601085</id><published>2006-05-01T07:22:00.000-04:00</published><updated>2006-05-02T21:13:14.593-04:00</updated><title type='text'>And now on a tree...</title><content type='html'>&lt;a href="http://photos1.blogger.com/blogger/6337/472/1600/tree-drop.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://photos1.blogger.com/blogger/6337/472/320/tree-drop.0.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;Here's a quick edit of the smooth list drop applied to a JTree. It doesn't actually handle the drop - it still needs to implement changes to the tree model.&lt;br /&gt;&lt;br /&gt;It looks best with a LAF that displays vertical lines, since the lines get stretched as space is made for the insert (OSX doesn't draw connector lines :().&lt;br /&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/SmoothTreeDrop.jnlp"&gt;Web Start&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;(There was a race condition in the animator in the first posting which would sometimes lock up; thanks to ploiku for the stack trace).&lt;br /&gt;&lt;br /&gt;A full implementation for the tree would need to have a few abstract methods defined:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;boolean canMove(TreePath src);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;boolean canMove(TreePath src, TreePath newParent, int index);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;void move(TreePath src, TreePath newParent, int index);&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You'd probably want to collapse a non-leaf when dragging it, allow drops onto collapsed nodes, auto-expand collapsed nodes after a delay, and probably a few other things I haven't thought of.  Too much for one afternoon, drat.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-114648266380601085?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/114648266380601085/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=114648266380601085' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114648266380601085'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114648266380601085'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/05/and-now-on-tree.html' title='And now on a tree...'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-114641059892975998</id><published>2006-04-30T10:48:00.000-04:00</published><updated>2007-05-27T19:52:55.611-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='drag n drop'/><title type='text'>Smooth JList Drop Target Animation</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://abbot.sf.net/demo/smooth.gif"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://abbot.sf.net/demo/smooth.gif" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/SmoothListDrop.jnlp"&gt;Web Start&lt;/a&gt; and &lt;a href="http://abbot.sf.net/demo/ListAnimator.jar"&gt;source&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I ran across this &lt;a href="http://jroller.com/page/swinguistuff?entry=animated_jlist"&gt;nice effect&lt;/a&gt; for indicating a drop target in a JList. The ghosted image of an item dragged from the list appears to push aside other entries as it moves through the list of items. A very simple method results in very smooth animation that can span several cells at once. The basic idea is to maintain a list of current positions for all cells, and gradually adjust those positions to match what their final positions would be with a proposed insertion. The animation consists of moving half the distance from the current to the final position (rounding up the final pixel difference). The problem with the sample implementation is that it requires tweaks to both the list model and its LAF UI.  &lt;br /&gt;&lt;br /&gt;I thought it would be nice to be able to apply this effect to any list without having to replace the LAF UI.  Since lists render themselves cell by cell anyway, it shouldn't be that hard to just trick the list into painting each cell where &lt;em&gt;we&lt;/em&gt; want it, instead of in its default location.&lt;br /&gt;&lt;br /&gt;I focused on just handling an internal drag, but the API on the smoother could easily be tweaked to accept native drags from external sources.&lt;br /&gt;&lt;br /&gt;First, to separate the functionality from user triggers, I decided to put the mouse event handling into the demo code. I've found that in UI testing, it's almost always beneficial to be able to drive bits of the UI from direct programmatic methods (would that Swing followed this pattern, so it would be easier to test). The mouse listener decides when to start, update, and end the drag, and calls in to the smoother's corresponding methods.&lt;br /&gt;&lt;br /&gt;The smoother itself is a &lt;a href="http://rabbit-hole.blogspot.com/2006/04/decorating-swing-components.html"&gt;decorator &lt;/a&gt;which performs the painting of individual cells by having the original list paint itself fully and then clipping out the unwanted stuff.  This could be optimized by just asking for the rendering components, but that potentially omits additional decoration provided by the LAF.  So I locate the cell I want within the full list as painted by the LAF, and copy that image to its "floating" location.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;pre&gt;&lt;span style="color: rgb(153, 51, 0);"&gt;    // Paint the background for the insertion point&lt;/span&gt;&lt;br /&gt;  Rectangle b = getDecorationBounds();&lt;br /&gt;  g.setColor(list.getBackground());&lt;br /&gt;  g.fillRect(b.x, b.y, b.width, b.height);&lt;br /&gt;  for (int i=0;i &amp;lt; list.getModel().getSize();i++) {&lt;br /&gt;      if (i == draggedIndex)&lt;br /&gt;           continue;&lt;br /&gt;      Rectangle r = getCurrentCellBounds(i);&lt;br /&gt;      Graphics g2 = g.create(r.x, r.y, r.width, r.height);&lt;br /&gt;      Rectangle r2 = list.getCellBounds(i, i);&lt;br /&gt;      ((Graphics2D)g2).translate(0, -r2.y);&lt;br /&gt;      list.paint(g2);&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;The Graphics object is set up to paint only in the desired area, then translated such that the JList will paint the desired cell into that area. We skip the index being dragged, since we want to handle that separately.&lt;br /&gt;&lt;br /&gt;To draw the ghosted image, I simply used another decorator that uses the JList to render the dragged item. As the insertion location is updated by the mouse listener, the ghost image adjusts its vertical drawing position (being careful to clamp the vertical bounds to the start and end of the list).&lt;br /&gt;&lt;br /&gt;The insertion point could use something more fancy if needed, like using the cell renderer or explicitly painting the rectangle. Just leaving the background exposed works well in this case, though.&lt;br /&gt;&lt;br /&gt;A timer task periodically moves each cell's current location toward its final location, and triggers a repaint if any of the locations actually changes. This should be optimized to track a smaller range of cells (like only those visible).&lt;br /&gt;&lt;br /&gt;The basic calculation of a cell's desired position simply calculates its normal position, then adjusts for any preceding dragged item (to be removed) and any preceding insertion point (to be inserted). The current locations start off with no dragged item or insertion point, and gradually float to their final positions based on a dragged item and possibly changing insertion point. Note that if you rapidly drag across several items, they &lt;span style="font-style: italic;font-family:times new roman;"&gt;all&lt;/span&gt; will float smoothly even as their destination changes.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-114641059892975998?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://abbot.sf.net/demo/SmoothListDrop.jnlp' title='Smooth JList Drop Target Animation'/><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/114641059892975998/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=114641059892975998' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114641059892975998'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114641059892975998'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/04/smooth-jlist-drop-target-animation.html' title='Smooth JList Drop Target Animation'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-114622955557937144</id><published>2006-04-28T08:48:00.000-04:00</published><updated>2006-04-28T16:46:22.346-04:00</updated><title type='text'>Matisse First Impressions</title><content type='html'>My compliments to the Matisse crew (and a hat tip to those who have walked this path before). This is truly what a layout editor should look like. Thanks for examining prior art and learning from it. I now have a few projects that now have a netbeans project folder and extra .form files.&lt;br /&gt;&lt;br /&gt;That said, here are a few bumps I've run into so far...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Custom Component Creation&lt;/span&gt;&lt;br /&gt;Rather than inline code in the XML form, initComponents should either take arguments or simply rely on field contents. Putting the code in XML makes it infeasible to automatically refactor. For example, I have a filename text field that provides a browsing action synched to the editability/enabled state of the text field. So to create the button, I need&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;new JButton(field.getBrowseAction())&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I'd rather just create the component myself prior to initComponents(), and have initComponents() know that that field has already been initialized. Perhaps split between initializing and doing the layout?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Changing Component Names&lt;/span&gt;&lt;br /&gt;If you edit the member field name for a component, you don't get a corresponding refactor of the code.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Alternate Components in Layout&lt;/span&gt;&lt;br /&gt;I'm trying to figure out the best way to swap out two different panels. One has just a password, one has a filename and a password. I'd like to swap between the two and have the layout still work, but the layout assumes that the component hierarchy is static. I'll probably just set up a container panel with the appropriate resizing attributes, then add/remove the alternates as needed.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Strings&lt;/span&gt;&lt;br /&gt;I should be able to substitute a string lookup for a hard-coded string, whether in labels or text fields or whatever. Granted, I can override whatever happens in initComponents, but then I'm maintaining the same string twice - once in my string/properties tables, and again in the UI designer.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Cross-Panel alignment&lt;/span&gt;&lt;br /&gt;Sometimes I'd like to operate on a number of components as a group (resize the right edge, for instance) and this doesn't seem possible.  It'd be nice if the alignment lines could be inferred and then  themselves could be draggable.  Putting things into sub-panels has the unfortunate side-effect that you can only align with other things in that panel.&lt;br /&gt;&lt;br /&gt; &lt;span style="font-weight: bold;"&gt;Grid Size/Spacing&lt;/span&gt;&lt;br /&gt;I'm surprised this one got through.  I'm stuck with 10 pixel margins that I can't change.  So my choices are 10 or 0 pixels.  Ugh.  There's a preference for Grid X/Grid Y, but it doesn't seem to have any effect.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-114622955557937144?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/114622955557937144/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=114622955557937144' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114622955557937144'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114622955557937144'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/04/matisse-first-impressions.html' title='Matisse First Impressions'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-114529283843059617</id><published>2006-04-17T12:50:00.002-04:00</published><updated>2008-09-25T20:52:57.289-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='overlay'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='decoration'/><category scheme='http://www.blogger.com/atom/ns#' term='validation'/><title type='text'>Decorator/Overpainter Update</title><content type='html'>I've updated the decorator to handle most LAF backgrounds.  You can now modify the background on just about anything that relies on a parent to paint its background.&lt;br /&gt;&lt;br /&gt;The selection marquee is also no longer clipped to its parent component.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/DecoratorDemo.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png"/&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Now you might say "I can just paint on my glass pane" and you'd be right except that:&lt;br /&gt;* Your glass pane eats all user input events once it's visible unless you explicitly redirect them&lt;br /&gt;* You glass pane doesn't know anything about what's under it&lt;br /&gt;* It always paints *above* everything else&lt;br /&gt;&lt;br /&gt;The AbstractComponentDecorator is a bit more flexible in that you can target all or part of a specific component or a specific area of your window.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-114529283843059617?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/114529283843059617/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=114529283843059617' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114529283843059617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114529283843059617'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/04/decoratoroverpainter-update.html' title='Decorator/Overpainter Update'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-114450664048360808</id><published>2006-04-08T09:53:00.004-04:00</published><updated>2008-09-25T20:53:30.126-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='overlay'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='decoration'/><category scheme='http://www.blogger.com/atom/ns#' term='validation'/><title type='text'>Decorating/Overpainting Swing Components</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://abbot.sf.net/demo/DecoratorDemo.jnlp"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://bp1.blogger.com/_ACEoHFt4yoQ/SFEaFvdkTAI/AAAAAAAAABw/dwTKaVSUqpE/s320/Picture+1.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5210974929786522626"/&gt;&lt;/a&gt;&lt;br /&gt;Have you ever wanted to change the way an existing component appears, just a little, without having to go and rewrite it or subclass it?&lt;br /&gt;&lt;br /&gt;The image at right demonstrates several implementations of an abstract base class which puts an arbitrary decoration over an existing component. This technique allows you to do any of the following:&lt;br /&gt;&lt;ul&gt;  &lt;br /&gt;&lt;li&gt;Put labels on a scrolled component which stay fixed relative to the scroll pane rather than the scrolled component (useful for labeling horizontal lines on a panned display).&lt;/li&gt;&lt;li&gt;Put an icon badge anywhere on a component, such as a stop sign over invalid text in a text field&lt;br /&gt;&lt;/li&gt; &lt;li&gt;Put tooltips on a component that you didn't write (like adding annotations to someone else's work).&lt;/li&gt;&lt;li&gt;Dim the entire painted image to gray, or fade it into the background color to make the component look disabled.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Change the appearance of component's background (gradients, stripes, or polka-dots).  &lt;br /&gt;&lt;/li&gt;   &lt;li&gt;Apply animation effects to the component.&lt;br /&gt;  &lt;/li&gt;   &lt;li&gt;Marching ants/Rubber band selection rectangle (xor is &lt;em&gt;so&lt;/em&gt; Seventies).&lt;br /&gt;&lt;/li&gt; &lt;li&gt;Highlight all or part of an existing component to indicate the effective target of a drop operation (changing the selection isn't always the best solution).&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;a href="http://abbot.sf.net/demo/DecoratorDemo.jnlp"&gt;&lt;img src="http://swinglabs.org/images/demobutton.png"/&gt;&lt;/a&gt; to see a few of these decorations in action.&lt;br /&gt;&lt;br /&gt;Note that the code for each of the following decorations consists of the painting logic and optionally positioning logic, which is all you should be concerned about.&lt;br /&gt;&lt;br /&gt;The implementation for drawing a badge:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;class Warning extends AbstractComponentDecorator {&lt;br /&gt;    private final int SIZE = 16;&lt;br /&gt;    public Warning(JTextField f) {&lt;br /&gt;        super(f);&lt;br /&gt;    }&lt;br /&gt;    &lt;/span&gt;&lt;span style="font-size:85%;color:brown;"&gt;/** Position the badge at the right-most edge. */&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;    public Rectangle getDecorationBounds() {&lt;br /&gt;        Rectangle r = super.getDecorationBounds();&lt;br /&gt;        Insets insets = getComponent().getInsets();&lt;br /&gt;        r.x += r.width - SIZE - 1;&lt;br /&gt;        r.y += (r.height - SIZE) / 2;&lt;br /&gt;        if (insets != null) {&lt;br /&gt;            r.x -= insets.right;&lt;br /&gt;        }&lt;br /&gt;        return r;&lt;br /&gt;    }&lt;br /&gt;    public void paint(Graphics graphics) {&lt;br /&gt;        Rectangle r = getDecorationBounds();&lt;br /&gt;        Graphics2D g = (Graphics2D)graphics;&lt;br /&gt;        GeneralPath triangle = new GeneralPath();&lt;br /&gt;        triangle.moveTo(r.x + SIZE/2, r.y);&lt;br /&gt;        triangle.lineTo(r.x + SIZE-1, r.y + SIZE-1);&lt;br /&gt;        triangle.lineTo(r.x, r.y + SIZE-1);&lt;br /&gt;        triangle.closePath();&lt;br /&gt;        g.setColor(Color.yellow);&lt;br /&gt;        g.fill(triangle);&lt;br /&gt;        g.setColor(Color.black);&lt;br /&gt;        g.draw(triangle);&lt;br /&gt;        g.drawLine(r.x + SIZE/2, r.y + 3, r.x + SIZE/2, r.y + SIZE*3/4 - 2);&lt;br /&gt;        g.drawLine(r.x + SIZE/2, r.y + SIZE*3/4+1, r.x + SIZE/2, r.y + SIZE - 4);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;The implementation for dimming:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;class Dimmer extends AbstractComponentDecorator {&lt;br /&gt;    public Dimmer(JComponent target) {&lt;br /&gt;        super(target);&lt;br /&gt;    }&lt;br /&gt;    &lt;/span&gt;&lt;span style="font-size:85%;color:brown;"&gt;/** Paint using a transparent version of the bg color. */&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;    public void paint(Graphics g) {&lt;br /&gt;        Color bg = getComponent().getBackground();&lt;br /&gt;        g.setColor(new Color(bg.getRed(), bg.getGreen(), bg.getBlue(), 200));&lt;br /&gt;        Rectangle r = getDecorationBounds();&lt;br /&gt;        g.fillRect(r.x, r.y, r.width, r.height);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;The implementation for semi-scrolling labels:&lt;br /&gt;&lt;pre&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;   private static final int SIZE = 1000;&lt;br /&gt;   private static final int BLOCK = 20;&lt;br /&gt;   private static final int LINE = 20;&lt;br /&gt;&lt;br /&gt;   private static class Labeler extends AbstractComponentDecorator {&lt;br /&gt;       public Labeler(JComponent target) {&lt;br /&gt;           super(target);&lt;br /&gt;       }&lt;br /&gt;       &lt;/span&gt;&lt;span style="font-size:85%;color:brown;"&gt;/** Ensure the label stays at the left-most visible&lt;br /&gt;        * edge of the component.&lt;br /&gt;        */&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;       public Rectangle getDecorationBounds() {&lt;br /&gt;           Rectangle r = super.getDecorationBounds();&lt;br /&gt;           Rectangle visible = getComponent().getVisibleRect();&lt;br /&gt;           if (r.x &amp;lt; visible.x)&lt;br /&gt;              r.x = visible.x;&lt;br /&gt;           return r;&lt;br /&gt;       }&lt;br /&gt;       public void paint(Graphics g) {&lt;br /&gt;           Rectangle r = getDecorationBounds();&lt;br /&gt;           for (int i=0;i &amp;lt; SIZE;i+= LINE) {&lt;br /&gt;               g.drawString("label " + (i/LINE + 1),&lt;br /&gt;                            r.x, r.y + i + g.getFontMetrics().getAscent() + 2);&lt;br /&gt;           }&lt;br /&gt;       }&lt;br /&gt;   } &lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This class started out as a way to highlight different drop targets in a tree or table, and a method for the Costello editor to highlight areas of a component to be captured as an image (without actually setting the target component's background). I've subsequently used it for other decorations as noted above.&lt;br /&gt;&lt;br /&gt;The whole idea would be trivial if Swing provided a hook into a component's paint method. There is no such explicit hook, and no implicit ones either without getting into a lot of complexity (perhaps by writing your own repaint manager). Since writing a repaint manager seems totally orthogonal to decorating a single component, we take a different tack.&lt;br /&gt;&lt;br /&gt;My first implementation was to simply add a sibling or child component into the hierarchy on top of the first (and indeed, that implementation is still in use by the Costello editor). Unfortunately, the normal component hierarchy doesn't really consider z ordering, so the results are not always consistent. You might get things painted properly 90% of the time, but occasionally have your decoration occluded by a scroll pane, or the fact that there's an extra component in the hierarchy causes the layout manager to occasionally freak out. So it's okay for a proof of concept, but kinda lame in practice.&lt;br /&gt;&lt;br /&gt;There is, however, a Swing component that knows about z ordering. Most Swing components will reside somewhere below a JLayeredPane (see the javadoc for javax.swing.RootPaneContainer for details). This little component has the capability of painting things in layers, which is &lt;em&gt;just&lt;/em&gt; the capability we're looking for. The JLayeredPane even has predefined layers and was explicitly designed for stacking components in a known Z order (it also has a sub-layer ordering referred to as "position", but this doesn't seem to work).&lt;br /&gt;&lt;br /&gt;So the basic idea is that for any decoration, we establish a painting component as a child of the JLayeredPane which is positioned in a layer above the decorated component. The Swing painting mechanism will automatically ensure that our decoration gets painted whenever the decorated component is painted, and that the decoration happens &lt;em&gt;after&lt;/em&gt; the target component is painted.&lt;br /&gt;&lt;br /&gt;A few details to note:&lt;br /&gt;&lt;ul&gt;  &lt;br /&gt;&lt;li&gt;The decorator must maintain proper position and size with respect to the target component. If the decoration is in the lower right corner of the component, it should stay there if the component grows.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The decorator must clip its painting according to how much of the target component is visible. If the component is partially obscured within a scroll pane, the decorator should be clipped in a similar fashion.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The decorator needs to track the target component's visibility. If the component is on a tabbed pane and a different tab is displayed, the decoration needs to be hidden.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Painting &lt;em&gt;under&lt;/em&gt; the decorated component is a bit more tricky. The current implementation for decorating component backgrounds assumes the current LAF doesn't change how ComponentUI paints the default background (gtk/synth probably won't work well). I haven't really looked into this much.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It's possible to composite with the existing graphics (so you could, for instance, paint only where there's already a blue pixel), but this capability varies by platform and VM version.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Full source for the &lt;a href="http://furbelow.svn.sourceforge.net/viewvc/furbelow/trunk/src/furbelow/DecoratorDemo.java?&amp;view=markup"&gt;demo&lt;/a&gt; and &lt;a href="http://furbelow.svn.sourceforge.net/viewvc/furbelow/trunk/src/furbelow/AbstractComponentDecorator.java?view=markup"&gt;AbstractComponentDecorator class&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-114450664048360808?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/114450664048360808/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=114450664048360808' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114450664048360808'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/114450664048360808'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2006/04/decoratingoverpainting-swing.html' title='Decorating/Overpainting Swing Components'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_ACEoHFt4yoQ/SFEaFvdkTAI/AAAAAAAAABw/dwTKaVSUqpE/s72-c/Picture+1.png' height='72' width='72'/><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-109115321372557215</id><published>2004-07-29T21:14:00.000-04:00</published><updated>2004-07-30T01:16:06.563-04:00</updated><title type='text'>Automatic email support for Java</title><content type='html'>&lt;p&gt;    I got tired of asking for more information in bug reports in the &lt;a href="http://abbot.sf.net"&gt;Costello&lt;/a&gt; editor, so I figured I'd put in a link to automatically post to the mailing list with the user's environment info. It would also help direct all traffic of that sort to the proper place, rather than having some people post to forums, some to the mailing list, or others filing bug reports or RFEs (even when they're just asking a question).&lt;/p&gt;&lt;p&gt;    You'd think that eons after the introduction of email and bug reports and that sort of thing it'd take no more than a line of code, but alas, not so for java. And I certainly don't want to introduce yet another library dependency. So I says to meself, how hard can it be? I mean, OSX already gives you a nice "open" command to launch something appropriate for whatever argument you give it (so much faster than digging through folders, too). Windows has the "start" command, and it's easy enough to pass an arbitrary script to /bin/sh for any *nix variants. So I set off to get my Runtime.exec on...&lt;/p&gt;&lt;h2&gt;Check out the landscape&lt;/h2&gt;&lt;p&gt;First off, someone on the Apple java-dev list suggested throwing a mailto: URL at the "open" command.  &lt;br /&gt; &lt;/p&gt;&lt;pre&gt;&lt;code&gt;% open mailto:abbot-users@lists.sf.net&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Cool, that works, now what about subject and body?  Quick google for proper mailto: syntax, reveals &lt;br /&gt; &lt;pre&gt;&lt;code&gt;% open 'mailto:abbot-users@lists.sf.net?subject=bug report&amp;body=java version'&lt;br /&gt;open[]: No such file mailto:abbot-users@lists.sf.net?subject=bug report&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Bummer, doesn't quite work, must be those spaces.  Duh, can you say URL encoding.  Try again...&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;i&gt;(For you *nix newbies, an ampersand is normally interpreted by the shell as direction to start the preceding command as a background process, thus the single quotes are required. When we stuff this into Runtime.exec, we can drop the quotes because Runtime.exec doesn't use the shell)&lt;/i&gt;&lt;/span&gt;&lt;br /&gt; &lt;pre&gt;&lt;code&gt;% open 'mailto:abbot-users@lists.sf.net?subject=bug%20report&amp;body=java%20version'&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Aha, note to self: make sure you URL-encode the subject and body, but make sure the encoder uses %20 and not '+', b/c the pluses won't get converted back into spaces.&lt;br /&gt; &lt;br /&gt;Over to w32 now, and see if "start" works the same way.  I normally use &lt;a href="http://www.cygwin.com"&gt;bash&lt;/a&gt; on windows, but this test wants to be in the Windows command shell.&lt;br /&gt; &lt;pre&gt;&lt;code&gt;% cmd.exe&lt;br /&gt;c:\&gt; start mailto:abbot-users@lists.sf.net?subject=bug%20report&amp;amp;body=java%20version&lt;br /&gt;'body' is not recognized as an internal or external command,&lt;br /&gt;operable program or batch file.&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Somehow I didn't feel quite so insulted when OSX said it. A quick look at the cmd.exe "man page" (actually not so quick. the MSDN page was way down the google search, and fourth even on the MSDN search). Seems some characters are special. We won't tell you all of them, but here are some of them. Oh great. In this case, seems like the ampersand. Well, let's just try it with quotes (supposedly you can escape individual characters with '^', but then things start getting nasty).&lt;br /&gt; &lt;pre&gt;&lt;code&gt;c:\&gt; start "mailto:abbot-users@lists.sf.net?subject=bug%20report&amp;body=java%20version"&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;What the @#$%!  I get a new command prompt with the title set.  Let's try&lt;br /&gt; &lt;pre&gt;&lt;code&gt;c:&gt; start /help&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span style="font-weight: bold;"&gt;@#$%!&lt;/span&gt;&lt;br /&gt; &lt;pre&gt;&lt;code&gt;c:&gt; start /?&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Ah, there it is.  If you pass the first argument in quotes, it becomes the title of whatever you "start".  Try again&lt;br /&gt; &lt;pre&gt;&lt;code&gt;c:&gt; start "title" "mailto:abbot-users@lists.sf.net?subject=bug%20report&amp;body=java%20version"&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Ah, finally. Up comes some flavor of Outlook. Good enough. What about *nix? Probably as easy as just picking a few popular mail programs and seeing if they exist. I'm sure *nix programs can handle a silly little mailto URL. Actually, most of the lame bug reports I get are from w32 users. The *nix and OSX users usually have much more esoteric problems. So off to the java code to provide some minimal automatic bug reporting.&lt;p&gt;&lt;/p&gt;&lt;h2&gt;Write up a TestCase&lt;/h2&gt;&lt;p&gt;I'd previously written a simple exec handler that would capture all the program output and error streams, so I'll use that here. I'm not concerned with the output (I don't expect any) and only want to throw up an error if the command somehow fails. So write up a little JUnit test case for it, let's call it the LauncherTest. I've rigged the test to only run if I invoke it manually; since it requires user interaction to get rid of the launched processes, I'd rather it not run with the rest of the automated suite. Writing the test case makes me think about exactly how I'd like to use this thing. Namely, I just want to send a post to the support mailing list with a prepared body. And to make sure I don't get any lame "Help!" subject lines, might as well fill that in as well. I just want to invoke a single line of code to preformat an email. Fill in the target address, subject, and sample body. No need for a complex API.&lt;br /&gt; &lt;/p&gt;&lt;pre&gt;&lt;code&gt;package abbot.util;&lt;br /&gt;&lt;br /&gt;import junit.framework.*;&lt;br /&gt;&lt;br /&gt;public class LauncherTest extends TestCase {&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:brown;"&gt;// only run these tests manually, since we can't control the launched apps automatically&lt;/span&gt;        &lt;br /&gt;    private static boolean run = false;&lt;br /&gt;&lt;br /&gt;    public void testMailTo() throws Exception {&lt;br /&gt;        if (!run)&lt;br /&gt;            return;&lt;br /&gt;        String SUBJECT = "subject";&lt;br /&gt;        String BODY = "body";&lt;br /&gt;        Launcher.mail("abbot-users@lists.sf.net", SUBJECT, BODY);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static void main(String[] args) {&lt;br /&gt;        run = true;&lt;br /&gt;        junit.textui.TestRunner.main(new String[] { "-c", LauncherTest.class.getName() });&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;Make the TestCase work&lt;/h2&gt;&lt;p&gt;Now, whipping out my trusty Runtime.exec wrapper, I simply wrap up the tricks I was playing in the console.  &lt;br /&gt; &lt;/p&gt;&lt;pre&gt;&lt;code&gt;package abbot.util;&lt;br /&gt;&lt;br /&gt;import java.net.URLEncoder;&lt;br /&gt;import java.util.ArrayList;&lt;br /&gt;import java.util.Arrays;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import abbot.Log;&lt;br /&gt;import abbot.Platform;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:brown;"&gt;/** Mail and browser launcher which augments &lt;code&gt;Runtime.exec()&lt;/code&gt;        &lt;br /&gt; * functions.  Provides for built-in email and web browser support.             &lt;br /&gt; */&lt;/span&gt;&lt;br /&gt;public class Launcher {&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:brown;"&gt;/** Sort of URL-encode the given string.  */&lt;/span&gt;&lt;br /&gt;    public static String encode(String s) {&lt;br /&gt;        &lt;span style="color:brown;"&gt;// simple implementation for now&lt;/span&gt;&lt;br /&gt;        return s;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:brown;"&gt;/** Format a message to the given user with the given subject and message   &lt;br /&gt;        body, including CC and BCC lists. */&lt;/span&gt;&lt;br /&gt;    public static void mail(String user, String subject, String body) throws IOException {&lt;br /&gt;        StringBuffer mailto = new StringBuffer("mailto:" + user + "?");&lt;br /&gt;&lt;br /&gt;        mailto.append("Subject=" + encode(subject)&lt;br /&gt;                      + "&amp;" + "Body=" + encode(body) + "");&lt;br /&gt;        open(mailto.toString());&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    &lt;span style="color:brown;"&gt;/** Use the given command/program to open the given target. */&lt;/span&gt;&lt;br /&gt;    public static void open(String command, String target) throws IOException {&lt;br /&gt;        ArrayList args = new ArrayList();&lt;br /&gt;        if (Platform.isOSX()) {&lt;br /&gt;            args.add("open");&lt;br /&gt;        }&lt;br /&gt;        else if (Platform.isWindows()) {&lt;br /&gt;            &lt;span style="color:brown;"&gt;// Probably won't work on win9x, but just in case&lt;/span&gt;                           &lt;br /&gt;            args.add(Platform.isWindows9X()&lt;br /&gt;                     ? "command.com" : "cmd.exe");&lt;br /&gt;            args.add("/c");&lt;br /&gt;            args.add("start");&lt;br /&gt;            args.add("\"Title\"");&lt;br /&gt;            &lt;span style="color:brown;"&gt;// Always quote the argument, just in case                      &lt;br /&gt;            // See MS docs for cmd.exe; &amp;, |, and () must be escaped with   &lt;br /&gt;            // ^ or double-quoted.  semicolon and comma are command         &lt;br /&gt;            // argument separators, and probably require quoting as well.&lt;/span&gt;   &lt;br /&gt;            target = "\"" + target + "\"";&lt;br /&gt;        }&lt;br /&gt;        else {&lt;br /&gt;            throw new Error("Not done yet!");&lt;br /&gt;        }&lt;br /&gt;        args.add(target);&lt;br /&gt;&lt;br /&gt;        String[] cmd = (String[])args.toArray(new String[args.size()]);&lt;br /&gt;        String output = ProcessOutputHandler.exec(cmd);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;The windows version actually required a bit of trial and error to get the argument accepted properly. I know I'm going to have to encode the subject and body, but I don't know all the details yet. So true to TDD form, I start with a very basic encoding implementation. Running the unit test on OSX brings up the Mail app, and running it on w32 brings up Outlook.&lt;br /&gt;&lt;h2&gt;Add just the features I need&lt;/h2&gt;&lt;p&gt;So far, so good. Now I want a little more complex subject and body.  First, adjust the test to use more representative values:&lt;br /&gt; &lt;/p&gt;&lt;pre&gt;&lt;code&gt;    String SUBJECT = "with spaces";&lt;br /&gt;    String BODY = "with spaces and &lt;[special % characters]&gt;";&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;I already know I need some flavor of URL encoding, so I'll take a slight shortcut and throw that in immediately:&lt;br /&gt; &lt;pre&gt;&lt;code&gt;    public static String encode(String s) {&lt;br /&gt;        return URLEncoder.encode(s);&lt;br /&gt;    }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Running the test we find that OSX's Mail will decode %20 but not '+', and URLEncoder.encode(String) turns spaces into '+'. So we need to pre-escape spaces, then replace them with the proper %20 encoding. What about w32? Outlook won't handle &lt;i&gt;either&lt;/i&gt; encoding, but it will accept the spaces directly.  So we immediately add a test for the encoding:&lt;br /&gt; &lt;/p&gt;&lt;pre&gt;&lt;code&gt;    public void testEncoding() {&lt;br /&gt;        String[][] strings = {&lt;br /&gt;            { "with space", Platform.isOSX() ? "with%20space" : "with space" },&lt;br /&gt;            &lt;span style="color:brown;"&gt;//TODO check encoding of other characters&lt;/span&gt;&lt;br /&gt;        };&lt;br /&gt;        for(int i=0;i &lt; strings.length;i++) {&lt;br /&gt;            assertEquals("Wrong string encoding for '" + strings[i][0] + "'", strings[i][1], Launcher.encode(strings[i][0]));&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Now that the &lt;code&gt;testEncoding()&lt;/code&gt; test properly fails, on we go to code up a proper solution.&lt;br /&gt; &lt;pre&gt;&lt;code&gt;    public static String encode(String s) {&lt;br /&gt;        StringBuffer buf = new StringBuffer(s);&lt;br /&gt;        &lt;span style="color:brown;"&gt;// Avoid URLEncoder.encode for spaces; it replaces them with plus       &lt;br /&gt;        // signs, which remain pluses when decoded.&lt;/span&gt;                             &lt;br /&gt;        String SPACE = "--SPACE--";&lt;br /&gt;        for (int idx = buf.toString().indexOf(" ");&lt;br /&gt;             idx != -1;idx = buf.toString().indexOf(" ")) {&lt;br /&gt;            Buf.replace(idx, idx+1, SPACE);&lt;br /&gt;        }&lt;br /&gt;        buf = new StringBuffer(URLEncoder.encode(buf.toString()));&lt;br /&gt;        for (int idx = buf.toString().indexOf(SPACE);&lt;br /&gt;             idx != -1;idx = buf.toString().indexOf(SPACE)) {&lt;br /&gt;            if (Platform.isOSX()) {&lt;br /&gt;                &lt;span style="color:brown;"&gt;// The "open" command parses spaces&lt;/span&gt;                             &lt;br /&gt;                buf.replace(idx, idx + SPACE.length(), "%20");&lt;br /&gt;            }&lt;br /&gt;            else {&lt;br /&gt;                buf.replace(idx, idx + SPACE.length(), " ");&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        return buf.toString();&lt;br /&gt;    }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;You can stop coding now&lt;/h2&gt;&lt;p&gt;Hoo-rah. Success on both OSX and Windows. The subject and body show up with exactly the strings I passed in. Now I just do the Launcher.mail invocation in response to a menu selection, and no more asking for more basic information!&lt;br /&gt; &lt;/p&gt; &lt;p&gt;I'll publish the finished Launcher code in the next installment.&lt;br /&gt; &lt;/p&gt; &lt;h3&gt;Next: Do it on *nix, please.&lt;/h3&gt;&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-109115321372557215?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/109115321372557215/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=109115321372557215' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/109115321372557215'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/109115321372557215'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2004/07/automatic-email-support-for-java.html' title='Automatic email support for Java'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-108972240518372226</id><published>2004-07-13T08:35:00.000-04:00</published><updated>2004-07-13T08:40:05.183-04:00</updated><title type='text'>I want my username</title><content type='html'>Back in the day you could choose whatever username you wanted, and it had a lot more meaning than a display name that could be changed at a whim.  Nowadays, your username is something like outtawhack2356 and good luck getting anything much recognizable.&lt;br /&gt;&lt;br /&gt;Maybe it's no big deal, just set your nickname to whatever you want and ignore the username.  If more people recognize your nickname you might be less likely to change it.&lt;br /&gt;&lt;br /&gt;Still, there's not the kind of tight binding from username to user like I remember.  Nobody says "yeah, that outtawhack2356, he's a real funny guy".&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-108972240518372226?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/108972240518372226/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=108972240518372226' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/108972240518372226'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/108972240518372226'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2004/07/i-want-my-username.html' title='I want my username'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7571541.post-108929673831818925</id><published>2004-07-08T10:24:00.000-04:00</published><updated>2004-07-08T10:25:38.316-04:00</updated><title type='text'>Mustn't be late, mustn't be late...</title><content type='html'>&lt;div class="blogger-post-footer"&gt;All Material Copyright (c) 2006 Timothy Wall
All Rights Reserved&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7571541-108929673831818925?l=rabbit-hole.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rabbit-hole.blogspot.com/feeds/108929673831818925/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7571541&amp;postID=108929673831818925' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/108929673831818925'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7571541/posts/default/108929673831818925'/><link rel='alternate' type='text/html' href='http://rabbit-hole.blogspot.com/2004/07/mustnt-be-late-mustnt-be-late.html' title='Mustn&apos;t be late, mustn&apos;t be late...'/><author><name>technomage</name><uri>http://www.blogger.com/profile/08018312109057235866</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_ACEoHFt4yoQ/SKVuxCz4vNI/AAAAAAAAAB8/pPhFdbcdwKc/S220/tardis.png'/></author><thr:total>0</thr:total></entry></feed>
