# dklibs 1.27.0

- Overview
- Download
- Download / Installation
- Installation from source on Windows systems
- Usage
- FAQ
- Change log
- Other projects
- Tutorial
- Developer docu
- stc
- tracecc and trana
- tracecc GUI
- tracecc PDF man.
- API: sort and search
- API: Generic I/O
- Preferences
- File search
- Font configuration
- LaTeX encodings
- Security
- DK libraries for Java

- Purpose of the libraries
- Managing software using DkTool
- DkTool in detail
- The DkTool constructor
- Resource search
- Specifying resource bases
- Suggestions for programmers and administrators
- To use or not to use localization
- Configuration directory.
- Configuration file "config.txt"
- Localized texts
- Where to place localized texts
- Hands on: Using the DkTool and DkFrame class
- Overview
- Files in the project
- Project setup
- First steps - The class for the main function
- The main window class
- Variables names
- Construction
- Destruction
- Adding GUI elements (1): Comments
- Main window class entire file
- Creating the GUI with jlayout
- Adding GUI elements (2): The *.jl file
- Adding GUI elements (3): Merging the *.jl file into *.java
- The Printable object
- Program resources
- Setting the configuration directory
- The "config.txt" file
- The GhostScript fonts
- Test
- Localization
- Localized text file
- Localized text file in an external resource directory
- Placing the useless.txt file
- Test
- Localized text file in the *.jar file

## Purpose of the libraries

The DK libraries for Java provide classes for both GUI and non-GUI related problems:

- DkTool class
  - Properties (configuration entries set by an administrator).
  - Preferences (configuration entries set by the user, ony available in dirk_krause.tools for applications, not for web-start applications).
  - Resource search, including search for localized resources.
  - String processing:
    - Search for index of a string in an array
    - Convert strings to booleans, numbers and colors
    - Encode strings for PostScript and HTML
    - Find and replace file name suffixes
  - Obtain information:
    - Current users login name
    - Home directory
    - Language
    - Region
    - Host name
    - File separator
    - Path separator
    - Run type (application, web-start application or applet)
  - Positioning of GUI elements.
- DkGuiTool class
  - Enforce a look and feel.
  - Enforce a minimum font size.
  - Font family replacement.
- DkGuiDialog class
  - Configure the settings for the DkGuiTool.
- DkFrame class
  The main window of an application can be derived from this class.
  - Save window position and size on exit, restore when running the next time.
  - Show dialog boxes for information, warning and error messages.
  - Bring up the GUI settings dialog (DkGuiDialog).
  - Send print data to a printer, either a String containing PostScript code (available in dirk_krause.tools.DkFrame for applications only, not for web-start applications) or a Printable object.
- DkMsgDialog class
  - Show a message, either plain text or HTML.
    This class can be used to show help or version and license information...
- DkLogPanel class
  - Scroll panel combined with text editor to show log text which grows and grows... while the application is running.
- DkColorArea class
  - Visualize a color setting, show color chooser dialog to change the color when clicking on the area.
- DkDropFile class
  - Handler for the drop part of drag'n'drop operations.
- DkDropOneDirectory class
  - Subclass of DkDropFile, accepts exactly one directory as object.
- DkPrintClient and DkPrintResult classes
  - These classes are used internally for printing.

## Managing software using DkTool

### DkTool in detail

#### The DkTool constructor

The 3-argument constructor of DkTool does the following steps:

- Save the application name, the application group name and the run type (application, web-start application or applet).
- Attempt to retrieve user name (login name), language, region, file separator, path separator, users home directory* and current directory* (* for applications only).
- For applications create preferences nodes for application, application group and general defaults.
- Attempt to find the host name.
- Read properties from file "config.txt" found via resource search (see below).
- Get font path, image path and application resources directory from preferences/properties. Add the application resources directory to the resources base list.

#### Resource search

The openResource method searches for a file in the following locations:

- the locations contained in the resource base list,
- the current directory (applications only) and
- the *.jar file.

The openLocalizedResource() method attempts the language/region and language subdirectories in each of the directories listed above first.

#### Specifying resource bases

