/** Reloads color info. */
  @Override
  public void loadSkin() {
    super.loadSkin();

    TAB_HIGHLIGHT_FOREGROUND_COLOR =
        new Color(DesktopUtilActivator.getResources().getColor("service.gui.TAB_TITLE_HIGHLIGHT"));

    TAB_SELECTED_FOREGROUND_COLOR =
        new Color(DesktopUtilActivator.getResources().getColor("service.gui.TAB_TITLE_SELECTED"));
  }
  /**
   * Initializes the icon image.
   *
   * @param icon the icon to show on the left of the window
   */
  private void initIcon(ImageIcon icon) {
    // If an icon isn't provided set the application logo icon by default.
    if (icon == null)
      icon =
          DesktopUtilActivator.getResources().getImage("service.gui.SIP_COMMUNICATOR_LOGO_64x64");

    JLabel iconLabel = new JLabel(icon);

    iconLabel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));

    iconLabel.setAlignmentY(Component.TOP_ALIGNMENT);

    JPanel iconPanel = new TransparentPanel(new BorderLayout());
    iconPanel.add(iconLabel, BorderLayout.NORTH);

    getContentPane().add(iconPanel, BorderLayout.WEST);
  }
  /**
   * Creates the subscribe label.
   *
   * @param linkName the link name
   * @return the newly created subscribe label
   */
  private Component createWebSignupLabel(String linkName, final String linkURL) {
    JLabel subscribeLabel =
        new JLabel("<html><a href=''>" + linkName + "</a></html>", JLabel.RIGHT);

    subscribeLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
    subscribeLabel.setToolTipText(
        DesktopUtilActivator.getResources()
            .getI18NString("plugin.simpleaccregwizz.SPECIAL_SIGNUP"));
    subscribeLabel.addMouseListener(
        new MouseAdapter() {
          public void mousePressed(MouseEvent e) {
            try {
              DesktopUtilActivator.getBrowserLauncher().openURL(linkURL);
            } catch (UnsupportedOperationException ex) {
              // This should not happen, because we check if the
              // operation is supported, before adding the sign
              // up.
              logger.error("The web sign up is not supported.", ex);
            }
          }
        });
    return subscribeLabel;
  }
  /** Constructs the <tt>LoginWindow</tt>. */
  private void init() {
    String title;

    if (windowTitle != null) title = windowTitle;
    else
      title =
          DesktopUtilActivator.getResources()
              .getI18NString("service.gui.AUTHENTICATION_WINDOW_TITLE", new String[] {server});

    String text;
    if (windowText != null) text = windowText;
    else
      text =
          DesktopUtilActivator.getResources()
              .getI18NString("service.gui.AUTHENTICATION_REQUESTED_SERVER", new String[] {server});

    String uinText;
    if (usernameLabelText != null) uinText = usernameLabelText;
    else uinText = DesktopUtilActivator.getResources().getI18NString("service.gui.IDENTIFIER");

    String passText;
    if (passwordLabelText != null) passText = passwordLabelText;
    else passText = DesktopUtilActivator.getResources().getI18NString("service.gui.PASSWORD");

    setTitle(title);

    infoTextArea.setEditable(false);
    infoTextArea.setOpaque(false);
    infoTextArea.setLineWrap(true);
    infoTextArea.setWrapStyleWord(true);
    infoTextArea.setFont(infoTextArea.getFont().deriveFont(Font.BOLD));
    infoTextArea.setText(text);
    infoTextArea.setAlignmentX(0.5f);

    JLabel uinLabel = new JLabel(uinText);
    uinLabel.setFont(uinLabel.getFont().deriveFont(Font.BOLD));

    JLabel passwdLabel = new JLabel(passText);
    passwdLabel.setFont(passwdLabel.getFont().deriveFont(Font.BOLD));

    TransparentPanel labelsPanel = new TransparentPanel(new GridLayout(0, 1, 8, 8));

    labelsPanel.add(uinLabel);
    labelsPanel.add(passwdLabel);

    TransparentPanel textFieldsPanel = new TransparentPanel(new GridLayout(0, 1, 8, 8));

    textFieldsPanel.add(uinValue);
    textFieldsPanel.add(passwdField);

    JPanel southFieldsPanel = new TransparentPanel(new GridLayout(1, 2));

    this.rememberPassCheckBox.setOpaque(false);
    this.rememberPassCheckBox.setBorder(null);

    southFieldsPanel.add(rememberPassCheckBox);
    if (signupLink != null && signupLink.length() > 0)
      southFieldsPanel.add(
          createWebSignupLabel(
              DesktopUtilActivator.getResources().getI18NString("plugin.simpleaccregwizz.SIGNUP"),
              signupLink));
    else southFieldsPanel.add(new JLabel());

    boolean allowRememberPassword = true;

    String allowRemPassStr =
        DesktopUtilActivator.getResources().getSettingsString(PNAME_ALLOW_SAVE_PASSWORD);
    if (allowRemPassStr != null) {
      allowRememberPassword = Boolean.parseBoolean(allowRemPassStr);
    }
    allowRememberPassword =
        DesktopUtilActivator.getConfigurationService()
            .getBoolean(PNAME_ALLOW_SAVE_PASSWORD, allowRememberPassword);

    setAllowSavePassword(allowRememberPassword);

    JPanel buttonPanel = new TransparentPanel(new FlowLayout(FlowLayout.CENTER));

    buttonPanel.add(loginButton);
    buttonPanel.add(cancelButton);

    JPanel southEastPanel = new TransparentPanel(new BorderLayout());
    southEastPanel.add(buttonPanel, BorderLayout.EAST);

    TransparentPanel mainPanel = new TransparentPanel(new BorderLayout(10, 10));

    mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 0, 20, 20));

    JPanel mainFieldsPanel = new TransparentPanel(new BorderLayout(0, 10));
    mainFieldsPanel.add(labelsPanel, BorderLayout.WEST);
    mainFieldsPanel.add(textFieldsPanel, BorderLayout.CENTER);
    mainFieldsPanel.add(southFieldsPanel, BorderLayout.SOUTH);

    mainPanel.add(infoTextArea, BorderLayout.NORTH);
    mainPanel.add(mainFieldsPanel, BorderLayout.CENTER);
    mainPanel.add(southEastPanel, BorderLayout.SOUTH);

    this.getContentPane().add(mainPanel, BorderLayout.EAST);

    this.loginButton.setName("ok");
    this.cancelButton.setName("cancel");
    if (loginButton.getPreferredSize().width > cancelButton.getPreferredSize().width)
      cancelButton.setPreferredSize(loginButton.getPreferredSize());
    else loginButton.setPreferredSize(cancelButton.getPreferredSize());

    this.loginButton.setMnemonic(
        DesktopUtilActivator.getResources().getI18nMnemonic("service.gui.OK"));
    this.cancelButton.setMnemonic(
        DesktopUtilActivator.getResources().getI18nMnemonic("service.gui.CANCEL"));

    this.loginButton.addActionListener(this);
    this.cancelButton.addActionListener(this);

    this.getRootPane().setDefaultButton(loginButton);
  }
