wingS User Guide

Writing and understanding wingS applications

wingS Project Team

wingS 3.2, March 2008

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License" .


Table of Contents

Preface
1. Target Audience
2. Typographical Conventions
3. Credits
1. The Basics
1.1. The Swing in wingS
1.1.1. Widgets
1.1.2. MVC - Events and Listeners
1.2. Web-related Concepts
1.2.1. Servlet API
1.2.2. Separating View and Logic
1.2.2.1. Pluggable Look & Feel (PLAF)
1.2.2.2. Layout managers
1.2.2.3. Styling
2. The 3-Step Quick Start
2.1. Get ready - Writing a wingS Frame
2.2. Set - Preparing the web.xml File
2.3. Go! - Deployment in Servlet Container
3. The Developer's Perspective
3.1. Project setup
3.1.1. Required libraries
3.1.2. Useful tools
3.2. Designing the application architecture
3.3. The basic components
3.3.1. Frames and Top-Level Components
3.3.1.1. Frames
3.3.1.2. Internal Frames
3.3.1.3. Frameset
3.3.2. SComponent
3.3.3. SContainer
3.3.3.1. Hierarchical nesting of containers
3.3.3.2. The basic SPanel container
3.3.3.3. The SForm container for input elements
3.3.3.4. Overview of all available container components
3.3.4. Labels and other display components
3.3.4.1. Simple labels
3.3.4.2. Images
3.3.4.3. Further display widgets
3.3.5. Buttons and Events
3.3.5.1. Creating buttons
3.3.5.2. Listening to Events
3.3.6. Input components
3.3.6.1. Text input fields and areas
3.3.6.2. Check boxes and Radio boxes
3.3.6.3. Comboboxes and selection lists
3.3.6.4. Special Input fields
3.4. Sophisticated components
3.4.1. STable
3.4.1.1. Presenting data using a table model
3.4.1.2. Use the selection model
3.4.1.3. Allow editing of table data
3.4.1.4. Customizing STable
3.4.2. STree
3.4.2.1. TreeModel
3.4.3. SScrollPane
3.4.4. SMenu and SMenuBar
3.4.5. Component context menu
3.5. AJAX-based componets
3.5.1. Prerequisites
3.6. Further events
3.7. Layout Managers
3.7.1. Swing-like Layout Managers
3.7.1.1. SBorderLayout
3.7.1.2. SBoxLayout
3.7.1.3. SGridLayout
3.7.1.4. SFlowLayout and SFlowDownLayout
3.7.1.5. SGridBagLayout
3.7.1.6. SCardLayout
3.7.2. STemplateLayout
3.7.2.1. Setting component properties via the template
3.7.2.2. SRootLayout
3.7.2.3. BeanScript support
3.8. Styling your application using CSS
3.8.1. CSS selector scheme inside wingS
3.8.2. Default style sheets
3.8.3. Borders and the Default Border
3.8.4. Attaching custom style sheets
3.8.5. CSS live editing
3.9. Miscellaneous Topics
3.9.1. Client debugging
3.9.2. wingS Session
3.9.3. File Upload
3.9.4. File Download
3.9.5. Overwriteable config files
3.9.6. Error Page Display
3.10. Best Practices
4. Advanced Topics
4.1. Back Button Navigation
4.1.1. Default Behaviour
4.1.2. Allow Back Navigation
4.1.3. Back Navigation Detection
4.2. Client Side Script Listeners
4.3. Drag & Drop support
4.4. Session Recording and Playback
4.4.1. Overview
4.4.2. Prerequisites
4.4.3. Generation of the session playback java source file
4.4.4. Playback
4.5. Advanced Techniques
4.5.1. Sharing Resources Among Servlets
4.5.2. Internationalization and Character encoding
4.6. Spring Integration
5. Behind the scenes - wingS internals
5.1. Session Management
5.2. Resources
5.2.1. Resource Types
5.2.2. Mapping Resources
5.3. Delivering Events
5.4. Externalisation and Rendering
5.4.1. Writing your own renderers
5.4.2. Interaction between CSS and PLAFs
A. Resources
B. GNU Free Documentation License
B.1. PREAMBLE
B.2. APPLICABILITY AND DEFINITIONS
B.3. VERBATIM COPYING
B.4. COPYING IN QUANTITY
B.5. MODIFICATIONS
B.6. COMBINING DOCUMENTS
B.7. COLLECTIONS OF DOCUMENTS
B.8. AGGREGATION WITH INDEPENDENT WORKS
B.9. TRANSLATION
B.10. TERMINATION
B.11. FUTURE REVISIONS OF THIS LICENSE
B.12. ADDENDUM: How to use this License for your documents

Preface

1. Target Audience

This document is intended to serve curious novices to wingS, developers using wingS, and developers willing to look into its internals. The further you progress in this document, the deeper you are getting to the core of wingS.

Chapter 1 provides an introduction to the concepts of wingS. Chapter 2 takes you through a step-by-step tutorial on how to write a wingS application and make it fly. Once you decide to develop an application chapter 3 will lead you through the wingS toolset and mindset. Chapter 4 is an architectural orientation to aid in understanding how wingS works. It tries to fill in enough background information such that a newcomer will be able to quickly understand wingS source code and code examples, since there is plenty of demo code that you can look at.

For management-oriented information please refer to the wingS whitepaper[1].

2. Typographical Conventions

session.Session - a wingS class name (the org.wings part of the full name is elided, but subnames, as in this example are shown)

"TODO" labels call out to you. Yes, you! Participate in the effort to build a first-class documentation, give something back!

3. Credits

Contributions to this documentation were provided by Holger Engels, Volker Fritzsch, Ian Gardner, Oliver Scheck and Benjamin Schmid.

Chapter 1. The Basics

1.1. The Swing in wingS

The Java Foundation Classes (JFC) and its Swing API are known by nearly every Java developer. Its inherent MVC2 model is solid, yet flexible and fairly easy to learn. These reasons led to the idea to create an API with the Swing-feel, but generating and handling web applications. This idea is the underlying theme of wingS.

So where is the Swing in wingS?

1.1.1. Widgets

Suppose you want to display a table. Having Swing in mind, you'd implement an AbstractTableModel and associate it with a JTable instance which you placed in a JFrame. Now with wingS you do just the same, except you replace J's with S's. That's it! Apart from this prefix character of widget names the code is identical.

For almost every J<xxx> component of Swing you find a S<xxx> counterpart in wingS with similar or identical behavior.

1.1.2. MVC - Events and Listeners

Furthermore, suppose you want to react on mouse clicks (selection events) in your table. Again, doing it the Swing way, you'd provide a ListSelectionModel implementation and register a ListSelectionListener. The exact same code will work in wingS, no difference to Swing.

So wingS relies on the model, listener and event infrastructure of Swing, since there is no need to reinvent the wheel.

1.2. Web-related Concepts

With the Swing feel of wingS, you sometimes forget you develop for a web application context. But there are concepts intrinsic to the web that wingS neither hides, nor intends to hide. Understanding these simple concepts helps you in creating successful projects.

1.2.1. Servlet API

Despite all of wingS's magic, at the end of the day a wingS applications is nothing else but a servlet. Quite some servlet though.

This means you will need a servlet container, such as Tomcat, Jetty, or JRun to execute wingS applications.

During application development you typically don't notice the servlet environment your application will run in. In fact, it is only in the web.xml where you need to do anything servlet-related. Still it certainly helps to be aware of it's underlying servlet foundation, as you will see in succeeding chapters.

1.2.2. Separating View and Logic

When developing web applications it proves very useful —if not necessary— to keep business logic separate from graphical representation. wingS provides simple, but empowering concepts for this separation:

1.2.2.1. Pluggable Look & Feel (PLAF)

From the developers perspective wingS applications are built up using the provided set of Java widgets. Hence a layer is needed to transform the visual properties set on the widget components into the according HTML code. This is done by the so called Pluggable Look & Feels (PLAF). A PLAF is an exchangeable collection of widget renderers called CGs. Every CG is responsible for the rendering of one specific type of widget.

This concept, combined with the fact that wingS ships with a very powerful default PLAF implementation, is a great step into the separation of view and logic, as the typical developer does not need to know any web specific details at all, but can fully rely on using the intuitive set of Swing-like wingS widgets.

1.2.2.2. Layout managers

Layout managers are responsible for arranging the widgets inside a container widget to a visual composite. These container widgets can be nested inside each other, each using its own layout manager, resulting in a containment hierarchy which will be reflected in the display. There are two classes of layout managers available, a static template layout manager and various dynamic layout managers.