When specifying a resource base or the configuration directory I strongly recommend to use a "http://" or "file://" prefix.
Names without the prefix are interpreted as a path in the *.jar file.
To open streams for resources in the *.jar file we use xyz.getClass().getClassLoader().getResourceAsStream(abc) so you must place resources in a correct path relative to the objects class file.

### Suggestions for programmers and administrators

#### To use or not to use localization

If your language is not English and you want to compile your software on both UTF-8 and non-UTF-8 enabled systems I suggest to use English strings in the source and to provide strings in your native language using localization.

#### Configuration directory.

You should pass the first command line argument (if any) as third argument to the DkTool constructor which uses it as configuration directory and first resource directory. Like all resource directories the directory should have a prefix "file://" (for local applications) or "http://".
Remember: Locations without "file://" or "http://" are interpreted as paths in the *.jar file.

#### Configuration file "config.txt"

One of the activities in the DkTool constructor is to search for a file "config.txt", which can be either in one of the resource bases (preferred) or in the *.jar file.
This file consists of a series of sections, each section consists of a scope part and a settings part.
The scope part consists of "[user/host/application]", "[user/host]" and "[user]" lines. The wildcard "*" represents all users/all hosts/all applications (depending on the position).
The setting part consists of key/value pairs, one per line, key and value separated by "=".
Example:

    [*]
    resources.directory.fonts=file:///usr/local/gs-fonts

    [*/*/UselessForm]
    resources.directory.application=file:///home/joe/exuf

In the example we set the property "resources.directory.fonts" to "file:///usr/local/gs-fonts" for all users on all hosts running any application.
For application "UselessForm" run by any user on any host we set "resources.directory.application" to "file:///home/joe/exuf".

The most important property to set is "resources.directory.application" for each application.
The application searches for resources (i.e. files containing localized texts) in this directory.
The properties "resources.directory.fonts" and "resources.directory.images" are used by objects implementing java.awt.print.Printable created by fig2vect to search for fonts and embedded images.

Summary: In the configuration directory there must be a file "config.txt" containing at least:

    [*]
    resources.directory.fonts=...

    [*/*/...]
    resources.directory.application=...

#### Localized texts

A String array of localized texts can be read using DkTool's getLocalizedArray() method. The arguments are an object reference and a file name. The file name is used in a resource search, the file is read, each non-comment line is used to build one of the strings in the returned array.
The file for localized texts consists of text lines and comment lines. A comment line is started by a raute '#' as first character in the line, all other lines are text lines (event empty lines, empty text lines result in an emty text).
I suggest to use comments for indices and the purpose of a text.
Note: The file must be UTF-8 encoded. I recommend to use vim or gedit on a Linux system with LANG=language_region.UTF-8 setting. There may be other editors on other systems allowing to save files using UTF-8 encoding too.

#### Where to place localized texts

In the Java application code the programmer specifies a file name without the leading directory.
You have to save the localized texts in a file language/region/filename or language/filename in one of the resource base directories in the resource base list.

## Hands on: Using the DkTool and DkFrame class

### Overview

In this example we will create a useless form application and show how to use the methods from DkTool and DkFrame.
A user can specify surname and name and his/her opinion about the form and create a printout of the entered data.
We will show how to use preferences and localization. You will also see how to create a Printable object from a *.fig drawing and how to send it to the printer.

### Files in the project

UselessFormMain.java | The main program.
UselessWindow.java | The programs main window, the class is derived from DkFrame.
UselessWindow.jl | Description of the contents of UselessWindow. This file is merged into UselessWindow.java by the jlayout program.
UselessPrintable.java | Implements java.awt.print.Printable. This file is created from certificate.fig using the fig2vect program.
certificate.fig | The printout, the pattern is created in XFig, jFig, WinFig or any other program producing a *.fig file. Remember not to use fill patterns on splines!

### Project setup

- In your Java IDE create a new project named "UselessForm". The applications main class is "uselessform.UselessFormMain".
  Some IDEs will create a main class automatically.
- Either add /usr/local/bin/dklibsj.jar to your projects library set (preferred method) or copy the dirk_krause directory including all contents from /usr/local/lib/java/src/dklibsj into your projects src directory.

### First steps - The class for the main function

