| /* | |
| * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package com.apple.laf; | |
| import java.awt.Color; | |
| import java.awt.Component; | |
| import java.awt.Graphics; | |
| import java.awt.Insets; | |
| import javax.accessibility.AccessibleContext; | |
| import javax.swing.ButtonModel; | |
| import javax.swing.CellRendererPane; | |
| import javax.swing.DefaultButtonModel; | |
| import javax.swing.JButton; | |
| import javax.swing.JComboBox; | |
| import javax.swing.JList; | |
| import javax.swing.JPanel; | |
| import javax.swing.ListCellRenderer; | |
| import javax.swing.UIManager; | |
| import javax.swing.plaf.UIResource; | |
| import apple.laf.JRSUIConstants.AlignmentHorizontal; | |
| import apple.laf.JRSUIConstants.AlignmentVertical; | |
| import apple.laf.JRSUIConstants.ArrowsOnly; | |
| import apple.laf.JRSUIConstants.Focused; | |
| import apple.laf.JRSUIConstants.IndicatorOnly; | |
| import apple.laf.JRSUIConstants.Size; | |
| import apple.laf.JRSUIConstants.State; | |
| import apple.laf.JRSUIConstants.Widget; | |
| import apple.laf.JRSUIState; | |
| @SuppressWarnings("serial") // Superclass is not serializable across versions | |
| class AquaComboBoxButton extends JButton { | |
| protected final JComboBox<Object> comboBox; | |
| protected final JList<?> list; | |
| protected final CellRendererPane rendererPane; | |
| protected final AquaComboBoxUI ui; | |
| protected final AquaPainter<JRSUIState> painter = AquaPainter.create(JRSUIState.getInstance()); | |
| boolean isPopDown; | |
| boolean isSquare; | |
| protected AquaComboBoxButton(final AquaComboBoxUI ui, | |
| final JComboBox<Object> comboBox, | |
| final CellRendererPane rendererPane, | |
| final JList<?> list) { | |
| super(""); | |
| putClientProperty("JButton.buttonType", "comboboxInternal"); | |
| this.ui = ui; | |
| this.comboBox = comboBox; | |
| this.rendererPane = rendererPane; | |
| this.list = list; | |
| setModel(new DefaultButtonModel() { | |
| @Override | |
| public void setArmed(final boolean armed) { | |
| super.setArmed(isPressed() ? true : armed); | |
| } | |
| }); | |
| setEnabled(comboBox.isEnabled()); | |
| } | |
| @Override | |
| public boolean isEnabled() { | |
| return comboBox == null ? true : comboBox.isEnabled(); | |
| } | |
| @Override | |
| public boolean isFocusable() { | |
| return false; | |
| } | |
| protected void setIsPopDown(final boolean isPopDown) { | |
| this.isPopDown = isPopDown; | |
| repaint(); | |
| } | |
| protected void setIsSquare(final boolean isSquare) { | |
| this.isSquare = isSquare; | |
| repaint(); | |
| } | |
| protected State getState(final ButtonModel buttonModel) { | |
| if (!comboBox.isEnabled()) return State.DISABLED; | |
| if (!AquaFocusHandler.isActive(comboBox)) return State.INACTIVE; | |
| if (buttonModel.isArmed()) return State.PRESSED; | |
| return State.ACTIVE; | |
| } | |
| @Override | |
| public void paintComponent(final Graphics g) { | |
| // Don't Paint the button as usual | |
| // super.paintComponent( g ); | |
| final boolean editable = comboBox.isEditable(); | |
| int top = 0; | |
| int left = 0; | |
| int width = getWidth(); | |
| int height = getHeight(); | |
| if (comboBox.isOpaque()) { | |
| g.setColor(getBackground()); | |
| g.fillRect(0, 0, width, height); | |
| } | |
| final Size size = AquaUtilControlSize.getUserSizeFrom(comboBox); | |
| painter.state.set(size == null ? Size.REGULAR : size); | |
| final ButtonModel buttonModel = getModel(); | |
| painter.state.set(getState(buttonModel)); | |
| painter.state.set(AlignmentVertical.CENTER); | |
| if (AquaComboBoxUI.isTableCellEditor(comboBox)) { | |
| painter.state.set(AlignmentHorizontal.RIGHT); | |
| painter.state.set(Widget.BUTTON_POP_UP); | |
| painter.state.set(ArrowsOnly.YES); | |
| painter.paint(g, this, left, top, width, height); | |
| doRendererPaint(g, buttonModel, editable, getInsets(), left, top, width, height); | |
| return; | |
| } | |
| painter.state.set(AlignmentHorizontal.CENTER); | |
| final Insets insets = getInsets(); | |
| if (!editable) { | |
| top += insets.top; | |
| left += insets.left; | |
| width -= insets.left + insets.right; | |
| height -= insets.top + insets.bottom; | |
| } | |
| if (height <= 0 || width <= 0) { | |
| return; | |
| } | |
| boolean hasFocus = comboBox.hasFocus(); | |
| if (editable) { | |
| painter.state.set(Widget.BUTTON_COMBO_BOX); | |
| painter.state.set(IndicatorOnly.YES); | |
| painter.state.set(AlignmentHorizontal.LEFT); | |
| hasFocus |= comboBox.getEditor().getEditorComponent().hasFocus(); | |
| } else { | |
| painter.state.set(IndicatorOnly.NO); | |
| painter.state.set(AlignmentHorizontal.CENTER); | |
| if (isPopDown) { | |
| painter.state.set(isSquare ? Widget.BUTTON_POP_DOWN_SQUARE : Widget.BUTTON_POP_DOWN); | |
| } else { | |
| painter.state.set(isSquare ? Widget.BUTTON_POP_UP_SQUARE : Widget.BUTTON_POP_UP); | |
| } | |
| } | |
| painter.state.set(hasFocus ? Focused.YES : Focused.NO); | |
| if (isSquare) { | |
| painter.paint(g, comboBox, left + 2, top - 1, width - 4, height); | |
| } else { | |
| painter.paint(g, comboBox, left, top, width, height); | |
| } | |
| // Let the renderer paint | |
| if (!editable && comboBox != null) { | |
| doRendererPaint(g, buttonModel, editable, insets, left, top, width, height); | |
| } | |
| } | |
| private Component getRendererComponent() { | |
| final ListCellRenderer<Object> renderer = comboBox.getRenderer(); | |
| return renderer.getListCellRendererComponent(list, comboBox.getSelectedItem(), -1, false, false); | |
| } | |
| protected void doRendererPaint(final Graphics g, final ButtonModel buttonModel, final boolean editable, final Insets insets, int left, int top, int width, int height) { | |
| // fake it out! not renderPressed | |
| final Component c = getRendererComponent(); | |
| if (!editable && !AquaComboBoxUI.isTableCellEditor(comboBox)) { | |
| final int indentLeft = 10; | |
| final int buttonWidth = 24; | |
| // hardcoded for now. We should adjust as necessary. | |
| top += 1; | |
| height -= 4; | |
| left += indentLeft; | |
| width -= (indentLeft + buttonWidth); | |
| } | |
| c.setFont(rendererPane.getFont()); | |
| if (buttonModel.isArmed() && buttonModel.isPressed()) { | |
| if (isOpaque()) { | |
| c.setBackground(UIManager.getColor("Button.select")); | |
| } | |
| c.setForeground(comboBox.getForeground()); | |
| } else if (!comboBox.isEnabled()) { | |
| if (isOpaque()) { | |
| c.setBackground(UIManager.getColor("ComboBox.disabledBackground")); | |
| } | |
| c.setForeground(UIManager.getColor("ComboBox.disabledForeground")); | |
| } else { | |
| c.setForeground(comboBox.getForeground()); | |
| c.setBackground(comboBox.getBackground()); | |
| } | |
| // Sun Fix for 4238829: should lay out the JPanel. | |
| boolean shouldValidate = false; | |
| if (c instanceof JPanel) { | |
| shouldValidate = true; | |
| } | |
| final int iconWidth = 0; | |
| final int cWidth = width - (insets.right + iconWidth); | |
| // fix for 3156483 we need to crop images that are too big. | |
| // if (height > 18) | |
| // always crop. | |
| { | |
| top = height / 2 - 8; | |
| height = 19; | |
| } | |
| // It doesn't need to draw its background, we handled it | |
| final Color bg = c.getBackground(); | |
| final boolean inhibitBackground = bg instanceof UIResource; | |
| if (inhibitBackground) c.setBackground(new Color(0, 0, 0, 0)); | |
| rendererPane.paintComponent(g, c, this, left, top, cWidth, height, shouldValidate); // h - (insets.top + insets.bottom) ); | |
| if (inhibitBackground) c.setBackground(bg); | |
| // Remove component from renderer pane, allowing it to be gc'ed. | |
| rendererPane.remove(c); | |
| } | |
| @Override | |
| public AccessibleContext getAccessibleContext() { | |
| if (accessibleContext == null) { | |
| accessibleContext = new AccessibleAquaComboBoxButton(); | |
| } | |
| return accessibleContext; | |
| } | |
| private final class AccessibleAquaComboBoxButton extends AccessibleJButton { | |
| @Override | |
| public String getAccessibleName() { | |
| String name = super.getAccessibleName(); | |
| if ((name == null || name.isEmpty()) | |
| && (!comboBox.isEditable() && comboBox.getSelectedItem() != null)) { | |
| Component c = getRendererComponent(); | |
| name = c.getAccessibleContext().getAccessibleName(); | |
| } | |
| return name; | |
| } | |
| } | |
| } |