/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2013-2015 ForgeRock AS.
 */
package org.opends.quicksetup.ui;

import static org.opends.messages.QuickSetupMessages.*;

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.util.HashMap;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JRadioButton;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.text.JTextComponent;
import javax.swing.text.html.HTMLEditorKit;

import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;

/**
 * This class provides constants an methods to create Swing objects and to
 * generate UI elements with a common look and feel.
 * <p>
 * When we want to change a color, a background or a font this is the class
 * that should be modified.
 */
public class UIFactory
{
  private static boolean initialized;
  private static String parentPackagePath;
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();

  /** Specifies the horizontal insets between buttons. */
  public static final int HORIZONTAL_INSET_BETWEEN_BUTTONS = 5;

  /** Specifies the top inset for the steps. */
  public static final int TOP_INSET_STEP = 15;

  /** Specifies the left inset for the steps. */
  public static final int LEFT_INSET_STEP = 5;

  /** Specifies the extra left inset for the sub-steps. */
  public static final int LEFT_INSET_SUBSTEP = 20;
  /** Specifies the top inset for the instructions sub panel. */
  public static final int TOP_INSET_INSTRUCTIONS_SUBPANEL = 5;

  /** Specifies the top inset for input subpanel. */
  public static final int TOP_INSET_INPUT_SUBPANEL = 10;

  /** Specifies the top inset for a primary field. */
  public static final int TOP_INSET_PRIMARY_FIELD = 10;

  /** Specifies the top inset for a secondary field. */
  public static final int TOP_INSET_SECONDARY_FIELD = 5;

  /** Specifies the top inset for a radio button. */
  public static final int TOP_INSET_RADIOBUTTON = 0;

  /** Specifies the top inset for a radio button subordinate panel. */
  public static final int TOP_INSET_RADIO_SUBORDINATE = 0;

  /** Specifies the top inset for the progress bar. */
  public static final int TOP_INSET_PROGRESS_BAR = 5;

  /** Specifies the top inset for the progress text area. */
  public static final int TOP_INSET_PROGRESS_TEXTAREA = 4;

  /** Specifies the top inset for the background image. */
  public static final int TOP_INSET_BACKGROUND = 70;

  /** Specifies the top inset for the error message. */
  public static final int TOP_INSET_ERROR_MESSAGE = 10;

  /** Specifies the top inset for the browse button. */
  public static final int TOP_INSET_BROWSE = 5;

  /** Specifies the right inset for background image. */
  public static final int RIGHT_INSET_BACKGROUND = 20;

  /** Specifies the left inset for the primary field. */
  public static final int LEFT_INSET_PRIMARY_FIELD = 10;

  /** Specifies the left inset for the browse button. */
  public static final int LEFT_INSET_BROWSE = 10;

  /** Specifies the left inset for radio subordinate panel. */
  public static final int LEFT_INSET_RADIO_SUBORDINATE = 35;

  /** Specifies the left inset for the secondary field. */
  public static final int LEFT_INSET_SECONDARY_FIELD = 5;

  /** Specifies the left inset for the background image. */
  public static final int LEFT_INSET_BACKGROUND = 20;

  /** Specifies the left inset for the copy url button. */
  public static final int LEFT_INSET_COPY_BUTTON = 10;

  /** Specifies the left inset for a subordinate subpanel. */
  public static final int LEFT_INSET_SUBPANEL_SUBORDINATE = 30;

  /** Specifies the left inset for the progress bar. */
  public static final int BOTTOM_INSET_PROGRESS_BAR = 10;

  /** Specifies the bottom inset for the background image. */
  public static final int BOTTOM_INSET_BACKGROUND = 30;

  /** Specifies the top inset for a secondary field. */
  public static final int BOTTOM_INSET_SECONDARY_FIELD = 5;

  /** Specifies the number of columns of a text field for a path. */
  public static final int PATH_FIELD_SIZE = 20;

  /** Specifies the number of columns of a text field for a relative path. */
  public static final int RELATIVE_PATH_FIELD_SIZE = 10;

  /** Specifies the number of columns of a text field for a host name. */
  public static final int HOST_FIELD_SIZE = 20;

  /** Specifies the number of columns of a text field for a UID. */
  public static final int UID_FIELD_SIZE = 15;

  /** Specifies the number of columns of a text field for a port. */
  public static final int PORT_FIELD_SIZE = 5;

  /** Specifies the number of columns of a text field for a dn. */
  public static final int DN_FIELD_SIZE = 20;

  /** Specifies the number of columns of a text field for a password. */
  public static final int PASSWORD_FIELD_SIZE = 15;

  /**
   * Specifies the number of columns of a text field for the number of entries.
   */
  public static final int NUMBER_ENTRIES_FIELD_SIZE = 7;

  /** Specifies the number of points for the width of the progress bar. */
  public static final int PROGRESS_BAR_SIZE = 220;

  /**
   * Specifies the number of extra points that we add to the minimum size of the
   * dialog.
   */
  public static final int EXTRA_DIALOG_HEIGHT = 75;

  private static final Insets BUTTONS_PANEL_INSETS = new Insets(5, 0, 5, 10);

  private static final Insets STEPS_PANEL_INSETS = new Insets(15, 10, 5, 10);

  private static final Insets CURRENT_STEP_PANEL_INSETS = new Insets(15, 15, 15, 15);