With the static STemplate layout manager you can create a XHTML template, which structurally and visually defines one component or the complete screen layout of your application. Then, put in <OBJECT> elements and give them a name, which will be referred to by your program code. These elements will be replaced with the real components at runtime. See Section 3.7.2, “STemplateLayout” for how this is done.

Besides this static approach you can also utilize different dynamic layout managers to arrange the widgets inside your application by pattern. Their usage is almost identical to that of the layout managers in Swing, and allows you to arrange your widgets in the usual patterns such as gridwise, floating or as a list. The browser-specific PLAF implementation then takes up the job to generate the optimized HTML code to achieve the desired layout. Please refer to Section 3.7.1, “Swing-like Layout Managers” for more details.

1.2.2.3. Styling

The styling of single widgets or the application as a whole heavily relies on Cascading Style Sheets (CSS). It uses CSS to assign specific visual properties, statically to a whole class of a widgets, or dynamically to specific widget instances during runtime by transparently generating new CSS styles on-the-fly.

But don't be afraid! In most cases you will not need to get in contact with these details at all, since wingS already ships with a decent default style which will provide a good start new eye-candy web applications while all the styling needed during runtime (i.e. colouring, specifying other fonts or assign spaces between widgets) can be achieved completely transparent using the Swing-like wingS API.

The chapters Section 3.8, “Styling your application using CSS” and Section 5.4.2, “Interaction between CSS and PLAFs” will give you a deeper view on this.

Chapter 2. The 3-Step Quick Start

In real-life applications the process of starting a new wingS application will begin as simply as designing the main application layout. You will probably prefer to use a template to arrange and embed the main application components inside your desired screen design. As an example think of an application with a dynamic menu bar on the left side and a center work area, placed inside your typical corporate identity design.

As the next step you will already be able to start designing and implementing the first functionality of your application. The default PLAF and CSS style sheets provided with the wingS distribution will already give you a decent start for a nice-looking and working web application, fulfilling most of the typical design needs.

After designing the main application layout you have already passed the point where you will no longer need to have any in-depth knowledge of web-related technologies or concepts, in order to implement and design the further application logic. You can solely use and rely on the Swing-like Java API to create, use and arrange the finer grained application components. To dynamically style components, for example to emphasise a label with red color to indicate an error, is just as simple as a label.setForeground(Color.RED); call in your application code.

Only if you need to style the look and feel of your application in detail will you extend or overwrite the default wingS CSS style sheets to adapt it to your individual styling needs. Section 3.8, “Styling your application using CSS” will give you a deeper view into leveraging CSS to style your application.

The following sections will demonstrate all the tasks needed to set up a simple "Hello World" example from scratch using the wingS framework.

Note

Please refer to the doc/tutorial/hellowings folder in the wingS distribution for the sources of this quickstart How-To.

2.1. Get ready - Writing a wingS Frame

First we need to set up your development environment. Just create a new directory helloworld and helloword/src and copy the contents of the two directories web and lib of the wingS distribution. Start your preferred IDE and create a new project using the directory you just generated. Include all the *.jarfiles in the copied lib and web/WEB-INF/libdirectories in the classpath of your newly created project.

Let's start writing our first wingS application. In general the starting point of a wingS application session is the constructor of a specific application root class. So all we need to do is to write a class which creates a wingS SFrame inside its constructor and adds some components to show before it calls SFrame.setVisible(true) to display the application.

Tip

Most IDEs support quick JavaDoc and parameter name lookup if you attach the src/java directoryof the wingS distribution as Java source and the dist/doc/api directory as JavaDoc source at the two main wingS libraries wings.jar.

We don't have any special layout desires, so we won't use a template layout file. For simplicity we'll just use one of the dynamic layout managers to arrange our components. So let's create a HelloWingS.java class in the src directory:

import org.wings.*;
import java.util.*;
import java.awt.event.*;

public class HelloWingS {
    public HelloWingS() {
        SGridLayout gridLayout = new SGridLayout(1);
        SForm panel = new SForm(gridLayout);
        SLabel titel = new SLabel("Hello World - this is wingS!");
        SButton okButton = new SButton("Guess!");
        titel.setFont(new SFont(null, SFont.BOLD, 18));
        gridLayout.setVgap(10);

        final SLabel message = new SLabel();
        final STextField textField = new STextField();
        final int randomNr = new Random().nextInt(10) + 1;

        // check our guesses and respond with according message
        okButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (Integer.toString(randomNr).equals(textField.getText()))
                    message.setText("Congratulations! You guessed my number!");
                else
                    message.setText("No - '" + textField.getText()+
                                    "' is not the right number. Try again!");
            }
        });

        // arrange components using a grid layout
        panel.add(titel);
        panel.add(new SLabel("We want fun, so let's play a game!\n" +
                             "Try to guess a number between 1 and 10."));
        panel.add(textField);
        panel.add(okButton);
        panel.add(message);

        SFrame rootFrame = new SFrame();
        rootFrame.getContentPane().add(panel);
        rootFrame.setVisible(true);
    }
}

2.2. Set - Preparing the web.xml File

In the next step we need to set up a web.xml file in the web/WEB-INF directory. This step is only needed once per application and it's only intention is to register the wingS servlet inside the servlet container and point wingS to our newly created application root class HelloWingS. A very minimalist web.xml file would look like this:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <servlet>
        <servlet-name>HelloWingS</servlet-name>
        <servlet-class>org.wings.session.WingServlet</servlet-class>
        <init-param>
            <param-name>wings.mainclass</param-name>
            <param-value>HelloWingS</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>HelloWingS</servlet-name>
        <url-pattern>/HelloWingS/*</url-pattern>
    </servlet-mapping>
</web-app>

Please note that if you want to use some advanced, Ajax-based components you have to add a few further declarations in this web.xml file as described in Section 3.5, “AJAX-based componets”

2.3. Go! - Deployment in Servlet Container

In the last step we only need to compile and package all our application files into a Servlet compatible Web Archive (WAR) file. You could do this either by manually compiling and zipping your application sources, but as this is a reccurring task it's better to let a tool do this task. Apache Ant[2] is a very popular Java based build tool. We'll create a very simple Ant build file which will instruct Apache Ant to package our files as needed into our application WAR file. So just create a new build.xml file in your project directory with the following contents:

<project name="HelloWingS" default="war">
    <path id="build.classpath">
        <fileset dir="lib" includes="*.jar"/>
        <fileset dir="web/WEB-INF/lib" includes="*.jar"/>
    </path>

    <target name="prepare">
        <mkdir dir="build/class"/>
    </target>

    <target name="compile" depends="prepare">
        <javac srcdir="src" destdir="build/class">
            <classpath refid="build.classpath"/>
        </javac>
    </target>

    <target name="war" depends="compile">
       <war destfile="build/hellowings.war"
            webxml="web/WEB-INF/web.xml">
         <classes dir="build/class"/>
         <fileset dir="web">
            <exclude name="**/web.xml" />
         </fileset>
       </war>
    </target>

    <target name="clean">
       <delete dir="build"/>
    </target>
</project>

After you've created the Ant build file we can execute it by calling Ant to parse and execute it. Use Ant to execute the war build target in your build file by executing Ant in your project home directory via

<path to your ant installation>\bin\ant war

Apache Ant will automatically look for a build.xml file in the current directory and try to execute the named target. If Ant fails to compile and complains that it does not know where to find the Java compiler you may need to specify the path to your JDK installation in the JAVA_HOME environment variable.

If everything went well you should find a file named hellowings.war in the build directory of your project. So we're almost done. The only thing we need to do is to deploy this WAR file in your servlet container. If you use Jakarta Tomcat[3]the only thing you need to do is to copy your hellowings.war file into the webapps directory, start Tomcat by calling bin/startup.bat and point your browser to http://localhost:8080/hellowings/HelloWingS



[2] You can download the latest version of Apache Ant at http://ant.apache.org/

[3] Jakarta Tomcat is available at http://tomcat.apache.org/

Chapter 3. The Developer's Perspective

This chapter presents what you need to know to develop with the wingS framework.

3.1. Project setup

Starting a new project with a new technology or framework often implies the need to learn a few things before you are able to fully leverage its benefits. Being confident that wingS has to offer really great advantages to you, when you are about to design and implement new web applications, this chapter tries to give you some hints to help you smoothly start developing applications with wingS.

3.1.1. Required libraries

Table 3.1. Overview of libraries shipped with wingS distribution

