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
26package com.sun.java.swing.plaf.windows;
27
28import javax.swing.*;
29import javax.swing.plaf.ButtonUI;
30import javax.swing.plaf.UIResource;
31
32import java.awt.*;
33import java.io.Serializable;
34
35import static com.sun.java.swing.plaf.windows.TMSchema.*;
36import static com.sun.java.swing.plaf.windows.XPStyle.Skin;
37
38import sun.swing.MenuItemCheckIconFactory;
39import 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
56public 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
72    public static Icon getMenuItemCheckIcon() {
73        if (menuItemCheckIcon == null) {
74            menuItemCheckIcon = new MenuItemCheckIcon();
75        }
76        return menuItemCheckIcon;
77    }
78
79    public static Icon getMenuItemArrowIcon() {
80        if (menuItemArrowIcon == null) {
81            menuItemArrowIcon = new MenuItemArrowIcon();
82        }
83        return menuItemArrowIcon;
84    }
85
86    public static Icon getMenuArrowIcon() {
87        if (menuArrowIcon == null) {
88            menuArrowIcon = new MenuArrowIcon();
89        }
90        return menuArrowIcon;
91    }
92
93    public static Icon getCheckBoxIcon() {
94        if (checkBoxIcon == null) {
95            checkBoxIcon = new CheckBoxIcon();
96        }
97        return checkBoxIcon;
98    }
99
100    public static Icon getRadioButtonIcon() {
101        if (radioButtonIcon == null) {
102            radioButtonIcon = new RadioButtonIcon();
103        }
104        return radioButtonIcon;
105    }
106
107    public static Icon getCheckBoxMenuItemIcon() {
108        if (checkBoxMenuItemIcon == null) {
109            checkBoxMenuItemIcon = new CheckBoxMenuItemIcon();
110        }
111        return checkBoxMenuItemIcon;
112    }
113
114    public static Icon getRadioButtonMenuItemIcon() {
115        if (radioButtonMenuItemIcon == null) {
116            radioButtonMenuItemIcon = new RadioButtonMenuItemIcon();
117        }
118        return radioButtonMenuItemIcon;
119    }
120
121    static
122    synchronized VistaMenuItemCheckIconFactory getMenuItemCheckIconFactory() {
123        if (menuItemCheckIconFactory == null) {
124            menuItemCheckIconFactory =
125                new VistaMenuItemCheckIconFactory();
126        }
127        return menuItemCheckIconFactory;
128    }
129
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
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
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
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
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
169        private FrameButtonIcon(Part part) {
170            this.part = part;
171        }
172
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
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
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 {
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            }
318            public int getIconWidth() { return 13; }
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;
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
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
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    {
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
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
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    {
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        }
620        public int getIconWidth() { return 9; }
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    {
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        }
637        public int getIconWidth() { return 12; }
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{
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        }
653        public int getIconWidth() { return 9; }
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 {
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        }
668        public int getIconWidth() { return 4; }
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 {
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        }
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        }
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
733        public Icon getIcon(JMenuItem component) {
734            return new VistaMenuItemCheckIcon(component);
735        }
736
737        public boolean isCompatible(Object icon, String prefix) {
738            return icon instanceof VistaMenuItemCheckIcon
739              && ((VistaMenuItemCheckIcon) icon).type == getType(prefix);
740        }
741
742        public Icon getIcon(String type) {
743            return new VistaMenuItemCheckIcon(type);
744        }
745
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
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
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
795            VistaMenuItemCheckIcon(JMenuItem menuItem) {
796                this.type = getType(menuItem);
797                this.menuItem = menuItem;
798            }
799            VistaMenuItemCheckIcon(String type) {
800                this.type = getType(type);
801                this.menuItem = null;
802            }
803
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
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
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            }
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
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            }
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             */
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
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