  private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0);

  /** Specifies the default background color. */
  public static final Color DEFAULT_BACKGROUND = getColor(INFO_DEFAULT_BACKGROUND_COLOR.get());

  /** Specifies the current step background color. */
  public static final Color CURRENT_STEP_PANEL_BACKGROUND = getColor(INFO_CURRENT_STEP_PANEL_BACKGROUND_COLOR.get());

  /** Specifies the default label color. */
  public static final Color DEFAULT_LABEL_COLOR = getColor(INFO_DEFAULT_LABEL_COLOR.get());

  /** Specifies the valid field color. */
  public static final Color FIELD_VALID_COLOR = getColor(INFO_FIELD_VALID_COLOR.get());

  /** Specifies the invalid field color. */
  public static final Color FIELD_INVALID_COLOR = getColor(INFO_FIELD_INVALID_COLOR.get());

  /** Specifies the read only text color. */
  public static final Color READ_ONLY_COLOR = getColor(INFO_READ_ONLY_COLOR.get());

  /** Specifies the check box text color. */
  public static final Color CHECKBOX_COLOR = getColor(INFO_CHECKBOX_COLOR.get());

  /** Specifies the progress text color. */
  public static final Color PROGRESS_COLOR = getColor(INFO_PROGRESS_COLOR.get());

  /** Specifies the instructions text color. */
  public static final Color INSTRUCTIONS_COLOR = getColor(INFO_INSTRUCTIONS_COLOR.get());

  /** Specifies the text field text color. */
  public static final Color TEXTFIELD_COLOR = getColor(INFO_TEXTFIELD_COLOR.get());

  /** Specifies the password field text color. */
  public static final Color PASSWORDFIELD_COLOR = getColor(INFO_PASSWORDFIELD_COLOR.get());

  /** Specifies the in-line help text color. */
  public static final Color INLINE_HELP_COLOR = getColor(INFO_INLINE_HELP_COLOR.get());

  /** Specifies the panel border color. */
  public static final Color PANEL_BORDER_COLOR = getColor(INFO_PANEL_BORDER_COLOR.get());

  /** Specifies the current step panel border. */
  public static final Border CURRENT_STEP_PANEL_BORDER =
      BorderFactory.createMatteBorder(0, 2, 2, 0, PANEL_BORDER_COLOR);

  /** Specifies the text area border. */
  public static final Border TEXT_AREA_BORDER =
      BorderFactory.createMatteBorder(1, 1, 1, 1, getColor(INFO_TEXT_AREA_BORDER_COLOR.get()));

  /** Specifies the dialog border. */
  public static final Border DIALOG_PANEL_BORDER = BorderFactory.createMatteBorder(0, 0, 2, 0, PANEL_BORDER_COLOR);

  private static Font DEFAULT_FONT;
  static
  {
    try
    {
      DEFAULT_FONT = UIManager.getFont("Label.font").deriveFont(Font.PLAIN).deriveFont(12f);
    }
    catch (Throwable t)
    {
      DEFAULT_FONT = Font.decode("SansSerif-PLAIN-12");
    }
  }

  /**
   * Specifies the font for the step which is not the current one in the steps
   * panel.
   */
  public static final Font NOT_CURRENT_STEP_FONT = DEFAULT_FONT.deriveFont(14f);

  /**
   * Specifies the font for the step which is the current one in the steps
   * panel.
   */
  public static final Font CURRENT_STEP_FONT = DEFAULT_FONT.deriveFont(14f).deriveFont(Font.BOLD);

  /** Specifies the font for the title of the current panel. */
  public static final Font TITLE_FONT = DEFAULT_FONT.deriveFont(14f).deriveFont(Font.BOLD);

  /** Specifies the font for the instructions of the current panel. */
  public static final Font INSTRUCTIONS_FONT = DEFAULT_FONT;

  /** Specifies the font for the instructions of the current panel. */
  public static final Font INSTRUCTIONS_MONOSPACE_FONT = Font.decode("Monospaced-PLAIN-14");

  /** Specifies the font for the primary valid field. */
  public static final Font PRIMARY_FIELD_VALID_FONT = DEFAULT_FONT.deriveFont(Font.BOLD);

  /** Specifies the font for the secondary valid field. */
  public static final Font SECONDARY_FIELD_VALID_FONT = DEFAULT_FONT;

  /** Specifies the font for the primary invalid field. */
  public static final Font PRIMARY_FIELD_INVALID_FONT = DEFAULT_FONT.deriveFont(Font.BOLD | Font.ITALIC);

  /** Specifies the font for the secondary invalid field. */
  public static final Font SECONDARY_FIELD_INVALID_FONT = DEFAULT_FONT.deriveFont(Font.ITALIC);

  /** Specifies the font for the secondary status field. */
  public static final Font SECONDARY_STATUS_FONT = DEFAULT_FONT.deriveFont(Font.ITALIC);

  /** Specifies the font for read only text. */
  public static final Font READ_ONLY_FONT = DEFAULT_FONT;

  /** Specifies the font for the check box text. */
  public static final Font CHECKBOX_FONT = DEFAULT_FONT;

  /** Specifies the font for the progress text. */
  public static final Font PROGRESS_FONT = DEFAULT_FONT;

  /** Specifies the font for the text field text. */
  public static final Font TEXTFIELD_FONT = DEFAULT_FONT;

  /** Specifies the font for the password field text. */
  public static final Font PASSWORD_FIELD_FONT = DEFAULT_FONT;

  /** Specifies the font for the points '....' in the progress panel. */
  public static final Font PROGRESS_POINTS_FONT = DEFAULT_FONT.deriveFont(Font.BOLD);

  /** Specifies the font for the done text 'Done' in the progress panel. */
  public static final Font PROGRESS_DONE_FONT = PROGRESS_POINTS_FONT;

  /** Specifies the font for the log messages in the progress panel. */
  public static final Font PROGRESS_LOG_FONT = Font.decode("Monospaced-PLAIN-12");

  /** Specifies the font for the error log messages in the progress panel. */
  public static final Font PROGRESS_LOG_ERROR_FONT = Font.decode("Monospaced-PLAIN-12");

  /** Specifies the font for the error messages in the progress panel. */
  public static final Font PROGRESS_ERROR_FONT = DEFAULT_FONT.deriveFont(Font.BOLD);

  /** Specifies the font for the warning messages in the progress panel. */
  public static final Font PROGRESS_WARNING_FONT = DEFAULT_FONT.deriveFont(Font.BOLD);

  /** Specifies the font for the stack trace in the progress panel. */
  public static final Font STACK_FONT = DEFAULT_FONT;

  /** Specifies the font for the text in the WebBrowserErrorDialog. */
  public static final Font ERROR_DIALOG_FONT = DEFAULT_FONT;

  /** Specifies the font for the text in the in-line help. */
  public static final Font INLINE_HELP_FONT = DEFAULT_FONT.deriveFont((float) (DEFAULT_FONT.getSize() - 2));

  private static final String SPAN_CLOSE = "</span>";

  private static final String DIV_CLOSE = "</div>";

  private static final String DIV_OPEN_ERROR_BACKGROUND =
    "<div style=\"color:#"+
    INFO_DIV_OPEN_ERROR_BACKGROUND_1_COLOR.get()+
    ";background-color:#"+
    INFO_DIV_OPEN_ERROR_BACKGROUND_2_COLOR.get()+
    ";padding:10px 10px 10px 10px;"+
    "border-style:solid;border-width:3px;border-color:#"+
    INFO_DIV_OPEN_ERROR_BACKGROUND_3_COLOR.get()+
    ";vertical-align:middle;text-align:left\">";

  private static final String DIV_OPEN_WARNING_BACKGROUND = DIV_OPEN_ERROR_BACKGROUND;

  private static final String DIV_OPEN_SUCCESSFUL_BACKGROUND =
    "<div style=\"color:#"+
    INFO_DIV_OPEN_SUCCESSFUL_BACKGROUND_1_COLOR.get()+
    ";background-color:#"+
    INFO_DIV_OPEN_SUCCESSFUL_BACKGROUND_2_COLOR.get()+
    ";padding:10px 10px 10px 10px;"+
    "border-style:solid;border-width:3px;border-color:#"+
    INFO_DIV_OPEN_SUCCESSFUL_BACKGROUND_3_COLOR.get()+
    ";vertical-align:middle;text-align:left\">";

  /** An HTML separator text that can be used in the progress panel. */
  public static final String HTML_SEPARATOR =
    "<div style=\"font-size:1px;background-color:#"+
    INFO_HTML_SEPARATOR_COLOR.get()+
    ";margin:10px 5px 10px 5px;\"></div>";

  private static final HashMap<IconType, ImageIcon> hmIcons = new HashMap<>();

  /** The following enumeration contains the different icons that we can have. */
  public enum IconType
  {
    /** Splash Icon. */
    SPLASH,
    /** Current Step Icon. */
    CURRENT_STEP,
    /** The icon displayed by the OS when the dialog is minimized. */
    MINIMIZED,
    /** The icon displayed by the Mac OS when the dialog is minimized. */
    MINIMIZED_MAC,
    /** The background icon. */
    BACKGROUND,
    /** The warning icon. */
    WARNING,
    /** The warning large icon. */
    WARNING_LARGE,
    /** The error icon. */
    ERROR,
    /** The error large icon. */
    ERROR_LARGE,
    /** The information icon. */
    INFORMATION,
    /** The information large icon. */
    INFORMATION_LARGE,
    /** Icon to create subsection title in Status Panel. */
    SUBSECTION_LEFT,
    /** Icon to create subsection title in Status Panel. */
    SUBSECTION_RIGHT,
    /** Question icon. */
    HELP_SMALL,
    /** Question medium icon. */
    HELP_MEDIUM,
    /** Hourglass to display when the user must wait. */
    WAIT,
    /** 8 x 8 Hourglass to display when the user must wait. */
    WAIT_TINY,
    /** No icon. */
    NO_ICON
  }

  /**
   * The following enumeration contains the different text styles that we can
   * have. A text style basically specifies the font and color to be used to
   * render the text.
   */
  public enum TextStyle
  {
    /** Current Step label style for the steps panel. */
    CURRENT_STEP,
    /** Not current Step label style for the steps panel. */
    NOT_CURRENT_STEP,
    /** Title label style for the current step panel. */
    TITLE,
    /** Primary field valid label style for the current step panel. */
    PRIMARY_FIELD_VALID,
    /** Primary field invalid text style for the current step panel. */
    PRIMARY_FIELD_INVALID,
    /** Secondary field valid text style for the current step panel. */
    SECONDARY_FIELD_VALID,
    /** Secondary field invalid text style for the current step panel. */
    SECONDARY_FIELD_INVALID,
    /** Status messages that appear near components. */
    SECONDARY_STATUS,
    /** Textfield text style for the current step panel. */
    TEXTFIELD,
    /** Password text style for the current step panel. */
    PASSWORD_FIELD,
    /** Read only text style for the current step panel. */
    READ_ONLY,
    /** Check box text text style for the current step panel. */
    CHECKBOX,
    /** Progress messages text style for the current step panel. */
    PROGRESS,
    /** Text style for the instructions. */
    INSTRUCTIONS,
    /** In-line help style. */
    INLINE_HELP,
    /** No text style. */
    NO_STYLE
  }

  /**
   * This method initialize the look and feel.
   *
   * @throws Throwable
   *           if there is a problem initializing the look and feel.
   */
  public static void initializeLookAndFeel() throws Throwable
  {
    final Throwable[] ts = { null };
    Runnable r = new Runnable()
    {
      @Override
      public void run()
      {
        System.setProperty("swing.aatext", "true");
        try
        {
          String lf = UIManager.getSystemLookAndFeelClassName();
          if ("com.sun.java.swing.plaf.motif.MotifLookAndFeel".equalsIgnoreCase(lf))
          {
            lf = UIManager.getCrossPlatformLookAndFeelClassName();
          }
          UIManager.setLookAndFeel(lf);
        }
        catch (Throwable t)
        {
          ts[0] = t;
        }
        JFrame.setDefaultLookAndFeelDecorated(false);
      }
    };
    if (SwingUtilities.isEventDispatchThread())
    {
      r.run();
    }
    else
    {
      try
      {
        SwingUtilities.invokeAndWait(r);
      }
      catch (Throwable t)
      {
        ts[0] = t;
      }
    }
    if (ts[0] != null)
    {
      throw ts[0];
    }
  }

  /**
   * This method initialize the look and feel and UI settings specific to quick
   * setup.
   *
   * @throws Throwable
   *           if there is a problem initializing the look and feel.
   */
  public static void initialize() throws Throwable
  {
    if (!initialized)
    {
      try
      {
        UIManager.put("OptionPane.background", getColor(INFO_OPTIONPANE_BACKGROUND_COLOR.get()));
        UIManager.put("Panel.background", getColor(INFO_PANEL_BACKGROUND_COLOR.get()));
        UIManager.put("ComboBox.background", getColor(INFO_COMBOBOX_BACKGROUND_COLOR.get()));
      }
      catch (Throwable t)
      {
        // This might occur when we do not get the display
        logger.warn(LocalizableMessage.raw("Error updating UIManager: " + t, t));
      }
      initializeLookAndFeel();
      initialized = true;
    }
  }

  /**
   * Creates a new JPanel.
   *
   * @return JPanel newly created
   */
  public static JPanel makeJPanel()
  {
    JPanel pnl = new JPanel();
    pnl.setOpaque(false);
    return pnl;
  }

  /**
   * Creates a JButton with the given label and tooltip.
   *
   * @param label
   *          the text of the button.
   * @param tooltip
   *          the tooltip of the button.
   * @return a JButton with the given label and tooltip.
   */
  public static JButton makeJButton(LocalizableMessage label, LocalizableMessage tooltip)
  {
    JButton b = new JButton();

    if (label != null)
    {
      b.setText(label.toString());
    }

    if (tooltip != null)
    {
      b.setToolTipText(tooltip.toString());
    }

    b.setOpaque(false);

    return b;
  }

  /**
   * Commodity method that returns a JLabel based on a LabelFieldDescriptor.
   *
   * @param desc
   *          the LabelFieldDescriptor describing the JLabel.
   * @return a JLabel based on a LabelFieldDescriptor.
   */
  public static JLabel makeJLabel(LabelFieldDescriptor desc)
  {
    UIFactory.TextStyle style;
    if (desc.getLabelType() == LabelFieldDescriptor.LabelType.PRIMARY)
    {
      style = UIFactory.TextStyle.PRIMARY_FIELD_VALID;
    }
    else
    {
      style = UIFactory.TextStyle.SECONDARY_FIELD_VALID;
    }
    return makeJLabel(UIFactory.IconType.NO_ICON, desc.getLabel(), style);
  }

  /**
   * Creates a JLabel with the given icon, text and text style.
   *
   * @param iconName
   *          the icon.
   * @param text
   *          the label text.
   * @param style
   *          the text style.
   * @return a JLabel with the given icon, text and text style.
   */
  public static JLabel makeJLabel(IconType iconName, LocalizableMessage text, TextStyle style)
  {
    JLabel l = new JLabel();

    if (text != null)
    {
      l.setText(text.toString());
    }

    ImageIcon icon = getImageIcon(iconName);
    l.setIcon(icon);
    LocalizableMessage tooltip = getIconTooltip(iconName);

    if (tooltip != null)
    {
      l.setToolTipText(tooltip.toString());
    }

    setTextStyle(l, style);
    return l;
  }

  /**
   * Commodity method that returns a JTextComponent based on a
   * LabelFieldDescriptor.
   *
   * @param desc
   *          the LabelFieldDescriptor describing the JTextField.
   * @param defaultValue
   *          the default value used to initialize the JTextComponent.
   * @return a JTextComponent based on a LabelFieldDescriptor.
   */
  public static JTextComponent makeJTextComponent(LabelFieldDescriptor desc, String defaultValue)
  {
    if (defaultValue == null)
    {
      defaultValue = "";
    }
    switch (desc.getType())
    {
    case TEXTFIELD:
      return makeJTextField(
          LocalizableMessage.raw(defaultValue), desc.getTooltip(), desc.getSize(), TextStyle.TEXTFIELD);

    case PASSWORD:
      return makeJPasswordField(
          LocalizableMessage.raw(defaultValue), desc.getTooltip(), desc.getSize(), TextStyle.PASSWORD_FIELD);

    case READ_ONLY:
      return makeTextPane(LocalizableMessage.raw(defaultValue), TextStyle.READ_ONLY);

    default:
      throw new IllegalArgumentException("Unknown type: " + desc.getType());
    }
  }

  /**
   * Creates a JTextField with the given icon, tooltip text, size and text
   * style.
   *
   * @param text
   *          the text.
   * @param tooltip
   *          the tooltip text.
   * @param size
   *          the number of columns of the JTextField.
   * @param style
   *          the text style.
   * @return a JTextField with the given icon, tooltip text, size and text
   *         style.
   */
  public static JTextField makeJTextField(
      LocalizableMessage text, LocalizableMessage tooltip, int size, TextStyle style)
  {
    JTextField f = new JTextField();
    updateTextFieldComponent(f, text, tooltip, size, style);
    f.addFocusListener(new TextFieldFocusListener(f));
    return f;
  }

  /**
   * Creates a JPasswordField with the given icon, tooltip text, size and text
   * style.
   *
   * @param text
   *          the text.
   * @param tooltip
   *          the tooltip text.
   * @param size
   *          the number of columns of the JPasswordField.
   * @param style
   *          the text style.
   * @return a JPasswordField with the given icon, tooltip text, size and text
   *         style.
   */
  public static JPasswordField makeJPasswordField(
      LocalizableMessage text, LocalizableMessage tooltip, int size, TextStyle style)
  {
    JPasswordField f = new JPasswordField();
    updateTextFieldComponent(f, text, tooltip, size, style);
    f.addFocusListener(new TextFieldFocusListener(f));
    return f;
  }

  /**
   * Creates a JRadioButton with the given text, tooltip text and text style.
   *
   * @param text
   *          the text of the radio button.
   * @param tooltip
   *          the tooltip text.
   * @param style
   *          the text style.
   * @return a JRadioButton with the given text, tooltip text and text style.
   */
  public static JRadioButton makeJRadioButton(LocalizableMessage text, LocalizableMessage tooltip, TextStyle style)
  {
    JRadioButton rb = new JRadioButton();
    rb.setOpaque(false);
    if (text != null)
    {
      rb.setText(text.toString());
    }

    if (tooltip != null)
    {
      rb.setToolTipText(tooltip.toString());
    }

    setTextStyle(rb, style);
    return rb;
  }

  /**
   * Creates a JCheckBox with the given text, tooltip text and text style.
   *
   * @param text
   *          the text of the radio button.
   * @param tooltip
   *          the tooltip text.
   * @param style
   *          the text style.
   * @return a JCheckBox with the given text, tooltip text and text style.
   */
  public static JCheckBox makeJCheckBox(LocalizableMessage text, LocalizableMessage tooltip, TextStyle style)
  {
    JCheckBox cb = new JCheckBox();
    cb.setOpaque(false);
    if (text != null)
    {
      cb.setText(text.toString());
    }

    if (tooltip != null)
    {
      cb.setToolTipText(tooltip.toString());
    }

    setTextStyle(cb, style);
    return cb;
  }

  /**
   * Creates a JList.
   *
   * @param textStyle
   *          the style to be used for the renderer.
   * @param <T>
   *          The type of the JList elements
   * @return a JList.
   */
  public static <T> JList<T> makeJList(TextStyle textStyle)
  {
    final JList<T> list = new JList<>();
    list.setCellRenderer(makeCellRenderer(textStyle));
    return list;
  }

  /**
   * Sets the specified text style to the component passed as parameter.
   *
   * @param l
   *          the component to update.
   * @param style
   *          the text style to use.
   */
  public static void setTextStyle(JComponent l, TextStyle style)
  {
    switch (style)
    {
    case NOT_CURRENT_STEP:
      l.setFont(UIFactory.NOT_CURRENT_STEP_FONT);
      l.setForeground(DEFAULT_LABEL_COLOR);
      break;

    case CURRENT_STEP:
      l.setFont(UIFactory.CURRENT_STEP_FONT);
      l.setForeground(DEFAULT_LABEL_COLOR);
      break;

    case TITLE:
      l.setFont(UIFactory.TITLE_FONT);
      l.setForeground(DEFAULT_LABEL_COLOR);
      break;

    case PRIMARY_FIELD_VALID:
      l.setFont(UIFactory.PRIMARY_FIELD_VALID_FONT);
      l.setForeground(FIELD_VALID_COLOR);
      break;

    case PRIMARY_FIELD_INVALID:
      l.setFont(UIFactory.PRIMARY_FIELD_INVALID_FONT);
      l.setForeground(FIELD_INVALID_COLOR);
      break;

    case SECONDARY_FIELD_VALID:
      l.setFont(UIFactory.SECONDARY_FIELD_VALID_FONT);
      l.setForeground(FIELD_VALID_COLOR);
      break;

    case SECONDARY_FIELD_INVALID:
      l.setFont(UIFactory.SECONDARY_FIELD_INVALID_FONT);
      l.setForeground(FIELD_INVALID_COLOR);
      break;

    case SECONDARY_STATUS:
      l.setFont(UIFactory.SECONDARY_STATUS_FONT);
      l.setForeground(FIELD_VALID_COLOR);
      break;

    case READ_ONLY:
      l.setFont(UIFactory.READ_ONLY_FONT);
      l.setForeground(READ_ONLY_COLOR);
      break;

    case CHECKBOX:
      l.setFont(UIFactory.CHECKBOX_FONT);
      l.setForeground(CHECKBOX_COLOR);
      break;

    case PROGRESS:
      l.setFont(UIFactory.PROGRESS_FONT);
      l.setForeground(PROGRESS_COLOR);
      break;

    case INSTRUCTIONS:
      l.setFont(INSTRUCTIONS_FONT);
      l.setForeground(INSTRUCTIONS_COLOR);
      break;

    case TEXTFIELD:
      l.setFont(UIFactory.TEXTFIELD_FONT);
      l.setForeground(TEXTFIELD_COLOR);
      break;

    case PASSWORD_FIELD:
      l.setFont(UIFactory.PASSWORD_FIELD_FONT);
      l.setForeground(PASSWORDFIELD_COLOR);
      break;

    case INLINE_HELP:
      l.setFont(INLINE_HELP_FONT);
      l.setForeground(INLINE_HELP_COLOR);
      break;

    case NO_STYLE:
      // Do nothing
      break;

    default:
      throw new IllegalArgumentException("Unknown textStyle: " + style);
    }
  }

  /**
   * Returns the HTML string representing the provided IconType.
   *
   * @param iconType
   *          the IconType for which we want the HTML representation.
   * @return the HTML string representing the provided IconType.
   */
  public static String getIconHtml(IconType iconType)
  {
    String url = String.valueOf(UIFactory.class.getClassLoader().getResource(getIconPath(iconType)));
    LocalizableMessage description = getIconDescription(iconType);
    LocalizableMessage title = getIconTooltip(iconType);
    return "<img src=\"" + url + "\" alt=\"" + description + "\" align=\"middle\" title=\"" + title + "\" >";
  }

  /**
   * Returns an ImageIcon object for the provided IconType.
   *
   * @param iconType
   *          the IconType for which we want to obtain the ImageIcon.
   * @return the ImageIcon.
   */
  public static ImageIcon getImageIcon(IconType iconType)
  {
    if (iconType == null)
    {
      iconType = IconType.NO_ICON;
    }
    ImageIcon icon = hmIcons.get(iconType);
    if (icon == null && iconType != IconType.NO_ICON)
    {
      String path = getIconPath(iconType);
      LocalizableMessage description = getIconDescription(iconType);
      try
      {
        Image im = Toolkit.getDefaultToolkit().createImage(UIFactory.class.getClassLoader().getResource(path));
        icon = new ImageIcon(im);
        String ds = description != null ? description.toString() : null;
        icon.setDescription(ds);

        hmIcons.put(iconType, icon);
      }
      catch (Exception ex)
      {
        ex.printStackTrace(); // A bug: this should not happen
        throw new IllegalStateException("Could not load icon for path " + path, ex);
      }
    }

    return icon;
  }

  /**
   * Returns a JEditorPane that works with the provided scroll.
   *
   * @see ProgressJEditorPane
   * @param scroll
   *          the scroll that will contain the JEditorPane.
   * @return a JEditorPane that works with the provided scroll.
   */
  public static JEditorPane makeProgressPane(JScrollPane scroll)
  {
    return new ProgressJEditorPane(scroll);
  }

  /**
   * Returns a read only JEditorPane containing the provided text with the
   * provided font. The JEditorPane will assume that the text is HTML text.
   *
   * @param text
   *          the text to be used to initialize the JEditorPane contents.
   * @param font
   *          the font to be used.
   * @return a read only JEditorPane containing the provided text with the
   *         provided font.
   */
  public static JEditorPane makeHtmlPane(LocalizableMessage text, Font font)
  {
    return makeHtmlPane(text, null, font);
  }

  /**
   * Returns a read only JEditorPane containing the provided text with the
   * provided font. The JEditorPane will assume that the text is HTML text.
   *
   * @param text
   *          the text to be used to initialize the JEditorPane contents.
   * @param ek
   *          HTMLEditor kit used for the new HTML pane
   * @param font
   *          the font to be used.
   * @return a read only JEditorPane containing the provided text with the
   *         provided font.
   */
  public static JEditorPane makeHtmlPane(LocalizableMessage text, HTMLEditorKit ek, Font font)
  {
    JEditorPane pane = new JEditorPane();
    if (ek != null) {
        pane.setEditorKit(ek);
    }
    pane.setContentType("text/html");
    String s = text != null ? String.valueOf(text) : null;
    pane.setText(applyFontToHtmlWithDiv(s, font));
    pane.setEditable(false);
    pane.setBorder(new EmptyBorder(0, 0, 0, 0));
    return pane;
  }

  /**
   * Returns a read only JEditorPane containing the provided text with the
   * provided TextStyle. The JEditorPane will assume that the text is plain
   * text.
   *
   * @param text
   *          the text to be used to initialize the JEditorPane contents.
   * @param style
   *          the TextStyle to be used.
   * @return a read only JEditorPane containing the provided text with the
   *         provided TextStyle.
   */
  public static JEditorPane makeTextPane(LocalizableMessage text, TextStyle style)
  {
    String s = text != null ? String.valueOf(text) : null;
    JEditorPane pane = new JEditorPane("text/plain", s);
    setTextStyle(pane, style);
    pane.setEditable(false);
    pane.setBorder(new EmptyBorder(0, 0, 0, 0));
    pane.setOpaque(false);
    return pane;
  }

  /**
   * Returns a JScrollPane that contains the provided component. The scroll pane
   * will not contain any border.
   *
   * @param comp
   *          the component contained in the scroll pane.
   * @return a JScrollPane that contains the provided component. The scroll pane
   *         will not contain any border.
   */
  public static JScrollPane createBorderLessScrollBar(Component comp)
  {
    JScrollPane scroll = new JScrollPane(comp);
    scroll.setBorder(new EmptyBorder(0, 0, 0, 0));
    scroll.setViewportBorder(new EmptyBorder(0, 0, 0, 0));
    scroll.setOpaque(false);
    scroll.getViewport().setOpaque(false);
    scroll.getViewport().setBackground(DEFAULT_BACKGROUND);
    scroll.setBackground(DEFAULT_BACKGROUND);
    setScrollIncrementUnit(scroll);
    return scroll;
  }

  /**
   * Sets the scroll increment unit for the scroll.
   *
   * @param scroll
   *          the scroll to be updated.
   */
  public static void setScrollIncrementUnit(JScrollPane scroll)
  {
    if (scroll.getVerticalScrollBar() != null)
    {
      int increment = scroll.getVerticalScrollBar().getUnitIncrement();
      if (increment < 16)
      {
        scroll.getVerticalScrollBar().setUnitIncrement(16);
      }
    }
  }

  /**
   * Return empty insets.
   *
   * @return empty insets.
   */
  public static Insets getEmptyInsets()
  {
    return (Insets) EMPTY_INSETS.clone();
  }

  /**
   * Returns the insets to be used for the button panel.
   *
   * @return the insets to be used for the button panel.
   */
  public static Insets getButtonsPanelInsets()
  {
    return (Insets) BUTTONS_PANEL_INSETS.clone();
  }

  /**
   * Returns the insets to be used for the steps panel.
   *
   * @return the insets to be used for the steps panel.
   */
  public static Insets getStepsPanelInsets()
  {
    return (Insets) STEPS_PANEL_INSETS.clone();
  }

  /**
   * Returns the insets to be used for the current step panel.
   *
   * @return the insets to be used for the current step panel.
   */
  public static Insets getCurrentStepPanelInsets()
  {
    return (Insets) CURRENT_STEP_PANEL_INSETS.clone();
  }

  /**
   * Returns a String that contains the html passed as parameter with a span
   * applied. The span style corresponds to the Font specified as parameter. The
   * goal of this method is to be able to specify a font for an HTML string.
   *
   * @param html
   *          the original html text.
   * @param font
   *          the font to be used to generate the new HTML.
   * @return a string that represents the original HTML with the font specified
   *         as parameter.
   */
  public static String applyFontToHtml(String html, Font font)
  {
    StringBuilder buf = new StringBuilder();

    buf.append("<span style=\"").append(getFontStyle(font)).append("\">").append(html).append(SPAN_CLOSE);

    return buf.toString();
  }

  /**
   * Returns a String that contains the html passed as parameter with a div
   * applied. The div style corresponds to the Font specified as parameter. The
   * goal of this method is to be able to specify a font for an HTML string.
   *
   * @param html
   *          the original html text.
   * @param font
   *          the font to be used to generate the new HTML.
   * @return a string that represents the original HTML with the font specified
   *         as parameter.
   */
  public static String applyFontToHtmlWithDiv(String html, Font font)
  {
    StringBuilder buf = new StringBuilder();

    buf.append("<div style=\"").append(getFontStyle(font)).append("\">").append(html).append(DIV_CLOSE);

    return buf.toString();
  }

  /**
   * Returns the HTML style representation for the given font.
   *
   * @param font
   *          the font for which we want to get an HTML style representation.
   * @return the HTML style representation for the given font.
   */
  private static String getFontStyle(Font font)
  {
    StringBuilder buf = new StringBuilder();

    buf.append("font-family:").append(font.getName()).append(";font-size:").append(font.getSize()).append("pt");

    if (font.isItalic())
    {
      buf.append(";font-style:italic");
    }

    if (font.isBold())
    {
      buf.append(";font-weight:bold;");
    }

    return buf.toString();
  }

  /**
   * Returns the html text passed as parameter with the error background applied
   * to it.
   *
   * @param html
   *          the original html.
   * @return the html text passed as parameter with the error background applied
   *         to it.
   */
  public static String applyErrorBackgroundToHtml(String html)
  {
    return DIV_OPEN_ERROR_BACKGROUND + html + DIV_CLOSE;
  }

  /**
   * Returns the html text passed as parameter with the warning background
   * applied to it.
   *
   * @param html
   *          the original html.
   * @return the html text passed as parameter with the warning background
   *         applied to it.
   */
  public static String applyWarningBackgroundToHtml(String html)
  {
    return DIV_OPEN_WARNING_BACKGROUND + html + DIV_CLOSE;
  }

  /**
   * Returns the html text passed as parameter with the success background
   * applied to it.
   *
   * @param html
   *          the original html.
   * @return the html text passed as parameter with the success background
   *         applied to it.
   */
  public static String applySuccessfulBackgroundToHtml(String html)
  {
    return DIV_OPEN_SUCCESSFUL_BACKGROUND + html + DIV_CLOSE;
  }

  /**
   * Returns the html text passed as parameter with some added margin.
   *
   * @param html
   *          the original html text.
   * @param top
   *          the top margin.
   * @param right
   *          the right margin.
   * @param bottom
   *          the bottom margin.
   * @param left
   *          the left margin.
   * @return the html text passed as parameter with some added margin.
   */
  public static String applyMargin(String html, int top, int right, int bottom, int left)
  {
    return "<div style=\"margin:" + top + "px " + right + "px " + bottom + "px " + left + "px;\">" + html + DIV_CLOSE;
  }

  /**
   * Updates the provided field with all the other arguments.
   *
   * @param field
   *          the field to be modified.
   * @param text
   *          the new text of the field.
   * @param tooltip
   *          the new tooltip text of the field.
   * @param size
   *          the new size of the field.
   * @param textStyle
   *          the new TextStyle of the field.
   */
  private static void updateTextFieldComponent(
      JTextField field, LocalizableMessage text, LocalizableMessage tooltip, int size, TextStyle textStyle)
  {
    field.setColumns(size);
    if (text != null)
    {
      field.setText(text.toString());
    }
    if (tooltip != null)
    {
      field.setToolTipText(tooltip.toString());
    }
    if (textStyle != null)
    {
      setTextStyle(field, textStyle);
    }
  }

  private static Color getColor(LocalizableMessage l)
  {
    String s = String.valueOf(l);
    String[] colors = s.split(",");
    int r = Integer.parseInt(colors[0].trim());
    int g = Integer.parseInt(colors[1].trim());
    int b = Integer.parseInt(colors[2].trim());

    return new Color(r, g, b);
  }

  /**
   * Returns the parent package path. This is used to retrieve the icon
   * qualified names.
   *
   * @return the parent package path.
   */
  private static String getParentPackagePath()
  {
    if (parentPackagePath == null)
    {
      String packageName = UIFactory.class.getPackage().getName();
      int lastDot = packageName.lastIndexOf('.');
      String parentPackage = packageName.substring(0, lastDot);
      parentPackagePath = parentPackage.replace(".", "/");
    }
    return parentPackagePath;
  }

  /**
   * Returns the path of the icon for the given IconType.
   *
   * @param iconType
   *          the IconType for which we want to get the path.
   * @return the path of the icon for the given IconType.
   */
  private static String getIconPath(IconType iconType)
  {
    return getParentPackagePath() + "/" + getKey(iconType);
  }

  private static LocalizableMessage getKey(IconType iconType)
  {
    switch (iconType)
    {
    case CURRENT_STEP:
      return INFO_CURRENT_STEP_ICON.get();
    case SPLASH:
      return INFO_SPLASH_ICON.get();
    case BACKGROUND:
      return INFO_BACKGROUND_ICON.get();
    case MINIMIZED:
      return INFO_MINIMIZED_ICON.get();
    case MINIMIZED_MAC:
      return INFO_MINIMIZED_MAC_ICON.get();
    case WARNING:
      return INFO_WARNING_ICON.get();
    case WARNING_LARGE:
      return INFO_WARNING_LARGE_ICON.get();
    case INFORMATION:
      return INFO_INFORMATION_ICON.get();
    case INFORMATION_LARGE:
      return INFO_INFORMATION_LARGE_ICON.get();
    case SUBSECTION_LEFT:
      return INFO_SUBSECTION_LEFT_ICON.get();
    case SUBSECTION_RIGHT:
      return INFO_SUBSECTION_RIGHT_ICON.get();
    case HELP_SMALL:
      return INFO_HELP_SMALL_ICON.get();
    case HELP_MEDIUM:
      return INFO_HELP_MEDIUM_ICON.get();
    case ERROR:
      return INFO_ERROR_ICON.get();
    case ERROR_LARGE:
      return INFO_ERROR_LARGE_ICON.get();
    case WAIT_TINY:
      return INFO_WAIT_TINY.get();
    case WAIT:
      return INFO_WAIT.get();
    default:
      throw new IllegalArgumentException("Unknown iconName: " + iconType);
    }
  }

  /**
   * Returns the icon description for the given IconType.
   *
   * @param iconType
   *          the IconType for which we want to get the description.
   * @return the icon description for the given IconType.
   */
  private static LocalizableMessage getIconDescription(IconType iconType)
  {
    switch (iconType)
    {
    case CURRENT_STEP:
      return INFO_CURRENT_STEP_ICON_DESCRIPTION.get();

    case SPLASH:
      return INFO_SPLASH_ICON_DESCRIPTION.get();

    case BACKGROUND:
      return INFO_BACKGROUND_ICON_DESCRIPTION.get();

    case MINIMIZED:
      return INFO_MINIMIZED_ICON_DESCRIPTION.get();

    case MINIMIZED_MAC:
      return INFO_MINIMIZED_ICON_DESCRIPTION.get();

    case WARNING:
      return INFO_WARNING_ICON_DESCRIPTION.get();

    case WARNING_LARGE:
      return INFO_WARNING_ICON_DESCRIPTION.get();

    case ERROR:
      return INFO_ERROR_ICON_DESCRIPTION.get();

    case ERROR_LARGE:
      return INFO_ERROR_ICON_DESCRIPTION.get();

    case INFORMATION:
      return INFO_INFORMATION_ICON_DESCRIPTION.get();

    case INFORMATION_LARGE:
      return INFO_INFORMATION_ICON_DESCRIPTION.get();

    case SUBSECTION_LEFT:
      return INFO_SUBSECTION_LEFT_ICON_DESCRIPTION.get();

    case SUBSECTION_RIGHT:
      return INFO_SUBSECTION_RIGHT_ICON_DESCRIPTION.get();

    case HELP_SMALL:
    case HELP_MEDIUM:
      return INFO_HELP_SMALL_ICON_DESCRIPTION.get();

    case WAIT_TINY:
      return INFO_HELP_WAIT_DESCRIPTION.get();

    case WAIT:
      return INFO_HELP_WAIT_DESCRIPTION.get();

    case NO_ICON:
      return null;

    default:
      throw new IllegalArgumentException("Unknown iconName: " + iconType);
    }
  }

  /**
   * Returns the icon tooltip text for the given IconType.
   *
   * @param iconType
   *          the IconType for which we want to get the tooltip text.
   * @return the icon tooltip text for the given IconType.
   */
  private static LocalizableMessage getIconTooltip(IconType iconType)
  {
    if (iconType == null)
    {
      iconType = IconType.NO_ICON;
    }
    switch (iconType)
    {
    case CURRENT_STEP:
      return INFO_CURRENT_STEP_ICON_TOOLTIP.get();

    case SPLASH:
      return INFO_SPLASH_ICON_TOOLTIP.get();

    case BACKGROUND:
      return INFO_BACKGROUND_ICON_TOOLTIP.get();

    case MINIMIZED:
      return INFO_MINIMIZED_ICON_TOOLTIP.get();

    case MINIMIZED_MAC:
      return INFO_MINIMIZED_ICON_TOOLTIP.get();

    case WARNING:
      return INFO_WARNING_ICON_TOOLTIP.get();

    case WARNING_LARGE:
      return INFO_WARNING_ICON_TOOLTIP.get();

    case ERROR:
      return INFO_ERROR_ICON_TOOLTIP.get();

    case ERROR_LARGE:
      return INFO_ERROR_ICON_TOOLTIP.get();

    case INFORMATION:
      return INFO_INFORMATION_ICON_TOOLTIP.get();

    case INFORMATION_LARGE:
      return INFO_INFORMATION_ICON_TOOLTIP.get();

    case SUBSECTION_LEFT:
    case SUBSECTION_RIGHT:
    case HELP_SMALL:
    case HELP_MEDIUM:
    case WAIT_TINY:
    case WAIT:
    case NO_ICON:
      return null;

    default:
      throw new IllegalArgumentException("Unknown iconName: " + iconType);
    }
  }

  private static <T> ListCellRenderer<T> makeCellRenderer(final TextStyle textStyle)
  {
    return new ListCellRenderer<T>()
    {
      @Override
      public Component getListCellRendererComponent(JList<? extends T> list, T value, int index, boolean isSelected,
          boolean cellHasFocus)
      {
        final JLabel l = makeJLabel(IconType.NO_ICON, LocalizableMessage.raw(value.toString()), textStyle);
        l.setBorder(new EmptyBorder(TOP_INSET_SECONDARY_FIELD, 0, 0, 0));
        return l;
      }
    };
  }
}