LibaryRequiredDescription
wings.jaryesThe wingS core classes
css.jaryesContains the default PLAF implementation for CSS capable browsers
commons-logging.jaryesApache Commons Logging API. Will delegate logging to Log4J or the standard java logging facility
bsh-core.jaryesBeanShell for scripting support in STemplateLayout
kdeclassic-lfgr.jaroptionalIcons used in default wingS widget (i.e. graphical checkboxes, icons for table cell editors)
dwr.jaroptionalDirect Web Remoting libraries for AJAX support. Refer to Section 4.2, “Client Side Script Listeners”
log4j-1.2.9.jaroptionalDeploy and configure Log4J with your application
commons-httpclient-x.x.jardevelopment onlyApache Commons HTTP client used for Section 4.4, “Session Recording and Playback”
servlet.jardevelopment onlyServlet API interface declaration. Only required for compiling as implementation is provided by the used servlet container
wingx.jaroptionalAdditional highlevel components.
cxx.jaroptionalPLAF for the additional highlevel components.

3.1.2. Useful tools

Here is a short list of tools you'll need or which might be helpful during development of wingS applications:

wingS

The latest version of the wingS framework can be always obtained from its website at http://wingsframework.org/. If you want to be in touch with the latest development you can also retrieve a development snapshot by anonymous CVS access.

apache ant

As a build tool to build and deploy your wingS application as demonstrated i.e. in Section 2.3, “Go! - Deployment in Servlet Container”. Some powerful IDEs like IntelliJ IDEA offer powerful alternatives for quick (re-)deployments during the development phase. But this requires some in-depth knowledge of the required configuration so at first for beginners the common used approach with ant as deployment tool might be the better choice.

Mozilla FireFox

Though choosing a browser is somehow a matter of personal taste and preferences, we heavily encourage to try out the Mozilla FireFox browser available via http://www.getfirefox.com/. Beneath being a great browser, there are also some great plugins freely available which can be a great help during developing web applications. Since this is favoured by the core wingS development team, new features tend to work first with FireFox !

Web Developer Extension and User Agent Switcher plugin

Chris Pederick donated these really two great plugins for the FireFox. They will be valuable tools when you are about to style your web application in detail. You can install them via http://chrispederick.com/work/.

Refer to Section 3.8.5, “CSS live editing” to learn how these plugins can help you.

Firebug Extension plugin for Firefox

This is a great javascript debugger, html- and css- editor, dom- inspector, ... available at: http://www.getfirebug.com/.

3.2. Designing the application architecture

Thinking about the intended architecture of your application before starting with the implementation of the code is always a good idea. Here are some points to think about:

  • First of all try to determine the basic layout and the logical groups in your web application. Section 3.3.3.1, “Hierarchical nesting of containers” shows an example of an application screen split into various logical areas. For floating layouts the STemplateLayout might be a good choice for the root panel (contentPane).

  • Introduce your own new components for reccurring composite visual blocks in your application by derivating from the standard components. Examples might be specially styled components or repeating groups of component. You will save time, and your look and feel will be consistent.

  • Try to keep the application styling at one place. Apart from using only a few, or possibly only one template file for global layout, and creating custom components, its also a good idea to avoid spreading styling instructions all over your code. Instead of manually setting the foreground color to red for error notification labels try either to introduce a new, global CSS style inside your custom CSS file and apply this to the according SLabel, or to create a special derived ErrorLabel subclass which gathers the styling specific code instructions.

  • For larger projects introduce your own subclasses for all major wingS components, or use a factory pattern. This allows you to modify or extend the default behaviour of specific UI components easily in one place during later phases of the project runtime. E.g. think of enhancing text based SButton with dynamical rendered icons.

  • Try to continue the MVC concept in wingS inside your application code. This means not to implement the business logic directly inside the UI components, but to delegate i.e. the click of a delete button to a delete(selectedObject) method call in a Controller class collecting all the business logic.

  • Leverage the event concept by learning how to efficiently use the listeners interfaces provided by the wingS components and introduce your own event based implementations inside your application logic.

Most architecture and design patterns which are a good choice for designing Swing-based applications are also a good choice for designing wingS based web applications. So if you are already familiar with designing Swing applications choosing the right application design for your web application will be an easy task with wingS.

3.3. The basic components

3.3.1. Frames and Top-Level Components

3.3.1.1. Frames

If you took a look at Section 2.1, “Get ready - Writing a wingS Frame” you probably realized that the main thing the entry class does, is simply constructing a new SFrame container component (see also Section 3.3.3, “SContainer”), adding further wingS components to it and finally calling setVisible(true) on this frame. So you probably already guessed that the root of all wingS applications is a frame, constructed in the root class of your application:

public class MyWingSApp {
        public MyWingSApp() {
            SFrame rootFrame = new SFrame("Window title");
            rootFrame.getContentPane().add(...);
            // ...
            rootFrame.setVisible(true);
        }
    }

Hence you can think of SFrame as an equivalent to a window in common desktop environments like Microsoft Windows or KDE.

Equally, at a more technical level, an SFrame corresponds directly with the dynamically created HTML document that is delivered to the browser as the current view of your web application. During the lifecycle of a typical wingS application session most of the time there will exist exactly one root frame. This root frame contains all the widgets currently visible and can be retrieved at any time via the session manager:

SFrame rootFrame = SessionManager.getSession().getRootFrame();

Though in most cases every session has only exactly one instance of SFrame, there are situations where multiple frames may exist, sequentially or concurrently, during a session. This is especially the case for frameset based applications[4] or wingS applications opening new browser windows for sub dialogs.

3.3.1.2. Internal Frames

Besides regular, single-framed single-windowed web applications wingS offers a great feature: It can simulate Multiple Document Interface (MDI) applications. MDI applications are applications hosting multiple documents or windows inside one parent application.

In wingS this can be achieved easily using SInternalFrame inside a SDesktopPane. The desktop demo shipped with wingS demonstrates in a very impressive way how internal frames can be used to build a multi-document Editor with RSS reader and Drag & Drop support.

Please refer to the source code of the desktop demo for an example of how to use internal frames. It can be found in the demo/desktop directory of the wingS distribution.

3.3.1.3. Frameset

Please note that the frameset support in wingS is currently experimental!

Using framesets is a way to separate the content displayed by the browser into two or more different and independent documents. Using framesets you can split the available space of the browser window in different sub-areas each called a Frame. In each Frame the browser displays an independent document. This corresponds exactly with the usage of the HTML frameset tag.

Note

Please don't mix the meaning of the term Frame as used in the context of browser framesets with its meaning in this chapter as instances of SFrame

The following code snippet shows how to construct a new frameset split horizontally into two inner frames:

SFrameSet frameset = new SFrameSet(new SFrameSetLayout("50%,50%", null));
SFrame leftFrame = new SFrame("left frame");
SFrame rightFrame = new SFrame("right frame");
horizontal.add(leftFrame);
horizontal.add(rightFrame);
frameset.setVisible(true);

This example created two new inner frames. Each of this frame will probably contain further displayed components. As an action in one frame may influence the presentation of a component in the other frame, this would mean that on every request the whole frameset will get reloaded, i.e. both inner frames will refresh.

Avoiding unnecessary reloads with the Reload Manager

The reload manager offers a way to avoid this. To use the reload manager you will need to add an additional, invisible frame to your frameset. In contrast to the default case, only this invisible frame will be used to fire the initial request on any user action. On the server side the reload manager determines which of the inner frames contain modified components, and returns a list of the inner frames to be updated to this invisible inner frame. Hence after receiving the response the hidden reload manager frame will trigger only explicit reloads of those frames whose visual representation has changed.

For a detailed example of how to use framesets and the reload manager please refer to the frameset demo available in the demo/frameset directory of the wingS distribution.

3.3.2. SComponent

In the previous section you already got in contact with your first wingS component: SFrame and its variants. Now we are about to introduce the root class of all wingS components SComponent. This class is the counterpart of the JComponent class in Swing. As in Swing all wingS widgets are subclasses and hence instances of SComponent.

The following sections we'll describe in more detail about the usage and different between the different SComponent variant. But first we'll explain the commons between all wingS components. Below you'll find a table which points out hand-selected methods with a short explanation of their meaning as declared in SComponent.

Table 3.2. Selected methods and properties of SComponent

