/* ====================================================================
*
* Office Look and Feels License
* http://sourceforge.net/projects/officelnfs
*
* Copyright (c) 2003-2005 Robert Futrell. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The names "Office Look And Feels" and "OfficeLnFs" must not
* be used to endorse or promote products derived from this software
* without prior written permission. For written permission, please
* contact robert_futrell@users.sourceforge.net.
*
* 4. Products derived from this software may not be called "OfficeLnFs"
* nor may "OfficeLnFs" appear in their names without prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*/
package org.fife.plaf.OfficeXP;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.plaf.ComponentUI;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.View;
import com.sun.java.swing.plaf.windows.WindowsToggleButtonUI;
/**
* Toggle button UI for the Office XP Look and Feel.
*
* @author Robert Futrell
* @version 0.5
*/
public class OfficeXPToggleButtonUI extends WindowsToggleButtonUI {
private static MouseInputHandler mouseInputHandler;
private boolean isMouseOver = false;
private Rectangle viewRect = new Rectangle();
private Rectangle iconRect = new Rectangle();
private Rectangle textRect = new Rectangle();
/**
* This is the width and height of toolbar buttons in Office XP
* applications that are only an icon, no text (which is almost all of
* them).
*/
private static final int PREFERRED_TOOLBAR_BUTTON_SIZE = 23;
public static ComponentUI createUI(JComponent c){
return new OfficeXPToggleButtonUI();
}
protected synchronized MouseInputHandler getMouseInputHandler() {
if (mouseInputHandler==null)
mouseInputHandler = new MouseInputHandler();
return mouseInputHandler;
}
public Dimension getPreferredSize(JComponent c) {
Component parent = c.getParent();
if (parent!=null && (parent instanceof JToolBar)) {
Dimension preferredSize = super.getPreferredSize(c);
if (preferredSize.width<PREFERRED_TOOLBAR_BUTTON_SIZE)
preferredSize.width = PREFERRED_TOOLBAR_BUTTON_SIZE;
if (preferredSize.height<PREFERRED_TOOLBAR_BUTTON_SIZE)
preferredSize.height = PREFERRED_TOOLBAR_BUTTON_SIZE;
return preferredSize;
}
return super.getPreferredSize(c);
}
public void installListeners(AbstractButton b) {
super.installListeners(b);
mouseInputHandler = getMouseInputHandler();
b.addMouseListener(mouseInputHandler);
}
protected boolean isMouseOver() {
return isMouseOver;
}
public void paint(Graphics g, JComponent c) {
AbstractButton button = (AbstractButton)c;
if (button.getParent() instanceof JToolBar) {
paintToolbarToggleButton(g, button);
}
else {
super.paint(g, c);
}
}
public void paintToolbarToggleButton(Graphics g, JComponent c) {
paintToolbarToggleButtonBackground(g,c);
AbstractButton b = (AbstractButton) c;
Icon icon = b.getIcon();
String text = b.getText();
FontMetrics fm = c.getFontMetrics(g.getFont());
Insets i = c.getInsets();
viewRect.x = i.left;
viewRect.y = i.top;
viewRect.width = b.getWidth() - (i.right + viewRect.x);
viewRect.height = b.getHeight() - (i.bottom + viewRect.y);
textRect.x = textRect.y = textRect.width = textRect.height = 0;
iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
text = SwingUtilities.layoutCompoundLabel(
c, fm, text, icon,
b.getVerticalAlignment(), b.getHorizontalAlignment(),
b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
viewRect, iconRect, textRect,
text==null ? 0 : b.getIconTextGap());
// Actually paint the text and icon.
if (text!=null && !text.equals("")) {
View v = (View) c.getClientProperty(BasicHTML.propertyKey);
if (v != null)
v.paint(g, textRect);
else
paintText(g, c, textRect, text);
}
paintIcon(g,c, iconRect);
}
protected void paintToolbarToggleButtonBackground(Graphics g,
JComponent c) {
AbstractButton button = (AbstractButton)c;
ButtonModel model = button.getModel();
int width = button.getWidth() - 1;
int height = button.getHeight() - 1;
// Before you try to "optimize" the below, remember this:
// model.isEnabled() returns whether the button is enabled.
// model.isArmed() returns true ONLY IF both the button is depressed
// (model.isPressed()) and the mouse is over the
// button.
// isMouseOver() returns true ONLY IF the mouse is over the button AND
// this button is the one over which the mouse was
// originally depressed. (This is equivalent to
// model.isRollover() in Sun's JRE 1.5, but not 1.4).
// model.isPressed() returns true if the user has down-clicked on the
// button and not yet released the mouse button.
// model.isSelected() returns true if the button is "pressed." I
// THINK that for regular JButtons, this is equivalent
// to model.isPressed(), but for JToggleButtons, this
// is whether or not the button is currently in its
// down position (i.e., the user may have clicked and
// released the mouse button already).
// model.isRollover() returns true if the mouse is over the button.
// Note that the behavior for this changes between Sun's
// 1.4 and 1.5 Windows JREs...
// This case is when the button isn't even enabled.
if (!model.isEnabled()) {
// Do nothing.
}
// If the toggle button is depressed...
else if (model.isSelected()) {
// And the mouse is over the button OR they've clicked a
// mouse button and (maybe) since moved the mouse.
if (isMouseOver() || model.isPressed()) {
OfficeXPButtonUI.paintArmedToolbarButtonBackground(g,
width, height);
}
// And the mouse isn't over the button and they haven't clicked
// a mouse button.
else {
OfficeXPButtonUI.paintMouseOverToolbarButtonBackground(g,
width, height);
}
}
// This case is when the user has down-clicked, and the mouse is
// still over the button.
else if (model.isArmed()) {
OfficeXPButtonUI.paintArmedToolbarButtonBackground(g,
width, height);
}
// This case is when the mouse is hovering over the button, or
// they've left-clicked, but moved the mouse off the button.
else if (isMouseOver() || model.isPressed()) {
OfficeXPButtonUI.paintMouseOverToolbarButtonBackground(g,
width, height);
}
// This case is when the user hasn't clicked on the button, nor is
// the mouse over it.
else {
// Do nothing.
}
}
/**
* Paints the icon for the specified button.
*
* @param g The graphics context.
* @param c The button.
* @param iconRect The area in which to paint the icon.
*/
protected void paintToolbarToggleButtonIcon(Graphics g, JComponent c,
Rectangle iconRect) {
// NOTE: This method is NOT the same as OfficeXPButtonUI's
// paintToolbarButtonIcon() so we cannot factor it out!
AbstractButton b = (AbstractButton) c;
ButtonModel model = b.getModel();
Icon icon;
if (!model.isEnabled()) {
OfficeXPUtilities.paintDisabledButtonIcon(g, b, iconRect);
}
else if (model.isArmed() || model.isSelected()) {
icon = (Icon)b.getIcon();
if (icon!=null)
icon.paintIcon(c,g, iconRect.x, iconRect.y);
}
else if (isMouseOver() || model.isPressed()) {
icon = (Icon)b.getDisabledIcon();
// Necessary as some icons (like JInternalFrame's) don't have
// disabled icons.
if (icon!=null) {
icon.paintIcon(c,g, iconRect.x+1, iconRect.y+1);
icon = (Icon)b.getIcon();
// Not sure why we'd have a disabled icon but not a regular
// one, but better safe than sorry...
if (icon!=null)
icon.paintIcon(c,g, iconRect.x-1, iconRect.y-1);
}
else {
icon = b.getIcon();
if (icon!=null)
icon.paintIcon(c,g, iconRect.x, iconRect.y);
}
}
else {
icon = (Icon)b.getIcon();
if (icon!=null)
icon.paintIcon(c,g, iconRect.x, iconRect.y);
}
}
protected void setMouseOver(boolean over) {
isMouseOver = over;
}
public void uninstallListeners(AbstractButton b) {
b.removeMouseListener(mouseInputHandler);
super.installListeners(b);
}
/**
* Listens for mouse events in all Office XP buttons, although it's only
* really interested in toolbar buttons (FIXME: It'd be nice to be able
* to just check the buttons' parents to see whether they're instances
* of <code>JToolBar</code> to add this listener, but then we'd
* need to add hierarchy listeners or something similar to each button
* in case they add/remove it during runtime, which would be even
* more overhead...). It's here so that we can have a toolbar button
* display the "rollover" effect only if no other toolbar button is
* depressed. For example, if you mouse click on one button, but before
* you release it, you move the mouse over other buttons. The other
* buttons will have their <code>model.isRollover()</code> method return
* <code>true</code>, which will result in us having two buttons
* highlighted at the same time, which isn't what we want.<p>
*
* Note that in Sun's JRE 1.5, it appears that the above scenario is
* "fixed;" that is, we don't need this mouse input handler. In 1.5,
* clicking on a button and moving the mouse over other buttons before
* releasing the mouse button does NOT make the "other buttons" have their
* <code>model.isRollover()</code> method return <code>true</code>. But,
* since we're remaining backwards-compatible with 1.4, we're keeping this
* here.
*/
protected static class MouseInputHandler extends MouseInputAdapter {
/**
* Flag for whether some toolbar button is depressed. This is to
* prevent the "rollover" property from making two toolbar buttons
* appear highlighted at the same time.
*/
private boolean someButtonDepressed;
public MouseInputHandler() {
}
public void mousePressed(MouseEvent e) {
someButtonDepressed = true;
}
public void mouseReleased(MouseEvent e) {
someButtonDepressed = false;
}
public void mouseEntered(MouseEvent e) {
if (!someButtonDepressed) {
AbstractButton b = (AbstractButton)e.getSource();
OfficeXPToggleButtonUI ui =
(OfficeXPToggleButtonUI)b.getUI();
ui.setMouseOver(true);
}
}
public void mouseExited(MouseEvent e) {
AbstractButton b = (AbstractButton)e.getSource();
OfficeXPToggleButtonUI ui = (OfficeXPToggleButtonUI)b.getUI();
ui.setMouseOver(false);
}
}
}