/**
 * This class has been written to have a better behaviour with the scroll pane
 * than the one we have by default in the case of the progress panel.
 * <p>
 * With the default scroll pane behaviour when we set a new text in a
 * JEditorPane the scroll bar goes systematically up.  With this implementation
 * the expected behaviour is:
 * <p>
 * If the scroll bar is at the bottom we will display the latest text contained
 * in the pane.
 * <p>
 * If the scroll bar is not at the bottom we will keep on displaying the same
 * thing that the user is viewing.
 * <p>
 * This behaviour allows the user to check the log content even when the
 * installation/uninstallation is still running and sending new log messages.
 */
class ProgressJEditorPane extends JEditorPane
{
  private static final long serialVersionUID = 1221976708322628818L;

  private final JScrollPane scroll;

  private boolean ignoreScrollToVisible;

  /**
   * Constructor for the ProgressJEditorPane.
   *
   * @param scroll
   *          the JScrollPane that will contain this editor pane.
   */
  public ProgressJEditorPane(JScrollPane scroll)
  {
    super("text/html", null);
    this.scroll = scroll;
    setEditable(false);
    setBorder(new EmptyBorder(3, 3, 3, 3));
  }

  @Override
  public void setText(String text)
  {
    // Scroll can be null in constructor
    if (scroll != null)
    {
      /*
       * We apply the following policy: if the user is displaying the latest
       * part of the JTextArea we assume that when we add text (s)he wants to
       * see the text that is added, if not we assume that (s)he want to keep
       * viewing what is visible and so we ignore the next scrollRectToVisible
       * call (that will be done inside JTextArea.setText method).
       */
      JScrollBar vBar = scroll.getVerticalScrollBar();
      ignoreScrollToVisible =
          vBar != null && vBar.getValue() + vBar.getVisibleAmount() < 0.97 * vBar.getMaximum();
      super.setText(text);
    }
  }

  @Override
  public void scrollRectToVisible(Rectangle rect)
  {
    if (!ignoreScrollToVisible)
    {
      super.scrollRectToVisible(rect);
      ignoreScrollToVisible = false;
    }
  }
}

/**
 * A class used to be able to select the contents of the text field when it gets
 * the focus.
 */
class TextFieldFocusListener implements FocusListener
{
  private final JTextField tf;

  /**
   * The constructor for this listener.
   *
   * @param tf
   *          the text field associated with this listener.
   */
  TextFieldFocusListener(JTextField tf)
  {
    this.tf = tf;
  }

  @Override
  public void focusGained(FocusEvent e)
  {
    if (tf.getText() == null || "".equals(tf.getText()))
    {
      tf.setText(" ");
      tf.selectAll();
      tf.setText("");
    }
    else
    {
      tf.selectAll();
    }
  }

  @Override
  public void focusLost(FocusEvent e)
  {
  }
}
