You are hereAria User Guide / Section III: In Depth / Component Factories

Component Factories


By luano - Posted on 14 January 2009

Working with components

At the heart of Aria is the component factory. The built-in component factory constructs the Aria components and comes in two flavours; one for Swing and one for AWT. Using component factories hides much of the detail of building individual components and makes it possible to change the implementation without having to change all the client code.

Aria allows component factories to be added and hence it can accommodate a wide range of components apart from the built-in set. Aria also includes a facility to register new components via an XML registry and even an editor for this registry

Choosing how to install components

Of the two methods of installing components, the XML registry is by far the simpler as you can simple use the interactive registry editor and its built-in component factory without any custom coding. The alternative is to build a custom component factory and directly instantiate the components.

While creating a component factory may be more work it also gives more control over the process of creating and initializing the components. Using a component factory you can achieve things that are not normally possible with the XML registered components, for example Aria's repeat element is implement via a special component factory.

Using the registry editor

The Integrated Component Registry Editor is a new feature in Aria 2.0

The component registry editor can be accessed by clicking the `*' button in the component palette toolbar. Almost all of Aria's non-core components are added via this registry so you can see how the properties of the indvidual components are configured.

The editor assumes that the components are held in Jar files and are represented as Java Beans. Upon opening the editor you may add or delete Jar libraries.

The editor then scans the Jar for suitable components and displays a hierarchical view of the available components. It is important to note that the editor does not automatically include any of the components or any of the component properties. It is up to you to tell the editor what to use.

The component properties are shown as an individual component is selected. You may rename the components so as to give it a tag that is suitable for use in an XML file. Then you can choose which methods to use as properties of an individual tag. Normally only the setter methods are used in the XML but you may also use the getter methods.

The methods may also be configured to be visible in various modes:

  1. Component configuration modes

Mode

Usage

Novice

A minimal set of properties for new users

Normal

A set of properties for normal everyday use

Expert

A fuller set of properties for detail component configuration.

The idea of these modes is that reducing the number of visible properties makes it easier to locate commonly used properties. Fewer options may also make it easier for a novice to make use of the component.

Once the component and its properties have been chosen the editor generates a new components.xml file for the application. The component palette is also updated and the components can be used in the application.

The registry file for each project also acts as an overlay for the built-in registry files. Both Aria Editor and Aria include registry files for their own components. These registry files are loaded as the first page is loaded after startup of the project. Each project has its own configuration file so that its configuration is independant of other projects even though it loads data from the shared sources of built-in registry files.

Aria itself uses the registry for loading component Jar files and therefore an individual application may use components registered via several configuration files. Aria and Aria Editor each register components this way and your application itself may add another, so an application will typically have several component registration files. However, for the most part you need not worry about these details as they are all catered for by the built-in regsitry editor.

The registry editor can be used in one of two modes. The first - a simple mode, is used when the editor is opened normally. In this mode only the methods with types that Aria and Aria can construct directly from XML are allowed.

In the advanced mode, which can be accessed by holding down the CTRL key while opening the editor, all the methods are shown. In the advanced mode it is left to the user to ensure that the property values are correctly set. Normally this is ok if coding via Java, but it is difficult to achieve when using XML to define the pages.

The registration file generated by the registration editor is saved under the user profile. As a project is edited the registration file held in the project's resources folder is updated as is the project's IDE settings. In addtion any Jars required by the components added during editing of the project are copied to the project's lib folder and references in the project's IDE settings.

Installing a component factory

The factory needs to be registered with the main class, this is done through the startup.properties file. The example adds one of the component factories shipped with Aria. This factory adds a number of Swing specific components. The registration factory is just another factory and following the above we add another startup property.

Including a Component Factory

  1. NumComponentFactories=1
  2. ComponentFactory0=org.formaria.swing.SwingComponentFactory

Setup the registration factory

First the factory needs to be registered with the main class, this is done through the startup.properties file.

Adding the XRegisteredComponentFactory

  1. <code>
  2. NumComponentFactories=2 </code>
  3.  
  4. <code>
  5. ComponentFactory0=org.formaria.swing.SwingComponentFactory</code>
  6.  
  7. <code>
  8. ComponentFactory1=org.formaria.registry.RegisteredComponentFactory</code>

Then, as this class is loaded it attempts to read the XML file detailing the components, this file is also named in the startup.properties file with the "ComponentRegistry" parameters e.g. Specifying the Component Registry

  1. ComponentRegistry=components.xml

The components file then details each component for example:

Using the New Components

  1. <components>
  2. <component name="MyComponent" class="myPackage.myClass">
  3. <property name="content" type="get" method="getText"/>
  4. <property name="content" type="set" method="setText"/>
  5. <Property type="both" name="autoStart">
  6. <Param type="boolean"/>
  7. </Property>
  8. </component>
  9. </components>

You can examine this file after the component registration editor has completed or you could code in manually. The registration can include all the methods available or just those of interest, and when editing an application it may be better to have fewer methods registered as the property sheet will then be more compact. Of course, the methods in the component classes can still be invoked so long as they follow the normal rules for Java beans in declaring getters and setters.

Using reflection

Reflection can be used to find all the component attributes if you don't wish to use the editor tools, or edit the registration XML manually

Taking the automatic registration one step further, it is possible to rely totally on reflection to find the accessor methods of a component. To register components in this way we just add the reflect="true" attribute. Thus, for example to register some Swing components we add the following entries to our project's components.xml file.

Registering Swing components via reflection.

  1. <Component name="JButton" class="javax.swing.JButton" reflect="true"/>
  2. <Component name="JCheckBox" class="javax.swing.JCheckBox" reflect="true"/>
  3. <Component name="JColorChooser " class="javax.swing.JColorChooser" reflect="true"/>
  4. <Component name="JComboBox" class="javax.swing.JComboBox" reflect="true"/>
  5. <Component name="JEditorPane" class="javax.swing.JEditorPane" reflect="true"/>
  6. <Component name="JFormattedTextField" class="javax.swing.JFormattedTextField" reflect="true"/>
  7. <Component name="JLabel" class="javax.swing.JLabel" reflect="true"/>
  8. <Component name="JList" class="javax.swing.JList" reflect="true"/>
  9. <Component name="JOptionPane" class="javax.swing.JOptionPane" reflect="true"/>
  10. <Component name="JProgressBar" class="javax.swing.JProgressBar" reflect="true"/>
  11. <Component name="JRadioButton" class="javax.swing.JRadioButton" reflect="true"/>
  12. <Component name="JSlider" class="javax.swing.JSlider" reflect="true"/>
  13. <Component name="JSpinner" class="javax.swing.JSpinner" reflect="true"/>
  14. <Component name="JSplitPane" class="javax.swing.JSplitPane" reflect="true"/>

In addition to describing the components in this way, the properties of a component can be set reflectively even if the (other) attributes have been specified explicitly. For example If there is no way of setting the attributes of a component directly, or through the XAttributedComponent interface, or through a ComponentAdapter then reflection will be used to set the properties. The methods signatures are parameter types are set using the JVM type signature syntax, for example

Using reflection to set a component property

  1. <List name="sourceList" x="10" y="11" w="305" h="251" dragEnabled="[Ztrue]"/>

where the dragEnabled attribute corresponds to a call to setDragEnabled( boolean state ) . See http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/types.html for more details of the type specifications.

Using the new components

The components added by the new factory can be used immediately. Each component type should have unique name so when adding a new component all that is necessary is that you know this name. Then the normal Java or XML coding can be used to construct the component.

Jars added automatically to project

When adding components to a page from the Aria palette, jar files are imported to the project lib folder and added to the project properties. For example, the Spinner component utilises the AriaRuntimeCommon jar, this jar is thereby added to the projects lib folder and then added to the libraries in the project properties when the XSpinner component is first added to a Aria page in the project.

Anatomy of a ComponentFactory

Every component factory must implement the ComponentConstructor interface. This interface is relatively simple and is discussed below in the context of the source code for the SwingComponentFactory class.

A Component Factory Implementation

  1. 1 package org.formaria.swing;
  2. 2
  3. 3 import org.formaria.aria.ComponentConstructor;
  4. 4 import org.formaria.aria.ComponentFactory;
  5. 5 import java.awt.Component;
  6. 6 import java.util.Hashtable;
  7. 7
  8. 8 /**
  9.  9 * A factory for non-base Swing components such as Trees
  10.  10 * <p>Copyright: Copyright (c) Formaria Ltd., 2001-2004</p>
  11.  11 * <p>$Revision: 1.3 $ </p>
  12.  12 * License see license.txt
  13.  13 */
  14. 14 public class SwingComponentFactory implements XComponentConstructor
  15. 16 {
  16. 17 private String packageName = "org.formaria.swing";
  17. 18
  18. 18 public SwingComponentFactory()
  19. 20 {
  20. 21 // Register the extra binding factory
  21. 22 SwingDataBindingFactory.register();
  22. 23 }
  23. 24
  24. 25 /**
  25.  26 * A generic factory for adding XComponents. The component is constructed, positioned and
  26.  27 * added to the parent panel if one exists. The component is named with a counter value
  27.  28 * to uniquely identify the control.
  28.  29 * This factory does not use this method and all components must be added by name
  29.  30 * When a ScrollPane is addd it becomes the parent.
  30.  31 * @param cf the calling component factory
  31.  32 * @param type a constant identifying the type of component to be created
  32.  33 * @param content the component text/content
  33.  34 */
  34. 35 public Component constructComponent( XComponentFactory cf, int type, String content )
  35. 36 {
  36. 37 return null;
  37. 38 }
  38. 39
  39. 40 /**
  40.  41 * A generic factory for adding XComponents. The component is constructed, positioned and
  41.  42 * added to the parent panel if one exists. The component is named with a counter value
  42.  43 * to uniquely identify the control.
  43.  44 * @param cf the calling component factory
  44.  45 * @param type a name identifying the type of component to be created
  45.  46 * @param content the component text/content
  46.  47 */
  47. 48 public Component constructComponent( ComponentFactory cf, String type, String content )
  48. 49 {
  49. 50 Component comp = null;
  50. 51
  51. 52
  52. 53
  53. 54 if ( type.compareToIgnoreCase( "Tree" ) == 0 )
  54. 55 comp = new Tree();
  55. 56 else if ( type.compareToIgnoreCase( "Table2" ) == 0 )
  56. 57 comp = new Table2();
  57. 58
  58. 59 return comp;
  59. 60 }
  60. 61
  61. 62 /**
  62.  63 * A generic factory method for adding non component elements.
  63.  64 * @param cf the calling component factory
  64.  65 * @param type the object type
  65.  66 * @param name a name identifying the element to be created
  66.  67 * @param content the component text/content
  67.  68 * @param attribs the element attributes if any
  68.  69 */
  69. 70 public Object addElement( ComponentFactory cf, String type, String name, String content, Hashtable attribs )
  70. 71 {
  71. 72 return null;
  72. 73 }
  73. 74
  74. 75 /**
  75.  76 * Notify the component factories that some of their settings may have changed.
  76.  77 * This factory does not yet use any startup properties or parameters
  77.  78 */
  78. 79 public void update() {}
  79. 80
  80. 81 /**
  81.  82 * Set the package name for the factory's widgets.
  82.  83 */
  83. 84 public void setPackageName( String defPackage )
  84. 85 {
  85. 86 packageName = defPackage;
  86. 87 }
  87. 88 }
  88.  

: .

  1. Overview of the Component Factory Code

Lines 1-15

Register/Import individual components

Line 16

Names the package.

Lines 18-22

Construct a new instance of the factory and sets up the binding factory. The binding factory specifies the bindings that are used by default with the components in this factory. It is not necessary to register the binding types at this point but it is convenient to do so and this helps ensure that the bindings will be available immediately after component construction.

Lines 34-37

This is a deprecated API so no implementation is provided. The API was used in the first edition of Aria and proved difficult to extend and was therefore replaced by the API implemented in the next section.

Lines 47-59

Does the actual construction of the components. Essentially this factory is a switch on the component type name. This method is called from the component factory whenever its addComponent method is invoked. The factory iterates through the built-in component constructors and then the registered constructors till a component has been constructed for the named type. In this way it is important to name the component types uniquely and to order the factories in the correct order should there be a name conflict.

Lines 69-71

This method is not implemented but is required by the interface specification. The interface makes it possible to include non component information in a page description. For example Aria's survey packages allow the response to questions to be specified along with the page. The response element is used by the Question component and does not itself create a component

The addElement method used for such implementations. The method is only called if no factory chooses to construct a component for the element. The essential difference from the previous method is that the addElement does not add a component to the page.

Lines 83-87

This method is used by the component factory and must be provided by all factories. The package name is that of the components rather than the factory itself.

Using the New Components

  1. <Question name="Q1" x="0" y="100" w="640" h="60" id="1001" content="Is Aria a great Java application framework?" cue="cue.gif">
  2. <Response content="Yes" id="1"/>
  3. <Response content="No" id="2"/>
  4. <Response content="No comment" id="2"/>
  5. </Question>

Sometimes it is useful to register components without first having to create a component factory. Components can be registered by adding entries to an XML file. The RegisteredComponentFactory can be used to load components specified in this way.

A sample component

To demonstrate how easy it is to include a new component in Aria we have included the source for a test component in the Aria Editor source distribution at test.Formaria.components.TestShape . The component doesn't do very much except paint a few trivial shapes. Here's the source code:

TestShape a sample component

  1. package test.Formaria.components;
  2.  
  3. import java.awt.Dimension;
  4. import java.awt.Graphics;
  5. import javax.swing.JComponent;
  6.  
  7. import org.formaria.aria.AttributedComponent;
  8.  
  9. /**
  10.   * Draws a simple shape
  11.   * <p>Copyright: Copyright Formaria Ltd. (c) 2001-2005</p>
  12.   * <p>License: see license.txt</p>
  13.   * <p>$Revision: 1.1 $</p>
  14.   */
  15. public class TestShape extends JComponent implements XAttributedComponent
  16. {
  17. protected int shape = 0;
  18.  
  19. public static final int RECTANGLE = 0;
  20. public static final int ORTHO_LINE = 1;
  21. public static final int EXTRABOLD_HORIZONTAL = 2;
  22. public static final int BOLD_HORIZONTAL = 3;
  23. public static final int NORMAL_HORIZONTAL = 4;
  24. public static final int THIN_HORIZONTAL = 5;
  25. public static final int EXTRABOLD_VERTICAL = 6;
  26. public static final int BOLD_VERTICAL = 7;
  27. public static final int NORMAL_VERTICAL = 8;
  28. public static final int THIN_VERTICAL = 9;
  29. public static final int RIGHT_TOP_LINE = 10;
  30. public static final int LEFT_TOP_LINE = 11;
  31. public static final int ELLIPSE = 12;
  32. public static final int SOLID_ELLIPSE = 13;
  33. public static final int SOLID_DIAMOND = 14;
  34. public static final int DIAMOND = 15;
  35.  
  36. /**
  37.   * Constructor for a new Shape
  38.   */
  39. public TestShape()
  40. {
  41. }
  42.  
  43. /**
  44.   * Fills the shape with the background color
  45.   * @param g the graphics context
  46.   */
  47. public void paintComponent( Graphics g )
  48. {
  49. super.paintComponent( g );
  50.  
  51. Dimension d = getSize();
  52. g.setColor( getForeground());
  53.  
  54. switch( shape ) {
  55. case RECTANGLE:
  56. g.fillRect( 0, 0, d.width, d.height );
  57. break;
  58. case ORTHO_LINE:
  59. g.fillRect( 0, 0, getSize().width, getSize().height );
  60. break;
  61.  
  62. case EXTRABOLD_HORIZONTAL:
  63. g.drawLine( 0, 0 +3, d.width, 3 );
  64. case BOLD_HORIZONTAL:
  65. g.drawLine( 0, 0 +2, d.width, 2 );
  66. case NORMAL_HORIZONTAL:
  67. g.drawLine( 0, 0 +1, d.width, 1 );
  68. case THIN_HORIZONTAL:
  69. g.drawLine( 0, 0, d.width, 0 );
  70. break;
  71.  
  72. case EXTRABOLD_VERTICAL:
  73. g.drawLine( 3, 0, 3, d.height );
  74. case BOLD_VERTICAL:
  75. g.drawLine( 2, 0, 2, d.height );
  76. case NORMAL_VERTICAL:
  77. g.drawLine( 1, 0, 1, d.height );
  78. case THIN_VERTICAL:
  79. g.drawLine( 0, 0, 0, d.height );
  80. break;
  81.  
  82. case EXTRABOLD_VERTICAL:
  83. g.drawLine( 3, 0, 3, d.height );
  84. case BOLD_VERTICAL:
  85. g.drawLine( 2, 0, 2, d.height );
  86. case NORMAL_VERTICAL:
  87. g.drawLine( 1, 0, 1, d.height );
  88. case THIN_VERTICAL:
  89. g.drawLine( 0, 0, 0, d.height );
  90. break;
  91.  
  92. case RIGHT_TOP_LINE:
  93. g.drawLine( d.width, 0, 0, d.height );
  94. break;
  95. case LEFT_TOP_LINE:
  96. g.drawLine( 0, 0, d.width, d.height );
  97. break;
  98.  
  99. case ELLIPSE:
  100. g.drawOval( 0, 0, d.width, d.height );
  101. break;
  102. case SOLID_ELLIPSE:
  103. g.fillOval( 0, 0, d.width, d.height );
  104. break;
  105.  
  106. case SOLID_DIAMOND:
  107. case DIAMOND:
  108. {
  109. int[] xpts = new int[ 4 ];
  110. int[] ypts = new int[ 4 ];
  111. xpts[ 0 ] = 0 + d.width / 2;
  112. xpts[ 1 ] = 0 + d.width;
  113. xpts[ 2 ] = 0 + d.width / 2;
  114. xpts[ 3 ] = 0;
  115. ypts[ 0 ] = 0;
  116. ypts[ 1 ] = 0 + d.height / 2;
  117. ypts[ 2 ] = 0 + d.height;
  118. ypts[ 3 ] = 0 + d.height / 2;
  119. if ( shape == SOLID_DIAMOND )
  120. g.fillPolygon( xpts, ypts, 4 );
  121. else
  122. g.drawPolygon( xpts, ypts, 4 );
  123. }
  124. break;
  125.  
  126. }
  127. }
  128.  
  129. /**
  130.   * Get shape identifier. The ID is an enumerated constant
  131.   * @param shapeId the new shape ID.
  132.   */
  133. public void setShape( int shapeId )
  134. {
  135. shape = shapeId;
  136. repaint();
  137. }
  138.  
  139. /**
  140.   * Get shape identifier.
  141.   * @return the enumerated constant for this shape
  142.   */
  143. public int getShape()
  144. {
  145. return shape;
  146. }
  147. /**
  148.   * Set one or more attributes of the component. Currently this handles the
  149.   * attributes:
  150.   * <OL>
  151.   * <LI>shape, value=1 to 15</LI>
  152.   * </OL>
  153.   * @param attribName the attribute name
  154.   * @param attribValue the attribute value
  155.   */
  156. public void setAttribute( String attribName, String attribValue )
  157. {
  158. if ( attribName.compareTo( "shape" ) == 0 )
  159. shape = new Integer( attribValue ).intValue();
  160.  
  161. repaint( 100 );
  162. }
  163. }

There isn't an awful lot to say about this component as it does so little and the details of the painting are unimportant. What is important though is the interface it offers for programming within Aria.

The component paints a shape which is dictated by the shape property. This property has a getter and a setter method ( getShape and setShape ).

The component also implements the XAttributedComponent method to assist the setting up of the component via XML. The setAttribute method required by this interface sets the value of an attribute, in this case the ` shape ' attribute.

