This entry describes the capabilities that release 2.1 of Substance look-and-feel provides for your tabbed panes. They are:

- Animation effect for tabs with modified (and unsaved) content
- Close buttons on tabs with listeners

Following the feedback from the users (thanks Raj), the latest daily drop of version 2.2 (code-named El Paso) provides even more functionality available to your Swing application once you start using Substance LAF.
The first request was to enhance the listener mechanism on close buttons of tabbed panes. As mentioned in the above blog, you can register any number of listeners that will be called when close button of some tab in some tabbed pane is clicked. The CloseTabListener interface defines the following two methods:

    /**
     * Called when a tab is about to be closed.
     */
    public void tabClosing(JTabbedPane tabbedPane, Component tabComponent);

    /**
     * Called when a tab is closed.
     */
    public void tabClosed(JTabbedPane tabbedPane, Component tabComponent);

There were two separate requests regarding this mechanism:

1. Allow the application listener to veto the tab closing
2. Allow registering listeners per tabbed pane (and not globally)

Now, if you want to provide additional logic and veto the tab closing, you need to implement the VetoableTabCloseListener that extends the TabCloseListener and adds the following function:

    /**
     * Called when a tab is about to be closed. Can veto the tab closing.
     *
     * @return true if the corresponding tab shouldn't be closed,
     *         false otherwise.
     */
    public boolean vetoTabClosing(JTabbedPane tabbedPane,
    	Component tabComponent);

All vetoable listeners that are either registered for the specific tabbed pane, or are registered globally (for all tabbed panes) are called when the close icon is clicked. If at least one of them returns false, the tab will not be closed. Otherwise, the tabClosing and tabClose functions will be called. A simple example is to show the confirmation dialog as:

    public void tabClosed(JTabbedPane tabbedPane,
                Component tabComponent) {
      System.out.println("Closed tab - specific");
    }

    public void tabClosing(JTabbedPane tabbedPane,
                Component tabComponent) {
      System.out.println("Closing tab - specific");
    }

    public boolean vetoTabClosing(JTabbedPane tabbedPane,
                Component tabComponent) {
      int userCloseAnswer = JOptionPane.showConfirmDialog(
          Check.this,
          "Are you sure you want to close this tab?",
          "Confirm dialog", JOptionPane.YES_NO_OPTION);
      return (userCloseAnswer == JOptionPane.NO_OPTION);
    }

The following dialog will be shown to the user when the tab close button is clicked:
Note that this extension doesn't break the existing API which is still fully supported and provides the exact same functionality as before.
The second request was to provide an option for drawing vertical rotated tabs when the current placement for the tabbed pane is either LEFT or RIGHT. The solutions described elsewhere (by Santosh and Lee Ann) involve a composite icon that simulates the tab and effectively hides the text from the tabbed pane UI delegate (by putting it in the icon and rotating the entire icon). The above solutions work, but involve using third-party classes and changing your code to use these classes. In addition, since Substance provides the close buttons on tabs, they are not easily applicable.
Substance provides unintrusive solution for rotated vertical tabs. There are two properties that you can set:

- SubstanceLookAndFeel.TABBED_PANE_VERTICAL_ORIENTATION is set on a tabbed pane or on UIManager (or both) and instructs the UI delegate to use vertical (rotated) layout for the LEFT and RIGHT-placed tabbed panes.
- SubstanceLookAndFeel.TABBED_PANE_VERTICAL_ORIENTATION_ROTATE_ICONS is set a tab, a tabbed pane or on UIManager (or any combination) and instructs the UI delegate to leave the icon unrotated.

Note that if you don't wish to create the explicit dependency in your code on the SubstanceLookAndFeel class, you can use the string values for the above properties. Here are a few screenshots to illustrate the outcome, followed by code samples:

- TOP-placed tabbed pane with five tabs. Each tab has text, icon and a close button (visible when the tab is either selected or under mouse):
- LEFT-placed tabbed pane under standard layout. Note that the tabs are not rotated and take too much horizontal space:
- Four parts of the image, from left to right:
  - LEFT-placed tabbed pane with vertical layout. The first tab has unrotated icon, the third tab has unrotated icon which is also disabled, all other tabs have rotated icons.
  - LEFT-placed tabbed pane with vertical and scroll layout. The icons are same as above.
  - RIGHT-placed tabbed pane with vertical and scroll layout. The icons are same as above.
  - RIGHT-placed tabbed pane with vertical layout. The icons are same as above.

Here is how the tabs in the last examples were created:

    JTabbedPane jtp = new JTabbedPane();
    TabNumberedPanel tnp1 = new TabNumberedPanel(jtp, 1);
    tnp1.putClientProperty(
      SubstanceLookAndFeel.TABBED_PANE_VERTICAL_ORIENTATION_ROTATE_ICONS,
      Boolean.TRUE);
    jtp.addTab("tab1", SubstanceImageCreator.getThemeIcon(null), tnp1);
    jtp.addTab("tab2", SubstanceImageCreator.getThemeIcon(null),
        new TabNumberedPanel(jtp, 2));
    TabNumberedPanel tnp3 = new TabNumberedPanel(jtp, 3);
    tnp3.putClientProperty(
      SubstanceLookAndFeel.TABBED_PANE_VERTICAL_ORIENTATION_ROTATE_ICONS,
      Boolean.TRUE);
    jtp.addTab("tab3", SubstanceImageCreator.getThemeIcon(null), tnp3);
    jtp.addTab("tab4", SubstanceImageCreator.getHexaMarker(4,
        SubstanceLookAndFeel.getTheme()), new TabNumberedPanel(jtp,
        4));
    jtp.addTab("tab5", SubstanceImageCreator.getHexaMarker(5,
        SubstanceLookAndFeel.getTheme()), new TabNumberedPanel(jtp,
        5));
    jtp.setEnabledAt(2, false);
    jtp.setEnabledAt(3, false);
    jtp.putClientProperty(
      SubstanceLookAndFeel.TABBED_PANE_VERTICAL_ORIENTATION, true);

Note that you get the desired behaviour by setting a few client properties, which have no effect under other look-and-feels.
The only limitation of this technique is for tabs that support arbitrary components (added in Mustang, described in this blog entry by Alexander Potochkin). Such tabs will not be rotated correctly, since these components are not painted using the UI delegates at all. I have discussed this with Alexander and he pointed out additional problem - the need to translate mouse coordinates to allow the tab component to correctly handle the events. The bottom line (at least for Mustang) is - do not use this feature if your application is using the new JTabbedPane functions (which automatically implies that it's running under Mustang).
One last thing - if you like Substance, help internationalize it! (small print - eternal glory is not guaranteed :) Starting from version 2.2 (code-named El Paso) the additional UI elements (system menu items, heap status panel, menu search panel) are internationalized. You are welcome to view the list of available translations and send me additional translations to kirillcool [at] yahoo.com. Thanks in advance