JCustomizer - Java AWT/Swing GUI Resource Manager

The target of this project is the customization of Java AWT/Swing GUI's at runtime, that means a support assistant or an user can customize the appearance of the graphical application he/she is deploying or using. This absolves developers from writing statements like   setFont(new Font("i don't know")).
A nice accompaniment is a WYSIWYG access to internationalization without programming efforts, including the possibility to switch the language of the GUI at runtime.

This package is open source, published under Lesser GNU Public License (LGPL).


SourceForge.net Logo
Author: Ritzberger Fritz, April 2004




Quick Start

The JCustomizer library consists of Swing (or AWT) dialogs and a resource manager that brings up these dialogs, manages customization actions and persists resources in property files. The resource manager works generically upon every Java GUI and even detects dynamically added components.

The only thing a user has to do is to focus the component he/she wants to customize and press F7 (configurable). Or to press Alt-F7 and choose a component that is not focusable, and edit label texts in multiple languages in a table.

The only thing the programmer has to do is to install a resource managing event queue. A geometry manager that persists sizes and locations of windows is contained within the package, but must be programmed separately. Persistence is provided (for both) by plain property files in the user home directory.


Currently supported resources

Currently supported resources are:

Configuration parameters

JCustomizer provides some system properties to influence dialog behaviour. A system property can be set by java commandline arguments
java -Dfri.gui.awt.resourcemanager.showRestricted=true ...
or programmatically
System.setProperty("fri.gui.awt.resourcemanager.showRestricted", "true");

fri.gui.awt.resourcemanager.showRestricted
When true, the user will not be able to change texts, tooltips, languages or menu shortcuts / accelerators / mnemonics. Nevertheless he/she will be able to choose a language, using one of the dialogs or a built-in language menu.
fri.gui.awt.resourcemanager.customizeKey
The key that brings up the customize dialog for the focused component. It is written e.g. as "Ctrl+Alt+Shift-F7", the order of modifiers (Ctrl+Alt+Shift) is not significant, but the key code must be the last and separated by "-".
fri.gui.awt.resourcemanager.chooseKey
The key that brings up the component choice dialog. Notation is the same as for customizeKey.




User Manual

Changing the appearance of a GUI is a question of ergonomy. Maybe you have seen enough white windows, turning your eyes into spirals after several hours of work. Maybe you quickly want to make the font bigger, as a lot of people are standing around your screen, trying to read your writings ...

All settings you make with JCustomizer will be persisted automatically, that means the next time you launch your Java application the colors and fonts will be those you have chosen. When you want to get rid of your resources, go to yur home directory, change to ".friware/guiresources", and delete all contained files.

Customizing a GUI component

To change GUI properties, first you have to focus a Component, e.g. a button. Pressing the mouse on it and dragging outside will do so, or you can focus it using the tab key.

Then you have to press F7 (or whatever key the configuration assistant has provided as customize accelerator). You will see the dialog above coming up, and you can choose a tab where the property you want to change is editable. The provided properties will be different for every component, as a textarea will not have an accelerator, and a panel will have no foreground color (as no text will be rendered on it).

After choosing your color, font ... you can test it by using the "Test" button. The "Ok" button will set the resource and end the dialog. The "Reset" button is active only when the resource was set before, use it when you want to remove your setting and reactivate the value the application provided. Using the "Cancel" button will reset all changes you tested, the "Others" button will bring up a component choice where you can choose other components for customizing (see next chapter).

You can activate a resource on all components of the currently edited type by checking the "For All" box, e.g. for JTree like in the screenshot below. That means all "tree" components of all Windows will have the color you chose. When you do not check that option, only the one tree you are working on will have that property, but nevertheless every window of the same type will show that tree color, too.

JCustomizer Screenshot

Choosing a Component for customizing

When you want to customize a component that is not focusable, e.g. a label, you have to call the component choice. You can do that by pressing Alt-F7 (or whatever hotkey was provided by the configuration assistant).

You see a dialog coming up that contains button representations for all components contained within the current window (frame or dialog). Pressing one of these buttons will start the customize dialog for this component (described in previous chapter).

You have a combo box for setting the Look And Feel for all visible frames (Swing only).

You can change to the "Text" tab and edit label texts in mulitple languages. You can choose a language then by selecting it within the list. This will make visible all the label texts of the the chosen language.

component choice screenshot

Internationalization of all GUI texts of a Window

On component choice dialog there is a tab that provides inserting and deleting of languages (Swing only). When you add a language, a new column will appear in the table below. When you remove a language, a column will disappear, and all texts of that language will be lost. The table provides editing all label texts contained within the current window, in all languages you provided.

Ending the dialog will make visible the label texts of current language. The current language is the one selected when you dispose the dialog. When your programmer provided a language menu, you can do that without calling the component choice dialog.


internationalization screenshot


For an AWT application you have to edit the label texts one by one within the customize dialog. There is no table providing all texts together like in Swing customizer.

