Archive

Posts Tagged ‘Swing’

Properly closing Swing windows

January 8th, 2012 No comments

There are many subjects one has to know when working with Swing and one of them is window closing. A beginner pass through some steps (and yes, I consider myself a beginner in Swing) and here are those I experienced myself.

Hiding is default

In the first step, you realize that by clicking the cross in the title bar, the window only disappears. It’s not disposed of, and if it’s your main application’s window, that’s bad because it means you’ve lost any handle on the underlying running JVM. It means users just keep spawning new JVMs when launching the application and do not close them, using more and more of the platform memory.

The basics

By then, you learn that any Swing JFrame can be set the right close operation by calling its setDefaultCloseOperation() method. This let you choose the behavior when closing the window: for example, WindowConstants.EXIT_ON_CLOSE exits the JVM when closing the window. This should be set of course on your main window.

Shutdown hook

This method has a slight disadvantage, however. What if we need to do some cleaning operations before exiting the JVM? There’s something called shutdown hooks that the JVM calls just before exiting. It already is explained in this post and fits our needs… provided what we need to do is related to the entire application.

Window listeners

But how can we release resources that are needed by a popup window before the JVM exits? The last way I found was to use window listeners: by implementing WindowListener (or better, by extending WindowAdapter), one can code the desired behavior easily in the windowClosed() method.

Send to Kindle
Categories: Java Tags:

Flamingo tutorial

June 27th, 2010 46 comments

In this article, I will provide you with the documentation to easily use the Flamingo framework and more precisely, its ribbon widget.

Introduction

Never say that Microsoft never innovates: in Office, it introduced an interesting concept, the ribbon band.

The ribbon band is a toolbar of sort. But whereas toolbars are fixed, ribbons layout can change according to the width they display. If you have such an application, just play with it for a few seconds and you will see the magic happens.

Recent versions of Swing do not have such widgets. However, I found the Flamingo project on java.net. Examples made with Flamingo look awfully similar to Office.

Trying to use Flamingo for the first time is no small feat since there’s no documentation on the Web, apart from Javadocs and the source for a test application. The following is what I understood since I began my trial and error journey.

The basics

Semantics

  • the ribbon is the large bar on the screenshot above. There can be only a single ribbon for a frame
  • a task is a tabbed group of one or more band. On the screenshot, tasks are Page Layout, Write, Animations and so on
  • a band is a group of one or more widgets. On the screenshot, bands are Clipboard, Quick Styles, Font and so on

Underlying concepts

The core difference between buttons in a toolbar and band in a ribbon bar is that bands are resizable. For examples, these are the steps for displaying the Document band, in regard to both its relative width and the ribbon width.

The last step is known as the iconified state. When you click on the button, it displays the entire band as a popup.

Your first ribbon

Setup

In order to use the Flamingo framework, the first step is to download it. If you’re using Maven, tough luck! I didn’t find Flamingo in central nor java.net repositories. So download it anyway and install it manually in your local (or enterprise) repository. For information, I choosed the net.java.dev.flamingo:flamingo location.

The frame

If you are starting from scratch, you’re lucky. Just inherit from JRibbonFrame: the method getRibbon() will provide you a reference to the ribbon instance. From there, you will be able to add tasks to it.

However, chances are you probably already have your own frame hierachy. In this case, you have to instantiate a JRibbon and add it on the NORTH location of your BorderLayout-ed frame.

In both cases, the result should be something akin to that:

Adding a task

Tasks represent logical band grouping. They look like tabs and act the part too. Let’s add two such tasks aptly named “One” and “Two”.

public class MainFrame extends JRibbonFrame {

  public static void main(String[] args) {

    SwingUtilities.invokeLater(new Runnable() {

      @Override
      public void run() {

        MainFrame frame = new MainFrame();

        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);

        RibbonTask task1 = new RibbonTask("One");
        RibbonTask task2 = new RibbonTask("Two");

        frame.getRibbon().addTask(task1);
        frame.getRibbon().addTask(task2);
      }
    });
  }
}

Notice the getRibbon() method on the JRibbonFrame. It is the reference on the ribbon bar.

