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