/**
 * The <tt>AuthenticationWindow</tt> is the window where the user should type his user identifier
 * and password to login.
 *
 * @author Yana Stamcheva
 */
public class AuthenticationWindow extends SIPCommDialog implements ActionListener {
  private static final long serialVersionUID = 1L;

  /** Used for logging. */
  private static Logger logger = Logger.getLogger(AuthenticationWindow.class);

  /** Info text area. */
  private final JTextArea infoTextArea = new JTextArea();

  /** The uin component. */
  private JComponent uinValue;

  /** The password field. */
  private final JPasswordField passwdField = new JPasswordField(15);

  /** The login button. */
  private final JButton loginButton =
      new JButton(DesktopUtilActivator.getResources().getI18NString("service.gui.OK"));

  /** The cancel button. */
  private final JButton cancelButton =
      new JButton(DesktopUtilActivator.getResources().getI18NString("service.gui.CANCEL"));

  /** The check box indicating if the password should be remembered. */
  private final JCheckBox rememberPassCheckBox =
      new SIPCommCheckBox(
          DesktopUtilActivator.getResources().getI18NString("service.gui.REMEMBER_PASSWORD"),
          DesktopUtilActivator.getConfigurationService()
              .getBoolean(PNAME_SAVE_PASSWORD_TICKED, false));

  /**
   * Property to disable/enable allow save password option in authentication window. By default it
   * is enabled.
   */
  private static final String PNAME_ALLOW_SAVE_PASSWORD =
      "******";

  /**
   * Property to set whether the save password option in the authentication window is ticked by
   * default or not. By default it is not ticked
   */
  private static final String PNAME_SAVE_PASSWORD_TICKED =
      "net.java.sip.communicator.util.swing.auth.SAVE_PASSWORD_TICKED";

  /** The name of the server, for which this authentication window is about. */
  private String server;

  /** The user name. */
  private String userName;

  /** The password. */
  private char[] password;

  /** Indicates if the password should be remembered. */
  private boolean isRememberPassword = false;

  /** Indicates if the window has been canceled. */
  private boolean isCanceled = false;

  /** A lock used to synchronize data setting. */
  private final Object lock = new Object();

  /** The condition that decides whether to continue waiting for data. */
  private boolean buttonClicked = false;

  /** Used to override default Authentication window title. */
  private String windowTitle = null;

  /** Used to override default window text. */
  private String windowText = null;

  /** Used to override username label text. */
  private String usernameLabelText = null;

  /** Used to override password label text. */
  private String passwordLabelText = null;

  /** The sign up link if specified. */
  private String signupLink = null;

  /**
   * Creates an instance of the <tt>LoginWindow</tt>.
   *
   * @param server the server name
   * @param isUserNameEditable indicates if the user name is editable
   * @param icon the icon to display on the left of the authentication window
   */
  public AuthenticationWindow(String server, boolean isUserNameEditable, ImageIcon icon) {
    this(null, null, server, isUserNameEditable, false, icon, null, null, null, null, null, null);
  }

  /**
   * Creates an instance of the <tt>LoginWindow</tt>.
   *
   * @param server the server name
   * @param isUserNameEditable indicates if the user name is editable
   * @param icon the icon to display on the left of the authentication window
   * @param windowTitle customized window title
   * @param windowText customized window text
   * @param usernameLabelText customized username field label text
   * @param passwordLabelText customized password field label text
   * @param errorMessage an error message if this dialog is shown to indicate the user that
   *     something went wrong
   * @param signupLink an URL that allows the user to sign up
   */
  private AuthenticationWindow(
      String userName,
      char[] password,
      String server,
      boolean isUserNameEditable,
      boolean isRememberPassword,
      ImageIcon icon,
      String windowTitle,
      String windowText,
      String usernameLabelText,
      String passwordLabelText,
      String errorMessage,
      String signupLink) {
    super(false);

    this.windowTitle = windowTitle;
    this.windowText = windowText;
    this.usernameLabelText = usernameLabelText;
    this.passwordLabelText = passwordLabelText;
    this.isRememberPassword = isRememberPassword;
    this.signupLink = signupLink;

    init(userName, password, server, isUserNameEditable, icon, errorMessage);
  }

  /**
   * Initializes this authentication window.
   *
   * @param server the server
   * @param isUserNameEditable indicates if the user name is editable
   * @param icon the icon to show on the authentication window
   */
  private void init(
      String userName,
      char[] password,
      String server,
      boolean isUserNameEditable,
      ImageIcon icon,
      String errorMessage) {
    this.server = server;

    initIcon(icon);

    if (!isUserNameEditable) this.uinValue = new JLabel();
    else this.uinValue = new JTextField();

    this.init();

    this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

    this.enableKeyActions();

    this.setResizable(false);

    /*
     * Workaround for the following bug:
     * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4446522
     * Need to pack() the window after it's opened in order to obtain the
     * correct size of our infoTextArea, otherwise window size is wrong and
     * buttons on the south are cut.
     */
    this.addWindowListener(
        new WindowAdapter() {
          public void windowOpened(WindowEvent e) {
            pack();
            removeWindowListener(this);
          }
        });

    if (OSUtils.IS_MAC) getRootPane().putClientProperty("apple.awt.brushMetalLook", Boolean.TRUE);

    if (userName != null) {
      if (uinValue instanceof JLabel) ((JLabel) uinValue).setText(userName);
      else if (uinValue instanceof JTextField) ((JTextField) uinValue).setText(userName);
    }

    if (password != null) passwdField.setText(new String(password));

    if (errorMessage != null) {
      this.infoTextArea.setForeground(Color.RED);
      this.infoTextArea.setText(errorMessage);
    }
  }

  /**
   * Creates an instance of the <tt>LoginWindow</tt>.
   *
   * @param userName the user name to set by default
   * @param password the password to set by default
   * @param server the server name this authentication window is about
   * @param isUserNameEditable indicates if the user name should be editable by the user or not
   * @param icon the icon displayed on the left of the authentication window
   * @param errorMessage an error message explaining a reason for opening the authentication dialog
   *     (when a wrong password was provided, etc.)
   */
  public AuthenticationWindow(
      String userName,
      char[] password,
      String server,
      boolean isUserNameEditable,
      ImageIcon icon,
      String errorMessage) {
    this(
        userName,
        password,
        server,
        isUserNameEditable,
        false,
        icon,
        null,
        null,
        null,
        null,
        errorMessage,
        null);
  }

