Section 17.3. Porting BudgetPro to SWT


17.3. Porting BudgetPro to SWT

The conversion of an existing application is a complex process. Always consider rewriting from scratch. Still, it is worthwhile to show an application converted from Swing to SWT, because it will emphasize the relationship between the two.

We begin with the reobjecting. Starting with the BudgetPro class, we need to add an instance of the Display class. Then the JFrame becomes a Shell. Likewise, the JLabels become Labels. Then ... Wait a minute. You don't need a blow-by-blow account. Maybe it would be simpler to show you what SWT classes roughly correspond to the equivalent Swing classes (Table 17.1).

Table 17.1. Major SWT widgets and their Swing equivalents

SWT widget

Analogous Swing component

Description

Button

JButton

Display widget that sends notification when pressed and/ or released.

Canvas

java.awt.Canvas, but see also java.awt.Graphics2D

Composite widget that provides a surface for drawing arbitrary graphics. May be used to create custom widgets.

Caret

javax.swing.text.Caret

A cursor used as the insertion point for text.

Combo

JComboBox

Widget that permits the user to choose a string from a list of strings, or to enter a new value into a text field.

Composite

JPanel

Widget that is capable of containing other widgets.

Group

JPanel

Composite widget that groups other widgets and surrounds them with an etched border and/or label.

Label

JLabel

Nonselectable widget that displays an image or a string.

List

JList

Selectable widget to choose a string or strings from a list of strings.

Menu

JMenu

User interface widget that contains menu items.

MenuItem

JMenuItemA, JCheckboxMenuitem, JRadioButtonMenuitem

Selectable widget that represents an item in a menu.

ProgressBar

JProgressBar

Nonelectable widget that displays progress to the user.

Scale

JSpinner

Widget that represents a range of numeric values.

ScrollBar

JScrollPane.

Widget that represents a range of positive numeric values. Used in a Composite that has V_SCROLL and/or H_SCROLL styles. The mapping to Swing is not very tight here, since JScrollPane is like a combination of Composite and ScrollBar.

Shell

JPanel

Window that is managed by the OS window manager. A Shell may be a child of a Display or another shell.

Slider

JSlider

Widget that represents a range of numeric values. Differs from a Scale by having a "thumb" to change the value along the range.

TabFolder

JTabPane

Composite widget that groups pages that can be selected by the user using labeled tabs.

TabItem

Any JComponent

Selectable user interface object corresponding to a tab for a page in a tab folder.

Table

JTable

A selectable widget that displays a list of table items that can be selected by the user. Rows are items, columns are attributes of items.

TableColumn

JTableColumn or instance of TableColumnModel

Selectable widget that represents a column in a table.

TableItem

TableCellRenderer or TableCellEditor

Selectable widget that represents an item in a table.

Text

JTextField, JPasswordField, JFormattedTextField, JTextArea, JEditorPane, JTextPane

Editable widget that allows the user to type text into it.

ToolBar

 

Composite widget that supports the layout of selectable toolbar items.

ToolItem

JButton

Selectable widget that represents an item in a toolbar.

tree

Jtree

A selectable widget that displays a hierarchical list of user-selectable tree items.

treeItem

MutableTreeNode

Selectable user interface object that represents a hierarchy of items in a tree.


We are going to walk you through converting only one of the GUI source files for the BudgetPro application. We will leave converting the rest as an exercise for you. We'll talk about some of the entertaining differences between the models. As you shall see, there is no clear "winner" here between SWT and Swing. Almost all technical choicesSWT versus Swing, Java versus C++, Emacs versus vi, or for that matter UNIX versus Windowsare tradeoffs. This is no exception. There are things we like about SWT. For simple GUI applications, we think it is easier to set up and use. We think it is easier to learn in its entirety than Swing. Swing, on the other hand, is more complete, offering classes that will do more than SWT. So the best solution depends (as always) on your requirements.

17.3.1. Step 1: Convert the Class Members

We are going to tackle converting BudgetPro.java from Swing to SWT. In real life, this is an exercise you are unlikely to have to carry out. You will more likely write your GUI applications from scratch. But going through the conversion provides a useful roadmap for talking about the architecture of SWT; it teaches you SWT in terms of a class library with which you are already familiar.

First off, we change the packages imported at the start of the file. Remove all of the awt and swing packages. If you are using an IDE, this should flag every single line of code that touches the GUI as an error. This can be a big help when you are doing a mass conversion like this. When you have killed all the compile errors, you know you are well on your way to completing the conversion.