MethodDescription
get/setName()The current name or id of this component. It is initially set on demand but can be overwritten with custom values. This name will also appear inside the generated HTML code, hence it must contain only a restricted set of characters. The name must be unique throughout the session! Otherwise event dispatching won't work consistently.
getParent()The parent container owning this component. This is set by the wingS framework at the time point when this component is added to another container like a panel or the root frame of the application.
getParentFrame()The root frame in which this component is placed as being recursively accessible via getParent().
get/setBackground()Defines an optional background colour for this component.
get/setForeground()Defines an optional foreground colour for this component. On SLabel instances i.e. this will become the colour of the text.
get/setBorder()Apply a border. Gives components a border and/ or applies insets. Besides the well known types SLineBorder, SEtchedBorder, etc. there is the SDefaultBorder, that components have attached by default. It makes the component appear, as defined in the stylesheet (see Section 3.8, “Styling your application using CSS”). If the border property is set to null, there will be no border and no insets and the rules in the stylesheet have no affect.
get/setHorizontalAlignment()Sets the horizontal alignment of this component inside the parent container (LEFT, RIGHTor CENTER). The effect of this value depends on the layout manager assigned the parent container.
get/setVerticalAlignment()As previous method the vertical alignment accepting TOP, CENTER and BOTTOM as valid values.
get/setPreferredSize()Apply a preferred size to a component. You may choose relative percentage values (new SDimension("50%", null)) as well as absolute pixel value as parameters (new SDimension(150, SDimension.AUTO_INT)). Another typical usage is to assign the global SDimension.FULL_WIDTH instance i.e. to a panel or similar component: This achieves that the component is stretched by the browser as far as possible to take up the full available width. Please be aware that the rendering result may vary depending on the browser. This is especially the case for relative vertical dimensions (FULLHEIGHT).
get/setStyle()The current CSS style class. By default the wingS PLAF assigns every component its unqualified Java class name as CSS style name. So for SButton instances this will have "SButton" as default value. Please be aware that the default wingS style definitions are defined using these default CSS class names. If you want to keep the wingS default styles but additionally apply your own styles defined in a separate CSS file, you should append your custom CSS style class names to a component separated by a space (i.e. "SButton myClass").
get/setToolTipText()Apply a tool tip textwhich will pop up after a short delay when the mouse pointer is over the component. The behaviour of tools tips can be defined more in detail via the SToolTipManager.
get/setShowAsFormComponent()The value of this property influences the technical representation of some components and the submit behaviour of most components. For example, an SList is either rendered as a select tag or as an unordered list (ul) of links. For most components, this decides only, if the whole form will be sumbitted on click or if the form content gets lost and a GET request will be sent. HTML Check boxes are only rendered in browsers if you nest them inside a HTML <FORM> element. Therefore wingS will render them as clickable images if it is not contained in an SForm. You can achieve the same behaviour inside forms on setting this value to false. A very common usage of this property is to force SButton to appear as text links and not browser submit buttons inside forms.
setAttribute()Dynamically set a CSS property value for a component. You can use this method to define any arbitrary CSS property/value pair on the current component. It will be rendered as an inline style.
is/setVisible()By default being true you can use this property to hide any componentfrom the current view.
is/setEnabled()This property has a different meaning on different components. Mainly it is used to defineinput elements as inactive respectively read only.

Besides the explained methods SComponent declares further methods for:

  • Adding and removing various event listeners

  • Applying key bindings and component context menus on a component

  • Modifying the used component renderer (PLAF)

  • Requesting the input focus for the next page refresh

3.3.3. SContainer

You probably guessed it already: SContainer is a derived variant of SComponent that can container further SComponents. Therefore it declares various methods to add other SComponent to it. Furthermore a container offers methods to access and modify the responsible layout manager. But let's start from the beginning.

3.3.3.1. Hierarchical nesting of containers

Contrary to other web applications framework, wingS is tries to be as component oriented as possible. One main concept of component-oriented application design is that you take simple component and arrange them to more complex compound components until you built up your complete application. Hence containers are the key feature for a hierarchical component-oriented design approach. The figure below shows how you can used and nest components to a complete screen dialog.

Example of how to build complex application via nesting components

Figure 3.1. Example of how to build complex application via nesting components

Similar to Swing the containers are only responsible for holding and managing the list of contained inner components. The layouting of the contained components inside the space taken up by the container component will be delegated to the layout manager. Refer to Section 3.7, “Layout Managers” for more information about details regarding the different layout managers.

After construction you can simply add the components which should be contained inside using one of the add(SComponent component) methods. Please note the variants of the add method allowing you to pass a Layout constraint as additional parameter. Some layout managers, such as the SBorderLayout or the STemplateLayout need additional information about where exactly you want to place the passed component inside their provided layout style, while simpler ones like the SGridLayout will just choose the next available place.

Finally you just need to attach your newly created container to the root container as shown in Section 2.1, “Get ready - Writing a wingS Frame” to make it visible to the user.

3.3.3.2. The basic SPanel container

The type of container you'll probably encounter most of the times during you every-day writing of wingS application is SPanel. This is the simplest form a container and like every SContainer it will be initialized with a default layout manager (SFlowLayout, can be overridden via default.properties) but you probably will prefer to define your own layout manager.

SPanel panel = new SPanel();
SGridLayout gridLayout = new SGridLayout(2);

panel.setLayout(gridLayout);
panel.add(new SLabel("First name:"));
panel.add(new STextField());
panel.add(new SLabel("Last name:"));
panel.add(new STextField());
panel.add(new SButton("Submit"));

SForm inputForm = new SForm();
inputForm.add(panel);

In this example we created a SPanel container, assigned a two-columed grid layout as layout manager and added several components to this container. In the last step we wrapped this container inside another container: SForm.

3.3.3.3. The SForm container for input elements

What's the reason behind the SForm instance in the previous example? If you are aware of HTML and already designed some web pages by coding HTML code directly, you'll probably know that input elements like text fields, submit buttons, radio buttons and so on require to be surrounded by a <FORM> HTML tag.

The SForm container is the object-oriented equivalent for this HTML tag. So if you want to place input elements on your screen you must ensure that the container or one of its parent container is a SForm. Please be aware that contrary to the other containers available in wingS you are not allowed to nest SForm containers into each others! This reflects the same HTML restriction.

Typically it's sufficient if you use one SForm container on the very top of your component hierarchy. The SForm container has also a property which advises the browser to send the input via HTTP POST or HTTP GET calls.

3.3.3.4. Overview of all available container components

There are some more containers available in wingS. Below you find a short summary of them including a short description of their characteristics.

Table 3.3. Overview of all public SContainer implementations

Container classDescription
SAnchorA very simple container to wrap a link around the inner components. Typically you use this to wrap SLabel for creating links on external URLs.
SDesktopPaneA special container to manager internal frames as demonstrated in the wingS desktop demo. Only needed for MDI applications.
SFormRefer to Section 3.3.3.3, “The SForm container for input elements”. Wrap contained components with a HTML <FORM> tag. May not contain other SForm containers.
SMenuBarA special container accepting SMenuItem object. Used to build a desktop-like menu bar. Refer to the desktop demo.
SPanelThe most common and basic container.
SRootContainerAbstract superclass for root containers. Root containers are the topmost containers which are not contained in other containers
SFrameRefer to Section 3.3.1.1, “Frames”. Typically the root container of every wingS session is a SFrame instance.
SInternalFrameRefer to Section 3.3.1.2, “Internal Frames”. A root container for MDI web applications.
SFrameSetRefer to Section 3.3.1.3, “Frameset”. A root container to create frameset based web applications. Will container further SFrame instances for each frameset segment.
SScrollPaneA special pane to create paged or graphical scroll panes.
STabbedPaneNeat container to create a tabbed stack of various components as demonstrated in the WingSet demo.

3.3.4. Labels and other display components

We already showed how to combine and arrange SComponents using containers and simple layout managers. So it is time to know how you can place content in them.

3.3.4.1. Simple labels

The simplest way to present information to the user is by using labels. You already encountered instances of SLabel in the preceding examples so we will explain in a bit more depth the usage and capabilities of this widget component. Here are some usage examples

// You can just pass the text to display as parameter of the constructor
SLabel simpleLabel = new SLabel("A simple label");

// ...but it can be changed at any time
SLabel emptyLabel = new SLabel();
// ... i.e. when something happened:
emptyLabel.setText("The trigger event occured! \n"+
                   "Second line of label");

// Extensive formatting can be done done during runtime
SLabel fancyLabel = new SLabel("A green, bold italic label");
fancyLabel.setForeground(Color.green);
fancyLabel.setFont(new SFont(SFont.BOLD + SFont.ITALIC));
fancyLabel.setWordWrap(true); // allow word wrapping

