The Source for Java Technology Collaboration
User: Password:



Fabrizio Giudici's Blog

Community: NetBeans Archives


I must admit: Eclipse is the best tool in its class

Posted by fabriziogiudici on April 01, 2008 at 12:22 AM | Permalink | Comments (5)

Well, this is the sound opinion of a person who has been using it for years. There's no history: Eclipse is just the best tool of its class. I've tried some alternatives, but nothing allowed me to operate quickly and having such a clean result as Eclipse did. Just nothing. And if you're in pain, trying to deliver when operating in the field, maybe in a problematic environment, Eclipse just gets you out of your troubles in a few minutes.

Don't trust of what competitors say, it's marketing hype, Eclipse is just the best tool of its class.

Long life to Eclipse! :-)




Mailing list di NetBeans in Italiano

Posted by fabriziogiudici on March 21, 2008 at 12:50 PM | Permalink | Comments (1)

Well, as an exceptional case I'm going to blog here in italian. The point is that we have an italian mailing list for NetBeans, so it's a pretty local interest.

Dunque, o voi tutti utilizzatori italiani di NetBeans, abbiamo finalmente una mailing list in cui possiamo scriverci nella nostra lingua madre. Questo strumento mi è stato chiesto da alcuni di voi durante i NetBeans Days tenuti nello scorso Settembre a Roma, Milano e Cagliari e Sun Microsystems è stata molto gentile nel risponderci positivamente. Janice Campbell, responsabile del programma di localizzazione, ci ricorda che questa è l'ottava mailing list su NetBeans non in inglese.

L'indirizzo della mailing list è nbdiscuss_it@netbeans.org e per iscriversi basta mandare un messaggio vuoto a nbdiscuss_it-subscribe@netbeans.org. Gli archivi sono disponibili su http://www.netbeans.org/servlets/SummarizeList?listName=nbdiscuss_it

Ora, non ci resta che far partire un po' di discussioni interessanti!



Feedback from the IDE Day in Genoa

Posted by fabriziogiudici on March 12, 2008 at 02:14 AM | Permalink | Comments (2)

A quick feedback from the IDE Day in Genoa, waiting for the other IDE Day in Rome to be over before posting a final report. Well, the day in Genoa was a success: while I don't have gotten yet the official data, there should have been 130+ attendees, which is very good. The talks by our guests from IntelliJ, Oracle and Sun were of very high quality, as expected; and the large number of questions proved that they caught the attention of people. Congratulations to the JUG steering commitee for their first large event, even considering that they resumed the JUG almost from zero about one year ago.

In spite of being my home town, unfortunately I wasn't able to attend it because of an engagement in Milan; I only got in in the latest minutes of the panel session. But I fully enjoyed the final dinner based on genoese cuisine and fish... :-)

P3100482n.jpg




ge1.jpg



New improvements for building NetBeans RCP apps - and OpenBlueSky available

Posted by fabriziogiudici on November 18, 2007 at 01:19 PM | Permalink | Comments (3)

After some refactoring, I've polished my ant tools for improving a bit the way NetBeans RCP projects are built.

In a previous blog post I've told you that I think a good way to distribute the sources of your NetBeans RCP project is to bundle the platform it requires to be built against (of course, if it's not the default NetBeans Platform).

I proposed the following directory layout in the distribution:
  • lib/nbm - where you place the referenced platform packaged in form of nbm files;
  • lib/nbm/NetBeans6.0RC1 - where you place the modules of the NetBeans Platform that you need to use;
  • lib/platform7-base - where you place some files from the NetBeans platform that can't be packaged into a nbm file;
  • src/ - your sources
Another directory would be used to keep the platform laid out so NetBeans can use it (this directory should not be distributed with sources):
  • lib/platform - where you place the referenced platform packaged in form of nbm files
Now, the following ant code will make it possible to generate the platform once and for all:

 <property name="platform.dir" value="lib/platform" />
 <property name="nbm.dir" value="lib/nbm" />
 
 ...

 <target name="generate-platform">
        <delete dir="${platform.dir}" failonerror="false" />
        <mkdir dir="${platform.dir}" />
        <copy todir="${platform.dir}/platform7">
            <fileset dir="lib/platform7-base" />
        </copy>
        <generatePlatform platformTargetFolder="${platform.dir}" >
            <fileset dir="${nbm.dir}"/>
            <fileset dir="${nbm.dir}/NetBeans6.0RC1"/>
        </generatePlatform>
    </target>

Things can be further improved. If your project uses a custom platform, normally people should first configure NetBeans with the new platform in order to compile the files. But if you put the following code at the beginning of your ant script, you can have everything done for you automatically:

<project name="MyApplication" basedir="." default="build">
    <description>...</description>
    <property name="platform.dir" value="lib/platform" />
    <property name="nbm.dir" value="lib/nbm" />
    <property file="nbproject/platform.properties"/>
    <property file="nbproject/private/platform-private.properties"/>
    <propertyfile file="${user.properties.file}" comment="">
        <entry key="nbplatform.${nbplatform.active}.harness.dir" value="$${nbplatform.default.harness.dir}"/>
        <entry key="nbplatform.${nbplatform.active}.label" value="${nbplatform.active}"/>
        <entry key="nbplatform.${nbplatform.active}.netbeans.dest.dir" value="${basedir}/${platform.dir}"/>
    </propertyfile>
    <import file="nbproject/build-impl.xml"/>
...

Basically this code will just define the new platform in the IDE as you had manually configured it (probably the IDE must be restarted to be aware of the changes).