Replace the import statements with the imports you are likely to need for your SWT application. These are:

 import org.eclipse.swt.*; // The static SWT class, which contains a number of constants. import org.eclipse.swt.widgets.*; // The widgets library.  Almost all your display elements are here. import org.eclipse.swt.events.*;  // Event handlers import org.eclipse.swt.layout.*;  // Layout managers 

We will go into these families of classes in more detail as we convert the members and methods of BudgetPro.java.

The next step is to convert the GUI members of the class from the Swing classes to their SWT counterparts. Of course, SWT requires the Display class, which has no analog in SWT, so we add a Display type member named disp just ahead of the frame member.

Next, we change the type of frame from JFrame to Shell. We could rename the member,[10] but why add to our typing burden? The name is still clear and meaningful, even if it doesn't match the SWT name.[11] There's more to it than just changing the type, however. The constructor call for the JFrame doesn't match any constructor for Shell. In fact, the Shell constructor requires a Display object argument, and all subsequent constructors for widgets and controls require a Composite as an argument.

[10] If you are using Eclipse, this is easily done throughout your code with the Refactoring feature.

[11] All right, I'm being lazy. Write your own book if you don't like it.

This is a key difference between Swing and SWT. Swing allows you to build GUI components in arbitrary order at arbitrary times and then join them up to the GUI with an add() method call. SWT instead requires that you link your components up to the GUI element they belong to when they are constructed. There are good reasons for this difference. Remember that SWT allocates native objects and memory that Java's garbage collector cannot recover. Because of this, SWT makes the promise that if you call the dispose() method on any SWT object, it will dispose of it and everything it contains. That allows you to clean up all resources from an SWT program by calling dispose() on the top level Display object. If SWT allowed you to build GUI structures independently and then graft them onto the hierarchy, it could not keep this promise. For this reason (amongst others) SWT objects are always built in a fairly rigid top-down manner.[12]

[12] In some ways, this greatly simplifies SWT programs, but at the cost of some reusability. With Swing, you could construct a panel or other GUI element and reuse it in many places. You can achieve the same thing in SWT by encapsulating such a construct in its own class and passing in a parent to the constructor, but this is a bit more bulky and complex than the Swing way.

The most direct consequence of this is that we have to get rid of the constructors on these declarations. We'll start construction in the main(). So, away with the constructors for the GUI elements. We now need to change the JButtons to Buttons and the JLabels to Labels. Again, if you are using a dynamic IDE, you should see your error count skyrocket with these changes (well, maybe not really skyrocket, since the import changes have already produced a lot of errors right off the bat).

Key SWT Abstractions

Composite is one of the key abstractions in SWT. Any control that may contain other controls is a Composite.

Here's a quick rundown of the key abstract classes and interfaces in SWT, along with the basics of the functionality they embody:

  • A Widget is the abstract superclass of all user interface objects in SWT. At this level the methods exist that create, dispose, and dispatch events to listeners. Every single class we use in this chapter, with the exception of event handlers and layout managers, is a Widget.

  • A Control is the abstract superclass of all windowed user interface classes. This is almost all of the UI classes, either by direct descent or through classes such as Sash or Scrollable. All constructors for Control classes require a Composite parent class as a constructor argument.

  • A Composite is a Control which is capable of containing other Controls. One direct descendant of Control which is very similar to the Swing JPanel is Group.

The relationships and the power of these abstractions will become clear as you work with real-life examples.


Finally, we remove the AbstractTableModel member. SWT has a simpler (and more limited) table functionality that we will discuss later.

17.3.2. Step 2: Converting the main() Method

The main (pun unintended) changes that need to be made here include allocating the SWT Display, changing from instantiating a JFrame to a Shell, doing away with the Swing "look and feel" stuff (an SWT application always looks like a platform-native application, that's SWT's main selling point), and reworking the construction of the GUI. We'll explain that a little bit later.

For now, we take care of the simple changes. Remember that main() is a static method, so we do not have any nonstatic class members available right now. The original BudgetPro constructor took a JFrame argument, now it will have to get a Display and a Shell. So we have to allocate a local Display and a local Shell. We also need to add the Display argument to the BudgetPro constructor.

After this is done, we modify the call to the constructor to pass the local Display and Shell to our class instance.

