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