1 /*
2  * Copyright (c) 1998, 2014, Oracle and/or its affiliates. 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.java.swing.plaf.windows;
27 
28 import javax.swing.*;
29 import javax.swing.plaf.ButtonUI;
30 import javax.swing.plaf.UIResource;
31 
32 import java.awt.*;
33 import java.io.Serializable;
34 
35 import static com.sun.java.swing.plaf.windows.TMSchema.*;
36 import static com.sun.java.swing.plaf.windows.XPStyle.Skin;
37 
38 import sun.swing.MenuItemCheckIconFactory;
39 import sun.swing.SwingUtilities2;
40 
41 /**
42  * Factory object that can vend Icons appropriate for the Windows {@literal L & F}.
43  * <p>
44  * <strong>Warning:</strong>
45  * Serialized objects of this class will not be compatible with
46  * future Swing releases.  The current serialization support is appropriate
47  * for short term storage or RMI between applications running the same
48  * version of Swing.  A future release of Swing will provide support for
49  * long term persistence.
50  *
51  * @author David Kloba
52  * @author Georges Saab
53  * @author Rich Schiavi
54  */
55 @SuppressWarnings("serial") // Same-version serialization only
56 public class WindowsIconFactory implements Serializable
57 {
58     private static Icon frame_closeIcon;
59     private static Icon frame_iconifyIcon;
60     private static Icon frame_maxIcon;
61     private static Icon frame_minIcon;
62     private static Icon frame_resizeIcon;
63     private static Icon checkBoxIcon;
64     private static Icon radioButtonIcon;
65     private static Icon checkBoxMenuItemIcon;
66     private static Icon radioButtonMenuItemIcon;
67     private static Icon menuItemCheckIcon;
68     private static Icon menuItemArrowIcon;
69     private static Icon menuArrowIcon;
70     private static VistaMenuItemCheckIconFactory menuItemCheckIconFactory;
71 
getMenuItemCheckIcon()72     public static Icon getMenuItemCheckIcon() {
73         if (menuItemCheckIcon == null) {
74             menuItemCheckIcon = new MenuItemCheckIcon();
75         }
76         return menuItemCheckIcon;
77     }
78 
getMenuItemArrowIcon()79     public static Icon getMenuItemArrowIcon() {
80         if (menuItemArrowIcon == null) {
81             menuItemArrowIcon = new MenuItemArrowIcon();
82         }
83         return menuItemArrowIcon;
84     }
85 
getMenuArrowIcon()86     public static Icon getMenuArrowIcon() {
87         if (menuArrowIcon == null) {
88             menuArrowIcon = new MenuArrowIcon();
89         }
90         return menuArrowIcon;
91     }
92 
getCheckBoxIcon()93     public static Icon getCheckBoxIcon() {
94         if (checkBoxIcon == null) {
95             checkBoxIcon = new CheckBoxIcon();
96         }
97         return checkBoxIcon;
98     }
99 
getRadioButtonIcon()100     public static Icon getRadioButtonIcon() {
101         if (radioButtonIcon == null) {
102             radioButtonIcon = new RadioButtonIcon();
103         }
104         return radioButtonIcon;
105     }
106 
getCheckBoxMenuItemIcon()107     public static Icon getCheckBoxMenuItemIcon() {
108         if (checkBoxMenuItemIcon == null) {
109             checkBoxMenuItemIcon = new CheckBoxMenuItemIcon();
110         }
111         return checkBoxMenuItemIcon;
112     }
113 
getRadioButtonMenuItemIcon()114     public static Icon getRadioButtonMenuItemIcon() {
115         if (radioButtonMenuItemIcon == null) {
116             radioButtonMenuItemIcon = new RadioButtonMenuItemIcon();
117         }
118         return radioButtonMenuItemIcon;
119     }
120 
121     static
getMenuItemCheckIconFactory()122     synchronized VistaMenuItemCheckIconFactory getMenuItemCheckIconFactory() {
123         if (menuItemCheckIconFactory == null) {
124             menuItemCheckIconFactory =
125                 new VistaMenuItemCheckIconFactory();
126         }
127         return menuItemCheckIconFactory;
128     }
129 
createFrameCloseIcon()130     public static Icon createFrameCloseIcon() {
131         if (frame_closeIcon == null) {
132             frame_closeIcon = new FrameButtonIcon(Part.WP_CLOSEBUTTON);
133         }
134         return frame_closeIcon;
135     }
136 
createFrameIconifyIcon()137     public static Icon createFrameIconifyIcon() {
138         if (frame_iconifyIcon == null) {
139             frame_iconifyIcon = new FrameButtonIcon(Part.WP_MINBUTTON);
140         }
141         return frame_iconifyIcon;
142     }
143 
createFrameMaximizeIcon()144     public static Icon createFrameMaximizeIcon() {
145         if (frame_maxIcon == null) {
146             frame_maxIcon = new FrameButtonIcon(Part.WP_MAXBUTTON);
147         }
148         return frame_maxIcon;
149     }
150 
createFrameMinimizeIcon()151     public static Icon createFrameMinimizeIcon() {
152         if (frame_minIcon == null) {
153             frame_minIcon = new FrameButtonIcon(Part.WP_RESTOREBUTTON);
154         }
155         return frame_minIcon;
156     }
157 
createFrameResizeIcon()158     public static Icon createFrameResizeIcon() {
159         if(frame_resizeIcon == null)
160             frame_resizeIcon = new ResizeIcon();
161         return frame_resizeIcon;
162     }
163 
164 
165     @SuppressWarnings("serial") // Same-version serialization only
166     private static class FrameButtonIcon implements Icon, Serializable {
167         private Part part;
168 
FrameButtonIcon(Part part)169         private FrameButtonIcon(Part part) {
170             this.part = part;
171         }
172 
paintIcon(Component c, Graphics g, int x0, int y0)173         public void paintIcon(Component c, Graphics g, int x0, int y0) {
174             int width = getIconWidth();
175             int height = getIconHeight();
176 
177             XPStyle xp = XPStyle.getXP();
178             if (xp != null) {
179                 Skin skin = xp.getSkin(c, part);
180                 AbstractButton b = (AbstractButton)c;
181                 ButtonModel model = b.getModel();
182 
183                 // Find out if frame is inactive
184                 JInternalFrame jif = (JInternalFrame)SwingUtilities.
185                                         getAncestorOfClass(JInternalFrame.class, b);
186                 boolean jifSelected = (jif != null && jif.isSelected());
187 
188                 State state;
189                 if (jifSelected) {
190                     if (!model.isEnabled()) {
191                         state = State.DISABLED;
192                     } else if (model.isArmed() && model.isPressed()) {
193                         state = State.PUSHED;
194                     } else if (model.isRollover()) {
195                         state = State.HOT;
196                     } else {
197                         state = State.NORMAL;
198                     }
199                 } else {
200                     if (!model.isEnabled()) {
201                         state = State.INACTIVEDISABLED;
202                     } else if (model.isArmed() && model.isPressed()) {
203                         state = State.INACTIVEPUSHED;
204                     } else if (model.isRollover()) {
205                         state = State.INACTIVEHOT;
206                     } else {
207                         state = State.INACTIVENORMAL;
208                     }
209                 }
210                 skin.paintSkin(g, 0, 0, width, height, state);
211             } else {
212                 g.setColor(Color.black);
213                 int x = width / 12 + 2;
214                 int y = height / 5;
215                 int h = height - y * 2 - 1;
216                 int w = width * 3/4 -3;
217                 int thickness2 = Math.max(height / 8, 2);
218                 int thickness  = Math.max(width / 15, 1);
219                 if (part == Part.WP_CLOSEBUTTON) {
220                     int lineWidth;
221                     if      (width > 47) lineWidth = 6;
222                     else if (width > 37) lineWidth = 5;
223                     else if (width > 26) lineWidth = 4;
224                     else if (width > 16) lineWidth = 3;
225                     else if (width > 12) lineWidth = 2;
226                     else                 lineWidth = 1;
227                     y = height / 12 + 2;
228                     if (lineWidth == 1) {
229                         if (w % 2 == 1) { x++; w++; }
230                         g.drawLine(x,     y, x+w-2, y+w-2);
231                         g.drawLine(x+w-2, y, x,     y+w-2);
232                     } else if (lineWidth == 2) {
233                         if (w > 6) { x++; w--; }
234                         g.drawLine(x,     y, x+w-2, y+w-2);
235                         g.drawLine(x+w-2, y, x,     y+w-2);
236                         g.drawLine(x+1,   y, x+w-1, y+w-2);
237                         g.drawLine(x+w-1, y, x+1,   y+w-2);
238                     } else {
239                         x += 2; y++; w -= 2;
240                         g.drawLine(x,     y,   x+w-1, y+w-1);
241                         g.drawLine(x+w-1, y,   x,     y+w-1);
242                         g.drawLine(x+1,   y,   x+w-1, y+w-2);
243                         g.drawLine(x+w-2, y,   x,     y+w-2);
244                         g.drawLine(x,     y+1, x+w-2, y+w-1);
245                         g.drawLine(x+w-1, y+1, x+1,   y+w-1);
246                         for (int i = 4; i <= lineWidth; i++) {
247                             g.drawLine(x+i-2,   y,     x+w-1,   y+w-i+1);
248                             g.drawLine(x,       y+i-2, x+w-i+1, y+w-1);
249                             g.drawLine(x+w-i+1, y,     x,       y+w-i+1);
250                             g.drawLine(x+w-1,   y+i-2, x+i-2,   y+w-1);
251                         }
252                     }
253                 } else if (part == Part.WP_MINBUTTON) {
254                     g.fillRect(x, y+h-thickness2, w-w/3, thickness2);
255                 } else if (part == Part.WP_MAXBUTTON) {
256                     g.fillRect(x, y, w, thickness2);
257                     g.fillRect(x, y, thickness, h);
258                     g.fillRect(x+w-thickness, y, thickness, h);
259                     g.fillRect(x, y+h-thickness, w, thickness);
260                 } else if (part == Part.WP_RESTOREBUTTON) {
261                     g.fillRect(x+w/3, y, w-w/3, thickness2);
262                     g.fillRect(x+w/3, y, thickness, h/3);
263                     g.fillRect(x+w-thickness, y, thickness, h-h/3);
264                     g.fillRect(x+w-w/3, y+h-h/3-thickness, w/3, thickness);
265 
266                     g.fillRect(x, y+h/3, w-w/3, thickness2);
267                     g.fillRect(x, y+h/3, thickness, h-h/3);
268                     g.fillRect(x+w-w/3-thickness, y+h/3, thickness, h-h/3);
269                     g.fillRect(x, y+h-thickness, w-w/3, thickness);
270                 }
271             }
272         }
273 
getIconWidth()274         public int getIconWidth() {
275             int width;
276             if (XPStyle.getXP() != null) {
277                 // Fix for XP bug where sometimes these sizes aren't updated properly
278                 // Assume for now that height is correct and derive width using the
279                 // ratio from the uxtheme part
280                 width = UIManager.getInt("InternalFrame.titleButtonHeight") -2;
281                 Dimension d = XPStyle.getPartSize(Part.WP_CLOSEBUTTON, State.NORMAL);
282                 if (d != null && d.width != 0 && d.height != 0) {
283                     width = (int) ((float) width * d.width / d.height);
284                 }
285             } else {
286                 width = UIManager.getInt("InternalFrame.titleButtonWidth") -2;
287             }
288             if (XPStyle.getXP() != null) {
289                 width -= 2;
290             }
291             return width;
292         }
293 
getIconHeight()294         public int getIconHeight() {
295             int height = UIManager.getInt("InternalFrame.titleButtonHeight")-4;
296             return height;
297         }
298     }
299 
300 
301 
302         @SuppressWarnings("serial") // Same-version serialization only
303         private static class ResizeIcon implements Icon, Serializable {
paintIcon(Component c, Graphics g, int x, int y)304             public void paintIcon(Component c, Graphics g, int x, int y) {
305                 g.setColor(UIManager.getColor("InternalFrame.resizeIconHighlight"));
306                 g.drawLine(0, 11, 11, 0);
307                 g.drawLine(4, 11, 11, 4);
308                 g.drawLine(8, 11, 11, 8);
309 
310                 g.setColor(UIManager.getColor("InternalFrame.resizeIconShadow"));
311                 g.drawLine(1, 11, 11, 1);
312                 g.drawLine(2, 11, 11, 2);
313                 g.drawLine(5, 11, 11, 5);
314                 g.drawLine(6, 11, 11, 6);
315                 g.drawLine(9, 11, 11, 9);
316                 g.drawLine(10, 11, 11, 10);
317             }
getIconWidth()318             public int getIconWidth() { return 13; }
getIconHeight()319             public int getIconHeight() { return 13; }
320         };
321 
322     @SuppressWarnings("serial") // Same-version serialization only
323     private static class CheckBoxIcon implements Icon, Serializable
324     {
325         static final int csize = 13;
paintIcon(Component c, Graphics g, int x, int y)326         public void paintIcon(Component c, Graphics g, int x, int y) {
327             JCheckBox cb = (JCheckBox) c;
328             ButtonModel model = cb.getModel();
329             XPStyle xp = XPStyle.getXP();
330 
331             if (xp != null) {
332                 State state;
333                 if (model.isSelected()) {
334                     state = State.CHECKEDNORMAL;
335                     if (!model.isEnabled()) {
336                         state = State.CHECKEDDISABLED;
337                     } else if (model.isPressed() && model.isArmed()) {
338                         state = State.CHECKEDPRESSED;
339                     } else if (model.isRollover()) {
340                         state = State.CHECKEDHOT;
341                     }
342                 } else {
343                     state = State.UNCHECKEDNORMAL;
344                     if (!model.isEnabled()) {
345                         state = State.UNCHECKEDDISABLED;
346                     } else if (model.isPressed() && model.isArmed()) {
347                         state = State.UNCHECKEDPRESSED;
348                     } else if (model.isRollover()) {
349                         state = State.UNCHECKEDHOT;
350                     }
351                 }
352                 Part part = Part.BP_CHECKBOX;
353                 xp.getSkin(c, part).paintSkin(g, x, y, state);
354             } else {
355                 // outer bevel
356                 if(!cb.isBorderPaintedFlat()) {
357                     // Outer top/left
358                     g.setColor(UIManager.getColor("CheckBox.shadow"));
359                     g.drawLine(x, y, x+11, y);
360                     g.drawLine(x, y+1, x, y+11);
361 
362                     // Outer bottom/right
363                     g.setColor(UIManager.getColor("CheckBox.highlight"));
364                     g.drawLine(x+12, y, x+12, y+12);
365                     g.drawLine(x, y+12, x+11, y+12);
366 
367                     // Inner top.left
368                     g.setColor(UIManager.getColor("CheckBox.darkShadow"));
369                     g.drawLine(x+1, y+1, x+10, y+1);
370                     g.drawLine(x+1, y+2, x+1, y+10);
371 
372                     // Inner bottom/right
373                     g.setColor(UIManager.getColor("CheckBox.light"));
374                     g.drawLine(x+1, y+11, x+11, y+11);
375                     g.drawLine(x+11, y+1, x+11, y+10);
376 
377                     // inside box
378                     if((model.isPressed() && model.isArmed()) || !model.isEnabled()) {
379                         g.setColor(UIManager.getColor("CheckBox.background"));
380                     } else {
381                         g.setColor(UIManager.getColor("CheckBox.interiorBackground"));
382                     }
383                     g.fillRect(x+2, y+2, csize-4, csize-4);
384                 } else {
385                     g.setColor(UIManager.getColor("CheckBox.shadow"));
386                     g.drawRect(x+1, y+1, csize-3, csize-3);
387 
388                     if((model.isPressed() && model.isArmed()) || !model.isEnabled()) {
389                         g.setColor(UIManager.getColor("CheckBox.background"));
390                     } else {
391                         g.setColor(UIManager.getColor("CheckBox.interiorBackground"));
392                     }
393                     g.fillRect(x+2, y+2, csize-4, csize-4);
394                 }
395 
396                 if(model.isEnabled()) {
397                     g.setColor(UIManager.getColor("CheckBox.foreground"));
398                 } else {
399                     g.setColor(UIManager.getColor("CheckBox.shadow"));
400                 }
401 
402                 // paint check
403                 if (model.isSelected()) {
404                     if (SwingUtilities2.isScaledGraphics(g)) {
405                         int[] xPoints = {3, 5, 9, 9, 5, 3};
406                         int[] yPoints = {5, 7, 3, 5, 9, 7};
407                         g.translate(x, y);
408                         g.fillPolygon(xPoints, yPoints, 6);
409                         g.drawPolygon(xPoints, yPoints, 6);
410                         g.translate(-x, -y);
411                     } else {
412                         g.drawLine(x + 9, y + 3, x + 9, y + 3);
413                         g.drawLine(x + 8, y + 4, x + 9, y + 4);
414                         g.drawLine(x + 7, y + 5, x + 9, y + 5);
415                         g.drawLine(x + 6, y + 6, x + 8, y + 6);
416                         g.drawLine(x + 3, y + 7, x + 7, y + 7);
417                         g.drawLine(x + 4, y + 8, x + 6, y + 8);
418                         g.drawLine(x + 5, y + 9, x + 5, y + 9);
419                         g.drawLine(x + 3, y + 5, x + 3, y + 5);
420                         g.drawLine(x + 3, y + 6, x + 4, y + 6);
421                     }
422                 }
423             }
424         }
425 
getIconWidth()426         public int getIconWidth() {
427             XPStyle xp = XPStyle.getXP();
428             if (xp != null) {
429                 return xp.getSkin(null, Part.BP_CHECKBOX).getWidth();
430             } else {
431                 return csize;
432             }
433         }
434 
getIconHeight()435         public int getIconHeight() {
436             XPStyle xp = XPStyle.getXP();
437             if (xp != null) {
438                 return xp.getSkin(null, Part.BP_CHECKBOX).getHeight();
439             } else {
440                 return csize;
441             }
442         }
443     }
444 
445     @SuppressWarnings("serial") // Same-version serialization only
446     private static class RadioButtonIcon implements Icon, UIResource, Serializable
447     {
paintIcon(Component c, Graphics g, int x, int y)448         public void paintIcon(Component c, Graphics g, int x, int y) {
449             AbstractButton b = (AbstractButton) c;
450             ButtonModel model = b.getModel();
451             XPStyle xp = XPStyle.getXP();
452 
453             if (xp != null) {
454                 Part part = Part.BP_RADIOBUTTON;
455                 Skin skin = xp.getSkin(b, part);
456                 State state;
457                 int index = 0;
458                 if (model.isSelected()) {
459                     state = State.CHECKEDNORMAL;
460                     if (!model.isEnabled()) {
461                         state = State.CHECKEDDISABLED;
462                     } else if (model.isPressed() && model.isArmed()) {
463                         state = State.CHECKEDPRESSED;
464                     } else if (model.isRollover()) {
465                         state = State.CHECKEDHOT;
466                     }
467                 } else {
468                     state = State.UNCHECKEDNORMAL;
469                     if (!model.isEnabled()) {
470                         state = State.UNCHECKEDDISABLED;
471                     } else if (model.isPressed() && model.isArmed()) {
472                         state = State.UNCHECKEDPRESSED;
473                     } else if (model.isRollover()) {
474                         state = State.UNCHECKEDHOT;
475                     }
476                 }
477                 skin.paintSkin(g, x, y, state);
478             } else {
479                 // fill interior
480                 if((model.isPressed() && model.isArmed()) || !model.isEnabled()) {
481                     g.setColor(UIManager.getColor("RadioButton.background"));
482                 } else {
483                     g.setColor(UIManager.getColor("RadioButton.interiorBackground"));
484                 }
485                 g.fillRect(x+2, y+2, 8, 8);
486 
487 
488                 boolean isScaledGraphics = SwingUtilities2.isScaledGraphics(g);
489 
490                 if (isScaledGraphics) {
491 
492                     Graphics2D g2d = (Graphics2D) g;
493                     Stroke oldStroke = g2d.getStroke();
494                     g2d.setStroke(new BasicStroke(1.03f, BasicStroke.CAP_ROUND,
495                                                   BasicStroke.JOIN_ROUND));
496                     Object aaHint = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
497                     g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
498                                          RenderingHints.VALUE_ANTIALIAS_ON);
499 
500                     // outter left arc
501                     g.setColor(UIManager.getColor("RadioButton.shadow"));
502                     g.drawArc(x, y, 11, 11, 45, 180);
503                     // outter right arc
504                     g.setColor(UIManager.getColor("RadioButton.highlight"));
505                     g.drawArc(x, y, 11, 11, 45, -180);
506                     // inner left arc
507                     g.setColor(UIManager.getColor("RadioButton.darkShadow"));
508                     g.drawArc(x + 1, y + 1, 9, 9, 45, 180);
509                     // inner right arc
510                     g.setColor(UIManager.getColor("RadioButton.light"));
511                     g.drawArc(x + 1, y + 1, 9, 9, 45, -180);
512 
513                     g2d.setStroke(oldStroke);
514 
515                     if (model.isSelected()) {
516                         if (model.isEnabled()) {
517                             g.setColor(UIManager.getColor("RadioButton.foreground"));
518                         } else {
519                             g.setColor(UIManager.getColor("RadioButton.shadow"));
520                         }
521                         g.fillOval(x + 3, y + 3, 5, 5);
522                     }
523                     g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aaHint);
524 
525                 } else {
526 
527                     // outter left arc
528                     g.setColor(UIManager.getColor("RadioButton.shadow"));
529                     g.drawLine(x+4, y+0, x+7, y+0);
530                     g.drawLine(x+2, y+1, x+3, y+1);
531                     g.drawLine(x+8, y+1, x+9, y+1);
532                     g.drawLine(x+1, y+2, x+1, y+3);
533                     g.drawLine(x+0, y+4, x+0, y+7);
534                     g.drawLine(x+1, y+8, x+1, y+9);
535 
536                     // outter right arc
537                     g.setColor(UIManager.getColor("RadioButton.highlight"));
538                     g.drawLine(x+2, y+10, x+3, y+10);
539                     g.drawLine(x+4, y+11, x+7, y+11);
540                     g.drawLine(x+8, y+10, x+9, y+10);
541                     g.drawLine(x+10, y+9, x+10, y+8);
542                     g.drawLine(x+11, y+7, x+11, y+4);
543                     g.drawLine(x+10, y+3, x+10, y+2);
544 
545 
546                     // inner left arc
547                     g.setColor(UIManager.getColor("RadioButton.darkShadow"));
548                     g.drawLine(x+4, y+1, x+7, y+1);
549                     g.drawLine(x+2, y+2, x+3, y+2);
550                     g.drawLine(x+8, y+2, x+9, y+2);
551                     g.drawLine(x+2, y+3, x+2, y+3);
552                     g.drawLine(x+1, y+4, x+1, y+7);
553                     g.drawLine(x+2, y+8, x+2, y+8);
554 
555 
556                     // inner right arc
557                     g.setColor(UIManager.getColor("RadioButton.light"));
558                     g.drawLine(x+2,  y+9,  x+3,  y+9);
559                     g.drawLine(x+4,  y+10, x+7,  y+10);
560                     g.drawLine(x+8,  y+9,  x+9,  y+9);
561                     g.drawLine(x+9,  y+8,  x+9,  y+8);
562                     g.drawLine(x+10, y+7,  x+10, y+4);
563                     g.drawLine(x+9,  y+3,  x+9,  y+3);
564 
565 
566                      // indicate whether selected or not
567                     if (model.isSelected()) {
568                         if (model.isEnabled()) {
569                             g.setColor(UIManager.getColor("RadioButton.foreground"));
570                         } else {
571                             g.setColor(UIManager.getColor("RadioButton.shadow"));
572                         }
573                         g.fillRect(x+4, y+5, 4, 2);
574                         g.fillRect(x+5, y+4, 2, 4);
575                     }
576                 }
577             }
578         }
579 
getIconWidth()580         public int getIconWidth() {
581             XPStyle xp = XPStyle.getXP();
582             if (xp != null) {
583                 return xp.getSkin(null, Part.BP_RADIOBUTTON).getWidth();
584             } else {
585                 return 13;
586             }
587         }
588 
getIconHeight()589         public int getIconHeight() {
590             XPStyle xp = XPStyle.getXP();
591             if (xp != null) {
592                 return xp.getSkin(null, Part.BP_RADIOBUTTON).getHeight();
593             } else {
594                 return 13;
595             }
596         }
597     } // end class RadioButtonIcon
598 
599 
600     @SuppressWarnings("serial") // Same-version serialization only
601     private static class CheckBoxMenuItemIcon implements Icon, UIResource, Serializable
602     {
paintIcon(Component c, Graphics g, int x, int y)603         public void paintIcon(Component c, Graphics g, int x, int y) {
604             AbstractButton b = (AbstractButton) c;
605             ButtonModel model = b.getModel();
606             boolean isSelected = model.isSelected();
607             if (isSelected) {
608                 y = y - getIconHeight() / 2;
609                 g.drawLine(x+9, y+3, x+9, y+3);
610                 g.drawLine(x+8, y+4, x+9, y+4);
611                 g.drawLine(x+7, y+5, x+9, y+5);
612                 g.drawLine(x+6, y+6, x+8, y+6);
613                 g.drawLine(x+3, y+7, x+7, y+7);
614                 g.drawLine(x+4, y+8, x+6, y+8);
615                 g.drawLine(x+5, y+9, x+5, y+9);
616                 g.drawLine(x+3, y+5, x+3, y+5);
617                 g.drawLine(x+3, y+6, x+4, y+6);
618             }
619         }
getIconWidth()620         public int getIconWidth() { return 9; }
getIconHeight()621         public int getIconHeight() { return 9; }
622 
623     } // End class CheckBoxMenuItemIcon
624 
625 
626     @SuppressWarnings("serial") // Same-version serialization only
627     private static class RadioButtonMenuItemIcon implements Icon, UIResource, Serializable
628     {
paintIcon(Component c, Graphics g, int x, int y)629         public void paintIcon(Component c, Graphics g, int x, int y) {
630             AbstractButton b = (AbstractButton) c;
631             ButtonModel model = b.getModel();
632             if (b.isSelected() == true) {
633                g.fillRoundRect(x+3,y+3, getIconWidth()-6, getIconHeight()-6,
634                                4, 4);
635             }
636         }
getIconWidth()637         public int getIconWidth() { return 12; }
getIconHeight()638         public int getIconHeight() { return 12; }
639 
640     } // End class RadioButtonMenuItemIcon
641 
642 
643     @SuppressWarnings("serial") // Same-version serialization only
644     private static class MenuItemCheckIcon implements Icon, UIResource, Serializable{
paintIcon(Component c, Graphics g, int x, int y)645         public void paintIcon(Component c, Graphics g, int x, int y) {
646             /* For debugging:
647                Color oldColor = g.getColor();
648             g.setColor(Color.orange);
649             g.fill3DRect(x,y,getIconWidth(), getIconHeight(), true);
650             g.setColor(oldColor);
651             */
652         }
getIconWidth()653         public int getIconWidth() { return 9; }
getIconHeight()654         public int getIconHeight() { return 9; }
655 
656     } // End class MenuItemCheckIcon
657 
658     @SuppressWarnings("serial") // Same-version serialization only
659     private static class MenuItemArrowIcon implements Icon, UIResource, Serializable {
paintIcon(Component c, Graphics g, int x, int y)660         public void paintIcon(Component c, Graphics g, int x, int y) {
661             /* For debugging:
662             Color oldColor = g.getColor();
663             g.setColor(Color.green);
664             g.fill3DRect(x,y,getIconWidth(), getIconHeight(), true);
665             g.setColor(oldColor);
666             */
667         }
getIconWidth()668         public int getIconWidth() { return 4; }
getIconHeight()669         public int getIconHeight() { return 8; }
670 
671     } // End class MenuItemArrowIcon
672 
673     @SuppressWarnings("serial") // Same-version serialization only
674     private static class MenuArrowIcon implements Icon, UIResource, Serializable {
paintIcon(Component c, Graphics g, int x, int y)675         public void paintIcon(Component c, Graphics g, int x, int y) {
676             XPStyle xp = XPStyle.getXP();
677             if (WindowsMenuItemUI.isVistaPainting(xp)) {
678                 State state = State.NORMAL;
679                 if (c instanceof JMenuItem) {
680                     state = ((JMenuItem) c).getModel().isEnabled()
681                     ? State.NORMAL : State.DISABLED;
682                 }
683                 Skin skin = xp.getSkin(c, Part.MP_POPUPSUBMENU);
684                 if (WindowsGraphicsUtils.isLeftToRight(c)) {
685                     skin.paintSkin(g, x, y, state);
686                 } else {
687                     Graphics2D g2d = (Graphics2D)g.create();
688                     g2d.translate(x + skin.getWidth(), y);
689                     g2d.scale(-1, 1);
690                     skin.paintSkin(g2d, 0, 0, state);
691                     g2d.dispose();
692                 }
693             } else {
694                 g.translate(x,y);
695                 if( WindowsGraphicsUtils.isLeftToRight(c) ) {
696                     g.drawLine( 0, 0, 0, 7 );
697                     g.drawLine( 1, 1, 1, 6 );
698                     g.drawLine( 2, 2, 2, 5 );
699                     g.drawLine( 3, 3, 3, 4 );
700                 } else {
701                     g.drawLine( 4, 0, 4, 7 );
702                     g.drawLine( 3, 1, 3, 6 );
703                     g.drawLine( 2, 2, 2, 5 );
704                     g.drawLine( 1, 3, 1, 4 );
705                 }
706                 g.translate(-x,-y);
707             }
708         }
getIconWidth()709         public int getIconWidth() {
710             XPStyle xp = XPStyle.getXP();
711             if (WindowsMenuItemUI.isVistaPainting(xp)) {
712                 Skin skin = xp.getSkin(null, Part.MP_POPUPSUBMENU);
713                 return skin.getWidth();
714             } else {
715                 return 4;
716             }
717         }
getIconHeight()718         public int getIconHeight() {
719             XPStyle xp = XPStyle.getXP();
720             if (WindowsMenuItemUI.isVistaPainting(xp)) {
721                 Skin skin = xp.getSkin(null, Part.MP_POPUPSUBMENU);
722                 return skin.getHeight();
723             } else {
724                 return 8;
725             }
726         }
727     } // End class MenuArrowIcon
728 
729     static class VistaMenuItemCheckIconFactory
730            implements MenuItemCheckIconFactory {
731         private static final int OFFSET = 3;
732 
getIcon(JMenuItem component)733         public Icon getIcon(JMenuItem component) {
734             return new VistaMenuItemCheckIcon(component);
735         }
736 
isCompatible(Object icon, String prefix)737         public boolean isCompatible(Object icon, String prefix) {
738             return icon instanceof VistaMenuItemCheckIcon
739               && ((VistaMenuItemCheckIcon) icon).type == getType(prefix);
740         }
741 
getIcon(String type)742         public Icon getIcon(String type) {
743             return new VistaMenuItemCheckIcon(type);
744         }
745 
getIconWidth()746         static int getIconWidth() {
747             XPStyle xp = XPStyle.getXP();
748             return ((xp != null) ? xp.getSkin(null, Part.MP_POPUPCHECK).getWidth() : 16)
749                 + 2 * OFFSET;
750         }
751 
getType(Component c)752         private static Class<? extends JMenuItem> getType(Component c) {
753             Class<? extends JMenuItem> rv = null;
754             if (c instanceof JCheckBoxMenuItem) {
755                 rv = JCheckBoxMenuItem.class;
756             } else if (c instanceof JRadioButtonMenuItem) {
757                 rv = JRadioButtonMenuItem.class;
758             } else if (c instanceof JMenu) {
759                 rv = JMenu.class;
760             } else if (c instanceof JMenuItem) {
761                 rv = JMenuItem.class;
762             }
763             return rv;
764         }
765 
getType(String type)766         private static Class<? extends JMenuItem> getType(String type) {
767             Class<? extends JMenuItem> rv = null;
768             if (type == "CheckBoxMenuItem") {
769                 rv = JCheckBoxMenuItem.class;
770             } else if (type == "RadioButtonMenuItem") {
771                 rv = JRadioButtonMenuItem.class;
772             } else if (type == "Menu") {
773                 rv = JMenu.class;
774             } else if (type == "MenuItem") {
775                 rv = JMenuItem.class;
776             } else {
777                 // this should never happen
778                 rv = JMenuItem.class;
779             }
780             return rv;
781         }
782 
783         /**
784          * CheckIcon for JMenuItem, JMenu, JCheckBoxMenuItem and
785          * JRadioButtonMenuItem.
786          * Note: to be used on Vista only.
787          */
788         @SuppressWarnings("serial") // Same-version serialization only
789         private static class VistaMenuItemCheckIcon
790               implements Icon, UIResource, Serializable {
791 
792             private final JMenuItem menuItem;
793             private final Class<? extends JMenuItem> type;
794 
VistaMenuItemCheckIcon(JMenuItem menuItem)795             VistaMenuItemCheckIcon(JMenuItem menuItem) {
796                 this.type = getType(menuItem);
797                 this.menuItem = menuItem;
798             }
VistaMenuItemCheckIcon(String type)799             VistaMenuItemCheckIcon(String type) {
800                 this.type = getType(type);
801                 this.menuItem = null;
802             }
803 
getIconHeight()804             public int getIconHeight() {
805                 Icon lafIcon = getLaFIcon();
806                 if (lafIcon != null) {
807                     return lafIcon.getIconHeight();
808                 }
809                 Icon icon = getIcon();
810                 int height = 0;
811                 if (icon != null) {
812                     height = icon.getIconHeight();
813                 } else {
814                     XPStyle xp = XPStyle.getXP();
815                     if (xp != null) {
816                         Skin skin = xp.getSkin(null, Part.MP_POPUPCHECK);
817                         height = skin.getHeight();
818                     } else {
819                         height = 16;
820                     }
821                 }
822                 height +=  2 * OFFSET;
823                 return height;
824             }
825 
getIconWidth()826             public int getIconWidth() {
827                 Icon lafIcon = getLaFIcon();
828                 if (lafIcon != null) {
829                     return lafIcon.getIconWidth();
830                 }
831                 Icon icon = getIcon();
832                 int width = 0;
833                 if (icon != null) {
834                     width = icon.getIconWidth() + 2 * OFFSET;
835                 } else {
836                     width = VistaMenuItemCheckIconFactory.getIconWidth();
837                 }
838                 return width;
839             }
840 
paintIcon(Component c, Graphics g, int x, int y)841             public void paintIcon(Component c, Graphics g, int x, int y) {
842                 Icon lafIcon = getLaFIcon();
843                 if (lafIcon != null) {
844                     lafIcon.paintIcon(c, g, x, y);
845                     return;
846                 }
847                 assert menuItem == null || c == menuItem;
848                 Icon icon = getIcon();
849                 if (type == JCheckBoxMenuItem.class
850                       || type == JRadioButtonMenuItem.class) {
851                     AbstractButton b = (AbstractButton) c;
852                     if (b.isSelected()) {
853                         Part backgroundPart = Part.MP_POPUPCHECKBACKGROUND;
854                         Part part = Part.MP_POPUPCHECK;
855                         State backgroundState;
856                         State state;
857                         if (isEnabled(c, null)) {
858                             backgroundState =
859                                 (icon != null) ? State.BITMAP : State.NORMAL;
860                             state = (type == JRadioButtonMenuItem.class)
861                               ? State.BULLETNORMAL
862                               : State.CHECKMARKNORMAL;
863                         } else {
864                             backgroundState = State.DISABLEDPUSHED;
865                             state =
866                                 (type == JRadioButtonMenuItem.class)
867                                   ? State.BULLETDISABLED
868                                   : State.CHECKMARKDISABLED;
869                         }
870                         XPStyle xp = XPStyle.getXP();
871                         if (xp != null) {
872                             Skin skin;
873                             skin =  xp.getSkin(c, backgroundPart);
874                             skin.paintSkin(g, x, y,
875                                 getIconWidth(), getIconHeight(), backgroundState);
876                             if (icon == null) {
877                                 skin = xp.getSkin(c, part);
878                                 skin.paintSkin(g, x + OFFSET, y + OFFSET, state);
879                             }
880                         }
881                     }
882                 }
883                 if (icon != null) {
884                     icon.paintIcon(c, g, x + OFFSET, y + OFFSET);
885                 }
886             }
getAccessor( JMenuItem menuItem)887             private static WindowsMenuItemUIAccessor getAccessor(
888                     JMenuItem menuItem) {
889                 WindowsMenuItemUIAccessor rv = null;
890                 ButtonUI uiObject = (menuItem != null) ? menuItem.getUI()
891                         : null;
892                 if (uiObject instanceof WindowsMenuItemUI) {
893                     rv = ((WindowsMenuItemUI) uiObject).accessor;
894                 } else if (uiObject instanceof WindowsMenuUI) {
895                     rv = ((WindowsMenuUI) uiObject).accessor;
896                 } else if (uiObject instanceof WindowsCheckBoxMenuItemUI) {
897                     rv = ((WindowsCheckBoxMenuItemUI) uiObject).accessor;
898                 } else if (uiObject instanceof WindowsRadioButtonMenuItemUI) {
899                     rv = ((WindowsRadioButtonMenuItemUI) uiObject).accessor;
900                 }
901                 return rv;
902             }
903 
isEnabled(Component c, State state)904             private static boolean isEnabled(Component  c, State state) {
905                 if (state == null && c instanceof JMenuItem) {
906                     WindowsMenuItemUIAccessor accessor =
907                         getAccessor((JMenuItem) c);
908                     if (accessor != null) {
909                         state = accessor.getState((JMenuItem) c);
910                     }
911                 }
912                 if (state == null) {
913                     if (c != null) {
914                         return c.isEnabled();
915                     } else {
916                         return true;
917                     }
918                 } else {
919                     return (state != State.DISABLED)
920                         && (state != State.DISABLEDHOT)
921                         && (state != State.DISABLEDPUSHED);
922                 }
923             }
getIcon()924             private Icon getIcon() {
925                 Icon rv = null;
926                 if (menuItem == null) {
927                     return rv;
928                 }
929                 WindowsMenuItemUIAccessor accessor =
930                     getAccessor(menuItem);
931                 State state = (accessor != null) ? accessor.getState(menuItem)
932                         : null;
933                 if (isEnabled(menuItem, null)) {
934                     if (state == State.PUSHED) {
935                         rv = menuItem.getPressedIcon();
936                     } else {
937                         rv = menuItem.getIcon();
938                     }
939                 } else {
940                     rv = menuItem.getDisabledIcon();
941                 }
942                 return rv;
943             }
944             /**
945              * Check if developer changed icon in the UI table.
946              *
947              * @return the icon to use or {@code null} if the current one is to
948              * be used
949              */
getLaFIcon()950             private Icon getLaFIcon() {
951                 // use icon from the UI table if it does not match this one.
952                 Icon rv = (Icon) UIManager.getDefaults().get(typeToString(type));
953                 if (rv instanceof VistaMenuItemCheckIcon
954                       && ((VistaMenuItemCheckIcon) rv).type == type) {
955                     rv = null;
956                 }
957                 return rv;
958             }
959 
typeToString( Class<? extends JMenuItem> type)960             private static String typeToString(
961                     Class<? extends JMenuItem> type) {
962                 assert type == JMenuItem.class
963                     || type == JMenu.class
964                     || type == JCheckBoxMenuItem.class
965                     || type == JRadioButtonMenuItem.class;
966                 StringBuilder sb = new StringBuilder(type.getName());
967                 // remove package name, dot and the first character
968                 sb.delete(0, sb.lastIndexOf("J") + 1);
969                 sb.append(".checkIcon");
970                 return sb.toString();
971             }
972         }
973     } // End class VistaMenuItemCheckIconFactory
974 }
975