Also notice that the addTask() method accepts a task but also a varargs of JRibbonBand. And if you launch the above code, it will fail miserably with the following error:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Cannot have empty ribbon task
  at org.jvnet.flamingo.ribbon.RibbonTask.<init>(RibbonTask.java:85)
  at MainFrame$1.run(MainFrame.java:37)
  at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
  at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
  at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
  at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
  at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
  at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
  at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
  at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

Adding bands

To satisfy our Flamingo friend, let’s add a ribbon band to each task. The constructor of JRibbonBand takes two argument, the label and an instance of a previously unknown class, ResizableIcon. It will be seen in detail in the next section.

As for now, if you just create the RibbonTask with a reference to the JRibbonBand and launch the application, you will get such an error:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Inconsistent preferred widths
Ribbon band 'Hello has the following resize policies
  org.jvnet.flamingo.ribbon.resize.CoreRibbonResizePolicies$None with preferred width -4
  org.jvnet.flamingo.ribbon.resize.CoreRibbonResizePolicies$Low2Mid with preferred width -4
  org.jvnet.flamingo.ribbon.resize.CoreRibbonResizePolicies$Mid2Mid with preferred width -4
  org.jvnet.flamingo.ribbon.resize.CoreRibbonResizePolicies$Mirror with preferred width -4
  org.jvnet.flamingo.ribbon.resize.CoreRibbonResizePolicies$Mid2Low with preferred width -4
  org.jvnet.flamingo.ribbon.resize.CoreRibbonResizePolicies$High2Mid with preferred width -4
  org.jvnet.flamingo.ribbon.resize.IconRibbonBandResizePolicy with preferred width 42

Remember that bands are resizable? Flamingo needs information on how to do it. Before initial display, it will check that those policies are consistent. By default, they are not and this is the reason why it complains: Flamingo requires you to have at least the iconified policy that must be in the last place. In most cases, however, you’ll want to have at least a normal display in the policies list.

Let’s modify the code to do it:

JRibbonBand band1 = new JRibbonBand("Hello", null);
JRibbonBand band2 = new JRibbonBand("world!", null);

band1.setResizePolicies((List) Arrays.asList(new IconRibbonBandResizePolicy(band1.getControlPanel())));
band2.setResizePolicies((List) Arrays.asList(new IconRibbonBandResizePolicy(band1.getControlPanel())));

RibbonTask task1 = new RibbonTask("One", band1);
RibbonTask task2 = new RibbonTask("Two", band2);

The previous code let us at least see something:

Adding buttons (at last!)

Even if the previous compiles and runs, it still holds no interest. Now is the time to add some buttons!

JCommandButton button1 = new JCommandButton("Square", null);
JCommandButton button2 = new JCommandButton("Circle", null);
JCommandButton button3 = new JCommandButton("Triangle", null);
JCommandButton button4 = new JCommandButton("Star", null);

band1.addCommandButton(button1, TOP);
band1.addCommandButton(button2, MEDIUM);
band1.addCommandButton(button3, MEDIUM);
band1.addCommandButton(button4, MEDIUM);

Too bad there’s no result! Where are our buttons? Well, they are well hidden. Remember the resize policies? There’s only one, the iconified one and its goal is only to display the iconified state. Just update the policies line with the code:

band1.setResizePolicies((List) Arrays.asList(new CoreRibbonResizePolicies.None(band1.getControlPanel()),
  new IconRibbonBandResizePolicy(band1.getControlPanel())));

The result looks the same at first, but when you resize the frame, it looks like this:

Even if it’s visually not very attractive, it looks much better than before. We see the taks, the name of the band and the labels on our four buttons.

Resizable icons

The JCommandButton‘s constructor has 2 parameters: one for the label, the other for a special Flamingo class, the ResizableIcon. Since Flamingo is all about displaying the same button in different sizes, that’s no surprise. Resizable icons can be constructed from Image, ico resources or even SVG.

Let’s add an utility method to our frame, and spice up our UI:

public static ResizableIcon getResizableIconFromResource(String resource) {

  return ImageWrapperResizableIcon.getIcon(MainFrame.class.getClassLoader().getResource(resource), new Dimension(48, 48));
}
...
JCommandButton button1 = new JCommandButton("Square", getResizableIconFromResource("path"));
JCommandButton button2 = new JCommandButton("Circle", getResizableIconFromResource("to"));
JCommandButton button3 = new JCommandButton("Triangle", getResizableIconFromResource("the"));
JCommandButton button4 = new JCommandButton("Star", getResizableIconFromResource("resource"));

