1 /* 2 * Copyright (c) 2002, 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 /* 27 * <p>These classes are designed to be used while the 28 * corresponding <code>LookAndFeel</code> class has been installed 29 * (<code>UIManager.setLookAndFeel(new <i>XXX</i>LookAndFeel())</code>). 30 * Using them while a different <code>LookAndFeel</code> is installed 31 * may produce unexpected results, including exceptions. 32 * Additionally, changing the <code>LookAndFeel</code> 33 * maintained by the <code>UIManager</code> without updating the 34 * corresponding <code>ComponentUI</code> of any 35 * <code>JComponent</code>s may also produce unexpected results, 36 * such as the wrong colors showing up, and is generally not 37 * encouraged. 38 * 39 */ 40 41 package com.sun.java.swing.plaf.windows; 42 43 import java.awt.*; 44 import java.awt.image.*; 45 import java.security.AccessController; 46 import java.util.*; 47 import java.util.concurrent.atomic.AtomicBoolean; 48 49 import javax.swing.*; 50 import javax.swing.border.*; 51 import javax.swing.plaf.*; 52 import javax.swing.text.JTextComponent; 53 54 import sun.awt.image.SunWritableRaster; 55 import sun.awt.windows.ThemeReader; 56 import sun.awt.windows.WToolkit; 57 import sun.security.action.GetPropertyAction; 58 import sun.swing.CachedPainter; 59 60 import static com.sun.java.swing.plaf.windows.TMSchema.*; 61 62 63 /** 64 * Implements Windows XP Styles for the Windows Look and Feel. 65 * 66 * @author Leif Samuelsson 67 */ 68 class XPStyle { 69 // Singleton instance of this class 70 private static XPStyle xp; 71 72 // Singleton instance of SkinPainter 73 private static SkinPainter skinPainter = new SkinPainter(); 74 75 private static Boolean themeActive = null; 76 77 private static final AtomicBoolean xpstyleEnabled = WToolkit.getWToolkit().getXPStyleEnabled(); 78 79 private HashMap<String, Border> borderMap; 80 private HashMap<String, Color> colorMap; 81 82 private boolean flatMenus; 83 84 static { 85 invalidateStyle(); 86 } 87 88 /** Static method for clearing the hashmap and loading the 89 * current XP style and theme 90 */ 91 static synchronized void invalidateStyle() { 92 xp = null; 93 themeActive = null; 94 skinPainter.flush(); 95 } 96 97 /** Get the singleton instance of this class 98 * 99 * @return the singleton instance of this class or null if XP styles 100 * are not active or if this is not Windows XP 101 */ 102 static synchronized XPStyle getXP() { 103 if (themeActive != null && themeActive.booleanValue() != xpstyleEnabled.get()) { 104 // JDK-8039383: theme changed, schedule updateAllUIs() 105 DesktopProperty.scheduleUpdateUI(); 106 } 107 if (themeActive == null) { 108 themeActive = Boolean.valueOf(xpstyleEnabled.get()); 109 if (themeActive.booleanValue()) { 110 GetPropertyAction propertyAction = 111 new GetPropertyAction("swing.noxp"); 112 if (AccessController.doPrivileged(propertyAction) == null && 113 ThemeReader.isThemed() && 114 !(UIManager.getLookAndFeel() 115 instanceof WindowsClassicLookAndFeel)) { 116 117 xp = new XPStyle(); 118 } 119 } 120 } 121 return xp; 122 } 123 124 static boolean isVista() { 125 XPStyle xp = XPStyle.getXP(); 126 return (xp != null && xp.isSkinDefined(null, Part.CP_DROPDOWNBUTTONRIGHT)); 127 } 128 129 /** Get a named <code>String</code> value from the current style 130 * 131 * @param part a <code>Part</code> 132 * @param state a <code>String</code> 133 * @param attributeKey a <code>String</code> 134 * @return a <code>String</code> or null if key is not found 135 * in the current style 136 * 137 * This is currently only used by WindowsInternalFrameTitlePane for painting 138 * title foregound and can be removed when no longer needed 139 */ 140 String getString(Component c, Part part, State state, Prop prop) { 141 return getTypeEnumName(c, part, state, prop); 142 } 143 144 TypeEnum getTypeEnum(Component c, Part part, State state, Prop prop) { 145 int enumValue = ThemeReader.getEnum(part.getControlName(c), part.getValue(), 146 State.getValue(part, state), 147 prop.getValue()); 148 return TypeEnum.getTypeEnum(prop, enumValue); 149 } 150 151 private static String getTypeEnumName(Component c, Part part, State state, Prop prop) { 152 int enumValue = ThemeReader.getEnum(part.getControlName(c), part.getValue(), 153 State.getValue(part, state), 154 prop.getValue()); 155 if (enumValue == -1) { 156 return null; 157 } 158 return TypeEnum.getTypeEnum(prop, enumValue).getName(); 159 } 160 161 162 163 164 /** Get a named <code>int</code> value from the current style 165 * 166 * @param part a <code>Part</code> 167 * @return an <code>int</code> or null if key is not found 168 * in the current style 169 */ 170 int getInt(Component c, Part part, State state, Prop prop, int fallback) { 171 return ThemeReader.getInt(part.getControlName(c), part.getValue(), 172 State.getValue(part, state), 173 prop.getValue()); 174 } 175 176 /** Get a named <code>Dimension</code> value from the current style 177 * 178 * @param key a <code>String</code> 179 * @return a <code>Dimension</code> or null if key is not found 180 * in the current style 181 * 182 * This is currently only used by WindowsProgressBarUI and the value 183 * should probably be cached there instead of here. 184 */ 185 Dimension getDimension(Component c, Part part, State state, Prop prop) { 186 return ThemeReader.getPosition(part.getControlName(c), part.getValue(), 187 State.getValue(part, state), 188 prop.getValue()); 189 } 190 191 /** Get a named <code>Point</code> (e.g. a location or an offset) value 192 * from the current style 193 * 194 * @param key a <code>String</code> 195 * @return a <code>Point</code> or null if key is not found 196 * in the current style 197 * 198 * This is currently only used by WindowsInternalFrameTitlePane for painting 199 * title foregound and can be removed when no longer needed 200 */ 201 Point getPoint(Component c, Part part, State state, Prop prop) { 202 Dimension d = ThemeReader.getPosition(part.getControlName(c), part.getValue(), 203 State.getValue(part, state), 204 prop.getValue()); 205 if (d != null) { 206 return new Point(d.width, d.height); 207 } else { 208 return null; 209 } 210 } 211 212 /** Get a named <code>Insets</code> value from the current style 213 * 214 * @param key a <code>String</code> 215 * @return an <code>Insets</code> object or null if key is not found 216 * in the current style 217 * 218 * This is currently only used to create borders and by 219 * WindowsInternalFrameTitlePane for painting title foregound. 220 * The return value is already cached in those places. 221 */ 222 Insets getMargin(Component c, Part part, State state, Prop prop) { 223 return ThemeReader.getThemeMargins(part.getControlName(c), part.getValue(), 224 State.getValue(part, state), 225 prop.getValue()); 226 } 227 228 229 /** Get a named <code>Color</code> value from the current style 230 * 231 * @param part a <code>Part</code> 232 * @return a <code>Color</code> or null if key is not found 233 * in the current style 234 */ 235 synchronized Color getColor(Skin skin, Prop prop, Color fallback) { 236 String key = skin.toString() + "." + prop.name(); 237 Part part = skin.part; 238 Color color = colorMap.get(key); 239 if (color == null) { 240 color = ThemeReader.getColor(part.getControlName(null), part.getValue(), 241 State.getValue(part, skin.state), 242 prop.getValue()); 243 if (color != null) { 244 color = new ColorUIResource(color); 245 colorMap.put(key, color); 246 } 247 } 248 return (color != null) ? color : fallback; 249 } 250 251 Color getColor(Component c, Part part, State state, Prop prop, Color fallback) { 252 return getColor(new Skin(c, part, state), prop, fallback); 253 } 254 255 256 257 /** Get a named <code>Border</code> value from the current style 258 * 259 * @param part a <code>Part</code> 260 * @return a <code>Border</code> or null if key is not found 261 * in the current style or if the style for the particular 262 * part is not defined as "borderfill". 263 */ 264 synchronized Border getBorder(Component c, Part part) { 265 if (part == Part.MENU) { 266 // Special case because XP has no skin for menus 267 if (flatMenus) { 268 // TODO: The classic border uses this color, but we should 269 // create a new UI property called "PopupMenu.borderColor" 270 // instead. 271 return new XPFillBorder(UIManager.getColor("InternalFrame.borderShadow"), 272 1); 273 } else { 274 return null; // Will cause L&F to use classic border 275 } 276 } 277 Skin skin = new Skin(c, part, null); 278 Border border = borderMap.get(skin.string); 279 if (border == null) { 280 String bgType = getTypeEnumName(c, part, null, Prop.BGTYPE); 281 if ("borderfill".equalsIgnoreCase(bgType)) { 282 int thickness = getInt(c, part, null, Prop.BORDERSIZE, 1); 283 Color color = getColor(skin, Prop.BORDERCOLOR, Color.black); 284 border = new XPFillBorder(color, thickness); 285 if (part == Part.CP_COMBOBOX) { 286 border = new XPStatefulFillBorder(color, thickness, part, Prop.BORDERCOLOR); 287 } 288 } else if ("imagefile".equalsIgnoreCase(bgType)) { 289 Insets m = getMargin(c, part, null, Prop.SIZINGMARGINS); 290 if (m != null) { 291 if (getBoolean(c, part, null, Prop.BORDERONLY)) { 292 border = new XPImageBorder(c, part); 293 } else if (part == Part.CP_COMBOBOX) { 294 border = new EmptyBorder(1, 1, 1, 1); 295 } else { 296 if(part == Part.TP_BUTTON) { 297 border = new XPEmptyBorder(new Insets(3,3,3,3)); 298 } else { 299 border = new XPEmptyBorder(m); 300 } 301 } 302 } 303 } 304 if (border != null) { 305 borderMap.put(skin.string, border); 306 } 307 } 308 return border; 309 } 310 311 @SuppressWarnings("serial") // Superclass is not serializable across versions 312 private class XPFillBorder extends LineBorder implements UIResource { 313 XPFillBorder(Color color, int thickness) { 314 super(color, thickness); 315 } 316 317 public Insets getBorderInsets(Component c, Insets insets) { 318 Insets margin = null; 319 // 320 // Ideally we'd have an interface defined for classes which 321 // support margins (to avoid this hackery), but we've 322 // decided against it for simplicity 323 // 324 if (c instanceof AbstractButton) { 325 margin = ((AbstractButton)c).getMargin(); 326 } else if (c instanceof JToolBar) { 327 margin = ((JToolBar)c).getMargin(); 328 } else if (c instanceof JTextComponent) { 329 margin = ((JTextComponent)c).getMargin(); 330 } 331 insets.top = (margin != null? margin.top : 0) + thickness; 332 insets.left = (margin != null? margin.left : 0) + thickness; 333 insets.bottom = (margin != null? margin.bottom : 0) + thickness; 334 insets.right = (margin != null? margin.right : 0) + thickness; 335 336 return insets; 337 } 338 } 339 340 @SuppressWarnings("serial") // Superclass is not serializable across versions 341 private class XPStatefulFillBorder extends XPFillBorder { 342 private final Part part; 343 private final Prop prop; 344 XPStatefulFillBorder(Color color, int thickness, Part part, Prop prop) { 345 super(color, thickness); 346 this.part = part; 347 this.prop = prop; 348 } 349 350 public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { 351 State state = State.NORMAL; 352 // special casing for comboboxes. 353 // there may be more special cases in the future 354 if(c instanceof JComboBox) { 355 JComboBox cb = (JComboBox)c; 356 // note. in the future this should be replaced with a call 357 // to BasicLookAndFeel.getUIOfType() 358 if(cb.getUI() instanceof WindowsComboBoxUI) { 359 WindowsComboBoxUI wcb = (WindowsComboBoxUI)cb.getUI(); 360 state = wcb.getXPComboBoxState(cb); 361 } 362 } 363 lineColor = getColor(c, part, state, prop, Color.black); 364 super.paintBorder(c, g, x, y, width, height); 365 } 366 } 367 368 @SuppressWarnings("serial") // Superclass is not serializable across versions 369 private class XPImageBorder extends AbstractBorder implements UIResource { 370 Skin skin; 371 372 XPImageBorder(Component c, Part part) { 373 this.skin = getSkin(c, part); 374 } 375 376 public void paintBorder(Component c, Graphics g, 377 int x, int y, int width, int height) { 378 skin.paintSkin(g, x, y, width, height, null); 379 } 380 381 public Insets getBorderInsets(Component c, Insets insets) { 382 Insets margin = null; 383 Insets borderInsets = skin.getContentMargin(); 384 if(borderInsets == null) { 385 borderInsets = new Insets(0, 0, 0, 0); 386 } 387 // 388 // Ideally we'd have an interface defined for classes which 389 // support margins (to avoid this hackery), but we've 390 // decided against it for simplicity 391 // 392 if (c instanceof AbstractButton) { 393 margin = ((AbstractButton)c).getMargin(); 394 } else if (c instanceof JToolBar) { 395 margin = ((JToolBar)c).getMargin(); 396 } else if (c instanceof JTextComponent) { 397 margin = ((JTextComponent)c).getMargin(); 398 } 399 insets.top = (margin != null? margin.top : 0) + borderInsets.top; 400 insets.left = (margin != null? margin.left : 0) + borderInsets.left; 401 insets.bottom = (margin != null? margin.bottom : 0) + borderInsets.bottom; 402 insets.right = (margin != null? margin.right : 0) + borderInsets.right; 403 404 return insets; 405 } 406 } 407 408 @SuppressWarnings("serial") // Superclass is not serializable across versions 409 private class XPEmptyBorder extends EmptyBorder implements UIResource { 410 XPEmptyBorder(Insets m) { 411 super(m.top+2, m.left+2, m.bottom+2, m.right+2); 412 } 413 414 public Insets getBorderInsets(Component c, Insets insets) { 415 insets = super.getBorderInsets(c, insets); 416 417 Insets margin = null; 418 if (c instanceof AbstractButton) { 419 Insets m = ((AbstractButton)c).getMargin(); 420 // if this is a toolbar button then ignore getMargin() 421 // and subtract the padding added by the constructor 422 if(c.getParent() instanceof JToolBar 423 && ! (c instanceof JRadioButton) 424 && ! (c instanceof JCheckBox) 425 && m instanceof InsetsUIResource) { 426 insets.top -= 2; 427 insets.left -= 2; 428 insets.bottom -= 2; 429 insets.right -= 2; 430 } else { 431 margin = m; 432 } 433 } else if (c instanceof JToolBar) { 434 margin = ((JToolBar)c).getMargin(); 435 } else if (c instanceof JTextComponent) { 436 margin = ((JTextComponent)c).getMargin(); 437 } 438 if (margin != null) { 439 insets.top = margin.top + 2; 440 insets.left = margin.left + 2; 441 insets.bottom = margin.bottom + 2; 442 insets.right = margin.right + 2; 443 } 444 return insets; 445 } 446 } 447 boolean isSkinDefined(Component c, Part part) { 448 return (part.getValue() == 0) 449 || ThemeReader.isThemePartDefined( 450 part.getControlName(c), part.getValue(), 0); 451 } 452 453 454 /** Get a <code>Skin</code> object from the current style 455 * for a named part (component type) 456 * 457 * @param part a <code>Part</code> 458 * @return a <code>Skin</code> object 459 */ 460 synchronized Skin getSkin(Component c, Part part) { 461 assert isSkinDefined(c, part) : "part " + part + " is not defined"; 462 return new Skin(c, part, null); 463 } 464 465 466 long getThemeTransitionDuration(Component c, Part part, State stateFrom, 467 State stateTo, Prop prop) { 468 return ThemeReader.getThemeTransitionDuration(part.getControlName(c), 469 part.getValue(), 470 State.getValue(part, stateFrom), 471 State.getValue(part, stateTo), 472 (prop != null) ? prop.getValue() : 0); 473 } 474 475 476 /** A class which encapsulates attributes for a given part 477 * (component type) and which provides methods for painting backgrounds 478 * and glyphs 479 */ 480 static class Skin { 481 final Component component; 482 final Part part; 483 final State state; 484 485 private final String string; 486 private Dimension size = null; 487 488 Skin(Component component, Part part) { 489 this(component, part, null); 490 } 491 492 Skin(Part part, State state) { 493 this(null, part, state); 494 } 495 496 Skin(Component component, Part part, State state) { 497 this.component = component; 498 this.part = part; 499 this.state = state; 500 501 String str = part.getControlName(component) +"." + part.name(); 502 if (state != null) { 503 str += "("+state.name()+")"; 504 } 505 string = str; 506 } 507 508 Insets getContentMargin() { 509 /* idk: it seems margins are the same for all 'big enough' 510 * bounding rectangles. 511 */ 512 int boundingWidth = 100; 513 int boundingHeight = 100; 514 515 return ThemeReader.getThemeBackgroundContentMargins( 516 part.getControlName(null), part.getValue(), 517 0, boundingWidth, boundingHeight); 518 } 519 520 private int getWidth(State state) { 521 if (size == null) { 522 size = getPartSize(part, state); 523 } 524 return size.width; 525 } 526 527 int getWidth() { 528 return getWidth((state != null) ? state : State.NORMAL); 529 } 530 531 private int getHeight(State state) { 532 if (size == null) { 533 size = getPartSize(part, state); 534 } 535 return size.height; 536 } 537 538 int getHeight() { 539 return getHeight((state != null) ? state : State.NORMAL); 540 } 541 542 public String toString() { 543 return string; 544 } 545 546 public boolean equals(Object obj) { 547 return (obj instanceof Skin && ((Skin)obj).string.equals(string)); 548 } 549 550 public int hashCode() { 551 return string.hashCode(); 552 } 553 554 /** Paint a skin at x, y. 555 * 556 * @param g the graphics context to use for painting 557 * @param dx the destination <i>x</i> coordinate 558 * @param dy the destination <i>y</i> coordinate 559 * @param state which state to paint 560 */ 561 void paintSkin(Graphics g, int dx, int dy, State state) { 562 if (state == null) { 563 state = this.state; 564 } 565 paintSkin(g, dx, dy, getWidth(state), getHeight(state), state); 566 } 567 568 /** Paint a skin in an area defined by a rectangle. 569 * 570 * @param g the graphics context to use for painting 571 * @param r a <code>Rectangle</code> defining the area to fill, 572 * may cause the image to be stretched or tiled 573 * @param state which state to paint 574 */ 575 void paintSkin(Graphics g, Rectangle r, State state) { 576 paintSkin(g, r.x, r.y, r.width, r.height, state); 577 } 578 579 /** Paint a skin at a defined position and size 580 * This method supports animation. 581 * 582 * @param g the graphics context to use for painting 583 * @param dx the destination <i>x</i> coordinate 584 * @param dy the destination <i>y</i> coordinate 585 * @param dw the width of the area to fill, may cause 586 * the image to be stretched or tiled 587 * @param dh the height of the area to fill, may cause 588 * the image to be stretched or tiled 589 * @param state which state to paint 590 */ 591 void paintSkin(Graphics g, int dx, int dy, int dw, int dh, State state) { 592 if (ThemeReader.isGetThemeTransitionDurationDefined() 593 && component instanceof JComponent 594 && SwingUtilities.getAncestorOfClass(CellRendererPane.class, 595 component) == null) { 596 AnimationController.paintSkin((JComponent) component, this, 597 g, dx, dy, dw, dh, state); 598 } else { 599 paintSkinRaw(g, dx, dy, dw, dh, state); 600 } 601 } 602 603 /** Paint a skin at a defined position and size. This method 604 * does not trigger animation. It is needed for the animation 605 * support. 606 * 607 * @param g the graphics context to use for painting 608 * @param dx the destination <i>x</i> coordinate. 609 * @param dy the destination <i>y</i> coordinate. 610 * @param dw the width of the area to fill, may cause 611 * the image to be stretched or tiled 612 * @param dh the height of the area to fill, may cause 613 * the image to be stretched or tiled 614 * @param state which state to paint 615 */ 616 void paintSkinRaw(Graphics g, int dx, int dy, int dw, int dh, State state) { 617 skinPainter.paint(null, g, dx, dy, dw, dh, this, state); 618 } 619 620 /** Paint a skin at a defined position and size 621 * 622 * @param g the graphics context to use for painting 623 * @param dx the destination <i>x</i> coordinate 624 * @param dy the destination <i>y</i> coordinate 625 * @param dw the width of the area to fill, may cause 626 * the image to be stretched or tiled 627 * @param dh the height of the area to fill, may cause 628 * the image to be stretched or tiled 629 * @param state which state to paint 630 * @param borderFill should test if the component uses a border fill 631 and skip painting if it is 632 */ 633 void paintSkin(Graphics g, int dx, int dy, int dw, int dh, State state, 634 boolean borderFill) { 635 if(borderFill && "borderfill".equals(getTypeEnumName(component, part, 636 state, Prop.BGTYPE))) { 637 return; 638 } 639 skinPainter.paint(null, g, dx, dy, dw, dh, this, state); 640 } 641 } 642 643 private static class SkinPainter extends CachedPainter { 644 SkinPainter() { 645 super(30); 646 flush(); 647 } 648 649 public void flush() { 650 super.flush(); 651 } 652 653 protected void paintToImage(Component c, Image image, Graphics g, 654 int w, int h, Object[] args) { 655 boolean accEnabled = false; 656 Skin skin = (Skin)args[0]; 657 Part part = skin.part; 658 State state = (State)args[1]; 659 if (state == null) { 660 state = skin.state; 661 } 662 if (c == null) { 663 c = skin.component; 664 } 665 BufferedImage bi = (BufferedImage)image; 666 667 WritableRaster raster = bi.getRaster(); 668 DataBufferInt dbi = (DataBufferInt)raster.getDataBuffer(); 669 // Note that stealData() requires a markDirty() afterwards 670 // since we modify the data in it. 671 ThemeReader.paintBackground(SunWritableRaster.stealData(dbi, 0), 672 part.getControlName(c), part.getValue(), 673 State.getValue(part, state), 674 0, 0, w, h, w); 675 SunWritableRaster.markDirty(dbi); 676 } 677 678 protected Image createImage(Component c, int w, int h, 679 GraphicsConfiguration config, Object[] args) { 680 return new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); 681 } 682 } 683 684 @SuppressWarnings("serial") // Superclass is not serializable across versions 685 static class GlyphButton extends JButton { 686 private Skin skin; 687 688 public GlyphButton(Component parent, Part part) { 689 XPStyle xp = getXP(); 690 skin = xp.getSkin(parent, part); 691 setBorder(null); 692 setContentAreaFilled(false); 693 setMinimumSize(new Dimension(5, 5)); 694 setPreferredSize(new Dimension(16, 16)); 695 setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)); 696 } 697 698 public boolean isFocusTraversable() { 699 return false; 700 } 701 702 protected State getState() { 703 State state = State.NORMAL; 704 if (!isEnabled()) { 705 state = State.DISABLED; 706 } else if (getModel().isPressed()) { 707 state = State.PRESSED; 708 } else if (getModel().isRollover()) { 709 state = State.HOT; 710 } 711 return state; 712 } 713 714 public void paintComponent(Graphics g) { 715 Dimension d = getSize(); 716 skin.paintSkin(g, 0, 0, d.width, d.height, getState()); 717 } 718 719 public void setPart(Component parent, Part part) { 720 XPStyle xp = getXP(); 721 skin = xp.getSkin(parent, part); 722 revalidate(); 723 repaint(); 724 } 725 726 protected void paintBorder(Graphics g) { 727 } 728 729 730 } 731 732 // Private constructor 733 private XPStyle() { 734 flatMenus = getSysBoolean(Prop.FLATMENUS); 735 736 colorMap = new HashMap<String, Color>(); 737 borderMap = new HashMap<String, Border>(); 738 // Note: All further access to the maps must be synchronized 739 } 740 741 742 private boolean getBoolean(Component c, Part part, State state, Prop prop) { 743 return ThemeReader.getBoolean(part.getControlName(c), part.getValue(), 744 State.getValue(part, state), 745 prop.getValue()); 746 } 747 748 749 750 static Dimension getPartSize(Part part, State state) { 751 return ThemeReader.getPartSize(part.getControlName(null), part.getValue(), 752 State.getValue(part, state)); 753 } 754 755 private static boolean getSysBoolean(Prop prop) { 756 // We can use any widget name here, I guess. 757 return ThemeReader.getSysBoolean("window", prop.getValue()); 758 } 759 }