package com.apple.laf;
import javax.swing.JComponent;
import javax.swing.ImageIcon;
import javax.swing.JRadioButton;
import javax.swing.Icon;
import javax.swing.AbstractButton;
import javax.swing.AbstractAction;
import javax.swing.KeyStroke;
import javax.swing.DefaultButtonModel;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.plaf.ComponentUI;
import java.awt.Component;
import java.awt.AWTKeyStroke;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import apple.laf.JRSUIConstants.Widget;
import com.apple.laf.AquaUtilControlSize.SizeVariant;
import com.apple.laf.AquaUtilControlSize.SizeDescriptor;
import com.apple.laf.AquaUtils.RecyclableSingleton;
import com.apple.laf.AquaUtils.RecyclableSingletonFromDefaultConstructor;
import java.util.HashSet;
import java.util.Set;
import java.util.Enumeration;
public class AquaButtonRadioUI extends AquaButtonLabeledUI {
private KeyListener keyListener = null;
@SuppressWarnings("serial")
private class SelectPreviousBtn extends AbstractAction {
public SelectPreviousBtn() {
super("Previous");
}
@Override
public void actionPerformed(ActionEvent e) {
AquaButtonRadioUI.this.selectRadioButton(e, false);
}
}
@SuppressWarnings("serial")
private class SelectNextBtn extends AbstractAction {
public SelectNextBtn() {
super("Next");
}
@Override
public void actionPerformed(ActionEvent e) {
AquaButtonRadioUI.this.selectRadioButton(e, true);
}
}
private static final RecyclableSingleton<AquaButtonRadioUI> instance = new RecyclableSingletonFromDefaultConstructor<AquaButtonRadioUI>(AquaButtonRadioUI.class);
private static final RecyclableSingleton<ImageIcon> sizingIcon = new RecyclableSingleton<ImageIcon>() {
protected ImageIcon getInstance() {
return new ImageIcon(AquaNativeResources.getRadioButtonSizerImage());
}
};
public static ComponentUI createUI(final JComponent b) {
return instance.get();
}
public static Icon getSizingRadioButtonIcon() {
return sizingIcon.get();
}
protected String getPropertyPrefix() {
return "RadioButton" + ".";
}
protected AquaButtonBorder getPainter() {
return new RadioButtonBorder();
}
public static class RadioButtonBorder extends LabeledButtonBorder {
public RadioButtonBorder() {
super(new SizeDescriptor(new SizeVariant().replaceMargins("RadioButton.margin")));
painter.state.set(Widget.BUTTON_RADIO);
}
public RadioButtonBorder(final RadioButtonBorder other) {
super(other);
}
}
private KeyListener createKeyListener() {
if (keyListener == null) {
keyListener = new KeyHandler();
}
return keyListener;
}
private boolean isValidRadioButtonObj(Object obj) {
return ((obj instanceof JRadioButton) &&
((JRadioButton)obj).isVisible() &&
((JRadioButton)obj).isEnabled());
}
@Override
protected void installListeners(AbstractButton button) {
super.installListeners(button);
if (!(button instanceof JRadioButton))
return;
keyListener = createKeyListener();
button.addKeyListener(keyListener);
button.setFocusTraversalKeysEnabled(false);
button.getActionMap().put("Previous", new SelectPreviousBtn());
button.getActionMap().put("Next", new SelectNextBtn());
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
put(KeyStroke.getKeyStroke("UP"), "Previous");
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
put(KeyStroke.getKeyStroke("DOWN"), "Next");
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
put(KeyStroke.getKeyStroke("LEFT"), "Previous");
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
put(KeyStroke.getKeyStroke("RIGHT"), "Next");
}
@Override
protected void uninstallListeners(AbstractButton button) {
super.uninstallListeners(button);
if (!(button instanceof JRadioButton))
return;
button.getActionMap().remove("Previous");
button.getActionMap().remove("Next");
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
remove(KeyStroke.getKeyStroke("UP"));
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
remove(KeyStroke.getKeyStroke("DOWN"));
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
remove(KeyStroke.getKeyStroke("LEFT"));
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
remove(KeyStroke.getKeyStroke("RIGHT"));
if (keyListener != null ) {
button.removeKeyListener(keyListener);
keyListener = null;
}
}
private void selectRadioButton(ActionEvent event, boolean next) {
Object eventSrc = event.getSource();
if (!isValidRadioButtonObj(eventSrc))
return;
ButtonGroupInfo btnGroupInfo = new ButtonGroupInfo((JRadioButton)eventSrc);
btnGroupInfo.selectNewButton(next);
}
private class ButtonGroupInfo {
JRadioButton activeBtn = null;
JRadioButton firstBtn = null;
JRadioButton lastBtn = null;
JRadioButton previousBtn = null;
JRadioButton nextBtn = null;
HashSet<JRadioButton> btnsInGroup = null;
boolean srcFound = false;
public ButtonGroupInfo(JRadioButton btn) {
activeBtn = btn;
btnsInGroup = new HashSet<JRadioButton>();
}
boolean containsInGroup(Object obj) {
return btnsInGroup.contains(obj);
}
Component getFocusTransferBaseComponent(boolean next) {
return firstBtn;
}
boolean getButtonGroupInfo() {
if (activeBtn == null)
return false;
btnsInGroup.clear();
ButtonModel model = activeBtn.getModel();
if (!(model instanceof DefaultButtonModel))
return false;
DefaultButtonModel bm = (DefaultButtonModel) model;
ButtonGroup group = bm.getGroup();
if (group == null)
return false;
Enumeration<AbstractButton> e = group.getElements();
if (e == null)
return false;
while (e.hasMoreElements()) {
AbstractButton curElement = e.nextElement();
if (!isValidRadioButtonObj(curElement))
continue;
btnsInGroup.add((JRadioButton) curElement);
if (null == firstBtn)
firstBtn = (JRadioButton)curElement;
if (activeBtn == curElement)
srcFound = true;
else if (!srcFound) {
previousBtn = (JRadioButton) curElement;
} else if (nextBtn == null) {
nextBtn = (JRadioButton) curElement;
}
lastBtn = (JRadioButton)curElement;
}
return true;
}
void selectNewButton(boolean next) {
if (!getButtonGroupInfo())
return;
if (srcFound) {
JRadioButton newSelectedBtn = null;
if (next) {
newSelectedBtn = (null == nextBtn) ? firstBtn : nextBtn;
} else {
newSelectedBtn = (null == previousBtn) ? lastBtn: previousBtn;
}
if (newSelectedBtn != null && newSelectedBtn != activeBtn) {
newSelectedBtn.requestFocusInWindow();
newSelectedBtn.setSelected(true);
}
}
}
void jumpToNextComponent(boolean next) {
if (!getButtonGroupInfo()) {
if (activeBtn != null) {
lastBtn = activeBtn;
firstBtn = activeBtn;
} else
return;
}
Component focusBase = getFocusTransferBaseComponent(next);
if (focusBase != null) {
if (next) {
KeyboardFocusManager.
getCurrentKeyboardFocusManager().focusNextComponent(focusBase);
} else {
KeyboardFocusManager.
getCurrentKeyboardFocusManager().focusPreviousComponent(focusBase);
}
}
}
}
private class KeyHandler implements KeyListener {
@Override
public void keyPressed(KeyEvent e) {
AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
if (stroke != null && e.getSource() instanceof JRadioButton) {
JRadioButton source = (JRadioButton) e.getSource();
boolean next = isFocusTraversalKey(source,
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, stroke);
if (next || isFocusTraversalKey(source,
KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, stroke)) {
e.consume();
ButtonGroupInfo btnGroupInfo = new ButtonGroupInfo(source);
btnGroupInfo.jumpToNextComponent(next);
}
}
}
private boolean isFocusTraversalKey(JComponent c, int id,
AWTKeyStroke stroke) {
Set<AWTKeyStroke> keys = c.getFocusTraversalKeys(id);
return keys != null && keys.contains(stroke);
}
@Override public void keyReleased(KeyEvent e) {}
@Override public void keyTyped(KeyEvent e) {}
}
}