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