  /**
   * Creates an instance of the <tt>LoginWindow</tt>.
   *
   * @param userName the user name to set by default
   * @param password the password to set by default
   * @param server the server name this authentication window is about
   * @param isUserNameEditable indicates if the user name should be editable by the user or not
   * @param icon the icon displayed on the left of the authentication window
   * @param errorMessage an error message explaining a reason for opening the authentication dialog
   *     (when a wrong password was provided, etc.)
   * @param signupLink an URL that allows the user to sign up
   */
  public AuthenticationWindow(
      String userName,
      char[] password,
      String server,
      boolean isUserNameEditable,
      ImageIcon icon,
      String errorMessage,
      String signupLink) {
    this(
        userName,
        password,
        server,
        isUserNameEditable,
        false,
        icon,
        null,
        null,
        null,
        null,
        errorMessage,
        signupLink);
  }

  /**
   * Creates an instance of the <tt>LoginWindow</tt>.
   *
   * @param userName the user name to set by default
   * @param password the password to set by default
   * @param server the server name this authentication window is about
   * @param isUserNameEditable indicates if the user name should be editable by the user or not
   * @param icon the icon displayed on the left of the authentication window
   */
  public AuthenticationWindow(
      String userName, char[] password, String server, boolean isUserNameEditable, ImageIcon icon) {
    this(userName, password, server, isUserNameEditable, icon, null, null);
  }

  /**
   * Creates an instance of the <tt>LoginWindow</tt>.
   *
   * @param owner the owner of this dialog
   * @param userName the user name to set by default
   * @param password the password to set by default
   * @param server the server name this authentication window is about
   * @param isUserNameEditable indicates if the user name should be editable by the user or not
   * @param icon the icon displayed on the left of the authentication window
   */
  public AuthenticationWindow(
      Dialog owner,
      String userName,
      char[] password,
      String server,
      boolean isUserNameEditable,
      ImageIcon icon) {
    super(owner, false);

    init(userName, password, server, isUserNameEditable, icon, null);
  }

  /**
   * Shows or hides the "save password" checkbox.
   *
   * @param allow the checkbox is shown when allow is <tt>true</tt>
   */
  public void setAllowSavePassword(boolean allow) {
    rememberPassCheckBox.setVisible(allow);
  }