As you can see, the text inside a label can be changed at any time using the setText() bean property setter. Using the properties declared in SComponent the font size, style, face as well as the text and background color can be easily controlled using pure Java code.

Please note that starting with version 2.0 the labels word wrapping behaviour changed to be in sync with Swing. To allow the browser to line-break a label call setWordWrap(true) on it.

Advanced usages

As in the Swing JLabel, the SLabel component in wingS also accepts HTML formatting directives like <b>...</b> or <i>...</i>inside the text if you prefix the text with an <HTML> tag. You should use this carefully to avoid mixing of view and logic.

If you assign specific styles on a regular basis to a component like a label, you should consider to declare a static new CSS style class and assign this to the label. This allows you on the one hand to modify this style globally in the style declaration, and avoids on the other hand the generation of many dynamically generated CSS styles generated by the wingS framework. Think for example of error labels you might want to have in a specific style. In your programming code you would additional assign a new CSS class to a label:

SLabel errorLabel = new SLabel();
errorLabel.setStyle(errorLabel.getStyle() + " errorLabel");
errorLabel.setVisible(false); // hide by default

//.. an error occured
errorLabel.setText("An error occured during the last operation");
errorLabel.setVisible(true);

This will allow the web designer in your project to declare a new static CSS style an optional application-specific CSS style sheet.

.errorLabel {
    color: #f00;
    font-weight: bold;
    border: 1px solid #ddd;
}

Please refer to Section 3.8, “Styling your application using CSS” for more information about using CSS to style your application in detail.

3.3.4.2. Images

The SIcon interface is your friend if you want to include images in your wingS application. An SIcon can be an image retrieved from an arbitrary source and used in conjunction with various wingS widgets. By default wingS comes with five different implementations of icon providers, each capable of handling a different source as source for the image data to be displayed. Possible sources are

  • static image files deployed with your application (SFileIcon)

  • static image files reachable via an absolute or relative URL in your application (SURLIcon)

  • static image files contained in your classpath (SResourceIcon)

  • static image data contained in a byte array (SByteArrayIcon)

  • and dynamical generated Swing ImageIcon images (SImageIcon)

Please note an important difference between the SImageIcon and the other mentioned SIcon implementations: While the content of the others images cannot change during runtime, an ImageIcon can have an updated image content during every request. Allowing you to display any dynamically generated image this has the side-effect that wingS needs to create and send new graphic data to the browser. So to include just image files in your application ensure that you use one of the other implementations as demonstrated in the example below:

// image contained in the class path
SResourceIcon javaCupIcon = new SResourceIcon("org/wings/icons/JavaCup.gif");
// image deployed in a icons directory of you application
SURLIcon okButtonIcon = new SURLIcon("../icons/ok-button.png");
// a file in the file system
SFileIcon background = new SFileIcon(new File("/home/blueshift/background.jpg"));

// Use i.e. just for display
SLabel javaCup = new SLabel(javaCupIcon);
// ..or as clickable button
SButton okButton = new SButton(okButtonIcon);
// or i.e. as container background
panel.setBackgroundImage(background);

SIcon images can be used in conjunction with many wingS widgets. But the most commons usage are in combination with SLabel for just displaying a graphic or with SButton to create graphical buttons in your application.

3.3.4.3. Further display widgets

Below you'll just find a list of further pure display-only widgets.

Table 3.4. Further non-input wingS components

ComponentDescription
SSeparatorSimple implementation of the HTML <HR> horizontal ruler element. Alternatively you can set single border lines on a component like setAttribute(CSSProperty.BORDER_BOTTOM, "1px solid #ccc");
SSpacerAn invisible component that can be used as spacer component. Typically renders as invisible pixel stretched to the desired size.
SProgressBarA progress bar indicator as demonstrated in the WingSet demo.

3.3.5. Buttons and Events

Buttons are simple, clickable input components. Instances of a button can present themselves as regular GUI buttons (HTML submit buttons), clickable text links or clickable images. To react to activations of a button, action listeners can be registered on buttons and will receive events notifying them about the click.

3.3.5.1. Creating buttons

To create and display a button just create a new SButton instance and add it to a visible container. By default buttons will appear as regular buttons if they are nested inside a SForm container, otherwise they will appear as simple text links due to constraints of the browser. Please refer to Section 3.3.3.3, “The SForm container for input elements” to learn more about the role of the SForm container. You can force SButtonto render as text links even if contained inside a form using a setShowAsFormComponent(false) method call. Alternatively you can use images as clickable buttons (perhaps dynamically modified at runtime). Below you will find a few example of how to create buttons

// a simple default button
SButton simpleButton = new SButton("ok");

// text link button
SButton textLinkButton = new SButton("Request support");
textLinkButton.setShowAsFormComponent(false);

// a graphical button
SButton graphicalButton = new SButton(
        new SResourceIcon("org/wings/icons/JavaCup.gif"));

The default style of form buttons are modified in the default CSS files delivered with the wingS default PLAF. You might want to attach a new custom CSS file and modify their appearance there.

3.3.5.2. Listening to Events

Most widgets provide various "listener" interfaces so that you can supply your own callbacks in order to handle these. Here we create a push button and register a callback to receive notification when the button is pushed. Note that the button is declared final so that we can access it within the anonymous callback class

final SButton submit = new SButton("Search");
form.add(submit);
ActionListener al = new ActionListener() {
  public void actionPerformed(ActionEvent e) {
     // ... trigger search
  }
};

submit.addActionListener(al);

In the above example the callback only receives events when the button is pushed. An alternative is to make the form the listener, since if the form is to have a "default" button (when the user hits the enter key) then it is the form which will be notified. In this case you would add the listener callback to the form instead of the button:

form.addActionListener(al);

The ActionEvent.getSourctree() will determine whether the event comes from the button or the form so you can use the following tests in your callback:

public void actionPerformed(ActionEvent e) {
  if(submit == e.getSource())
  // submit button pushed
  if(form == e.getSource())
  // user hit enter key
}

You can extract data from the text field as shown above with a call to STextField.getText().

Whenever a listener is attached to a component or its model, wingS assures, that the listener is invoked immediately after the user modifies the component. For example, if you add a DocumentListener to a textfield's document, wingS will add a form submission javascript listener (onchange) to the respective input field.

3.3.6. Input components

Input to your wingS application comes in the form of state changes to the various widgets you have created, and in application events that you can listen to. To illustrate this we use the simplest case of a form which contains an entry field and a button.

Recall that user input for entry fields must be contained in a SForm . The first task is to create the form and entry field (layout details omitted):

SForm form = new SForm(new STemplateLayout(...));
// ... add to top pane, add layout
// The following assumes a template layout, the names "W1", "W2"
// identify layout components
SLabel label = new SLabel("Enter stuff:");
STextField text = new STextField("initial value");
form.add(label, "W1");
form.add(text, "W2");

The text field variable might be made a class member variable so that you can access it later to retrieve its contents:

String dataValue = text.getText();

Alternatively you might declare it as a local final variable, and access it from within a local closure callback, reacting to user input as shown in the next section.

3.3.6.1. Text input fields and areas

The example below demonstrates that you can restrict the number of columns independently from the physical size a text input field. Furthermore text areas can be advised to line-wrap the entered text at their physical, virtual size or not to wrap the text themselves at all (default).

// A text field restricted to 40 character and
// a fixed width of 400px
STextField simpleField = new STextField("default text");
simpleField.setMaxColumns(40);
simpleField.setPreferredSize(new SDimension(400, SDimension.AUTO_INT));

// An multilines input field with 6 lines and 40 colmns
// Line will wrap after 40 colums
STextArea textArea = new STextArea(6,40);
textArea.setLineWrap(STextArea.VIRTUAL_WRAP);

// A read only textfield
STextField readonlyField = new STextField("read only");
readonlyField.setEditable(false);

You can retrieve the entered text via the getText() method i.e. as an reaction of an "O.K." or "Save" button click as already drafted in the previous examples.

It is worth mentioning a less obvious source of event triggers - the text fields/areas themselves ! You can attach Swing-like document change listeners which will notify every value update of a text component.

textArea.addDocumentListener(new SDocumentListener() {
    public void insertUpdate(SDocumentEvent e) {
        STextComponent source = (STextComponent) e.getSource();
        System.out.println("Text after insertion of " +
                e.getLength()+" characters: "+source.getText());
    }

    public void removeUpdate(SDocumentEvent e) {
        STextComponent source = (STextComponent) e.getSource();
        System.out.println("Text after removal of " +
                e.getLength()+" characters: "+source.getText());
    }

    public void changedUpdate(SDocumentEvent e) {
        // won't happen - refers to style changes, not text changes
    }
});                                                                 