Switching the language at runtime

To switch the language at runtime, you have three options:


Thats all for users, have fun!



Technical Manual

JCustomizer is a rewrite of an idea i had in 2000. It has 10 000 lines of code and is a framework. Maybe someday i will take the time to draw some UML diagrams about it.

Programming JCustomizer is easy. All you have to do is to install the ResourceManagingEventQueue (or JResourceManagingEventQueue for Swing), a special event queue that detects the creation and disposal of Frame and Dialog windows.

How to install the resource managing event queue

There is one queue for AWT and one for Swing. On every creation event the queue installs a ResourceManager instance upon the new window. The ResourceManager itself listens for window close and frees all resources then. The ResourceManager will loop the component tree recursively, assign unique names to every component, and associate persistent resources to those names.

The following example installs the Swing event queue.

import fri.gui.swing.resourcemanager.JResourceManagingEventQueue;

public class MyFrame extends JFrame
{
    {   // instance initializer
        JResourceManagingEventQueue.install();
        // ignores all calls except the first
    }

    public MyFrame()    {
        ...
    }

 
As you can guess the AWT installation works like this:
import fri.gui.awt.resourcemanager.ResourceManagingEventQueue;

public class MyFrame extends Frame
{
    {
        ResourceManagingEventQueue.install();
    }

    public MyFrame()    {
        ...
    }
(Actually the installation can be done anywhere, but i had problems when doing it in the application main, or within a static initializer.)

Adding popup menus using the resource event queue

Popup menus are not contained within a component tree. So you have to publish them to the ResourceManager explicitely. You can do this by using the ResourceManagingEventQueue statically:

JResourceManagingEventQueue.addComponents(this, new Object [] { popup });
// "this" is MyFrame instance


You can do this anywhere, even before any component has been added to content pane. After having done this the popup menu items will be customizable by component choice dialog after the Frame was shown on screen.


How to use the resource manager without event queue

When you want to use the ResourceManager without installing the special event queue, you must progam it in your application Frame / Dialog. You can do this after all components were allocated. If you set the ResourceManager before the components were added to content pane (or Frame for AWT), the ResourceManager will find no components, and F7 keypress will not work.
import fri.gui.swing.resourcemanager.ResourceManager;

public class MyFrame extends JFrame
{
...
getContentPane().add(p1, BorderLayout.NORTH);
getContentPane().add(p2, BorderLayout.CENTER);
getContentPane().add(p3, BorderLayout.SOUTH);

new JResourceManager(this);
...      


If you have popup menus you must do this:

    new JResourceManager(this, new Object { popup1, popup2 });

As you can guess the AWT work is the same, using ResourceManager instead of JResourceManager.


Internationalization

Internationalization is a complex issue. Commonly it is done by using ResourceBundle derivations that read from files or databases. Normally the language can be configured only before application startup, being immutable at runtime.

JCustomizer provides a lightweight access to internationalization. It enables you to create a list of supported languages, and edit label texts for all those languages then. The language can be switched at runtime, showing immediately on the GUI.
All that is done without one line of sourcecode, so customizing the GUI can be a separate step before deploying the application, done by another person than the developer. The Swing customizer provides a language table that enables you to translate all GUI texts of a Window into different languages in one place.

JCustomizer works with a basic implementation of MultiLanguageString, and a static MultiLanguage class that obtains the current language setting (is NOT the factory for MultiLanguageStrings). The toString() method of MultiLanguageString shows the translation of current MultiLanguage setting. All runtime resources that are generated programmatically can be packed into MultiLanguageString, this is straight forward (it is not possible for JCustomizer to provide a multi-language solution that is pluggable into all the implementations that exist).

The ways to fill a MultiLanguageString objects are various. There is no way to fill JCustomizer MultiLanguageString resources without using the GUI, as they must be assigned to their components, and JCustomizer uses its own identifiers for them. Generating resource files with already existing language resources is possible but difficult.

Language resources are expensive. Program modifications can confuse the ResourceManager, resources could be lost after. Read the "component identification" chapter before deciding for JCustomizer, to be sure this is the right solution for your internationalization.


Customizeable Text-Components

JTextComponent derivates are not customizable by default. This prevents them from having other contents than programmatically loaded data. Nevertheless in some cases it might be useful to have such textareas, e.g. for multilingual help texts. When you need such a component, you can let it implement the interface ResourceProvidingTextComponent to make it available for customization/internationalization:

public class HelpTextArea extends JTextArea implements ResourceProvidingTextComponent
{
    ...
}

Resource-Ignoring Components

On the other hand you would like to prevent some component from being found by the ResourceManager. Then you can let it implement one of the the interfaces ResourceIgnoringContainer (prevents the component and all its sub-components from being customizable) or ResourceIgnoringComponent (prevents the component but NONE of its sub-components from being customizable):

public class MyComboBox extends JComboBox implements ResourceIgnoringContainer
{
    ...
}

Providing an application language menu

A developer can provide a pre-made language menu within the application main menu or some popup doing the following:
import fri.gui.swing.resourcemanager.JLanguageMenu;