  /**
   * Initializes the icon image.
   *
   * @param icon the icon to show on the left of the window
   */
  private void initIcon(ImageIcon icon) {
    // If an icon isn't provided set the application logo icon by default.
    if (icon == null)
      icon =
          DesktopUtilActivator.getResources().getImage("service.gui.SIP_COMMUNICATOR_LOGO_64x64");

    JLabel iconLabel = new JLabel(icon);

    iconLabel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));

    iconLabel.setAlignmentY(Component.TOP_ALIGNMENT);

    JPanel iconPanel = new TransparentPanel(new BorderLayout());
    iconPanel.add(iconLabel, BorderLayout.NORTH);

    getContentPane().add(iconPanel, BorderLayout.WEST);
  }

  /** Constructs the <tt>LoginWindow</tt>. */
  private void init() {
    String title;

    if (windowTitle != null) title = windowTitle;
    else
      title =
          DesktopUtilActivator.getResources()
              .getI18NString("service.gui.AUTHENTICATION_WINDOW_TITLE", new String[] {server});

    String text;
    if (windowText != null) text = windowText;
    else
      text =
          DesktopUtilActivator.getResources()
              .getI18NString("service.gui.AUTHENTICATION_REQUESTED_SERVER", new String[] {server});

    String uinText;
    if (usernameLabelText != null) uinText = usernameLabelText;
    else uinText = DesktopUtilActivator.getResources().getI18NString("service.gui.IDENTIFIER");

    String passText;
    if (passwordLabelText != null) passText = passwordLabelText;
    else passText = DesktopUtilActivator.getResources().getI18NString("service.gui.PASSWORD");

    setTitle(title);

    infoTextArea.setEditable(false);
    infoTextArea.setOpaque(false);
    infoTextArea.setLineWrap(true);
    infoTextArea.setWrapStyleWord(true);
    infoTextArea.setFont(infoTextArea.getFont().deriveFont(Font.BOLD));
    infoTextArea.setText(text);
    infoTextArea.setAlignmentX(0.5f);

    JLabel uinLabel = new JLabel(uinText);
    uinLabel.setFont(uinLabel.getFont().deriveFont(Font.BOLD));

    JLabel passwdLabel = new JLabel(passText);
    passwdLabel.setFont(passwdLabel.getFont().deriveFont(Font.BOLD));

    TransparentPanel labelsPanel = new TransparentPanel(new GridLayout(0, 1, 8, 8));

    labelsPanel.add(uinLabel);
    labelsPanel.add(passwdLabel);

    TransparentPanel textFieldsPanel = new TransparentPanel(new GridLayout(0, 1, 8, 8));

    textFieldsPanel.add(uinValue);
    textFieldsPanel.add(passwdField);

    JPanel southFieldsPanel = new TransparentPanel(new GridLayout(1, 2));

    this.rememberPassCheckBox.setOpaque(false);
    this.rememberPassCheckBox.setBorder(null);

    southFieldsPanel.add(rememberPassCheckBox);
    if (signupLink != null && signupLink.length() > 0)
      southFieldsPanel.add(
          createWebSignupLabel(
              DesktopUtilActivator.getResources().getI18NString("plugin.simpleaccregwizz.SIGNUP"),
              signupLink));
    else southFieldsPanel.add(new JLabel());

    boolean allowRememberPassword = true;

    String allowRemPassStr =
        DesktopUtilActivator.getResources().getSettingsString(PNAME_ALLOW_SAVE_PASSWORD);
    if (allowRemPassStr != null) {
      allowRememberPassword = Boolean.parseBoolean(allowRemPassStr);
    }
    allowRememberPassword =
        DesktopUtilActivator.getConfigurationService()
            .getBoolean(PNAME_ALLOW_SAVE_PASSWORD, allowRememberPassword);

    setAllowSavePassword(allowRememberPassword);

    JPanel buttonPanel = new TransparentPanel(new FlowLayout(FlowLayout.CENTER));

    buttonPanel.add(loginButton);
    buttonPanel.add(cancelButton);

    JPanel southEastPanel = new TransparentPanel(new BorderLayout());
    southEastPanel.add(buttonPanel, BorderLayout.EAST);

    TransparentPanel mainPanel = new TransparentPanel(new BorderLayout(10, 10));

    mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 0, 20, 20));

    JPanel mainFieldsPanel = new TransparentPanel(new BorderLayout(0, 10));
    mainFieldsPanel.add(labelsPanel, BorderLayout.WEST);
    mainFieldsPanel.add(textFieldsPanel, BorderLayout.CENTER);
    mainFieldsPanel.add(southFieldsPanel, BorderLayout.SOUTH);

    mainPanel.add(infoTextArea, BorderLayout.NORTH);
    mainPanel.add(mainFieldsPanel, BorderLayout.CENTER);
    mainPanel.add(southEastPanel, BorderLayout.SOUTH);

    this.getContentPane().add(mainPanel, BorderLayout.EAST);

    this.loginButton.setName("ok");
    this.cancelButton.setName("cancel");
    if (loginButton.getPreferredSize().width > cancelButton.getPreferredSize().width)
      cancelButton.setPreferredSize(loginButton.getPreferredSize());
    else loginButton.setPreferredSize(cancelButton.getPreferredSize());

    this.loginButton.setMnemonic(
        DesktopUtilActivator.getResources().getI18nMnemonic("service.gui.OK"));
    this.cancelButton.setMnemonic(
        DesktopUtilActivator.getResources().getI18nMnemonic("service.gui.CANCEL"));

    this.loginButton.addActionListener(this);
    this.cancelButton.addActionListener(this);

    this.getRootPane().setDefaultButton(loginButton);
  }

  /**
   * Handles the <tt>ActionEvent</tt> triggered when one of the buttons is clicked. When "Login"
   * button is chosen installs a new account from the user input and logs in.
   *
   * @param evt the action event that has just occurred.
   */
  public void actionPerformed(ActionEvent evt) {
    JButton button = (JButton) evt.getSource();
    String buttonName = button.getName();

    if ("ok".equals(buttonName)) {
      if (uinValue instanceof JLabel) userName = ((JLabel) uinValue).getText();
      else if (uinValue instanceof JTextField) userName = ((JTextField) uinValue).getText();

      password = passwdField.getPassword();
      isRememberPassword = rememberPassCheckBox.isSelected();
    } else {
      isCanceled = true;
    }

    // release the caller that opened the window
    buttonClicked = true;
    synchronized (lock) {
      lock.notify();
    }

    this.dispose();
  }

  /** Enables the actions when a key is pressed, for now closes the window when esc is pressed. */
  private void enableKeyActions() {
    @SuppressWarnings("serial")
    UIAction act =
        new UIAction() {
          public void actionPerformed(ActionEvent e) {
            close(true);
          }
        };

    getRootPane().getActionMap().put("close", act);

    InputMap imap = this.getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
    imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close");
  }

  /**
   * Automatically clicks the cancel button, when this window is closed.
   *
   * @param isEscaped indicates if the window has been closed by pressing the Esc key
   */
  @Override
  protected void close(boolean isEscaped) {
    this.cancelButton.doClick();
  }

  /**
   * Shows this modal dialog.
   *
   * @param isVisible specifies whether we should be showing or hiding the window.
   */
  @Override
  public void setVisible(final boolean isVisible) {
    this.setName("AUTHENTICATION");

    if (getOwner() != null) setModal(true);

    if (isVisible) {
      addWindowFocusListener(
          new WindowAdapter() {
            public void windowGainedFocus(WindowEvent e) {
              removeWindowFocusListener(this);

              if (uinValue instanceof JTextField && "".equals(((JTextField) uinValue).getText())) {
                uinValue.requestFocusInWindow();
              } else passwdField.requestFocusInWindow();
            }
          });
    }

    super.setVisible(isVisible);

    if (isVisible) {
      if (getOwner() != null) return;

      synchronized (lock) {
        while (!buttonClicked) {
          try {
            lock.wait();
          } catch (InterruptedException e) {
          } // we don't care, just retry
        }
      }
    }
  }

  /**
   * Indicates if this window has been canceled.
   *
   * @return <tt>true</tt> if this window has been canceled, <tt>false</tt> - otherwise
   */
  public boolean isCanceled() {
    return isCanceled;
  }

  /**
   * Returns the user name entered by the user or previously set if the user name is not editable.
   *
   * @return the user name
   */
  public String getUserName() {
    return userName;
  }

  /**
   * Returns the password entered by the user.
   *
   * @return the password
   */
  public char[] getPassword() {
    return password;
  }

  /**
   * Indicates if the password should be remembered.
   *
   * @return <tt>true</tt> if the password should be remembered, <tt>false</tt> - otherwise
   */
  public boolean isRememberPassword() {
    return isRememberPassword;
  }

  /**
   * Creates the subscribe label.
   *
   * @param linkName the link name
   * @return the newly created subscribe label
   */
  private Component createWebSignupLabel(String linkName, final String linkURL) {
    JLabel subscribeLabel =
        new JLabel("<html><a href=''>" + linkName + "</a></html>", JLabel.RIGHT);

    subscribeLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
    subscribeLabel.setToolTipText(
        DesktopUtilActivator.getResources()
            .getI18NString("plugin.simpleaccregwizz.SPECIAL_SIGNUP"));
    subscribeLabel.addMouseListener(
        new MouseAdapter() {
          public void mousePressed(MouseEvent e) {
            try {
              DesktopUtilActivator.getBrowserLauncher().openURL(linkURL);
            } catch (UnsupportedOperationException ex) {
              // This should not happen, because we check if the
              // operation is supported, before adding the sign
              // up.
              logger.error("The web sign up is not supported.", ex);
            }
          }
        });
    return subscribeLabel;
  }
}
/** Panel that shows the content of an X509Certificate. */
public class X509CertificatePanel extends TransparentPanel {
  private static final long serialVersionUID = -8368302061995971947L;

  private final JEditorPane infoTextPane = new JEditorPane();

  private final ResourceManagementService R = DesktopUtilActivator.getResources();

  /**
   * Constructs a X509 certificate panel from a single certificate. If a chain is available instead
   * use the second constructor. This constructor is kept for backwards compatibility and for
   * convenience when there is only one certificate of interest.
   *
   * @param certificate <tt>X509Certificate</tt> object
   */
  public X509CertificatePanel(Certificate certificate) {
    this(new Certificate[] {certificate});
  }

