001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2006-2010 Sun Microsystems, Inc.
015 * Portions Copyright 2013-2015 ForgeRock AS.
016 */
017package org.opends.quicksetup.ui;
018
019import static org.opends.messages.QuickSetupMessages.*;
020
021import java.awt.Color;
022import java.awt.Component;
023import java.awt.Font;
024import java.awt.Image;
025import java.awt.Insets;
026import java.awt.Rectangle;
027import java.awt.Toolkit;
028import java.awt.event.FocusEvent;
029import java.awt.event.FocusListener;
030import java.util.HashMap;
031
032import javax.swing.BorderFactory;
033import javax.swing.ImageIcon;
034import javax.swing.JButton;
035import javax.swing.JCheckBox;
036import javax.swing.JComponent;
037import javax.swing.JEditorPane;
038import javax.swing.JFrame;
039import javax.swing.JLabel;
040import javax.swing.JList;
041import javax.swing.JPanel;
042import javax.swing.JPasswordField;
043import javax.swing.JRadioButton;
044import javax.swing.JScrollBar;
045import javax.swing.JScrollPane;
046import javax.swing.JTextField;
047import javax.swing.ListCellRenderer;
048import javax.swing.SwingUtilities;
049import javax.swing.UIManager;
050import javax.swing.border.Border;
051import javax.swing.border.EmptyBorder;
052import javax.swing.text.JTextComponent;
053import javax.swing.text.html.HTMLEditorKit;
054
055import org.forgerock.i18n.LocalizableMessage;
056import org.forgerock.i18n.slf4j.LocalizedLogger;
057
058/**
059 * This class provides constants an methods to create Swing objects and to
060 * generate UI elements with a common look and feel.
061 * <p>
062 * When we want to change a color, a background or a font this is the class
063 * that should be modified.
064 */
065public class UIFactory
066{
067  private static boolean initialized;
068  private static String parentPackagePath;
069  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
070
071  /** Specifies the horizontal insets between buttons. */
072  public static final int HORIZONTAL_INSET_BETWEEN_BUTTONS = 5;
073
074  /** Specifies the top inset for the steps. */
075  public static final int TOP_INSET_STEP = 15;
076
077  /** Specifies the left inset for the steps. */
078  public static final int LEFT_INSET_STEP = 5;
079
080  /** Specifies the extra left inset for the sub-steps. */
081  public static final int LEFT_INSET_SUBSTEP = 20;
082  /** Specifies the top inset for the instructions sub panel. */
083  public static final int TOP_INSET_INSTRUCTIONS_SUBPANEL = 5;
084
085  /** Specifies the top inset for input subpanel. */
086  public static final int TOP_INSET_INPUT_SUBPANEL = 10;
087
088  /** Specifies the top inset for a primary field. */
089  public static final int TOP_INSET_PRIMARY_FIELD = 10;
090
091  /** Specifies the top inset for a secondary field. */
092  public static final int TOP_INSET_SECONDARY_FIELD = 5;
093
094  /** Specifies the top inset for a radio button. */
095  public static final int TOP_INSET_RADIOBUTTON = 0;
096
097  /** Specifies the top inset for a radio button subordinate panel. */
098  public static final int TOP_INSET_RADIO_SUBORDINATE = 0;
099
100  /** Specifies the top inset for the progress bar. */
101  public static final int TOP_INSET_PROGRESS_BAR = 5;
102
103  /** Specifies the top inset for the progress text area. */
104  public static final int TOP_INSET_PROGRESS_TEXTAREA = 4;
105
106  /** Specifies the top inset for the background image. */
107  public static final int TOP_INSET_BACKGROUND = 70;
108
109  /** Specifies the top inset for the error message. */
110  public static final int TOP_INSET_ERROR_MESSAGE = 10;
111
112  /** Specifies the top inset for the browse button. */
113  public static final int TOP_INSET_BROWSE = 5;
114
115  /** Specifies the right inset for background image. */
116  public static final int RIGHT_INSET_BACKGROUND = 20;
117
118  /** Specifies the left inset for the primary field. */
119  public static final int LEFT_INSET_PRIMARY_FIELD = 10;
120
121  /** Specifies the left inset for the browse button. */
122  public static final int LEFT_INSET_BROWSE = 10;
123
124  /** Specifies the left inset for radio subordinate panel. */
125  public static final int LEFT_INSET_RADIO_SUBORDINATE = 35;
126
127  /** Specifies the left inset for the secondary field. */
128  public static final int LEFT_INSET_SECONDARY_FIELD = 5;
129
130  /** Specifies the left inset for the background image. */
131  public static final int LEFT_INSET_BACKGROUND = 20;
132
133  /** Specifies the left inset for the copy url button. */
134  public static final int LEFT_INSET_COPY_BUTTON = 10;
135
136  /** Specifies the left inset for a subordinate subpanel. */
137  public static final int LEFT_INSET_SUBPANEL_SUBORDINATE = 30;
138
139  /** Specifies the left inset for the progress bar. */
140  public static final int BOTTOM_INSET_PROGRESS_BAR = 10;
141
142  /** Specifies the bottom inset for the background image. */
143  public static final int BOTTOM_INSET_BACKGROUND = 30;
144
145  /** Specifies the top inset for a secondary field. */
146  public static final int BOTTOM_INSET_SECONDARY_FIELD = 5;
147
148  /** Specifies the number of columns of a text field for a path. */
149  public static final int PATH_FIELD_SIZE = 20;
150
151  /** Specifies the number of columns of a text field for a relative path. */
152  public static final int RELATIVE_PATH_FIELD_SIZE = 10;
153
154  /** Specifies the number of columns of a text field for a host name. */
155  public static final int HOST_FIELD_SIZE = 20;
156
157  /** Specifies the number of columns of a text field for a UID. */
158  public static final int UID_FIELD_SIZE = 15;
159
160  /** Specifies the number of columns of a text field for a port. */
161  public static final int PORT_FIELD_SIZE = 5;
162
163  /** Specifies the number of columns of a text field for a dn. */
164  public static final int DN_FIELD_SIZE = 20;
165
166  /** Specifies the number of columns of a text field for a password. */
167  public static final int PASSWORD_FIELD_SIZE = 15;
168
169  /**
170   * Specifies the number of columns of a text field for the number of entries.
171   */
172  public static final int NUMBER_ENTRIES_FIELD_SIZE = 7;
173
174  /** Specifies the number of points for the width of the progress bar. */
175  public static final int PROGRESS_BAR_SIZE = 220;
176
177  /**
178   * Specifies the number of extra points that we add to the minimum size of the
179   * dialog.
180   */
181  public static final int EXTRA_DIALOG_HEIGHT = 75;
182
183  private static final Insets BUTTONS_PANEL_INSETS = new Insets(5, 0, 5, 10);
184
185  private static final Insets STEPS_PANEL_INSETS = new Insets(15, 10, 5, 10);
186
187  private static final Insets CURRENT_STEP_PANEL_INSETS = new Insets(15, 15, 15, 15);
188
189  private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0);
190
191  /** Specifies the default background color. */
192  public static final Color DEFAULT_BACKGROUND = getColor(INFO_DEFAULT_BACKGROUND_COLOR.get());
193
194  /** Specifies the current step background color. */
195  public static final Color CURRENT_STEP_PANEL_BACKGROUND = getColor(INFO_CURRENT_STEP_PANEL_BACKGROUND_COLOR.get());
196
197  /** Specifies the default label color. */
198  public static final Color DEFAULT_LABEL_COLOR = getColor(INFO_DEFAULT_LABEL_COLOR.get());
199
200  /** Specifies the valid field color. */
201  public static final Color FIELD_VALID_COLOR = getColor(INFO_FIELD_VALID_COLOR.get());
202
203  /** Specifies the invalid field color. */
204  public static final Color FIELD_INVALID_COLOR = getColor(INFO_FIELD_INVALID_COLOR.get());
205
206  /** Specifies the read only text color. */
207  public static final Color READ_ONLY_COLOR = getColor(INFO_READ_ONLY_COLOR.get());
208
209  /** Specifies the check box text color. */
210  public static final Color CHECKBOX_COLOR = getColor(INFO_CHECKBOX_COLOR.get());
211
212  /** Specifies the progress text color. */
213  public static final Color PROGRESS_COLOR = getColor(INFO_PROGRESS_COLOR.get());
214
215  /** Specifies the instructions text color. */
216  public static final Color INSTRUCTIONS_COLOR = getColor(INFO_INSTRUCTIONS_COLOR.get());
217
218  /** Specifies the text field text color. */
219  public static final Color TEXTFIELD_COLOR = getColor(INFO_TEXTFIELD_COLOR.get());
220
221  /** Specifies the password field text color. */
222  public static final Color PASSWORDFIELD_COLOR = getColor(INFO_PASSWORDFIELD_COLOR.get());
223
224  /** Specifies the in-line help text color. */
225  public static final Color INLINE_HELP_COLOR = getColor(INFO_INLINE_HELP_COLOR.get());
226
227  /** Specifies the panel border color. */
228  public static final Color PANEL_BORDER_COLOR = getColor(INFO_PANEL_BORDER_COLOR.get());
229
230  /** Specifies the current step panel border. */
231  public static final Border CURRENT_STEP_PANEL_BORDER =
232      BorderFactory.createMatteBorder(0, 2, 2, 0, PANEL_BORDER_COLOR);
233
234  /** Specifies the text area border. */
235  public static final Border TEXT_AREA_BORDER =
236      BorderFactory.createMatteBorder(1, 1, 1, 1, getColor(INFO_TEXT_AREA_BORDER_COLOR.get()));
237
238  /** Specifies the dialog border. */
239  public static final Border DIALOG_PANEL_BORDER = BorderFactory.createMatteBorder(0, 0, 2, 0, PANEL_BORDER_COLOR);
240
241  private static Font DEFAULT_FONT;
242  static
243  {
244    try
245    {
246      DEFAULT_FONT = UIManager.getFont("Label.font").deriveFont(Font.PLAIN).deriveFont(12f);
247    }
248    catch (Throwable t)
249    {
250      DEFAULT_FONT = Font.decode("SansSerif-PLAIN-12");
251    }
252  }
253
254  /**
255   * Specifies the font for the step which is not the current one in the steps
256   * panel.
257   */
258  public static final Font NOT_CURRENT_STEP_FONT = DEFAULT_FONT.deriveFont(14f);
259
260  /**
261   * Specifies the font for the step which is the current one in the steps
262   * panel.
263   */
264  public static final Font CURRENT_STEP_FONT = DEFAULT_FONT.deriveFont(14f).deriveFont(Font.BOLD);
265
266  /** Specifies the font for the title of the current panel. */
267  public static final Font TITLE_FONT = DEFAULT_FONT.deriveFont(14f).deriveFont(Font.BOLD);
268
269  /** Specifies the font for the instructions of the current panel. */
270  public static final Font INSTRUCTIONS_FONT = DEFAULT_FONT;
271
272  /** Specifies the font for the instructions of the current panel. */
273  public static final Font INSTRUCTIONS_MONOSPACE_FONT = Font.decode("Monospaced-PLAIN-14");
274
275  /** Specifies the font for the primary valid field. */
276  public static final Font PRIMARY_FIELD_VALID_FONT = DEFAULT_FONT.deriveFont(Font.BOLD);
277
278  /** Specifies the font for the secondary valid field. */
279  public static final Font SECONDARY_FIELD_VALID_FONT = DEFAULT_FONT;
280
281  /** Specifies the font for the primary invalid field. */
282  public static final Font PRIMARY_FIELD_INVALID_FONT = DEFAULT_FONT.deriveFont(Font.BOLD | Font.ITALIC);
283
284  /** Specifies the font for the secondary invalid field. */
285  public static final Font SECONDARY_FIELD_INVALID_FONT = DEFAULT_FONT.deriveFont(Font.ITALIC);
286
287  /** Specifies the font for the secondary status field. */
288  public static final Font SECONDARY_STATUS_FONT = DEFAULT_FONT.deriveFont(Font.ITALIC);
289
290  /** Specifies the font for read only text. */
291  public static final Font READ_ONLY_FONT = DEFAULT_FONT;
292
293  /** Specifies the font for the check box text. */
294  public static final Font CHECKBOX_FONT = DEFAULT_FONT;
295
296  /** Specifies the font for the progress text. */
297  public static final Font PROGRESS_FONT = DEFAULT_FONT;
298
299  /** Specifies the font for the text field text. */
300  public static final Font TEXTFIELD_FONT = DEFAULT_FONT;
301
302  /** Specifies the font for the password field text. */
303  public static final Font PASSWORD_FIELD_FONT = DEFAULT_FONT;
304
305  /** Specifies the font for the points '....' in the progress panel. */
306  public static final Font PROGRESS_POINTS_FONT = DEFAULT_FONT.deriveFont(Font.BOLD);
307
308  /** Specifies the font for the done text 'Done' in the progress panel. */
309  public static final Font PROGRESS_DONE_FONT = PROGRESS_POINTS_FONT;
310
311  /** Specifies the font for the log messages in the progress panel. */
312  public static final Font PROGRESS_LOG_FONT = Font.decode("Monospaced-PLAIN-12");
313
314  /** Specifies the font for the error log messages in the progress panel. */
315  public static final Font PROGRESS_LOG_ERROR_FONT = Font.decode("Monospaced-PLAIN-12");
316
317  /** Specifies the font for the error messages in the progress panel. */
318  public static final Font PROGRESS_ERROR_FONT = DEFAULT_FONT.deriveFont(Font.BOLD);
319
320  /** Specifies the font for the warning messages in the progress panel. */
321  public static final Font PROGRESS_WARNING_FONT = DEFAULT_FONT.deriveFont(Font.BOLD);
322
323  /** Specifies the font for the stack trace in the progress panel. */
324  public static final Font STACK_FONT = DEFAULT_FONT;
325
326  /** Specifies the font for the text in the WebBrowserErrorDialog. */
327  public static final Font ERROR_DIALOG_FONT = DEFAULT_FONT;
328
329  /** Specifies the font for the text in the in-line help. */
330  public static final Font INLINE_HELP_FONT = DEFAULT_FONT.deriveFont((float) (DEFAULT_FONT.getSize() - 2));
331
332  private static final String SPAN_CLOSE = "</span>";
333
334  private static final String DIV_CLOSE = "</div>";
335
336  private static final String DIV_OPEN_ERROR_BACKGROUND =
337    "<div style=\"color:#"+
338    INFO_DIV_OPEN_ERROR_BACKGROUND_1_COLOR.get()+
339    ";background-color:#"+
340    INFO_DIV_OPEN_ERROR_BACKGROUND_2_COLOR.get()+
341    ";padding:10px 10px 10px 10px;"+
342    "border-style:solid;border-width:3px;border-color:#"+
343    INFO_DIV_OPEN_ERROR_BACKGROUND_3_COLOR.get()+
344    ";vertical-align:middle;text-align:left\">";
345
346  private static final String DIV_OPEN_WARNING_BACKGROUND = DIV_OPEN_ERROR_BACKGROUND;
347
348  private static final String DIV_OPEN_SUCCESSFUL_BACKGROUND =
349    "<div style=\"color:#"+
350    INFO_DIV_OPEN_SUCCESSFUL_BACKGROUND_1_COLOR.get()+
351    ";background-color:#"+
352    INFO_DIV_OPEN_SUCCESSFUL_BACKGROUND_2_COLOR.get()+
353    ";padding:10px 10px 10px 10px;"+
354    "border-style:solid;border-width:3px;border-color:#"+
355    INFO_DIV_OPEN_SUCCESSFUL_BACKGROUND_3_COLOR.get()+
356    ";vertical-align:middle;text-align:left\">";
357
358  /** An HTML separator text that can be used in the progress panel. */
359  public static final String HTML_SEPARATOR =
360    "<div style=\"font-size:1px;background-color:#"+
361    INFO_HTML_SEPARATOR_COLOR.get()+
362    ";margin:10px 5px 10px 5px;\"></div>";
363
364  private static final HashMap<IconType, ImageIcon> hmIcons = new HashMap<>();
365
366  /** The following enumeration contains the different icons that we can have. */
367  public enum IconType
368  {
369    /** Splash Icon. */
370    SPLASH,
371    /** Current Step Icon. */
372    CURRENT_STEP,
373    /** The icon displayed by the OS when the dialog is minimized. */
374    MINIMIZED,
375    /** The icon displayed by the Mac OS when the dialog is minimized. */
376    MINIMIZED_MAC,
377    /** The background icon. */
378    BACKGROUND,
379    /** The warning icon. */
380    WARNING,
381    /** The warning large icon. */
382    WARNING_LARGE,
383    /** The error icon. */
384    ERROR,
385    /** The error large icon. */
386    ERROR_LARGE,
387    /** The information icon. */
388    INFORMATION,
389    /** The information large icon. */
390    INFORMATION_LARGE,
391    /** Icon to create subsection title in Status Panel. */
392    SUBSECTION_LEFT,
393    /** Icon to create subsection title in Status Panel. */
394    SUBSECTION_RIGHT,
395    /** Question icon. */
396    HELP_SMALL,
397    /** Question medium icon. */
398    HELP_MEDIUM,
399    /** Hourglass to display when the user must wait. */
400    WAIT,
401    /** 8 x 8 Hourglass to display when the user must wait. */
402    WAIT_TINY,
403    /** No icon. */
404    NO_ICON
405  }
406
407  /**
408   * The following enumeration contains the different text styles that we can
409   * have. A text style basically specifies the font and color to be used to
410   * render the text.
411   */
412  public enum TextStyle
413  {
414    /** Current Step label style for the steps panel. */
415    CURRENT_STEP,
416    /** Not current Step label style for the steps panel. */
417    NOT_CURRENT_STEP,
418    /** Title label style for the current step panel. */
419    TITLE,
420    /** Primary field valid label style for the current step panel. */
421    PRIMARY_FIELD_VALID,
422    /** Primary field invalid text style for the current step panel. */
423    PRIMARY_FIELD_INVALID,
424    /** Secondary field valid text style for the current step panel. */
425    SECONDARY_FIELD_VALID,
426    /** Secondary field invalid text style for the current step panel. */
427    SECONDARY_FIELD_INVALID,
428    /** Status messages that appear near components. */
429    SECONDARY_STATUS,
430    /** Textfield text style for the current step panel. */
431    TEXTFIELD,
432    /** Password text style for the current step panel. */
433    PASSWORD_FIELD,
434    /** Read only text style for the current step panel. */
435    READ_ONLY,
436    /** Check box text text style for the current step panel. */
437    CHECKBOX,
438    /** Progress messages text style for the current step panel. */
439    PROGRESS,
440    /** Text style for the instructions. */
441    INSTRUCTIONS,
442    /** In-line help style. */
443    INLINE_HELP,
444    /** No text style. */
445    NO_STYLE
446  }
447
448  /**
449   * This method initialize the look and feel.
450   *
451   * @throws Throwable
452   *           if there is a problem initializing the look and feel.
453   */
454  public static void initializeLookAndFeel() throws Throwable
455  {
456    final Throwable[] ts = { null };
457    Runnable r = new Runnable()
458    {
459      @Override
460      public void run()
461      {
462        System.setProperty("swing.aatext", "true");
463        try
464        {
465          String lf = UIManager.getSystemLookAndFeelClassName();
466          if ("com.sun.java.swing.plaf.motif.MotifLookAndFeel".equalsIgnoreCase(lf))
467          {
468            lf = UIManager.getCrossPlatformLookAndFeelClassName();
469          }
470          UIManager.setLookAndFeel(lf);
471        }
472        catch (Throwable t)
473        {
474          ts[0] = t;
475        }
476        JFrame.setDefaultLookAndFeelDecorated(false);
477      }
478    };
479    if (SwingUtilities.isEventDispatchThread())
480    {
481      r.run();
482    }
483    else
484    {
485      try
486      {
487        SwingUtilities.invokeAndWait(r);
488      }
489      catch (Throwable t)
490      {
491        ts[0] = t;
492      }
493    }
494    if (ts[0] != null)
495    {
496      throw ts[0];
497    }
498  }
499
500  /**
501   * This method initialize the look and feel and UI settings specific to quick
502   * setup.
503   *
504   * @throws Throwable
505   *           if there is a problem initializing the look and feel.
506   */
507  public static void initialize() throws Throwable
508  {
509    if (!initialized)
510    {
511      try
512      {
513        UIManager.put("OptionPane.background", getColor(INFO_OPTIONPANE_BACKGROUND_COLOR.get()));
514        UIManager.put("Panel.background", getColor(INFO_PANEL_BACKGROUND_COLOR.get()));
515        UIManager.put("ComboBox.background", getColor(INFO_COMBOBOX_BACKGROUND_COLOR.get()));
516      }
517      catch (Throwable t)
518      {
519        // This might occur when we do not get the display
520        logger.warn(LocalizableMessage.raw("Error updating UIManager: " + t, t));
521      }
522      initializeLookAndFeel();
523      initialized = true;
524    }
525  }
526
527  /**
528   * Creates a new JPanel.
529   *
530   * @return JPanel newly created
531   */
532  public static JPanel makeJPanel()
533  {
534    JPanel pnl = new JPanel();
535    pnl.setOpaque(false);
536    return pnl;
537  }
538
539  /**
540   * Creates a JButton with the given label and tooltip.
541   *
542   * @param label
543   *          the text of the button.
544   * @param tooltip
545   *          the tooltip of the button.
546   * @return a JButton with the given label and tooltip.
547   */
548  public static JButton makeJButton(LocalizableMessage label, LocalizableMessage tooltip)
549  {
550    JButton b = new JButton();
551
552    if (label != null)
553    {
554      b.setText(label.toString());
555    }
556
557    if (tooltip != null)
558    {
559      b.setToolTipText(tooltip.toString());
560    }
561
562    b.setOpaque(false);
563
564    return b;
565  }
566
567  /**
568   * Commodity method that returns a JLabel based on a LabelFieldDescriptor.
569   *
570   * @param desc
571   *          the LabelFieldDescriptor describing the JLabel.
572   * @return a JLabel based on a LabelFieldDescriptor.
573   */
574  public static JLabel makeJLabel(LabelFieldDescriptor desc)
575  {
576    UIFactory.TextStyle style;
577    if (desc.getLabelType() == LabelFieldDescriptor.LabelType.PRIMARY)
578    {
579      style = UIFactory.TextStyle.PRIMARY_FIELD_VALID;
580    }
581    else
582    {
583      style = UIFactory.TextStyle.SECONDARY_FIELD_VALID;
584    }
585    return makeJLabel(UIFactory.IconType.NO_ICON, desc.getLabel(), style);
586  }
587
588  /**
589   * Creates a JLabel with the given icon, text and text style.
590   *
591   * @param iconName
592   *          the icon.
593   * @param text
594   *          the label text.
595   * @param style
596   *          the text style.
597   * @return a JLabel with the given icon, text and text style.
598   */
599  public static JLabel makeJLabel(IconType iconName, LocalizableMessage text, TextStyle style)
600  {
601    JLabel l = new JLabel();
602
603    if (text != null)
604    {
605      l.setText(text.toString());
606    }
607
608    ImageIcon icon = getImageIcon(iconName);
609    l.setIcon(icon);
610    LocalizableMessage tooltip = getIconTooltip(iconName);
611
612    if (tooltip != null)
613    {
614      l.setToolTipText(tooltip.toString());
615    }
616
617    setTextStyle(l, style);
618    return l;
619  }
620
621  /**
622   * Commodity method that returns a JTextComponent based on a
623   * LabelFieldDescriptor.
624   *
625   * @param desc
626   *          the LabelFieldDescriptor describing the JTextField.
627   * @param defaultValue
628   *          the default value used to initialize the JTextComponent.
629   * @return a JTextComponent based on a LabelFieldDescriptor.
630   */
631  public static JTextComponent makeJTextComponent(LabelFieldDescriptor desc, String defaultValue)
632  {
633    if (defaultValue == null)
634    {
635      defaultValue = "";
636    }
637    switch (desc.getType())
638    {
639    case TEXTFIELD:
640      return makeJTextField(
641          LocalizableMessage.raw(defaultValue), desc.getTooltip(), desc.getSize(), TextStyle.TEXTFIELD);
642
643    case PASSWORD:
644      return makeJPasswordField(
645          LocalizableMessage.raw(defaultValue), desc.getTooltip(), desc.getSize(), TextStyle.PASSWORD_FIELD);
646
647    case READ_ONLY:
648      return makeTextPane(LocalizableMessage.raw(defaultValue), TextStyle.READ_ONLY);
649
650    default:
651      throw new IllegalArgumentException("Unknown type: " + desc.getType());
652    }
653  }
654
655  /**
656   * Creates a JTextField with the given icon, tooltip text, size and text
657   * style.
658   *
659   * @param text
660   *          the text.
661   * @param tooltip
662   *          the tooltip text.
663   * @param size
664   *          the number of columns of the JTextField.
665   * @param style
666   *          the text style.
667   * @return a JTextField with the given icon, tooltip text, size and text
668   *         style.
669   */
670  public static JTextField makeJTextField(
671      LocalizableMessage text, LocalizableMessage tooltip, int size, TextStyle style)
672  {
673    JTextField f = new JTextField();
674    updateTextFieldComponent(f, text, tooltip, size, style);
675    f.addFocusListener(new TextFieldFocusListener(f));
676    return f;
677  }
678
679  /**
680   * Creates a JPasswordField with the given icon, tooltip text, size and text
681   * style.
682   *
683   * @param text
684   *          the text.
685   * @param tooltip
686   *          the tooltip text.
687   * @param size
688   *          the number of columns of the JPasswordField.
689   * @param style
690   *          the text style.
691   * @return a JPasswordField with the given icon, tooltip text, size and text
692   *         style.
693   */
694  public static JPasswordField makeJPasswordField(
695      LocalizableMessage text, LocalizableMessage tooltip, int size, TextStyle style)
696  {
697    JPasswordField f = new JPasswordField();
698    updateTextFieldComponent(f, text, tooltip, size, style);
699    f.addFocusListener(new TextFieldFocusListener(f));
700    return f;
701  }
702
703  /**
704   * Creates a JRadioButton with the given text, tooltip text and text style.
705   *
706   * @param text
707   *          the text of the radio button.
708   * @param tooltip
709   *          the tooltip text.
710   * @param style
711   *          the text style.
712   * @return a JRadioButton with the given text, tooltip text and text style.
713   */
714  public static JRadioButton makeJRadioButton(LocalizableMessage text, LocalizableMessage tooltip, TextStyle style)
715  {
716    JRadioButton rb = new JRadioButton();
717    rb.setOpaque(false);
718    if (text != null)
719    {
720      rb.setText(text.toString());
721    }
722
723    if (tooltip != null)
724    {
725      rb.setToolTipText(tooltip.toString());
726    }
727
728    setTextStyle(rb, style);
729    return rb;
730  }
731
732  /**
733   * Creates a JCheckBox with the given text, tooltip text and text style.
734   *
735   * @param text
736   *          the text of the radio button.
737   * @param tooltip
738   *          the tooltip text.
739   * @param style
740   *          the text style.
741   * @return a JCheckBox with the given text, tooltip text and text style.
742   */
743  public static JCheckBox makeJCheckBox(LocalizableMessage text, LocalizableMessage tooltip, TextStyle style)
744  {
745    JCheckBox cb = new JCheckBox();
746    cb.setOpaque(false);
747    if (text != null)
748    {
749      cb.setText(text.toString());
750    }
751
752    if (tooltip != null)
753    {
754      cb.setToolTipText(tooltip.toString());
755    }
756
757    setTextStyle(cb, style);
758    return cb;
759  }
760
761  /**
762   * Creates a JList.
763   *
764   * @param textStyle
765   *          the style to be used for the renderer.
766   * @param <T>
767   *          The type of the JList elements
768   * @return a JList.
769   */
770  public static <T> JList<T> makeJList(TextStyle textStyle)
771  {
772    final JList<T> list = new JList<>();
773    list.setCellRenderer(makeCellRenderer(textStyle));
774    return list;
775  }
776
777  /**
778   * Sets the specified text style to the component passed as parameter.
779   *
780   * @param l
781   *          the component to update.
782   * @param style
783   *          the text style to use.
784   */
785  public static void setTextStyle(JComponent l, TextStyle style)
786  {
787    switch (style)
788    {
789    case NOT_CURRENT_STEP:
790      l.setFont(UIFactory.NOT_CURRENT_STEP_FONT);
791      l.setForeground(DEFAULT_LABEL_COLOR);
792      break;
793
794    case CURRENT_STEP:
795      l.setFont(UIFactory.CURRENT_STEP_FONT);
796      l.setForeground(DEFAULT_LABEL_COLOR);
797      break;
798
799    case TITLE:
800      l.setFont(UIFactory.TITLE_FONT);
801      l.setForeground(DEFAULT_LABEL_COLOR);
802      break;
803
804    case PRIMARY_FIELD_VALID:
805      l.setFont(UIFactory.PRIMARY_FIELD_VALID_FONT);
806      l.setForeground(FIELD_VALID_COLOR);
807      break;
808
809    case PRIMARY_FIELD_INVALID:
810      l.setFont(UIFactory.PRIMARY_FIELD_INVALID_FONT);
811      l.setForeground(FIELD_INVALID_COLOR);
812      break;
813
814    case SECONDARY_FIELD_VALID:
815      l.setFont(UIFactory.SECONDARY_FIELD_VALID_FONT);
816      l.setForeground(FIELD_VALID_COLOR);
817      break;
818
819    case SECONDARY_FIELD_INVALID:
820      l.setFont(UIFactory.SECONDARY_FIELD_INVALID_FONT);
821      l.setForeground(FIELD_INVALID_COLOR);
822      break;
823
824    case SECONDARY_STATUS:
825      l.setFont(UIFactory.SECONDARY_STATUS_FONT);
826      l.setForeground(FIELD_VALID_COLOR);
827      break;
828
829    case READ_ONLY:
830      l.setFont(UIFactory.READ_ONLY_FONT);
831      l.setForeground(READ_ONLY_COLOR);
832      break;
833
834    case CHECKBOX:
835      l.setFont(UIFactory.CHECKBOX_FONT);
836      l.setForeground(CHECKBOX_COLOR);
837      break;
838
839    case PROGRESS:
840      l.setFont(UIFactory.PROGRESS_FONT);
841      l.setForeground(PROGRESS_COLOR);
842      break;
843
844    case INSTRUCTIONS:
845      l.setFont(INSTRUCTIONS_FONT);
846      l.setForeground(INSTRUCTIONS_COLOR);
847      break;
848
849    case TEXTFIELD:
850      l.setFont(UIFactory.TEXTFIELD_FONT);
851      l.setForeground(TEXTFIELD_COLOR);
852      break;
853
854    case PASSWORD_FIELD:
855      l.setFont(UIFactory.PASSWORD_FIELD_FONT);
856      l.setForeground(PASSWORDFIELD_COLOR);
857      break;
858
859    case INLINE_HELP:
860      l.setFont(INLINE_HELP_FONT);
861      l.setForeground(INLINE_HELP_COLOR);
862      break;
863
864    case NO_STYLE:
865      // Do nothing
866      break;
867
868    default:
869      throw new IllegalArgumentException("Unknown textStyle: " + style);
870    }
871  }
872
873  /**
874   * Returns the HTML string representing the provided IconType.
875   *
876   * @param iconType
877   *          the IconType for which we want the HTML representation.
878   * @return the HTML string representing the provided IconType.
879   */
880  public static String getIconHtml(IconType iconType)
881  {
882    String url = String.valueOf(UIFactory.class.getClassLoader().getResource(getIconPath(iconType)));
883    LocalizableMessage description = getIconDescription(iconType);
884    LocalizableMessage title = getIconTooltip(iconType);
885    return "<img src=\"" + url + "\" alt=\"" + description + "\" align=\"middle\" title=\"" + title + "\" >";
886  }
887
888  /**
889   * Returns an ImageIcon object for the provided IconType.
890   *
891   * @param iconType
892   *          the IconType for which we want to obtain the ImageIcon.
893   * @return the ImageIcon.
894   */
895  public static ImageIcon getImageIcon(IconType iconType)
896  {
897    if (iconType == null)
898    {
899      iconType = IconType.NO_ICON;
900    }
901    ImageIcon icon = hmIcons.get(iconType);
902    if (icon == null && iconType != IconType.NO_ICON)
903    {
904      String path = getIconPath(iconType);
905      LocalizableMessage description = getIconDescription(iconType);
906      try
907      {
908        Image im = Toolkit.getDefaultToolkit().createImage(UIFactory.class.getClassLoader().getResource(path));
909        icon = new ImageIcon(im);
910        String ds = description != null ? description.toString() : null;
911        icon.setDescription(ds);
912
913        hmIcons.put(iconType, icon);
914      }
915      catch (Exception ex)
916      {
917        ex.printStackTrace(); // A bug: this should not happen
918        throw new IllegalStateException("Could not load icon for path " + path, ex);
919      }
920    }
921
922    return icon;
923  }
924
925  /**
926   * Returns a JEditorPane that works with the provided scroll.
927   *
928   * @see ProgressJEditorPane
929   * @param scroll
930   *          the scroll that will contain the JEditorPane.
931   * @return a JEditorPane that works with the provided scroll.
932   */
933  public static JEditorPane makeProgressPane(JScrollPane scroll)
934  {
935    return new ProgressJEditorPane(scroll);
936  }
937
938  /**
939   * Returns a read only JEditorPane containing the provided text with the
940   * provided font. The JEditorPane will assume that the text is HTML text.
941   *
942   * @param text
943   *          the text to be used to initialize the JEditorPane contents.
944   * @param font
945   *          the font to be used.
946   * @return a read only JEditorPane containing the provided text with the
947   *         provided font.
948   */
949  public static JEditorPane makeHtmlPane(LocalizableMessage text, Font font)
950  {
951    return makeHtmlPane(text, null, font);
952  }
953
954  /**
955   * Returns a read only JEditorPane containing the provided text with the
956   * provided font. The JEditorPane will assume that the text is HTML text.
957   *
958   * @param text
959   *          the text to be used to initialize the JEditorPane contents.
960   * @param ek
961   *          HTMLEditor kit used for the new HTML pane
962   * @param font
963   *          the font to be used.
964   * @return a read only JEditorPane containing the provided text with the
965   *         provided font.
966   */
967  public static JEditorPane makeHtmlPane(LocalizableMessage text, HTMLEditorKit ek, Font font)
968  {
969    JEditorPane pane = new JEditorPane();
970    if (ek != null) {
971        pane.setEditorKit(ek);
972    }
973    pane.setContentType("text/html");
974    String s = text != null ? String.valueOf(text) : null;
975    pane.setText(applyFontToHtmlWithDiv(s, font));
976    pane.setEditable(false);
977    pane.setBorder(new EmptyBorder(0, 0, 0, 0));
978    return pane;
979  }
980
981  /**
982   * Returns a read only JEditorPane containing the provided text with the
983   * provided TextStyle. The JEditorPane will assume that the text is plain
984   * text.
985   *
986   * @param text
987   *          the text to be used to initialize the JEditorPane contents.
988   * @param style
989   *          the TextStyle to be used.
990   * @return a read only JEditorPane containing the provided text with the
991   *         provided TextStyle.
992   */
993  public static JEditorPane makeTextPane(LocalizableMessage text, TextStyle style)
994  {
995    String s = text != null ? String.valueOf(text) : null;
996    JEditorPane pane = new JEditorPane("text/plain", s);
997    setTextStyle(pane, style);
998    pane.setEditable(false);
999    pane.setBorder(new EmptyBorder(0, 0, 0, 0));
1000    pane.setOpaque(false);
1001    return pane;
1002  }
1003
1004  /**
1005   * Returns a JScrollPane that contains the provided component. The scroll pane
1006   * will not contain any border.
1007   *
1008   * @param comp
1009   *          the component contained in the scroll pane.
1010   * @return a JScrollPane that contains the provided component. The scroll pane
1011   *         will not contain any border.
1012   */
1013  public static JScrollPane createBorderLessScrollBar(Component comp)
1014  {
1015    JScrollPane scroll = new JScrollPane(comp);
1016    scroll.setBorder(new EmptyBorder(0, 0, 0, 0));
1017    scroll.setViewportBorder(new EmptyBorder(0, 0, 0, 0));
1018    scroll.setOpaque(false);
1019    scroll.getViewport().setOpaque(false);
1020    scroll.getViewport().setBackground(DEFAULT_BACKGROUND);
1021    scroll.setBackground(DEFAULT_BACKGROUND);
1022    setScrollIncrementUnit(scroll);
1023    return scroll;
1024  }
1025
1026  /**
1027   * Sets the scroll increment unit for the scroll.
1028   *
1029   * @param scroll
1030   *          the scroll to be updated.
1031   */
1032  public static void setScrollIncrementUnit(JScrollPane scroll)
1033  {
1034    if (scroll.getVerticalScrollBar() != null)
1035    {
1036      int increment = scroll.getVerticalScrollBar().getUnitIncrement();
1037      if (increment < 16)
1038      {
1039        scroll.getVerticalScrollBar().setUnitIncrement(16);
1040      }
1041    }
1042  }
1043
1044  /**
1045   * Return empty insets.
1046   *
1047   * @return empty insets.
1048   */
1049  public static Insets getEmptyInsets()
1050  {
1051    return (Insets) EMPTY_INSETS.clone();
1052  }
1053
1054  /**
1055   * Returns the insets to be used for the button panel.
1056   *
1057   * @return the insets to be used for the button panel.
1058   */
1059  public static Insets getButtonsPanelInsets()
1060  {
1061    return (Insets) BUTTONS_PANEL_INSETS.clone();
1062  }
1063
1064  /**
1065   * Returns the insets to be used for the steps panel.
1066   *
1067   * @return the insets to be used for the steps panel.
1068   */
1069  public static Insets getStepsPanelInsets()
1070  {
1071    return (Insets) STEPS_PANEL_INSETS.clone();
1072  }
1073
1074  /**
1075   * Returns the insets to be used for the current step panel.
1076   *
1077   * @return the insets to be used for the current step panel.
1078   */
1079  public static Insets getCurrentStepPanelInsets()
1080  {
1081    return (Insets) CURRENT_STEP_PANEL_INSETS.clone();
1082  }
1083
1084  /**
1085   * Returns a String that contains the html passed as parameter with a span
1086   * applied. The span style corresponds to the Font specified as parameter. The
1087   * goal of this method is to be able to specify a font for an HTML string.
1088   *
1089   * @param html
1090   *          the original html text.
1091   * @param font
1092   *          the font to be used to generate the new HTML.
1093   * @return a string that represents the original HTML with the font specified
1094   *         as parameter.
1095   */
1096  public static String applyFontToHtml(String html, Font font)
1097  {
1098    StringBuilder buf = new StringBuilder();
1099
1100    buf.append("<span style=\"").append(getFontStyle(font)).append("\">").append(html).append(SPAN_CLOSE);
1101
1102    return buf.toString();
1103  }
1104
1105  /**
1106   * Returns a String that contains the html passed as parameter with a div
1107   * applied. The div style corresponds to the Font specified as parameter. The
1108   * goal of this method is to be able to specify a font for an HTML string.
1109   *
1110   * @param html
1111   *          the original html text.
1112   * @param font
1113   *          the font to be used to generate the new HTML.
1114   * @return a string that represents the original HTML with the font specified
1115   *         as parameter.
1116   */
1117  public static String applyFontToHtmlWithDiv(String html, Font font)
1118  {
1119    StringBuilder buf = new StringBuilder();
1120
1121    buf.append("<div style=\"").append(getFontStyle(font)).append("\">").append(html).append(DIV_CLOSE);
1122
1123    return buf.toString();
1124  }
1125
1126  /**
1127   * Returns the HTML style representation for the given font.
1128   *
1129   * @param font
1130   *          the font for which we want to get an HTML style representation.
1131   * @return the HTML style representation for the given font.
1132   */
1133  private static String getFontStyle(Font font)
1134  {
1135    StringBuilder buf = new StringBuilder();
1136
1137    buf.append("font-family:").append(font.getName()).append(";font-size:").append(font.getSize()).append("pt");
1138
1139    if (font.isItalic())
1140    {
1141      buf.append(";font-style:italic");
1142    }
1143
1144    if (font.isBold())
1145    {
1146      buf.append(";font-weight:bold;");
1147    }
1148
1149    return buf.toString();
1150  }
1151
1152  /**
1153   * Returns the html text passed as parameter with the error background applied
1154   * to it.
1155   *
1156   * @param html
1157   *          the original html.
1158   * @return the html text passed as parameter with the error background applied
1159   *         to it.
1160   */
1161  public static String applyErrorBackgroundToHtml(String html)
1162  {
1163    return DIV_OPEN_ERROR_BACKGROUND + html + DIV_CLOSE;
1164  }
1165
1166  /**
1167   * Returns the html text passed as parameter with the warning background
1168   * applied to it.
1169   *
1170   * @param html
1171   *          the original html.
1172   * @return the html text passed as parameter with the warning background
1173   *         applied to it.
1174   */
1175  public static String applyWarningBackgroundToHtml(String html)
1176  {
1177    return DIV_OPEN_WARNING_BACKGROUND + html + DIV_CLOSE;
1178  }
1179
1180  /**
1181   * Returns the html text passed as parameter with the success background
1182   * applied to it.
1183   *
1184   * @param html
1185   *          the original html.
1186   * @return the html text passed as parameter with the success background
1187   *         applied to it.
1188   */
1189  public static String applySuccessfulBackgroundToHtml(String html)
1190  {
1191    return DIV_OPEN_SUCCESSFUL_BACKGROUND + html + DIV_CLOSE;
1192  }
1193
1194  /**
1195   * Returns the html text passed as parameter with some added margin.
1196   *
1197   * @param html
1198   *          the original html text.
1199   * @param top
1200   *          the top margin.
1201   * @param right
1202   *          the right margin.
1203   * @param bottom
1204   *          the bottom margin.
1205   * @param left
1206   *          the left margin.
1207   * @return the html text passed as parameter with some added margin.
1208   */
1209  public static String applyMargin(String html, int top, int right, int bottom, int left)
1210  {
1211    return "<div style=\"margin:" + top + "px " + right + "px " + bottom + "px " + left + "px;\">" + html + DIV_CLOSE;
1212  }
1213
1214  /**
1215   * Updates the provided field with all the other arguments.
1216   *
1217   * @param field
1218   *          the field to be modified.
1219   * @param text
1220   *          the new text of the field.
1221   * @param tooltip
1222   *          the new tooltip text of the field.
1223   * @param size
1224   *          the new size of the field.
1225   * @param textStyle
1226   *          the new TextStyle of the field.
1227   */
1228  private static void updateTextFieldComponent(
1229      JTextField field, LocalizableMessage text, LocalizableMessage tooltip, int size, TextStyle textStyle)
1230  {
1231    field.setColumns(size);
1232    if (text != null)
1233    {
1234      field.setText(text.toString());
1235    }
1236    if (tooltip != null)
1237    {
1238      field.setToolTipText(tooltip.toString());
1239    }
1240    if (textStyle != null)
1241    {
1242      setTextStyle(field, textStyle);
1243    }
1244  }
1245
1246  private static Color getColor(LocalizableMessage l)
1247  {
1248    String s = String.valueOf(l);
1249    String[] colors = s.split(",");
1250    int r = Integer.parseInt(colors[0].trim());
1251    int g = Integer.parseInt(colors[1].trim());
1252    int b = Integer.parseInt(colors[2].trim());
1253
1254    return new Color(r, g, b);
1255  }
1256
1257  /**
1258   * Returns the parent package path. This is used to retrieve the icon
1259   * qualified names.
1260   *
1261   * @return the parent package path.
1262   */
1263  private static String getParentPackagePath()
1264  {
1265    if (parentPackagePath == null)
1266    {
1267      String packageName = UIFactory.class.getPackage().getName();
1268      int lastDot = packageName.lastIndexOf('.');
1269      String parentPackage = packageName.substring(0, lastDot);
1270      parentPackagePath = parentPackage.replace(".", "/");
1271    }
1272    return parentPackagePath;
1273  }
1274
1275  /**
1276   * Returns the path of the icon for the given IconType.
1277   *
1278   * @param iconType
1279   *          the IconType for which we want to get the path.
1280   * @return the path of the icon for the given IconType.
1281   */
1282  private static String getIconPath(IconType iconType)
1283  {
1284    return getParentPackagePath() + "/" + getKey(iconType);
1285  }
1286
1287  private static LocalizableMessage getKey(IconType iconType)
1288  {
1289    switch (iconType)
1290    {
1291    case CURRENT_STEP:
1292      return INFO_CURRENT_STEP_ICON.get();
1293    case SPLASH:
1294      return INFO_SPLASH_ICON.get();
1295    case BACKGROUND:
1296      return INFO_BACKGROUND_ICON.get();
1297    case MINIMIZED:
1298      return INFO_MINIMIZED_ICON.get();
1299    case MINIMIZED_MAC:
1300      return INFO_MINIMIZED_MAC_ICON.get();
1301    case WARNING:
1302      return INFO_WARNING_ICON.get();
1303    case WARNING_LARGE:
1304      return INFO_WARNING_LARGE_ICON.get();
1305    case INFORMATION:
1306      return INFO_INFORMATION_ICON.get();
1307    case INFORMATION_LARGE:
1308      return INFO_INFORMATION_LARGE_ICON.get();
1309    case SUBSECTION_LEFT:
1310      return INFO_SUBSECTION_LEFT_ICON.get();
1311    case SUBSECTION_RIGHT:
1312      return INFO_SUBSECTION_RIGHT_ICON.get();
1313    case HELP_SMALL:
1314      return INFO_HELP_SMALL_ICON.get();
1315    case HELP_MEDIUM:
1316      return INFO_HELP_MEDIUM_ICON.get();
1317    case ERROR:
1318      return INFO_ERROR_ICON.get();
1319    case ERROR_LARGE:
1320      return INFO_ERROR_LARGE_ICON.get();
1321    case WAIT_TINY:
1322      return INFO_WAIT_TINY.get();
1323    case WAIT:
1324      return INFO_WAIT.get();
1325    default:
1326      throw new IllegalArgumentException("Unknown iconName: " + iconType);
1327    }
1328  }
1329
1330  /**
1331   * Returns the icon description for the given IconType.
1332   *
1333   * @param iconType
1334   *          the IconType for which we want to get the description.
1335   * @return the icon description for the given IconType.
1336   */
1337  private static LocalizableMessage getIconDescription(IconType iconType)
1338  {
1339    switch (iconType)
1340    {
1341    case CURRENT_STEP:
1342      return INFO_CURRENT_STEP_ICON_DESCRIPTION.get();
1343
1344    case SPLASH:
1345      return INFO_SPLASH_ICON_DESCRIPTION.get();
1346
1347    case BACKGROUND:
1348      return INFO_BACKGROUND_ICON_DESCRIPTION.get();
1349
1350    case MINIMIZED:
1351      return INFO_MINIMIZED_ICON_DESCRIPTION.get();
1352
1353    case MINIMIZED_MAC:
1354      return INFO_MINIMIZED_ICON_DESCRIPTION.get();
1355
1356    case WARNING:
1357      return INFO_WARNING_ICON_DESCRIPTION.get();
1358
1359    case WARNING_LARGE:
1360      return INFO_WARNING_ICON_DESCRIPTION.get();
1361
1362    case ERROR:
1363      return INFO_ERROR_ICON_DESCRIPTION.get();
1364
1365    case ERROR_LARGE:
1366      return INFO_ERROR_ICON_DESCRIPTION.get();
1367
1368    case INFORMATION:
1369      return INFO_INFORMATION_ICON_DESCRIPTION.get();
1370
1371    case INFORMATION_LARGE:
1372      return INFO_INFORMATION_ICON_DESCRIPTION.get();
1373
1374    case SUBSECTION_LEFT:
1375      return INFO_SUBSECTION_LEFT_ICON_DESCRIPTION.get();
1376
1377    case SUBSECTION_RIGHT:
1378      return INFO_SUBSECTION_RIGHT_ICON_DESCRIPTION.get();
1379
1380    case HELP_SMALL:
1381    case HELP_MEDIUM:
1382      return INFO_HELP_SMALL_ICON_DESCRIPTION.get();
1383
1384    case WAIT_TINY:
1385      return INFO_HELP_WAIT_DESCRIPTION.get();
1386
1387    case WAIT:
1388      return INFO_HELP_WAIT_DESCRIPTION.get();
1389
1390    case NO_ICON:
1391      return null;
1392
1393    default:
1394      throw new IllegalArgumentException("Unknown iconName: " + iconType);
1395    }
1396  }
1397
1398  /**
1399   * Returns the icon tooltip text for the given IconType.
1400   *
1401   * @param iconType
1402   *          the IconType for which we want to get the tooltip text.
1403   * @return the icon tooltip text for the given IconType.
1404   */
1405  private static LocalizableMessage getIconTooltip(IconType iconType)
1406  {
1407    if (iconType == null)
1408    {
1409      iconType = IconType.NO_ICON;
1410    }
1411    switch (iconType)
1412    {
1413    case CURRENT_STEP:
1414      return INFO_CURRENT_STEP_ICON_TOOLTIP.get();
1415
1416    case SPLASH:
1417      return INFO_SPLASH_ICON_TOOLTIP.get();
1418
1419    case BACKGROUND:
1420      return INFO_BACKGROUND_ICON_TOOLTIP.get();
1421
1422    case MINIMIZED:
1423      return INFO_MINIMIZED_ICON_TOOLTIP.get();
1424
1425    case MINIMIZED_MAC:
1426      return INFO_MINIMIZED_ICON_TOOLTIP.get();
1427
1428    case WARNING:
1429      return INFO_WARNING_ICON_TOOLTIP.get();
1430
1431    case WARNING_LARGE:
1432      return INFO_WARNING_ICON_TOOLTIP.get();
1433
1434    case ERROR:
1435      return INFO_ERROR_ICON_TOOLTIP.get();
1436
1437    case ERROR_LARGE:
1438      return INFO_ERROR_ICON_TOOLTIP.get();
1439
1440    case INFORMATION:
1441      return INFO_INFORMATION_ICON_TOOLTIP.get();
1442
1443    case INFORMATION_LARGE:
1444      return INFO_INFORMATION_ICON_TOOLTIP.get();
1445
1446    case SUBSECTION_LEFT:
1447    case SUBSECTION_RIGHT:
1448    case HELP_SMALL:
1449    case HELP_MEDIUM:
1450    case WAIT_TINY:
1451    case WAIT:
1452    case NO_ICON:
1453      return null;
1454
1455    default:
1456      throw new IllegalArgumentException("Unknown iconName: " + iconType);
1457    }
1458  }
1459
1460  private static <T> ListCellRenderer<T> makeCellRenderer(final TextStyle textStyle)
1461  {
1462    return new ListCellRenderer<T>()
1463    {
1464      @Override
1465      public Component getListCellRendererComponent(JList<? extends T> list, T value, int index, boolean isSelected,
1466          boolean cellHasFocus)
1467      {
1468        final JLabel l = makeJLabel(IconType.NO_ICON, LocalizableMessage.raw(value.toString()), textStyle);
1469        l.setBorder(new EmptyBorder(TOP_INSET_SECONDARY_FIELD, 0, 0, 0));
1470        return l;
1471      }
1472    };
1473  }
1474}
1475
1476/**
1477 * This class has been written to have a better behaviour with the scroll pane
1478 * than the one we have by default in the case of the progress panel.
1479 * <p>
1480 * With the default scroll pane behaviour when we set a new text in a
1481 * JEditorPane the scroll bar goes systematically up.  With this implementation
1482 * the expected behaviour is:
1483 * <p>
1484 * If the scroll bar is at the bottom we will display the latest text contained
1485 * in the pane.
1486 * <p>
1487 * If the scroll bar is not at the bottom we will keep on displaying the same
1488 * thing that the user is viewing.
1489 * <p>
1490 * This behaviour allows the user to check the log content even when the
1491 * installation/uninstallation is still running and sending new log messages.
1492 */
1493class ProgressJEditorPane extends JEditorPane
1494{
1495  private static final long serialVersionUID = 1221976708322628818L;
1496
1497  private final JScrollPane scroll;
1498
1499  private boolean ignoreScrollToVisible;
1500
1501  /**
1502   * Constructor for the ProgressJEditorPane.
1503   *
1504   * @param scroll
1505   *          the JScrollPane that will contain this editor pane.
1506   */
1507  public ProgressJEditorPane(JScrollPane scroll)
1508  {
1509    super("text/html", null);
1510    this.scroll = scroll;
1511    setEditable(false);
1512    setBorder(new EmptyBorder(3, 3, 3, 3));
1513  }
1514
1515  @Override
1516  public void setText(String text)
1517  {
1518    // Scroll can be null in constructor
1519    if (scroll != null)
1520    {
1521      /*
1522       * We apply the following policy: if the user is displaying the latest
1523       * part of the JTextArea we assume that when we add text (s)he wants to
1524       * see the text that is added, if not we assume that (s)he want to keep
1525       * viewing what is visible and so we ignore the next scrollRectToVisible
1526       * call (that will be done inside JTextArea.setText method).
1527       */
1528      JScrollBar vBar = scroll.getVerticalScrollBar();
1529      ignoreScrollToVisible =
1530          vBar != null && vBar.getValue() + vBar.getVisibleAmount() < 0.97 * vBar.getMaximum();
1531      super.setText(text);
1532    }
1533  }
1534
1535  @Override
1536  public void scrollRectToVisible(Rectangle rect)
1537  {
1538    if (!ignoreScrollToVisible)
1539    {
1540      super.scrollRectToVisible(rect);
1541      ignoreScrollToVisible = false;
1542    }
1543  }
1544}
1545
1546/**
1547 * A class used to be able to select the contents of the text field when it gets
1548 * the focus.
1549 */
1550class TextFieldFocusListener implements FocusListener
1551{
1552  private final JTextField tf;
1553
1554  /**
1555   * The constructor for this listener.
1556   *
1557   * @param tf
1558   *          the text field associated with this listener.
1559   */
1560  TextFieldFocusListener(JTextField tf)
1561  {
1562    this.tf = tf;
1563  }
1564
1565  @Override
1566  public void focusGained(FocusEvent e)
1567  {
1568    if (tf.getText() == null || "".equals(tf.getText()))
1569    {
1570      tf.setText(" ");
1571      tf.selectAll();
1572      tf.setText("");
1573    }
1574    else
1575    {
1576      tf.selectAll();
1577    }
1578  }
1579
1580  @Override
1581  public void focusLost(FocusEvent e)
1582  {
1583  }
1584}