Flamingo component suite provides a pure Swing implementation of Office 2007 ribbon component, and the latest 4.2dev drop of the core library has enhanced the support for ribbon application menu to provide default content of the secondary menu panel.

The application menu button is a big round button in the top left corner of the ribbon:

https://flamingo.dev.java.net/release-info/4.2/ribbon-appmenu-notshowing.png

It is not a direct replacement for the usual application menu bar, but rather a place to hold actions that (as a general rule) do not affect the visual content of the document – such as saving, printing, sharing etc. When the application menu button is clicked, it shows a panel with two levels of actions:

  • Primary action
  • Secondary actions for the selected primary action

For example, a primary “Print” action will have a number of secondary actions to print the document as is, print from a dialog with all the options or open the print preview. In order to specify secondary actions, use theĀ RibbonApplicationMenuEntryPrimary.addSecondaryMenuGroup API:

RibbonApplicationMenuEntryPrimary amEntryPrint = new RibbonApplicationMenuEntryPrimary(
      new document_print(), "Print", new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent e) {
            System.out.println("Invoked printing document");
         }
      }, CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION);
amEntryPrint.setActionKeyTip("P");
amEntryPrint.setPopupKeyTip("W");

RibbonApplicationMenuEntrySecondary amEntryPrintSelect = new RibbonApplicationMenuEntrySecondary(
      new printer(), "Print", null, CommandButtonKind.ACTION_ONLY);
amEntryPrintSelect
      .setDescriptionText("Select a printer, number of copies and other printing options before printing");
amEntryPrintSelect.setActionKeyTip("P");
RibbonApplicationMenuEntrySecondary amEntryPrintDefault = new RibbonApplicationMenuEntrySecondary(
      new document_print(), "Quick Print", null,
      CommandButtonKind.ACTION_ONLY);
amEntryPrintDefault
      .setDescriptionText("Send the document directly to the default printer without making changes");
amEntryPrintDefault.setActionKeyTip("Q");
RibbonApplicationMenuEntrySecondary amEntryPrintPreview = new RibbonApplicationMenuEntrySecondary(
      new document_print_preview(), "Print Preview", null,
      CommandButtonKind.ACTION_ONLY);
amEntryPrintPreview
      .setDescriptionText("Preview and make changes to the pages before printing");
amEntryPrintPreview.setActionKeyTip("V");

amEntryPrint.addSecondaryMenuGroup("Preview and print the document",
      amEntryPrintSelect, amEntryPrintDefault, amEntryPrintPreview);

At runtime when the user moves the mouse over the “Print” entry in the left panel, the matching secondary entries are shown on the right:

https://flamingo.dev.java.net/release-info/4.2/ribbon-appmenu-rollovermenu.png

While most of the primary entries have a predefined (static) list of secondary entries, some require dynamic content. For example, the “Open” entry shows the list of recently open documents. In order to associate dynamic secondary content, use the RibbonApplicationMenuEntryPrimary.setRolloverCallback API:

RibbonApplicationMenuEntryPrimary amEntryOpen = new RibbonApplicationMenuEntryPrimary(
      new document_open(), "Open", new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent e) {
            System.out.println("Invoked opening document");
         }
      }, CommandButtonKind.ACTION_ONLY);
amEntryOpen
      .setRolloverCallback(new RibbonApplicationMenuEntryPrimary.PrimaryRolloverCallback() {
         @Override
         public void menuEntryActivated(JPanel targetPanel) {
            targetPanel.removeAll();
            JCommandButtonPanel openHistoryPanel = new JCommandButtonPanel(
                  CommandButtonDisplayState.MEDIUM);
            String groupName = "Recent Documents";
            openHistoryPanel.addButtonGroup(groupName);
            for (int i = 0; i < 5; i++) {
               JCommandButton historyButton = new JCommandButton(i
                     + "    " + "document" + i + ".html",
                     new text_html());
               historyButton
                     .setHorizontalAlignment(SwingUtilities.LEFT);
               openHistoryPanel
                     .addButtonToLastGroup(historyButton);
            }
            openHistoryPanel.setMaxButtonColumns(1);
            targetPanel.setLayout(new BorderLayout());
            targetPanel.add(openHistoryPanel, BorderLayout.CENTER);
         }
      });
amEntryOpen.setActionKeyTip("O");

Here, the application code is responsible for clearing and populating the contents of the secondary panel. At runtime when the user moves the mouse over the “Open” entry in the left panel, the application callback is invoked to populate the secondary panel:

https://flamingo.dev.java.net/release-info/4.2/ribbon-appmenu-rollovercontent.png

The latest 4.2dev drop of Flamingo core library also allows the application code to specify the default content of the secondary panel. This is done with the new RibbonApplicationMenu.setDefaultCallback API. When set, it will be called when the ribbon application menu is shown, and when the currently active (under mouse) primary entry has neither secondary entries, nor rollover callback. In Office 2007 the default content of the secondary panel is the list of recently opened documents, but this API allows you to provide a custom implementation if necessary:

applicationMenu
      .setDefaultCallback(new RibbonApplicationMenuEntryPrimary.PrimaryRolloverCallback() {
         @Override
         public void menuEntryActivated(JPanel targetPanel) {
            targetPanel.removeAll();
            JCommandButtonPanel openHistoryPanel = new JCommandButtonPanel(
                  CommandButtonDisplayState.MEDIUM);
            String groupName = "Default Documents";
            openHistoryPanel.addButtonGroup(groupName);
            for (int i = 0; i < 5; i++) {
               JCommandButton historyButton = new JCommandButton(i
                     + "    " + "default" + i + ".html",
                     new text_html());
               historyButton
                     .setHorizontalAlignment(SwingUtilities.LEFT);
               openHistoryPanel
                     .addButtonToLastGroup(historyButton);
            }
            openHistoryPanel.setMaxButtonColumns(1);
            targetPanel.setLayout(new BorderLayout());
            targetPanel.add(openHistoryPanel, BorderLayout.CENTER);
         }
      });

When the default callback is invoked at runtime (under one of the two scenarios mentioned above), it populates the secondary panel:

https://flamingo.dev.java.net/release-info/4.2/ribbon-appmenu-defaultcontent.png

If you want to see the enhanced application menu button in action, run the following WebStart demo:

The demo above works for the core look-and-feels. If you want to see this functionality under Substance, run the following WebStart demo:

If you want to test the new functionality in your applications, you would need the following (the last two only for applications running under Substance look-and-feel):

The command button component is a central building block for the Flamingo component suite. It aims to address the deficiencies of the core Swing button components, adding features expected by the modern applications. While the main goal of Flamingo is to provide a pure Java implementation of the Office 2007 ribbon container, the command buttons can certainly be used outside the ribbon.

The latest 4.2dev drop of the core Flamingo library and 6.0dev drop of the Substance Flamingo plugin provide support for command buttons that have no text and/or no icon. This is a big step forward that positions the Flamingo command button as a drop-in replacement for core Swing buttons, and here are a few screenshots to illustrate these new capabilities.

Here is a screenshot of a few command buttons that have both text and icon:

https://flamingo.dev.java.net/release-info/4.2/command-buttons-icon-and-text1.png

Here, the rows show the functionally equivalent command buttons that arrange the text and icon in different layouts – addressing the varying space available to host the specific button. The first column shows action buttons – clicking anywhere on a button will activate the registered listeners. The second column shows split buttons – clicking on icon / text will activate the listeners, while clicking on the drop arrow will show the popup menu.

The next screenshot shows text / icon command buttons of the other two kinds:

https://flamingo.dev.java.net/release-info/4.2/command-buttons-icon-and-text2.png

The first column shows another type of split button – where the popup menu is shown when the text is clicked (as opposed to the first split button type where clicking the text activates the main action). The second column is a menu button – clicking anywhere shows the popup menu.

The next two screenshots show the same button arrangement, but this time for buttons with no icons:

https://flamingo.dev.java.net/release-info/4.2/command-buttons-no-icon1.png

https://flamingo.dev.java.net/release-info/4.2/command-buttons-no-icon2.png

And the final screenshot shows the same button arrangement, but this time for buttons with no texts:

https://flamingo.dev.java.net/release-info/4.2/command-buttons-no-text.png

With the release 1.1 of Trident animation library available a couple of weeks ago, it’s time to start the development of version 1.2 (code-named Cookie Jar). The first 1.2dev drop has a few minor API enhancements, as well as a small new demo. This demo is based on the Flash mouse star trailer, and is slightly reminiscent of the Glitzer applet from Paul Schmidinger. Here is a video showing “Star Dust” in action:

The code is quite straightforward (see the test.swing.StarDust class). It has a looping timeline that spawns new stars. At every pulse this timeline checks the current location of the mouse. If the mouse is inside the panel, it creates a new Star object, as well as a matching Timeline object. The paintComponent() method of the panel iterates over all “live” stars and paints them based on their current position, size, rotation, alpha and color.

Here is the relevant code for the spawner timeline:

Timeline spawner = new Timeline();
spawner.addCallback(new UIThreadTimelineCallbackAdapter() {
   private float currHue = 0.0f;

   @Override
   public void onTimelinePulse(float durationFraction,
         float timelinePosition) {
      Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
      SwingUtilities.convertPointFromScreen(mouseLoc, mainPanel);
      double currX = mouseLoc.getX();
      double currY = mouseLoc.getY();
      if ((currX < 0) || (currY < 0) || (currX > mainPanel.getWidth())
            || (currY > mainPanel.getHeight()))
         return;

      double outerStartSpan = 5;
      double outerFinalSpan = 20;
      Star star = new Star(currX, currY, outerStartSpan);

Here, we use the MouseInfo class to get the mouse location, and then create a Star object centered at that point. Then, we create a timeline to animate the star location, size, rotation and alpha:

Timeline starTimeline = new Timeline(star);
double angle = Math.random() * 2.0 * Math.PI;
double distance = 20.0 + 30.0 * Math.random();
starTimeline.addPropertyToInterpolate("x", currX, currX
	+ distance * Math.cos(angle));
starTimeline.addPropertyToInterpolate("y", currY, currY
	+ distance * Math.sin(angle));
starTimeline.addPropertyToInterpolate("alpha", 1.0f, 0.0f);
starTimeline.addPropertyToInterpolate("rotation", 0.0f,
	(float) (2 * Math.PI * Math.random()));
starTimeline.addPropertyToInterpolate("outerSpan",
	outerStartSpan, outerFinalSpan);
starTimeline.addPropertyToInterpolate("color", Color.white,
	new Color(Color.HSBtoRGB(currHue, 0.8f, 0.7f)));
currHue += 0.01f;

When the timeline for the specific star is created, it is simply played for 3 seconds. The main spawning timeline is looped indefinitely:

      starTimeline.setDuration(3000);
      starTimeline.play();
   }
});
spawner.playLoop(RepeatBehavior.LOOP);

If you have Java 6 or later on your machine, click on the button below to launch the WebStart demo of “Star Dust”:

Substance 6.0 roadmap

October 13th, 2009

Today I want to share my plans for the next major release of Substance look-and-feel library. I’ve started the development of version 6.0 (code-named Sonoma) about a week ago, and it’s time to talk about the major changes that are coming in this release.

  • Removal of deprecated methods / classes. All APIs detailed at the end of release notes for version 5.3 have been removed from the latest 6.0dev drop. If your application is using one or more of these APIs, consult the release notes to see what you should use instead.
  • Restructuring the code base. Due to lack of proper modularity in Java, some of the internal utility classes have been incorrectly viewed as published APIs by applications. Version 6.0 will have two major packages – substance.api and substance.internal. The application code can only use the api package, and the bundled test applications have been corrected to follow this guideline. For most applications this will require simple re-organization of import sections.
  • Renaming the main package. In a few days the main package will be renamed from org.jvnet.substance to org.pushingpixels.substance. For most applications this will require simple re-organization of import sections.
  • Moving animations to use Trident library. The internal animation engine from the Laf-Widget library will be replaced by the Trident animation library.

Smaller changes planned for Substance 6.0:

  • Polishing of existing skins and a few new skins
  • Support for custom decoration areas – pending performance evaluation
  • Improving performance of tables
  • Further support for High DPI mode

Applications using Substance will be mostly affected by package renaming and usage of Trident.

If your code is setting Substance via a fully-qualified class name, you will need to replace “org.jvnet.substance.skin…” with “org.pushingpixels.substance.api.skin…“. Otherwise you will just need to reorganize your imports section.

Switching to use Trident will require applications to add the matching trident.jar to the classpath (in addition to substance.jar). If you are running Substance-powered application in a signed WebStart environment, you will need to sign the Trident jar with the same key.

The latest 6.0dev drop of Substance contains some of the changes described above. The deprecated APIs have been removed, the packages are now organized in api / internal – but not yet under org.pushingpixels.substance, and custom animations in the test application are powered by Trident.

If you have any questions, comments, suggestions or objections, please open a discussion on the project forums or mailing lists.