band1.addCommandButton(button1, TOP);
band1.addCommandButton(button2, MEDIUM);
band1.addCommandButton(button3, MEDIUM);
band1.addCommandButton(button4, MEDIUM);

This is somewhat more satisfying:

Choosing policies

Now we’re ready to tackle Flamingo’s core business, resizing management. If you have Office, and played with it, you saw that the resizing policies are very rich. And we also saw previously that with only two meager policies, we can either see the iconified display or the full display.

Let’s see how we could go further. You probably noticed that the addCommandButton() of JRibbonBand has 2 parameters: the button to add and a priority. It is this priority and the policy that Flamingo use to choose how to display the band.

Priorities are the following: TOP, MEDIUM and LOW.

Policies are:

Policy Description
CoreRibbonResizePolicies.None Command buttons will be represented in the TOP state (big button with label and icon)
CoreRibbonResizePolicies.Mid2Mid Command buttons that have MEDIUM priority will be represented in the MEDIUM state (small button with label and icon)
CoreRibbonResizePolicies.Mid2Low Command buttons that have MEDIUM priority will be represented in the LOW state (small button only icon)
CoreRibbonResizePolicies.High2Mid Command buttons that have HIGH priority will be represented in the MEDIUM state
CoreRibbonResizePolicies.High2Low Command buttons that have HIGH priority will be represented in the LOW state
CoreRibbonResizePolicies.Low2Mid Command buttons that have LOW priority will be represented in the MEDIUM state
CoreRibbonResizePolicies.Mirror Command buttons will be represented in the priority they were assigned to
IconRibbonBandResizePolicy Command buttons will be not represented. The entire band will be represented by a command button that when pressed will show a popup of the unconstrained band

Now, you have all elements to let you decide which policies to apply. There’s one rule though: when setting policies, the width of the band must get lower and lower the higher the index of the policy (and it must end with the IconRibbonBandResizePolicy) let you’ll get a nasty IllegalStateException: Inconsistent preferred widths (see above).

Let’s apply some policies to our band:

band1.setResizePolicies((List) Arrays.asList(
  new CoreRibbonResizePolicies.None(band1.getControlPanel()),
  new CoreRibbonResizePolicies.Mirror(band1.getControlPanel()),
  new CoreRibbonResizePolicies.Mid2Low(band1.getControlPanel()),
  new CoreRibbonResizePolicies.High2Low(band1.getControlPanel()),
  new IconRibbonBandResizePolicy(band1.getControlPanel())));

This will get us the following result:

Note: there won’t be any iconified state in my example since the band does not compete for space with another one.

More features

Flamingo’s ribbon feature let you also:

  • add standard Swing components to the ribbon
  • add a menu on the top left corner

  • integration with standard Look and Feels

Those are also undocumented but are much easier to understand on your own.

It also has other features:

  • Breadcrumb bar
  • Command button strips and panels

Conclusion

Flamingo is a nice and powerful product, hindered by a big lack of documentation. I hope this article will go one step toward documenting it.

Here are the sources for this article in Eclipse/Maven format.

To go further:

– Edit on 28th june 2010: feedback from Kirill –

  1. Version 5.0 no longer requires “iconified” policy to be present
  2. I strongly suggest not using image-based icons. The recommended way is to transcode SVG files to Java2D-based classes with an offline process and use those classes at runtime. This is what i do in the BasicCheckRibbon class.
Send to Kindle
Categories: Java Tags:

Lessons learned from CDI in Swing

June 21st, 2010 7 comments

Sinced I dived into CDI, I explored classical Java EE uses. Then, I used CDI into a pet project of mine to see how it could be used in Swing applications. This article sums up what lessons I learned from itthis far.

Note: this article assumes you have some familiarity with CDI; if not, please read my previous articles on CDI (CDI an overview part 1 and part 2) or read the documentation.