3.3.6.2. Check boxes and Radio boxes

Check boxes and radio boxes are widgets that can be either selected or unselected. While check boxes can be checked and unchecked independently, radio boxes always allow only one instance to be selected within a button group.

Similar to the behaviour of SButton, their appearance differs depending on if they are contained inside a form or not. If those input elements are not contained inside a Section 3.3.3.3, “The SForm container for input elements” element, they will be rendered as graphical widgets. You can force the graphical version, as well as customising the used images for this case.

// You gotta' decide yourself for exact one gender ;-)
SButtonGroup buttonGroup = new SButtonGroup();
SRadioButton optionF = new SRadioButton("Female");
SRadioButton optionM = new SRadioButton("Male");
buttonGroup.add(optionF);
buttonGroup.add(optionM);

// a checked checkbox
SCheckBox agreedBox = new SCheckBox("accept disclaimer");
agreedBox.setSelected(true);

// an unmodifyable checkbox rendered as graphic
SCheckBox disabledCheckBox = new SCheckBox("Umodifyable checkbox");
disabledCheckBox.setEnabled(false);
disabledCheckBox.setShowAsFormComponent(false);
disabledCheckBox.setDisabledIcon(new SResourceIcon(...));

3.3.6.3. Comboboxes and selection lists

Comboboxes are one-lined item selection widgets with further options to choose via a drop-down menu. SLists have two and more lines and support multiple item selection modes[5] additionally to the single selection mode offered also by the combobox.

// A combo box containing a coloured label and two string values
SLabel color = new SLabel(Color.green.toString());
color.setForeground(Color.green);

SComboBox comboBox = new SComboBox();
comboBox.setModel(new DefaultComboBoxModel(new Object[]{ "element1", color, "element3" }));

// A simple selection list
SList multiSelectionList = new SList();
multiSelectionList.setName("multiple");
multiSelectionList.setSelectionMode(SList.MULTIPLE_SELECTION);
multiSelectionList.setListData(new String[] {"Item 1", "Item 2", "Item 3"});

You can attach ListSelectionListener to SList instances and ItemListener to SComboBox instances to receive events on selection changes.

comboBox.addScriptListener(ComboBoxCG.JS_ON_CHANGE_SUBMIT);

3.3.6.4. Special Input fields

Last but not least we have to mention two more special input component SFormattedTextField and SPasswordField. The latter one is simply the password entry variant of a STextField meaning that typed characters will appear obfuscated i.e. as asterix instead of being displayed plain-text.

The SFormattedTextField is a conceptual very interesting example of an AJAX based components. To work correctly you must deploy and register the DWR jar and servlet correctly as shown in WingSet example. Below you'll find a usage example

SFormattedTextField numberFormattedField =
           new SFormattedTextField(new NumberFormatter());

private static class NumberFormatter extends SAbstractFormatter {
    NumberFormat format = NumberFormat.getNumberInstance(
            SessionManager.getSession().getLocale());

    public Object stringToValue(String text) throws ParseException {
        return text == null || text.trim().length() == 0
                ? null : format.parse(text.trim());
    }

    public String valueToString(Object value) throws ParseException {
        return value == null ? "" : format.format(value);
    }
}

This component does the following: If you enter the SFormattedTextField you can enter or modify the current value of the text field. If you entered a parseable value, the value of the text field will be updated to the parsed value. Otherwise the input field will regain the input focus and be marked red to indicate an input error.

Technical background

This is realized in the following way: When you try to leave the text field, e.g. by pressing the Tab key to jump to the next input field, the text field will trigger a XML encoded request to the server in the background. There the DWR servlet takes the request and delegates it to the Java listener (here: NumberFormatter). If it succeeds the value of the text field will be updated with the formatted value returned by this. If it fails and indicates this by throwing a ParseException the cursor will be set back to the input field and it will be marked red to grab the user's attention.

3.4. Sophisticated components

3.4.1. STable

With the STable widgets you can

  • present data in tabular format

  • allow the user to make row selections

  • allow the user to edit the data directly inside the table

According to the paradigms of the MVC pattern this component explicitly separates the contained data from the controlling logic. STable contains two model objects holding the data: The Swing compatible TableModel is the provider of the presented table data while a SListSelectionModel object holds the current selection status.

But let's start step by step

3.4.1.1. Presenting data using a table model

Before you can present a table of data you first have to create a data provider for the table data. The used TableModel data model is actually the one declared by Swing, so the Swing tutorial might give you additional hints about implementing and using a table model.

In the example below we just create a very simple table model showing just a list of names hard coded in a static array. In typical real-life table models you would probably implement table models which operate on a list of objects and implement the getValueAt() in that way that it retrieves the according object from this list and returns an according object property value depending on the column.

// a very simple table model implementation
private static class MyTableModel extends AbstractTableModel {
    private final String[] COLUMN_NAMES = new String[]
            {"First name", "Last name", "Male"};
    private final Object[][] DATA = new Object[][] {
            new Object[] {"Michael","Bolton",Boolean.TRUE},
            new Object[] {"Samantha","Fox",Boolean.FALSE},
            new Object[] {"Eric","Clapton",Boolean.TRUE},
            new Object[] {"David","Bowie",null}
    };
    public int getRowCount() {
        return DATA.length;
    }

    public int getColumnCount() {
        return 3;
    }

    public String getColumnName(int i) {
        return COLUMN_NAMES[i];
    }

    public Object getValueAt(int row, int column) {
        return DATA[row][column];
    }
}

// .... in the main code

  // create new table instance and add to visible container
  final STable table = new STable(new MyTableModel());
  form.add(table);

Below you'll find a screenshot of the resulting visual representation

3.4.1.2. Use the selection model

Having shown a list of data to the object we might want to allow the user to do some operations on the data show. In the first step we'll allow the user to select single rows and offer him operations on them like e.g. deletion. Modifying the table to accept row selections is just as simple as

// allow single-line selection in table
table.setSelectionMode(STable.SINGLE_SELECTION);

After this statement you can select rows just by clicking the numbers in the first column. Now we also add a delete button and a status label telling us about the this event

// create a status label and a delete button
final SLabel statusLabel = new SLabel();
final SButton deleteButton = new SButton("Delete selected row");
deleteButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent actionEvent) {
        statusLabel.setText("Currently selected row: "+table.getSelectedRow());
        // business logic for operation
    }
});
// add them
form.add(statusLabel);
form.add(deleteButton);

So each time we click on the new presented delete button a status label will be updated telling us the index of the currently selected row. If no row has been selected then we will receive -1 as value. As well as single row selections, tables can also allow selection of continuous intervals (STable.SINGLE_INTERVAL_SELECTION) or discontinuous intervals (STable.MULTIPLE_INTERVAL_SELECTION). According to Swing the SListSelectionModel allows you to retrieve the necessary information about the selected rows via getMin/MaxSelectionIndex() methods for the continuous case, and an isSelectedIndex(int rownum) for the discontinuous case.

The comfortable way: Get notified about selections

Besides explicitly asking the table's selection model about the current selected rows, we can also use a ListSelectionListener to get notified via explicits events when the selection changes. The code below will update the status label every time the selection changes.

table.addSelectionListener(new ListSelectionListener() {
     public void valueChanged(ListSelectionEvent listSelectionEvent) {
           statusLabel.setText("Selection changed to: "+table.getSelectedRow());
     }
});

3.4.1.3. Allow editing of table data

But now we want allow the user to be able to edit the table content. This is quite easy in our simple demo. We have to modify the implementation of the table model so that it tells the table which cells are editable. In our case we just allow all cells to be editable. Furthermore we must overwrite the empty default implementation of the setValueAt() method in AbstractTableModel to accept and store the changed data. Last but not least the getColumnClass() returns the class type of the column values so that the framework knows to choose the right cell editor for the value type. Adding the few lines below to MyTableModel do this job

public boolean isCellEditable(int row, int column) {
    return true; // allow for all cells
}

public void setValueAt(Object object, int row, int column) {
    DATA[row][column] = object; // just take the canged value
}

public Class getColumnClass(int column) {
    return column == 2 ? Boolean.class : String.class;
}

And below you see the final outcome of our efforts

3.4.1.4. Customizing STable