Next, we have to set a layout manager. The original application used the Swing BorderLayout layout manager. SWT doesn't have such a critter. Fortunately, the original used only the north, center, and south positions of the BorderLayout. SWT has a simple layout manager called a FillLayout that puts its contained controls in a single row or column, equally sized. Putting the three controls in a column will end up looking much like using the north, center, and south of a BorderLayout. So we change the call to the frame.setLayout() to pass in a new FillLayout and add the SWT.VERTICAL attribute.

The SWT Class

The SWT class is pretty bare-bones. Its primary use is a library of named constants used for attributes to Widget (and other) constructors. You'll see such SWT. xxxx constants all over your typical SWT application. There are a handful of methods that the SWT class provides, all of them static, including error(), which throws an SWTException, getPlatform(), which returns a string with the name of the platform on which SWT is running, and getVersion(), which returns an int version number.

It also has a subclass, called OLE, which is a Windows-only class that provides ActiveX support for SWT. Obviously, such use is nonportable and non-Linux, so we won't talk any more about it.


The next block of code in main() sets the Swing look and feel. SWT has nothing like this. All SWT applications look like native applications (we seem to be saying that a lot), so all of this code may be removed.

The next block of code calls methods on the application object (app) that, in the original, construct the three "chunks" of UI and add them to the frame using the BorderLayout attributes. Since, as we explained earlier, all SWT controls must be explicitly joined to a parent control when they are constructed, the separate create-then-add semantics used in the original will not apply. In the next section, we will walk through converting one of these three create methods. For now, it is enough to know that they will be changed to be methods that return void (no return value) and the calls to add() may be deleted.

That completes the conversion of main().

17.3.3. Step 3: Converting the GUI build() and init() Methods

Lest you believe that this means the application is ready to run, just try compiling what you have. Got a few errors yet, don't we?

Let's walk through converting the createStatus() method and its related methods. We'll then briefly discuss converting the createList() and createButtons() concentrating on the details of the unique UI widgets used in each.

17.3.3.1 Converting the GUI build() Method

In BudgetPro, the top part of the UI is the status pane. It consists, basically, of three labels. In the original application, this pane is constructed by the createStatus() method. In the original, it returns a Swing Component, which is then placed by calling add() on a container managed by the caller.

In SWT, Widgets must be joined to their containers at construction, so we must restructure this code a little bit. We create a Group to hold our classes together as a unit. We attach the group directly to the parent Shell by using the member variable frame. We set the layout manager to be RowLayout.

We then populate the Group. First, we add the Up button, which is only enabled when in a subaccount. While SWT does support image buttons, we take the shortcut of using the SWT.ARROW style, bitwise-or'ed with the SWT.UP style. Next, we populate the group with our Labels.

Note a change we will talk about some more below: The listener for the Button object called upton is changed. The method is renamed from addActionListener() to addSelectionListener(). Event handling in SWT is similar to Swing/AWT, but not identical, as we will see when we go over the rewrite of the actual event handler code a little later on.

These are the only changes we make to this method.

Caution

If a Composite has no layout manager, each Widget in the Composite must have its size and position explicitly set, or else their sizes will default to zero, and they will all be invisible! Tremendous details on SWT layout manager classes can be found in the article "Understanding Layouts in SWT" by Carolyn MacLeod and Shantha Ramachandran on the Eclipse Web site.[13]

[13] http://www.eclipse.org/articles/Understanding%20Layouts/Understanding%20Layouts.htm


17.3.3.2 Converting the GUI init() Method

The setStatus() method is called whenever the data in the core model changes. Its job is to update the UI to reflect those changes. More specifically, it updates the status pane at the top of the UI. There are corresponding methods for the list pane and the button pane.

Oddly, there are no changes in this particular method. The purpose of this method is unchanged. It updates the Labels with the new numbers and checks to see if the current Account is the top level Account. If it is, the Up button is disabled, otherwise it is enabled.

It turns out that all of the methods called on the UI classes in this method have the same names and purposes in Swing and SWT. Don't assume this will be true in the other cases.

17.3.3.3 Reworking Event Handlers

Finally, in the litany of conversion, we have to modify the event handlers. In this case, the only event of interest is when the Up button is pressed. Pressing a Button produces a Selection event.

In SWT, there are several types of events. Generally, you specify a class that will handle the event by calling one of the add...Listener() methods on the Widget that you wish to process the event for. Examples of these method calls include:

  • addSelectionListener()

  • addControlListener()

  • addFocusListener()

  • addHelpListener()

  • addKeyListener()

  • addMouseListener()

  • addMouseMoveListener()

  • addMouseTrackListener()

  • addPaintListener()

  • addTraverseListener()

There are others. SWT naming conventions define an interface for which each add...Listener() method is named. For example, there is a SelectionListener interface. Many such interfaces have multiple methods, each to handle a distinct kind of event; for example, the MouseListener interface defines separate methods to handle a button down event, a button release event, and a double-click event. As in Swing, it is common to implement event listeners as anonymous inner classes that implement the listener interface. However, since it is common to be interested only in some (or even only one) listener event, it is annoying to have to implement the full interface, since you have to provide method implementations for every event. For this reason, SWT also provides classes called adapters that implement "do-nothing" methods for every listener event. These also follow a naming convention. For example, the adapter for the MouseListener interface is a class named MouseAdapter; the SelectionListener interface has an adapter named SelectionAdapter, and so on.

For us, this means that we are going to create a reference to an anonymous inner class that implements the SelectionListener interface by extending the SelectionAdapter class. This is probably the weirdest common code construct in Java. Let's take a direct look at that method (Example 17.2).

If you can correctly answer the following question, then you can be reasonably assured that you do, in fact, understand what is going on here. Would the program compile and run correctly if the type of the upAction variable were changed to SelectionAdapter? The answer is in the footnote.[14]

[14] Yes, it would. The reason is that the addSelectionListener() method takes an argument of type SelectionListener. Both SelectionListener and SelectionAdapter are of that base type. Aren't Objects wonderful?

Example 17.2. The upton Button event listener class reference declaration
 private SelectionListener upAction = new SelectionAdapter() {   public void widgetSelected(SelectionEvent e)   {     // this is the action for UP arrow icon;     Account next;     next = current.getParent();     if (next != null) {       current = next;       setStatus();     }   } } ; 

17.3.4. Completing the Conversion of the BudgetPro Class

To keep this book to a reasonable size, we are always trying to avoid covering the same ground more than once. We won't walk you through the details of converting the createList() and createButtons() methods as we did with the createStatus() method, but we will talk about the details of converting to SWT of some of the classes used in those methods.

17.3.4.1 The Table, TableColumn, and TableItem Classes

Without a doubt, the biggest change the BudgetPro class requires in order to convert from Swing to SWT lies in the table pane of the UI. The Table class is the root of tables in SWT. The TableColumn class defines the names and headers of the columns. TableColumn constructors must have a Table as their first argument, followed, as usual, by a numeric style specification. The TableItem class defines a row in the Table. As with TableColumn, the TableItem constructor must have a Table as its first argument, followed by a numeric style.

If you think about it, this is an extension of the same design philosophy that requires that all constructors name their parent Composite. While Swing's abstract table model permits a nice separation between the data and the presentation, implementing a similar system in SWT would violate its strict container semantics.

You will need to follow the basic rewrite process outlined above and you will have to squish the Swing abstract table model into the simpler SWT table model. This will be your biggest challenge. Go to it. It is a great way to learn. Of course, you can also just download the complete SWT application from the book Web site.[15]

[15] http://www.javalinuxbook.com/

17.3.5. Completing the Conversion of the Application

Completing the conversion of the BudgetPro class does not complete the conversion of the entire application. The AcctDialog class must also be converted. Use the same techniques we described here to convert that class as well. (Or, again, just download the complete SWT application.)

17.3.6. Closing Thoughts

Our overall impression is that SWT is more easily comprehended in its entirety than Swing. It may be easier to learn SWT first, since Swing's core model is more complex but more powerful. But SWT and Swing weren't developed in that order and Swing is still much more widely used.[16]

[16] A gut feelnot based on any real statistics.

For many GUI applications, our feeling is that it may be faster to write in the SWT idiom. The problem lies in that SWT's model has limitations that Swing's does not. Notably, SWT GUI elements are in a single rigid tree structure. It is not possible to have a factory class that constructs a GUI element such as a dialog box which is passed up to the caller to be grafted into place on a GUI. Instead, the parent element must be passed in, so all GUI elements belong to the single tree from the moment they are created. Also, by introducing objects that cannot be garbage-collected, SWT brings into your application the possibility of a class of bugs that Java otherwise eliminates.

Moreover, while converting a Swing application helped give this chapter shape, we would, in general, prefer that an application be designed with its GUI toolkit in mind. You would likely make slightly different design decisions depending on which style of the GUI you choose.



    Java Application Development with Linux
    Java Application Development on Linux
    ISBN: 013143697X
    EAN: 2147483647
    Year: 2004
    Pages: 292

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net