/**
   * The aggregate components which compise the combo box are unregistered and uninitialized. This
   * method is called as part of the UI uninstallation process.
   */
  protected void uninstallComponents() {
    iconLabel = null;
    errorMessage = null;
    closeButton = null;
    reportButton = null;

    detailButton.removeActionListener(detailListener);
    detailButton = null;

    details.setTransferHandler(null);
    details = null;

    detailsScrollPane.removeAll();
    detailsScrollPane = null;

    detailsPanel.setLayout(null);
    detailsPanel.removeAll();
    detailsPanel = null;

    copyToClipboardButton.removeActionListener(copyToClipboardListener);
    copyToClipboardButton = null;

    pane.removeAll();
    pane.setLayout(null);
    pane.setBorder(null);
  }
  /**
   * Creates and initializes the components which make up the aggregate combo box. This method is
   * called as part of the UI installation process.
   */
  protected void installComponents() {
    iconLabel = new JLabel(pane.getIcon());

    errorMessage = new JEditorPane();
    errorMessage.setEditable(false);
    errorMessage.setContentType("text/html");
    errorMessage.setEditorKitForContentType("text/plain", new StyledEditorKit());
    errorMessage.setEditorKitForContentType("text/html", new HTMLEditorKit());

    errorMessage.setOpaque(false);
    errorMessage.putClientProperty(JXEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE);

    closeButton =
        new JButton(
            UIManagerExt.getString(CLASS_NAME + ".ok_button_text", errorMessage.getLocale()));

    reportButton = new EqualSizeJButton(pane.getActionMap().get(JXErrorPane.REPORT_ACTION_KEY));

    detailButton =
        new EqualSizeJButton(
            UIManagerExt.getString(CLASS_NAME + ".details_expand_text", errorMessage.getLocale()));

    details = new JXEditorPane();
    details.setContentType("text/html");
    details.putClientProperty(JXEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE);
    details.setTransferHandler(createDetailsTransferHandler(details));
    detailsScrollPane = new JScrollPane(details);
    detailsScrollPane.setPreferredSize(new Dimension(10, 250));
    details.setEditable(false);
    detailsPanel = new JPanel();
    detailsPanel.setVisible(false);
    copyToClipboardButton =
        new JButton(
            UIManagerExt.getString(
                CLASS_NAME + ".copy_to_clipboard_button_text", errorMessage.getLocale()));
    copyToClipboardListener =
        new ActionListener() {
          public void actionPerformed(ActionEvent ae) {
            details.copy();
          }
        };
    copyToClipboardButton.addActionListener(copyToClipboardListener);

    detailsPanel.setLayout(createDetailPanelLayout());
    detailsPanel.add(detailsScrollPane);
    detailsPanel.add(copyToClipboardButton);

    // Create error scroll pane. Make sure this happens before call to createErrorPaneLayout() in
    // case any extending
    // class wants to manipulate the component there.
    errorScrollPane = new JScrollPane(errorMessage);
    errorScrollPane.setBorder(new EmptyBorder(0, 0, 5, 0));
    errorScrollPane.setOpaque(false);
    errorScrollPane.getViewport().setOpaque(false);

    // initialize the gui. Most of this code is similar between Mac and PC, but
    // where they differ protected methods have been written allowing the
    // mac implementation to alter the layout of the dialog.
    pane.setLayout(createErrorPaneLayout());

    // An empty border which constitutes the padding from the edge of the
    // dialog to the content. All content that butts against this border should
    // not be padded.
    Insets borderInsets = new Insets(16, 24, 16, 17);
    pane.setBorder(
        BorderFactory.createEmptyBorder(
            borderInsets.top, borderInsets.left, borderInsets.bottom, borderInsets.right));

    // add the JLabel responsible for displaying the icon.
    // TODO: in the future, replace this usage of a JLabel with a JXImagePane,
    // which may add additional "coolness" such as allowing the user to drag
    // the image off the dialog onto the desktop. This kind of coolness is common
    // in the mac world.
    pane.add(iconLabel);
    pane.add(errorScrollPane);
    pane.add(closeButton);
    pane.add(reportButton);
    reportButton.setVisible(false); // not visible by default
    pane.add(detailButton);
    pane.add(detailsPanel);

    // make the buttons the same size
    EqualSizeJButton[] buttons =
        new EqualSizeJButton[] {(EqualSizeJButton) detailButton, (EqualSizeJButton) reportButton};
    ((EqualSizeJButton) reportButton).setGroup(buttons);
    ((EqualSizeJButton) detailButton).setGroup(buttons);

    reportButton.setMinimumSize(reportButton.getPreferredSize());
    detailButton.setMinimumSize(detailButton.getPreferredSize());

    // set the event handling
    detailListener = new DetailsClickEvent();
    detailButton.addActionListener(detailListener);
  }