private int addButton(SIPCommButton button, int gridX, int xBounds, boolean isLast) {
    lastAddedButton = button;

    constraints.insets = new Insets(0, 0, V_GAP, 0);
    constraints.anchor = GridBagConstraints.WEST;
    constraints.fill = GridBagConstraints.NONE;
    constraints.gridx = gridX;
    constraints.gridy = 2;
    constraints.gridwidth = 1;
    constraints.gridheight = 1;
    constraints.weightx = 0f;
    constraints.weighty = 0f;
    this.add(button, constraints);

    int yBounds =
        TOP_BORDER
            + BOTTOM_BORDER
            + 2 * V_GAP
            + ComponentUtils.getStringSize(nameLabel, nameLabel.getText()).height
            + ComponentUtils.getStringSize(displayDetailsLabel, displayDetailsLabel.getText())
                .height;

    button.setBounds(xBounds, yBounds, BUTTON_WIDTH, BUTTON_HEIGHT);

    button.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));

    setButtonBg(button, gridX, isLast);

    return button.getWidth();
  }
  /**
   * Initializes the display details component for the given <tt>UIContact</tt>.
   *
   * @param displayDetails the display details to show
   */
  private void initDisplayDetails(String displayDetails) {
    remove(displayDetailsLabel);
    displayDetailsLabel.setText("");

    if (displayDetails != null && displayDetails.length() > 0) {
      // Replace all occurrences of new line with slash.
      displayDetails = Html2Text.extractText(displayDetails);
      displayDetails = displayDetails.replaceAll("\n|<br>|<br/>", " / ");

      displayDetailsLabel.setText(displayDetails);
    }

    constraints.anchor = GridBagConstraints.WEST;
    constraints.fill = GridBagConstraints.NONE;
    constraints.gridx = 1;
    constraints.gridy = 1;
    constraints.weightx = 1f;
    constraints.weighty = 0f;
    constraints.gridwidth = 1;
    constraints.gridheight = 1;

    this.add(displayDetailsLabel, constraints);
  }
  /**
   * Initializes buttons panel.
   *
   * @param uiContact the <tt>UIContact</tt> for which we initialize the button panel
   */
  private void initButtonsPanel(UIContact uiContact) {
    this.remove(chatButton);
    this.remove(callButton);
    this.remove(callVideoButton);
    this.remove(desktopSharingButton);
    this.remove(addContactButton);

    clearCustomActionButtons();

    if (!isSelected) return;

    UIContactDetail imContact = null;
    // For now we support instance messaging only for contacts in our
    // contact list until it's implemented for external source contacts.
    if (uiContact.getDescriptor() instanceof MetaContact)
      imContact = uiContact.getDefaultContactDetail(OperationSetBasicInstantMessaging.class);

    int x = (statusIcon == null ? 0 : statusIcon.getIconWidth()) + LEFT_BORDER + H_GAP;

    // Re-initialize the x grid.
    constraints.gridx = 0;
    int gridX = 0;

    if (imContact != null) {
      x += addButton(chatButton, ++gridX, x, false);
    }

    UIContactDetail telephonyContact =
        uiContact.getDefaultContactDetail(OperationSetBasicTelephony.class);

    // Check if contact has additional phone numbers, if yes show the
    // call button
    ContactPhoneUtil contactPhoneUtil = null;

    // check for phone stored in contact info only
    // if telephony contact is missing
    if (uiContact.getDescriptor() != null
        && uiContact.getDescriptor() instanceof MetaContact
        && telephonyContact == null) {
      contactPhoneUtil = ContactPhoneUtil.getPhoneUtil((MetaContact) uiContact.getDescriptor());

      MetaContact metaContact = (MetaContact) uiContact.getDescriptor();
      Iterator<Contact> contacts = metaContact.getContacts();

      while (contacts.hasNext()) // && !hasPhone)
      {
        Contact contact = contacts.next();

        if (!contact.getProtocolProvider().isRegistered()) continue;

        contactPhoneUtil.addDetailsResponseListener(
            contact, new DetailsListener(treeNode, callButton, uiContact));
      }
    }

    // for SourceContact in history that do not support telephony, we
    // show the button but disabled
    List<ProtocolProviderService> providers =
        AccountUtils.getOpSetRegisteredProviders(OperationSetBasicTelephony.class, null, null);

    if ((telephonyContact != null && telephonyContact.getAddress() != null)
        || (contactPhoneUtil != null && contactPhoneUtil.isCallEnabled() && providers.size() > 0)) {
      x += addButton(callButton, ++gridX, x, false);
    }

    UIContactDetail videoContact =
        uiContact.getDefaultContactDetail(OperationSetVideoTelephony.class);

    if (videoContact != null
        || (contactPhoneUtil != null && contactPhoneUtil.isVideoCallEnabled())) {
      x += addButton(callVideoButton, ++gridX, x, false);
    }

    UIContactDetail desktopContact =
        uiContact.getDefaultContactDetail(OperationSetDesktopSharingServer.class);

    if (desktopContact != null
        || (contactPhoneUtil != null && contactPhoneUtil.isDesktopSharingEnabled())) {
      x += addButton(desktopSharingButton, ++gridX, x, false);
    }

    // enable add contact button if contact source has indicated
    // that this is possible
    if (uiContact.getDescriptor() instanceof SourceContact
        && uiContact.getDefaultContactDetail(OperationSetPersistentPresence.class) != null
        && AccountUtils.getOpSetRegisteredProviders(
                    OperationSetPersistentPresence.class, null, null)
                .size()
            > 0
        && !ConfigurationUtils.isAddContactDisabled()) {
      x += addButton(addContactButton, ++gridX, x, false);
    }

    // The list of the contact actions
    // we will create a button for every action
    Collection<SIPCommButton> contactActions = uiContact.getContactCustomActionButtons();

    int lastGridX = gridX;
    if (contactActions != null && contactActions.size() > 0) {
      lastGridX = initContactActionButtons(contactActions, gridX, x);
    } else {
      addLabels(gridX);
    }

    if (lastAddedButton != null) setButtonBg(lastAddedButton, lastGridX, true);

    this.setBounds(0, 0, treeContactList.getWidth(), getPreferredSize().height);
  }
  /**
   * Adds contact entry labels.
   *
   * @param nameLabelGridWidth the grid width of the contact entry name label
   */
  private void addLabels(int nameLabelGridWidth) {
    remove(nameLabel);
    remove(rightLabel);
    remove(displayDetailsLabel);

    if (treeNode != null && !(treeNode instanceof GroupNode))
      constraints.insets = new Insets(0, 0, V_GAP, H_GAP);
    else constraints.insets = new Insets(0, 0, 0, H_GAP);

    constraints.anchor = GridBagConstraints.WEST;
    constraints.fill = GridBagConstraints.NONE;
    constraints.gridx = 1;
    constraints.gridy = 0;
    constraints.weightx = 1f;
    constraints.weighty = 0f;
    constraints.gridheight = 1;
    constraints.gridwidth = nameLabelGridWidth;
    this.add(nameLabel, constraints);

    constraints.anchor = GridBagConstraints.NORTHEAST;
    constraints.fill = GridBagConstraints.VERTICAL;
    constraints.gridx = nameLabelGridWidth + 1;
    constraints.gridy = 0;
    constraints.gridheight = 3;
    constraints.weightx = 0f;
    constraints.weighty = 1f;
    this.add(rightLabel, constraints);

    if (treeNode != null && treeNode instanceof ContactNode) {
      constraints.anchor = GridBagConstraints.WEST;
      constraints.fill = GridBagConstraints.NONE;
      constraints.gridx = 1;
      constraints.gridy = 1;
      constraints.weightx = 1f;
      constraints.weighty = 0f;
      constraints.gridwidth = nameLabelGridWidth;
      constraints.gridheight = 1;

      this.add(displayDetailsLabel, constraints);
    } else if (treeNode != null && treeNode instanceof GroupNode) {
      constraints.anchor = GridBagConstraints.WEST;
      constraints.fill = GridBagConstraints.NONE;
      constraints.gridx = 1;
      constraints.gridy = 1;
      constraints.weightx = 1f;
      constraints.weighty = 0f;
      constraints.gridwidth = nameLabelGridWidth;
      constraints.gridheight = 1;

      this.add(displayDetailsLabel, constraints);
    }
  }
  /** Initializes the panel containing the node. */
  public ContactListTreeCellRenderer() {
    super(new GridBagLayout());

    loadSkin();

    this.setOpaque(true);
    this.nameLabel.setOpaque(false);

    this.displayDetailsLabel.setFont(getFont().deriveFont(9f));
    this.displayDetailsLabel.setForeground(Color.GRAY);

    this.rightLabel.setHorizontalAlignment(JLabel.RIGHT);

    // !! IMPORTANT: General insets used for all components if not
    // overwritten!
    constraints.insets = new Insets(0, 0, 0, H_GAP);

    constraints.anchor = GridBagConstraints.WEST;
    constraints.fill = GridBagConstraints.NONE;
    constraints.gridx = 0;
    constraints.gridy = 0;
    constraints.gridheight = 1;
    constraints.weightx = 0f;
    constraints.weighty = 1f;
    this.add(statusLabel, constraints);

    addLabels(1);

    callButton.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            if (treeNode != null && treeNode instanceof ContactNode) {
              call(treeNode, callButton, false, false);
            }
          }
        });

    callVideoButton.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            if (treeNode != null && treeNode instanceof ContactNode) {
              call(treeNode, callVideoButton, true, false);
            }
          }
        });

    desktopSharingButton.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            if (treeNode != null && treeNode instanceof ContactNode) {
              call(treeNode, desktopSharingButton, true, true);
            }
          }
        });

    chatButton.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            if (treeNode != null && treeNode instanceof ContactNode) {
              UIContact contactDescriptor = ((ContactNode) treeNode).getContactDescriptor();

              if (contactDescriptor.getDescriptor() instanceof MetaContact) {
                GuiActivator.getUIService()
                    .getChatWindowManager()
                    .startChat((MetaContact) contactDescriptor.getDescriptor());
              }
            }
          }
        });

    addContactButton.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            if (treeNode != null && treeNode instanceof ContactNode) {
              UIContact contactDescriptor = ((ContactNode) treeNode).getContactDescriptor();

              // The add contact function has only sense for external
              // source contacts.
              if (contactDescriptor instanceof SourceUIContact) {
                addContact((SourceUIContact) contactDescriptor);
              }
            }
          }
        });

    initButtonToolTips();
    this.setToolTipText("");
  }