/*
* @(#)QuaquaButtonListener.java
*
* Copyright (c) 2004-2010 Werner Randelshofer, Immensee, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package ch.randelshofer.quaqua;
import ch.randelshofer.quaqua.util.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.plaf.basic.*;
import java.beans.*;
import java.util.Enumeration;
/**
* QuaquaButtonListener.
*
* @author Werner Randelshofer
* @version $Id: QuaquaButtonListener.java 414 2011-07-29 20:55:05Z wrandelshofer $
*/
public class QuaquaButtonListener extends BasicButtonListener {
transient long lastPressedTimestamp = -1;
transient boolean shouldDiscardRelease = false;
/** Creates a new instance. */
public QuaquaButtonListener(AbstractButton button) {
super(button);
}
@Override
public void propertyChange(PropertyChangeEvent e) {
String prop = e.getPropertyName();
if (e.getSource() instanceof AbstractButton) {
AbstractButton btn = ((AbstractButton) e.getSource());
if (prop==null||
prop.equals("Frame.active")||
prop.equals("Quaqua.Button.type")||
prop.equals("JButton.buttonType")||
prop.equals("JButton.segmentPosition")||
prop.equals("JComponent.sizeVariant")) {
btn.repaint();
}
}
super.propertyChange(e);
}
@Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
AbstractButton b = (AbstractButton) e.getSource();
if (b.contains(e.getX(), e.getY())) {
long multiClickThreshhold = Methods.invokeGetter(b, "getMultiClickThreshhold", (long) 0);
long lastTime = lastPressedTimestamp;
long currentTime = lastPressedTimestamp = e.getWhen();
if (lastTime != -1 && currentTime - lastTime < multiClickThreshhold) {
shouldDiscardRelease = true;
return;
}
ButtonModel model = b.getModel();
if (!model.isEnabled()) {
// Disabled buttons ignore all input...
return;
}
if (!model.isArmed()) {
// button not armed, should be
model.setArmed(true);
}
if (!b.hasFocus()) {
if (b.isRequestFocusEnabled()) {
b.requestFocus();
} else {
// request focus if one of the buttons in the button group
// has focus
if (model instanceof DefaultButtonModel) {
ButtonGroup grp = ((DefaultButtonModel) model).getGroup();
if (grp != null) {
for (Enumeration i = grp.getElements(); i.hasMoreElements();) {
AbstractButton grpButton = (AbstractButton) i.nextElement();
if (grpButton.hasFocus()) {
b.setFocusable(true);
b.requestFocus();
break;
}
}
}
}
}
}
// Update model after focus changes have been requested, so that
// model can request focus changes of its own.
model.setPressed(true);
}
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
// Support for multiClickThreshhold
if (shouldDiscardRelease) {
shouldDiscardRelease = false;
return;
}
AbstractButton b = (AbstractButton) e.getSource();
ButtonModel model = b.getModel();
model.setPressed(false);
model.setArmed(false);
// restore focus in the button group
if (!model.isSelected() && b.hasFocus()) {
if (model instanceof DefaultButtonModel) {
ButtonGroup grp = ((DefaultButtonModel) model).getGroup();
if (grp != null) {
boolean groupHasFocus = false;
for (Enumeration i = grp.getElements(); i.hasMoreElements();) {
AbstractButton grpButton = (AbstractButton) i.nextElement();
if (grpButton.isSelected()) {
grpButton.requestFocus();
break;
}
}
b.setFocusable(false);
}
}
}
}
}
@Override
public void mouseEntered(MouseEvent e) {
AbstractButton b = (AbstractButton) e.getSource();
ButtonModel model = b.getModel();
if (b.isRolloverEnabled()) {
model.setRollover(true);
}
if (model.isPressed()) {
model.setArmed(true);
}
}
@Override
public void mouseExited(MouseEvent e) {
AbstractButton b = (AbstractButton) e.getSource();
ButtonModel model = b.getModel();
if (b.isRolloverEnabled()) {
model.setRollover(false);
}
model.setArmed(false);
}
@Override
public void focusGained(FocusEvent e) {
AbstractButton b = (AbstractButton) e.getSource();
if (b instanceof JButton && ((JButton) b).isDefaultCapable()) {
JRootPane root = b.getRootPane();
if (root != null) {
QuaquaButtonUI ui = (QuaquaButtonUI) QuaquaUtilities.getUIOfType(
((AbstractButton) b).getUI(), QuaquaButtonUI.class);
if (ui != null
&& UIManager.get(ui.getPropertyPrefix() + "defaultButtonFollowsFocus") != Boolean.FALSE) {
root.putClientProperty("temporaryDefaultButton", b);
root.setDefaultButton((JButton) b);
root.putClientProperty("temporaryDefaultButton", null);
}
}
}
b.repaint();
}
@Override
public void focusLost(FocusEvent e) {
AbstractButton b = (AbstractButton) e.getSource();
JRootPane root = b.getRootPane();
if (root != null) {
JButton initialDefault = (JButton) root.getClientProperty("initialDefaultButton");
if (b != initialDefault) {
QuaquaButtonUI ui = (QuaquaButtonUI) QuaquaUtilities.getUIOfType(
((AbstractButton) b).getUI(), QuaquaButtonUI.class);
if (ui != null
&& UIManager.get(ui.getPropertyPrefix() + "defaultButtonFollowsFocus") != Boolean.FALSE) {
root.setDefaultButton(initialDefault);
}
}
}
b.getModel().setArmed(false);
b.repaint();
}
/**
* Populates Buttons actions.
*/
static void loadActionMap(QuaquaLazyActionMap map) {
map.put(new Actions(Actions.PRESS));
map.put(new Actions(Actions.RELEASE));
map.put(new Actions(Actions.SELECT_NEXT_BUTTON));
map.put(new Actions(Actions.SELECT_PREVIOUS_BUTTON));
}
/** Keyboard action for selecting the next/previous button in a radio
* button group.
*/
private static class Actions extends AbstractAction {
private static final String PRESS = "pressed";
private static final String RELEASE = "released";
private static final String SELECT_NEXT_BUTTON = "selectNextButton";
private static final String SELECT_PREVIOUS_BUTTON = "selectPreviousButton";
public Actions(String name) {
super(name);
}
public String getName() {
return (String) getValue(Action.NAME);
}
public void actionPerformed(ActionEvent e) {
AbstractButton b = (AbstractButton) e.getSource();
String key = getName();
if (key == PRESS) {
ButtonModel model = b.getModel();
model.setArmed(true);
model.setPressed(true);
if (!b.hasFocus()) {
b.requestFocus();
}
} else if (key == RELEASE) {
ButtonModel model = b.getModel();
model.setPressed(false);
model.setArmed(false);
} else if (key == SELECT_NEXT_BUTTON) {
ButtonModel model = b.getModel();
if (model instanceof DefaultButtonModel) {
DefaultButtonModel defaultButtonModel = (DefaultButtonModel) model;
ButtonGroup group = defaultButtonModel.getGroup();
if (group != null) {
AbstractButton btn = getSiblingButton(group, true);
if (btn != null) {
// Invoke doClick before requesting focus in window,
// so that focus stays on button regardless of what
// listeners do.
btn.doClick();
btn.requestFocusInWindow();
}
}
}
} else if (key == SELECT_PREVIOUS_BUTTON) {
ButtonModel model = b.getModel();
if (model instanceof DefaultButtonModel) {
DefaultButtonModel defaultButtonModel = (DefaultButtonModel) model;
ButtonGroup group = defaultButtonModel.getGroup();
if (group != null) {
AbstractButton btn = getSiblingButton(group, false);
if (btn != null) {
// Invoke doClick before requesting focus in window,
// so that focus stays on button regardless of what
// listeners do.
btn.doClick();
btn.requestFocusInWindow();
}
}
}
}
}
public boolean isEnabled(Object sender) {
if (sender != null && (sender instanceof AbstractButton)
&& !((AbstractButton) sender).getModel().isEnabled()) {
return false;
} else {
return true;
}
}
private AbstractButton getSiblingButton(ButtonGroup group, boolean isSelectNext) {
AbstractButton adjacentToSelected = null;
AbstractButton adjacentToFocused = null;
if (isSelectNext) {
boolean takeNextSelected = false;
boolean takeNextFocused = false;
for (Enumeration i = group.getElements(); i.hasMoreElements();) {
final AbstractButton buttonInGroup = (AbstractButton) i.nextElement();
if (takeNextSelected && buttonInGroup.isEnabled()) {
adjacentToSelected = buttonInGroup;
takeNextSelected = false;
}
if (takeNextFocused && buttonInGroup.isEnabled()) {
adjacentToFocused = buttonInGroup;
takeNextFocused = false;
}
if (buttonInGroup.isSelected()) {
takeNextSelected = true;
}
if (buttonInGroup.isFocusOwner()) {
takeNextFocused = true;
}
}
} else {
AbstractButton previousButton = null;
for (Enumeration i = group.getElements(); i.hasMoreElements();) {
final AbstractButton buttonInGroup = (AbstractButton) i.nextElement();
if (buttonInGroup.isSelected()) {
adjacentToSelected = previousButton;
}
if (buttonInGroup.isFocusOwner()) {
adjacentToFocused = previousButton;
}
if (buttonInGroup.isEnabled()) {
previousButton = buttonInGroup;
}
}
}
return (adjacentToFocused == null) ? adjacentToSelected : adjacentToFocused;
}
}
}