  /**
   * Constructs a X509 certificate panel.
   *
   * @param certificates <tt>X509Certificate</tt> objects
   */
  public X509CertificatePanel(Certificate[] certificates) {
    setLayout(new BorderLayout(5, 5));

    // Certificate chain list
    TransparentPanel topPanel = new TransparentPanel(new BorderLayout());
    topPanel.add(
        new JLabel(
            "<html><body><b>"
                + R.getI18NString("service.gui.CERT_INFO_CHAIN")
                + "</b></body></html>"),
        BorderLayout.NORTH);

    DefaultMutableTreeNode top = new DefaultMutableTreeNode();
    DefaultMutableTreeNode previous = top;
    for (int i = certificates.length - 1; i >= 0; i--) {
      Certificate cert = certificates[i];
      DefaultMutableTreeNode next = new DefaultMutableTreeNode(cert);
      previous.add(next);
      previous = next;
    }
    JTree tree = new JTree(top);
    tree.setBorder(new BevelBorder(BevelBorder.LOWERED));
    tree.setRootVisible(false);
    tree.setExpandsSelectedPaths(true);
    tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
    tree.setCellRenderer(
        new DefaultTreeCellRenderer() {

          @Override
          public Component getTreeCellRendererComponent(
              JTree tree,
              Object value,
              boolean sel,
              boolean expanded,
              boolean leaf,
              int row,
              boolean hasFocus) {
            JLabel component =
                (JLabel)
                    super.getTreeCellRendererComponent(
                        tree, value, sel, expanded, leaf, row, hasFocus);
            if (value instanceof DefaultMutableTreeNode) {
              Object o = ((DefaultMutableTreeNode) value).getUserObject();
              if (o instanceof X509Certificate) {
                component.setText(getSimplifiedName((X509Certificate) o));
              } else {
                // We don't know how to represent this certificate type,
                // let's use the first 20 characters
                String text = o.toString();
                if (text.length() > 20) {
                  text = text.substring(0, 20);
                }
                component.setText(text);
              }
            }
            return component;
          }
        });
    tree.getSelectionModel()
        .addTreeSelectionListener(
            new TreeSelectionListener() {

              @Override
              public void valueChanged(TreeSelectionEvent e) {
                valueChangedPerformed(e);
              }
            });
    tree.setSelectionPath(
        new TreePath((((DefaultTreeModel) tree.getModel()).getPathToRoot(previous))));
    topPanel.add(tree, BorderLayout.CENTER);

    add(topPanel, BorderLayout.NORTH);

    // Certificate details pane
    Caret caret = infoTextPane.getCaret();
    if (caret instanceof DefaultCaret) {
      ((DefaultCaret) caret).setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
    }

    /*
     * Make JEditorPane respect our default font because we will be using it
     * to just display text.
     */
    infoTextPane.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, true);

    infoTextPane.setOpaque(false);
    infoTextPane.setEditable(false);
    infoTextPane.setContentType("text/html");
    infoTextPane.setText(toString(certificates[0]));