In my last attempts at Swing (I’m more of a web developer), I figured it could be nice to configure most of the UI components in Spring rather than coding the parameters by hand. In doing this, I used the traditional XML configuration approach, since using annotations would couple my code to Spring JAR, something I loathe to do. With CDI, however, I can live with having my code tied to a standard API: I can change the implementation. This is akin to developing a servlet where you reference the Servlet API, then run it in any container you want.

I coded and coded this way, looked at things made, refactored, then code some more, just like when it’s a personal project and you have no deadlines to meet. Then, I realized I could learn some lessons I could put to good use for the future. Here they are, in no particular order.

The right JAR

Getting all the right CDI JARs for your Java SE project can be a rather daunting task. In order to ease your pain, Weld provides you with a all-in-one JAR aptly named weld-se. Don’t argue that it’s bloated and repackaged, just use it, you will save yourself a lot of pain. For Maven users, this is the configuration:

<project>
...
  <dependencies>
    <dependency>
      <groupId>org.jboss.weld</groupId>
      <artifactId>weld-se</artifactId>
      <version>1.0.1-Final</version>
    </dependency>
  </dependencies>
</project>

She’s an easy logger

All the log frameworks I know of (JDK, Commons Logging, Log4J and SLF4J) let you declare your class logger rather easily:

private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class);

Anyway, most of the time, in order to use this, you either:

  • create a template in your IDE. Yet configuring templates in each of your IDE can be rather tedious. Note to self: propose a feature where such templates (and preferences) could be saved on the web and shared across all of my IDE instances
  • copy the line from somewhere then paste it where you need it. Then you change the class to be logged. To be frank, and though it does not add to my legend (quite the contrary) this is what I do all the time

And doing the latter all the time, it happens I forget to change the logged class. Stupid, isn’t it? Perhaps, but what’s even more stupid is that I have to write it in the first place: the class is my context, it should be inferred. Guess what, Weld does it, with a little help from the weld-logger JAR (it uses SLF4J so beware or use the code as a template to use another framework):

@Inject
private Logger logger;

I truly don’t think it could be easier! The only drawback is that you lose the constant: for the projects I handle now, I can live with it. To use this feature, just add the following dependency to your POM:

<project>
...
  <dependencies>
    <dependency>
      <groupId>org.jboss.weld</groupId>
      <artifactId>weld-logger</artifactId>
      <version>1.0.1-Final</version>
    </dependency>
  </dependencies>
</project>

Remember initialization

CDI can of course inject your dependencies. But injecting your dependencies does not assemble them on the screen. Luckily, CDI also provides you with a hook in order to do it: this is the realm of the @PostConstruct annotation. Let’s take a very simple example:

public class MyPanel extend JPanel {

  @Inject
  private JCheckBox checkbox;

  @PostConstruct
  public void afterPropertiesSet() {

    setLayout(new FlowLayout());

    add(checkbox);
  }
}

The afterPropertiesSet() method is called after having injected all dependencies, and plays the role previously done in the constructor.

Note: yes, the method is named after an old Spring method defined in the InitializingBean interface, dating from version 1. It can now be replaced by specifying the init-method attribute of the bean in the XML beans definition file or using the @PostConstruct annotation.

(Don’t) grow in numbers

With CDI, your classes are injected into one another. However, this means that each component should have its own class. Tthis can be rather cumbersome: for buttons, for example, each should be attached its action on a specific basis. With time, even a moderately size application will grow in term of classs to be much bigger than a standard application. IMHO, this is not desirable since:

  • it makes your codebase bigger, and thus increase your development complexity
  • in Sun’s JVM, loading more classes mean you will use more PermGen space and you’re more likely to run into OutOfMemoryError

In order to limit the number of classes I produce, I used a component factory filled with producer methods. These methods are identified by CDI as injection providers.

public class ComponentFactory {

  @Produces
  public JButton getButton() {

    return new JButton();
  }
}

Use the injection point

This is good but not enough, since action (or text and icon) association will have to be done in the injected class. I would like to annotate the injected attribute with informations like the text of the button and get that information in my producer method. This is the goal of the InjectionPoint optional parameter. CDI provides it to you free of charge if you reference it in your method as a parameter.

public class ComponentFactory {

  @Produces
  public JButton getButton(InjectionPoint ip) {

    JButton button = new JButton();

    // Do something based on annotations on the parameter
    ...

    return button;
  }
}