If we then build this component into a Jar file for redistribution it can be used within Aria. Using the component registry editor we can add the file and select the Shape property for inclusion. This process generates the following XML:

The XML registration for the test component.

  1. <Components>
  2. <Jars>
  3. <Jar path="C:\CVS\AriaEditor\dist\TestComponents.jar" project="MyProject"/>
  4. </Jars>
  5. <Component name="TestShape" class="test.Formaria.components.TestShape" icon="" ui="Swing">
  6. <Property name="Shape" mode="Normal" type="both">
  7. <Param type="int"/>
  8. </Property>
  9. </Component>
  10. </Components>

Special components

Sometimes the component factories may not be enough to allow you handle the needs of an individual component. However, as a Java framework you can employ the normal programming techniques to implement your functionality. This can take you from one line code fragments for setting an esoteric component attribute to complete sets of custom classes. You can freely mix Aria code, Aria XML and normal Java and to illustrate this ability to extend the framework lets look at how to add a web browser component.

Hooking up a web browser

Using the Java.net JDIC project (http://jdic.dev.java.net) we can hook up an instance of the desktop's default web browser. In this example the browser will be embedded in a popup window, an instance of the Dialog class and therefore we need to subclass Dialog :

Embed a web browser.

  1. package org.formaria.mypackage;
  2.  
  3. import java.net.URL;
  4.  
  5. import java.awt.BorderLayout;
  6. import java.awt.Panel;
  7.  
  8. import org.jdesktop.jdic.browser.WebBrowser;
  9. import org.formaria.swing.Dialog;
  10. import org.formaria.aria.data.BaseModel;
  11. import org.formaria.aria.data.Model;
  12. import javax.swing.SwingUtilities;
  13. import org.jdesktop.jdic.browser.WebBrowserListener;
  14. import org.jdesktop.jdic.browser.WebBrowserEvent;
  15. import java.net.URLConnection;
  16.  
  17. /**
  18.   * Show more information about a component
  19.   * <p>Copyright Formaria Ltd. (c) 2003-2004 </p>
  20.   * <p>License: see license.txt </p>
  21.   * $Revision: 1.3 $ not attributable
  22.   */
  23. public class BrowserDialog extends Dialog implements WebBrowserListener
  24. {
  25. DataModel selectedNode;
  26. int nodeNumber;
  27. Dialog webDialog;
  28. WebBrowser webBrowser;
  29.  
  30. public BrowserDialog()
  31. {
  32. }
  33.  
  34. public void pageActivated()
  35. {
  36. SwingUtilities.invokeLater( new Runnable()
  37. {
  38. public void run()
  39. {
  40. website();
  41. }
  42. } );
  43. }
  44.  
  45. public void done()
  46. {
  47. if ( wasMouseClicked() ) {
  48. stop();
  49. closeDlg();
  50. }
  51. }
  52.  
  53.  
  54. /**
  55.   * Show the website
  56.   */
  57. public void website()
  58. {
  59. try {
  60. // Check if the net is accessible
  61. URL url = new URL( "http://www.Formaria.com" );
  62. URLConnection conn = url.openConnection();
  63. if ( conn != null ) {
  64. conn.connect();
  65.  
  66. String websiteUrl = ( String ) getAttribute( "url", "websiteBtn" );
  67. webDialog = new Dialog( false, 0 );
  68. webDialog.setCaption( "Internet content" );
  69. webDialog.setModal( true );
  70.  
  71. //Use below code to check the status of the navigation process,
  72. //or register a listener for the notification events.
  73. webBrowser = new WebBrowser();
  74. webBrowser.setDebug( true );
  75. webBrowser.addWebBrowserListener( this );
  76. WebBrowser.Status myStatus = webBrowser.getStatus();
  77.  
  78. try {
  79. webBrowser.setURL( new URL( websiteUrl ) );
  80. }
  81. catch ( Exception e ) {
  82. System.out.println( e.getMessage() );
  83. return;
  84. }
  85.  
  86. Panel panel = new Panel();
  87. panel.setLayout( new BorderLayout() );
  88. panel.setLocation( 0, 0 );
  89. panel.setSize( 700, 500 );
  90. panel.add( webBrowser, BorderLayout.CENTER );
  91.  
  92. webDialog.setLayout( new BorderLayout() );
  93. webDialog.getContentPane().add( panel, BorderLayout.CENTER );
  94. webDialog.setSize( 700, 500 );
  95. webDialog.doLayout();
  96. webDialog.showDialog( parent );
  97. closeDlg();
  98. }
  99. }
  100. catch ( Exception ex ) {
  101. }
  102. }
  103.  
  104. private void stop()
  105. {
  106. if ( webBrowser != null ) {
  107. webBrowser.stop();
  108. webBrowser.setVisible( false );
  109. webBrowser = null;
  110. }
  111. }
  112.  
  113. // Methods from WebBrowserListener
  114. public void downloadStarted(WebBrowserEvent webBrowserEvent)
  115. {}
  116.  
  117. public void downloadCompleted(WebBrowserEvent webBrowserEvent)
  118. {
  119. // webDialog.hide();
  120. }
  121.  
  122. public void downloadProgress(WebBrowserEvent webBrowserEvent)
  123. {}
  124.  
  125. public void downloadError(WebBrowserEvent webBrowserEvent)
  126. {}
  127.  
  128. public void titleChange(WebBrowserEvent webBrowserEvent)
  129. {}
  130.  
  131. public void statusTextChange(WebBrowserEvent webBrowserEvent)
  132. {}
  133. }

Much of the above code is dictated by the WebBrowserListener interface but it is worth noting a couple of points in relation to the code. First the pageActivated method use the SwingUtilities invokeLater method to delay display of the web page as the download of the web content may take considerably longer than displaying the dialog. Secondly the code calls the web browser's setDebug( true ) method to turn on extra diagnostics. The method causes additional information to be output to the console and this can be quite useful given the number of components involved in the process of displaying the web page and the fact that some of these components are native or operating system components and fall outside of the normal realm of debugging for Java programs.

Using third party components in this way also introduces issues with regard to distribution. While the development environment may allow you to include all the various sub components needed design and build your application yo may need to take some additional steps to ensure that the component can be properly used on end user systems. This additional requirement may simply be to include some additional Jar files or it may be more complex. Unfortunately Aria can do little of this work for you but once you have figured out the requirements it should be possible to include the additional step(s), including the steps necessary to build the distribution files within your project's ANT build file.

Adding a splash screen

The customization of a component is not restricted to creation time and you can continue to interact and customize a component as you would in a typical Java application. To demonstrate this capability we will create an application splash screen that automatically dismisses itself after five seconds. (Please note that as of Version 3.0 with the Swing widget set there is an easier way to create a simple splash screen and this is demonstrated at the end of this chapter).

To display a splash screen for an application the simplest method is to invoke the display of a popup dialog in your application's first page. The process requires a small amount of Java coding. The display code can be embedded in the first page's pageActivated method as below:

Launch a splash screen

  1. private static boolean splashActivated = false;
  2.  
  3. public void pageActivated()
  4. {
  5.  
  6. // This flag prevents the splash screen being redisplayed if the first page is reactivated
  7. // or shown again.
  8. if ( !splashActivated ) {
  9. showVersionInfoDialog( true );
  10. splashActivated = true;
  11. }
  12. }
  13.  
  14. private Dialog showVersionInfoDialog( boolean modal )
  15. {
  16. final Dialog popupDialog = (Dialog)pageMgr.loadPage( "SplashScreen" );
  17. final Page thisPage = this;
  18. final boolean isModal = modal;
  19.  
  20. SwingUtilities.invokeLater( new Runnable()
  21. {
  22. public void run()
  23. {
  24. // Only needed if the content contains bound controls.
  25. popupDialog.updateBindings();
  26.  
  27. popupDialog.setModal( isModal );
  28.  
  29. // Force the dialog to calculate its size
  30. popupDialog.pack();
  31. popupDialog.setSaveOnClose( false );
  32.  
  33. // Sleep for 5 seconds and then close the dialog - only for the `modeless' version.
  34. // Change or remove the next if statement if you want a model splash screen.
  35. // to be dismissed on the timer
  36. if ( !isModal ) {
  37. new Thread()
  38. {
  39. public void run()
  40. {
  41. long startTime = new Date().getTime();
  42. try {
  43. // Loop so that the dialog can repaint itself once any images have loaded
  44. while (( new Date().getTime() - startTime ) < 5000L ) {
  45. popupDialog.repaint();
  46. Thread.currentThread().sleep( 100 );
  47. }
  48. }
  49. catch ( Exception ex ) {}
  50. popupDialog.closeDlg();
  51. }
  52. }.start();
  53. }
  54. popupDialog.showDialog( thisPage, "Welcome to my application", null );
  55.  
  56. }
  57. } );
  58. return popupDialog;
  59. }

The showVersionInfoDialog method loads the SplashScreen page (it can be setup like any other dialog/page) and then starts a background thread that will dismiss the dialog after a sleep period of 5000 milliseconds.

Finally in implementing the actual splash screen it is important to note that the page being displayed is assumed to be an instance of Dialog and as such it needs to derived from the Dialog class instead of Page . If you are using XML don't forget to specify the class in the page declaration as follows:

Specify the Dialog class for your splash screen in XML

  1. <Page class="org.formaria.swing.Dialog">

Like in the case of the web browser the interesting code embedded in a call to invokeLater .

Dropping to Java can help work around many limitations and solve many more problems than the above examples. In fact you should be able to employ just about any type of component or component feature within your Aria application. And remember, where XML is insufficient you can supplement it with Java.

Swing splash screen

In the Swing version of the Aria libraries there is a class XSplashWindow that can be used to display a simple splash screen. The splash window is loaded prior to the rest of the Aria framework being loaded and therefore not all the resource handling of the framework is available to this class. In fact the class loads and then invoked the main method in the framework proper as we will see below. The sequence of execution is thus, that firstly the SplashWindow class is invoked and it in turn starts the class passed as an argument (or Applet if none is specified), which should be the entry point to the Aria framework (which, of course depending on the application style being used).

The the application is invoked as follows:

Starting with a splash screen

  1. java -cp ./lib/AriaRuntimeCommon.jar org.formaria.swing.splash.SplashWindow <arglist>

where is the argument list that would otherwise be passed to the Applet , or whatever was the normal startup class for your application. The application also needs to find the file splash.gif , which should be located on the classpath. The splash screen functionality can be found in a number of the examples that ship with Aria. The splash screen functionality is also setup by the project wizard, by choosing the

The results really depend on what you add as the splash image. The splash screen can also be customized by subclassing the SplashWindow class, and in the image below this has been done to add transparency support

Component customization

Aria 3.0 adds a new facility for customizing components such that a collection of method calls can be made to customize a component and set properties on that component. The facility is itself customizable, supporting adapters to control how the properties are applied to the component. The customizations can be applied post creation of the component or just before activation of the page, when the component has been populated.

For example in the case of a table the property may apply to the table columns rather than the table itself. In the example below a table is customized in this way:

Setting atable customizer

  1. <ScrollPane>
  2. <Table2 name="currentAED" .... customizer="tableCustomizer"/>
  3. </ScrollPane>

the customizer attribute refers to a customization specified in the customizations.xml file. The customization file is as follows:

A sample table customization

  1. <customizer>
  2. <Adaptors>
  3. <Adaptor name="ColumnAdaptor" class="org.formaria.registry.TableColumnCustomizer"/>
  4. </Adaptors>
  5. <PropertySets>
  6. <PropertySet name="tableCustomizer" when="1">
  7. <AutoCreateColumnsFromModel args="false"/>
  8. <GridColor args="white"/>
  9. <ViewportBackground args="white"/>
  10. <Background target="parent"
  11. value="white"/>
  12. <HeaderRenderer adaptor="ColumnAdaptor"
  13. scope="*"
  14. class="org.formaria.swing.table.TableHeaderRenderer"
  15. args="getTableHeader()"/>
  16. <CellRenderer adaptor="ColumnAdaptor"
  17. scope="0-6"
  18. class="org.formaria.swing.table.AltRowTableCellRenderer">
  19. <VerticalAlignment args="1"/><!--SwingConstants.TOP -->
  20. <AltUnselectedColors args="0,0,102;252,252,255"/>
  21. </CellRenderer>
  22.  
  23. <CellRenderer adaptor="ColumnAdaptor" scope="N"
  24. class="org.formaria.swing.table.AltRowTableCellRenderer">
  25. <VerticalAlignment args="1"/><!--SwingConstants.TOP -->
  26. <AltUnselectedColors args="0,0,102;252,252,255"/>
  27. <WrapsText args="true"/>
  28. </CellRenderer>
  29. <AutoResizeMode args="1"/><!--JTable.AUTO_RESIZE_NEXT_COLUMN -->
  30. </PropertySet>
  31. </PropertySets>
  32. </customizer>

The result of the above customization is a table like the following with row striping and customized headers:

Using Drag and Drop

Drag and drop supported has been added for selected Swing components. Support is enabled by adding dragEnabled="true" as an attribute. Additional support can be registered by adding a transferhandlers.xml file to the project. The setup is similar to that of the setup of data bindings.

Drag and drop configuration

  1. <Bindings>
  2. <InspectorTransferHandlers>
  3. </InspectorTransferHandlers>
  4.  
  5. <ClassTransferHandlers>
  6. <TransferHandler target="org.formaria.swing.List"
  7. class="org.formaria.swing.dnd.ModelTransferHandler"
  8. type=""/>
  9. </ClassTransferHandlers>
  10.  
  11. <InterfaceTransferHandlers>
  12. <!-- This does not actually use a binding -->
  13. <!--TransferHandler target="org.formaria.aria.ModelHolder">
  14. <onError fixError="DataSourceClassMissing"/>
  15. </Binding-->
  16. </InterfaceTransferHandlers>
  17.  
  18. <InstanceTransferHandlers>
  19.  
  20. <TransferHandler target="org.formaria.swing.Image"
  21. class="org.formaria.swing.dnd.ModelTransferHandler"
  22. type=""/>
  23. </InstanceTransferHandlers>
  24. </Bindings>