We want to create a GUI, this should be done from within the AWT thread. We create a class GuiCreator implementing the Runnable interface and pass an object of this class to SwingUtilities.invokeLater().

    class GuiCreator implements Runnable {
        private String dir = null;
        private String[] def_t = null;

        public GuiCreator(String s, String[] t) {
        }

        public void run() {
        }
    }

The constructor of GuiCreator uses two arguments: a configuration directory name (may be null) and an array containing default values for all the texts or strings used in the application.
Both arguments are saved for later use.

    class GuiCreator implements Runnable {
        private String dir = null;
        private String[] def_t = null;

        public GuiCreator(String s, String[] t) {
            dir = s; def_t = t;
        }

        public void run() {
        }
    }

The GuiCreator.run() method is run from the AWT thread. First we create a DkTool object, we pass the application name, an application group name and the directory saved by the constructor to the new object.
The constructor of the DkGuiTool object uses dkt to retrieve properties and preferences for GUI setup. It establishes a look and feel, enforces minimum font sizes and can replace a font family by another one.
We attempt to retrieve an array containing localized texts for our application from a file "useless.txt". If such an array is found it will be used in the further steps, otherwise we use the default texts in def_t.
The UselessWindow class is the main window class of our project. We pass dkt, dkg and either def_t or loc_t to the constructor. After packing the window we can attempt to restore position and size stored by a previous run of the program before we make the window visible. If there is no stored information the window will appear centered on the screen.

    class GuiCreator implements Runnable {
        private String dir = null;
        private String[] def_t = null;

        public GuiCreator(String s, String[] t) {
            dir = s; def_t = t;
        }

        public void run() {
            DkTool dkt = new DkTool(def_t[0], def_t[1], dir);
            DkGuiTool dkg = new DkGuiTool(dkt);
            String[] loc_t = dkt.getLocalizedArray(this, "useless.txt");
            String[] t = def_t;
            if(loc_t != null) { if(loc_t.length >= def_t.length) t = loc_t; }
            UselessWindow w = new UselessWindow(dkt, dkg, t);
            w.pack();
            w.choosePosition();
            w.setVisible(true);
        }
    }