    ...

    menubar.add(new JLanguageMenu());
    ...

All language menu items can be customized themselves without influencing their functionality (which means you can customize "English" to "Englisch").

Launching the component choice from some menu item or action button

A developer can provide the start of the component choice by some menu item or action button doing the following:

import fri.gui.swing.resourcemanager.JResourceDialogActionListener;

public class MyFrame extends JFrame
{
    {
       
JResourceManagingEventQueue.install();
    }

    public MyFrame()    {

        ...
        ActionListener callback = new
JResourceDialogActionListener(this);

        JButton b = new JButton("Customize");
   
    b.addActionListener(callback);
        toolbar.add(b);

        JMenuItem mi = new JMenuItem("Customize");
        mi.addActionListener(callback);
        menu.add(mi);
        ...

When you do not use the resource managing event queue, you can allocate the action listener like this:
import fri.gui.awt.resourcemanager.ResourceDialogActionListener;

    ...
    ResourceManager rmgr = new ResourceManager(this);    // loops component tree
    ...
    ActionListener callback = new ResourceDialogActionListener(
rmgr);
    b.addActionListener(callback);
    ...


Identification of components after program modification

The key question is: will my tree view still be blue with bold font after i inserted a new panel, i.e. will the new component hierarchy be recognized by the ResourceManager?

When the ResourceManager loops the component tree of a window, it assigns window-type-unique names to all found components. These names are made of the base name of the underlying Java class (e.g. "button" for "MyButton" which is derived from "JButton"), and the hierarchical context where the component resides. When naming menuitems, buttons or labels that have text getter and setter methods,  the ResourceManager uses the  initial programmatic text value of that component to name it. Following is an excerpt of a dump of resource names of a Swing test window ("New Frame" and "New Dialog" are button labels):
rootpane.layeredpane.contentpane.panel
rootpane.layeredpane.contentpane.panel.button_New_Frame
rootpane.layeredpane.contentpane.panel.button_New_Dialog
After a program modification this could look like the following (a new panel was added):
rootpane.layeredpane.contentpane.panel.panel
rootpane.layeredpane.contentpane.panel.panel.button_New_Frame
rootpane.layeredpane.contentpane.panel.panel.button_New_Dialog
The resource names have changed. Persistent resources cannot be assigned anymore to those new hierarchical names.

Fortunately ResourceManager has a recovery mechanism for lost resources that searches the label text, e.g. "button_New_Frame", ignoring the hierarchy the button is embedded. Thus your expensive mulitlanguage resources will survive a program modification, as long as the programmatic initial label text of that component was not changed (there is no reason for doing that, as these labels will never be shown when the application was customized).

There is a matchLostResources() method in ResourceManager implementation that performs the recovery task. You can hack on it, but only text holding components can be safely recognized.

So it is best to do customization after the component hierarchy of your application is stable.

How to use the geometry manager

Many implementations have been written to make AWT and Swing windows well behaved when showing on screen. This is not covered by the JDK, as it is not very complicated to do this using a WindowListener. GeometryManager is yet another implementation of that issue.

The GeometryManager obtains window geometries associated with window types. It locates windows on screen where they have been at last usage, and cascades or tiles mulitple instances of a certain window type.

Use it like shown below:
import fri.gui.awt.geometrymanager.GeometryManager;

public class MyFrame extends JFrame
{
...
getContentPane().add(p1, BorderLayout.NORTH);
getContentPane().add(p2, BorderLayout.CENTER);
getContentPane().add(p3, BorderLayout.SOUTH);

new GeometryManager(this).show();    // must store before exit happens

addWindowListener(new WindowAdapter() {
    public void windowClosing(WindowEvent e) {
        System.exit(0);
    }
});

The GeometryManager is a small separate package and is not associated with the resource managing event queue in any way.

Property file sample

Following is a representative excerpt from a sample resource property file.

frame.Text=Deutsch\=Swing (J)Customizer Beispiel\nEnglisch\=Swing (J)Customizer Example\n
rootpane.layeredpane.menubar.menu_File.Text=Deutsch\=Datei\nEnglisch\=File\n
rootpane.layeredpane.menubar.menu_File.menu_Remove.Text=Deutsch\=L\\u00F6schen\nEnglisch\=Remove\n
rootpane.layeredpane.contentpane.splitpane.panel0.scrollpane0.viewport.table.Font=Dialog-plain-14
tab_First_Tab.Text=Deutsch\=Erste Lasche\nEnglisch\=First Tab\n
tab_Second_Tab.Text=Deutsch\=Zweite Lasche\nEnglisch\=Second Tab\n
panel.scrollpane0.viewport.textarea.Background=\#ffff00






Still unsolved

After that breathless story of succes here come the unsolved problems, commonly called TODO.







Author: Ritzberger Fritz, April 2004