    final JScrollPane certScroll = new JScrollPane(infoTextPane);
    certScroll.setPreferredSize(new Dimension(300, 500));
    add(certScroll, BorderLayout.CENTER);
  }

  /**
   * Creates a String representation of the given object.
   *
   * @param certificate to print
   * @return the String representation
   */
  private String toString(Object certificate) {
    final StringBuilder sb = new StringBuilder();
    sb.append("<html><body>\n");

    if (certificate instanceof X509Certificate) {
      renderX509(sb, (X509Certificate) certificate);
    } else {
      sb.append("<pre>\n");
      sb.append(certificate.toString());
      sb.append("</pre>\n");
    }

    sb.append("</body></html>");
    return sb.toString();
  }

  /**
   * Appends an HTML representation of the given X509Certificate.
   *
   * @param sb StringBuilder to append to
   * @param certificate to print
   */
  private void renderX509(StringBuilder sb, X509Certificate certificate) {
    X500Principal issuer = certificate.getIssuerX500Principal();
    X500Principal subject = certificate.getSubjectX500Principal();

    sb.append("<table cellspacing='1' cellpadding='1'>\n");

    // subject
    addTitle(sb, R.getI18NString("service.gui.CERT_INFO_ISSUED_TO"));
    try {
      for (Rdn name : new LdapName(subject.getName()).getRdns()) {
        String nameType = name.getType();
        String lblKey = "service.gui.CERT_INFO_" + nameType;
        String lbl = R.getI18NString(lblKey);

        if ((lbl == null) || ("!" + lblKey + "!").equals(lbl)) lbl = nameType;

        final String value;
        Object nameValue = name.getValue();

        if (nameValue instanceof byte[]) {
          byte[] nameValueAsByteArray = (byte[]) nameValue;

          value = getHex(nameValueAsByteArray) + " (" + new String(nameValueAsByteArray) + ")";
        } else value = nameValue.toString();

        addField(sb, lbl, value);
      }
    } catch (InvalidNameException ine) {
      addField(sb, R.getI18NString("service.gui.CERT_INFO_CN"), subject.getName());
    }

    // issuer
    addTitle(sb, R.getI18NString("service.gui.CERT_INFO_ISSUED_BY"));
    try {
      for (Rdn name : new LdapName(issuer.getName()).getRdns()) {
        String nameType = name.getType();
        String lblKey = "service.gui.CERT_INFO_" + nameType;
        String lbl = R.getI18NString(lblKey);

        if ((lbl == null) || ("!" + lblKey + "!").equals(lbl)) lbl = nameType;

        final String value;
        Object nameValue = name.getValue();

        if (nameValue instanceof byte[]) {
          byte[] nameValueAsByteArray = (byte[]) nameValue;

          value = getHex(nameValueAsByteArray) + " (" + new String(nameValueAsByteArray) + ")";
        } else value = nameValue.toString();

        addField(sb, lbl, value);
      }
    } catch (InvalidNameException ine) {
      addField(sb, R.getI18NString("service.gui.CERT_INFO_CN"), issuer.getName());
    }

    // validity
    addTitle(sb, R.getI18NString("service.gui.CERT_INFO_VALIDITY"));
    addField(
        sb,
        R.getI18NString("service.gui.CERT_INFO_ISSUED_ON"),
        certificate.getNotBefore().toString());
    addField(
        sb,
        R.getI18NString("service.gui.CERT_INFO_EXPIRES_ON"),
        certificate.getNotAfter().toString());

    addTitle(sb, R.getI18NString("service.gui.CERT_INFO_FINGERPRINTS"));
    try {
      String sha1String = getThumbprint(certificate, "SHA1");
      String md5String = getThumbprint(certificate, "MD5");

      addField(sb, "SHA1:", sha1String);
      addField(sb, "MD5:", md5String);
    } catch (CertificateException e) {
      // do nothing as we cannot show this value
    }

    addTitle(sb, R.getI18NString("service.gui.CERT_INFO_CERT_DETAILS"));

    addField(
        sb,
        R.getI18NString("service.gui.CERT_INFO_SER_NUM"),
        certificate.getSerialNumber().toString());

    addField(
        sb, R.getI18NString("service.gui.CERT_INFO_VER"), String.valueOf(certificate.getVersion()));

    addField(
        sb,
        R.getI18NString("service.gui.CERT_INFO_SIGN_ALG"),
        String.valueOf(certificate.getSigAlgName()));

    addTitle(sb, R.getI18NString("service.gui.CERT_INFO_PUB_KEY_INFO"));

    addField(
        sb,
        R.getI18NString("service.gui.CERT_INFO_ALG"),
        certificate.getPublicKey().getAlgorithm());

    if (certificate.getPublicKey().getAlgorithm().equals("RSA")) {
      RSAPublicKey key = (RSAPublicKey) certificate.getPublicKey();

      addField(
          sb,
          R.getI18NString("service.gui.CERT_INFO_PUB_KEY"),
          R.getI18NString(
              "service.gui.CERT_INFO_KEY_BYTES_PRINT",
              new String[] {
                String.valueOf(key.getModulus().toByteArray().length - 1),
                key.getModulus().toString(16)
              }));

      addField(
          sb, R.getI18NString("service.gui.CERT_INFO_EXP"), key.getPublicExponent().toString());

      addField(
          sb,
          R.getI18NString("service.gui.CERT_INFO_KEY_SIZE"),
          R.getI18NString(
              "service.gui.CERT_INFO_KEY_BITS_PRINT",
              new String[] {String.valueOf(key.getModulus().bitLength())}));
    } else if (certificate.getPublicKey().getAlgorithm().equals("DSA")) {
      DSAPublicKey key = (DSAPublicKey) certificate.getPublicKey();

      addField(sb, "Y:", key.getY().toString(16));
    }

    addField(
        sb,
        R.getI18NString("service.gui.CERT_INFO_SIGN"),
        R.getI18NString(
            "service.gui.CERT_INFO_KEY_BYTES_PRINT",
            new String[] {
              String.valueOf(certificate.getSignature().length), getHex(certificate.getSignature())
            }));

    sb.append("</table>\n");
  }

  /**
   * Add a title.
   *
   * @param sb StringBuilder to append to
   * @param title to print
   */
  private void addTitle(StringBuilder sb, String title) {
    sb.append("<tr><td colspan='2'")
        .append(" style='margin-top: 5pt; white-space: nowrap'><p><b>")
        .append(title)
        .append("</b></p></td></tr>\n");
  }

  /**
   * Add a field.
   *
   * @param sb StringBuilder to append to
   * @param field name of the certificate field
   * @param value to print
   */
  private void addField(StringBuilder sb, String field, String value) {
    sb.append("<tr>")
        .append("<td style='margin-left: 5pt; margin-right: 25pt;")
        .append(" white-space: nowrap'>")
        .append(field)
        .append("</td>")
        .append("<td>")
        .append(value)
        .append("</td>")
        .append("</tr>\n");
  }

  /**
   * Converts the byte array to hex string.
   *
   * @param raw the data.
   * @return the hex string.
   */
  private String getHex(byte[] raw) {
    if (raw == null) return null;

    StringBuilder hex = new StringBuilder(2 * raw.length);
    Formatter f = new Formatter(hex);
    try {
      for (byte b : raw) f.format("%02x", b);
    } finally {
      f.close();
    }
    return hex.toString();
  }

  /**
   * Calculates the hash of the certificate known as the "thumbprint" and returns it as a string
   * representation.
   *
   * @param cert The certificate to hash.
   * @param algorithm The hash algorithm to use.
   * @return The SHA-1 hash of the certificate.
   * @throws CertificateException
   */
  private static String getThumbprint(X509Certificate cert, String algorithm)
      throws CertificateException {
    MessageDigest digest;
    try {
      digest = MessageDigest.getInstance(algorithm);
    } catch (NoSuchAlgorithmException e) {
      throw new CertificateException(e);
    }
    byte[] encodedCert = cert.getEncoded();
    StringBuilder sb = new StringBuilder(encodedCert.length * 2);
    Formatter f = new Formatter(sb);
    try {
      for (byte b : digest.digest(encodedCert)) f.format("%02x", b);
    } finally {
      f.close();
    }
    return sb.toString();
  }

  /**
   * Construct a "simplified name" based on the subject DN from the certificate. The purpose is to
   * have something shorter to display in the list. The name used is one of the following DN parts,
   * if available, otherwise the complete DN: 'CN', 'OU' or else 'O'.
   *
   * @param cert to read subject DN from
   * @return the simplified name
   */
  private static String getSimplifiedName(X509Certificate cert) {
    final HashMap<String, String> parts = new HashMap<String, String>();
    try {
      for (Rdn name : new LdapName(cert.getSubjectX500Principal().getName()).getRdns()) {
        if (name.getType() != null && name.getValue() != null) {
          parts.put(name.getType(), name.getValue().toString());
        }
      }
    } catch (InvalidNameException ignored) // NOPMD
    {
    }

    String result = parts.get("CN");
    if (result == null) {
      result = parts.get("OU");
    }
    if (result == null) {
      result = parts.get("O");
    }
    if (result == null) {
      result = cert.getSubjectX500Principal().getName();
    }
    return result;
  }

  /**
   * Called when the selection changed in the tree. Loads the selected certificate.
   *
   * @param e the event
   */
  private void valueChangedPerformed(TreeSelectionEvent e) {
    Object o = e.getNewLeadSelectionPath().getLastPathComponent();
    if (o instanceof DefaultMutableTreeNode) {
      DefaultMutableTreeNode node = (DefaultMutableTreeNode) o;
      infoTextPane.setText(toString(node.getUserObject()));
    }
  }
}
/**
 * This UI displays a different interface, which is independent from the look and feel.
 *
 * @author David Bismut, [email protected]
 * @author Yana Stamcheva
 * @author Adam Netocny
 */
public class SIPCommTabbedPaneEnhancedUI extends SIPCommTabbedPaneUI implements Skinnable {
  private static Color TAB_HIGHLIGHT_FOREGROUND_COLOR =
      new Color(DesktopUtilActivator.getResources().getColor("service.gui.TAB_TITLE_HIGHLIGHT"));

  private static Color TAB_SELECTED_FOREGROUND_COLOR =
      new Color(DesktopUtilActivator.getResources().getColor("service.gui.TAB_TITLE_SELECTED"));

  private static final int TAB_OVERLAP =
      Integer.parseInt(
          DesktopUtilActivator.getResources().getSettingsString("impl.gui.TAB_OVERLAP"));

  private static final int PREFERRED_WIDTH = 150;

  /**
   * The image used in the <tt>SIPCommLookAndFeel</tt> to paint the background of a selected tab.
   */
  private static final String SELECTED_TAB_LEFT_BG = "service.gui.lookandfeel.SELECTED_TAB_LEFT_BG";

  /**
   * The image used in the <tt>SIPCommLookAndFeel</tt> to paint the background of a selected tab.
   */
  private static final String SELECTED_TAB_MIDDLE_BG =
      "service.gui.lookandfeel.SELECTED_TAB_MIDDLE_BG";

