/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2010 Sun Microsystems, Inc. * Portions Copyright 2013-2015 ForgeRock AS. */ package org.opends.quicksetup.ui; import static org.opends.messages.QuickSetupMessages.*; import java.awt.Color; import java.awt.Component; import java.awt.Font; import java.awt.Image; import java.awt.Insets; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.util.HashMap; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JRadioButton; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.ListCellRenderer; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.text.JTextComponent; import javax.swing.text.html.HTMLEditorKit; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.slf4j.LocalizedLogger; /** * This class provides constants an methods to create Swing objects and to * generate UI elements with a common look and feel. * <p> * When we want to change a color, a background or a font this is the class * that should be modified. */ public class UIFactory { private static boolean initialized; private static String parentPackagePath; private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); /** Specifies the horizontal insets between buttons. */ public static final int HORIZONTAL_INSET_BETWEEN_BUTTONS = 5; /** Specifies the top inset for the steps. */ public static final int TOP_INSET_STEP = 15; /** Specifies the left inset for the steps. */ public static final int LEFT_INSET_STEP = 5; /** Specifies the extra left inset for the sub-steps. */ public static final int LEFT_INSET_SUBSTEP = 20; /** Specifies the top inset for the instructions sub panel. */ public static final int TOP_INSET_INSTRUCTIONS_SUBPANEL = 5; /** Specifies the top inset for input subpanel. */ public static final int TOP_INSET_INPUT_SUBPANEL = 10; /** Specifies the top inset for a primary field. */ public static final int TOP_INSET_PRIMARY_FIELD = 10; /** Specifies the top inset for a secondary field. */ public static final int TOP_INSET_SECONDARY_FIELD = 5; /** Specifies the top inset for a radio button. */ public static final int TOP_INSET_RADIOBUTTON = 0; /** Specifies the top inset for a radio button subordinate panel. */ public static final int TOP_INSET_RADIO_SUBORDINATE = 0; /** Specifies the top inset for the progress bar. */ public static final int TOP_INSET_PROGRESS_BAR = 5; /** Specifies the top inset for the progress text area. */ public static final int TOP_INSET_PROGRESS_TEXTAREA = 4; /** Specifies the top inset for the background image. */ public static final int TOP_INSET_BACKGROUND = 70; /** Specifies the top inset for the error message. */ public static final int TOP_INSET_ERROR_MESSAGE = 10; /** Specifies the top inset for the browse button. */ public static final int TOP_INSET_BROWSE = 5; /** Specifies the right inset for background image. */ public static final int RIGHT_INSET_BACKGROUND = 20; /** Specifies the left inset for the primary field. */ public static final int LEFT_INSET_PRIMARY_FIELD = 10; /** Specifies the left inset for the browse button. */ public static final int LEFT_INSET_BROWSE = 10; /** Specifies the left inset for radio subordinate panel. */ public static final int LEFT_INSET_RADIO_SUBORDINATE = 35; /** Specifies the left inset for the secondary field. */ public static final int LEFT_INSET_SECONDARY_FIELD = 5; /** Specifies the left inset for the background image. */ public static final int LEFT_INSET_BACKGROUND = 20; /** Specifies the left inset for the copy url button. */ public static final int LEFT_INSET_COPY_BUTTON = 10; /** Specifies the left inset for a subordinate subpanel. */ public static final int LEFT_INSET_SUBPANEL_SUBORDINATE = 30; /** Specifies the left inset for the progress bar. */ public static final int BOTTOM_INSET_PROGRESS_BAR = 10; /** Specifies the bottom inset for the background image. */ public static final int BOTTOM_INSET_BACKGROUND = 30; /** Specifies the top inset for a secondary field. */ public static final int BOTTOM_INSET_SECONDARY_FIELD = 5; /** Specifies the number of columns of a text field for a path. */ public static final int PATH_FIELD_SIZE = 20; /** Specifies the number of columns of a text field for a relative path. */ public static final int RELATIVE_PATH_FIELD_SIZE = 10; /** Specifies the number of columns of a text field for a host name. */ public static final int HOST_FIELD_SIZE = 20; /** Specifies the number of columns of a text field for a UID. */ public static final int UID_FIELD_SIZE = 15; /** Specifies the number of columns of a text field for a port. */ public static final int PORT_FIELD_SIZE = 5; /** Specifies the number of columns of a text field for a dn. */ public static final int DN_FIELD_SIZE = 20; /** Specifies the number of columns of a text field for a password. */ public static final int PASSWORD_FIELD_SIZE = 15; /** * Specifies the number of columns of a text field for the number of entries. */ public static final int NUMBER_ENTRIES_FIELD_SIZE = 7; /** Specifies the number of points for the width of the progress bar. */ public static final int PROGRESS_BAR_SIZE = 220; /** * Specifies the number of extra points that we add to the minimum size of the * dialog. */ public static final int EXTRA_DIALOG_HEIGHT = 75; private static final Insets BUTTONS_PANEL_INSETS = new Insets(5, 0, 5, 10); private static final Insets STEPS_PANEL_INSETS = new Insets(15, 10, 5, 10); private static final Insets CURRENT_STEP_PANEL_INSETS = new Insets(15, 15, 15, 15); private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0); /** Specifies the default background color. */ public static final Color DEFAULT_BACKGROUND = getColor(INFO_DEFAULT_BACKGROUND_COLOR.get()); /** Specifies the current step background color. */ public static final Color CURRENT_STEP_PANEL_BACKGROUND = getColor(INFO_CURRENT_STEP_PANEL_BACKGROUND_COLOR.get()); /** Specifies the default label color. */ public static final Color DEFAULT_LABEL_COLOR = getColor(INFO_DEFAULT_LABEL_COLOR.get()); /** Specifies the valid field color. */ public static final Color FIELD_VALID_COLOR = getColor(INFO_FIELD_VALID_COLOR.get()); /** Specifies the invalid field color. */ public static final Color FIELD_INVALID_COLOR = getColor(INFO_FIELD_INVALID_COLOR.get()); /** Specifies the read only text color. */ public static final Color READ_ONLY_COLOR = getColor(INFO_READ_ONLY_COLOR.get()); /** Specifies the check box text color. */ public static final Color CHECKBOX_COLOR = getColor(INFO_CHECKBOX_COLOR.get()); /** Specifies the progress text color. */ public static final Color PROGRESS_COLOR = getColor(INFO_PROGRESS_COLOR.get()); /** Specifies the instructions text color. */ public static final Color INSTRUCTIONS_COLOR = getColor(INFO_INSTRUCTIONS_COLOR.get()); /** Specifies the text field text color. */ public static final Color TEXTFIELD_COLOR = getColor(INFO_TEXTFIELD_COLOR.get()); /** Specifies the password field text color. */ public static final Color PASSWORDFIELD_COLOR = getColor(INFO_PASSWORDFIELD_COLOR.get()); /** Specifies the in-line help text color. */ public static final Color INLINE_HELP_COLOR = getColor(INFO_INLINE_HELP_COLOR.get()); /** Specifies the panel border color. */ public static final Color PANEL_BORDER_COLOR = getColor(INFO_PANEL_BORDER_COLOR.get()); /** Specifies the current step panel border. */ public static final Border CURRENT_STEP_PANEL_BORDER = BorderFactory.createMatteBorder(0, 2, 2, 0, PANEL_BORDER_COLOR); /** Specifies the text area border. */ public static final Border TEXT_AREA_BORDER = BorderFactory.createMatteBorder(1, 1, 1, 1, getColor(INFO_TEXT_AREA_BORDER_COLOR.get())); /** Specifies the dialog border. */ public static final Border DIALOG_PANEL_BORDER = BorderFactory.createMatteBorder(0, 0, 2, 0, PANEL_BORDER_COLOR); private static Font DEFAULT_FONT; static { try { DEFAULT_FONT = UIManager.getFont("Label.font").deriveFont(Font.PLAIN).deriveFont(12f); } catch (Throwable t) { DEFAULT_FONT = Font.decode("SansSerif-PLAIN-12"); } } /** * Specifies the font for the step which is not the current one in the steps * panel. */ public static final Font NOT_CURRENT_STEP_FONT = DEFAULT_FONT.deriveFont(14f); /** * Specifies the font for the step which is the current one in the steps * panel. */ public static final Font CURRENT_STEP_FONT = DEFAULT_FONT.deriveFont(14f).deriveFont(Font.BOLD); /** Specifies the font for the title of the current panel. */ public static final Font TITLE_FONT = DEFAULT_FONT.deriveFont(14f).deriveFont(Font.BOLD); /** Specifies the font for the instructions of the current panel. */ public static final Font INSTRUCTIONS_FONT = DEFAULT_FONT; /** Specifies the font for the instructions of the current panel. */ public static final Font INSTRUCTIONS_MONOSPACE_FONT = Font.decode("Monospaced-PLAIN-14"); /** Specifies the font for the primary valid field. */ public static final Font PRIMARY_FIELD_VALID_FONT = DEFAULT_FONT.deriveFont(Font.BOLD); /** Specifies the font for the secondary valid field. */ public static final Font SECONDARY_FIELD_VALID_FONT = DEFAULT_FONT; /** Specifies the font for the primary invalid field. */ public static final Font PRIMARY_FIELD_INVALID_FONT = DEFAULT_FONT.deriveFont(Font.BOLD | Font.ITALIC); /** Specifies the font for the secondary invalid field. */ public static final Font SECONDARY_FIELD_INVALID_FONT = DEFAULT_FONT.deriveFont(Font.ITALIC); /** Specifies the font for the secondary status field. */ public static final Font SECONDARY_STATUS_FONT = DEFAULT_FONT.deriveFont(Font.ITALIC); /** Specifies the font for read only text. */ public static final Font READ_ONLY_FONT = DEFAULT_FONT; /** Specifies the font for the check box text. */ public static final Font CHECKBOX_FONT = DEFAULT_FONT; /** Specifies the font for the progress text. */ public static final Font PROGRESS_FONT = DEFAULT_FONT; /** Specifies the font for the text field text. */ public static final Font TEXTFIELD_FONT = DEFAULT_FONT; /** Specifies the font for the password field text. */ public static final Font PASSWORD_FIELD_FONT = DEFAULT_FONT; /** Specifies the font for the points '....' in the progress panel. */ public static final Font PROGRESS_POINTS_FONT = DEFAULT_FONT.deriveFont(Font.BOLD); /** Specifies the font for the done text 'Done' in the progress panel. */ public static final Font PROGRESS_DONE_FONT = PROGRESS_POINTS_FONT; /** Specifies the font for the log messages in the progress panel. */ public static final Font PROGRESS_LOG_FONT = Font.decode("Monospaced-PLAIN-12"); /** Specifies the font for the error log messages in the progress panel. */ public static final Font PROGRESS_LOG_ERROR_FONT = Font.decode("Monospaced-PLAIN-12"); /** Specifies the font for the error messages in the progress panel. */ public static final Font PROGRESS_ERROR_FONT = DEFAULT_FONT.deriveFont(Font.BOLD); /** Specifies the font for the warning messages in the progress panel. */ public static final Font PROGRESS_WARNING_FONT = DEFAULT_FONT.deriveFont(Font.BOLD); /** Specifies the font for the stack trace in the progress panel. */ public static final Font STACK_FONT = DEFAULT_FONT; /** Specifies the font for the text in the WebBrowserErrorDialog. */ public static final Font ERROR_DIALOG_FONT = DEFAULT_FONT; /** Specifies the font for the text in the in-line help. */ public static final Font INLINE_HELP_FONT = DEFAULT_FONT.deriveFont((float) (DEFAULT_FONT.getSize() - 2)); private static final String SPAN_CLOSE = "</span>"; private static final String DIV_CLOSE = "</div>"; private static final String DIV_OPEN_ERROR_BACKGROUND = "<div style=\"color:#"+ INFO_DIV_OPEN_ERROR_BACKGROUND_1_COLOR.get()+ ";background-color:#"+ INFO_DIV_OPEN_ERROR_BACKGROUND_2_COLOR.get()+ ";padding:10px 10px 10px 10px;"+ "border-style:solid;border-width:3px;border-color:#"+ INFO_DIV_OPEN_ERROR_BACKGROUND_3_COLOR.get()+ ";vertical-align:middle;text-align:left\">"; private static final String DIV_OPEN_WARNING_BACKGROUND = DIV_OPEN_ERROR_BACKGROUND; private static final String DIV_OPEN_SUCCESSFUL_BACKGROUND = "<div style=\"color:#"+ INFO_DIV_OPEN_SUCCESSFUL_BACKGROUND_1_COLOR.get()+ ";background-color:#"+ INFO_DIV_OPEN_SUCCESSFUL_BACKGROUND_2_COLOR.get()+ ";padding:10px 10px 10px 10px;"+ "border-style:solid;border-width:3px;border-color:#"+ INFO_DIV_OPEN_SUCCESSFUL_BACKGROUND_3_COLOR.get()+ ";vertical-align:middle;text-align:left\">"; /** An HTML separator text that can be used in the progress panel. */ public static final String HTML_SEPARATOR = "<div style=\"font-size:1px;background-color:#"+ INFO_HTML_SEPARATOR_COLOR.get()+ ";margin:10px 5px 10px 5px;\"></div>"; private static final HashMap<IconType, ImageIcon> hmIcons = new HashMap<>(); /** The following enumeration contains the different icons that we can have. */ public enum IconType { /** Splash Icon. */ SPLASH, /** Current Step Icon. */ CURRENT_STEP, /** The icon displayed by the OS when the dialog is minimized. */ MINIMIZED, /** The icon displayed by the Mac OS when the dialog is minimized. */ MINIMIZED_MAC, /** The background icon. */ BACKGROUND, /** The warning icon. */ WARNING, /** The warning large icon. */ WARNING_LARGE, /** The error icon. */ ERROR, /** The error large icon. */ ERROR_LARGE, /** The information icon. */ INFORMATION, /** The information large icon. */ INFORMATION_LARGE, /** Icon to create subsection title in Status Panel. */ SUBSECTION_LEFT, /** Icon to create subsection title in Status Panel. */ SUBSECTION_RIGHT, /** Question icon. */ HELP_SMALL, /** Question medium icon. */ HELP_MEDIUM, /** Hourglass to display when the user must wait. */ WAIT, /** 8 x 8 Hourglass to display when the user must wait. */ WAIT_TINY, /** No icon. */ NO_ICON } /** * The following enumeration contains the different text styles that we can * have. A text style basically specifies the font and color to be used to * render the text. */ public enum TextStyle { /** Current Step label style for the steps panel. */ CURRENT_STEP, /** Not current Step label style for the steps panel. */ NOT_CURRENT_STEP, /** Title label style for the current step panel. */ TITLE, /** Primary field valid label style for the current step panel. */ PRIMARY_FIELD_VALID, /** Primary field invalid text style for the current step panel. */ PRIMARY_FIELD_INVALID, /** Secondary field valid text style for the current step panel. */ SECONDARY_FIELD_VALID, /** Secondary field invalid text style for the current step panel. */ SECONDARY_FIELD_INVALID, /** Status messages that appear near components. */ SECONDARY_STATUS, /** Textfield text style for the current step panel. */ TEXTFIELD, /** Password text style for the current step panel. */ PASSWORD_FIELD, /** Read only text style for the current step panel. */ READ_ONLY, /** Check box text text style for the current step panel. */ CHECKBOX, /** Progress messages text style for the current step panel. */ PROGRESS, /** Text style for the instructions. */ INSTRUCTIONS, /** In-line help style. */ INLINE_HELP, /** No text style. */ NO_STYLE } /** * This method initialize the look and feel. * * @throws Throwable * if there is a problem initializing the look and feel. */ public static void initializeLookAndFeel() throws Throwable { final Throwable[] ts = { null }; Runnable r = new Runnable() { @Override public void run() { System.setProperty("swing.aatext", "true"); try { String lf = UIManager.getSystemLookAndFeelClassName(); if ("com.sun.java.swing.plaf.motif.MotifLookAndFeel".equalsIgnoreCase(lf)) { lf = UIManager.getCrossPlatformLookAndFeelClassName(); } UIManager.setLookAndFeel(lf); } catch (Throwable t) { ts[0] = t; } JFrame.setDefaultLookAndFeelDecorated(false); } }; if (SwingUtilities.isEventDispatchThread()) { r.run(); } else { try { SwingUtilities.invokeAndWait(r); } catch (Throwable t) { ts[0] = t; } } if (ts[0] != null) { throw ts[0]; } } /** * This method initialize the look and feel and UI settings specific to quick * setup. * * @throws Throwable * if there is a problem initializing the look and feel. */ public static void initialize() throws Throwable { if (!initialized) { try { UIManager.put("OptionPane.background", getColor(INFO_OPTIONPANE_BACKGROUND_COLOR.get())); UIManager.put("Panel.background", getColor(INFO_PANEL_BACKGROUND_COLOR.get())); UIManager.put("ComboBox.background", getColor(INFO_COMBOBOX_BACKGROUND_COLOR.get())); } catch (Throwable t) { // This might occur when we do not get the display logger.warn(LocalizableMessage.raw("Error updating UIManager: " + t, t)); } initializeLookAndFeel(); initialized = true; } } /** * Creates a new JPanel. * * @return JPanel newly created */ public static JPanel makeJPanel() { JPanel pnl = new JPanel(); pnl.setOpaque(false); return pnl; } /** * Creates a JButton with the given label and tooltip. * * @param label * the text of the button. * @param tooltip * the tooltip of the button. * @return a JButton with the given label and tooltip. */ public static JButton makeJButton(LocalizableMessage label, LocalizableMessage tooltip) { JButton b = new JButton(); if (label != null) { b.setText(label.toString()); } if (tooltip != null) { b.setToolTipText(tooltip.toString()); } b.setOpaque(false); return b; } /** * Commodity method that returns a JLabel based on a LabelFieldDescriptor. * * @param desc * the LabelFieldDescriptor describing the JLabel. * @return a JLabel based on a LabelFieldDescriptor. */ public static JLabel makeJLabel(LabelFieldDescriptor desc) { UIFactory.TextStyle style; if (desc.getLabelType() == LabelFieldDescriptor.LabelType.PRIMARY) { style = UIFactory.TextStyle.PRIMARY_FIELD_VALID; } else { style = UIFactory.TextStyle.SECONDARY_FIELD_VALID; } return makeJLabel(UIFactory.IconType.NO_ICON, desc.getLabel(), style); } /** * Creates a JLabel with the given icon, text and text style. * * @param iconName * the icon. * @param text * the label text. * @param style * the text style. * @return a JLabel with the given icon, text and text style. */ public static JLabel makeJLabel(IconType iconName, LocalizableMessage text, TextStyle style) { JLabel l = new JLabel(); if (text != null) { l.setText(text.toString()); } ImageIcon icon = getImageIcon(iconName); l.setIcon(icon); LocalizableMessage tooltip = getIconTooltip(iconName); if (tooltip != null) { l.setToolTipText(tooltip.toString()); } setTextStyle(l, style); return l; } /** * Commodity method that returns a JTextComponent based on a * LabelFieldDescriptor. * * @param desc * the LabelFieldDescriptor describing the JTextField. * @param defaultValue * the default value used to initialize the JTextComponent. * @return a JTextComponent based on a LabelFieldDescriptor. */ public static JTextComponent makeJTextComponent(LabelFieldDescriptor desc, String defaultValue) { if (defaultValue == null) { defaultValue = ""; } switch (desc.getType()) { case TEXTFIELD: return makeJTextField( LocalizableMessage.raw(defaultValue), desc.getTooltip(), desc.getSize(), TextStyle.TEXTFIELD); case PASSWORD: return makeJPasswordField( LocalizableMessage.raw(defaultValue), desc.getTooltip(), desc.getSize(), TextStyle.PASSWORD_FIELD); case READ_ONLY: return makeTextPane(LocalizableMessage.raw(defaultValue), TextStyle.READ_ONLY); default: throw new IllegalArgumentException("Unknown type: " + desc.getType()); } } /** * Creates a JTextField with the given icon, tooltip text, size and text * style. * * @param text * the text. * @param tooltip * the tooltip text. * @param size * the number of columns of the JTextField. * @param style * the text style. * @return a JTextField with the given icon, tooltip text, size and text * style. */ public static JTextField makeJTextField( LocalizableMessage text, LocalizableMessage tooltip, int size, TextStyle style) { JTextField f = new JTextField(); updateTextFieldComponent(f, text, tooltip, size, style); f.addFocusListener(new TextFieldFocusListener(f)); return f; } /** * Creates a JPasswordField with the given icon, tooltip text, size and text * style. * * @param text * the text. * @param tooltip * the tooltip text. * @param size * the number of columns of the JPasswordField. * @param style * the text style. * @return a JPasswordField with the given icon, tooltip text, size and text * style. */ public static JPasswordField makeJPasswordField( LocalizableMessage text, LocalizableMessage tooltip, int size, TextStyle style) { JPasswordField f = new JPasswordField(); updateTextFieldComponent(f, text, tooltip, size, style); f.addFocusListener(new TextFieldFocusListener(f)); return f; } /** * Creates a JRadioButton with the given text, tooltip text and text style. * * @param text * the text of the radio button. * @param tooltip * the tooltip text. * @param style * the text style. * @return a JRadioButton with the given text, tooltip text and text style. */ public static JRadioButton makeJRadioButton(LocalizableMessage text, LocalizableMessage tooltip, TextStyle style) { JRadioButton rb = new JRadioButton(); rb.setOpaque(false); if (text != null) { rb.setText(text.toString()); } if (tooltip != null) { rb.setToolTipText(tooltip.toString()); } setTextStyle(rb, style); return rb; } /** * Creates a JCheckBox with the given text, tooltip text and text style. * * @param text * the text of the radio button. * @param tooltip * the tooltip text. * @param style * the text style. * @return a JCheckBox with the given text, tooltip text and text style. */ public static JCheckBox makeJCheckBox(LocalizableMessage text, LocalizableMessage tooltip, TextStyle style) { JCheckBox cb = new JCheckBox(); cb.setOpaque(false); if (text != null) { cb.setText(text.toString()); } if (tooltip != null) { cb.setToolTipText(tooltip.toString()); } setTextStyle(cb, style); return cb; } /** * Creates a JList. * * @param textStyle * the style to be used for the renderer. * @param <T> * The type of the JList elements * @return a JList. */ public static <T> JList<T> makeJList(TextStyle textStyle) { final JList<T> list = new JList<>(); list.setCellRenderer(makeCellRenderer(textStyle)); return list; } /** * Sets the specified text style to the component passed as parameter. * * @param l * the component to update. * @param style * the text style to use. */ public static void setTextStyle(JComponent l, TextStyle style) { switch (style) { case NOT_CURRENT_STEP: l.setFont(UIFactory.NOT_CURRENT_STEP_FONT); l.setForeground(DEFAULT_LABEL_COLOR); break; case CURRENT_STEP: l.setFont(UIFactory.CURRENT_STEP_FONT); l.setForeground(DEFAULT_LABEL_COLOR); break; case TITLE: l.setFont(UIFactory.TITLE_FONT); l.setForeground(DEFAULT_LABEL_COLOR); break; case PRIMARY_FIELD_VALID: l.setFont(UIFactory.PRIMARY_FIELD_VALID_FONT); l.setForeground(FIELD_VALID_COLOR); break; case PRIMARY_FIELD_INVALID: l.setFont(UIFactory.PRIMARY_FIELD_INVALID_FONT); l.setForeground(FIELD_INVALID_COLOR); break; case SECONDARY_FIELD_VALID: l.setFont(UIFactory.SECONDARY_FIELD_VALID_FONT); l.setForeground(FIELD_VALID_COLOR); break; case SECONDARY_FIELD_INVALID: l.setFont(UIFactory.SECONDARY_FIELD_INVALID_FONT); l.setForeground(FIELD_INVALID_COLOR); break; case SECONDARY_STATUS: l.setFont(UIFactory.SECONDARY_STATUS_FONT); l.setForeground(FIELD_VALID_COLOR); break; case READ_ONLY: l.setFont(UIFactory.READ_ONLY_FONT); l.setForeground(READ_ONLY_COLOR); break; case CHECKBOX: l.setFont(UIFactory.CHECKBOX_FONT); l.setForeground(CHECKBOX_COLOR); break; case PROGRESS: l.setFont(UIFactory.PROGRESS_FONT); l.setForeground(PROGRESS_COLOR); break; case INSTRUCTIONS: l.setFont(INSTRUCTIONS_FONT); l.setForeground(INSTRUCTIONS_COLOR); break; case TEXTFIELD: l.setFont(UIFactory.TEXTFIELD_FONT); l.setForeground(TEXTFIELD_COLOR); break; case PASSWORD_FIELD: l.setFont(UIFactory.PASSWORD_FIELD_FONT); l.setForeground(PASSWORDFIELD_COLOR); break; case INLINE_HELP: l.setFont(INLINE_HELP_FONT); l.setForeground(INLINE_HELP_COLOR); break; case NO_STYLE: // Do nothing break; default: throw new IllegalArgumentException("Unknown textStyle: " + style); } } /** * Returns the HTML string representing the provided IconType. * * @param iconType * the IconType for which we want the HTML representation. * @return the HTML string representing the provided IconType. */ public static String getIconHtml(IconType iconType) { String url = String.valueOf(UIFactory.class.getClassLoader().getResource(getIconPath(iconType))); LocalizableMessage description = getIconDescription(iconType); LocalizableMessage title = getIconTooltip(iconType); return "<img src=\"" + url + "\" alt=\"" + description + "\" align=\"middle\" title=\"" + title + "\" >"; } /** * Returns an ImageIcon object for the provided IconType. * * @param iconType * the IconType for which we want to obtain the ImageIcon. * @return the ImageIcon. */ public static ImageIcon getImageIcon(IconType iconType) { if (iconType == null) { iconType = IconType.NO_ICON; } ImageIcon icon = hmIcons.get(iconType); if (icon == null && iconType != IconType.NO_ICON) { String path = getIconPath(iconType); LocalizableMessage description = getIconDescription(iconType); try { Image im = Toolkit.getDefaultToolkit().createImage(UIFactory.class.getClassLoader().getResource(path)); icon = new ImageIcon(im); String ds = description != null ? description.toString() : null; icon.setDescription(ds); hmIcons.put(iconType, icon); } catch (Exception ex) { ex.printStackTrace(); // A bug: this should not happen throw new IllegalStateException("Could not load icon for path " + path, ex); } } return icon; } /** * Returns a JEditorPane that works with the provided scroll. * * @see ProgressJEditorPane * @param scroll * the scroll that will contain the JEditorPane. * @return a JEditorPane that works with the provided scroll. */ public static JEditorPane makeProgressPane(JScrollPane scroll) { return new ProgressJEditorPane(scroll); } /** * Returns a read only JEditorPane containing the provided text with the * provided font. The JEditorPane will assume that the text is HTML text. * * @param text * the text to be used to initialize the JEditorPane contents. * @param font * the font to be used. * @return a read only JEditorPane containing the provided text with the * provided font. */ public static JEditorPane makeHtmlPane(LocalizableMessage text, Font font) { return makeHtmlPane(text, null, font); } /** * Returns a read only JEditorPane containing the provided text with the * provided font. The JEditorPane will assume that the text is HTML text. * * @param text * the text to be used to initialize the JEditorPane contents. * @param ek * HTMLEditor kit used for the new HTML pane * @param font * the font to be used. * @return a read only JEditorPane containing the provided text with the * provided font. */ public static JEditorPane makeHtmlPane(LocalizableMessage text, HTMLEditorKit ek, Font font) { JEditorPane pane = new JEditorPane(); if (ek != null) { pane.setEditorKit(ek); } pane.setContentType("text/html"); String s = text != null ? String.valueOf(text) : null; pane.setText(applyFontToHtmlWithDiv(s, font)); pane.setEditable(false); pane.setBorder(new EmptyBorder(0, 0, 0, 0)); return pane; } /** * Returns a read only JEditorPane containing the provided text with the * provided TextStyle. The JEditorPane will assume that the text is plain * text. * * @param text * the text to be used to initialize the JEditorPane contents. * @param style * the TextStyle to be used. * @return a read only JEditorPane containing the provided text with the * provided TextStyle. */ public static JEditorPane makeTextPane(LocalizableMessage text, TextStyle style) { String s = text != null ? String.valueOf(text) : null; JEditorPane pane = new JEditorPane("text/plain", s); setTextStyle(pane, style); pane.setEditable(false); pane.setBorder(new EmptyBorder(0, 0, 0, 0)); pane.setOpaque(false); return pane; } /** * Returns a JScrollPane that contains the provided component. The scroll pane * will not contain any border. * * @param comp * the component contained in the scroll pane. * @return a JScrollPane that contains the provided component. The scroll pane * will not contain any border. */ public static JScrollPane createBorderLessScrollBar(Component comp) { JScrollPane scroll = new JScrollPane(comp); scroll.setBorder(new EmptyBorder(0, 0, 0, 0)); scroll.setViewportBorder(new EmptyBorder(0, 0, 0, 0)); scroll.setOpaque(false); scroll.getViewport().setOpaque(false); scroll.getViewport().setBackground(DEFAULT_BACKGROUND); scroll.setBackground(DEFAULT_BACKGROUND); setScrollIncrementUnit(scroll); return scroll; } /** * Sets the scroll increment unit for the scroll. * * @param scroll * the scroll to be updated. */ public static void setScrollIncrementUnit(JScrollPane scroll) { if (scroll.getVerticalScrollBar() != null) { int increment = scroll.getVerticalScrollBar().getUnitIncrement(); if (increment < 16) { scroll.getVerticalScrollBar().setUnitIncrement(16); } } } /** * Return empty insets. * * @return empty insets. */ public static Insets getEmptyInsets() { return (Insets) EMPTY_INSETS.clone(); } /** * Returns the insets to be used for the button panel. * * @return the insets to be used for the button panel. */ public static Insets getButtonsPanelInsets() { return (Insets) BUTTONS_PANEL_INSETS.clone(); } /** * Returns the insets to be used for the steps panel. * * @return the insets to be used for the steps panel. */ public static Insets getStepsPanelInsets() { return (Insets) STEPS_PANEL_INSETS.clone(); } /** * Returns the insets to be used for the current step panel. * * @return the insets to be used for the current step panel. */ public static Insets getCurrentStepPanelInsets() { return (Insets) CURRENT_STEP_PANEL_INSETS.clone(); } /** * Returns a String that contains the html passed as parameter with a span * applied. The span style corresponds to the Font specified as parameter. The * goal of this method is to be able to specify a font for an HTML string. * * @param html * the original html text. * @param font * the font to be used to generate the new HTML. * @return a string that represents the original HTML with the font specified * as parameter. */ public static String applyFontToHtml(String html, Font font) { StringBuilder buf = new StringBuilder(); buf.append("<span style=\"").append(getFontStyle(font)).append("\">").append(html).append(SPAN_CLOSE); return buf.toString(); } /** * Returns a String that contains the html passed as parameter with a div * applied. The div style corresponds to the Font specified as parameter. The * goal of this method is to be able to specify a font for an HTML string. * * @param html * the original html text. * @param font * the font to be used to generate the new HTML. * @return a string that represents the original HTML with the font specified * as parameter. */ public static String applyFontToHtmlWithDiv(String html, Font font) { StringBuilder buf = new StringBuilder(); buf.append("<div style=\"").append(getFontStyle(font)).append("\">").append(html).append(DIV_CLOSE); return buf.toString(); } /** * Returns the HTML style representation for the given font. * * @param font * the font for which we want to get an HTML style representation. * @return the HTML style representation for the given font. */ private static String getFontStyle(Font font) { StringBuilder buf = new StringBuilder(); buf.append("font-family:").append(font.getName()).append(";font-size:").append(font.getSize()).append("pt"); if (font.isItalic()) { buf.append(";font-style:italic"); } if (font.isBold()) { buf.append(";font-weight:bold;"); } return buf.toString(); } /** * Returns the html text passed as parameter with the error background applied * to it. * * @param html * the original html. * @return the html text passed as parameter with the error background applied * to it. */ public static String applyErrorBackgroundToHtml(String html) { return DIV_OPEN_ERROR_BACKGROUND + html + DIV_CLOSE; } /** * Returns the html text passed as parameter with the warning background * applied to it. * * @param html * the original html. * @return the html text passed as parameter with the warning background * applied to it. */ public static String applyWarningBackgroundToHtml(String html) { return DIV_OPEN_WARNING_BACKGROUND + html + DIV_CLOSE; } /** * Returns the html text passed as parameter with the success background * applied to it. * * @param html * the original html. * @return the html text passed as parameter with the success background * applied to it. */ public static String applySuccessfulBackgroundToHtml(String html) { return DIV_OPEN_SUCCESSFUL_BACKGROUND + html + DIV_CLOSE; } /** * Returns the html text passed as parameter with some added margin. * * @param html * the original html text. * @param top * the top margin. * @param right * the right margin. * @param bottom * the bottom margin. * @param left * the left margin. * @return the html text passed as parameter with some added margin. */ public static String applyMargin(String html, int top, int right, int bottom, int left) { return "<div style=\"margin:" + top + "px " + right + "px " + bottom + "px " + left + "px;\">" + html + DIV_CLOSE; } /** * Updates the provided field with all the other arguments. * * @param field * the field to be modified. * @param text * the new text of the field. * @param tooltip * the new tooltip text of the field. * @param size * the new size of the field. * @param textStyle * the new TextStyle of the field. */ private static void updateTextFieldComponent( JTextField field, LocalizableMessage text, LocalizableMessage tooltip, int size, TextStyle textStyle) { field.setColumns(size); if (text != null) { field.setText(text.toString()); } if (tooltip != null) { field.setToolTipText(tooltip.toString()); } if (textStyle != null) { setTextStyle(field, textStyle); } } private static Color getColor(LocalizableMessage l) { String s = String.valueOf(l); String[] colors = s.split(","); int r = Integer.parseInt(colors[0].trim()); int g = Integer.parseInt(colors[1].trim()); int b = Integer.parseInt(colors[2].trim()); return new Color(r, g, b); } /** * Returns the parent package path. This is used to retrieve the icon * qualified names. * * @return the parent package path. */ private static String getParentPackagePath() { if (parentPackagePath == null) { String packageName = UIFactory.class.getPackage().getName(); int lastDot = packageName.lastIndexOf('.'); String parentPackage = packageName.substring(0, lastDot); parentPackagePath = parentPackage.replace(".", "/"); } return parentPackagePath; } /** * Returns the path of the icon for the given IconType. * * @param iconType * the IconType for which we want to get the path. * @return the path of the icon for the given IconType. */ private static String getIconPath(IconType iconType) { return getParentPackagePath() + "/" + getKey(iconType); } private static LocalizableMessage getKey(IconType iconType) { switch (iconType) { case CURRENT_STEP: return INFO_CURRENT_STEP_ICON.get(); case SPLASH: return INFO_SPLASH_ICON.get(); case BACKGROUND: return INFO_BACKGROUND_ICON.get(); case MINIMIZED: return INFO_MINIMIZED_ICON.get(); case MINIMIZED_MAC: return INFO_MINIMIZED_MAC_ICON.get(); case WARNING: return INFO_WARNING_ICON.get(); case WARNING_LARGE: return INFO_WARNING_LARGE_ICON.get(); case INFORMATION: return INFO_INFORMATION_ICON.get(); case INFORMATION_LARGE: return INFO_INFORMATION_LARGE_ICON.get(); case SUBSECTION_LEFT: return INFO_SUBSECTION_LEFT_ICON.get(); case SUBSECTION_RIGHT: return INFO_SUBSECTION_RIGHT_ICON.get(); case HELP_SMALL: return INFO_HELP_SMALL_ICON.get(); case HELP_MEDIUM: return INFO_HELP_MEDIUM_ICON.get(); case ERROR: return INFO_ERROR_ICON.get(); case ERROR_LARGE: return INFO_ERROR_LARGE_ICON.get(); case WAIT_TINY: return INFO_WAIT_TINY.get(); case WAIT: return INFO_WAIT.get(); default: throw new IllegalArgumentException("Unknown iconName: " + iconType); } } /** * Returns the icon description for the given IconType. * * @param iconType * the IconType for which we want to get the description. * @return the icon description for the given IconType. */ private static LocalizableMessage getIconDescription(IconType iconType) { switch (iconType) { case CURRENT_STEP: return INFO_CURRENT_STEP_ICON_DESCRIPTION.get(); case SPLASH: return INFO_SPLASH_ICON_DESCRIPTION.get(); case BACKGROUND: return INFO_BACKGROUND_ICON_DESCRIPTION.get(); case MINIMIZED: return INFO_MINIMIZED_ICON_DESCRIPTION.get(); case MINIMIZED_MAC: return INFO_MINIMIZED_ICON_DESCRIPTION.get(); case WARNING: return INFO_WARNING_ICON_DESCRIPTION.get(); case WARNING_LARGE: return INFO_WARNING_ICON_DESCRIPTION.get(); case ERROR: return INFO_ERROR_ICON_DESCRIPTION.get(); case ERROR_LARGE: return INFO_ERROR_ICON_DESCRIPTION.get(); case INFORMATION: return INFO_INFORMATION_ICON_DESCRIPTION.get(); case INFORMATION_LARGE: return INFO_INFORMATION_ICON_DESCRIPTION.get(); case SUBSECTION_LEFT: return INFO_SUBSECTION_LEFT_ICON_DESCRIPTION.get(); case SUBSECTION_RIGHT: return INFO_SUBSECTION_RIGHT_ICON_DESCRIPTION.get(); case HELP_SMALL: case HELP_MEDIUM: return INFO_HELP_SMALL_ICON_DESCRIPTION.get(); case WAIT_TINY: return INFO_HELP_WAIT_DESCRIPTION.get(); case WAIT: return INFO_HELP_WAIT_DESCRIPTION.get(); case NO_ICON: return null; default: throw new IllegalArgumentException("Unknown iconName: " + iconType); } } /** * Returns the icon tooltip text for the given IconType. * * @param iconType * the IconType for which we want to get the tooltip text. * @return the icon tooltip text for the given IconType. */ private static LocalizableMessage getIconTooltip(IconType iconType) { if (iconType == null) { iconType = IconType.NO_ICON; } switch (iconType) { case CURRENT_STEP: return INFO_CURRENT_STEP_ICON_TOOLTIP.get(); case SPLASH: return INFO_SPLASH_ICON_TOOLTIP.get(); case BACKGROUND: return INFO_BACKGROUND_ICON_TOOLTIP.get(); case MINIMIZED: return INFO_MINIMIZED_ICON_TOOLTIP.get(); case MINIMIZED_MAC: return INFO_MINIMIZED_ICON_TOOLTIP.get(); case WARNING: return INFO_WARNING_ICON_TOOLTIP.get(); case WARNING_LARGE: return INFO_WARNING_ICON_TOOLTIP.get(); case ERROR: return INFO_ERROR_ICON_TOOLTIP.get(); case ERROR_LARGE: return INFO_ERROR_ICON_TOOLTIP.get(); case INFORMATION: return INFO_INFORMATION_ICON_TOOLTIP.get(); case INFORMATION_LARGE: return INFO_INFORMATION_ICON_TOOLTIP.get(); case SUBSECTION_LEFT: case SUBSECTION_RIGHT: case HELP_SMALL: case HELP_MEDIUM: case WAIT_TINY: case WAIT: case NO_ICON: return null; default: throw new IllegalArgumentException("Unknown iconName: " + iconType); } } private static <T> ListCellRenderer<T> makeCellRenderer(final TextStyle textStyle) { return new ListCellRenderer<T>() { @Override public Component getListCellRendererComponent(JList<? extends T> list, T value, int index, boolean isSelected, boolean cellHasFocus) { final JLabel l = makeJLabel(IconType.NO_ICON, LocalizableMessage.raw(value.toString()), textStyle); l.setBorder(new EmptyBorder(TOP_INSET_SECONDARY_FIELD, 0, 0, 0)); return l; } }; } } /** * This class has been written to have a better behaviour with the scroll pane * than the one we have by default in the case of the progress panel. * <p> * With the default scroll pane behaviour when we set a new text in a * JEditorPane the scroll bar goes systematically up. With this implementation * the expected behaviour is: * <p> * If the scroll bar is at the bottom we will display the latest text contained * in the pane. * <p> * If the scroll bar is not at the bottom we will keep on displaying the same * thing that the user is viewing. * <p> * This behaviour allows the user to check the log content even when the * installation/uninstallation is still running and sending new log messages. */ class ProgressJEditorPane extends JEditorPane { private static final long serialVersionUID = 1221976708322628818L; private final JScrollPane scroll; private boolean ignoreScrollToVisible; /** * Constructor for the ProgressJEditorPane. * * @param scroll * the JScrollPane that will contain this editor pane. */ public ProgressJEditorPane(JScrollPane scroll) { super("text/html", null); this.scroll = scroll; setEditable(false); setBorder(new EmptyBorder(3, 3, 3, 3)); } @Override public void setText(String text) { // Scroll can be null in constructor if (scroll != null) { /* * We apply the following policy: if the user is displaying the latest * part of the JTextArea we assume that when we add text (s)he wants to * see the text that is added, if not we assume that (s)he want to keep * viewing what is visible and so we ignore the next scrollRectToVisible * call (that will be done inside JTextArea.setText method). */ JScrollBar vBar = scroll.getVerticalScrollBar(); ignoreScrollToVisible = vBar != null && vBar.getValue() + vBar.getVisibleAmount() < 0.97 * vBar.getMaximum(); super.setText(text); } } @Override public void scrollRectToVisible(Rectangle rect) { if (!ignoreScrollToVisible) { super.scrollRectToVisible(rect); ignoreScrollToVisible = false; } } } /** * A class used to be able to select the contents of the text field when it gets * the focus. */ class TextFieldFocusListener implements FocusListener { private final JTextField tf; /** * The constructor for this listener. * * @param tf * the text field associated with this listener. */ TextFieldFocusListener(JTextField tf) { this.tf = tf; } @Override public void focusGained(FocusEvent e) { if (tf.getText() == null || "".equals(tf.getText())) { tf.setText(" "); tf.selectAll(); tf.setText(""); } else { tf.selectAll(); } } @Override public void focusLost(FocusEvent e) { } }