Until now the creation of our table was really painless. As we did not override them, the table uses the default cell renderer and editors for rendering the cell contents and providing the appropriate components for editing a cell value. In our simple case this is absolutely fine as we have very simple data types.

3.4.1.4.1. Custom table cell renderer

But look at the last column. Wouldn't it be better to display this boolean value as checkable check box instead of the toString() representation of the boolean values? This can be easily achieved by overwriting the default cell renderer to return a accordingly checked SCheckBox instead of the default creating cell renderer implementation which just renders a SLabel with the cell content toString() value.

private static class MyCellRenderer extends SDefaultTableCellRenderer {
    private final SCheckBox checkBox = new SCheckBox();

    public MyCellRenderer() {
        // set icon to indicate editable, empty cells
        setEditIcon(getSession().getCGManager().getIcon("TableCG.editIcon"));
        // render always as graphic icon
        checkBox.setShowAsFormComponent(false);
    }

    public SComponent getTableCellRendererComponent(
            STable table, Object value, boolean selected, int row, int col) {
        if (value instanceof Boolean && row != -1) {
            checkBox.setSelected(((Boolean)value).booleanValue());
            return checkBox;
        }
        else
            return super.getTableCellRendererComponent(table, value, selected, row, col);
    }
}

After implementing our own, extended table cell renderer we just have to assign it to our table and we're done.

table.setDefaultRenderer(new MyCellRenderer());

See the result

Beware that the checkbox is readonly. Clicking it will not update the table model. Making cells editable is the job of a cell editor. Look at the wingSet Table example and XTable example to learn more about editable tables.

3.4.1.4.2. Customizing the table

Further customization can be applied to tables. Many tables contain a large count of data rows which result in large table size. In this case it is a good idea to make the table scrollable. Please refer to Section 3.4.3, “SScrollPane” for details how this can be done.

As already demonstrated the cell renderers can be exchanged as a whole or for specific column classes. If necessary you can exchange the cell editors in the same way as demonstrated for the cell renderers.

STable also allows to exchange the renderer component for the header cells as well to turn the display of the header cell.

The visibility of the horizontal and vertical separator lines can be easily modified using the setShowHorizontalLines() and setShowVerticalLines() methods of STable.

The spacing and padding between the table cells can be modified with the setIntercellPadding() and setIntercellSpacing() methods.

Colouring

Extensive adjusting of the table colours is possible. The recommended way is to add a custom static CSS style overwriting the wingS default styles for tables. Look at Section 3.8, “Styling your application using CSS” for details. As alternative there exists also the convenient possibility to all the styling via the Java API and the CSS Pseudo selector objects provided by STable. Just look at the example below

// Really fancy and colourful table
table.setAttribute(STable.SELECTOR_HEADER, CSSProperty.BACKGROUND, Color.blue);
table.setAttribute(STable.SELECTOR_NUMBERING_COLUMN, CSSProperty.BACKGROUND, Color.gray);
table.setAttribute(STable.SELECTOR_ODD_ROWS, CSSProperty.BACKGROUND, Color.orange);
table.setAttribute(STable.SELECTOR_EVEN_ROWS, CSSProperty.BACKGROUND, Color.magenta);
// Special marking as !important necessary due to existing !imporant declaration
// in wingS default styles. Might be obsolete already
table.setAttribute(STable.SELECTOR_SELECTION, CSSProperty.BACKGROUND,
        CSSStyleSheet.getAttribute(Color.yellow)+ " !important");

... and here is the result

3.4.2. STree

The STree component is a visual explorer-like component with expandable nodes. In many aspects it is very similar to tables with STable, so this section will give only a very quick and short presentation of the STree component. Beginners might first read Section 3.4.1, “STable” to read a more verbose introduction of an advanced wingS components. Furthermore the Swing documentation and tutorials of JTree might also be a good supplementary starting point for newbies.

3.4.2.1. TreeModel

Similarly to the STable component, the STree component stores its data in a separate model. Therefore it uses the Swing TreeModel for retrieving the data presented in the tree and a STreeSelectionModel object to store the currently selected items.

TreeModel is an interface of Swing declaring methods to retrieve a root object of a logical tree of objects, as well as methods to retrieve the child objects of these node objects. Typical node objects are instances of the TreeNode interface. In most cases you'll just deal with the default implementations of these interfaces provided by Swing: DefaultTreeModel and DefaultMutableTreeNode.

So let's look at a very simple example tree model constructed using these default implementations:

// Create a root node
DefaultMutableTreeNode root = new DefaultMutableTreeNode("STree");
DefaultMutableTreeNode parent;

// Add first child node with more sub nodes
parent = new DefaultMutableTreeNode("colors");
root.add(parent);
parent.add(new DefaultMutableTreeNode("blue"));
parent.add(new DefaultMutableTreeNode("violet"));
parent.add(new DefaultMutableTreeNode("red"));
parent.add(new DefaultMutableTreeNode("yellow"));

// ... second child node ...
parent = new DefaultMutableTreeNode("sports");
root.add(parent);
parent.add(new DefaultMutableTreeNode("basketball"));
parent.add(new DefaultMutableTreeNode("soccer"));
parent.add(new DefaultMutableTreeNode("football"));
parent.add(new DefaultMutableTreeNode("hockey"));

// ... and third and last root child node
parent = new DefaultMutableTreeNode("food");
root.add(parent);
parent.add(new DefaultMutableTreeNode("hot dogs"));
parent.add(new DefaultMutableTreeNode("pizza"));
parent.add(new DefaultMutableTreeNode("ravioli"));
parent.add(new DefaultMutableTreeNode("bananas"));

Note that in this example we used simple String object as data model objects for each node. But you can use any custom object type as data model objects but you should ensure that the used STreeCellRenderer is capable of rendering them nicely.

With creation of our tree model much is already done. Just wrap this tree model in a new instance of a STree component, and you receive an expandable visual tree with selectable nodes.

STree tree = new STree(treeModel);
form.add(tree);
3.4.2.1.1. React on interaction with the tree

Equivalent to the table case, the STree component also allows you to register listeners to get notified about selecting items (TreeSelectionListener), nodes that are requested to expand (TreeWillExpandListener) and rows that are expanding (TreeExpansionListener). The expansion notification event could for example be used to lazily load the children from the backend system. The code example belows demonstrates a very simple selection listener which will update a label every time the selection changed

final SLabel treeEvent = new SLabel();
form.add(treeEvent);
tree.addTreeSelectionListener(new TreeSelectionListener() {
    public void valueChanged(TreeSelectionEvent treeSelectionEvent) {
        TreePath path = treeSelectionEvent.getPath();
        String action = treeSelectionEvent.isAddedPath() ? "Added " : "Removed ";
        treeEvent.setText(action + path.getLastPathComponent().toString());
    }
});

By the way: You can also change the selection and expansion state of the tree programatically by calling the addSelectionPath(), removeSelectionPath(), expandRow() and collapseRow() methods. These methods expect TreePath objects which describe the full node path to the node to select.

3.4.2.1.2. Customization of trees

A STreeCellRenderer is used to render the values of the tree nodes into the displayed representation. If you decide to choose other data model object this is the place where customization is needed.

Furthermore trees implement the Scrollable interface and therefore can be wrapped into SScrollPane as described in Section 3.4.3, “SScrollPane”.

The indentation width of the tree nodes, the graphics used to indicate the node state (open, closed, etc.) and the selection mode are configurable via the API, too. Please refer to the JavaDoc and the WingSet demo for more details.

3.4.3. SScrollPane

The SScrollPane is special container which allows you to display viewports of Scrollable components. Namely Scrollable is implemented by

  • STable

  • STree

  • and SList

A SScrollPane accepts instances of these Scrollable and provides default horizontal and vertical adjustables. By default the scroll pane will use a viewport of 10x10 units. In the case of a table this would mean that you will see maximum 10 rows and 10 columns of the table at once. If the table has more rows or columns the according adjustables will provide the possibility to move the viewport inside the overall range of the table's columns and rows.

In the following example we'll take the demo table as described in Section 3.4.1, “STable”. So - in the simplest case you can make a Scrollable scrollable if you wrap it inside a SScrollPane instead of directly adding it to a container. The modified code from the table example looks like this

// create new table instance and add to visible container
final STable table = new STable(new MyTableModel());
//  form.add(table);
form.add(new SScrollPane(table)); // wrap in a scrollpane

This results in a presentation of the table as shown in the following screenshot

That's all!

Extensive customization can be done on the SScrollPane and its corresponding SAdjustable. Below we present a short summary of the relevant properties and their function

