001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.guitools.controlpanel.ui.components;
028
029import java.awt.Color;
030import java.awt.Dimension;
031import java.awt.Font;
032import java.awt.Graphics;
033import java.awt.event.ActionEvent;
034import java.awt.event.MouseEvent;
035
036import javax.swing.BorderFactory;
037import javax.swing.JButton;
038import javax.swing.SwingConstants;
039import javax.swing.SwingUtilities;
040import javax.swing.UIManager;
041import javax.swing.border.Border;
042import javax.swing.border.EmptyBorder;
043
044import org.forgerock.i18n.LocalizableMessage;
045import org.opends.guitools.controlpanel.datamodel.Action;
046import org.opends.guitools.controlpanel.datamodel.Category;
047import org.opends.guitools.controlpanel.ui.ColorAndFontConstants;
048
049/**
050 * A basic extension of a button that changes its rendering so that the looks
051 * are more similar to a row in a list.  It is used in the actions on the left
052 * of the main Control Center dialog (in actions like 'Manage Entries...',
053 * 'Import from LDIF...' etc.
054 */
055public class ActionButton extends JButton
056{
057  private static final long serialVersionUID = -1898192406268037714L;
058
059  private static final Border buttonBorder;
060  private static final Border focusBorder;
061  private final Action action;
062  private boolean isBeingPressed;
063  private boolean hasMouseOver;
064  static
065  {
066    //Calculate border based on category settings
067    Category cat = new Category();
068    cat.setName(LocalizableMessage.EMPTY);
069    CategoryButton b = new CategoryButton(cat);
070    int n = b.getIconTextGap() + b.getIcon().getIconWidth() +
071    b.getBorder().getBorderInsets(b).left;
072    buttonBorder = new EmptyBorder(5, n, 5, 25);
073    Border highlightBorder =
074      UIManager.getBorder("List.focusCellHighlightBorder");
075    // This is required (see issue
076    // https://opends.dev.java.net/issues/show_bug.cgi?id=4400)
077    // since in OpenJDK the CompoundBorder class does not handle properly
078    // null insets.
079    if (highlightBorder != null)
080    {
081      try
082      {
083        b.setBorder(BorderFactory.createCompoundBorder(
084          highlightBorder, buttonBorder));
085      }
086      catch (Throwable t)
087      {
088        highlightBorder = null;
089      }
090    }
091    if (highlightBorder == null)
092    {
093      highlightBorder =
094        new javax.swing.plaf.BorderUIResource.LineBorderUIResource(
095            ColorAndFontConstants.pressedForeground, 1);
096    }
097    focusBorder = BorderFactory.createCompoundBorder(
098        highlightBorder, buttonBorder);
099  }
100
101  private static final Color defaultBackground =
102    ColorAndFontConstants.background;
103
104  private static final Color defaultForeground =
105    ColorAndFontConstants.foreground;
106
107  private static final Color mouseOverBackground =
108    ColorAndFontConstants.mouseOverBackground;
109
110  private static final Color mouseOverForeground =
111    ColorAndFontConstants.mouseOverForeground;
112
113  private static final Color pressedBackground =
114    ColorAndFontConstants.pressedBackground;
115
116  private static final Color pressedForeground =
117    ColorAndFontConstants.pressedForeground;
118
119  private static final Font actionFont = ColorAndFontConstants.defaultFont;
120
121
122  /**
123   * Creates a button associated with the provided action.
124   * @param action the action.
125   */
126  public ActionButton(Action action) {
127    super();
128    this.action = action;
129    setText(action.getName().toString());
130    setIconTextGap(0);
131    setHorizontalTextPosition(SwingConstants.TRAILING);
132    setHorizontalAlignment(SwingConstants.LEADING);
133    setOpaque(true);
134
135    setBorder(buttonBorder);
136    setFont(actionFont);
137
138    setFocusPainted(true);
139    setContentAreaFilled(false);
140    setToolTipText(action.getName().toString());
141    setRolloverEnabled(false);
142
143    Dimension d1 = getPreferredSize();
144    setBorder(focusBorder);
145    Dimension d2 = getPreferredSize();
146    setPreferredSize(new Dimension(Math.max(d1.width,d2.width),
147        Math.max(d1.height, d2.height)));
148    setBorder(buttonBorder);
149  }
150
151  /**
152   * Callback when an action has been performed.
153   *
154   * @param ev
155   *          the action event
156   */
157  public void actionPerformed(ActionEvent ev)
158  {
159    isBeingPressed = true;
160    final boolean[] hadMouseOver = {hasMouseOver};
161    hasMouseOver = true;
162    repaint();
163    SwingUtilities.invokeLater(new Runnable()
164    {
165      @Override
166      public void run()
167      {
168        isBeingPressed = false;
169        hasMouseOver = hadMouseOver[0];
170        repaint();
171      }
172    });
173  }
174
175  /**
176   * Callback when a mouse button has been pressed.
177   *
178   * @param e
179   *          the mouse event
180   */
181  public void mousePressed(MouseEvent e)
182  {
183    isBeingPressed = true;
184  }
185
186  /**
187   * Callback when a mouse button has been released.
188   *
189   * @param e
190   *          the mouse event
191   */
192  public void mouseReleased(MouseEvent e)
193  {
194    isBeingPressed = false;
195  }
196
197  /**
198   * Callback when mouse exited a component.
199   *
200   * @param e
201   *          the mouse event
202   */
203  public void mouseExited(MouseEvent e)
204  {
205    hasMouseOver = false;
206    repaint();
207  }
208
209  /**
210   * Callback when mouse entered a component.
211   *
212   * @param e
213   *          the mouse event
214   */
215  public void mouseEntered(MouseEvent e)
216  {
217    hasMouseOver = true;
218    repaint();
219  }
220
221  /** {@inheritDoc} */
222  @Override
223  public void updateUI() {
224      super.updateUI();
225      // some look and feels replace our border, so take it back
226      setBorder(buttonBorder);
227  }
228
229  /** {@inheritDoc} */
230  @Override
231  protected void paintComponent(Graphics g) {
232    setBorder(hasFocus() ? focusBorder : buttonBorder);
233    if (isBeingPressed && hasMouseOver)
234    {
235      setColors(g, pressedBackground, pressedForeground);
236    }
237    else if (hasMouseOver)
238    {
239      setColors(g, mouseOverBackground, mouseOverForeground);
240    }
241    else {
242      setColors(g, defaultBackground, defaultForeground);
243    }
244    super.paintComponent(g);
245  }
246
247  private void setColors(Graphics g, Color backgroundColor, Color foregroundColor)
248  {
249    setBackground(backgroundColor);
250    g.setColor(backgroundColor);
251    Dimension size = getSize();
252    g.fillRect(0, 0, size.width, size.height);
253    setForeground(foregroundColor);
254  }
255
256  /**
257   * Returns the action associated with this button.
258   * @return the action associated with this button.
259   */
260  public Action getActionObject() {
261      return action;
262  }
263}