The def_t array in the UselessFormMain class contains the default values for all texts used in the application. This array will grow while we continue to work on the project.
It is a good idea to have comments showing the index before each array element and a comment about the use of each element at the line end.

        public static final String[] def_t = {
            /*   0 */ "UselessForm",            // Application name
            /*   1 */ "NonsensApplications",    // Application group name
            /*   2 */ "Surname:",               // GUI elements
            /*   3 */ "Name:",
            /*   4 */ "Opinion:",
            /*   5 */ "Very good",
            /*   6 */ "Useless",
            /*   7 */ "Waste of time",
            /*   8 */ "Print",
            /*   9 */ "Exit",
            /*  10 */ "Please enter your surname here.",    // Tool-tip texts
            /*  11 */ "Please enter your name here.",
            /*  12 */ "Please specify your opinion about this program here.",
            /*  13 */ "Print certificate about the filled form.",
            /*  14 */ "Exit this application.",
            /*  15 */ "Error",  // Error message
            /*  16 */ "Please fill all the text fields.",

Finally we have the entire file here:

    package uselessform;

    import javax.swing.SwingUtilities;

    import dirk_krause.tools.*;

    class GuiCreator implements Runnable {
        private String dir = null;
        private String[] def_t = null;

        public GuiCreator(String s, String[] t) {
            dir = s; def_t = t;
        }

        public void run() {
            DkTool dkt = new DkTool(def_t[0], def_t[1], dir);
            DkGuiTool dkg = new DkGuiTool(dkt);
            String[] loc_t = dkt.getLocalizedArray(this, "useless.txt");
            String[] t = def_t;
            if(loc_t != null) { if(loc_t.length >= def_t.length) t = loc_t; }
            UselessWindow w = new UselessWindow(dkt, dkg, t);
            w.pack();
            w.choosePosition();
            w.setVisible(true);
        }
    }

    /**
     *
     * @author krause
     */
    public class UselessFormMain {
        /** Texts used in the GUI. */
        public static final String[] def_t = {
            /*   0 */ "UselessForm",            // Application name
            /*   1 */ "NonsensApplications",    // Application group name
            /*   2 */ "Surname:",               // GUI elements
            /*   3 */ "Name:",
            /*   4 */ "Opinion:",
            /*   5 */ "Very good",
            /*   6 */ "Useless",
            /*   7 */ "Waste of time",
            /*   8 */ "Print",
            /*   9 */ "Exit",
            /*  10 */ "Please enter your surname here.",    // Tool-tip texts
            /*  11 */ "Please enter your name here.",
            /*  12 */ "Please specify your opinion about this program here.",
            /*  13 */ "Print certificate about the filled form.",
            /*  14 */ "Exit this application.",
            /*  15 */ "Error",  // Error message
            /*  16 */ "Please fill all the text fields.",
        };

        /**
         * @param args the command line arguments
         */
        public static void main(String[] args) {
            String dir = null;
            if(args.length > 0) { dir = args[0]; }
            SwingUtilities.invokeLater(new GuiCreator(dir, def_t));
        }

    }

### The main window class

#### Variables names

We will use the following variable names for the GUI elements:

lSurname | Label before the surname text field.
tfSurname | Surname text field
lName | Label before the name text field.
tfName | Text field for the name.
lOpinion | Label before the opinion combo box.
cbOpinion | Opinion combo box
bPrint | Print button.
bExit | Exit button.

#### Construction

The main window class is derived from DkFrame. So the first action of the constructor is to call the DkFrame constructor, the window title, ad DkTool and a DkGuiTool are passed to the DkFrame constructor.
The DkTool object and the DkGuiTool object are saved to the variables dkt and dkgt by the DkFrame constructor. Both variables are protected, so they are available to our classes methods.

        public UselessWindow(DkTool d, DkGuiTool g, String[] new_t) {
            super(new_t[0], d, g);
            ...
        }

The texts provided by the GuiCreator are saved in an internal variable String[] t.

        private String[] t = null;

        public UselessWindow(DkTool d, DkGuiTool g, String[] new_t) {
            super(new_t[0], d, g);
            t = new_t;
            ...
        }

For the "Opinion:" combo box we need an array of the three choices.

        public UselessWindow(DkTool d, DkGuiTool g, String[] new_t) {
            super(new_t[0], d, g);
            t = new_t;
            cbValues = new String[3];
            cbValues[0] = t[5];
            cbValues[1] = t[6];
            cbValues[2] = t[7];
            ...
        }

Now we call jLayoutSetup(). This method does not yet exist, the jlayout program will generate it.

        public UselessWindow(DkTool d, DkGuiTool g, String[] new_t) {
            super(new_t[0], d, g);
            t = new_t;
            cbValues = new String[3];
            cbValues[0] = t[5];
            cbValues[1] = t[6];
            cbValues[2] = t[7];
            jLayoutSetup();
            ...
        }

The final action in the constructor is to check whether settings were saved in a previous run of the program. If we find settings we want to restore them.

        private String[] cbValues = null;

        public UselessWindow(DkTool d, DkGuiTool g, String[] new_t) {
            super(new_t[0], d, g);
            t = new_t;
            cbValues = new String[3];
            cbValues[0] = t[5];
            cbValues[1] = t[6];
            cbValues[2] = t[7];
            jLayoutSetup();
            restoreTexts();
        }

We use names "uselessform.surname"... for the preferences. The result array values and ivalues are initialized with useful default which can be used if no previous settings are found.
After retrieving the preferences we check the ivalues[0] value and correct it if necessary to avoit out-of-bounds exceptions. If the values[] elements are not null they can be used as initial texts for the text fields.

        private static final String[] ufPkStr = {
            "uselessform.surname",
            "uselessform.name",
        };

        private static final String[] ufPkInt = {
            "uselesform.opinion",
        };

        private void restoreTexts() {
            String[] values = { null, null };
            int[] ivalues = { 0 };
            dkt.getMultiplePreferenceStrings(ufPkStr, values);
            dkt.getMultiplePreferenceInts(ufPkInt, ivalues);
            if(ivalues[0] < 0) { ivalues[0] = 0; }
            if(ivalues[0] >= cbValues.length) {
                ivalues[0] = cbValues.length - 1;
            }
            if(values[0] != null) { tfSurname.setText(values[0]); }
            if(values[1] != null) { tfName.setText(values[1]); }
            cbOpinion.setSelectedIndex(ivalues[0]);
        }

#### Destruction

Each time a window inherited from DkFrame is closed the method cleanup() is invoked. The isLast parameter is true for the last window closed. This is the right time to save settings.

        @Override
        public void cleanup(boolean isLast) {
            if(isLast) {
                saveTexts();
            }
            jLayoutCleanup();
        }

To save the settings we collect the text field texts in an array and create an array (array size 1) for the int value. After saving the preferences to the DkTool object

        private void saveTexts() {
            String[] values = { null, null };
            int[] ivalues = { 0 };
            values[0] = tfSurname.getText();
            values[1] = tfName.getText();
            ivalues[0] = cbOpinion.getSelectedIndex();
            dkt.setMultiplePreferenceStrings(ufPkStr, values);
            dkt.setMultiplePreferenceInts(ufPkInt, ivalues);
            dkt.flushPreferences();
        }

#### Adding GUI elements (1): Comments

The jlayout program reads a *.jl file describing the GUI and adds four sections to the *.java source:

- Package imports,
- Variables,
- A GUI setup function jLayoutSetup() and
- A cleanup function jLayoutCleanup().

The places where to add which section are marked up by special comment pairs.

    package uselessform;

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.WindowEvent;

    import dirk_krause.tools.*;

    // jlayout.import.begin
    // jlayout.import.end

    /**
     *
     * @author krause
     */
    public class UselessWindow
        extends DkFrame
        implements ActionListener
    {
       ...

    // jlayout.variables.begin
    // jlayout.variables.end

    // jlayout.setup.begin
    // jlayout.setup.end

    // jlayout.cleanup.begin
    // jlayout.cleanup.end

       ...
    }

#### Main window class entire file

    package uselessform;

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.WindowEvent;

    import dirk_krause.tools.*;

    // jlayout.import.begin
    // jlayout.import.end

    /**
     *
     * @author krause
     */
    public class UselessWindow
        extends DkFrame
        implements ActionListener
    {
        private String[] t = null;

        private String[] cbValues = null;

        private static final String[] ufPkStr = {
            "uselessform.surname",
            "uselessform.name",
        };

        private static final String[] ufPkInt = {
            "uselesform.opinion",
        };

    // jlayout.variables.begin
    // jlayout.variables.end

    // jlayout.setup.begin
    // jlayout.setup.end

    // jlayout.cleanup.begin
    // jlayout.cleanup.end

        public UselessWindow(DkTool d, DkGuiTool g, String[] new_t) {
            super(new_t[0], d, g);
            t = new_t;
            cbValues = new String[3];
            cbValues[0] = t[5];
            cbValues[1] = t[6];
            cbValues[2] = t[7];
            jLayoutSetup();
            restoreTexts();
        }

        private void saveTexts() {
            String[] values = { null, null };
            int[] ivalues = { 0 };
            values[0] = tfSurname.getText();
            values[1] = tfName.getText();
            ivalues[0] = cbOpinion.getSelectedIndex();
            dkt.setMultiplePreferenceStrings(ufPkStr, values);
            dkt.setMultiplePreferenceInts(ufPkInt, ivalues);
            dkt.flushPreferences();
        }

        private void restoreTexts() {
            String[] values = { null, null };
            int[] ivalues = { 0 };
            dkt.getMultiplePreferenceStrings(ufPkStr, values);
            dkt.getMultiplePreferenceInts(ufPkInt, ivalues);
            if(ivalues[0] < 0) { ivalues[0] = 0; }
            if(ivalues[0] >= cbValues.length) {
                ivalues[0] = cbValues.length - 1;
            }
            if(values[0] != null) { tfSurname.setText(values[0]); }
            if(values[1] != null) { tfName.setText(values[1]); }
            cbOpinion.setSelectedIndex(ivalues[0]);
        }

        private boolean checkString(String s) {
            boolean back = false;
            if(s != null) {
                if(s.length() > 0) {
                    back = true;
                }
            }
            return back;
        }

        private void attemptToPrint() {
            String surName = dkt.trimString(tfSurname.getText());
            String name = dkt.trimString(tfName.getText());
            int i = cbOpinion.getSelectedIndex();
            if(checkString(surName)) {
                if(checkString(name)) {
                    UselessPrintable up = new UselessPrintable(dkt);
                    up.replaceText("Person", surName + " " + name);
                    up.replaceText("Opinion", cbValues[i]);
                    up.replaceText("01.01.2000", DkTool.getCurrentDateGerman());
                    sendToPrinter(up);
                } else {
                    tfName.grabFocus();
                    msgError(t[15], t[16], false);
                }
            } else {
                tfSurname.grabFocus();
                msgError(t[15], t[16], false);
            }
        }

        int[] keywordsToCheck = {
            8,  // Print
            9,  // Exit
        };

        @Override
        public void actionPerformed(ActionEvent e) {
            String ac = e.getActionCommand();
            int a = DkTool.getStringArrayIndex(t, ac, keywordsToCheck);
            switch(a) {
                case 8: {
                    attemptToPrint();
                } break;
                case 9: {
                    processWindowEvent(
                        new WindowEvent(this, WindowEvent.WINDOW_CLOSING)
                    );
                } break;
            }
        }

        @Override
        public void cleanup(boolean isLast) {
            if(isLast) {
                saveTexts();
            }
            jLayoutCleanup();
        }
    }

### Creating the GUI with jlayout

#### Adding GUI elements (2): The *.jl file

The UselessWindow.jl file must be placed in the same directory as the UselessWindow.java file.

At the beginning of the file we have some options and default settings:

- If we specify a tool tip text for a GUI element we also want to use that text as accessible description.
- The border (space between components in pixels) is in the int variable named "border". This variable is declared as protected in the DkFrame class, the value is obtained from the DkGuiTool object in DkFrame's constructor.
- We don't want objects to be filled.
- The default anchor of objects (position within the room available for an object) is on the left side (west).

    .option use tool tip text as accessible description     =       yes
    .default        border  =       border
    .default        fill    =       none
    .default        anchor  =       west

Next we are dealing with the features of the main window itself:

- Again we set the border and the fill style.
- The title is stored in t[0]. Remember, we have set the t array in the constructor.
- If the window is closed we dispose it (dispose all contents, if everything in a program is disposed the program exits).
- Elements in the main window are layed out in a gridbag.
- There is in the first level only one element, paContents. The elements starts in column 0, row 0 and is 1 column wide and 1 row high.
  The purpose of this element is to ensure a space of border between GUI components and the frame border as border is only used with gridbag layouts (and we want to use other layout managers too).

    border                  =       border
    fill                    =       none
    title                   =       t[0]
    default close operation =       dispose
    layout                  =       gridbag
    contents                =       paContents       0  0  1  1

The paContents element is of type JPanel. It uses a BoxLayout along the page axis (elements go from top to bottom).
Note: In difference to classical programs elements/variables are used before they are declared.
The paContents panel contains the panel for the input elements and another panel for the buttons.

    [JPanel paContents]
    layout                  =       box.page_axis
    contents                =       pInput
    contents                =       pButtons

In the pInput panel the elements are organized in a gridbag layout again. The lSurname label is placed in column 0, row 0 (left top) and is one column wide and one row high.
The tfSurname is placed one column right beside the previous element (+1) in the same row (.). The element is also one column wide and one row high.
The next element lName starts in column 0 (0) on the next row (+1)... (I hope you got it).

    [JPanel pInput]
    layout                  =       gridbag
    contents                =       lSurname         0  0  1  1
    contents                =       tfSurname       +1  .  1  1
    contents                =       lName            0 +1  1  1
    contents                =       tfName          +1  .  1  1
    contents                =       lOpinion         0 +1  1  1
    contents                =       cbOpinion       +1  .  1  1

The text for the label lSurname is taken from t[2]. The label is right aligned (the anchor is east).

    [JLabel lSurname]
    text                    =       t[2]
    anchor                  =       east

The text field tfSurname has a width of 20 characters.
The text for the tool tip is obtained from t[10].

    [JTextField tfSurname]
    constructor             =       20
    tool tip text           =       t[10]

For a combo box we provide a text array as constructor argument. We created the array in the constructor of the main window.

    [JComboBox cbOpinion]
    constructor             =       cbValues
    tool tip text           =       t[12]

The buttons panel uses a BoxLayout on a line (if possible all elements are placed in one line).
We want to have glue (the translation from german to english would result in: gum) between the panel borders and the buttons and between the buttons. The keyword $glue is used for that. If the panel grows (horizontally) the additional space is is used for all the glues equally.

    [JPanel pButtons]
    layout                  =       box.line_axis
    contents                =       $glue
    contents                =       bPrint
    contents                =       $glue
    contents                =       bExit
    contents                =       $glue

The label and the action command text for the bPrint button are obtained from t[8]. The action listener is the main window itself (remember: the jLayoutSetup() function to generate the GUI contents elements is a method of this class).
The bPrint and bExit button are set to equal size because they are members of the same size group.

    [JButton bPrint]
    text                    =       t[8]
    tool tip text           =       t[13]
    action listener         =       this
    size group              =       buttons

    [JButton bExit]
    text                    =       t[9]
    tool tip text           =       t[14]
    action listener         =       this
    size group              =       buttons

Here is the entire file:

    .option use tool tip text as accessible description     =       yes
    .default        border  =       border
    .default        fill    =       none
    .default        anchor  =       west

    border                  =       border
    fill                    =       none
    title                   =       t[0]
    default close operation =       dispose
    layout                  =       gridbag
    contents                =       paContents       0  0  1  1

    [JPanel paContents]
    layout                  =       box.page_axis
    contents                =       pInput
    contents                =       pButtons

    [JPanel pInput]
    layout                  =       gridbag
    contents                =       lSurname         0  0  1  1
    contents                =       tfSurname       +1  .  1  1
    contents                =       lName            0 +1  1  1
    contents                =       tfName          +1  .  1  1
    contents                =       lOpinion         0 +1  1  1
    contents                =       cbOpinion       +1  .  1  1

    [JLabel lSurname]
    text                    =       t[2]
    anchor                  =       east

    [JTextField tfSurname]
    constructor             =       20
    tool tip text           =       t[10]

    [JLabel lName]
    text                    =       t[3]
    anchor                  =       east

    [JTextField tfName]
    constructor             =       20
    tool tip text           =       t[11]

    [JLabel lOpinion]
    text                    =       t[4]
    anchor                  =       east

    [JComboBox cbOpinion]
    constructor             =       cbValues
    tool tip text           =       t[12]

    [JPanel pButtons]
    layout                  =       box.line_axis
    contents                =       $glue
    contents                =       bPrint
    contents                =       $glue
    contents                =       bExit
    contents                =       $glue

    [JButton bPrint]
    text                    =       t[8]
    tool tip text           =       t[13]
    action listener         =       this
    size group              =       buttons

    [JButton bExit]
    text                    =       t[9]
    tool tip text           =       t[14]
    action listener         =       this
    size group              =       buttons

#### Adding GUI elements (3): Merging the *.jl file into *.java

Run

    jlayout UselessWindow.jl UselessWindow.java

To merge the information from the *.jl file into the *.java file.
When rewriting the *.java file jlayout keeps the comment pairs marking up the section positions.
In an iterative development cycle you can change the *.jl file and run jlayout again as often as necessary.

### The Printable object

To print something we need an object implementing the java.awt.print.Printable interface.
A large part of the printout is constant, only the name, the opinion and the current date are varied by the program.
We prepare the printout as *.fig file first, use either XFig, jFig or WinFig to create the file certificate.fig in a new subdirectory "fig" of our project.
We use placeholders for the texts to vary:

Person | The surname and name entered in the form.
Opinion | The opinion entered in the form.
01.01.2000 | The current date.

After finishing the *.fig file we run

    fig2vect -ljava.a4-web -odklibsj=yes certificate.fig ../src/uselessform/UselessPrintable.java

The new class UselessPrintable appears in your Java IDE in the uselessform package.

At the beginning of the file you see a list of resources needed by the file, fonts and images. You can either pack the fonts and images into the *.jar file or make them available in resource directories specified as file:// or http:// URLs.

In the attemptToPrint() method of the UselessWindow class you see that replaceText() is used to replace the placeholders "Person", "Opinion" and "01.01.2000" by real data.
The sendToPrinter() method from the DkFrame class is used to print the object.

### Program resources

#### Setting the configuration directory

In our example the main() function passes the first command line argument to the constructor of the GuiCreator object which in turn passes it to the DkTool constructor.
This third argument of the DkTool constructor is used as configuration directory and as the first resource directory. In your home directory create a subdirectory "/home/joe/conf".
In your IDE set up the first command line argument when running the UselessForm application to "file:///home/joe/conf". Do not forget the "file://" prefix.

#### The "config.txt" file

The constructor of DkTool attempts to find a DkTool-property file "config.txt". Typically this file is located in the configuration directory.
It should contain three properties for each application:

- resources.directory.application
  This directory is added to the list of resource bases by the DkTool constructor. This directory can be used for files containing localized texts.
  In this example we use "file:///home/joe/exuf".
- resources.directory.fonts
  The directory where Printable objects created by fig2vect search for fonts. Typically there is one directory containing all the GhostScript fonts, all applications use the same directory.
  In this example we use "file:///usr/local/gs-fonts".
- resources.directory.images
  The directory where Printable objects created by fig2vect search for images.
  Our Printable does not use embedded images so we don't need this directory.

    [*]
    resources.directory.font=file:///usr/local/gs-fonts

    [*/*/UselessForm]
    resources.directory.application=file:///home/joe/exuf

#### The GhostScript fonts

Get the recent GhostScript source distribution and unpack it. Copy all the files from the Resources/Fonts directory to /usr/local/gs-fonts.

### Test

Now you should be able to run the application and print the useless form.

### Localization

#### Localized text file

The file for localized texts consists of text lines and comment lines. A comment line is started by a raute '#' as first character in the line, all other lines are text lines (event empty lines, empty text lines result in an emty text).
I suggest to use comments for indices and the purpose of a text.

    #
    #  0: Application name
    UselessForm
    #
    #  1: Application group name
    NonsensApplications
    #
    #  2: Label: Surname
    Vorname:
    #
    #  3: Label: Name
    Name:
    #
    #  4: Label: Opinion
    Meinung:
    #
    #  5: Combo box value: Opinion = Very good
    Toll
    #
    #  6: Combo box value: Opinion = Useless
    Nutzlos
    #
    #  7: Combo box value: Opinion = Waste of Time
    Zeitverschwendung
    #
    #  8: Button text: Print
    Drucken
    #
    #  9: Button text: Exit
    Beenden
    #
    # 10: Tool tip text: Enter your surname here
    Bitte geben Sie hier Ihren Vornamen an.
    #
    # 11: Tool tip text: Enter your name here
    Bitte geben Sie hier Ihren Namen an.
    #
    # 12: Tool tip text: Specify your opinion
    Bitte geben Sie Ihre Meinung zu diesem Formular an.
    #
    # 13: Tool tip text: Print certificate
    Zertifikat ausdrucken.
    #
    # 14: Tool tip text: Exit this application
    Diese Anwendung verlassen.
    #
    # 15: Error message box title
    Fehler
    #
    # 16: Error text: Please fill all the fields
    Bitte füllen Sie alle Felder aus.

Note: Please leave texts 0 and 1 as in the original.

#### Localized text file in an external resource directory

##### Placing the useless.txt file

In /home/joe/exuf create a subdirectory "de" and place the file in this directory.

##### Test

Now you should be able to run the application. If your language setting (LANG environment variable) is "de" or "de_DE" or "de_DE.UTF-8" you should see the GUI in german language.
The printout is in english language as we did not make any attempt to localize it. To localize the printout we would need to create a Printable or each language we want to support...

#### Localized text file in the *.jar file

The localized texts for favorite languages (i.e. English and your native language) should be included to the *.jar file.
The text file should be included in a "language" or "language/region" subdirectory in the *.jar file, language and region are the two-letter codes for your country and region converted to lower case. I.e. a german version of useless.txt can be placed in a "de" subdirectory.
The way to insert additional files into a *.jar file depends on the IDE you use.
If you use NetBeans and the useless form project is placed in /home/joe/NetBeansProjects/UselessForm, create a directory /home/joe/NetBeansProjects/UselessForm/build/classes/de and place useless.txt in this directory. Force the IDE to rebuild the *.jar file by adding a useless space to one of the *.java files, remove the space and save the file.