0001: /*
0002: * Copyright (c) 2005-2008 Substance Kirill Grouchnikov. All Rights Reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without
0005: * modification, are permitted provided that the following conditions are met:
0006: *
0007: * o Redistributions of source code must retain the above copyright notice,
0008: * this list of conditions and the following disclaimer.
0009: *
0010: * o Redistributions in binary form must reproduce the above copyright notice,
0011: * this list of conditions and the following disclaimer in the documentation
0012: * and/or other materials provided with the distribution.
0013: *
0014: * o Neither the name of Substance Kirill Grouchnikov nor the names of
0015: * its contributors may be used to endorse or promote products derived
0016: * from this software without specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
0020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
0021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
0022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
0025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
0026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
0027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
0028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0029: */
0030: package org.jvnet.substance.utils;
0031:
0032: import java.awt.*;
0033: import java.awt.event.ActionListener;
0034: import java.awt.event.MouseEvent;
0035: import java.awt.image.*;
0036: import java.net.URL;
0037: import java.util.*;
0038:
0039: import javax.swing.*;
0040: import javax.swing.border.Border;
0041: import javax.swing.border.CompoundBorder;
0042: import javax.swing.plaf.*;
0043: import javax.swing.text.*;
0044:
0045: import org.jvnet.lafwidget.*;
0046: import org.jvnet.lafwidget.animation.*;
0047: import org.jvnet.lafwidget.layout.TransitionLayout;
0048: import org.jvnet.substance.*;
0049: import org.jvnet.substance.border.SubstanceBorderPainter;
0050: import org.jvnet.substance.button.BaseButtonShaper;
0051: import org.jvnet.substance.button.SubstanceButtonShaper;
0052: import org.jvnet.substance.color.*;
0053: import org.jvnet.substance.combo.ComboPopupPrototypeCallback;
0054: import org.jvnet.substance.combo.SubstanceComboBoxButton;
0055: import org.jvnet.substance.painter.ControlBackgroundComposite;
0056: import org.jvnet.substance.painter.SubstanceGradientPainter;
0057: import org.jvnet.substance.painter.decoration.*;
0058: import org.jvnet.substance.painter.text.SubstanceTextPainter;
0059: import org.jvnet.substance.scroll.SubstanceScrollButton;
0060: import org.jvnet.substance.skin.AutumnSkin;
0061: import org.jvnet.substance.skin.MagmaSkin;
0062: import org.jvnet.substance.tabbed.TabCloseCallback;
0063: import org.jvnet.substance.theme.*;
0064: import org.jvnet.substance.theme.SubstanceTheme.ThemeKind;
0065: import org.jvnet.substance.utils.SubstanceConstants.*;
0066: import org.jvnet.substance.utils.icon.SubstanceIconFactory;
0067: import org.jvnet.substance.utils.icon.TransitionAwareIcon;
0068:
0069: /**
0070: * Various utility functions. This class is <b>for internal use only</b>.
0071: *
0072: * @author Kirill Grouchnikov
0073: * @author Romain Guy
0074: */
0075: public class SubstanceCoreUtilities {
0076: /**
0077: * Client property name for the previous component state. The client
0078: * property value should be an instance of {@link ComponentState}.
0079: */
0080: private static final String PREV_COMPONENT_STATE = "substancelaf.internal.prevComponentState";
0081:
0082: /**
0083: * Client property name for the previous component state. The client
0084: * property value should be an instance of {@link ComponentState}.
0085: */
0086: private static final String PREV_SEL_COMPONENT_STATE = "substancelaf.internal.prevSelComponentState";
0087:
0088: /**
0089: * Client property name for the next component state. The client property
0090: * value should be an instance of {@link ComponentState}.
0091: */
0092: private static final String NEXT_COMPONENT_STATE = "substancelaf.internal.nextComponentState";
0093:
0094: /**
0095: * Client property name for the next component state. The client property
0096: * value should be an instance of {@link ComponentState}.
0097: */
0098: private static final String NEXT_SEL_COMPONENT_STATE = "substancelaf.internal.nextSelComponentState";
0099:
0100: /**
0101: * Client property name to mark a component as skipping the background fill
0102: * during the painting sequence that involves text painters.
0103: */
0104: public static final String DO_NOT_FILL_BACKGROUND = "substancelaf.internal.textPainter.doNotFillBackground";
0105:
0106: public static final String HAS_CUSTOM_BACKGROUND_FILL = "substancelaf.internal.textPainter.hasCustomBackgroundFill";
0107:
0108: public static final String IS_COVERED_BY_LIGHTWEIGHT_POPUPS = "substancelaf.internal.paint.isCoveredByLightweightPopups";
0109:
0110: // public static final String IS_WINDOW_DECORATION_AREA =
0111: // "substancelaf.internal.headerPainter.isDecorationArea";
0112:
0113: /**
0114: * Private constructor. Is here to enforce using static methods only.
0115: */
0116: private SubstanceCoreUtilities() {
0117: }
0118:
0119: /**
0120: * Clips string based on specified font metrics and available width (in
0121: * pixels). Returns the clipped string, which contains the beginning and the
0122: * end of the input string separated by ellipses (...) in case the string is
0123: * too long to fit into the specified width, and the origianl string
0124: * otherwise.
0125: *
0126: * @param metrics
0127: * Font metrics.
0128: * @param availableWidth
0129: * Available width in pixels.
0130: * @param fullText
0131: * String to clip.
0132: * @return The clipped string, which contains the beginning and the end of
0133: * the input string separated by ellipses (...) in case the string
0134: * is too long to fit into the specified width, and the origianl
0135: * string otherwise.
0136: */
0137: public static String clipString(FontMetrics metrics,
0138: int availableWidth, String fullText) {
0139:
0140: if (metrics.stringWidth(fullText) <= availableWidth)
0141: return fullText;
0142:
0143: String ellipses = "...";
0144: int ellipsesWidth = metrics.stringWidth(ellipses);
0145: if (ellipsesWidth > availableWidth)
0146: return "";
0147:
0148: String starter = "";
0149: String ender = "";
0150:
0151: int w = fullText.length();
0152: int w2 = (w / 2) + (w % 2);
0153: String prevTitle = "";
0154: for (int i = 0; i < w2; i++) {
0155: String newStarter = starter + fullText.charAt(i);
0156: String newEnder = ender;
0157: if ((w - i) > w2)
0158: newEnder = fullText.charAt(w - i - 1) + newEnder;
0159: String newTitle = newStarter + ellipses + newEnder;
0160: if (metrics.stringWidth(newTitle) <= availableWidth) {
0161: starter = newStarter;
0162: ender = newEnder;
0163: prevTitle = newTitle;
0164: continue;
0165: }
0166: return prevTitle;
0167: }
0168: return fullText;
0169: }
0170:
0171: /**
0172: * Checks whether the specified button has associated icon.
0173: *
0174: * @param button
0175: * Button.
0176: * @return If the button has associated icon, <code>true</code> is
0177: * returned, otherwise <code>false</code>.
0178: */
0179: public static boolean hasIcon(AbstractButton button) {
0180: return (button.getIcon() != null);
0181: }
0182:
0183: /**
0184: * Checks whether the specified button has associated text.
0185: *
0186: * @param button
0187: * Button.
0188: * @return If the button has associated text, <code>true</code> is
0189: * returned, otherwise <code>false</code>.
0190: */
0191: public static boolean hasText(AbstractButton button) {
0192: String text = button.getText();
0193: if ((text != null) && (text.length() > 0)) {
0194: return true;
0195: }
0196: return false;
0197: }
0198:
0199: /**
0200: * Checks and answers if the specified button is in a combo box.
0201: *
0202: * @param button
0203: * the button to check
0204: * @return <code>true</code> if in combo box, <code>false</code>
0205: * otherwise
0206: */
0207: public static boolean isComboBoxButton(AbstractButton button) {
0208: Container parent = button.getParent();
0209: return (parent != null)
0210: && ((parent instanceof JComboBox) || (parent
0211: .getParent() instanceof JComboBox));
0212: }
0213:
0214: /**
0215: * Checks and answers if the specified button is in a scroll bar.
0216: *
0217: * @param button
0218: * the button to check
0219: * @return <code>true</code> if in scroll bar, <code>false</code>
0220: * otherwise
0221: */
0222: public static boolean isScrollBarButton(AbstractButton button) {
0223: Container parent = button.getParent();
0224: return (parent != null)
0225: && ((parent instanceof JScrollBar) || (parent
0226: .getParent() instanceof JScrollBar));
0227: }
0228:
0229: /**
0230: * Checks and answers if the specified button is in a spinner.
0231: *
0232: * @param button
0233: * the button to check
0234: * @return <code>true</code> if in spinner, <code>false</code> otherwise
0235: */
0236: public static boolean isSpinnerButton(AbstractButton button) {
0237: Container parent = button.getParent();
0238: if (!(button instanceof SubstanceSpinnerButton))
0239: return false;
0240: return (parent != null)
0241: && ((parent instanceof JSpinner) || (parent.getParent() instanceof JSpinner));
0242: }
0243:
0244: /**
0245: * Checks and answers if the specified button is in a toolbar.
0246: *
0247: * @param button
0248: * the button to check
0249: * @return <code>true</code> if in toolbar, <code>false</code> otherwise
0250: */
0251: public static boolean isToolBarButton(AbstractButton button) {
0252: if (button instanceof SubstanceComboBoxButton)
0253: return false;
0254: if (button instanceof SubstanceSpinnerButton)
0255: return false;
0256: Container parent = button.getParent();
0257: return (parent != null)
0258: && ((parent instanceof JToolBar) || (parent.getParent() instanceof JToolBar));
0259: }
0260:
0261: /**
0262: * Checks answers if the specified component is a button in a scroll
0263: * control, such as scroll bar or tabbed pane (as tab scroller).
0264: *
0265: * @param comp
0266: * The component to check
0267: * @return <code>true</code> if the specified component is a button in a
0268: * scroll control, <code>false</code> otherwise
0269: */
0270: public static boolean isScrollButton(JComponent comp) {
0271: return (comp instanceof SubstanceScrollButton);
0272: }
0273:
0274: /**
0275: * Checks whether the specified control is always painted in currently
0276: * active color (ignoring the transition states that normally result in
0277: * default appearance).
0278: *
0279: * @param comp
0280: * Control.
0281: * @return <code>true</code> if the specified control is always painted in
0282: * currently active color (ignoring the transition states that
0283: * normally result in default appearance), <code>false</code>
0284: * otherwise.
0285: */
0286: public static boolean isControlAlwaysPaintedActive(Component comp) {
0287: return isControlAlwaysPaintedActive(comp, false);
0288: }
0289:
0290: /**
0291: * Checks whether the specified control is always painted in currently
0292: * active color (ignoring the transition states that normally result in
0293: * default appearance).
0294: *
0295: * @param comp
0296: * Control.
0297: * @param checkHierarchy
0298: * If <code>true</code>, the entire component hierarchy will
0299: * be scanned for the
0300: * {@link SubstanceLookAndFeel#PAINT_ACTIVE_PROPERTY}.
0301: * @return <code>true</code> if the specified control is always painted in
0302: * currently active color (ignoring the transition states that
0303: * normally result in default appearance), <code>false</code>
0304: * otherwise.
0305: */
0306: public static boolean isControlAlwaysPaintedActive(Component comp,
0307: boolean checkHierarchy) {
0308: if (!checkHierarchy) {
0309: if (comp instanceof JComponent) {
0310: JComponent jcomp = (JComponent) comp;
0311: Object componentProp = jcomp
0312: .getClientProperty(SubstanceLookAndFeel.PAINT_ACTIVE_PROPERTY);
0313: if (componentProp != null) {
0314: if (Boolean.TRUE.equals(componentProp))
0315: return true;
0316: if (Boolean.FALSE.equals(componentProp))
0317: return false;
0318: }
0319: }
0320: } else {
0321: Component c = comp;
0322: while (c != null) {
0323: if (c instanceof JComponent) {
0324: JComponent jcomp = (JComponent) c;
0325: Object componentProp = jcomp
0326: .getClientProperty(SubstanceLookAndFeel.PAINT_ACTIVE_PROPERTY);
0327: if (componentProp != null) {
0328: if (Boolean.TRUE.equals(componentProp))
0329: return true;
0330: if (Boolean.FALSE.equals(componentProp))
0331: return false;
0332: }
0333: }
0334: c = c.getParent();
0335: }
0336: }
0337: return Boolean.TRUE.equals(UIManager
0338: .get(SubstanceLookAndFeel.PAINT_ACTIVE_PROPERTY));
0339: }
0340:
0341: /**
0342: * Checks whether the specified button never paints its background.
0343: *
0344: * @param button
0345: * Button.
0346: * @return <code>true</code> if the specified button never paints its
0347: * background, <code>false</code> otherwise.
0348: */
0349: public static boolean isButtonNeverPainted(AbstractButton button) {
0350: Component c = button;
0351: while (c != null) {
0352: if (c instanceof JComponent) {
0353: JComponent jc = (JComponent) c;
0354: Object prop = jc
0355: .getClientProperty(SubstanceLookAndFeel.BUTTON_PAINT_NEVER_PROPERTY);
0356: if (prop != null) {
0357: if (Boolean.TRUE.equals(prop))
0358: return true;
0359: if (Boolean.FALSE.equals(prop))
0360: return false;
0361: }
0362: }
0363: c = c.getParent();
0364: }
0365:
0366: return Boolean.TRUE.equals(UIManager
0367: .get(SubstanceLookAndFeel.BUTTON_PAINT_NEVER_PROPERTY));
0368: }
0369:
0370: /**
0371: * Returns the focus ring kind of the specified component.
0372: *
0373: * @param component
0374: * Component.
0375: * @return The focus ring kind of the specified component.
0376: */
0377: public static FocusKind getFocusKind(Component component) {
0378: while (component != null) {
0379: if (component instanceof JComponent) {
0380: JComponent jcomp = (JComponent) component;
0381: Object jcompFocusKind = jcomp
0382: .getClientProperty(SubstanceLookAndFeel.FOCUS_KIND);
0383: if (jcompFocusKind instanceof FocusKind)
0384: return (FocusKind) jcompFocusKind;
0385: }
0386: component = component.getParent();
0387: }
0388: Object globalFocusKind = UIManager
0389: .get(SubstanceLookAndFeel.FOCUS_KIND);
0390: if (globalFocusKind instanceof FocusKind)
0391: return (FocusKind) globalFocusKind;
0392: return FocusKind.ALL_INNER;
0393: }
0394:
0395: /**
0396: * Returns the text alignment kind of the specified tabbed pane.
0397: *
0398: * @param tabPane
0399: * Tabbed pane.
0400: * @return The text alignment kind of the specified tabbed pane.
0401: */
0402: public static TabTextAlignmentKind getTabTextAlignmentKind(
0403: JTabbedPane tabPane) {
0404: Object jcompAlignmentKind = tabPane
0405: .getClientProperty(SubstanceLookAndFeel.TABBED_PANE_TEXT_ALIGNMENT_KIND);
0406: if (jcompAlignmentKind instanceof TabTextAlignmentKind)
0407: return (TabTextAlignmentKind) jcompAlignmentKind;
0408:
0409: Object globalAlignmentKind = UIManager
0410: .get(SubstanceLookAndFeel.TABBED_PANE_TEXT_ALIGNMENT_KIND);
0411: if (globalAlignmentKind instanceof TabTextAlignmentKind)
0412: return (TabTextAlignmentKind) globalAlignmentKind;
0413: return TabTextAlignmentKind.DEFAULT;
0414: }
0415:
0416: /**
0417: * Returns indication whether the watermark should be drawn on the specified
0418: * component.
0419: *
0420: * @param component
0421: * Component.
0422: * @return <code>true</code> if the watermark should be drawn on the
0423: * specified component, <code>false</code> otherwise.
0424: */
0425: public static boolean toDrawWatermark(Component component) {
0426: Component c = component;
0427: while (c != null) {
0428: if (c instanceof JComponent) {
0429: JComponent jcomp = (JComponent) component;
0430: Object obj = jcomp
0431: .getClientProperty(SubstanceLookAndFeel.WATERMARK_IGNORE);
0432: if (Boolean.TRUE.equals(obj))
0433: return false;
0434: if (Boolean.FALSE.equals(obj))
0435: return true;
0436: }
0437: c = c.getParent();
0438: }
0439: Object obj = UIManager
0440: .get(SubstanceLookAndFeel.WATERMARK_IGNORE);
0441: if (Boolean.TRUE.equals(obj))
0442: return false;
0443:
0444: // special cases - lists, tables and trees that show watermarks only
0445: // when the WATERMARK_BLEED is set to Boolean.TRUE
0446: if (component instanceof JList)
0447: return false;
0448: if (component instanceof JTree)
0449: return false;
0450: if (component instanceof JTable)
0451: return false;
0452: if (component instanceof JTextComponent)
0453: return false;
0454: return true;
0455: }
0456:
0457: /**
0458: * Returns indication whether the watermark should "bleed" through the
0459: * specified component.
0460: *
0461: * @param component
0462: * Component.
0463: * @return <code>true</code> if the watermark should "bleed" through the
0464: * specified component, <code>false</code> otherwise.
0465: */
0466: public static boolean toBleedWatermark(Component component) {
0467: if (component instanceof JComponent) {
0468: JComponent jcomp = (JComponent) component;
0469: Object obj = jcomp
0470: .getClientProperty(SubstanceLookAndFeel.WATERMARK_TO_BLEED);
0471: if (Boolean.TRUE.equals(obj))
0472: return true;
0473: if (Boolean.FALSE.equals(obj))
0474: return false;
0475: }
0476:
0477: return SubstanceLookAndFeel.toBleedWatermark();
0478: }
0479:
0480: /**
0481: * Returns the button shaper of the specified button.
0482: *
0483: * @param button
0484: * The button.
0485: * @return The button shaper of the specified button.
0486: */
0487: public static SubstanceButtonShaper getButtonShaper(Component button) {
0488: SubstanceButtonShaper shaper = null;
0489: Component c = button;
0490: while (c != null) {
0491: if (c instanceof JComponent) {
0492: JComponent jcomp = (JComponent) c;
0493: Object customShaper = jcomp
0494: .getClientProperty(SubstanceLookAndFeel.BUTTON_SHAPER_PROPERTY);
0495: if (customShaper != null) {
0496: if (customShaper instanceof SubstanceButtonShaper)
0497: return (SubstanceButtonShaper) customShaper;
0498: if (customShaper instanceof String) {
0499: try {
0500: shaper = (SubstanceButtonShaper) Class
0501: .forName((String) customShaper)
0502: .newInstance();
0503: } catch (Exception exc) {
0504: shaper = null;
0505: }
0506:
0507: if (shaper != null)
0508: return shaper;
0509: }
0510: }
0511: }
0512: c = c.getParent();
0513: }
0514: if (shaper == null) {
0515: shaper = SubstanceLookAndFeel.getCurrentButtonShaper();
0516: }
0517: return shaper;
0518: }
0519:
0520: /**
0521: * Returns the gradient painter of the specified component.
0522: *
0523: * @param comp
0524: * Component.
0525: * @return The gradient painter of the specified component.
0526: */
0527: public static SubstanceGradientPainter getGradientPainter(
0528: JComponent comp) {
0529: SubstanceGradientPainter painter = SubstanceLookAndFeel
0530: .getCurrentGradientPainter();
0531: Object customPainter = comp
0532: .getClientProperty(SubstanceLookAndFeel.GRADIENT_PAINTER_PROPERTY);
0533: if (customPainter != null) {
0534: if (customPainter instanceof SubstanceGradientPainter)
0535: return (SubstanceGradientPainter) customPainter;
0536: try {
0537: painter = (SubstanceGradientPainter) Class.forName(
0538: (String) customPainter).newInstance();
0539: } catch (Exception exc) {
0540: painter = SubstanceLookAndFeel
0541: .getCurrentGradientPainter();
0542: }
0543: }
0544: return painter;
0545: }
0546:
0547: /**
0548: * Retrieves the <code>modified</code> state for the specified component
0549: * in a tabbed pane.
0550: *
0551: * @param tabComponent
0552: * The associated tab component.
0553: * @return <code>true</code> if the specified component in a tabbed pane
0554: * is marked as modified, <code>false</code> otherwise.
0555: * @see SubstanceLookAndFeel#WINDOW_MODIFIED
0556: */
0557: public static boolean isTabModified(Component tabComponent) {
0558: boolean isWindowModified = false;
0559: // boolean toEnd = (tabComponent == null);
0560: Component comp = tabComponent;
0561: // while (!toEnd) {
0562: if (comp instanceof JComponent) {
0563: JComponent jc = (JComponent) comp;
0564:
0565: isWindowModified = Boolean.TRUE
0566: .equals(jc
0567: .getClientProperty(SubstanceLookAndFeel.WINDOW_MODIFIED));
0568: // if (isWindowModified)
0569: // break;
0570: }
0571: // if (comp instanceof JTabbedPane)
0572: // toEnd = true;
0573: // comp = comp.getParent();
0574: // if (comp == null)
0575: // toEnd = true;
0576: // }
0577: return isWindowModified;
0578: }
0579:
0580: /**
0581: * Retrieves the <code>modified</code> state for the specified root pane.
0582: *
0583: * @param rootPane
0584: * The root pane.
0585: * @return <code>true</code> if the specified root pane is marked as
0586: * modified, <code>false</code> otherwise.
0587: * @see SubstanceLookAndFeel#WINDOW_MODIFIED
0588: */
0589: public static boolean isRootPaneModified(JRootPane rootPane) {
0590: return Boolean.TRUE
0591: .equals(rootPane
0592: .getClientProperty(SubstanceLookAndFeel.WINDOW_MODIFIED));
0593: }
0594:
0595: /**
0596: * Retrieves the <code>modified</code> state for the specified internal
0597: * frame.
0598: *
0599: * @param internalFrame
0600: * The internal frame.
0601: * @return <code>true</code> if the specified internal frame is marked as
0602: * modified, <code>false</code> otherwise.
0603: * @see SubstanceLookAndFeel#WINDOW_MODIFIED
0604: */
0605: public static boolean isInternalFrameModified(
0606: JInternalFrame internalFrame) {
0607: Object frameProp = internalFrame
0608: .getClientProperty(SubstanceLookAndFeel.WINDOW_MODIFIED);
0609: if (Boolean.TRUE.equals(frameProp))
0610: return true;
0611: if (Boolean.FALSE.equals(frameProp))
0612: return false;
0613: return Boolean.TRUE
0614: .equals(internalFrame.getRootPane().getClientProperty(
0615: SubstanceLookAndFeel.WINDOW_MODIFIED));
0616: }
0617:
0618: // /**
0619: // * Returns the tab animation kind of the specified tab component.
0620: // *
0621: // * @param tabComponent
0622: // * Tab component.
0623: // * @return Tab animation kind of the specified tab component.
0624: // */
0625: // public static TabAnimationKind getTabAnimationKind(Component
0626: // tabComponent) {
0627: // Component comp = tabComponent;
0628: // // while (!toEnd) {
0629: // if (comp instanceof JComponent) {
0630: // JComponent jc = (JComponent) comp;
0631: //
0632: // Object animObj = jc
0633: // .getClientProperty(SubstanceLookAndFeel.TABBED_PANE_TAB_ANIMATION_KIND);
0634: // if (animObj instanceof TabAnimationKind)
0635: // return (TabAnimationKind) animObj;
0636: // }
0637: // return null;
0638: // }
0639: //
0640: /**
0641: * Returns the tab background composite of the specified tabbed pane.
0642: *
0643: * @param component
0644: * Tabbed pane.
0645: * @return Tab background composite of the specified tabbed pane.
0646: */
0647: public static ControlBackgroundComposite getControlBackgroundComposite(
0648: Component component) {
0649: Component c = component;
0650: while (c != null) {
0651: if (c instanceof JComponent) {
0652: JComponent jc = (JComponent) c;
0653: Object objProp = jc
0654: .getClientProperty(SubstanceLookAndFeel.BACKGROUND_COMPOSITE);
0655: if (objProp instanceof ControlBackgroundComposite)
0656: return (ControlBackgroundComposite) objProp;
0657: }
0658: c = c.getParent();
0659: }
0660: Object globalProp = UIManager
0661: .get(SubstanceLookAndFeel.BACKGROUND_COMPOSITE);
0662: if (globalProp instanceof ControlBackgroundComposite)
0663: return (ControlBackgroundComposite) globalProp;
0664:
0665: return SubstanceLookAndFeel.getBackgroundComposite(component);
0666: }
0667:
0668: /**
0669: * Checks whether the specified tab has a close button.
0670: *
0671: * @param tabbedPane
0672: * Tabbed pane.
0673: * @param tabIndex
0674: * Tab index.
0675: * @return <code>true</code> if the specified tab has a close button,
0676: * <code>false</code> otherwise.
0677: */
0678: public static boolean hasCloseButton(JTabbedPane tabbedPane,
0679: int tabIndex) {
0680: int tabCount = tabbedPane.getTabCount();
0681: if ((tabIndex < 0) || (tabIndex >= tabCount))
0682: return false;
0683:
0684: // if tab is disabled, it doesn't have a close button
0685: if (!tabbedPane.isEnabledAt(tabIndex))
0686: return false;
0687:
0688: // check property on tab component
0689: Component tabComponent = tabbedPane.getComponentAt(tabIndex);
0690: if (tabComponent instanceof JComponent) {
0691: Object compProp = ((JComponent) tabComponent)
0692: .getClientProperty(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_PROPERTY);
0693: if (Boolean.TRUE.equals(compProp))
0694: return true;
0695: if (Boolean.FALSE.equals(compProp))
0696: return false;
0697: }
0698: // check property on tabbed pane
0699: Object tabProp = tabbedPane
0700: .getClientProperty(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_PROPERTY);
0701: if (Boolean.TRUE.equals(tabProp))
0702: return true;
0703: if (Boolean.FALSE.equals(tabProp))
0704: return false;
0705: // check property in UIManager
0706: return UIManager
0707: .getBoolean(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_PROPERTY);
0708: }
0709:
0710: /**
0711: * Returns the size of the close button for a tab in the specified tabbed
0712: * pane.
0713: *
0714: * @param tabbedPane
0715: * Tabbed pane.
0716: * @param tabIndex
0717: * Tab index.
0718: * @return The size of the close button for a tab in the specified tabbed
0719: * pane.
0720: */
0721: public static int getCloseButtonSize(JTabbedPane tabbedPane,
0722: int tabIndex) {
0723: if (!SubstanceCoreUtilities
0724: .hasCloseButton(tabbedPane, tabIndex))
0725: return 0;
0726: return SubstanceSizeUtils
0727: .getTabCloseIconSize(SubstanceSizeUtils
0728: .getComponentFontSize(tabbedPane));
0729: }
0730:
0731: /**
0732: * Checks whether the specified tab should show vertically-aligned (rotated)
0733: * components.
0734: *
0735: * @param tabbedPane
0736: * Tabbed pane.
0737: * @return <code>true</code> if the specified tab should show
0738: * vertically-aligned (rotated) components, <code>false</code>
0739: * otherwise.
0740: */
0741: public static boolean toLayoutVertically(JTabbedPane tabbedPane) {
0742: int tabPlacement = tabbedPane.getTabPlacement();
0743: if ((tabPlacement == SwingConstants.TOP)
0744: || (tabPlacement == SwingConstants.BOTTOM))
0745: return false;
0746:
0747: // check property on tabbed pane
0748: Object tabProp = tabbedPane
0749: .getClientProperty(SubstanceLookAndFeel.TABBED_PANE_VERTICAL_ORIENTATION);
0750: if (Boolean.TRUE.equals(tabProp))
0751: return true;
0752: if (Boolean.FALSE.equals(tabProp))
0753: return false;
0754: // check property in UIManager
0755: return UIManager
0756: .getBoolean(SubstanceLookAndFeel.TABBED_PANE_VERTICAL_ORIENTATION);
0757: }
0758:
0759: /**
0760: * Checks whether the specified tab should show unrotated icon when the tab
0761: * itself is layed-out vertically.
0762: *
0763: * @param tabbedPane
0764: * Tabbed pane.
0765: * @param tabIndex
0766: * Tab index.
0767: * @return <code>true</code> if the specified tab should show unrotated
0768: * icon when the tab itself is layed-out vertically,
0769: * <code>false</code> otherwise.
0770: */
0771: public static boolean toShowIconUnrotated(JTabbedPane tabbedPane,
0772: int tabIndex) {
0773: int tabCount = tabbedPane.getTabCount();
0774: if ((tabIndex < 0) || (tabIndex >= tabCount))
0775: return false;
0776: // check property on tab component
0777: Component tabComponent = tabbedPane.getComponentAt(tabIndex);
0778: if (tabComponent instanceof JComponent) {
0779: Object compProp = ((JComponent) tabComponent)
0780: .getClientProperty(SubstanceLookAndFeel.TABBED_PANE_VERTICAL_ORIENTATION_ROTATE_ICONS);
0781: if (Boolean.TRUE.equals(compProp))
0782: return true;
0783: if (Boolean.FALSE.equals(compProp))
0784: return false;
0785: }
0786: // check property on tabbed pane
0787: Object tabProp = tabbedPane
0788: .getClientProperty(SubstanceLookAndFeel.TABBED_PANE_VERTICAL_ORIENTATION_ROTATE_ICONS);
0789: if (Boolean.TRUE.equals(tabProp))
0790: return true;
0791: if (Boolean.FALSE.equals(tabProp))
0792: return false;
0793: // check property in UIManager
0794: return UIManager
0795: .getBoolean(SubstanceLookAndFeel.TABBED_PANE_VERTICAL_ORIENTATION_ROTATE_ICONS);
0796: }
0797:
0798: /**
0799: * Returns the content border kind of the specified tabbed pane.
0800: *
0801: * @param tabbedPane
0802: * Tabbed pane.
0803: * @return Content border kind of the specified tabbed pane.
0804: */
0805: public static TabContentPaneBorderKind getContentBorderKind(
0806: JTabbedPane tabbedPane) {
0807: // check property on tabbed pane
0808: Object tabProp = tabbedPane
0809: .getClientProperty(SubstanceLookAndFeel.TABBED_PANE_CONTENT_BORDER_KIND);
0810: if (tabProp instanceof TabContentPaneBorderKind)
0811: return (TabContentPaneBorderKind) tabProp;
0812: // check property in UIManager
0813: Object globalProp = UIManager
0814: .get(SubstanceLookAndFeel.TABBED_PANE_CONTENT_BORDER_KIND);
0815: if (globalProp instanceof TabContentPaneBorderKind)
0816: return (TabContentPaneBorderKind) globalProp;
0817: return TabContentPaneBorderKind.DOUBLE_FULL;
0818: }
0819:
0820: /**
0821: * Checks whether the specified tab should show modified animation only on
0822: * its close button.
0823: *
0824: * @param tabbedPane
0825: * Tabbed pane.
0826: * @param tabIndex
0827: * Tab index.
0828: * @return <code>true</code> if the specified tab should show modified
0829: * animation only on its close button, <code>false</code>
0830: * otherwise.
0831: */
0832: public static boolean toAnimateCloseIconOfModifiedTab(
0833: JTabbedPane tabbedPane, int tabIndex) {
0834: int tabCount = tabbedPane.getTabCount();
0835: if ((tabIndex < 0) || (tabIndex >= tabCount))
0836: return false;
0837:
0838: if (!hasCloseButton(tabbedPane, tabIndex))
0839: return false;
0840:
0841: // check property on tab component
0842: Component tabComponent = tabbedPane.getComponentAt(tabIndex);
0843: if (tabComponent instanceof JComponent) {
0844: Object compProp = ((JComponent) tabComponent)
0845: .getClientProperty(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_MODIFIED_ANIMATION);
0846: if (Boolean.TRUE.equals(compProp))
0847: return true;
0848: if (Boolean.FALSE.equals(compProp))
0849: return false;
0850: }
0851: // check property on tabbed pane
0852: Object tabProp = tabbedPane
0853: .getClientProperty(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_MODIFIED_ANIMATION);
0854: if (Boolean.TRUE.equals(tabProp))
0855: return true;
0856: if (Boolean.FALSE.equals(tabProp))
0857: return false;
0858: // check property in UIManager
0859: return UIManager
0860: .getBoolean(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_MODIFIED_ANIMATION);
0861: }
0862:
0863: /**
0864: * Retrieves transparent image of specified dimension.
0865: *
0866: * @param width
0867: * Image width.
0868: * @param height
0869: * Image height.
0870: * @return Transparent image of specified dimension.
0871: */
0872: public static BufferedImage getBlankImage(int width, int height) {
0873: if (MemoryAnalyzer.isRunning()) {
0874: // see if the request is unusual
0875: if ((width >= 100) || (height >= 100)) {
0876: // throw an Exception and then analyze it
0877: try {
0878: throw new Exception();
0879: } catch (Exception exc) {
0880: StringBuffer sb = new StringBuffer();
0881: StackTraceElement[] stack = exc.getStackTrace();
0882: int count = 0;
0883: for (StackTraceElement stackEntry : stack) {
0884: if (count++ > 8)
0885: break;
0886: sb.append(stackEntry.getClassName() + ".");
0887: sb.append(stackEntry.getMethodName() + " [");
0888: sb.append(stackEntry.getLineNumber() + "]");
0889: sb.append("\n");
0890: }
0891: MemoryAnalyzer.enqueueUsage("Blank " + width + "*"
0892: + height + "\n" + sb.toString());
0893:
0894: }
0895: }
0896: }
0897:
0898: BufferedImage image = new BufferedImage(width, height,
0899: BufferedImage.TYPE_INT_ARGB);
0900:
0901: // get graphics and set hints
0902: Graphics2D graphics = (Graphics2D) image.getGraphics().create();
0903: graphics.setColor(new Color(0, 0, 0, 0));
0904: graphics.setComposite(AlphaComposite.Src);
0905: graphics.fillRect(0, 0, width, height);
0906: graphics.dispose();
0907:
0908: return image;
0909: }
0910:
0911: /**
0912: * Checks whether the specified button should have minimal size.
0913: *
0914: * @param button
0915: * Button.
0916: * @return <code>false</code> if the specified button should have minimal
0917: * size, <code>true</code> otherwise.
0918: */
0919: public static boolean hasNoMinSizeProperty(AbstractButton button) {
0920: Object noMinSizeProperty = button
0921: .getClientProperty(SubstanceLookAndFeel.BUTTON_NO_MIN_SIZE_PROPERTY);
0922: if (Boolean.TRUE.equals(noMinSizeProperty))
0923: return true;
0924: if (Boolean.FALSE.equals(noMinSizeProperty))
0925: return false;
0926: Container parent = button.getParent();
0927: if (parent instanceof JComponent) {
0928: noMinSizeProperty = ((JComponent) parent)
0929: .getClientProperty(SubstanceLookAndFeel.BUTTON_NO_MIN_SIZE_PROPERTY);
0930: if (Boolean.TRUE.equals(noMinSizeProperty))
0931: return true;
0932: if (Boolean.FALSE.equals(noMinSizeProperty))
0933: return false;
0934: }
0935: return (Boolean.TRUE.equals(UIManager
0936: .get(SubstanceLookAndFeel.BUTTON_NO_MIN_SIZE_PROPERTY)));
0937: }
0938:
0939: /**
0940: * Checks whether the specified component is flat.
0941: *
0942: * @param comp
0943: * Component.
0944: * @param defaultValue
0945: * The value to return if there is no
0946: * {@link SubstanceLookAndFeel#FLAT_PROPERTY} defined on button
0947: * hierarchy or {@link UIManager}.
0948: * @return <code>false</code> if the specified button is flat,
0949: * <code>true</code> otherwise.
0950: */
0951: public static boolean hasFlatAppearance(Component comp,
0952: boolean defaultValue) {
0953: Component c = comp;
0954: while (c != null) {
0955: if (c instanceof JComponent) {
0956: JComponent jcomp = (JComponent) c;
0957: Object flatProperty = jcomp
0958: .getClientProperty(SubstanceLookAndFeel.FLAT_PROPERTY);
0959: if (Boolean.TRUE.equals(flatProperty))
0960: return true;
0961: if (Boolean.FALSE.equals(flatProperty))
0962: return false;
0963: }
0964: c = c.getParent();
0965: }
0966: Object flatProperty = UIManager
0967: .get(SubstanceLookAndFeel.FLAT_PROPERTY);
0968: if (Boolean.TRUE.equals(flatProperty))
0969: return true;
0970: if (Boolean.FALSE.equals(flatProperty))
0971: return false;
0972: return defaultValue;
0973: }
0974:
0975: /**
0976: * Computes whether the specified button has flat appearance.
0977: *
0978: * @param button
0979: * Button.
0980: * @return <code>true</code> if the button has flat appearance,
0981: * <code>false</code> otherwise.
0982: */
0983: public static boolean hasFlatAppearance(AbstractButton button) {
0984: return ((SubstanceCoreUtilities.isToolBarButton(button) && SubstanceCoreUtilities
0985: .hasFlatAppearance(button, true)) || SubstanceCoreUtilities
0986: .hasFlatAppearance(button, false));
0987: }
0988:
0989: /**
0990: * Returns the popup flyout orientation for the specified combobox.
0991: *
0992: * @param combobox
0993: * Combobox.
0994: * @return The popup flyout orientation for the specified combobox.
0995: */
0996: public static int getPopupFlyoutOrientation(JComboBox combobox) {
0997: Object comboProperty = combobox
0998: .getClientProperty(SubstanceLookAndFeel.COMBO_BOX_POPUP_FLYOUT_ORIENTATION);
0999: if (comboProperty instanceof Integer)
1000: return (Integer) comboProperty;
1001: Object globalProperty = UIManager
1002: .get(SubstanceLookAndFeel.COMBO_BOX_POPUP_FLYOUT_ORIENTATION);
1003: if (globalProperty instanceof Integer)
1004: return (Integer) globalProperty;
1005: return SwingConstants.SOUTH;
1006: }
1007:
1008: /**
1009: * Makes the specified component and all its descendants non-opaque.
1010: *
1011: * @param comp
1012: * Component.
1013: * @param opacitySnapshot
1014: * The "snapshot" map that will contain the original opacity
1015: * status of the specified component and all its descendants.
1016: */
1017: public static void makeNonOpaque(Component comp,
1018: Map<Component, Boolean> opacitySnapshot) {
1019: if (comp instanceof JComponent) {
1020: JComponent jcomp = (JComponent) comp;
1021: opacitySnapshot.put(comp, jcomp.isOpaque());
1022: jcomp.setOpaque(false);
1023: }
1024: if (comp instanceof Container) {
1025: Container cont = (Container) comp;
1026: for (int i = 0; i < cont.getComponentCount(); i++)
1027: makeNonOpaque(cont.getComponent(i), opacitySnapshot);
1028: }
1029: }
1030:
1031: /**
1032: * Restores the opacity of the specified component and all its descendants.
1033: *
1034: * @param comp
1035: * Component.
1036: * @param opacitySnapshot
1037: * The "snapshot" map that contains the original opacity status
1038: * of the specified component and all its descendants.
1039: */
1040: public static void restoreOpaque(Component comp,
1041: Map<Component, Boolean> opacitySnapshot) {
1042: if (comp instanceof JComponent) {
1043: JComponent jcomp = (JComponent) comp;
1044: // fix for defect 159 - snapshot may not contain
1045: // opacity for table header of a table when it's used
1046: // inside tree cell renderer (wrapper in a scroll pane).
1047: if (opacitySnapshot.containsKey(comp))
1048: jcomp.setOpaque(opacitySnapshot.get(comp));
1049: else
1050: jcomp.setOpaque(true);
1051: }
1052: if (comp instanceof Container) {
1053: Container cont = (Container) comp;
1054: for (int i = 0; i < cont.getComponentCount(); i++)
1055: restoreOpaque(cont.getComponent(i), opacitySnapshot);
1056: }
1057: }
1058:
1059: /**
1060: * Makes the specified component and all its descendants non-double
1061: * buffered.
1062: *
1063: * @param comp
1064: * Component.
1065: * @param dbSnapshot
1066: * The "snapshot" map that will contain the original double
1067: * buffer status of the specified component and all its
1068: * descendants.
1069: */
1070: public static void makeNonDoubleBuffered(Component comp,
1071: Map<Component, Boolean> dbSnapshot) {
1072: if (comp instanceof JComponent) {
1073: JComponent jcomp = (JComponent) comp;
1074: dbSnapshot.put(comp, jcomp.isDoubleBuffered());
1075: jcomp.setDoubleBuffered(false);
1076: }
1077: if (comp instanceof Container) {
1078: Container cont = (Container) comp;
1079: for (int i = 0; i < cont.getComponentCount(); i++)
1080: makeNonDoubleBuffered(cont.getComponent(i), dbSnapshot);
1081: }
1082: }
1083:
1084: /**
1085: * Restores the double buffer of the specified component and all its
1086: * descendants.
1087: *
1088: * @param comp
1089: * Component.
1090: * @param dbSnapshot
1091: * The "snapshot" map that contains the original double buffer
1092: * status of the specified component and all its descendants.
1093: */
1094: public static void restoreDoubleBuffered(Component comp,
1095: Map<Component, Boolean> dbSnapshot) {
1096: if (comp instanceof JComponent) {
1097: JComponent jcomp = (JComponent) comp;
1098: // fix for defect 159 - snapshot may not contain
1099: // opacity for table header of a table when it's used
1100: // inside tree cell renderer (wrapper in a scroll pane).
1101: if (dbSnapshot.containsKey(comp))
1102: jcomp.setDoubleBuffered(dbSnapshot.get(comp));
1103: else
1104: jcomp.setOpaque(true);
1105: }
1106: if (comp instanceof Container) {
1107: Container cont = (Container) comp;
1108: for (int i = 0; i < cont.getComponentCount(); i++)
1109: restoreDoubleBuffered(cont.getComponent(i), dbSnapshot);
1110: }
1111: }
1112:
1113: /**
1114: * Creates a compatible image (for efficient processing and drawing).
1115: *
1116: * @param image
1117: * The original image.
1118: * @return Compatible version of the original image.
1119: * @author Romain Guy
1120: */
1121: public static BufferedImage createCompatibleImage(
1122: BufferedImage image) {
1123: GraphicsEnvironment e = GraphicsEnvironment
1124: .getLocalGraphicsEnvironment();
1125: GraphicsDevice d = e.getDefaultScreenDevice();
1126: GraphicsConfiguration c = d.getDefaultConfiguration();
1127: BufferedImage compatibleImage = c.createCompatibleImage(image
1128: .getWidth(), image.getHeight());
1129: Graphics g = compatibleImage.getGraphics();
1130: g.drawImage(image, 0, 0, null);
1131: g.dispose();
1132: return compatibleImage;
1133: }
1134:
1135: // /**
1136: // * Checks whether the specified button in a toolbar is flat.
1137: // *
1138: // * @param button
1139: // * Button.
1140: // * @return <code>true</code> if the specified button is flat in a toolbar,
1141: // * <code>false</code> otherwise.
1142: // */
1143: // public static boolean isToolbarButtonFlat(AbstractButton button) {
1144: // Component comp = button;
1145: // JToolBar toolbar = null;
1146: // while (comp != null) {
1147: // if (comp instanceof JToolBar) {
1148: // toolbar = (JToolBar) comp;
1149: // break;
1150: // }
1151: // comp = comp.getParent();
1152: // }
1153: // if (toolbar == null)
1154: // return false;
1155: //
1156: // return isToolbarButtonFlat(button, toolbar);
1157: // }
1158: //
1159: // /**
1160: // * Checks whether the specified button in the specified toolbar is flat.
1161: // *
1162: // * @param button
1163: // * Button.
1164: // * @param toolbar
1165: // * Toolbar that contains the specified button.
1166: // * @return <code>true</code> if the specified button is flat in the
1167: // * specified toolbar, <code>false</code> otherwise.
1168: // */
1169: // public static boolean isToolbarButtonFlat(AbstractButton button,
1170: // JToolBar toolbar) {
1171: // Object isFlatProperty = button
1172: // .getClientProperty(SubstanceLookAndFeel.FLAT_PROPERTY);
1173: // if (Boolean.TRUE.equals(isFlatProperty))
1174: // return true;
1175: // if (Boolean.FALSE.equals(isFlatProperty))
1176: // return false;
1177: // isFlatProperty = toolbar
1178: // .getClientProperty(SubstanceLookAndFeel.FLAT_PROPERTY);
1179: // if (Boolean.TRUE.equals(isFlatProperty))
1180: // return true;
1181: // if (Boolean.FALSE.equals(isFlatProperty))
1182: // return false;
1183: // if (Boolean.FALSE.equals(UIManager
1184: // .get(SubstanceLookAndFeel.FLAT_PROPERTY)))
1185: // return false;
1186: // return true;
1187: // }
1188:
1189: /**
1190: * Checks whether the specified component will show theme-colorized icon in
1191: * the default state.
1192: *
1193: * @param comp
1194: * Component.
1195: * @return <code>true</code> if the specified component will show
1196: * theme-colorized icon in the default state, <code>false</code>
1197: * otherwise.
1198: */
1199: public static boolean useThemedDefaultIcon(JComponent comp) {
1200: if (comp != null) {
1201: Object useThemedDefaultIconProperty = comp
1202: .getClientProperty(SubstanceLookAndFeel.USE_THEMED_DEFAULT_ICONS);
1203: if (Boolean.TRUE.equals(useThemedDefaultIconProperty))
1204: return true;
1205: if (Boolean.FALSE.equals(useThemedDefaultIconProperty))
1206: return false;
1207: }
1208: if (Boolean.TRUE.equals(UIManager
1209: .get(SubstanceLookAndFeel.USE_THEMED_DEFAULT_ICONS)))
1210: return true;
1211: return false;
1212: }
1213:
1214: /**
1215: * Returns the callback to be called upon tab closing (using the tab close
1216: * button).
1217: *
1218: * @param me
1219: * Mouse event.
1220: * @param tabbedPane
1221: * Tabbed pane.
1222: * @param tabIndex
1223: * Tab index.
1224: * @return Callback to be called upon tab closing (using the tab close
1225: * button).
1226: */
1227: public static TabCloseCallback getTabCloseCallback(MouseEvent me,
1228: JTabbedPane tabbedPane, int tabIndex) {
1229: int tabCount = tabbedPane.getTabCount();
1230: if ((tabIndex < 0) || (tabIndex >= tabCount))
1231: return null;
1232:
1233: // check property on tab component
1234: Component tabComponent = tabbedPane.getComponentAt(tabIndex);
1235: if (tabComponent instanceof JComponent) {
1236: Object compProp = ((JComponent) tabComponent)
1237: .getClientProperty(SubstanceLookAndFeel.TABBED_PANE_CLOSE_CALLBACK);
1238: if (compProp instanceof TabCloseCallback)
1239: return (TabCloseCallback) compProp;
1240: }
1241:
1242: // check property on tabbed pane
1243: Object tabProp = tabbedPane
1244: .getClientProperty(SubstanceLookAndFeel.TABBED_PANE_CLOSE_CALLBACK);
1245: if (tabProp instanceof TabCloseCallback)
1246: return (TabCloseCallback) tabProp;
1247:
1248: Object globProp = UIManager
1249: .get(SubstanceLookAndFeel.TABBED_PANE_CLOSE_CALLBACK);
1250: if (globProp instanceof TabCloseCallback)
1251: return (TabCloseCallback) globProp;
1252:
1253: return null;
1254: }
1255:
1256: /**
1257: * Blends two images along X-axis.
1258: *
1259: * @param imageLeft
1260: * The left image.
1261: * @param imageRight
1262: * The right image.
1263: * @param start
1264: * Relative start of the blend area (in 0.0-1.0 range).
1265: * @param end
1266: * Relative end of the blend area (in 0.0-1.0 range).
1267: * @return Blended image.
1268: */
1269: public static BufferedImage blendImagesHorizontal(
1270: BufferedImage imageLeft, BufferedImage imageRight,
1271: double start, double end) {
1272: int width = imageLeft.getWidth();
1273: if (width != imageRight.getWidth())
1274: throw new IllegalArgumentException(
1275: "Widths are not the same: " + imageLeft.getWidth()
1276: + " and " + imageRight.getWidth());
1277: int height = imageLeft.getHeight();
1278: if (height != imageRight.getHeight())
1279: throw new IllegalArgumentException(
1280: "Heights are not the same: "
1281: + imageLeft.getHeight() + " and "
1282: + imageRight.getHeight());
1283:
1284: BufferedImage result = getBlankImage(width, height);
1285: Graphics2D graphics = (Graphics2D) result.getGraphics()
1286: .create();
1287:
1288: int startX = (int) (start * width);
1289: int endX = (int) (end * width);
1290: int rampWidth = endX - startX;
1291:
1292: if (rampWidth == 0) {
1293: graphics.drawImage(imageLeft, 0, 0, startX, height, 0, 0,
1294: startX, height, null);
1295: graphics.drawImage(imageRight, startX, 0, width, height,
1296: startX, 0, width, height, null);
1297: } else {
1298: BufferedImage rampRight = getBlankImage(rampWidth, height);
1299: Graphics2D rampRightG = (Graphics2D) rampRight
1300: .getGraphics();
1301: rampRightG.setPaint(new GradientPaint(new Point(0, 0),
1302: new Color(0, 0, 0, 255), new Point(rampWidth, 0),
1303: new Color(0, 0, 0, 0)));
1304: rampRightG.fillRect(0, 0, rampWidth, height);
1305:
1306: BufferedImage tempRight = getBlankImage(width - startX,
1307: height);
1308: Graphics2D tempRightG = (Graphics2D) tempRight
1309: .getGraphics();
1310: tempRightG.drawImage(imageRight, 0, 0, width - startX,
1311: height, startX, 0, width, height, null);
1312: tempRightG.setComposite(AlphaComposite.DstOut);
1313: tempRightG.drawImage(rampRight, 0, 0, null);
1314:
1315: tempRightG.setComposite(AlphaComposite.SrcOver);
1316: graphics.drawImage(imageLeft, 0, 0, null);
1317: graphics.drawImage(tempRight, startX, 0, null);
1318: }
1319: graphics.dispose();
1320: return result;
1321: }
1322:
1323: /**
1324: * Blends two images along Y-axis.
1325: *
1326: * @param imageTop
1327: * The left image.
1328: * @param imageBottom
1329: * The right image.
1330: * @param start
1331: * Relative start of the blend area (in 0.0-1.0 range).
1332: * @param end
1333: * Relative end of the blend area (in 0.0-1.0 range).
1334: * @return Blended image.
1335: */
1336: public static BufferedImage blendImagesVertical(
1337: BufferedImage imageTop, BufferedImage imageBottom,
1338: double start, double end) {
1339: int width = imageTop.getWidth();
1340: if (width != imageBottom.getWidth())
1341: throw new IllegalArgumentException(
1342: "Widths are not the same: " + imageTop.getWidth()
1343: + " and " + imageBottom.getWidth());
1344: int height = imageTop.getHeight();
1345: if (height != imageBottom.getHeight())
1346: throw new IllegalArgumentException(
1347: "Heights are not the same: " + imageTop.getHeight()
1348: + " and " + imageBottom.getHeight());
1349:
1350: BufferedImage result = getBlankImage(width, height);
1351: Graphics2D graphics = (Graphics2D) result.getGraphics()
1352: .create();
1353:
1354: int startY = (int) (start * height);
1355: int endY = (int) (end * height);
1356: int rampHeight = endY - startY;
1357:
1358: if (rampHeight == 0) {
1359: graphics.drawImage(imageTop, 0, 0, width, startY, 0, 0,
1360: width, startY, null);
1361: graphics.drawImage(imageBottom, 0, startY, width, height,
1362: 0, startY, width, height, null);
1363: } else {
1364: BufferedImage rampBottom = getBlankImage(width, rampHeight);
1365: Graphics2D rampBottomG = (Graphics2D) rampBottom
1366: .getGraphics();
1367: rampBottomG.setPaint(new GradientPaint(new Point(0, 0),
1368: new Color(0, 0, 0, 255), new Point(0, rampHeight),
1369: new Color(0, 0, 0, 0)));
1370: rampBottomG.fillRect(0, 0, width, rampHeight);
1371:
1372: BufferedImage tempBottom = getBlankImage(width, height
1373: - startY);
1374: Graphics2D tempBottomG = (Graphics2D) tempBottom
1375: .getGraphics();
1376: tempBottomG.drawImage(imageBottom, 0, 0, width, height
1377: - startY, 0, startY, width, height, null);
1378: tempBottomG.setComposite(AlphaComposite.DstOut);
1379: tempBottomG.drawImage(rampBottom, 0, 0, null);
1380:
1381: tempBottomG.setComposite(AlphaComposite.SrcOver);
1382: graphics.drawImage(imageTop, 0, 0, null);
1383: graphics.drawImage(tempBottom, 0, startY, null);
1384: }
1385: graphics.dispose();
1386: return result;
1387: }
1388:
1389: /**
1390: * Retruns the unique ID for the specified color scheme.
1391: *
1392: * @param colorScheme
1393: * Color scheme.
1394: * @return Unique ID for the specified color scheme.
1395: */
1396: public static String getSchemeId(ColorScheme colorScheme) {
1397: if (colorScheme instanceof BaseColorScheme) {
1398: BaseColorScheme base = (BaseColorScheme) colorScheme;
1399: return base.getId();
1400: }
1401:
1402: return colorScheme.getClass().getName();
1403: }
1404:
1405: /**
1406: * Returns the color scheme for the icon of option panes with the specified
1407: * message type.
1408: *
1409: * @param messageType
1410: * Option pane message type.
1411: * @param mainScheme
1412: * Main color scheme.
1413: * @return Color scheme for the icon of option panes with the specified
1414: * message type.
1415: */
1416: public static ColorScheme getOptionPaneColorScheme(int messageType,
1417: ColorScheme mainScheme) {
1418: if (!SubstanceLookAndFeel.isToUseConstantThemesOnDialogs())
1419: return mainScheme;
1420:
1421: switch (messageType) {
1422: case JOptionPane.INFORMATION_MESSAGE:
1423: return new BottleGreenColorScheme();
1424: case JOptionPane.QUESTION_MESSAGE:
1425: return new LightAquaColorScheme();
1426: case JOptionPane.WARNING_MESSAGE:
1427: return new SunsetColorScheme();
1428: case JOptionPane.ERROR_MESSAGE:
1429: return new SunfireRedColorScheme();
1430: }
1431: return null;
1432: }
1433:
1434: /**
1435: * Checks whether the specified theme is dark.
1436: *
1437: * @param theme
1438: * Theme.
1439: * @return <code>true</code> if the specified theme is dark,
1440: * <code>false</code> otherwise.
1441: */
1442: public static boolean isThemeDark(SubstanceTheme theme) {
1443: if (theme instanceof SubstanceInvertedTheme) {
1444: return !isThemeDark(((SubstanceInvertedTheme) theme)
1445: .getOriginalTheme());
1446: }
1447: if (theme instanceof SubstanceNegatedTheme) {
1448: return isThemeDark(((SubstanceNegatedTheme) theme)
1449: .getOriginalTheme());
1450: }
1451: if (theme instanceof SubstanceMixTheme) {
1452: SubstanceMixTheme mix = (SubstanceMixTheme) theme;
1453: for (SubstanceTheme mixCompTheme : mix.getOriginalThemes())
1454: if (isThemeDark(mixCompTheme))
1455: return true;
1456: return false;
1457: }
1458: if (theme instanceof SubstanceBlendBiTheme) {
1459: SubstanceBlendBiTheme blend = (SubstanceBlendBiTheme) theme;
1460: return isThemeDark(blend.getOriginalFirstTheme())
1461: || isThemeDark(blend.getOriginalSecondTheme());
1462: }
1463: return (theme.getKind() == ThemeKind.DARK);
1464: }
1465:
1466: /**
1467: * Returns the popup prototype display value for the specified combo box.
1468: * This value is used to compute the width of the combo popup.
1469: *
1470: * @param combo
1471: * Combo box.
1472: * @return The popup prototype display value for the specified combo box.
1473: */
1474: public static Object getComboPopupPrototypeDisplayValue(
1475: JComboBox combo) {
1476: Object objProp = combo
1477: .getClientProperty(SubstanceLookAndFeel.COMBO_POPUP_PROTOTYPE);
1478: if (objProp == null)
1479: objProp = UIManager
1480: .get(SubstanceLookAndFeel.COMBO_POPUP_PROTOTYPE);
1481: if (objProp == null)
1482: return null;
1483:
1484: if (objProp instanceof ComboPopupPrototypeCallback) {
1485: ComboPopupPrototypeCallback callback = (ComboPopupPrototypeCallback) objProp;
1486: return callback.getPopupPrototypeDisplayValue(combo);
1487: }
1488:
1489: // check if this object is in the model ???
1490: return objProp;
1491: }
1492:
1493: /**
1494: * Returns the scroll bar buttons kind of the specified scroll bar.
1495: *
1496: * @param scrollBar
1497: * Scroll bar.
1498: * @return The scroll bar buttons kind of the specified scroll bar.
1499: */
1500: public static ScrollPaneButtonPolicyKind getScrollPaneButtonsPolicyKind(
1501: JScrollBar scrollBar) {
1502: Component parent = scrollBar.getParent();
1503: if (parent instanceof JScrollPane) {
1504: Object jspKind = ((JScrollPane) parent)
1505: .getClientProperty(SubstanceLookAndFeel.SCROLL_PANE_BUTTONS_POLICY);
1506: if (jspKind instanceof ScrollPaneButtonPolicyKind)
1507: return (ScrollPaneButtonPolicyKind) jspKind;
1508: }
1509: Object globalJspKind = UIManager
1510: .get(SubstanceLookAndFeel.SCROLL_PANE_BUTTONS_POLICY);
1511: if (globalJspKind instanceof ScrollPaneButtonPolicyKind)
1512: return (ScrollPaneButtonPolicyKind) globalJspKind;
1513: return ScrollPaneButtonPolicyKind.OPPOSITE;
1514: }
1515:
1516: /**
1517: * Returns the set of sides registered on the specified button.
1518: *
1519: * @param button
1520: * Button.
1521: * @param propertyName
1522: * Client property name for retrieving the registered sides.
1523: * @return Set of sides registered on the specified button.
1524: */
1525: @SuppressWarnings("unchecked")
1526: public static Set<Side> getSides(AbstractButton button,
1527: String propertyName) {
1528: Object prop = button.getClientProperty(propertyName);
1529:
1530: if (prop instanceof Set) {
1531: return (Set<Side>) prop;
1532: }
1533:
1534: Set<Side> result = new HashSet<Side>();
1535: if (prop != null) {
1536: if (prop instanceof String) {
1537: result.add(SubstanceConstants.Side
1538: .valueOf((String) prop));
1539: }
1540: if (prop instanceof String[]) {
1541: String[] clientSides = (String[]) prop;
1542: for (String side : clientSides) {
1543: result.add(SubstanceConstants.Side.valueOf(side));
1544: }
1545: }
1546: if (prop instanceof Side) {
1547: result.add((Side) prop);
1548: }
1549: if (prop instanceof Side[]) {
1550: Side[] clientSides = (Side[]) prop;
1551: for (Side side : clientSides) {
1552: result.add(side);
1553: }
1554: }
1555: }
1556: return result;
1557: }
1558:
1559: /**
1560: * Returns the corner radius of the specified toolbar button.
1561: *
1562: * @param button
1563: * Toolbar button.
1564: * @param insets
1565: * Button insets.
1566: * @return Corner radius of the specified toolbar button.
1567: */
1568: public static float getToolbarButtonCornerRadius(
1569: AbstractButton button, Insets insets) {
1570:
1571: JToolBar toolbar = null;
1572: Component c = button.getParent();
1573: while (c != null) {
1574: if (c instanceof JToolBar) {
1575: toolbar = (JToolBar) c;
1576: break;
1577: }
1578: c = c.getParent();
1579: }
1580: if (toolbar == null)
1581: return 2.0f;
1582:
1583: int width = button.getWidth();
1584: int height = button.getHeight();
1585:
1586: if (insets != null) {
1587: width -= (insets.left + insets.right);
1588: height -= (insets.top + insets.bottom);
1589: }
1590: float maxRadius = (width > height) ? (height) / 2.0f
1591: : (width) / 2.0f;
1592:
1593: Object buttonProp = button
1594: .getClientProperty(SubstanceLookAndFeel.CORNER_RADIUS);
1595: if (buttonProp instanceof Float)
1596: return Math.min(maxRadius, ((Float) buttonProp)
1597: .floatValue());
1598:
1599: Object toolbarProp = toolbar
1600: .getClientProperty(SubstanceLookAndFeel.CORNER_RADIUS);
1601: if (toolbarProp instanceof Float)
1602: return Math.min(maxRadius, ((Float) toolbarProp)
1603: .floatValue());
1604:
1605: Object globalProp = UIManager
1606: .get(SubstanceLookAndFeel.CORNER_RADIUS);
1607: if (globalProp instanceof Float)
1608: return Math.min(maxRadius, ((Float) globalProp)
1609: .floatValue());
1610:
1611: return 2.0f;
1612: }
1613:
1614: /**
1615: * Returns the number of echo characters per each password chanaracter.
1616: *
1617: * @param jpf
1618: * Password field.
1619: * @return The number of echo characters per each password chanaracter.
1620: */
1621: public static int getEchoPerChar(JPasswordField jpf) {
1622: Object obj = jpf
1623: .getClientProperty(SubstanceLookAndFeel.PASSWORD_ECHO_PER_CHAR);
1624: if ((obj != null) && (obj instanceof Integer)) {
1625: int result = (Integer) obj;
1626: if (result >= 1)
1627: return result;
1628: }
1629:
1630: obj = UIManager
1631: .get(SubstanceLookAndFeel.PASSWORD_ECHO_PER_CHAR);
1632: if ((obj != null) && (obj instanceof Integer)) {
1633: int result = (Integer) obj;
1634: if (result >= 1)
1635: return result;
1636: }
1637: return 1;
1638: }
1639:
1640: /**
1641: * Creates a clip image for soft-clipping. Code taken from <a
1642: * href="http://weblogs.java.net/blog/campbell/archive/2006/07/java_2d_tricker_2.html">here</a>.
1643: *
1644: * @param s
1645: * Clip shape.
1646: * @param width
1647: * Image width.
1648: * @param height
1649: * Image height.
1650: * @return Clip image.
1651: * @author Chris Campbell.
1652: */
1653: public static BufferedImage createClipImage(Shape s, int width,
1654: int height) {
1655: // Create a translucent intermediate image in which we can perform
1656: // the soft clipping
1657: GraphicsEnvironment e = GraphicsEnvironment
1658: .getLocalGraphicsEnvironment();
1659: GraphicsDevice d = e.getDefaultScreenDevice();
1660: GraphicsConfiguration c = d.getDefaultConfiguration();
1661:
1662: BufferedImage img = c.createCompatibleImage(width, height,
1663: Transparency.TRANSLUCENT);
1664: Graphics2D g2 = img.createGraphics();
1665:
1666: // Clear the image so all pixels have zero alpha
1667: g2.setComposite(AlphaComposite.Clear);
1668: g2.fillRect(0, 0, width, height);
1669:
1670: // Render our clip shape into the image. Note that we enable
1671: // antialiasing to achieve the soft clipping effect. Try
1672: // commenting out the line that enables antialiasing, and
1673: // you will see that you end up with the usual hard clipping.
1674: g2.setComposite(AlphaComposite.Src);
1675: g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1676: RenderingHints.VALUE_ANTIALIAS_ON);
1677: g2.setColor(Color.WHITE);
1678: g2.fill(s);
1679: g2.dispose();
1680:
1681: return img;
1682: }
1683:
1684: /**
1685: * Returns the color of mark icons (checkbox, radio button, scrollbar
1686: * arrows, combo arrows, menu arrows etc) for the specified theme.
1687: *
1688: * @param theme
1689: * Theme.
1690: * @param isEnabled
1691: * If <code>true</code>, the mark should be painted in enabled
1692: * state.
1693: * @return Color of mark icons.
1694: */
1695: public static Color getMarkColor(SubstanceTheme theme,
1696: boolean isEnabled) {
1697: ColorScheme scheme = theme.getColorScheme();
1698: if (SubstanceCoreUtilities.isThemeDark(theme)) {
1699: if (!isEnabled) {
1700: // return SubstanceColorUtilities.getInterpolatedColor(theme
1701: // .getBorderTheme().getColorScheme().getDarkColor(),
1702: // scheme.getForegroundColor(), 0.7);
1703: return theme.getBorderTheme().getColorScheme()
1704: .getDarkColor();
1705:
1706: } else {
1707: return SubstanceColorUtilities.getInterpolatedColor(
1708: scheme.getForegroundColor(), scheme
1709: .getUltraLightColor(), 0.9);
1710: }
1711: } else {
1712: Color color1 = isEnabled ? scheme.getUltraDarkColor()
1713: : scheme.getUltraDarkColor();
1714: Color color2 = isEnabled ? scheme.getDarkColor() : scheme
1715: .getLightColor();
1716: return SubstanceColorUtilities.getInterpolatedColor(color1,
1717: color2, 0.9);
1718: }
1719: }
1720:
1721: /**
1722: * Checks whether the specified component has overlay enabled.
1723: *
1724: * @param component
1725: * Component.
1726: * @return <code>true</code> if the specified component has overlay
1727: * enabled, <code>false</code> otherwise.
1728: */
1729: public static boolean hasOverlayProperty(Component component) {
1730: if (component instanceof JComponent) {
1731: Object flatProperty = ((JComponent) component)
1732: .getClientProperty(SubstanceLookAndFeel.OVERLAY_PROPERTY);
1733: if (Boolean.TRUE.equals(flatProperty))
1734: return true;
1735: if (Boolean.FALSE.equals(flatProperty))
1736: return false;
1737: }
1738:
1739: return Boolean.TRUE.equals(UIManager
1740: .get(SubstanceLookAndFeel.OVERLAY_PROPERTY));
1741: }
1742:
1743: /**
1744: * Checks whether the specified component has extra Substance-specific UI
1745: * elements.
1746: *
1747: * @param component
1748: * Component.
1749: * @return <code>true</code> if the specified component has extra
1750: * Substance-specific UI elements, <code>false</code> otherwise.
1751: */
1752: public static boolean toShowExtraElements(Component component) {
1753: Component c = component;
1754: while (c != null) {
1755: if (c instanceof JComponent) {
1756: JComponent jcomp = (JComponent) c;
1757: Object componentProp = jcomp
1758: .getClientProperty(SubstanceLookAndFeel.NO_EXTRA_ELEMENTS);
1759: if (componentProp != null) {
1760: if (Boolean.TRUE.equals(componentProp))
1761: return false;
1762: if (Boolean.FALSE.equals(componentProp))
1763: return true;
1764: }
1765: }
1766: c = c.getParent();
1767: }
1768: return SubstanceLookAndFeel.toShowExtraElements();
1769: }
1770:
1771: // /**
1772: // * Returns the grip painter for the specified component.
1773: // *
1774: // * @param component
1775: // * Component.
1776: // * @param defaultPainter
1777: // * Default painter to use if no custom grip painter is specified.
1778: // * @return The grip painter for the specified component.
1779: // */
1780: // public static GripPainter getGripPainter(JComponent component,
1781: // GripPainter defaultPainter) {
1782: // Component c = component;
1783: // while (c != null) {
1784: // if (c instanceof JComponent) {
1785: // JComponent jcomp = (JComponent) c;
1786: // Object jcompSetting = jcomp
1787: // .getClientProperty(SubstanceLookAndFeel.GRIP_PAINTER);
1788: // if (jcompSetting instanceof GripPainter)
1789: // return (GripPainter) jcompSetting;
1790: // }
1791: // c = c.getParent();
1792: // }
1793: // Object globalSetting = UIManager.get(SubstanceLookAndFeel.GRIP_PAINTER);
1794: // if (globalSetting instanceof GripPainter)
1795: // return (GripPainter) globalSetting;
1796: // return defaultPainter;
1797: // }
1798: //
1799: /**
1800: * Returns indication whether the specified component's border is a
1801: * Substance-specific border.
1802: *
1803: * @param c
1804: * Component.
1805: * @return <code>true</code> if the specified component's border is a
1806: * Substance-specific border, <code>false</code> otherwise.
1807: */
1808: public static boolean hasSubstanceBorder(JComponent c) {
1809: if (c == null)
1810: return false;
1811:
1812: Border border = c.getBorder();
1813: if (border instanceof SubstanceBorder)
1814: return true;
1815:
1816: if (border instanceof CompoundBorder) {
1817: CompoundBorder cb = (CompoundBorder) border;
1818: if (cb.getOutsideBorder() instanceof SubstanceBorder)
1819: return true;
1820: }
1821:
1822: return false;
1823: }
1824:
1825: public static Icon getActiveIcon(Icon origIcon, JComponent comp,
1826: ButtonModel model, Icon glowingIcon,
1827: boolean ignoreRolloverSetting) {
1828: if (origIcon == null)
1829: return null;
1830: Icon result = origIcon;
1831: if (SubstanceCoreUtilities.useThemedDefaultIcon(comp)) {
1832: boolean useThemedVersion = !model.isArmed()
1833: && !model.isPressed() && !model.isSelected();
1834: if (!ignoreRolloverSetting)
1835: useThemedVersion = useThemedVersion
1836: && !model.isRollover();
1837: if (useThemedVersion) {
1838: result = new ImageIcon(SubstanceImageCreator
1839: .getThemeImage(comp, result,
1840: SubstanceThemeUtilities.getTheme(comp,
1841: ComponentState.DEFAULT), false));
1842: }
1843: }
1844: if (FadeConfigurationManager.getInstance().fadeAllowed(
1845: FadeKind.ICON_GLOW, comp)
1846: && model.isRollover() && (glowingIcon != null)) {
1847: result = glowingIcon;
1848: }
1849: return result;
1850: }
1851:
1852: /**
1853: * Returns the current icon for the specified button. This method is <b>for
1854: * internal use only</b>.
1855: *
1856: * @param b
1857: * Button.
1858: * @param glowingIcon
1859: * The glowing icon.
1860: * @param ignoreRolloverSetting
1861: * If <code>true</code>, the rollover status of the specified
1862: * button is ignored.
1863: * @return Icon for the specified button.
1864: */
1865: public static Icon getIcon(AbstractButton b, Icon defaultIcon,
1866: Icon glowingIcon, boolean ignoreRolloverSetting) {
1867: ButtonModel model = b.getModel();
1868: Icon icon = getActiveIcon(b.getIcon() == null ? defaultIcon : b
1869: .getIcon(), b, model, glowingIcon,
1870: ignoreRolloverSetting);
1871: if (icon == null)
1872: return null;
1873:
1874: Icon tmpIcon = null;
1875:
1876: if (icon != null) {
1877: if (!model.isEnabled()) {
1878: if (model.isSelected()) {
1879: tmpIcon = b.getDisabledSelectedIcon();
1880: } else {
1881: tmpIcon = b.getDisabledIcon();
1882: }
1883: } else if (model.isPressed() && model.isArmed()) {
1884: tmpIcon = b.getPressedIcon();
1885: } else if (b.isRolloverEnabled() && model.isRollover()) {
1886: if (model.isSelected()) {
1887: tmpIcon = b.getRolloverSelectedIcon();
1888: } else {
1889: tmpIcon = b.getRolloverIcon();
1890: }
1891: } else if (model.isSelected()) {
1892: tmpIcon = b.getSelectedIcon();
1893: }
1894:
1895: if (tmpIcon != null) {
1896: icon = tmpIcon;
1897: }
1898: }
1899: return icon;
1900: }
1901:
1902: /**
1903: * Returns the global menu gutter fill kind.
1904: *
1905: * @return The global menu gutter fill kind.
1906: */
1907: public static MenuGutterFillKind getMenuGutterFillKind() {
1908: Object globalSetting = UIManager
1909: .get(SubstanceLookAndFeel.MENU_GUTTER_FILL_KIND);
1910: if (globalSetting instanceof MenuGutterFillKind)
1911: return (MenuGutterFillKind) globalSetting;
1912: return MenuGutterFillKind.HARD;
1913: }
1914:
1915: /**
1916: * Given a component, returns the parent for computing the
1917: * {@link SubstanceDecorationPainter}.
1918: *
1919: * @param c
1920: * Component.
1921: * @return The parent for computing the {@link SubstanceDecorationPainter}.
1922: */
1923: public static Container getHeaderParent(Component c) {
1924: Component comp = c.getParent();
1925: Container result = null;
1926: while (comp != null) {
1927: // the second part fixes the incorrect alignments on
1928: // internal frames.
1929: if ((comp instanceof JLayeredPane) && (result == null))
1930: result = (Container) comp;
1931: if ((result == null) && (comp instanceof Window))
1932: result = (Container) comp;
1933: comp = comp.getParent();
1934: }
1935: return result;
1936: //
1937: // while (comp != null) {
1938: // if (comp instanceof JToolBar)
1939: // return (JToolBar) comp;
1940: // if (comp instanceof JMenuBar)
1941: // return (JMenuBar) comp;
1942: // comp = comp.getParent();
1943: // }
1944: // return c.getParent();
1945: }
1946:
1947: // /**
1948: // * Checks whether the specified component is inside a container painted by
1949: // * {@link SubstanceHeaderPainter}.
1950: // *
1951: // * @param c
1952: // * Component.
1953: // * @return <code>true</code>, if the specified component is inside a
1954: // * container painted by {@link SubstanceHeaderPainter},
1955: // * <code>false</code> otherwise.
1956: // */
1957: // public static boolean isInHeader(Component c) {
1958: // Component comp = c.getParent();
1959: // while (comp != null) {
1960: // if (comp instanceof JToolBar)
1961: // return true;
1962: // if (comp instanceof JMenuBar)
1963: // return true;
1964: // if (comp instanceof JComponent) {
1965: // JComponent jcomp = (JComponent) comp;
1966: // if (Boolean.TRUE
1967: // .equals(jcomp
1968: // .getClientProperty(SubstanceCoreUtilities.IS_WINDOW_DECORATION_AREA))) {
1969: // return true;
1970: // }
1971: // }
1972: // comp = comp.getParent();
1973: // }
1974: //
1975: // return false;
1976: // }
1977:
1978: /**
1979: * Paints the text.
1980: *
1981: * @param button
1982: * Button
1983: * @param textRect
1984: * Text rectangle
1985: * @param text
1986: * Text to paint
1987: * @param mnemonicIndex
1988: * Mnemonic index.
1989: * @return Text alpha channel.
1990: */
1991: public static float paintText(AbstractButton button,
1992: Rectangle textRect, String text, int mnemonicIndex) {
1993: ButtonModel model = button.getModel();
1994:
1995: // Ignore the selection state of the menu item. This is especially
1996: // relevant for dark themes.
1997: ComponentState state = ComponentState.getState(model, button,
1998: button instanceof JMenuItem);
1999: ComponentState prevState = SubstanceCoreUtilities
2000: .getPrevComponentState(button);
2001:
2002: // special case for enabled buttons with no background -
2003: // always use the theme for the default state.
2004: if (SubstanceCoreUtilities.isButtonNeverPainted(button)
2005: || !button.isContentAreaFilled()
2006: || (button instanceof JRadioButton)
2007: || (button instanceof JCheckBox)) {
2008: if (state.isKindActive(FadeKind.ENABLE)) {
2009: state = ComponentState.DEFAULT;
2010: prevState = ComponentState.DEFAULT;
2011: }
2012: }
2013:
2014: return paintText(button, textRect, text, mnemonicIndex, state,
2015: prevState);
2016: // graphics.dispose();
2017: }
2018:
2019: public static float paintText(JComponent component,
2020: Rectangle textRect, String text, int mnemonicIndex,
2021: ComponentState state, ComponentState prevState) {
2022:
2023: float themeAlpha = SubstanceThemeUtilities.getTheme(component)
2024: .getThemeAlpha(component, state);
2025:
2026: Color fgColor = getForegroundColor(component, state, prevState);
2027:
2028: boolean isColorized = SubstanceCoreUtilities
2029: .hasColorization(component);
2030: boolean isForegroundUiResource = component.getForeground() instanceof UIResource;
2031: if (!isForegroundUiResource && !component.isEnabled()
2032: && (themeAlpha == 1.0)) {
2033: // Application-specific color on disabled buttons should be "dimmed"
2034: // a little. The higher the colorization factor, the more dimming
2035: // should be.
2036: double colorizationFactor = isColorized ? SubstanceCoreUtilities
2037: .getColorizationFactor(component)
2038: : 1.0;
2039: themeAlpha /= (1.0 + colorizationFactor);
2040: }
2041:
2042: // Graphics2D graphics = (Graphics2D) g.create();
2043:
2044: SubstanceTextPainter textPainter = SubstanceLookAndFeel
2045: .getCurrentTextPainter();
2046:
2047: textPainter.attachText(component, textRect, text,
2048: mnemonicIndex, component.getFont(), fgColor, null);
2049:
2050: return themeAlpha;
2051: }
2052:
2053: public static Color getForegroundColor(Component component,
2054: ComponentState state, ComponentState prevState) {
2055: // Three cases:
2056: // 1. No colorization and the foreground color is UIResource.
2057: // Interpolate the color based on the button states.
2058: // 2. No colorization and the foreground color is not a UIResource.
2059: // Use the button foreground color.
2060: // 3. Colorization. If the foreground color is not a UIResource,
2061: // colorize the button theme. Then, interpolate the color based on the
2062: // button states.
2063: // compute unshifted theme
2064: SubstanceTheme theme = SubstanceThemeUtilities.getTheme(
2065: component, state);
2066: boolean isColorized = SubstanceCoreUtilities
2067: .hasColorization(component);
2068: Color buttonFgColor = component.getForeground();
2069: boolean isForegroundUiResource = buttonFgColor instanceof UIResource;
2070: if (isColorized && !isForegroundUiResource) {
2071: // case 3
2072: double colorizationFactor = SubstanceCoreUtilities
2073: .getColorizationFactor(component);
2074: theme = SubstanceShiftTheme.getShiftedTheme(theme,
2075: buttonFgColor, colorizationFactor, buttonFgColor,
2076: colorizationFactor);
2077: }
2078: Color fgColor = buttonFgColor;
2079: if (isColorized || (!isColorized && isForegroundUiResource)) {
2080: if (component instanceof JMenuItem) {
2081: fgColor = SubstanceCoreUtilities
2082: .getInterpolatedForegroundColor(component,
2083: null, theme, state, prevState,
2084: FadeKind.ROLLOVER, FadeKind.SELECTION,
2085: FadeKind.PRESS, FadeKind.ARM);
2086: } else {
2087: // if (SwingUtilities
2088: // .getAncestorOfClass(JMenuBar.class, component) == null) {
2089: fgColor = SubstanceCoreUtilities
2090: .getInterpolatedForegroundColor(component,
2091: null, theme, state, prevState,
2092: FadeKind.ROLLOVER, FadeKind.SELECTION,
2093: FadeKind.PRESS);
2094: // }
2095: }
2096: }
2097: return fgColor;
2098: }
2099:
2100: // /**
2101: // * Paints menu text.
2102: // *
2103: // * @param menuItem
2104: // * Menu item.
2105: // * @param g
2106: // * Graphics context.
2107: // * @param text
2108: // * Text to paint.
2109: // * @param textRect
2110: // * Text rectangle.
2111: // */
2112: // public static void paintMenuText(JMenuItem menuItem, Graphics g,
2113: // String text, Rectangle textRect) {
2114: // int mnemIndex = menuItem.getDisplayedMnemonicIndex();
2115: //
2116: // ComponentState state = ComponentState.getState(menuItem.getModel(),
2117: // menuItem, true);
2118: // Graphics2D graphics = (Graphics2D) g.create();
2119: // graphics.setComposite(TransitionLayout.getAlphaComposite(menuItem,
2120: // SubstanceCoreUtilities.getTheme(menuItem, true).getThemeAlpha(
2121: // menuItem, state), g));
2122: //
2123: // // System.out.println(menuItem.getText() + "->" + graphics.getFont());
2124: // SubstanceTextPainter textPainter = SubstanceLookAndFeel
2125: // .getCurrentTextPainter();
2126: // if (!state.isKindActive(FadeKind.ENABLE)) {
2127: // graphics
2128: // .setColor(UIManager.getColor("MenuItem.disabledForeground"));
2129: // textPainter
2130: // .paintText(graphics, menuItem, textRect, text, mnemIndex);
2131: // } else {
2132: // // Ignore the selection state of the menu item. This is especially
2133: // // relevant for dark themes.
2134: // ComponentState prevState = SubstanceCoreUtilities
2135: // .getPrevComponentState(menuItem);
2136: // // fix for defect 232 - respect the application menu color
2137: // Color fg = menuItem.getForeground();
2138: // if (fg instanceof UIResource) {
2139: // fg = SubstanceCoreUtilities.getInterpolatedForegroundColor(
2140: // menuItem, null, SubstanceCoreUtilities
2141: // .getHighlightTheme(menuItem, menuItem
2142: // .getParent(), state, true), state,
2143: // prevState, FadeKind.ROLLOVER, FadeKind.PRESS,
2144: // FadeKind.ARM, FadeKind.SELECTION);
2145: // }
2146: //
2147: // graphics.setColor(fg);
2148: // textPainter
2149: // .paintText(graphics, menuItem, textRect, text, mnemIndex);
2150: // }
2151: // graphics.dispose();
2152: // }
2153:
2154: /**
2155: * Paints the focus ring on the specified component.
2156: *
2157: * @param g
2158: * Graphics context.
2159: * @param mainComp
2160: * The main component for the focus painting.
2161: * @param focusedComp
2162: * The actual component that has the focus. For example, the main
2163: * component can be a {@link JSpinner}, while the focused
2164: * component is a text field inside the the spinner editor.
2165: * @param focusShape
2166: * Focus shape. May be <code>null</code> - in this case, the
2167: * bounds of <code>mainComp</code> will be used.
2168: * @param textRect
2169: * Text rectangle (if relevant).
2170: * @param maxAlphaCoef
2171: * Maximum alhpa coefficient for painting the focus. Values lower
2172: * than 1.0 will result in a translucent focus ring (can be used
2173: * to paint a focus ring that doesn't draw too much attention
2174: * away from the content, for example on text components).
2175: * @param extraPadding
2176: * Extra padding between the component bounds and the focus ring
2177: * painting.
2178: */
2179: public static void paintFocus(Graphics g, Component mainComp,
2180: Component focusedComp, Shape focusShape,
2181: Rectangle textRect, float maxAlphaCoef, int extraPadding) {
2182: FadeTracker fadeTracker = FadeTracker.getInstance();
2183: FocusKind focusKind = SubstanceCoreUtilities
2184: .getFocusKind(mainComp);
2185: if ((focusKind == FocusKind.NONE)
2186: && (!fadeTracker.isTracked(focusedComp, FadeKind.FOCUS)))
2187: return;
2188:
2189: Graphics2D graphics = (Graphics2D) g.create();
2190: graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
2191: RenderingHints.VALUE_ANTIALIAS_ON);
2192:
2193: float alpha = 1.0f;
2194: if (fadeTracker.isTracked(focusedComp, FadeKind.FOCUS)) {
2195: alpha = fadeTracker.getFade10(focusedComp, FadeKind.FOCUS) / 10.f;
2196: }
2197: alpha *= maxAlphaCoef;
2198: graphics.setComposite(TransitionLayout.getAlphaComposite(
2199: mainComp, alpha, g));
2200:
2201: Color color = SubstanceColorUtilities.getFocusColor(mainComp);
2202: graphics.setColor(color);
2203: focusKind.paintFocus(mainComp, focusedComp, graphics,
2204: focusShape, textRect, extraPadding);
2205: graphics.dispose();
2206: }
2207:
2208: /**
2209: * Paints the focus ring on the specified component.
2210: *
2211: * @param g
2212: * Graphics context.
2213: * @param mainComp
2214: * The main component for the focus painting.
2215: * @param focusedComp
2216: * The actual component that has the focus. For example, the main
2217: * component can be a {@link JSpinner}, while the focused
2218: * component is a text field inside the the spinner editor.
2219: * @param textRect
2220: * Text rectangle (if relevant).
2221: */
2222: public static void paintFocus(Graphics g, Component mainComp,
2223: Component focusedComp, Rectangle textRect) {
2224: paintFocus(g, mainComp, focusedComp, null, textRect, 1.0f, 0);
2225: }
2226:
2227: /**
2228: * Paints a separator.
2229: *
2230: * @param c
2231: * Component.
2232: * @param graphics
2233: * Graphics context.
2234: * @param colorScheme
2235: * Color scheme.
2236: * @param isDark
2237: * Indication whether the color scheme is dark.
2238: * @param width
2239: * Separator width.
2240: * @param height
2241: * Separator height.
2242: * @param orientation
2243: * Separator orientation.
2244: */
2245: public static void paintSeparator(Component c, Graphics2D graphics,
2246: ColorScheme colorScheme, boolean isDark, int width,
2247: int height, int orientation) {
2248: SubstanceCoreUtilities.paintSeparator(c, graphics, colorScheme,
2249: isDark, width, height, orientation, true, 10);
2250: }
2251:
2252: /**
2253: * Paints a separator.
2254: *
2255: * @param c
2256: * Component.
2257: * @param graphics
2258: * Graphics context.
2259: * @param colorScheme
2260: * Color scheme.
2261: * @param isDark
2262: * Indication whether the color scheme is dark.
2263: * @param width
2264: * Separator width.
2265: * @param height
2266: * Separator height.
2267: * @param orientation
2268: * Separator orientation.
2269: * @param hasShadow
2270: * If <code>true</code>, the separator painting will have
2271: * shadow.
2272: * @param maxGradLength
2273: * Specifies the maximum pixel length of "ramp" portions of the
2274: * separator. The ramp portions are located on separator ends and
2275: * allow providing a faded appearance on those ends.
2276: */
2277: public static void paintSeparator(Component c, Graphics2D graphics,
2278: ColorScheme colorScheme, boolean isDark, int width,
2279: int height, int orientation, boolean hasShadow,
2280: int maxGradLength) {
2281: paintSeparator(c, graphics, colorScheme, isDark, width, height,
2282: orientation, hasShadow, maxGradLength, maxGradLength);
2283: }
2284:
2285: /**
2286: * Paints a separator.
2287: *
2288: * @param c
2289: * Component.
2290: * @param graphics
2291: * Graphics context.
2292: * @param colorScheme
2293: * Color scheme.
2294: * @param isDark
2295: * Indication whether the color scheme is dark.
2296: * @param width
2297: * Separator width.
2298: * @param height
2299: * Separator height.
2300: * @param orientation
2301: * Separator orientation.
2302: * @param hasShadow
2303: * If <code>true</code>, the separator painting will have
2304: * shadow.
2305: * @param maxGradLengthStart
2306: * Specifies the maximum pixel length of the starting "ramp"
2307: * portion of the separator. The starting ramp portion is located
2308: * on top / left separator end and allows providing a faded
2309: * appearance on that end.
2310: * @param maxGradLengthEnd
2311: * Specifies the maximum pixel length of the ending "ramp"
2312: * portion of the separator. The ending ramp portion is located
2313: * on bottom / right separator end and allows providing a faded
2314: * appearance on that end.
2315: */
2316: public static void paintSeparator(Component c, Graphics2D graphics,
2317: ColorScheme colorScheme, boolean isDark, int width,
2318: int height, int orientation, boolean hasShadow,
2319: int maxGradLengthStart, int maxGradLengthEnd) {
2320: Color foreLight = isDark ? colorScheme.getLightColor()
2321: : SubstanceColorUtilities.getInterpolatedColor(
2322: colorScheme.getLightColor(), colorScheme
2323: .getDarkColor(), 0.8);
2324: Color foreDark = isDark ? colorScheme.getExtraLightColor()
2325: : SubstanceColorUtilities.getInterpolatedColor(
2326: colorScheme.getMidColor(), colorScheme
2327: .getDarkColor(), 0.4);
2328: Color back = isDark ? colorScheme.getDarkColor() : colorScheme
2329: .getUltraLightColor();
2330:
2331: float borderStrokeWidth = SubstanceSizeUtils
2332: .getBorderStrokeWidth(SubstanceSizeUtils
2333: .getComponentFontSize(c));
2334: graphics.setStroke(new BasicStroke(borderStrokeWidth,
2335: BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
2336: if (orientation == JSeparator.VERTICAL) {
2337: int gradStart = Math.min(maxGradLengthStart, height / 2);
2338: int gradEnd = Math.min(maxGradLengthEnd, height / 2);
2339: graphics.translate(Math.max(0, width / 2 - 1), 0);
2340: graphics.setPaint(new GradientPaint(0, 0,
2341: SubstanceColorUtilities
2342: .getAlphaColor(foreLight, 32), 0,
2343: gradStart, SubstanceColorUtilities.getAlphaColor(
2344: foreDark, 240)));
2345: graphics.drawLine(0, 0, 0, gradStart);
2346: if (height / 2 > gradStart) {
2347: graphics.setColor(SubstanceColorUtilities
2348: .getAlphaColor(foreDark, 240));
2349: graphics.drawLine(0, gradStart, 0, height - gradEnd);
2350: }
2351: graphics.setPaint(new GradientPaint(0, height - gradEnd,
2352: SubstanceColorUtilities
2353: .getAlphaColor(foreDark, 240), 0, height,
2354: SubstanceColorUtilities
2355: .getAlphaColor(foreLight, 32)));
2356: graphics.drawLine(0, height - gradEnd, 0, height);
2357:
2358: if (hasShadow) {
2359: int offset = (int) borderStrokeWidth;
2360: graphics
2361: .setPaint(new GradientPaint(offset, 0,
2362: SubstanceColorUtilities.getAlphaColor(
2363: back, 32), offset, gradStart,
2364: SubstanceColorUtilities.getAlphaColor(
2365: back, 196)));
2366: graphics.drawLine(offset, 0, offset, gradStart);
2367: if (height / 2 > gradStart) {
2368: graphics.setColor(SubstanceColorUtilities
2369: .getAlphaColor(back, 196));
2370: graphics.drawLine(offset, gradStart, offset, height
2371: - gradEnd);
2372: }
2373: graphics
2374: .setPaint(new GradientPaint(offset, height
2375: - gradEnd, SubstanceColorUtilities
2376: .getAlphaColor(back, 196), offset,
2377: height, SubstanceColorUtilities
2378: .getAlphaColor(back, 32)));
2379: graphics.drawLine(offset, height - gradEnd, offset,
2380: height);
2381: }
2382: } else {
2383: // HORIZONTAL
2384: int gradStart = Math.min(maxGradLengthStart, width / 2);
2385: int gradEnd = Math.min(maxGradLengthEnd, width / 2);
2386: graphics.translate(0, Math.max(0, height / 2 - 1));
2387: graphics.setPaint(new GradientPaint(0, 0,
2388: SubstanceColorUtilities
2389: .getAlphaColor(foreLight, 32), gradStart,
2390: 0, SubstanceColorUtilities.getAlphaColor(foreDark,
2391: 240)));
2392: graphics.drawLine(0, 0, gradStart, 0);
2393: // if (width / 2 > gradStart) {
2394: graphics.setColor(SubstanceColorUtilities.getAlphaColor(
2395: foreDark, 240));
2396: graphics.drawLine(gradStart, 0, width - gradEnd, 0);
2397: // }
2398: graphics.setPaint(new GradientPaint(width - gradEnd, 0,
2399: SubstanceColorUtilities
2400: .getAlphaColor(foreDark, 240), width, 0,
2401: SubstanceColorUtilities
2402: .getAlphaColor(foreLight, 32)));
2403: graphics.drawLine(width - gradEnd, 0, width, 0);
2404:
2405: if (hasShadow) {
2406: int offset = (int) borderStrokeWidth;
2407: graphics
2408: .setPaint(new GradientPaint(0, offset,
2409: SubstanceColorUtilities.getAlphaColor(
2410: back, 32), gradStart, offset,
2411: SubstanceColorUtilities.getAlphaColor(
2412: back, 196)));
2413: graphics.drawLine(0, offset, gradStart, offset);
2414: // if (width / 2 > gradStart) {
2415: graphics.setColor(SubstanceColorUtilities
2416: .getAlphaColor(back, 196));
2417: graphics.drawLine(gradStart, offset, width - gradEnd,
2418: offset);
2419: // }
2420: graphics
2421: .setPaint(new GradientPaint(width - gradEnd,
2422: offset, SubstanceColorUtilities
2423: .getAlphaColor(back, 196),
2424: width, offset, SubstanceColorUtilities
2425: .getAlphaColor(back, 32)));
2426: graphics.drawLine(width - gradEnd, offset, width,
2427: offset);
2428: }
2429: }
2430:
2431: }
2432:
2433: /**
2434: * Returns indication whether the specified button is a close button on some
2435: * title pane.
2436: *
2437: * @param ab
2438: * Button.
2439: * @return <code>true</code> if the specified button is a close button on
2440: * some title pane, <code>false</code> otherwise.
2441: */
2442: public static boolean isTitleCloseButton(AbstractButton ab) {
2443: if ((ab instanceof SubstanceTitleButton)
2444: && Boolean.TRUE
2445: .equals(ab
2446: .getClientProperty(SubstanceButtonUI.IS_TITLE_CLOSE_BUTTON)))
2447: return true;
2448: return false;
2449: }
2450:
2451: /**
2452: * Uninstalls the specified menu item.
2453: *
2454: * @param menuItem
2455: * Menu item.
2456: */
2457: public static void uninstallMenu(JMenuItem menuItem) {
2458: if (menuItem instanceof JMenu) {
2459: JMenu menu = (JMenu) menuItem;
2460: for (Component comp : menu.getMenuComponents())
2461: if (comp instanceof JMenuItem)
2462: SubstanceCoreUtilities
2463: .uninstallMenu((JMenuItem) comp);
2464: }
2465:
2466: ButtonUI menuItemUI = menuItem.getUI();
2467: if (menuItemUI instanceof SubstanceMenu) {
2468: SubstanceMenu sMenu = (SubstanceMenu) menuItemUI;
2469: if (sMenu.getAssociatedMenuItem() != null) {
2470: menuItemUI.uninstallUI(menuItem);
2471: }
2472: }
2473:
2474: for (ActionListener actionListener : menuItem
2475: .getActionListeners())
2476: menuItem.removeActionListener(actionListener);
2477:
2478: menuItem.removeAll();
2479: }
2480:
2481: /**
2482: * Returns an icon pointed to by the specified string.
2483: *
2484: * @param iconResource
2485: * Resource location string.
2486: * @return Icon.
2487: */
2488: public static Icon getIcon(String iconResource) {
2489: ClassLoader cl = getClassLoaderForResources();
2490: URL iconUrl = cl.getResource(iconResource);
2491: if (iconUrl == null)
2492: return null;
2493: return new IconUIResource(new ImageIcon(iconUrl));
2494: }
2495:
2496: public static ClassLoader getClassLoaderForResources() {
2497: // the following is fix by Dag Joar and Christian Schlichtherle
2498: // for application running with -Xbootclasspath VM flag. In this case,
2499: // the using MyClass.class.getClassLoader() would return null,
2500: // but the context class loader will function properly that classes will
2501: // be properly loaded regardless of whether the lib is added to the
2502: // system class path, the extension class path and regardless of the
2503: // class loader architecture set up by some frameworks.
2504: ClassLoader cl = (ClassLoader) UIManager.get("ClassLoader");
2505: if (cl == null)
2506: cl = Thread.currentThread().getContextClassLoader();
2507: return cl;
2508: }
2509:
2510: /**
2511: * Returns the fade callback for the specified button.
2512: *
2513: * @param button
2514: * Button.
2515: * @return Fade callback for the specified button.
2516: */
2517: public static FadeTrackerCallback getFadeCallback(
2518: final AbstractButton button, boolean toRepaintParent) {
2519: return getFadeCallback(button, button.getModel(), false,
2520: toRepaintParent, button);
2521: }
2522:
2523: /**
2524: * Returns the fade callback for the specified component.
2525: *
2526: * @param component
2527: * Component.
2528: * @param model
2529: * Model for tracking the transitions. For button components,
2530: * pass the {@link AbstractButton#getModel()}, for other
2531: * controls pass a dummy (synthesized) model.
2532: * @param toIgnoreSelection
2533: * If <code>true</code>, the {@link ButtonModel#isSelected()}
2534: * will not be checked. This can be used for tracking transitions
2535: * on menu items that use <code>armed</code> state instead,
2536: * when we don't want to use different rollover themes for
2537: * selected and unselected checkbox and radio button menu items
2538: * (to preserve consistent visual appearence of highlights).
2539: * @return Fade callback for the specified component.
2540: */
2541: public static FadeTrackerCallback getFadeCallback(
2542: final JComponent component, final ButtonModel model,
2543: final boolean toIgnoreSelection,
2544: final boolean toRepaintParent,
2545: final Component componentToRepaint) {
2546: if (isScrollButton(component))
2547: return null;
2548: FadeTrackerCallback callback = new FadeTrackerAdapter() {
2549: @Override
2550: public void fadeReversed(FadeKind fadeKind,
2551: boolean isFadingIn, float fadeCycle10) {
2552: component
2553: .putClientProperty(
2554: SubstanceCoreUtilities.PREV_COMPONENT_STATE,
2555: component
2556: .getClientProperty(SubstanceCoreUtilities.NEXT_COMPONENT_STATE));
2557: component
2558: .putClientProperty(
2559: SubstanceCoreUtilities.PREV_SEL_COMPONENT_STATE,
2560: component
2561: .getClientProperty(SubstanceCoreUtilities.NEXT_SEL_COMPONENT_STATE));
2562: // String text = (component instanceof AbstractButton) ?
2563: // ((AbstractButton)
2564: // component)
2565: // .getText()
2566: // : "";
2567: // System.out.println(component.getClass().getSimpleName()
2568: // + "["
2569: // + text
2570: // + "]"
2571: // + " : "
2572: // + fadeKind.toString()
2573: // + " - tracking prev as "
2574: // + ((ComponentState) component
2575: // .getClientProperty(PREV_COMPONENT_STATE))
2576: // .name());
2577: if (!toRepaintParent) {
2578: componentToRepaint.repaint();
2579: } else {
2580: Container parent = component.getParent();
2581: if (parent != null) {
2582: parent.repaint();
2583: }
2584: }
2585: }
2586:
2587: /*
2588: * (non-Javadoc)
2589: *
2590: * @see org.jvnet.lafwidget.animation.FadeTrackerAdapter#fadeEnded(org.jvnet.lafwidget.animation.FadeKind)
2591: */
2592: @Override
2593: public void fadeEnded(FadeKind fadeKind) {
2594: // System.out.println(toIgnoreSelection);
2595: component.putClientProperty(
2596: SubstanceCoreUtilities.PREV_COMPONENT_STATE,
2597: ComponentState.getState(model, component,
2598: toIgnoreSelection));
2599: component
2600: .putClientProperty(
2601: SubstanceCoreUtilities.PREV_SEL_COMPONENT_STATE,
2602: ComponentState.getState(model,
2603: component, false));
2604: component.putClientProperty(
2605: SubstanceCoreUtilities.NEXT_COMPONENT_STATE,
2606: null);
2607: component
2608: .putClientProperty(
2609: SubstanceCoreUtilities.NEXT_SEL_COMPONENT_STATE,
2610: null);
2611: // String text = (component instanceof AbstractButton) ?
2612: // ((AbstractButton)
2613: // component)
2614: // .getText()
2615: // : "";
2616: // ComponentState prevState = (ComponentState) component
2617: // .getClientProperty(PREV_COMPONENT_STATE);
2618: // System.out.println(component.getClass().getSimpleName() + "["
2619: // + text + "]" + " : " + fadeKind.toString()
2620: // + " - tracking prev as " + prevState.name());
2621: if (!toRepaintParent) {
2622: SwingUtilities.invokeLater(new Runnable() {
2623: public void run() {
2624: if (component instanceof JMenuItem) {
2625: if (isCoveredByLightweightPopups(component)) {
2626: component
2627: .putClientProperty(
2628: SubstanceCoreUtilities.IS_COVERED_BY_LIGHTWEIGHT_POPUPS,
2629: Boolean.TRUE);
2630: } else {
2631: component
2632: .putClientProperty(
2633: SubstanceCoreUtilities.IS_COVERED_BY_LIGHTWEIGHT_POPUPS,
2634: null);
2635: }
2636: }
2637: componentToRepaint.repaint();
2638: }
2639: });
2640: } else {
2641: Container parent = component.getParent();
2642: if (parent != null) {
2643: parent.repaint();
2644: }
2645: }
2646: }
2647:
2648: @Override
2649: public void fadePerformed(FadeKind fadeKind,
2650: float fadeCycle10) {
2651: component.putClientProperty(
2652: SubstanceCoreUtilities.NEXT_COMPONENT_STATE,
2653: ComponentState.getState(model, component,
2654: toIgnoreSelection));
2655: component
2656: .putClientProperty(
2657: SubstanceCoreUtilities.NEXT_SEL_COMPONENT_STATE,
2658: ComponentState.getState(model,
2659: component, false));
2660: if (!toRepaintParent) {
2661: SwingUtilities.invokeLater(new Runnable() {
2662: public void run() {
2663: if (component instanceof JMenuItem) {
2664: if (isCoveredByLightweightPopups(component)) {
2665: component
2666: .putClientProperty(
2667: SubstanceCoreUtilities.IS_COVERED_BY_LIGHTWEIGHT_POPUPS,
2668: Boolean.TRUE);
2669: } else {
2670: component
2671: .putClientProperty(
2672: SubstanceCoreUtilities.IS_COVERED_BY_LIGHTWEIGHT_POPUPS,
2673: null);
2674: }
2675: }
2676: componentToRepaint.repaint();
2677: }
2678: });
2679: } else {
2680: Container parent = component.getParent();
2681: if (parent != null) {
2682: parent.repaint();
2683: }
2684: }
2685: }
2686:
2687: private boolean isCoveredByLightweightPopups(
2688: final Component comp) {
2689: JRootPane rootPane = SwingUtilities.getRootPane(comp);
2690: if (rootPane == null)
2691: return false;
2692: Component[] popups = rootPane.getLayeredPane()
2693: .getComponentsInLayer(JLayeredPane.POPUP_LAYER);
2694: if (popups == null)
2695: return false;
2696:
2697: // System.out.println("Has " + popups.length + " popups");
2698:
2699: // Convert the component bounds to the layered pane.
2700: // Can't use the SwingUtilities.convertRectangle() as that
2701: // eventually will try to acquire the lock on the container
2702: Rectangle compBoundsConverted = SwingUtilities
2703: .convertRectangle(comp.getParent(), comp
2704: .getBounds(), rootPane.getLayeredPane());
2705:
2706: // System.out.println("Bounds : \n\torig : " + comp.getBounds()
2707: // + "\n\ttrans:" + compBoundsConverted);
2708:
2709: int popupIndexToStartWith = getPopupParentIndexOf(comp,
2710: popups) - 1;
2711: // String txt = (comp instanceof JMenuItem) ? ((JMenuItem) comp)
2712: // .getText() : "";
2713: // System.out.println("Starting scan from popup "
2714: // + popupIndexToStartWith + " for " + txt);
2715: for (int i = popupIndexToStartWith; i >= 0; i--) {
2716: Component popup = popups[i];
2717: // System.out.println("Popup " +
2718: // popup.getClass().getName());
2719: // System.out.println("Popup bounds " + popup.getBounds());
2720: if (compBoundsConverted.intersects(popup
2721: .getBounds())) {
2722: return true;
2723: }
2724: }
2725: return false;
2726: }
2727: };
2728: return callback;
2729: }
2730:
2731: public static int getPopupParentIndexOf(Component comp,
2732: Component[] popups) {
2733: for (int i = 0; i < popups.length; i++) {
2734: Component popup = popups[i];
2735:
2736: Component currComp = comp;
2737: while (currComp != null) {
2738: if (currComp == popup) {
2739: return i;
2740: }
2741: currComp = currComp.getParent();
2742: }
2743: }
2744: return popups.length;
2745: }
2746:
2747: /**
2748: * Returns the previous state of the specified component.
2749: *
2750: * @param comp
2751: * Component.
2752: * @return The previous state of the specified component.
2753: */
2754: public static ComponentState getPrevComponentState(JComponent comp) {
2755: ComponentState result = (ComponentState) comp
2756: .getClientProperty(PREV_COMPONENT_STATE);
2757: if (result == null) {
2758: result = ComponentState.DEFAULT;
2759: }
2760: return result;
2761: }
2762:
2763: /**
2764: * Returns the previous state of the specified component.
2765: *
2766: * @param comp
2767: * Component.
2768: * @return The previous state of the specified component.
2769: */
2770: public static ComponentState getPrevSelComponentState(
2771: JComponent comp) {
2772: ComponentState result = (ComponentState) comp
2773: .getClientProperty(PREV_SEL_COMPONENT_STATE);
2774: if (result == null) {
2775: result = ComponentState.DEFAULT;
2776: }
2777: return result;
2778: }
2779:
2780: /**
2781: * Checks whether a component has the specified client property set to the
2782: * specified value.
2783: *
2784: * @param comp
2785: * Component.
2786: * @param propertyName
2787: * Client property name.
2788: * @param expectedValue
2789: * Expected value.
2790: * @param checkHierarchy
2791: * if <code>true</code>, the entire component hierarchy is
2792: * traversed.
2793: * @return <code>true</code> if the component has the specified client
2794: * property set to the specified value, <code>false</code>
2795: * otherwise.
2796: */
2797: public static boolean hasPropertySetTo(Component comp,
2798: String propertyName, boolean expectedValue,
2799: boolean checkHierarchy) {
2800: if (!checkHierarchy) {
2801: if (comp instanceof JComponent) {
2802: JComponent jcomp = (JComponent) comp;
2803: Object componentProp = jcomp
2804: .getClientProperty(propertyName);
2805: if (componentProp != null) {
2806: if (componentProp.equals(expectedValue))
2807: return true;
2808: }
2809: }
2810: } else {
2811: Component c = comp;
2812: while (c != null) {
2813: if (c instanceof JComponent) {
2814: JComponent jcomp = (JComponent) c;
2815: Object componentProp = jcomp
2816: .getClientProperty(propertyName);
2817: if (componentProp != null) {
2818: if (componentProp.equals(expectedValue))
2819: return true;
2820: }
2821: }
2822: c = c.getParent();
2823: }
2824: }
2825: Object globalProp = UIManager.get(propertyName);
2826: if (globalProp != null) {
2827: return globalProp.equals(expectedValue);
2828: }
2829: return false;
2830: }
2831:
2832: /**
2833: * Returns the resource bundle for the specified component.
2834: *
2835: * @param jcomp
2836: * Component.
2837: * @return Resource bundle for the specified component.
2838: */
2839: public static ResourceBundle getResourceBundle(JComponent jcomp) {
2840: if (LafWidgetUtilities.toIgnoreGlobalLocale(jcomp)) {
2841: return SubstanceLookAndFeel.getLabelBundle(jcomp
2842: .getLocale());
2843: } else {
2844: return SubstanceLookAndFeel.getLabelBundle();
2845: }
2846: }
2847:
2848: /**
2849: * Returns the border painter for the specified component.
2850: *
2851: * @param comp
2852: * Component.
2853: * @return Border painter for the specified component.
2854: */
2855: public static SubstanceBorderPainter getBorderPainter(Component comp) {
2856: // check the client property on component hierarchy
2857: while (comp != null) {
2858: if (comp instanceof JComponent) {
2859: Object clientProp = ((JComponent) comp)
2860: .getClientProperty(SubstanceLookAndFeel.BORDER_PAINTER_PROPERTY);
2861: if (clientProp instanceof SubstanceBorderPainter)
2862: return (SubstanceBorderPainter) clientProp;
2863: if (clientProp instanceof String) {
2864: try {
2865: return (SubstanceBorderPainter) Class.forName(
2866: (String) clientProp).newInstance();
2867: } catch (Exception exc) {
2868: }
2869: }
2870: }
2871: comp = comp.getParent();
2872: }
2873: // check property on UIManager
2874: Object globalProp = UIManager
2875: .get(SubstanceLookAndFeel.BORDER_PAINTER_PROPERTY);
2876: if (globalProp instanceof SubstanceBorderPainter)
2877: return (SubstanceBorderPainter) globalProp;
2878: if (globalProp instanceof String) {
2879: try {
2880: return (SubstanceBorderPainter) Class.forName(
2881: (String) globalProp).newInstance();
2882: } catch (Exception exc) {
2883: }
2884: }
2885: // return currently installed global border painter
2886: return SubstanceLookAndFeel.getCurrentBorderPainter();
2887: }
2888:
2889: /**
2890: * Resets the menu bars on the specified component.
2891: *
2892: * @param component
2893: * Component.
2894: */
2895: public static void resetMenuBars(Component component) {
2896: if (component instanceof JRootPane) {
2897: JRootPane jrp = (JRootPane) component;
2898: JMenuBar jmb = jrp.getJMenuBar();
2899: if (jmb != null) {
2900: MenuBarUI ui = jmb.getUI();
2901: if (ui instanceof SubstanceMenuBarUI) {
2902: Set<?> lafWidgets = ((SubstanceMenuBarUI) ui)
2903: .getLafWidgets();
2904: for (Iterator<?> it = lafWidgets.iterator(); it
2905: .hasNext();) {
2906: LafWidget lw = (LafWidget) it.next();
2907: if (lw instanceof Resettable) {
2908: ((Resettable) lw).reset();
2909: }
2910: }
2911: }
2912: }
2913: }
2914: if (component instanceof Container) {
2915: Container cont = (Container) component;
2916: for (int i = 0; i < cont.getComponentCount(); i++) {
2917: Component child = cont.getComponent(i);
2918: resetMenuBars(child);
2919: }
2920: }
2921: }
2922:
2923: /**
2924: * Returns the foreground color for the specified component.
2925: *
2926: * @param comp
2927: * Component.
2928: * @param componentId
2929: * Optional component ID. Can be used to differentiate sub-parts
2930: * of the component, such as tabs in tabbed pane, cells in list
2931: * etc.
2932: * @param theme
2933: * Component theme.
2934: * @param state
2935: * Component current state.
2936: * @param prevState
2937: * Component previous state.
2938: * @param kinds
2939: * Animation kinds to consult for computing the foreground color.
2940: * @return Foreground color.
2941: */
2942: public static Color getInterpolatedForegroundColor(Component comp,
2943: Comparable<?> componentId, SubstanceTheme theme,
2944: ComponentState state, ComponentState prevState,
2945: FadeKind... kinds) {
2946: ColorScheme colorScheme = theme.getColorScheme();
2947: ColorScheme colorScheme2 = colorScheme;
2948: float cyclePos = state.getCycleCount();
2949:
2950: FadeState fadeState = SubstanceFadeUtilities.getFadeState(comp,
2951: componentId, kinds);
2952: if (fadeState != null) {
2953: colorScheme = SubstanceThemeUtilities.getTheme(comp, state)
2954: .getColorScheme();
2955: colorScheme2 = SubstanceThemeUtilities.getTheme(comp,
2956: prevState).getColorScheme();
2957: cyclePos = fadeState.getFadePosition();
2958: if (!fadeState.isFadingIn())
2959: cyclePos = 10 - cyclePos;
2960: }
2961:
2962: Color c1 = colorScheme.getForegroundColor();
2963: Color c2 = colorScheme2.getForegroundColor();
2964:
2965: // // special case for components in default state on menu bar
2966: // if (comp.getParent() instanceof JMenuBar) {
2967: // Color menuBarForegr = UIManager.getColor("MenuBar.foreground");
2968: // if (SubstanceCoreUtilities.hasColorization(comp)) {
2969: // Color fore = comp.getForeground();
2970: // if (!(fore instanceof UIResource)) {
2971: // double colorizationFactor = SubstanceCoreUtilities
2972: // .getColorizationFactor(comp);
2973: // if (!comp.isEnabled())
2974: // colorizationFactor /= 2.0;
2975: // Color toShiftTo = SubstanceColorUtilities
2976: // .deriveByBrightness(fore, menuBarForegr);
2977: // menuBarForegr = SubstanceColorUtilities
2978: // .getInterpolatedColor(toShiftTo, menuBarForegr,
2979: // colorizationFactor);
2980: // }
2981: // }
2982: // if (state == ComponentState.DEFAULT)
2983: // c1 = menuBarForegr;
2984: // if (prevState == ComponentState.DEFAULT)
2985: // c2 = menuBarForegr;
2986: // }
2987:
2988: return SubstanceColorUtilities.getInterpolatedColor(c1, c2,
2989: cyclePos / 10.);
2990: }
2991:
2992: /**
2993: * Paints text with drop shadow.
2994: *
2995: * @param c
2996: * Component.
2997: * @param g
2998: * Graphics context.
2999: * @param foregroundColor
3000: * Foreground color.
3001: * @param text
3002: * Text to paint.
3003: * @param width
3004: * Text rectangle width.
3005: * @param height
3006: * Text rectangle height.
3007: * @param xOffset
3008: * Text rectangle X offset.
3009: * @param yOffset
3010: * Text rectangle Y offset.
3011: */
3012: public static void paintTextWithDropShadow(final JComponent c,
3013: Graphics g, Color foregroundColor, String text, int width,
3014: int height, int xOffset, int yOffset) {
3015: Graphics2D graphics = (Graphics2D) g.create();
3016:
3017: // blur the text shadow
3018: BufferedImage blurred = SubstanceCoreUtilities.getBlankImage(
3019: width, height);
3020: Graphics2D gBlurred = (Graphics2D) blurred.getGraphics();
3021: gBlurred.setFont(graphics.getFont());
3022: gBlurred.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
3023: RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
3024: // Color neg =
3025: // SubstanceColorUtilities.getNegativeColor(foregroundColor);
3026: final float luminFactor = SubstanceColorUtilities
3027: .getColorStrength(foregroundColor);
3028: gBlurred.setColor(SubstanceColorUtilities
3029: .getNegativeColor(foregroundColor));
3030: ConvolveOp convolve = new ConvolveOp(new Kernel(3, 3,
3031: new float[] { .0f, .05f, .1f, .05f, .0f, .1f, .1f, .1f,
3032: .1f }), ConvolveOp.EDGE_NO_OP, null);
3033: gBlurred.drawString(text, xOffset + 1, yOffset + 1);
3034: blurred = convolve.filter(blurred, null);
3035:
3036: SubstanceTextPainter textPainter = SubstanceLookAndFeel
3037: .getCurrentTextPainter();
3038: if (textPainter.needsBackgroundImage()) {
3039: final BufferedImage finalBlurred = blurred;
3040: textPainter
3041: .attachCallback(new SubstanceTextPainter.BackgroundPaintingCallback() {
3042: public void paintBackground(Graphics g) {
3043: Graphics2D graphics = (Graphics2D) g
3044: .create();
3045: graphics.setComposite(TransitionLayout
3046: .getAlphaComposite(c, luminFactor,
3047: g));
3048: graphics
3049: .drawImage(finalBlurred, 0, 0, null);
3050: graphics.setComposite(TransitionLayout
3051: .getAlphaComposite(c, g));
3052: graphics.dispose();
3053: }
3054: });
3055: } else {
3056: graphics.setComposite(TransitionLayout.getAlphaComposite(c,
3057: luminFactor, g));
3058: graphics.drawImage(blurred, 0, 0, null);
3059: graphics.setComposite(TransitionLayout.getAlphaComposite(c,
3060: g));
3061: }
3062:
3063: // graphics.setColor(foregroundColor);
3064: // RenderingUtils.installDesktopHints(graphics);
3065: FontMetrics fm = graphics.getFontMetrics();
3066: textPainter.attachText(c, new Rectangle(xOffset, yOffset
3067: - fm.getAscent(), width - xOffset, fm.getHeight()),
3068: text, -1, graphics.getFont(), foregroundColor, graphics
3069: .getClipBounds());
3070: // textPainter.paint(graphics);
3071: // graphics.drawString(text, xOffset, yOffset);
3072:
3073: graphics.dispose();
3074: }
3075:
3076: /**
3077: * Provides workaround for <a
3078: * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6576507">bug
3079: * 6576507</a>. This is especially relevant for skins that use translucent
3080: * themes, such as {@link AutumnSkin} and {@link MagmaSkin}.
3081: *
3082: * @param graphics
3083: * Graphics context.
3084: */
3085: public static void workaroundBug6576507(Graphics graphics) {
3086: Font font = graphics.getFont();
3087: font = font.deriveFont(font.getStyle(), font.getSize2D());
3088: graphics.setFont(font);
3089: }
3090:
3091: /**
3092: * Returns the component hierarchy.
3093: *
3094: * @param comp
3095: * Component.
3096: * @return Component hierarchy string.
3097: */
3098: public static String getHierarchy(Component comp) {
3099: StringBuffer buffer = new StringBuffer();
3100: getHierarchy(comp, buffer, 0);
3101: while (true) {
3102: if (comp instanceof Window) {
3103: Window w = (Window) comp;
3104: comp = w.getOwner();
3105: if (comp != null) {
3106: buffer.append("Owner --->\n");
3107: getHierarchy(comp, buffer, 0);
3108: }
3109: } else {
3110: break;
3111: }
3112: }
3113: return buffer.toString();
3114: }
3115:
3116: /**
3117: * Computes the component hierarchy.
3118: *
3119: * @param comp
3120: * Component.
3121: * @param buffer
3122: * Hierarchy representation buffer.
3123: * @param level
3124: * Hierarchy level.
3125: */
3126: public static void getHierarchy(Component comp,
3127: StringBuffer buffer, int level) {
3128: for (int i = 0; i < level; i++)
3129: buffer.append(" ");
3130: String name = comp.getName();
3131: if (comp instanceof Dialog)
3132: name = ((Dialog) comp).getTitle();
3133: if (comp instanceof Frame)
3134: name = ((Frame) comp).getTitle();
3135: buffer.append(comp.getClass().getName() + "[" + name + "]\n");
3136: if (comp instanceof Container) {
3137: Container cont = (Container) comp;
3138: for (int i = 0; i < cont.getComponentCount(); i++)
3139: getHierarchy(cont.getComponent(i), buffer, level + 1);
3140: }
3141: }
3142:
3143: /**
3144: * Returns the title pane of the specified root pane.
3145: *
3146: * @param rootPane
3147: * Root pane.
3148: * @return The title pane of the specified root pane.
3149: */
3150: public static JComponent getTitlePane(JRootPane rootPane) {
3151: JInternalFrame jif = (JInternalFrame) SwingUtilities
3152: .getAncestorOfClass(JInternalFrame.class, rootPane);
3153: if (jif != null) {
3154: SubstanceInternalFrameUI ui = (SubstanceInternalFrameUI) jif
3155: .getUI();
3156: return ui.getTitlePane();
3157: }
3158: SubstanceRootPaneUI ui = (SubstanceRootPaneUI) rootPane.getUI();
3159: return ui.getTitlePane();
3160: }
3161:
3162: public static void resetCaches() {
3163: SubstanceIconFactory.reset();
3164: ButtonBackgroundDelegate.reset();
3165: SubstanceCheckBoxUI.reset();
3166: SubstanceProgressBarUI.reset();
3167: SubstanceRadioButtonUI.reset();
3168: SubstanceScrollBarUI.reset();
3169: SubstanceTabbedPaneUI.reset();
3170: // SubstanceComboBoxUI.reset();
3171: SubstanceScrollBarUI.reset();
3172: ClassicDecorationPainter.reset();
3173: }
3174:
3175: /**
3176: * Returns the arrow icon.
3177: *
3178: * @param comp
3179: * Component.
3180: * @param button
3181: * Button.
3182: * @param orientation
3183: * Arrow orientation.
3184: * @return Arrow icon.
3185: */
3186: public static Icon getArrowIcon(final Component comp,
3187: final AbstractButton button, final int orientation) {
3188: Icon result = new TransitionAwareIcon(button,
3189: new TransitionAwareIcon.Delegate() {
3190: public Icon getThemeIcon(SubstanceTheme theme) {
3191: int fontSize = SubstanceSizeUtils
3192: .getComponentFontSize(comp);
3193: return SubstanceImageCreator
3194: .getArrowIcon(
3195: SubstanceSizeUtils
3196: .getArrowIconWidth(fontSize),
3197: (orientation == SwingConstants.CENTER) ? 2 * SubstanceSizeUtils
3198: .getArrowIconHeight(fontSize)
3199: : SubstanceSizeUtils
3200: .getArrowIconHeight(fontSize),
3201: SubstanceSizeUtils
3202: .getArrowStrokeWidth(fontSize),
3203: orientation, theme);
3204: }
3205: });
3206: return result;
3207: }
3208:
3209: /**
3210: * Returns the arrow icon.
3211: *
3212: * @param comp
3213: * Component.
3214: * @param button
3215: * Button.
3216: * @param orientation
3217: * Arrow orientation.
3218: * @return Arrow icon.
3219: */
3220: public static Icon getDoubleArrowIcon(final Component comp,
3221: final AbstractButton button, final int orientation) {
3222: Icon result = new TransitionAwareIcon(button,
3223: new TransitionAwareIcon.Delegate() {
3224: public Icon getThemeIcon(SubstanceTheme theme) {
3225: int fontSize = SubstanceSizeUtils
3226: .getComponentFontSize(comp);
3227: return SubstanceImageCreator
3228: .getDoubleArrowIcon(
3229: SubstanceSizeUtils
3230: .getArrowIconWidth(fontSize),
3231: SubstanceSizeUtils
3232: .getArrowIconHeight(fontSize) + 2,
3233: SubstanceSizeUtils
3234: .getDoubleArrowStrokeWidth(fontSize),
3235: orientation, theme);
3236: }
3237: });
3238: return result;
3239: }
3240:
3241: public static double getColorizationFactor(Component c) {
3242: JPopupMenu popupMenu = null;
3243: while (c != null) {
3244: if (c instanceof JComponent) {
3245: JComponent jcomp = (JComponent) c;
3246: Object compProp = jcomp
3247: .getClientProperty(SubstanceLookAndFeel.COLORIZATION_FACTOR);
3248: if (compProp instanceof Double)
3249: return (Double) compProp;
3250: }
3251: if (c instanceof JPopupMenu) {
3252: popupMenu = (JPopupMenu) c;
3253: }
3254: c = c.getParent();
3255: }
3256:
3257: if (popupMenu != null) {
3258: Component invoker = popupMenu.getInvoker();
3259: if (popupMenu != invoker)
3260: return getColorizationFactor(popupMenu.getInvoker());
3261: }
3262:
3263: Object globalProp = UIManager
3264: .get(SubstanceLookAndFeel.COLORIZATION_FACTOR);
3265: if (globalProp instanceof Double) {
3266: return (Double) globalProp;
3267: }
3268:
3269: return 0.0;
3270: }
3271:
3272: public static boolean hasColorization(Component c) {
3273: JPopupMenu popupMenu = null;
3274: while (c != null) {
3275: if (c instanceof JComponent) {
3276: JComponent jcomp = (JComponent) c;
3277: Object compProp = jcomp
3278: .getClientProperty(SubstanceLookAndFeel.COLORIZATION_FACTOR);
3279: if (compProp instanceof Double)
3280: return true;
3281: }
3282: if (c instanceof JPopupMenu) {
3283: popupMenu = (JPopupMenu) c;
3284: }
3285: c = c.getParent();
3286: }
3287:
3288: if (popupMenu != null) {
3289: Component invoker = popupMenu.getInvoker();
3290: if (popupMenu != invoker)
3291: return hasColorization(popupMenu.getInvoker());
3292: }
3293:
3294: Object globalProp = UIManager
3295: .get(SubstanceLookAndFeel.COLORIZATION_FACTOR);
3296: if (globalProp instanceof Double) {
3297: return true;
3298: }
3299: return false;
3300: }
3301:
3302: public static Color getBackgroundFillColor(Component component) {
3303: // special case - sliders, check boxes and radio buttons. For this,
3304: // switch to component parent
3305: boolean isColorized = hasColorization(component);
3306: if (isColorized
3307: && ((component instanceof JCheckBox)
3308: || (component instanceof JRadioButton) || (component instanceof JSlider))) {
3309: component = component.getParent();
3310: } else {
3311: // Fix for 325 - respect the opacity setting of the text
3312: // component
3313: if (component instanceof JTextComponent
3314: && !component.isOpaque())
3315: component = component.getParent();
3316: }
3317:
3318: Color backgr = component.getBackground();
3319: boolean isBackgroundUiResource = backgr instanceof UIResource;
3320:
3321: if (!isBackgroundUiResource) {
3322: // custom background color set on component
3323: if (isColorized) {
3324: // colorized - shift the theme
3325: SubstanceTheme theme = SubstanceThemeUtilities
3326: .getTheme(
3327: component,
3328: component.isEnabled() ? ComponentState.DEFAULT
3329: : ComponentState.DISABLED_UNSELECTED);
3330:
3331: // double colorizationFactor = getColorizationFactor(component);
3332: // if (!component.isEnabled())
3333: // colorizationFactor /= 2.0;
3334: // theme = SubstanceShiftTheme.getShiftedTheme(theme, backgr,
3335: // colorizationFactor, null, 0.0);
3336: backgr = theme.getBackgroundColor();
3337: } else {
3338: if (!component.isEnabled()) {
3339: SubstanceTheme theme = SubstanceThemeUtilities
3340: .getTheme(component,
3341: ComponentState.DISABLED_UNSELECTED);
3342: // theme = SubstanceShiftTheme.getShiftedTheme(theme,
3343: // backgr,
3344: // 0.5, null, 0.0);
3345: backgr = theme.getBackgroundColor();
3346: }
3347: }
3348: } else {
3349: DecorationAreaType decorationType = SubstanceDecorationUtilities
3350: .getDecorationType(component);
3351: if (decorationType != null) {
3352: // Component is in the decoration area. Check if the current
3353: // theme is actively decorating it
3354: SubstanceTheme theme = SubstanceThemeUtilities
3355: .getNonColorizedTheme(component, true);
3356: if (theme.toUseDecorationPainter(decorationType)) {
3357: ComponentState state = component.isEnabled() ? ComponentState.DEFAULT
3358: : ComponentState.DISABLED_UNSELECTED;
3359: if (component instanceof JTextComponent) {
3360: if (!((JTextComponent) component).isEditable()) {
3361: state = ComponentState.DISABLED_UNSELECTED;
3362: }
3363: }
3364: backgr = getDefaultBackgroundColor(component
3365: .getClass(), SubstanceThemeUtilities
3366: .getTheme(component, state));
3367: }
3368: }
3369: }
3370: return backgr;
3371: }
3372:
3373: public static ColorUIResource getDefaultBackgroundColor(
3374: Class componentClass, SubstanceTheme componentTheme) {
3375: boolean useLightBackgroundColor = (JTextComponent.class
3376: .isAssignableFrom(componentClass))
3377: || (JComboBox.class.isAssignableFrom(componentClass))
3378: || (JSpinner.class.isAssignableFrom(componentClass));
3379: if (useLightBackgroundColor)
3380: return new ColorUIResource(componentTheme
3381: .getLightBackgroundColor());
3382: return new ColorUIResource(componentTheme.getBackgroundColor());
3383: }
3384:
3385: public static Color getStripedBackground(JComponent component,
3386: int index) {
3387: Color backgr = getBackgroundFillColor(component);
3388: if (backgr == null)
3389: return null;
3390:
3391: if (index % 2 == 0)
3392: return backgr;
3393: int r = backgr.getRed();
3394: int g = backgr.getGreen();
3395: int b = backgr.getBlue();
3396: double coef = 0.96;
3397: if (!component.isEnabled())
3398: coef = 1.0 - (1.0 - coef) / 2.0;
3399: Color darkerColor = new ColorUIResource((int) (coef * r),
3400: (int) (coef * g), (int) (coef * b));
3401:
3402: return darkerColor;
3403: }
3404:
3405: public static void applyStripedBackground(JComponent component,
3406: int index, JComponent renderer) {
3407: Color backgr = getStripedBackground(component, index);
3408: if (backgr == null)
3409: return;
3410:
3411: // System.out.println(index + ":" + backgr);
3412: renderer.setBackground(backgr);
3413: }
3414:
3415: public static void paintTextCompBackground(Graphics g,
3416: JComponent comp) {
3417: Graphics2D g2d = (Graphics2D) g.create();
3418: Color backgr = SubstanceCoreUtilities
3419: .getBackgroundFillColor(comp);
3420:
3421: int borderDelta = (int) Math.floor(SubstanceSizeUtils
3422: .getBorderStrokeWidth(SubstanceSizeUtils
3423: .getComponentFontSize(comp)) / 2.0);
3424: Border compBorder = comp.getBorder();
3425: boolean isSubstanceBorder = compBorder instanceof SubstanceBorder;
3426: if (compBorder instanceof CompoundBorder) {
3427: isSubstanceBorder = isSubstanceBorder
3428: || (((CompoundBorder) compBorder)
3429: .getOutsideBorder() instanceof SubstanceBorder);
3430: }
3431: Shape contour = isSubstanceBorder ? BaseButtonShaper
3432: .getBaseOutline(
3433: comp.getWidth(),
3434: comp.getHeight(),
3435: SubstanceSizeUtils
3436: .getClassicButtonCornerRadius(SubstanceSizeUtils
3437: .getComponentFontSize(comp)),
3438: null, borderDelta)
3439: : new Rectangle(0, 0, comp.getWidth(), comp.getHeight());
3440:
3441: g2d.setColor(backgr);
3442: g2d.fill(contour);
3443:
3444: if (SubstanceCoreUtilities.toBleedWatermark(comp)
3445: || !comp.isOpaque()) {
3446: // g2d.clip(contour);
3447: SubstanceLookAndFeel.getCurrentWatermark()
3448: .drawWatermarkImage(g2d, comp, 0, 0,
3449: comp.getWidth(), comp.getHeight());
3450: }
3451:
3452: g2d.dispose();
3453: }
3454:
3455: public static Container getSpecialBackgroundFillContainer(
3456: JComponent comp) {
3457: Component c = comp;
3458: while (c != null) {
3459: if (c instanceof JComponent) {
3460: JComponent jc = (JComponent) c;
3461: if (Boolean.TRUE.equals(jc
3462: .getClientProperty(HAS_CUSTOM_BACKGROUND_FILL)))
3463: return jc;
3464: }
3465: c = c.getParent();
3466: }
3467: return null;
3468: }
3469:
3470: public static void paintTextComponent(Graphics g,
3471: final JTextComponent textComponent, View rootView,
3472: Rectangle visibleEditorRect) {
3473: final SubstanceTextPainter textPainter = SubstanceLookAndFeel
3474: .getCurrentTextPainter();
3475:
3476: Graphics2D g2d = (Graphics2D) g.create();
3477: if (textComponent.isOpaque()
3478: || textPainter.needsBackgroundImage()) {
3479: SubstanceFillBackgroundDelegate.GLOBAL_INSTANCE.update(g2d,
3480: textComponent, false);
3481: }
3482:
3483: ComponentState currState = textComponent.isEnabled() ? ComponentState.DEFAULT
3484: : ComponentState.DISABLED_UNSELECTED;
3485: float alpha = SubstanceThemeUtilities.getTheme(textComponent)
3486: .getThemeAlpha(textComponent, currState);
3487: g2d.setComposite(TransitionLayout.getAlphaComposite(
3488: textComponent, alpha, g));
3489:
3490: SubstanceTextPainter.BackgroundPaintingCallback callback = new SubstanceTextPainter.BackgroundPaintingCallback() {
3491: public void paintBackground(Graphics g) {
3492: Highlighter highlighter = textComponent
3493: .getHighlighter();
3494:
3495: // paint the background
3496: // if (textComponent.isOpaque()) {
3497: if (textPainter.needsBackgroundImage())
3498: paintTextCompBackground(g, textComponent);
3499: // }
3500:
3501: // paint the highlights
3502: if (highlighter != null) {
3503: highlighter.paint(g);
3504: }
3505: }
3506: };
3507:
3508: textPainter.init(textComponent, null, true);
3509: if (textPainter.needsBackgroundImage()) {
3510: // Fix for 325 - respect the opacity setting of the text
3511: // component
3512: textPainter.setBackgroundFill(textComponent,
3513: SubstanceCoreUtilities
3514: .getBackgroundFillColor(textComponent),
3515: false, 0, 0);
3516: textPainter.attachCallback(callback);
3517: } else {
3518: callback.paintBackground(g2d);
3519: }
3520:
3521: // paint the view hierarchy
3522: if (visibleEditorRect != null) {
3523: rootView.paint(g2d, visibleEditorRect);
3524: }
3525: textPainter.renderSurface(g2d);
3526:
3527: // paint the caret
3528: Caret caret = textComponent.getCaret();
3529: if (caret != null) {
3530: caret.paint(g2d);
3531: }
3532:
3533: g2d.dispose();
3534: }
3535: }
|