  /**
   * The image used in the <tt>SIPCommLookAndFeel</tt> to paint the background of a selected tab.
   */
  private static final String SELECTED_TAB_RIGHT_BG =
      "service.gui.lookandfeel.SELECTED_TAB_RIGHT_BG";

  /** The image used in the <tt>SIPCommLookAndFeel</tt> to paint the background of a tab. */
  private static final String TAB_LEFT_BG = "service.gui.lookandfeel.TAB_LEFT_BG";

  /** The image used in the <tt>SIPCommLookAndFeel</tt> to paint the background of a tab. */
  private static final String TAB_MIDDLE_BG = "service.gui.lookandfeel.TAB_MIDDLE_BG";

  /** The image used in the <tt>SIPCommLookAndFeel</tt> to paint the background of a tab. */
  private static final String TAB_RIGHT_BG = "service.gui.lookandfeel.TAB_RIGHT_BG";

  protected final java.util.List<Integer> highlightedTabs = new Vector<Integer>();

  public static ComponentUI createUI(JComponent c) {
    return new SIPCommTabbedPaneEnhancedUI();
  }

  @Override
  protected void paintFocusIndicator(
      Graphics g,
      int tabPlacement,
      Rectangle[] rects,
      int tabIndex,
      Rectangle iconRect,
      Rectangle textRect,
      boolean isSelected) {}

