blob: 3a42e4f3dbe8ba1ef2bf8dbac1db8e7b13fad370 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.java.swing.plaf.windows;
27
28import java.beans.PropertyChangeListener;
29import java.beans.PropertyChangeEvent;
30import javax.swing.plaf.basic.*;
31import javax.swing.plaf.*;
32import javax.swing.border.*;
33import javax.swing.*;
34import java.awt.event.*;
35import java.awt.*;
36
37import static com.sun.java.swing.plaf.windows.TMSchema.Part;
38import static com.sun.java.swing.plaf.windows.TMSchema.State;
39import static com.sun.java.swing.plaf.windows.XPStyle.Skin;
40import sun.swing.DefaultLookup;
41import sun.swing.StringUIClientPropertyKey;
42
43
44/**
45 * Windows combo box.
46 * <p>
47 * <strong>Warning:</strong>
48 * Serialized objects of this class will not be compatible with
49 * future Swing releases. The current serialization support is appropriate
50 * for short term storage or RMI between applications running the same
51 * version of Swing. A future release of Swing will provide support for
52 * long term persistence.
53 *
54 * @author Tom Santos
55 * @author Igor Kushnirskiy
56 */
57
58public class WindowsComboBoxUI extends BasicComboBoxUI {
59
60 private static final MouseListener rolloverListener =
61 new MouseAdapter() {
62 private void handleRollover(MouseEvent e, boolean isRollover) {
63 JComboBox comboBox = getComboBox(e);
64 WindowsComboBoxUI comboBoxUI = getWindowsComboBoxUI(e);
65 if (comboBox == null || comboBoxUI == null) {
66 return;
67 }
68 if (! comboBox.isEditable()) {
69 //mouse over editable ComboBox does not switch rollover
70 //for the arrow button
71 ButtonModel m = null;
72 if (comboBoxUI.arrowButton != null) {
73 m = comboBoxUI.arrowButton.getModel();
74 }
75 if (m != null ) {
76 m.setRollover(isRollover);
77 }
78 }
79 comboBoxUI.isRollover = isRollover;
80 comboBox.repaint();
81 }
82
83 public void mouseEntered(MouseEvent e) {
84 handleRollover(e, true);
85 }
86
87 public void mouseExited(MouseEvent e) {
88 handleRollover(e, false);
89 }
90
91 private JComboBox getComboBox(MouseEvent event) {
92 Object source = event.getSource();
93 JComboBox rv = null;
94 if (source instanceof JComboBox) {
95 rv = (JComboBox) source;
96 } else if (source instanceof XPComboBoxButton) {
97 rv = ((XPComboBoxButton) source)
98 .getWindowsComboBoxUI().comboBox;
99 }
100 return rv;
101 }
102
103 private WindowsComboBoxUI getWindowsComboBoxUI(MouseEvent event) {
104 JComboBox comboBox = getComboBox(event);
105 WindowsComboBoxUI rv = null;
106 if (comboBox != null
107 && comboBox.getUI() instanceof WindowsComboBoxUI) {
108 rv = (WindowsComboBoxUI) comboBox.getUI();
109 }
110 return rv;
111 }
112
113 };
114 private boolean isRollover = false;
115
116 private static final PropertyChangeListener componentOrientationListener =
117 new PropertyChangeListener() {
118 public void propertyChange(PropertyChangeEvent e) {
119 String propertyName = e.getPropertyName();
120 Object source = null;
121 if ("componentOrientation" == propertyName
122 && (source = e.getSource()) instanceof JComboBox
123 && ((JComboBox) source).getUI() instanceof
124 WindowsComboBoxUI) {
125 JComboBox comboBox = (JComboBox) source;
126 WindowsComboBoxUI comboBoxUI = (WindowsComboBoxUI) comboBox.getUI();
127 if (comboBoxUI.arrowButton instanceof XPComboBoxButton) {
128 ((XPComboBoxButton) comboBoxUI.arrowButton).setPart(
129 (comboBox.getComponentOrientation() ==
130 ComponentOrientation.RIGHT_TO_LEFT)
131 ? Part.CP_DROPDOWNBUTTONLEFT
132 : Part.CP_DROPDOWNBUTTONRIGHT);
133 }
134 }
135 }
136 };
137
138 public static ComponentUI createUI(JComponent c) {
139 return new WindowsComboBoxUI();
140 }
141
142 public void installUI( JComponent c ) {
143 super.installUI( c );
144 isRollover = false;
145 comboBox.setRequestFocusEnabled( true );
146 if (XPStyle.getXP() != null && arrowButton != null) {
147 //we can not do it in installListeners because arrowButton
148 //is initialized after installListeners is invoked
149 comboBox.addMouseListener(rolloverListener);
150 arrowButton.addMouseListener(rolloverListener);
151 }
152 }
153
154 public void uninstallUI(JComponent c ) {
155 comboBox.removeMouseListener(rolloverListener);
156 if(arrowButton != null) {
157 arrowButton.removeMouseListener(rolloverListener);
158 }
159 super.uninstallUI( c );
160 }
161
162 /**
163 * {@inheritDoc}
164 * @since 1.6
165 */
166 @Override
167 protected void installListeners() {
168 super.installListeners();
169 XPStyle xp = XPStyle.getXP();
170 //button glyph for LTR and RTL combobox might differ
171 if (xp != null
172 && xp.isSkinDefined(comboBox, Part.CP_DROPDOWNBUTTONRIGHT)) {
173 comboBox.addPropertyChangeListener("componentOrientation",
174 componentOrientationListener);
175 }
176 }
177
178 /**
179 * {@inheritDoc}
180 * @since 1.6
181 */
182 @Override
183 protected void uninstallListeners() {
184 super.uninstallListeners();
185 comboBox.removePropertyChangeListener("componentOrientation",
186 componentOrientationListener);
187 }
188
189 /**
190 * {@inheritDoc}
191 * @since 1.6
192 */
193 protected void configureEditor() {
194 super.configureEditor();
195 if (XPStyle.getXP() != null) {
196 editor.addMouseListener(rolloverListener);
197 }
198 }
199
200 /**
201 * {@inheritDoc}
202 * @since 1.6
203 */
204 protected void unconfigureEditor() {
205 super.unconfigureEditor();
206 editor.removeMouseListener(rolloverListener);
207 }
208
209 /**
210 * {@inheritDoc}
211 * @since 1.6
212 */
213 public void paint(Graphics g, JComponent c) {
214 if (XPStyle.getXP() != null) {
215 paintXPComboBoxBackground(g, c);
216 }
217 super.paint(g, c);
218 }
219
220 State getXPComboBoxState(JComponent c) {
221 State state = State.NORMAL;
222 if (!c.isEnabled()) {
223 state = State.DISABLED;
224 } else if (isPopupVisible(comboBox)) {
225 state = State.PRESSED;
226 } else if (isRollover) {
227 state = State.HOT;
228 }
229 return state;
230 }
231
232 private void paintXPComboBoxBackground(Graphics g, JComponent c) {
233 XPStyle xp = XPStyle.getXP();
234 State state = getXPComboBoxState(c);
235 Skin skin = null;
236 if (! comboBox.isEditable()
237 && xp.isSkinDefined(c, Part.CP_READONLY)) {
238 skin = xp.getSkin(c, Part.CP_READONLY);
239 }
240 if (skin == null) {
241 skin = xp.getSkin(c, Part.CP_COMBOBOX);
242 }
243 skin.paintSkin(g, 0, 0, c.getWidth(), c.getHeight(), state);
244 }
245
246 /**
247 * If necessary paints the currently selected item.
248 *
249 * @param g Graphics to paint to
250 * @param bounds Region to paint current value to
251 * @param hasFocus whether or not the JComboBox has focus
252 * @throws NullPointerException if any of the arguments are null.
253 * @since 1.5
254 */
255 public void paintCurrentValue(Graphics g, Rectangle bounds,
256 boolean hasFocus) {
257 XPStyle xp = XPStyle.getXP();
258 if ( xp != null) {
259 bounds.x += 2;
260 bounds.y += 2;
261 bounds.width -= 4;
262 bounds.height -= 4;
263 } else {
264 bounds.x += 1;
265 bounds.y += 1;
266 bounds.width -= 2;
267 bounds.height -= 2;
268 }
269 if (! comboBox.isEditable()
270 && xp != null
271 && xp.isSkinDefined(comboBox, Part.CP_READONLY)) {
272 // On vista for READNLY ComboBox
273 // color for currentValue is the same as for any other item
274
275 // mostly copied from javax.swing.plaf.basic.BasicComboBoxUI.paintCurrentValue
276 ListCellRenderer renderer = comboBox.getRenderer();
277 Component c;
278 if ( hasFocus && !isPopupVisible(comboBox) ) {
279 c = renderer.getListCellRendererComponent(
280 listBox,
281 comboBox.getSelectedItem(),
282 -1,
283 true,
284 false );
285 } else {
286 c = renderer.getListCellRendererComponent(
287 listBox,
288 comboBox.getSelectedItem(),
289 -1,
290 false,
291 false );
292 }
293 c.setFont(comboBox.getFont());
294 if ( comboBox.isEnabled() ) {
295 c.setForeground(comboBox.getForeground());
296 c.setBackground(comboBox.getBackground());
297 } else {
298 c.setForeground(DefaultLookup.getColor(
299 comboBox, this, "ComboBox.disabledForeground", null));
300 c.setBackground(DefaultLookup.getColor(
301 comboBox, this, "ComboBox.disabledBackground", null));
302 }
303 boolean shouldValidate = false;
304 if (c instanceof JPanel) {
305 shouldValidate = true;
306 }
307 currentValuePane.paintComponent(g, c, comboBox, bounds.x, bounds.y,
308 bounds.width, bounds.height, shouldValidate);
309
310 } else {
311 super.paintCurrentValue(g, bounds, hasFocus);
312 }
313 }
314
315 /**
316 * {@inheritDoc}
317 * @since 1.6
318 */
319 public void paintCurrentValueBackground(Graphics g, Rectangle bounds,
320 boolean hasFocus) {
321 if (XPStyle.getXP() == null) {
322 super.paintCurrentValueBackground(g, bounds, hasFocus);
323 }
324 }
325
326 public Dimension getMinimumSize( JComponent c ) {
327 Dimension d = super.getMinimumSize(c);
328 if (XPStyle.getXP() != null) {
329 d.width += 5;
330 } else {
331 d.width += 4;
332 }
333 d.height += 2;
334 return d;
335 }
336
337 /**
338 * Creates a layout manager for managing the components which make up the
339 * combo box.
340 *
341 * @return an instance of a layout manager
342 */
343 protected LayoutManager createLayoutManager() {
344 return new BasicComboBoxUI.ComboBoxLayoutManager() {
345 public void layoutContainer(Container parent) {
346 super.layoutContainer(parent);
347
348 if (XPStyle.getXP() != null && arrowButton != null) {
349 Dimension d = parent.getSize();
350 Insets insets = getInsets();
351 int buttonWidth = arrowButton.getPreferredSize().width;
352 arrowButton.setBounds(WindowsGraphicsUtils.isLeftToRight((JComboBox)parent)
353 ? (d.width - insets.right - buttonWidth)
354 : insets.left,
355 insets.top,
356 buttonWidth, d.height - insets.top - insets.bottom);
357 }
358 }
359 };
360 }
361
362 protected void installKeyboardActions() {
363 super.installKeyboardActions();
364 }
365
366 protected ComboPopup createPopup() {
367 return super.createPopup();
368 }
369
370 /**
371 * Creates the default editor that will be used in editable combo boxes.
372 * A default editor will be used only if an editor has not been
373 * explicitly set with <code>setEditor</code>.
374 *
375 * @return a <code>ComboBoxEditor</code> used for the combo box
376 * @see javax.swing.JComboBox#setEditor
377 */
378 protected ComboBoxEditor createEditor() {
379 return new WindowsComboBoxEditor();
380 }
381
382 /**
383 * {@inheritDoc}
384 * @since 1.6
385 */
386 @Override
387 protected ListCellRenderer createRenderer() {
388 XPStyle xp = XPStyle.getXP();
389 if (xp != null && xp.isSkinDefined(comboBox, Part.CP_READONLY)) {
390 return new WindowsComboBoxRenderer();
391 } else {
392 return super.createRenderer();
393 }
394 }
395
396 /**
397 * Creates an button which will be used as the control to show or hide
398 * the popup portion of the combo box.
399 *
400 * @return a button which represents the popup control
401 */
402 protected JButton createArrowButton() {
403 if (XPStyle.getXP() != null) {
404 return new XPComboBoxButton();
405 } else {
406 return super.createArrowButton();
407 }
408 }
409
410 private class XPComboBoxButton extends XPStyle.GlyphButton {
411 public XPComboBoxButton() {
412 super(null,
413 (! XPStyle.getXP().isSkinDefined(comboBox, Part.CP_DROPDOWNBUTTONRIGHT))
414 ? Part.CP_DROPDOWNBUTTON
415 : (comboBox.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
416 ? Part.CP_DROPDOWNBUTTONLEFT
417 : Part.CP_DROPDOWNBUTTONRIGHT
418 );
419 setRequestFocusEnabled(false);
420 }
421
422 @Override
423 protected State getState() {
424 State rv;
425 rv = super.getState();
426 if (rv != State.DISABLED
427 && ! comboBox.isEditable()
428 && XPStyle.getXP().isSkinDefined(comboBox,
429 Part.CP_DROPDOWNBUTTONRIGHT)) {
430 /*
431 * for non editable ComboBoxes Vista seems to have the
432 * same glyph for all non DISABLED states
433 */
434 rv = State.NORMAL;
435 }
436 return rv;
437 }
438
439 public Dimension getPreferredSize() {
440 return new Dimension(17, 21);
441 }
442
443 void setPart(Part part) {
444 setPart(comboBox, part);
445 }
446
447 WindowsComboBoxUI getWindowsComboBoxUI() {
448 return WindowsComboBoxUI.this;
449 }
450 }
451
452
453 /**
454 * Subclassed to add Windows specific Key Bindings.
455 * This class is now obsolete and doesn't do anything.
456 * Only included for backwards API compatibility.
457 * Do not call or override.
458 *
459 * @deprecated As of Java 2 platform v1.4.
460 */
461 @Deprecated
462 protected class WindowsComboPopup extends BasicComboPopup {
463
464 public WindowsComboPopup( JComboBox cBox ) {
465 super( cBox );
466 }
467
468 protected KeyListener createKeyListener() {
469 return new InvocationKeyHandler();
470 }
471
472 protected class InvocationKeyHandler extends BasicComboPopup.InvocationKeyHandler {
473 protected InvocationKeyHandler() {
474 WindowsComboPopup.this.super();
475 }
476 }
477 }
478
479
480 /**
481 * Subclassed to highlight selected item in an editable combo box.
482 */
483 public static class WindowsComboBoxEditor
484 extends BasicComboBoxEditor.UIResource {
485
486 /**
487 * {@inheritDoc}
488 * @since 1.6
489 */
490 protected JTextField createEditorComponent() {
491 JTextField editor = super.createEditorComponent();
492 Border border = (Border)UIManager.get("ComboBox.editorBorder");
493 if (border != null) {
494 editor.setBorder(border);
495 }
496 editor.setOpaque(false);
497 return editor;
498 }
499
500 public void setItem(Object item) {
501 super.setItem(item);
502 if (editor.hasFocus()) {
503 editor.selectAll();
504 }
505 }
506 }
507
508 /**
509 * Subclassed to set opacity {@code false} on the renderer
510 * and to show border for focused cells.
511 */
512 private static class WindowsComboBoxRenderer
513 extends BasicComboBoxRenderer.UIResource {
514 private static final Object BORDER_KEY
515 = new StringUIClientPropertyKey("BORDER_KEY");
516 private static final Border NULL_BORDER = new EmptyBorder(0, 0, 0, 0);
517 /**
518 * {@inheritDoc}
519 */
520 @Override
521 public Component getListCellRendererComponent(
522 JList list,
523 Object value,
524 int index,
525 boolean isSelected,
526 boolean cellHasFocus) {
527 Component rv =
528 super.getListCellRendererComponent(list, value, index,
529 isSelected, cellHasFocus);
530 if (rv instanceof JComponent) {
531 JComponent component = (JComponent) rv;
532 if (index == -1 && isSelected) {
533 Border border = component.getBorder();
534 Border dashedBorder =
535 new WindowsBorders.DashedBorder(list.getForeground());
536 component.setBorder(dashedBorder);
537 //store current border in client property if needed
538 if (component.getClientProperty(BORDER_KEY) == null) {
539 component.putClientProperty(BORDER_KEY,
540 (border == null) ? NULL_BORDER : border);
541 }
542 } else {
543 if (component.getBorder() instanceof
544 WindowsBorders.DashedBorder) {
545 Object storedBorder = component.getClientProperty(BORDER_KEY);
546 if (storedBorder instanceof Border) {
547 component.setBorder(
548 (storedBorder == NULL_BORDER) ? null
549 : (Border) storedBorder);
550 }
551 component.putClientProperty(BORDER_KEY, null);
552 }
553 }
554 if (index == -1) {
555 component.setOpaque(false);
556 component.setForeground(list.getForeground());
557 } else {
558 component.setOpaque(true);
559 }
560 }
561 return rv;
562 }
563
564 }
565}