001: /*
002: * Copyright (c) 2001-2007 JGoodies Karsten Lentzsch. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of JGoodies Karsten Lentzsch nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package com.jgoodies.looks.plastic;
032:
033: import java.awt.Component;
034: import java.awt.Graphics;
035: import java.awt.Insets;
036:
037: import javax.swing.*;
038: import javax.swing.border.Border;
039: import javax.swing.border.EmptyBorder;
040: import javax.swing.plaf.basic.BasicComboBoxRenderer;
041:
042: /**
043: * The default button for combo boxes in the JGoodies Plastic Look&Feel.
044: * <p>
045: * It differs from <code>MetalComboBoxButton</code> in that the border
046: * is quite the same as for text fields: a compound border with an inner
047: * <code>MarginBorder</code>.
048: * <p>
049: * Also, we try to switch the <code>ListCellRenderer</code> to transparent,
050: * which works for most <code>JComponent</code> renderes including the
051: * <code>BasicComboBoxRenderer</code>.
052: *
053: * @author Karsten Lentzsch
054: * @version $Revision: 1.6 $
055: */
056: final class PlasticComboBoxButton extends JButton {
057:
058: private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0);
059: private static final Border EMPTY_BORDER = new EmptyBorder(
060: EMPTY_INSETS);
061: private static final int LEFT_MARGIN = 2;
062: private static final int RIGHT_MARGIN = 2;
063:
064: private final JList listBox;
065: private final CellRendererPane rendererPane;
066:
067: private JComboBox comboBox;
068: private Icon comboIcon;
069: private boolean iconOnly = false;
070: private boolean borderPaintsFocus;
071:
072: /**
073: * Constructs a <code>PlasticComboBoxButton</code>.
074: */
075: PlasticComboBoxButton(JComboBox comboBox, Icon comboIcon,
076: boolean iconOnly, CellRendererPane rendererPane,
077: JList listBox) {
078: super ("");
079: setModel(new DefaultButtonModel() {
080: public void setArmed(boolean armed) {
081: super .setArmed(isPressed() || armed);
082: }
083: });
084: this .comboBox = comboBox;
085: this .comboIcon = comboIcon;
086: this .iconOnly = iconOnly;
087: this .rendererPane = rendererPane;
088: this .listBox = listBox;
089: setEnabled(comboBox.isEnabled());
090: setFocusable(false);
091: setRequestFocusEnabled(comboBox.isEnabled());
092: setBorder(UIManager.getBorder("ComboBox.arrowButtonBorder"));
093: setMargin(new Insets(0, LEFT_MARGIN, 0, RIGHT_MARGIN));
094: borderPaintsFocus = UIManager
095: .getBoolean("ComboBox.borderPaintsFocus");
096: }
097:
098: public JComboBox getComboBox() {
099: return comboBox;
100: }
101:
102: public void setComboBox(JComboBox cb) {
103: comboBox = cb;
104: }
105:
106: public Icon getComboIcon() {
107: return comboIcon;
108: }
109:
110: public void setComboIcon(Icon i) {
111: comboIcon = i;
112: }
113:
114: public boolean isIconOnly() {
115: return iconOnly;
116: }
117:
118: public void setIconOnly(boolean b) {
119: iconOnly = b;
120: }
121:
122: public void setEnabled(boolean enabled) {
123: super .setEnabled(enabled);
124: // Set the background and foreground to the combobox colors.
125: if (enabled) {
126: setBackground(comboBox.getBackground());
127: setForeground(comboBox.getForeground());
128: } else {
129: setBackground(UIManager
130: .getColor("ComboBox.disabledBackground"));
131: setForeground(UIManager
132: .getColor("ComboBox.disabledForeground"));
133: }
134: }
135:
136: /**
137: * The combo's arrow button should be excluded from the focus traversal.
138: * Since Java 6 the arrow button is configured as being focusable
139: * in <code>BasicComboBoxUI#configureArrowButton</code>.
140: * Therefore it doesn't help to call <code>setFocusable(false)</code>
141: * in the constructor; instead we override this method.
142: */
143: public boolean isFocusTraversable() {
144: return false;
145: }
146:
147: /**
148: * Paints the component; honors the 3D settings and
149: * tries to switch the renderer component to transparent.
150: */
151: public void paintComponent(Graphics g) {
152: super .paintComponent(g);
153: boolean leftToRight = PlasticUtils.isLeftToRight(comboBox);
154: Insets insets = getInsets();
155: int width = getWidth() - (insets.left + insets.right);
156: int height = getHeight() - (insets.top + insets.bottom);
157: if (height <= 0 || width <= 0) {
158: return;
159: }
160: int left = insets.left;
161: int top = insets.top;
162: int right = left + (width - 1);
163:
164: int iconWidth = 0;
165: int iconLeft = (leftToRight) ? right : left;
166:
167: // Paint the icon
168: if (comboIcon != null) {
169: iconWidth = comboIcon.getIconWidth();
170: int iconHeight = comboIcon.getIconHeight();
171: int iconTop;
172:
173: if (iconOnly) {
174: iconLeft = (getWidth() - iconWidth) / 2;
175: iconTop = (getHeight() - iconHeight) / 2;
176: } else {
177: iconLeft = leftToRight ? left + (width - 1) - iconWidth
178: : left;
179: iconTop = (getHeight() - iconHeight) / 2;
180: }
181: comboIcon.paintIcon(this , g, iconLeft, iconTop);
182: }
183:
184: // Let the renderer paint
185: if (!iconOnly && comboBox != null) {
186: ListCellRenderer renderer = comboBox.getRenderer();
187: boolean renderPressed = getModel().isPressed();
188: Component c = renderer.getListCellRendererComponent(
189: listBox, comboBox.getSelectedItem(), -1,
190: renderPressed, false);
191:
192: int x = leftToRight ? left : left + iconWidth;
193: int y = top;
194: int w = getWidth() - left
195: - PlasticComboBoxUI.getEditableButtonWidth();
196: int h = height;
197:
198: Border oldBorder = null;
199: if ((c instanceof JComponent) && !isTableCellEditor()) {
200: JComponent component = (JComponent) c;
201: if (c instanceof BasicComboBoxRenderer.UIResource) {
202: oldBorder = component.getBorder();
203: component.setBorder(EMPTY_BORDER);
204: }
205: Insets rendererInsets = component.getInsets();
206: Insets editorInsets = UIManager
207: .getInsets("ComboBox.editorInsets");
208: int offsetTop = Math.max(0, editorInsets.top
209: - rendererInsets.top);
210: int offsetBottom = Math.max(0, editorInsets.bottom
211: - rendererInsets.bottom);
212: y += offsetTop;
213: h -= offsetTop + offsetBottom;
214: }
215: c.setFont(rendererPane.getFont());
216: configureColors(c);
217:
218: // Fix for 4238829: should lay out the JPanel.
219: boolean shouldValidate = c instanceof JPanel;
220:
221: if (!is3D() || !(c instanceof JComponent) || !c.isOpaque()) {
222: rendererPane.paintComponent(g, c, this , x, y, w, h,
223: shouldValidate);
224: } else {
225: // In case, we are in 3D mode _and_ have a non-transparent
226: // JComponent renderer, store the opaque state, set it
227: // to transparent, paint, then restore.
228: JComponent component = (JComponent) c;
229: boolean oldOpaque = component.isOpaque();
230: component.setOpaque(false);
231: rendererPane.paintComponent(g, c, this , x, y, w, h,
232: shouldValidate);
233: component.setOpaque(oldOpaque);
234: }
235: if (oldBorder != null) {
236: ((JComponent) c).setBorder(oldBorder);
237: }
238: }
239:
240: if (comboIcon != null) {
241: // Paint the focus
242: boolean hasFocus = comboBox.hasFocus();
243: if (!borderPaintsFocus && hasFocus) {
244: g.setColor(PlasticLookAndFeel.getFocusColor());
245: g.drawRect(2, 2, getWidth() - 6, getHeight() - 6);
246: }
247: }
248:
249: }
250:
251: private void configureColors(Component c) {
252: if (model.isArmed() && model.isPressed()) {
253: if (isOpaque()) {
254: c.setBackground(UIManager.getColor("Button.select"));
255: }
256: c.setForeground(comboBox.getForeground());
257: } else if (!comboBox.isEnabled()) {
258: if (isOpaque()) {
259: c.setBackground(UIManager
260: .getColor("ComboBox.disabledBackground"));
261: }
262: c.setForeground(UIManager
263: .getColor("ComboBox.disabledForeground"));
264: } else {
265: c.setForeground(comboBox.getForeground());
266: c.setBackground(comboBox.getBackground());
267: }
268: }
269:
270: // Helper Code ************************************************************
271:
272: /**
273: * Checks and answers if we should paint a pseudo 3D effect.
274: */
275: private boolean is3D() {
276: if (PlasticUtils.force3D(comboBox))
277: return true;
278: if (PlasticUtils.forceFlat(comboBox))
279: return false;
280: return PlasticUtils.is3D("ComboBox.");
281: }
282:
283: /**
284: * Checks and answers if this UI's combo has a client property
285: * that indicates that the combo is used as a table cell editor.
286: *
287: * @return <code>true</code> if the table cell editor client property
288: * is set to <code>Boolean.TRUE</code>, <code>false</code> otherwise
289: */
290: private boolean isTableCellEditor() {
291: return Boolean.TRUE.equals(comboBox
292: .getClientProperty(PlasticComboBoxUI.CELL_EDITOR_KEY));
293: }
294:
295: }
|