  /** Overriden to paint nothing. */
  @Override
  protected void paintTabBorder(
      Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {}

  @Override
  protected void paintContentBorderTopEdge(
      Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) {
    if (tabPane.getTabCount() < 1) return;

    g.setColor(shadow);
    g.drawLine(x, y, x + w - 2, y);
  }

  @Override
  protected void paintContentBorderLeftEdge(
      Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) {
    if (tabPane.getTabCount() < 1) return;

    g.setColor(shadow);

    g.drawLine(x, y, x, y + h - 3);
  }

  @Override
  protected void paintContentBorderBottomEdge(
      Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) {
    if (tabPane.getTabCount() < 1) return;

    g.setColor(shadow);
    g.drawLine(x + 1, y + h - 3, x + w - 2, y + h - 3);
    g.drawLine(x + 1, y + h - 2, x + w - 2, y + h - 2);
    g.setColor(shadow.brighter());
    g.drawLine(x + 2, y + h - 1, x + w - 1, y + h - 1);
  }

  @Override
  protected void paintContentBorderRightEdge(
      Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) {
    if (tabPane.getTabCount() < 1) return;

    g.setColor(shadow);

    g.drawLine(x + w - 3, y + 1, x + w - 3, y + h - 3);
    g.drawLine(x + w - 2, y + 1, x + w - 2, y + h - 3);
    g.setColor(shadow.brighter());
    g.drawLine(x + w - 1, y + 2, x + w - 1, y + h - 2);
  }

  @Override
  protected void paintTabBackground(
      Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
    g = g.create();
    try {
      internalPaintTabBackground(g, tabPlacement, tabIndex, x, y, w, h, isSelected);
    } finally {
      g.dispose();
    }
  }

  private void internalPaintTabBackground(
      Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
    BufferedImage leftImg = null;
    BufferedImage middleImg = null;
    BufferedImage rightImg = null;

    Graphics2D g2 = (Graphics2D) g;

    AntialiasingManager.activateAntialiasing(g2);

    int tabOverlap = 0;

    if (isSelected) {
      if (tabPane.isEnabledAt(tabIndex)) {
        leftImg = DesktopUtilActivator.getImage(SELECTED_TAB_LEFT_BG);
        middleImg = DesktopUtilActivator.getImage(SELECTED_TAB_MIDDLE_BG);
        rightImg = DesktopUtilActivator.getImage(SELECTED_TAB_RIGHT_BG);

        tabOverlap = TAB_OVERLAP;
      } else {
        leftImg = DesktopUtilActivator.getImage(TAB_LEFT_BG);
        middleImg = DesktopUtilActivator.getImage(TAB_MIDDLE_BG);
        rightImg = DesktopUtilActivator.getImage(TAB_RIGHT_BG);
      }
    } else {
      leftImg = DesktopUtilActivator.getImage(TAB_LEFT_BG);
      middleImg = DesktopUtilActivator.getImage(TAB_MIDDLE_BG);
      rightImg = DesktopUtilActivator.getImage(TAB_RIGHT_BG);
    }

    // Remove the existing gap between the tabs and the panel, which is due
    // to the removal of the tabbed pane border.
    y++;
    // If the current tab is not the selected tab we paint it 2 more pixels
    // to the bottom in order to create the disabled look.
    if (!isSelected) y += 2;

    g2.drawImage(leftImg, x, y, null);
    g2.drawImage(
        middleImg,
        x + leftImg.getWidth(),
        y,
        w - leftImg.getWidth() - rightImg.getWidth() + tabOverlap,
        leftImg.getHeight(),
        null);
    g2.drawImage(rightImg, x + w - rightImg.getWidth() + tabOverlap, y, null);
  }

  @Override
  protected void paintText(
      Graphics g,
      int tabPlacement,
      Font font,
      FontMetrics metrics,
      int tabIndex,
      String title,
      Rectangle textRect,
      boolean isSelected) {
    g.setFont(font);

    int titleWidth = SwingUtilities.computeStringWidth(metrics, title);

    int preferredWidth = 0;
    if (isOneActionButtonEnabled()) {
      preferredWidth = calculateTabWidth(tabPlacement, tabIndex, metrics) - WIDTHDELTA - 15;

      if (isCloseEnabled()) preferredWidth -= BUTTONSIZE;

      if (isMaxEnabled()) preferredWidth -= BUTTONSIZE;
    } else {
      preferredWidth = titleWidth;
    }

    while (titleWidth > preferredWidth) {
      if (title.endsWith("...")) title = title.substring(0, title.indexOf("...") - 1).concat("...");
      else title = title.substring(0, title.length() - 4).concat("...");

      titleWidth = SwingUtilities.computeStringWidth(metrics, title);
    }

    textRect.width = titleWidth;

    View v = getTextViewForTab(tabIndex);
    if (v != null) {
      // html
      v.paint(g, textRect);
    } else {
      // plain text
      int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);

      if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex)) {
        if (isSelected) g.setColor(TAB_SELECTED_FOREGROUND_COLOR);
        else {
          if (this.isTabHighlighted(tabIndex)) {
            g.setColor(TAB_HIGHLIGHT_FOREGROUND_COLOR);
          } else g.setColor(tabPane.getForegroundAt(tabIndex));
        }

        BasicGraphicsUtils.drawString(
            g, title, mnemIndex, textRect.x, textRect.y + metrics.getAscent());
      } else { // tab disabled
        g.setColor(tabPane.getBackgroundAt(tabIndex).brighter());
        BasicGraphicsUtils.drawStringUnderlineCharAt(
            g, title, mnemIndex, textRect.x, textRect.y + metrics.getAscent());

        g.setColor(tabPane.getBackgroundAt(tabIndex).darker());
        BasicGraphicsUtils.drawStringUnderlineCharAt(
            g, title, mnemIndex, textRect.x - 1, textRect.y + metrics.getAscent() - 1);
      }
    }
  }

  protected class ScrollableTabButton extends SIPCommTabbedPaneUI.ScrollableTabButton {
    /** Serial version UID. */
    private static final long serialVersionUID = 0L;

    public ScrollableTabButton(int direction) {
      super(direction);
      setRolloverEnabled(true);
    }

    @Override
    public Dimension getPreferredSize() {
      return new Dimension(16, calculateMaxTabHeight(0));
    }

    @Override
    public void paint(Graphics g) {
      Color origColor;
      boolean isPressed, isRollOver, isEnabled;
      int w, h, size;

      w = getWidth();
      h = getHeight();
      origColor = g.getColor();
      isPressed = getModel().isPressed();
      isRollOver = getModel().isRollover();
      isEnabled = isEnabled();

      g.setColor(getBackground());
      g.fillRect(0, 0, w, h);

      g.setColor(shadow);
      // Using the background color set above
      if (direction == WEST) {
        g.drawLine(0, 0, 0, h - 1); // left
        g.drawLine(w - 1, 0, w - 1, 0); // right
      } else g.drawLine(w - 2, h - 1, w - 2, 0); // right

      g.drawLine(0, 0, w - 2, 0); // top

      if (isRollOver) {
        // do highlights or shadows
        Color color1;
        Color color2;

        if (isPressed) {
          color2 = Color.WHITE;
          color1 = shadow;
        } else {
          color1 = Color.WHITE;
          color2 = shadow;
        }

        g.setColor(color1);

        if (direction == WEST) {
          g.drawLine(1, 1, 1, h - 1); // left
          g.drawLine(1, 1, w - 2, 1); // top
          g.setColor(color2);
          g.drawLine(w - 1, h - 1, w - 1, 1); // right
        } else {
          g.drawLine(0, 1, 0, h - 1);
          g.drawLine(0, 1, w - 3, 1); // top
          g.setColor(color2);
          g.drawLine(w - 3, h - 1, w - 3, 1); // right
        }
      }

      // g.drawLine(0, h - 1, w - 1, h - 1); //bottom

      // If there's no room to draw arrow, bail
      if (h < 5 || w < 5) {
        g.setColor(origColor);
        return;
      }

      if (isPressed) {
        g.translate(1, 1);
      }

      // Draw the arrow
      size = Math.min((h - 4) / 3, (w - 4) / 3);
      size = Math.max(size, 2);

      boolean highlight = false;

      if (!highlightedTabs.isEmpty()
          && ((direction == WEST && tabScroller.scrollBackwardButton.isEnabled())
              || (direction == EAST && tabScroller.scrollForwardButton.isEnabled()))) {
        Rectangle viewRect = tabScroller.viewport.getViewRect();

        if (direction == WEST) {
          int leadingTabIndex = getClosestTab(viewRect.x, viewRect.y);

          for (int i = 0; i < leadingTabIndex; i++) {
            if (highlightedTabs.contains(i) && !isScrollTabVisible(i)) {
              highlight = true;
              break;
            }
          }
        } else {
          int leadingTabIndex = getClosestTab(viewRect.x + viewRect.y, viewRect.y);

          for (int i = leadingTabIndex; i < tabPane.getTabCount(); i++) {
            if (highlightedTabs.contains(i) && !isScrollTabVisible(i)) {
              highlight = true;
              break;
            }
          }
        }

        if (highlight) {
          Image img =
              DesktopUtilActivator.getImage(
                  direction == WEST
                      ? "service.gui.icons.TAB_UNREAD_BACKWARD_ICON"
                      : "service.gui.icons.TAB_UNREAD_FORWARD_ICON");

          int wi = img.getWidth(null);

          g.drawImage(img, (w - wi) / 2, (h - size) / 2 - 2 /* 2 borders 1px width*/, null);
        }
      }

      if (!highlight) paintTriangle(g, (w - size) / 2, (h - size) / 2, size, direction, isEnabled);

      // Reset the Graphics back to it's original settings
      if (isPressed) {
        g.translate(-1, -1);
      }
      g.setColor(origColor);
    }
  }

  /**
   * Checks whether the <tt>tabIndex</tt> is visible in the scrollable tabs list.
   *
   * @param tabIndex the tab index to check.
   * @return whether <tt>tabIndex</tt> is visible in the list of scrollable tabs.
   */
  private boolean isScrollTabVisible(int tabIndex) {
    Rectangle tabRect = rects[tabIndex];
    Rectangle viewRect = tabScroller.viewport.getViewRect();
    if ((tabRect.x + tabRect.width - BUTTONSIZE < viewRect.x)
        || (tabRect.x + BUTTONSIZE > viewRect.x + viewRect.width)) {
      return false;
    } else {
      return true;
    }
  }

  @Override
  protected SIPCommTabbedPaneUI.ScrollableTabButton createScrollableTabButton(int direction) {
    return new ScrollableTabButton(direction);
  }

  @Override
  protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) {
    int width = super.calculateTabWidth(tabPlacement, tabIndex, metrics);

    if (isOneActionButtonEnabled()) {
      if (width > PREFERRED_WIDTH) width = PREFERRED_WIDTH;
    }

    return width + WIDTHDELTA;
  }

  public void tabAddHightlight(int tabIndex) {
    this.highlightedTabs.add(tabIndex);
  }

  public void tabRemoveHighlight(int tabIndex) {
    Iterator<Integer> highlightedIter = highlightedTabs.iterator();

    while (highlightedIter.hasNext()) {
      if (highlightedIter.next().intValue() == tabIndex) {
        highlightedIter.remove();
        break;
      }
    }
  }

  public boolean isTabHighlighted(int tabIndex) {
    return highlightedTabs.contains(tabIndex);
  }

  /** Reloads color info. */
  @Override
  public void loadSkin() {
    super.loadSkin();

    TAB_HIGHLIGHT_FOREGROUND_COLOR =
        new Color(DesktopUtilActivator.getResources().getColor("service.gui.TAB_TITLE_HIGHLIGHT"));

    TAB_SELECTED_FOREGROUND_COLOR =
        new Color(DesktopUtilActivator.getResources().getColor("service.gui.TAB_TITLE_SELECTED"));
  }
}