Table 3.5. Overview of customizable properties for scrolling feature

PropertyDescription
SScrollPane properties
verticalScrollBar / horizontalScrollBarThe instance horizontal / vertical scrollbar. By default semi-graphical SScrollBar but can be also the available SPageScroller or null.
horizontalExtent / verticalExtentThe horizontal / vertical extent describes the size of the view port in number of 'units' (table cells, list elements) which can be seen at once.
horizontalScrollBarPolicy / verticalScrollBarPolicyAllows to modify the visibility of the scrollbars. Can be defined to be always visible, only if necessary or never.
modeSets the scroll mode. See below for supported options.
SAdjustable properties
layoutModeDefines if this adjustable is layouted as a horizontal or vertical adjustable
directPageClickablesProperty for page scroller
unitIncrementReturns the amount to change the scrollbar's value by, given a unit up/down request
blockIncrementReturns the amount to change the scrollbar's value by, given a block (usually "page") up/down request

The scrollpane distinguishes three different scroll modes:

Table 3.6. Scroll modes

ModeBehaviour
COMPLETEThe whole scrollable viewport of the scrollable is rendered. Give it a preferred size! It will be rendered as a scrollable div (overflow: auto), that requires a defined size.
SCROLLINGOnly the portion defined by the extents will be rendered. The scrollbar's extent spans exactly the scrollable's scrollable viewport. Use it in combination with the standard SScrollBar.
PAGINGOnly the portion defined by the extents will be rendered. The scrollbar's extent is the first multiple of the respective scrollpane extent, that covers the scrollable viewport. Use it in combination with the SPageScroller.

Using these properties we know how to modify our scroller in the previous example to achieve a paged view as i.e. known from popular search engines or other websites.

SScrollPane scrollPane = new SScrollPane(table);
SPageScroller pageScroller = new SPageScroller(SPageScroller.VERTICAL);
pageScroller.setLayoutMode(SPageScroller.HORIZONTAL);
scrollPane.setHorizontalScrollBar(pageScroller);
scrollPane.setVerticalScrollBar(null);
scrollPane.setMode(SScrollPane.PAGING);
pageScroller.setExtent(5);
form.add(scrollPane);

3.4.4. SMenu and SMenuBar

TODO

Please refer to the WingSet demo an the following section for hints about using desktop like menus.

3.4.5. Component context menu

A component context can be attached to any component. It will appear on right-clicking a component inside a browser. To react on the presented menu items a event listener can be attached to each menu item. See example code below

final SLabel testLabel = new SLabel("This label has a context menu.");
ActionListener menuItemListener = new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        String selecteMenu = ((SMenuItem) e.getSource()).getText();
        testLabel.setText("This label has a context menu. You selected " + selecteMenu);
    }
};
SPopupMenu menu = new SPopupMenu();
SMenuItem cutMenu = new SMenuItem("Cut");
cutMenu.addActionListener(menuItemListener);
menu.add(cutMenu);
SMenuItem copyMenu = new SMenuItem("Copy");
copyMenu.addActionListener(menuItemListener);
menu.add(copyMenu);
SMenuItem pasteMenu = new SMenuItem("Paste");
pasteMenu.addActionListener(menuItemListener);
menu.add(pasteMenu);

SMenu subMenu = new SMenu("Help");
SMenuItem about = new SMenuItem("About");
about.addActionListener(menuItemListener);
subMenu.add(about);
SMenuItem topics = new SMenuItem("Topics");
topics.addActionListener(menuItemListener);
subMenu.add(topics);
menu.add(subMenu);

testLabel.setComponentPopupMenu(menu);

form.add(testLabel);

3.5. AJAX-based componets

3.5.1. Prerequisites

Besides the AJAX based incremental update feature contained in wingS, some components use also AJAX mechanism to provide a better and smoother user expierience. Those components use the DWR library as communication mechanism. Currently these are namely:

  • SFormattedTextField

  • XCalendar

  • XColorPicker

  • XInplaceEditor

  • XSuggest

Please ensure that your web.xml contain the following declaration of the DWR servlet, so that they work properly:

<servlet>
   <servlet-name>dwr-invoker</servlet-name>
   <display-name>DWR Servlet</display-name>
   <description>Direct Web Remoter Servlet</description>
   <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
   <init-param>
     <param-name>debug</param-name>
     <param-value>true</param-value>
     <description>Do we startup in debug/test mode?</description>
   </init-param>
   <init-param>
     <param-name>logLevel</param-name>
     <param-value>DEBUG</param-value>
     <description>How verbose do we want logging to be?</description>
   </init-param>
   <init-param>
     <param-name>org.directwebremoting.extend.CreatorManager</param-name>
     <param-value>org.wings.plaf.css.dwr.SessionCreatorManager</param-value>
     <description>Install a session local CreatorManager</description>
   </init-param>
   <init-param>
     <param-name>org.directwebremoting.extend.AccessControl</param-name>
     <param-value>org.wings.plaf.css.dwr.SessionAccessControl</param-value>
     <description>Install a session local AccessControl</description>
   </init-param>
   <init-param>
     <param-name>org.directwebremoting.extend.Remoter</param-name>
     <param-value>org.wings.plaf.css.dwr.SessionRemoter</param-value>
     <description>Install a session local Remoter</description>
   </init-param>
   <load-on-startup>-1</load-on-startup>
</servlet>

<servlet-mapping>
   <servlet-name>dwr-invoker</servlet-name>
   <url-pattern>/dwr/*</url-pattern>
</servlet-mapping>

this is because DWR calls are handled by a separate servlet, the DwrServlet, which needs to be configured accordingly (with another <servlet> and <servlet-mapping> section). You can integrate and write your own components if required and use the DWR communcation mechanism.

3.6. Further events

The previous sections already demonstrated in depth some sample events fired by the presented wingS components and how you can use custom implemented event listeners to react to these events. The table below points out a few more listener interfaces provided by wingS

Table 3.7. Selection of listener interfaces provided by wingS

ListenerProviderDescription
SRenderListenerSComponentThis listener will receive events before starting and after finishing the rendering of a component
SRequestListenerSessionReceives notifications about the different phases during the HTTP request processing of wingS. Refer to SRequestEvent for a documented list of these different phases.
SParentFrameListenerSComponentReceives events on adding and removing a component from a parent container. Use this if you need to modify or initialize things depending on the owning container, as the container is not set during construction time of a component.
SContainerListenerSContainerReceive events on every component added or removed from a container
SComponentListenerSComponentTo be notified about changed visibility of a component via setVisible() method calls
SDocumentListenerSTextComponentListener provided by text areas and input fields notifying about changes in the underlying 'document' which can be retrieved via the getText() method.
SExitListenerSessionListener to get notified on programatically destroying of the wingS user session. If this session exit is initiated by calling the Session.setExitAddress() method you can throw a veto exception to abort the session destroy (i.e. to prompt for unsaved values).
SInternalFrameListenerSInternalFrameListener for receiving events on opening, closing, iconinfying, deiconifying, minimizing and maximizing of internal frames (windows)
SComponentDropListenerDragAndDropManagerListener to get notified about DragSource objects dropped to a DragTarget
SInvalidLowLevelEventListenerSFrameEvents listener that gets notified about outdated request (mainly due to browser back navigation or double clicks). Used for back button detection and handling logic.

3.7. Layout Managers

As mentioned already in section Section 1.2.2, “Separating View and Logic” the layout managers are the components responsible for arranging and layouting your wingS widgets components on the screen. Therefore the container components like SPanel declare a setLayoutManager() method accepting instances of SLayoutManager. Container components can be nested into each other, so even very complex application layouts can be easily assembled using the simpler layout managers.

wingS ships with two main classes of layout managers: the static STemplateLayout and a set of dynamic layout managers similar to those present in Swing.

The static STemplateLayout uses a HTML template file as layout source, inside which you declare fixed and named positions for your components to arrange. So this layout is the best choice if you have exact notions how the resulting HTML for this container should look like. Hence this layout manager is typically used to implement the main screen layout of your web application arranging the main components like the menu bar, working area or notification elements in an template reflecting your corporate website design.

The set of powerful dynamic layout managers arrange the contained components by various patterns. Their advantage is their capability to handle an arbitrary number of components without the need to know and declare the resulting layout positions already during development time.

If using relative preferred sizes, please understand, how layout calculations are performed in the browser. If you give an element 100% width or height, this is 100% of the available space. A typical desktop layout, that spans the whole browser window requires, that all containers, starting from the frame's content pane, have a pr