This is exactly the way that Weld loggers (see above) are created.

Respect the Higlander rule

The Highlander rule is: “There can be only one”. Apart from being taken from a movie that young people mostly don’t know, it also enunciate a basic truth. Since injection must be deterministic, there cannot be two candidates for an injection point. Using producer methods as previously stated will run you into this problem: CDI will have both the JButton class and the producer method and will loudly complains about it in its usual way.

Exception in thread "main" org.jboss.weld.exceptions.DeploymentException: WELD-001409 Injection point has ambiguous dependencies.....
 at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:280)
 at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:122)
 at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:141)
 at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:331)
 at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:317)
 at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:399)
 at org.jboss.weld.environment.se.Weld.initialize(Weld.java:81)
 at org.jboss.weld.environment.se.StartMain.go(StartMain.java:45)
 at org.jboss.weld.environment.se.StartMain.main(StartMain.java:57)

To be compliant with the Highlander rule, you’ll have to discard the JButton class as a candidate for injection. In order to do this, I used qualifiers, both on the injection point and on the producer method. Since I did not want a qualifier per injection point / producer method pair, I made it parameterizable:

@Qualifier
@Target( { FIELD, METHOD })
@Retention(RUNTIME)
public @interface JType {

  public Class<? extends JComponent> value();
}

Annotating both the injected attribute and the producer method with the JType annotation made my code compliant with the Highlander rule!

Spread the word

A good practice I can recommend is to create a special Properties class tasked with initializing your labels (and another for your preferences, etc.), then inject it in all the client classes you need. Now, all classes have access to your internationalized labels. Truly terrific!

Forget Java WebStart

Weld JAR analyze process is incompatible with Java WebStart. You will likely have such nice error messages:

122 [javawsApplicationMain] WARN org.jboss.weld.environment.se.discovery.URLScanner - could not read entries
java.io.FileNotFoundException: http:\xxxxxxxxxx\weld-logger-1.0.0-CR2.jar (La syntaxe du nom de fichier, de répertoire ou de volume est incorrecte)
  at java.util.zip.ZipFile.open(Native Method)
  at java.util.zip.ZipFile.<init>(Unknown Source)
  at java.util.zip.ZipFile.<init>(Unknown Source)
  at org.jboss.weld.environment.se.discovery.URLScanner.handleArchiveByFile(URLScanner.java:142)
  at org.jboss.weld.environment.se.discovery.URLScanner.handle(URLScanner.java:126)
  at org.jboss.weld.environment.se.discovery.URLScanner.scanResources(URLScanner.java:107)
  at org.jboss.weld.environment.se.discovery.SEWeldDiscovery.scan(SEWeldDiscovery.java:71)
  at org.jboss.weld.environment.se.discovery.SEWeldDiscovery.<init>(SEWeldDiscovery.java:45)
  at org.jboss.weld.environment.se.discovery.SEBeanDeploymentArchive$1.<init>(SEBeanDeploymentArchive.java:45)
  at org.jboss.weld.environment.se.discovery.SEBeanDeploymentArchive.<init>(SEBeanDeploymentArchive.java:44)
  at org.jboss.weld.environment.se.discovery.SEWeldDeployment.<init>(SEWeldDeployment.java:37)
  ...
  at com.sun.javaws.Launcher.run(Unknown Source)
  at java.lang.Thread.run(Unknown Source)

I hope it will be fixed in future releases…

Singletons are NOT evil

Twice already I stumbled upon a strange behaviour: toggle buttons selected when they shouldn’t or state was lost. After debugging like mad, I saw that @PostConstruct methods where called well beyond the initial call. It seems my code called for another injection after that. In order to remedy to this, annotate your class with @Singleton in order to share the instance across multiple calls. I haven’t investigated more than that because:

  • I resolved the bug
  • I don’t know why I shouldn’t use singletons in a Swing application

Conclusion

I’m still in development, so I don’t think I’ve seen all there’s to see. Yet, the previous points can make a good starting point for any project wanting to use CDI in a Java SE context.

And please pardon the puns, I was feeling jolly because of this fine summer of ours :-S

Send to Kindle
Categories: Java Tags: ,