This means that you can distribute your project and just ask people to run ant generate-platform once, and then they can start working with it. As soon as I understand how to customize the ant initialization tasks, it will be possible to work by just opening the project with the IDE, as a plain J2SE project. More information in the OpenBlueSky documentation. You can download the sources and a preliminary binary of these ant tasks in OpenBlueSky.

BTW, what is OpenBlueSky? As I anticipated a few days ago, it's a collection of tools and components that I've factored out of blueMarine, for use in other applications. There are different things inside, from simple enhancements to RCP APIs, up to something more sophisticated as this CoverFlowView (here shining in blueMarine):





that can be just used as easily as a ListView (just set up a Lookup, an ExplorerManager and you've done). I'll post more details on this soon.

More in general, OpenBlueSky will provide some ad-hoc integration of cool stuff from SwingLabs so it will be easy to build a Filthy NetBeans Rich Client Application!

Stay tuned.

Technorati Tags: ,

NetBeans RCP - beyond suite chaining

Posted by fabriziogiudici on November 03, 2007 at 04:30 PM | Permalink | Comments (5)

A few months ago I blogged about how to optimize a large NetBeans RCP project by splitting it in parts and taking advantage of a technique known as "suite chaining". Now I'm going forward as I have some more complex requirements to meet and suite chaining doesn't fit with them.

If you are not comfortable with the layout of NetBeans RCP projects and the related jargon I suggest you to read first my previous blog (at least the introductory part to understand the basic concepts such as "platform", "suite", "cluster", "NBM file").

And now let's recall what I wrote in the previous post. At that time, I basically split blueMarine in three parts (and defined some APIs so people could develop plugins to extend the application):
  • blueMarine Libraries. It's a "fake" component which only contains some module wrappers for a certain number of libraries required by the project(s) just for saving some compilation time.
  • blueMarine Core. It's the basic imaging components of blueMarine. It can be used by developers for developing other imaging applications.
  • blueMarine. It's the actual application.
  • Generic Plugin. It's a generic plugin that people can develop for blueMarine.


As you can see a simple, linear chain of dependences emerges from the design and fits well with the "platform chain" concept of NetBeans RCP: basically each project builds upon the previous stage of the chain and delivers a new platform enhanced with new features.



With the latest refactoring, two new components appeared:
  • OpenBlueSky. It's a set of components that enhance the NetBeans RCP. It is a new spin off of blueMarine Core.
  • ForceTen. It's a set of components for showing 2D and 3D maps. It is a new spin off of blueMarine.


The basic concept is that OpenBlueSky must be reusable by other programmers outside the blueMarine ecosystem, as well as ForceTen that also originates a standalone application (and thus uses OpenBlueSky too). These new requirements lead to a more complex set of dependencies, where linear suite chaining can't be used any longer.
Also consider that the previous diagram has been simplified: we should take into account that, for instance, there are multiple dependencies on JOGL (they are best shown in the next diagram).
This lead me to have a sort of Copernican shift. In the previous approach, the final deliverable of any component was a public platform, exposed to further projects in the chain. Now I focus on delivering a set of NBM files (the pink component in each package in the diagram below), much like a standard Java application produces some JAR files, and each project defines its own "private" platform (named "***-base") which is "artificially" created by taking the required NBM files from multiple sources and unpacking them. Each set of NBM files creates a different cluster in the platform. Platforms are now "private" since they are only used by the project they belong to and not exposed to others.


You see that there's an exception, since blueMarine Core and blueMarine are platforms, yet still publicly exposed. This is because they can be used by other people (for instance during the development of a Plugin) and the process of initially creating a platform requires some manual steps, which I want to avoid at the moment. But I think I'll work on it, so at a certain point I'll be able to deliver only a set of NBM files also in these cases (see the final remarks at the end of this post).

Automating the process

Now, a practical problem is to keep everything up-to-date. For instance, let's suppose I make some changes in OpenBlueSky. This means that one or more NBMs are updated and, following the dependency chain, you see that the "blueMarine Core base", "ForceTen base" and "blueMarine base" platforms need to be updated (as well as some further dependencies).

We have some things to do:
  1. Update NBMs in chain, which can't be just achieved by copying files, since e.g. a certain module could be deleted or renamed at a point; this means that the old file should be removed from dependent platforms as well.
  2. Create / update the platforms with their clusters. NetBeans isn't able to directly compile against NBM files, which are just a convenient way to pack things. Instead, they are expanded following a well known file tree into a platform, which is the artifact NetBeans is able to compile against.
  3. Last but not least, you might want to have platforms and NBM files checked into Subversion. This approach not only makes it easier to work on a single sub-project without downloading all the sources of ancestor projects, but also delivers reproducibility as each revision has a well established set of both sources and libraries. Not only you should use Subversion appropriately whenever a certain file has been added, modified or removed, but you have to cope with a subtle problem: since NBM files contain the compilation timestamp, if you rebuild them even with no change in the sources, you always create a file with some changed bits. This means that any time you recompile NBMs Subversion has to commit all of them. Not only this eats up a lot of space in the source repository, but makes it much harder to track changes by looking at the commit history.
For dealing with all these problems I've created a new ant task, named "copyUpdatedStuff" (I might change its name as soon as I find something more appropriate). This task takes three arguments:
  1. sourceFolders, a colon-separated list of directories where there are some "source" NBMs to get.
  2. nbmFolder, a single directory where the NBM files are copied. This directory is kept as a reference to understand which files have been changed since the last time. To solve the timestamp problem cited above, copyUpdatedStuff doesn't compare contents, but the embedded version tag from the manifest of each module.
  3. platformFolders, a colon-separated list of directories where copyUpdatedStuff expands the NBM files to create clusters.
To understand better how the thing works, let's look at how to manage the platform "blueMarine base". First we define the set of directories where to search for NBM files (look at the previous diagram, there are five dependencies):
    <property name="source.nbm.path" value="${openbluesky.dir}/build/updates:
${bluemarine-libraries.dir}/build/updates:
${bluemarine-core.dir}/build/updates:
${jogl-libraries.dir}/build/updates:
${forceten.dir}/build/updates"/>
Basically we're looking at the build/updates directory of each ancestor project, where NetBeans placed the NBM files after compiling the sources.

Then we define the target directories:
    <property name="nbm.dir" value="lib/nbm" />
    <property name="platform.dir" value="lib/platform"/>
and specifically a set of cluster names to match the ancestor projects:
    <property name="platform.path" value="${platform.dir}/openbluesky:
${platform.dir}/bluemarine_libraries:
${platform.dir}/bluemarine_core:
${platform.dir}/jogl-libraries:
${platform.dir}/forceten"/>
And now what we need is just to run ant prepare-platform whenever we want to bring into blueMarine the last updates from the ancestor projects:
    <target name="prepare-platform">
        <copyUpdatedStuff sourceFolders="${source.nbm.path}"
                          nbmFolder="${nbm.dir}"
                          platformFolders="${platform.path}"/>
    </target>
The set of files produced by this task is the following:
   ./nbm
./lib/platform/bluemarine_core/config/Modules
   ./lib/platform/bluemarine_core/config
   ./lib/platform/bluemarine_core/core/locale
   ./lib/platform/bluemarine_core/core
   ./lib/platform/bluemarine_core/modules/locale
   ./lib/platform/bluemarine_core/modules
   ./lib/platform/bluemarine_core/update_tracking
   ./lib/platform/bluemarine_core
   ./lib/platform/bluemarine_libraries/config/Modules
   ./lib/platform/bluemarine_libraries/config
   ./lib/platform/bluemarine_libraries/modules/ext
   ./lib/platform/bluemarine_libraries/modules
   ./lib/platform/bluemarine_libraries/update_tracking
   ./lib/platform/bluemarine_libraries
   ./lib/platform/forceten/config/Modules
   ./lib/platform/forceten/config
   ./lib/platform/forceten/modules/ext
   ./lib/platform/forceten/modules
   ./lib/platform/forceten/update_tracking
   ./lib/platform/forceten
  ./lib/platform/jogl-libraries/config/Modules
   ./lib/platform/jogl-libraries/config
   ./lib/platform/jogl-libraries/modules/ext
   ./lib/platform/jogl-libraries/modules/lib
   ./lib/platform/jogl-libraries/modules
   ./lib/platform/jogl-libraries/update_tracking
   ./lib/platform/jogl-libraries
   ./lib/platform/openbluesky/config/Modules
   ./lib/platform/openbluesky/config
   ./lib/platform/openbluesky/modules/ext
   ./lib/platform/openbluesky/modules/lib
   ./lib/platform/openbluesky/modules
   ./lib/platform/openbluesky/update_tracking
   ./lib/platform/openbluesky
Indeed blueMarine also depends on some modules from the NetBeans platform:
   ./lib/platform/ide8/config/Modules
   ./lib/platform/ide8/config
   ./lib/platform/ide8/modules/ext
   ./lib/platform/ide8/modules/lib
   ./lib/platform/ide8/modules
  ./lib/platform/ide8/update_tracking
   ./lib/platform/platform7/config/ModuleAutoDeps
   ./lib/platform/platform7/config/Modules
   ./lib/platform/platform7/config
   ./lib/platform/platform7/core
   ./lib/platform/platform7/docs
   ./lib/platform/platform7/lib
   ./lib/platform/platform7/modules/ext
   ./lib/platform/platform7/modules
   ./lib/platform/platform7/update_tracking
but at the moment I don't know how to automatically put them in the target directory - so I had to manually copy them the first time.

The task also prints on the console some relevant information about what's happening:
prepare-platform:
[copyUpdatedStuff] 0 new module(s):     
[copyUpdatedStuff] 2 updated module(s):  it-tidalwave-bluemarine-calendarexplorer (2.2.10) in: bluemarine-core, it-tidalwave-geo-geoexplorer (1.0.6) in: ForceTen
[copyUpdatedStuff] 0 obsolete module(s):
[copyUpdatedStuff] Copying and updating: it-tidalwave-bluemarine-calendarexplorer.nbm
[copyUpdatedStuff] Copying and updating: it-tidalwave-geo-geoexplorer.nbm
[copyUpdatedStuff] Copying and updating: config/Modules/it-tidalwave-bluemarine-calendarexplorer.xml
[copyUpdatedStuff] Copying and updating: modules/it-tidalwave-bluemarine-calendarexplorer.jar
[copyUpdatedStuff] Copying and updating: update_tracking/it-tidalwave-bluemarine-calendarexplorer.xml
[copyUpdatedStuff] Copying and updating: config/Modules/it-tidalwave-geo-geoexplorer.xml
[copyUpdatedStuff] Copying and updating: modules/it-tidalwave-geo-geoexplorer.jar
[copyUpdatedStuff] Copying and updating: update_tracking/it-tidalwave-geo-geoexplorer.xml


What's next

The relationship among my projects have turned out to be a bit cumbersome, but after all they reflect the dependencies in the new design. And the stuff seems to work. I also like the fact of focusing of the NBM files as a final deliverable for a component since it matches the same approach we have with JAR files. But one of the problems is a sort of redundancy (because of how NetBeans works): we need to keep both the NBM files and the platform folders, even though they share the same contents. Even worse, they both get checked in, thus wasting some space in Subversion. I thought a bit of it, and tried to use copyUpdatedStuff to add a target to build.xml that would recreate the platform starting from the NBM files in case it doesn't exist. In this way one could avoid putting the platform in the repository and the first time a project is checked out, the platform would be generated on the fly.

Unfortunately, at the moment this is impossible: in fact, before being able to run ant on a NetBeans RCP project you must first set the referenced platform (by means of the Platform Manager tool of the IDE), but we're supposing it is not present at the very beginning, since ant should create it! I'll investigate how work around this.

If you want to try it, copyUpdatedStuff sources can be checked out from https://bluemarine.dev.java.net/svn/bluemarine/trunk/src/UpdateManager. Beware, it's still alpha and has got some bugs.

PS I'll blog about OpenBlueSky and ForceTen as soon as I have some basic documentation for them. But the good news is that you can check them out from https://openbluesky.dev.java.net and https://forceten.dev.java.net and try them since now all the dependencies on blueMarine has been removed.



Technorati Tags: , , , , ,

Work in progress: ForceTen

Posted by fabriziogiudici on September 16, 2007 at 11:19 AM | Permalink | Comments (5)

By the end of the year, blueMarine should hopefully spin off another sub-project: ForceTen, focused on the world of map rendering and geotagging.

To tell the truth, I hoped to be able to spin it off earlier, but this month of September has been (and still is) extremely busy. At the moment I have only been able to move the relevant sources to a specific Subversion project (https://forceten.dev.java.net/svn/forceten/trunk/src), while the website (http://forceten.tidalwave.it) is not ready yet (but you can already subscribe to the news feed). Also, sources need some refactoring and have still dependencies on blueMarine, so you will need to change them if you want to use them now.

But maybe it's the time for posting a little teaser. The basic features of this component are:
  • integration with different map renderers: SwingLabs JXMapViewer, NASA World Wind (done), possibly a JavaScript-based renderer (for running the Google Maps APIs and showing Google Maps in a legal way (*)) and a SVG renderer (for SVG data published by OpenStreetMap);
  • integration with different geo-coding providers;
  • support for drag-and-drop geotagging;
  • specific integration with NetBeans (even though I'll try to publish some stuff as plain J2SE).
Here is a quick screencast (400k, QuickTime, low quality to keep the size small) of the currently implemented features showing off in blueMarine.

I'll be showing ForceTen capabilities integrated in blueMarine at the next NetBeans Tech Days in Milan, Rome and Cagliari.

PS The part of the screencast showing photos dragged on the map is achieved by integrating the NetBeans Visual Library: after the Light Table, another creative use of it. Stay tuned on the next issue of the NetBeans Magazine for more details on it!

(*) Which is technically possible with JXMapViewer but - sadly - not legal.

Technorati Tags: , , , , , ,







Creative use of the NetBeans Visual Library: the Light Table

Posted by fabriziogiudici on August 21, 2007 at 11:24 AM | Permalink | Comments (4)

The Visual Library is one of the coolest things that the NetBeans guys delivered with NetBeans 6. It is a rich API which allows you to create a sort of "blackboard" where objects can be added, removed, edited, moved, resized, and connected in a visual graph. The cream on the cake is that you can use the Visual Library even in regular Swing applications by just adding the related JAR to the classpath, since it has no dependencies on the NetBeans core.

The Visual Library is deeply used inside NetBeans: it's at the core of Matisse, the visual designer, as well as the UML support, the J2ME designer, in a few words everything that renders a graph. When Roumen posted a screencast about using the Visual Library I got really excited since I understood that it would have allowed me to easily implement a new feature in blueMarine: the (Virtual) Light Table. It is a visual component where you can drop thumbnails from your photo collection and arrange them as you prefer with the mouse. You can see a screenshot below and a very short screencast (.MOV, 300k) is also available.




Now I'm going to tell you how I was able to implement the LightTable in a matter of hours (tests excluded) yesterday night with the Visual Library.

WARNING: this blog assumes you're confident with the basic concepts of Swing and the DataSystem and Nodes API of NetBeans.

You're going to see all the relevant code in the sketches below; in any case, the sources can be checked out with Subversion from https://bluemarine-incubator.dev.java.net/svn/bluemarine-incubator/trunk/src/LightTable

The binary code isn't available in blueMarine yet (hey, I just wrote it last night! Give me the time to test it... ;-) - it will be available soon by means of the Update Centers and will be part of the next round of blueMarine demos I'll give starting from the next month.



Some blueMarine APIs

Of course I've been facilitated by the consolidation of the blueMarine APIs, that are a set of NetBeans modules for manipulating photos (and - with the latest refactoring - any kind of media type). Since I'd like to focus on the Visual Library and not on blueMarine, I'm just telling you what the few blueMarine classes you'll see in the code listings are about:
  • Thumbnail is just a reduced size image used as a photo preview. It is smart enough to be asynchronous, that is it can be generated in background and fire events when data (raster and metadata) are available.
  • ThumbnailManager is the manager for Thumbnails; you usually call it as
    DataObject dataObject = ...;
    Thumbnail thumbnail = thumbnailManager.findThumbnail(dataObject);
    Remember that DataObject is the standard way of representing a datum with NetBeans. Whenever you're dealing with a photo, the code above will give you a working thumbnail (or create one if it doesn't exist).
  • ThumbnailRenderer is the renderer for thumbnails. It is able to deal with the asynchronous model of a Thumbnail, that is to quickly render a placeholder if the Thumbnail is not ready yet, and repaint it later when the image is available.
  • ThumbnailTracker is a container of a set of Nodes which are capable of update themselves when the related Thumbnails are updated. The typical use is:

    Node node = thumbnailTracker.add(dataObject.getNodeDelegate());
For the record, these APIs are part of the blueMarine Core, the foundation APIs of blueMarine. They are going to be frozen an documented soon. Stuff for another blog post.


The Scene

The Scene is the container for the "blackboard", that is the container of all the graphics objects (that are called "Widgets"). While Roumen's screencast introduced the regular Scene class, I'm going to use an ObjectScene, which provides some additional features:
  • it defines the concept of "selected" widgets, as well as "hovering" widgets (i.e. the object under the mouse) and others; and fires the proper events to notify external listeners;
  • it provides an association between a Widget and a custom object - that is, the model for your Widget.
For instance, the following code will create a working ObjectScene with the capability of add and remove DataObjects from it:
    /** This object manages a set of thumbnailed nodes. */
    private final ThumbnailTracker thumbnailTracker = new ThumbnailTracker();
   
    /** The scene manager from Visual Library. */
    private final ObjectScene scene = new ObjectScene();
   
    /** The main layer where Widgets are added. */
    private final LayerWidget mainLayer = new LayerWidget(scene);

    private void internalAdd (final DataObject dataObject, final Point location)
      {
        final Node node = thumbnailTracker.add(dataObject.getNodeDelegate());
        final Thumbnail thumbnail = ThumbnailManager.Locator.findThumbnailManager().findThumbnail(dataObject);
        final ThumbnailWidget widget = new ThumbnailWidget(scene, thumbnail, node);
        // widget.setPreferredLocation(widget.convertLocalToScene(location));
        final Point sceneLocation = scene.convertViewToScene(viewLocation);
        final Point localLocation = mainLayer.convertSceneToLocal(sceneLocation);
        widget.setPreferredLocation (localLocation);
        mainLayer.addChild(widget);
        scene.addObject(dataObject, widget);
        widget.getActions().addAction(scene.createSelectAction());
        widget.getActions().addAction(scene.createObjectHoverAction());
        scene.validate(); // See first entry of http://graph.netbeans.org/faq.html
      }

    private void internalRemove (final DataObject dataObject)
      {
        scene.removeObject(dataObject);
        final List widgets = scene.findWidgets(dataObject);
        scene.removeObject(dataObject);

        for (final Widget widget : widgets) // removeObject() doesn't remove widgets
          {
            widget.removeFromParent();
          }
      }

Just a few comments:
  • ThumbnailWidget is a specialized visual object that I'll describe below.
  • A Scene can contain many layers (LayerWidget), that are the place where Widgets are placed. In the simplest cases, you just need one of them.
  • Capabilities can be added to Widgets by adding WidgetActions to them. Most of WidgetActions can be created from an ActionFactory (see code sketches below), while some are provided by the ObjectScene (in the code, the action for managing the selection and the hovering status).
Once you have created an ObjectScene, you just get the Swing components out of it:

    /** The view component. */
    private final JComponent view = scene.createView();
   
    /** The view component. */
    private final JComponent satelliteView = scene.createSatelliteView();

The view is the blackboard renderer itself, and you just need to place it in a Swing JWindow or such (in most cases you'd put it into a JScrollPane first); the satelliteView is an optional component that provides a "bird's eye" view of the blackboard.

As those components are just Swing components, you can integrate them with things such as drag and drop. For instance, the following code enables dropping DataObjects directly on the LightTable:
    private final DropTarget dropTarget = new DropTarget()
      {
        @Override
        public void drop (final DropTargetDropEvent event)
          {
            try
              {
                for (final DataFlavor flavor : event.getCurrentDataFlavors())
                  {
                    final Class<?> clazz = flavor.getRepresentationClass();

                    if (DataObject.class.isAssignableFrom(clazz))
                      {
                        final DataObject dataObject = (DataObject)event.getTransferable().getTransferData(flavor);
                        //
                        // This tests if the current DataObject has Thumbnail capability.
                        //
                        if (dataObject.getLookup().lookup(Thumbnail.DataProvider.class) != null)
                          {
                            event.acceptDrop(event.getDropAction());
                            internalAdd(dataObject, event.getLocation());
                            event.dropComplete(true);
                            return;
                          }
                      }
                  }
               
              }
            catch (Exception e)
              {
                logger.throwing(CLASS, "drop()", e);
              }

            event.dropComplete(false);
          }
      };

...
    view.setDropTarget(dropTarget);

Creating a Widget

The Visual Library comes with a lot of pre-made Widgets, such as LabelWidget (which contains a text), ImageWidget (which contains an image), up to ComponentWidget, which contains a JComponent. This is really flexible for most cases; nevertheless you can subclass Widget if you want to do something special.

Which is precisely what I need. In spite of the fact that a Thumbnail is an image, ImageWidget is not ok for me, since rendering Thumbnails is somewhat complex because of their asynchronous behaviour. So I'm going with the creation of a custom Widget with specialized rendering capabilities:

public class ThumbnailWidget extends Widget
  {
    private static final ThumbnailRenderer thumbnailRenderer = new SimpleThumbnailRenderer();
   
    private final Thumbnail thumbnail;
   
    private final Node node;

    public ThumbnailWidget (final Scene scene, final Thumbnail thumbnail, final Node node)
      {
        super(scene); 
        this.thumbnail = thumbnail;
        this.node = node;
      }

    @Override
    protected void paintWidget()
      {
        final Graphics2D g = getGraphics();
        final AffineTransform transformSave = g.getTransform();
        final Rectangle bounds = getClientArea();
        thumbnailRenderer.setThumbnail(thumbnail);
        g.translate(bounds.x, bounds.y);
        thumbnailRenderer.setBounds(bounds);
        final double zoomFactor = getScene().getZoomFactor();
        g.scale(1 / zoomFactor, 1 / zoomFactor);
        thumbnailRenderer.paint(g);
        g.setTransform(transformSave);       
      }
  }
Keep in mind that you aren't forced to use Nodes with the Visual Library: but the next code samples will demonstrate why it's a good thing to have them behind the scenes.

Rendering happens in the paintWidget() method, where you can retrieve a Graphics2D object and paint all the stuff. Since a Scene can be zoomed in and out, it's important that you deal properly with the current scale.

The second thing to implement is the capability of automatically update the Widget when the Thumbnail state changes. Since this capability is pretty important throughout blueMarine, I've already told you that Nodes coming out from a ThumbnailTracker have automatic update capabilities. So what I need now is just a NodeListener:
    private final NodeListener iconChangeListener = new NodeAdapter() 
      {
        @Override
        public void propertyChange (final PropertyChangeEvent event)
          {
            if (Node.PROP_ICON.equals(event.getPropertyName()))
              {
                repaint();
                //
                // The Nodes API can fire events outside of the AWT Thread
                //
                if (SwingUtilities.isEventDispatchThread())
                  {
                    repaint();

                    getScene().validate();
                  }
                else
                  {
                    SwingUtilities.invokeLater(new Runnable()
                  {
                    public void run()
                      {
                        repaint();
                        getScene().validate();
                      }
                  });
                }
              }
          }
      };

// in the constructor of ThumbnailWidget:
   node.addNodeListener(iconChangeListener);

repaint(), as the similar method in JComponent, just causes the current Widget to be repainted (another method, revalidate(), should be called if you have changed the size of the Widget, which is something I'm not doing here).

Now I would like to have a popup menu working on my ThumbnailWidget. By default, they don't have one, but you can specify a PopupMenuProvider for this purpose:

    private final PopupMenuProvider popupMenuProvider = new PopupMenuProvider()
      {
        public JPopupMenu getPopupMenu (final Widget widget, final Point location)
          {
            return node.getContextMenu();
          }
      };

// in the constructor of ThumbnailWidget:
        getActions().addAction(ActionFactory.createPopupMenuAction(popupMenuProvider));
As you can see, the code retrieves the context menu of the Node. This is pretty neat, since I'm just transparently getting the actions that have been configured in the NetBeans platform; in other words, I'm being consistent with the fact that whenever I have something that represents a photo, I always get the same functionalities.

Furthermore, I can take advantage of the Nodes API. For instance, I'd like to retouch a bit the popup menu. In facts, it contains an "Add to light table" action that I've defined elsewhere - trust me, I'm not giving the code for this since it's not part of the Visual Library stuff. But there's no meaning in executing that action on a Widget that is already in the Light Table. On the contrary, a "Remove from light table" action would make sense. I can easily exchange the "add to..." with the "remove from..." actions by using a FilterNode:

    private class ActionFilterNode extends FilterNode
      {
        public ActionFilterNode (final Node node)
          {
            super(node);   
          }
       
        @Override
        public final Action[] getActions (final boolean context)
          {
            final List<Action> actions = new ArrayList<Action>(Arrays.asList(super.getActions(context)));
           
            for (int i = 0; i < actions.size(); i++)
              {
                final Action action = actions.get(i);
               
                if ((action != null) && AddToLightTableAction.class.equals(action.getClass()))
                  {
                    actions.set(i, SystemAction.get(RemoveFromLightTableAction.class));
                    break;
                  }
              }
               
            return actions.toArray(new Action[0]);
          }
      }

// in the constructor of ThumbnailWidget:
    this.node = new ActionFilterNode(node);

Now I'd like to have my ThumbnailWidget brought to front when a simple mouse click is performed on it. On this purpose, I can create a custom WidgetAction as follows:
    private static final WidgetAction.Adapter bringToFrontAction = new WidgetAction.Adapter()
      {
        @Override
        public State mouseClicked (final Widget widget, final WidgetMouseEvent event)
          {
            if (event.getButton() == MouseEvent.BUTTON1)
              {
                widget.bringToFront();
                return State.CONSUMED;
              }

            return State.REJECTED;
          }
      };

// in the constructor of ThumbnailWidget:
  getActions().addAction(bringToFrontAction);
I think that the above code is self-explaining.

Now I'd like that my ThumbnailWidget can be moved by just clicking and dragging the mouse. This is really easy: I just need to add this in the constructor:
        getActions().addAction(ActionFactory.createMoveAction());
And what about resizing? The Visual Library provides out-of-the box support for resizing a Widget. You just need to add the related action in the Widget constructor:
        getActions().addAction( ActionFactory.createResizeAction());
At this point, by pointing the mouse on the Widget border and dragging, it gets resized. Easy! But it's not good for me: my ThumbnailWidget represents a photo and I don't want arbitrary resizing that changes the aspect ratio of the image. Fortunately I can put a constraint with the following code:

        final WidgetAction resizeAction = ActionFactory.createResizeAction(resizeStrategy,
ActionFactory.createDefaultResizeProvider());
        getActions().addAction(resizeAction); // add this BEFORE createMoveAction()
A ResizeStrategy lets me override the actual size the will be applied to the Widget:
    private final ResizeStrategy resizeStrategy = new ResizeStrategy() 
      {
        public Rectangle boundsSuggested (final Widget widget,
                                          final Rectangle originalBounds,
                                          final Rectangle suggestedBounds,
                                          final ResizeProvider.ControlPoint controlPoint)
          {
            final Rectangle result = new Rectangle(suggestedBounds);
            final double deltaW = Math.abs(suggestedBounds.getWidth() - originalBounds.getWidth());
            final double deltaH = Math.abs(suggestedBounds.getHeight() - originalBounds.getHeight());
            final Insets insets = getBorder().getInsets();
            final int mw = insets.left + insets.right;
            final int mh = insets.bottom + insets.top;
 
            if (deltaW >= deltaH) // moving mostly in horizontal
              {
                result.height = mh + Math.round((result.width - mw) * aspectRatio);
              }
            else // moving mostly in vertical
              {
                result.width = mw + Math.round((result.height - mh) / aspectRatio);
              }
           
            return result;
          }
      };

The code looks a bit tricky, but the point is that I just need to return the new size that I want to apply. I'm basically enforcing the aspect ratio, just caring if the user is moving the mouse mostly horizontally (in this case I'd leave the new width unchanged and compute the height accordingly) or vertically (in this case I'd do the opposite). Also I'm taking the Insets in the computation, since they are usually non zero due to the capability of setting a Border to the Widget.

Yes, the Border. I actually want to change the border of my ThumbnailWidget dynamically:
  • by drawing a solid, white border around the selected widget;
  • by drawing a "resize border", with the classic eight control point where you should drag the mouse, when the mouse is hovering on the Widget.
This is also easy: I just need to override the notifyStateChanged() method, that is called whenever the Widget changes state:
    @Override
    protected void notifyStateChanged (final ObjectState oldState, final ObjectState newState)
      {
        super.notifyStateChanged(oldState, newState);
        setBorder(newState.isSelected() ? (newState.isHovered() ? RESIZE_SELECTED_BORDER : SELECTED_BORDER) :
                                          (newState.isHovered() ? RESIZE_BORDER : EMPTY_BORDER));
      }
Just keep in mind that Borders for Widgets aren't the same classes as for the plain Swing component, and they have a specific BorderFactory. The details for creating a border are quite boring and I'll leave you to inspect the source code for them.

That's enough for this post. I need to run tests on the new component, but I've seen only minor glitches so far and I'm pretty pleased with the small amount of code that the Visual Library required to implement the features I had in mind. The most important point that should be addressed is the persistence of the Light View, that is a way to "remember" the items and their positions. It should be not hard since the Scene exposes methods for enumerating the contained Widgets, but I'll deal with this later - also because this first experience with the Visual Library made me thinking of some other cool thing that I could do with it... :-)

And remember that I just scratched the surface of the Visual Library: even if you need more complex renderings, including graphs with nodes, arcs, floating connections, etc... , the Visual Library has probably what you need.

Technorati Tags: , ,

Quick feedback from Jazoon

Posted by fabriziogiudici on June 27, 2007 at 03:42 PM | Permalink | Comments (6)

I still need some time to get to a comprehensive evaluation about Jazoon, so I will talk about the conference in general after the event is over. Nevertheless today I can say what has been the most interesting talk I've attended, since I think it will be highly unlikely that one of tomorrow's talks are better (also considering that I have to speak at two talks and I won't be able to attend other stuff in the same slots).

My favourite talk was Gregg Sporar's Integrated Profiling (with the NetBeans IDE). Well, I know the NetBeans Profiler quite well and I often use it, but Gregg illustrated some new trick that I wasn't aware of. I found it unbelievably useful: sure there are some debugging practices of mine that will be greatly improved by using all the potential of the tool. And also the Profiler in NetBeans 6 has some even new, great stuff (BTW, I've been using NetBeans 6 Milestones for months for a couple of projects, but never tried the profiler on them).

PS I've accomplished with large excess my goal of meeting more than twelve bloggers/VIP people; mostly thanks to the meeting of the JUG leaders. Also I'm so glad I've met Moritz Petersen. Moritz and I have been exchanging some emails about Java and imaging since a few months, and together with Emmanuele are planning to do something together...



My first NetBeans Day

Posted by fabriziogiudici on June 23, 2007 at 02:25 PM | Permalink | Comments (0)

And today I was finally able to attend my first NetBeans day, in Zurich!. Brian Leonard, Gregg Sporar, Roumen Strobl, Fabiola Rios gave the attendees an overview of the platform, both as IDE with the three Java editions (standard, enterprise, micro) and as Rich Client Platform API. Even though I use NetBeans a lot, I found the presentations really interesting (too bad there were no NetBeans Days in Italy...) and I also learnt something that I didn't knew. For instance, the new Profiler in 6.0 M9: it has been a great tool since some time already, but there's a new "Find GC path" feature, which tells you why a certain object has not been garbage collected yet (that is, which objects are still referencing it). It literally rocks for finding out memory leaks.

To me the day has been important for some reasons (in random order):

  • I met Gregg and Brian (as well as Felipe Gaucho a.k.a. Felipe Silva) for the first time - I hope that at Jazoon, next week, I'll be able to meet at least a dozen people that I only talked by email or blog;
  • believe it or not, I've seen my first sketch of Ruby on Rails code (never looked at a blog about it so far);
  • it was my first public event as member of the Dream Team (unfortunately no other Dreamers were there, I hope for the next week);
  • I gave a very quick introduction of blueMarine (waiting for the next week demos); with the exception of a meeting of the JUG Milan (but the project had just restarted its new NetBeans life), it's the first time it goes in a public meeting.

Last but not least, the weather in Zurich is variable, but the temperature is pleasant. A very good appetizer for Jazoon, next week. Now, my only problems are that I've to recover several hours of sleep (I've had some hard times last week) and I should get a better WiFi connection in my hotel (writing this blog entry has been a pain).


Below: Brian, Gregg, Roumen, Fabiola.
20070623-0003-300.jpg

20070623-0005-300.jpg

20070623-0009-300.jpg

20070623-0012-300.jpg



Feeling like the Buridan's Donkey

Posted by fabriziogiudici on May 12, 2007 at 01:24 AM | Permalink | Comments (0)

I don't know whether the expression "Buridan's Donkey" has the same meaning in english as in italian (I've just recenly discovered that the latin "qui(d) pro quo" has nowadays different meanings in english and romance languages). In any case, the history is about a donkey that starved and died since it had a lot of alternate foods to eat and it couldn't decide which was the most tasty to start eating with.

I feel in a similar way as I've found a lot of new Java toys to play with! :-)

  • NASA World Wind - and Geertjan will soon tell us how to include it in NetBeans RPC apps
  • With the latest NetBeans update I've found a Sun Grid plugin that allows to create and start jobs on the grid
  • ... and I just discovered that now the Sun Grid is accessible from 24 countries other than USA, including Italy!!

Well, maybe the plugin was already there since some time, but I didn't notice it - knowing that the grid was not available for Italy it could not have attracted my attention. Now I've just subscribed to it, hopefully in a few days I'll have my account working. This is important, since the USA-only access really slowed down the development of the Grid plugin for Mistral. I can't promise anything... but maybe there will be something ready at the blueMarine / Mistral demo at Jazoon. ;-)

Wow Sun: I looooove you sooooooooo! ;-)

Picture 3.png



Automating test coverage reports with NetBeans and Cobertura

Posted by fabriziogiudici on February 04, 2007 at 04:57 PM | Permalink | Comments (4)

Whether you're an agilist or not, you should be aware that automated testing is one of the key factors for the success of your projects. While test runners such as JUnit are widely known, there are other powerful tools that can help: those that produce reports about your test coverage.

The count of tests for class, in fact, is not enough to understand whether your code is well covered; this depends on their structure and complexity. A basic rule says that tests should exercise every single line of your classes: but how can you check this in an easy way?

Tools such as Cobertura are useful in this scenario: by instrumenting the byte code of your classes, they can precisely report which percentage of your code is covered by test and even produce a pretty printed listing of your code where you can easily spot the uncovered parts in red (look here for an example).

Today coverage tools come also with plugins for the most popular IDE such as NetBeans, Eclipse or IDEA, so they can be launched with a button or a menu. I like this approach, but anyway I'm a fan of the old batch mode ;-) also because it can be easily integrated into a continuous integration server (in this way, automated testing and coverage report run without consuming computing resources on my laptop). So I want to have coverage tests both available in my NetBeans GUI and from the command line.

To make it possible is easy, thanks to the fact that Cobertura comes with a specific Ant target and the Ant scripts used by NetBeans can be customized.

Continue Reading...



Setting properties for JUnit testing and NetBeans

Posted by fabriziogiudici on January 31, 2007 at 04:48 PM | Permalink | Comments (1)

Keeping an old habit since the old times of C/C++ development and Makefile, mostly under Unix, I usually set environment variables to specify some properties that should be available during compiling or testing. But environment variables can create troubles sometimes.

For instance, Mistral automated tests need to deal with a number of images that are used as a test case. Unfortunately, most of these images are quite large (several megabytes) since they are digital photos (the main target of Mistral), often stored in a camera raw format (which Mistral can handle thank to jrawio). In the first distribution of Mistral just a couple of images were included, and this made the source zip 20mb large. Putting more images in the distribution is not a viable option - a best approach is to ask developers to download them optionally (after all many people who download sources could be not interested in running tests) - indeed the new JUnit tests are smart enough to automatically download the required images from the internet the first time they run.

Continue Reading...



God bless the tools that support well your process

Posted by fabriziogiudici on January 24, 2007 at 03:17 PM | Permalink | Comments (0)

Well, my last blog post was about the Rio plugin of Mistral, and ended with an open (and expected) problem related to performance - something that I thought I'd have addressed in the next post. But as often happens, other commitments prevented me from completing the next step in the planned time - I think we'll have to wait two or three weeks still.

In the meantime, I'm busy - among other things - with two small projects (to be precise, a tiny one and a small one, which anyhow is just the first phase of something that should evolve in future in a very interesting stuff). Both are related to imaging and both make use of Mistral, even though they just need its basic features. I'm taking the chance of another little diversion from my main blog topics and I'm going to talk about the process (you know, I'm a big fan of it, it's the process that saves a project rather than cool language syntax :-P ) and how a good tool should support it.

Continue Reading...





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds