Swing TaskDialog 1.1 release


Today I’m announcing the immediate availability of TaskDialog library version 1.1

This release includes several requested enchacements and fixes to the reported issues.
Information on issue fixes is available in the issue tracker, so I will concentrate on the enhancements.

Command Wait Intervals

Command Wait Intervals were discussed in a previous post and are now a permanent feature of the library

Ability to conditionally enable/disable commands

This feature will allow adding simple validation to more complex task dialogs. It is based on the IValidationListener interface which is implemented by commands and other internal components. This simply means that they are able to react on validation events triggered by TaskDialog’s fireValidationFinished method.
For example, a panel with several fields can be set as a fixed dialog component. As the field content is changing, the panel may trigger validation events based on it’s internal logic, effectively disabling the OK command if the data is invalid. Here is how such a code may look:


boolean validationResult = ...// your business logic here
TaskDialof dlg = TaskDialog.getInstance(this); //find the task dialog panel belongs to
dlg.fireValidationFinished( validationResult );

The new library version is available immediately.

As always… Your comments and suggestions are welcome
The project is available at http://code.google.com/p/oxbow under BSD license.

Wait Timer for Commands in TaskDialog library


I’ve got several requests to implement timer functionality in TaskDialog commands.  The latest code drop contains requested changes. This is a preliminary design and I invite a discussion on implementation which would cover the most common use cases.

Here are the main points of current implementation:

TaskDialog.Command interface now includes getWaitInterval function which should return required wait interval in seconds.
TaskDialog.StandardCommand
by default returns 0, but includes new variant of the derive function with additional waitInterval parameter. This allows for adding custom commands with specified wait interval.

How would we do it with our very convenient methods from TaskDialogs class? By passing special commands with instruction text. Following will show a warning dialog with wait interval of 10 seconds.

TaskDialogs.warn( "Are you sure you want to quit?@@10", "Please do not quit yet!");


This works on all TaskDialogs methods except choice and showException.
My current feeling is that more work is required to make it perfect.

As always… Your comments and suggestions are welcome :)
The project is available at http://code.google.com/p/oxbow under BSD license.

Swing TaskDialog 1.0 official release


It is my pleasure to announce the availability of the official release of TaskDialoglibrary version 1.0.

This release includes following features:

  • Implementation of Microsoft TaskDialog standard.
  • Support for additional custom components
  • Standard and Custom commands
  • Support for Command Links
  • i18n support
  • Customizable UI defaults
  • Simplified utility methods for common cases
  • Support for Mac OS X

The project is available at http://code.google.com/p/oxbow under BSD license.
As always – comments and suggestions are welcome.

Swinging Task Dialog (part 5)


This morning I uploaded the new release  of  TaskDialog library 1.0RC.  I consider it a release candidate because all features planned for version 1.0 are now implemented. This release adds ability to produce dialogs with command links and many small enhancements and optimizations to the library.

Command Links

This is the last feature I had in my plan for the final release of the library and it proved to be the hardest to accomplish. I spent last 3 weeks working on it and now I think it is ready for general consumption :)

From the API point of view implementing command link based task dialog is very simple.

int choice = TaskDialogs.choice(
    "What do you want to do with your game in\nprogress?",
    "",
    1,
    new CommandLink("Exit and save my game", "Save your game in progress, then exit. " +
                    "This will\noverwrite any previosely saved games."),
    new CommandLink("Exit and don't save", "Exit without saving your game. " +
                    "This is counted\nas a loss in your statistics." ),
    new CommandLink("Don't exit", "Return to your game progress" ));

This produces the following dialog


It is possible to pass the custom icon into command link. By default standard “green arrow” icon is shown. All the text on command link obeys overrall style rules of the dialog and can be expressed in HTML.

Overall look and feel on Windows tries to closely resemble current Windows standard. Mac OS UI guidelines do not specify anything similar to the command links so framework resorts to the use of standard buttons.

I’m planning the final release of TaskDialog 1.0 library in one week  if no serious issues will be found.

The project is available at http://code.google.com/p/oxbow/ under BSD license.
As always – comments and suggestions are welcome.

Swinging Task Dialog (part 4)


It’s my pleasure today to announce the availability of the new TaskDialog 0.5.0 library release, which is dedicated to Mac OS support.

The last two weeks were spent researching the Apple standards and making task dialogs on Mac OS look as close as possible  to original ones. The list of specific Mac OS differences, supported by library includes:

  • Correct Resources (Fonts, icons, text) and component gaps
  • No separators between Message, Command and Footer areas
  • Expandable component is showing directly under “Details” check box ( disclosure triangle )
  • Expandable component is automatically wrapped in correct border
  • Correct Command button order

Here are examples of the same dialog under windows 7 and Mac OS 10.6.2

I cannot say I’m confident I caught all the details but with your help I’m sure I’m going to :)

The project is available at http://code.google.com/p/oxbow/ under BSD license.
As always – comments and suggestions are welcome.

Swinging Task Dialog (part 3)


Following user requests several new features were added to the library this week.

i18n support

The Task Dialog library now has i18n support.  So  common text in the dialog can be shown in any language by supplying locale specific resource bundle named `task-dialog_<locale_id>.properties` and switching default locale. Library has built-in default and Spanish(es) bundles, which can be found under ‘src/main/resources’.

Customizable defaults

The defaults for resources (such as colors, fonts, icons) used by the library is stored in `UIDefaults`, same way as for any other Swing component.  This means the dialog can be customized by simply replacing these defaults with desired ones. Defaults are  set on the first use of `TaskDialog` class. List of keys can be found in `com.ezware.dialog.task.iDesign` interface.

New TaskDialogs.radioChoice methods

New`radioChoice` methods were added to `TaskDialogs` class. They provide almost a trivial way to request user selection based on a set of radio buttons. Here is an example

int choice = TaskDialogs.radioChoice("You've got selection to make", "Go ahead", 1, "Yes", "No", "May be" );

Partial support for OS specific look

This feature is not finished yet, but I made great inroads and can report on a status.

After doing research  into this topic I realized that there are significant differences not only in resources (fonts, icons, colors) used but actual layout of components in the dialog. This realization inspired internal design change which is based on concept of “dialog design” – a class responsible for all aspects of the dialog design. The appropriate design class is instantiated according to the underlying OS and is be used to create the dialog’s resources and layouts.

While creation of the  dialog layout is still work in progress, setting OS specific resources is pretty much done for Windows and Mac OS. Here is how the same dialog currently looks on Windows  and Mac OS X.

That is all I could accomplish this week. Further progress will be reported in the next installments of this series.

The project is available at http://code.google.com/p/oxbow/ under BSD license.
As always – comments and suggestions are welcome.

Swinging Task Dialog (part 2)


This week’s focus was on adding more functionality.

The API was extended with an ability to set commands (buttons on the bottom of the dialog). TaskDialog features a set of standard commands ( currently only Ok and Cancel ). But it is very easy to add any arbitrary command. The only requirement is that the command has to implement TaskDialog.Command interface.

TaskDialog’s getResult() method returns last executed command. A simpler way to use it is show() method which shows the dialog and returns last executed command. Here is a warning dialog:

TaskDialog dlg = new TaskDialog("Security Warning");
dlg.setInstruction( "The publisher cannot be verified.\nDo you want to run this software?" );
dlg.setText( "Name: C:\\Program Files\\eclipse\\eclipse.exe\n" +
      		     "Publisher: <b>Unknown Publisher</b>\n" +
       		     "Type: Application\n");
dlg.setIcon( TaskDialog.StandardIcon.WARNING );
dlg.getFooter().setCheckBoxText("Always ask before opening this file");
dlg.setCommands( StandardCommand.OK.derive("Run"), StandardCommand.CANCEL );
if ( dlg.show().equals(StandardCommand.OK)) {
       	//do something
}

Sometimes custom command has to not just close the dialog but execute some additional logic.  To accomplish this PropertyChangeListener can be attached to the TaskDialog’s “result” property. As soon as result changes (command button is pressed ) your code will be notified.

It is possible now to add any fixed component to the dialog ( in addition to expandable one ). Here is the example of the  dialog with the progress bar.

TaskDialog dlg = new TaskDialog("Copying...");
dlg.setInstruction("Copying file");
dlg.setText( "Location: From 'Others' to 'Others'\n" +
      		     "File Name: <b>Photo.jpg</b>" );
dlg.setIcon( TaskDialog.StandardIcon.INFO );

JProgressBar pb = new JProgressBar();
pb.setValue(30);
dlg.setFixedComponent( pb );
dlg.show();

More work is required in the area as I want to to provide prebuilt components such as command links.

To even further simplify usage I added TaskDialogs class. It makes creation of certain task dialogs a one liner.

TaskDialogs.inform( "You've won!", "The game is over with the 15:3 score");
if (TaskDialogs.warn( "Are you sure you want to quit?", "Please do not quit yet!")) {
        try {
	        new BigDecimal("seven");
       } catch( Throwable ex ) {
	       TaskDialogs.showException(ex);
       }
}

The above code produces three task dialogs:

The project is available at http://code.google.com/p/oxbow/ under BSD license.
As always – comments and suggestions are welcome.

Swinging Task Dialog (part 1)


I often take inspiration in modern operating system UIs. Windows User Interface is one of those. I stumbled upon Task Dialogs when reading Windows User Experience Guidelines and realized that even though it is a very precisely designed interaction standard, nothing like that exists in the Swing world. Thus I decided to create “Task Dialog” library for Swing.

After approximately a week of designing and coding I can present the first rough “alpha”. The main point is a very simple usage. In future common uses can be simplified even more by using TaskDialogFactory, but now I’m mostly concentrated on functionality.

The simplest dialog can be done in few lines of code:

TaskDialog dlg = new TaskDialog("Task Dialog" );
dlg.setText( "Hello World!" );
dlg.show();

Here is a more Task Dialog like one:

TaskDialog dlg = new TaskDialog("Application Error" );
dlg.setInstruction( "CRASH AND BURN!");
dlg.setIcon( TaskDialog.StandardIcon.ERROR );
dlg.setText( "The applicaiton has performed an illegal action. This action has been logged and reported." );
dlg.show();


Here is task dialog with details

TaskDialog dlg = new TaskDialog("Application Error" );
dlg.setInstruction( "CRASH AND BURN!");
dlg.setIcon( TaskDialog.StandardIcon.ERROR );
dlg.setText( "The applicaiton has performed an illegal action. This action has been logged and reported." );
dlg.getDetails().setExpandedComponent(
       new JLabel( toHtml(" javax.activity.InvalidActivityException \n " +
                          "at com.ezware.dialog.task.TaskDialogTestBed.main(TaskDialogTestBed.java:316)")));
dlg.show();



There is a way to add a footer for important information which may not work anywhere else in the dialog:

TaskDialog dlg = new TaskDialog("Application Error" );
dlg.setInstruction( "CRASH AND BURN!");
dlg.setIcon( TaskDialog.StandardIcon.ERROR );
dlg.setText( "The applicaiton has performed an illegal action. This action has been logged and reported." );
dlg.getDetails().setExpandedComponent(
       new JLabel( toHtml(" javax.activity.InvalidActivityException \n " +
                          "at com.ezware.dialog.task.TaskDialogTestBed.main(TaskDialogTestBed.java:316)")));
dlg.getFooter().setText( "Your application chrashed because a developer forgot to write a unit test");
dlg.getFooter().setIcon( TaskDialog.StandardIcon.WARNING );
dlg.getFooter().setCheckBoxText( "Don't show me this error next time" );
dlg.getFooter().setCheckBoxSelected( true );
dlg.show();


The library is carefully built to work automatically with any look and feel and has a testbed for testing most of the functionality.
Here is few under Nimbus and Metal:


This is the first release. There is lot to more to do. Current plan includes standard components, command links, custom dialog command buttons etc.

I’m planning to continue documenting the development progress in this blog.
The project is available at http://code.google.com/p/oxbow/ under BSD license.
Comments and suggestions are welcome.

Minimizing TableModel maintenance (continued)


The idea of using enums as column definitions for Swing table models proved to be very valuable. It simplifies the model maintenance immensely. The support class described in previous post was enhanced and now has many more useful methods which are futher simplifying the work with such table model.

The class has following enhancements:

  • Support for cell selection
  • Support for assigning column renderers and editors
  • Conversion of column definitions to column indexes
  • Support for SwingX JXTable colum identifiers

So without any further due, here the code:


public final class TableEnumColumnSupport {

      public interface ITableColumDefinition {
            String getTitle();
      }

      private TableEnumColumnSupport() {}

      /**
       * Finds column definition by index
       * @param <T>
       * @param cls
       * @param index
       * @return
       */
      public final static > T getDefinition( Class cls, int index ) {
            return cls.getEnumConstants()[index];
      }

      /**
       * Returns titles for all column definitions.
       * If column definition implements ITableColumDefinition interface then it's getTitle method is used,
       * else enum name is converted to be used as a title
       * @param <T>
       * @param cls
       * @return
       */
      public final static > String[] getTitles( Class cls ) {

            T[] defs = cls.getEnumConstants();
            String[] titles = new String[defs.length];

            int i=0;
            for( T c: defs) {
               titles[i++] = getTitle(c);
            }
            return titles;

      }

      /**
       * Returns column title for specified colum definition
       * If column definition implements ITableColumDefinition interface then it's getTitle method is used,
       * else enum name is converted to be used as a title
       * @param <T>
       * @param columnDef
       * @return
       */
      public final static > String getTitle(T columnDef) {
            return columnDef instanceof ITableColumDefinition? ((ITableColumDefinition)columnDef).getTitle(): enumToName(columnDef);
      }

      /**
       * Returns column title for specified column index
       * @param <T>
       * @param cls
       * @param columnIndex
       * @return
       */
      public final static > String getTitle( Class cls, int columnIndex ) {
            return getTitle( getDefinition( cls, columnIndex ));
      }

      /**
       * Converts enum to readable column name
       * @param <T>
       * @param columnDef
       * @return
       */
      public static > String enumToName(T columnDef) {
            return columnDef == null? null:
                   StringUtils.capitalize( columnDef.name().replace('_', ' ').toLowerCase());
      }

      /**
       * Selects cell for specified rowIndex and column definition
       * @param <T>
       * @param table
       * @param rowIndex
       * @param columnDef
       */
      public final static > void setSelectedCell(JTable table, int rowIndex, T columnDef) {
            table.changeSelection(rowIndex, columnDef.ordinal(), false, false);
      }

      /**
       * Sets cell editor for specified column definitions in the table
       * @param <T>
       * @param table
       * @param cellEditor
       * @param columnDefs
       * @return
       */
      public final static > JTable setColumnEditor(JTable table, TableCellEditor cellEditor, T... columnDefs) {
            return TableUtils.setColumnEditor(table, cellEditor, toIndexes(columnDefs));
      }

      /**
       * Sets cell renderer for specified column definitions in the table
       * @param <T>
       * @param table
       * @param cellRenderer
       * @param columnDefs
       * @return
       */
      public final static > JTable setColumnRenderer(JTable table, TableCellRenderer cellRenderer, T... columnDefs) {
            return TableUtils.setColumnRenderer(table, cellRenderer, toIndexes(columnDefs));
      }

      /**
       * Converts column definitions to an array of actual column indexes
       * @param <T>
       * @param columnDefs
       * @return
       */
      public final static > int[] toIndexes(Collection columnDefs) {
            int[] colIndexes = new int[columnDefs.size()];
            int i = 0;
            for( T def: columnDefs ) {
                  colIndexes[i++] = def.ordinal();
            }
            return colIndexes;
      }

      /**
       * Converts column definitions to an array of actual column indexes
       * @param <T>
       * @param columnDefs
       * @return
       */
      public final static > int[] toIndexes(T... columnDefs) {
            return toIndexes( Arrays.asList(columnDefs));
      }

      /**
       * Returns table column for specified colum definition
       * @param <T>
       * @param table
       * @param columnDef
       * @return
       */
      public final static > TableColumn getColumn( JTable table, T columnDef ) {
            return table.getColumnModel().getColumn( columnDef.ordinal());
      }

       /**
     * Sets the identifiers on the table columns to match the specified column definition.
     * This will allow to use getTableColumn(Obj id) to retrieve cols.  This is preferrable to
     * using indexes (not reliable-when hiding/showing cols) or the header string (which is not reliable b/c of renderers).
     */
      public final static > void setColumnIdentifiers( JXTable table, Class cls ) {
          TableColumnModel columnModel = table.getColumnModel();
          Enumeration cols = columnModel.getColumns();
          for (Object c : cls.getEnumConstants())
          {
              cols.nextElement().setIdentifier( c );
          }
      }

}