/*
* Copyright (c) 2005-2016 Substance Kirill Grouchnikov. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* o Neither the name of Substance Kirill Grouchnikov nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.pushingpixels.substance.internal.painter;
import java.awt.*;
import javax.swing.*;
import org.pushingpixels.lafwidget.LafWidgetUtilities;
import org.pushingpixels.substance.api.*;
import org.pushingpixels.substance.api.painter.decoration.SubstanceDecorationPainter;
import org.pushingpixels.substance.api.watermark.SubstanceWatermark;
import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities;
/**
* Contains utility methods related to decoration painters. This class is for
* internal use only.
*
* @author Kirill Grouchnikov
*/
public class DecorationPainterUtils {
/**
* Client property for marking a component with an instance of
* {@link DecorationAreaType} enum.
*/
private static final String DECORATION_AREA_TYPE =
"substancelaf.internal.painter.decorationAreaType";
public static final String POPUP_INVOKER_LINK =
"substancelaf.internal.popupInvokerLink";
public static interface PopupInvokerLink {
public JComponent getPopupInvoker();
}
/**
* Sets the decoration type of the specified component.
*
* @param comp
* Component.
* @param type
* Decoration type of the component and its children.
*/
public static void setDecorationType(JComponent comp,
DecorationAreaType type) {
comp.putClientProperty(DECORATION_AREA_TYPE, type);
}
/**
* Clears the client properties related to the decoration area type.
*
* @param comp
* Component.
*/
public static void clearDecorationType(JComponent comp) {
if (comp != null) {
comp.putClientProperty(DECORATION_AREA_TYPE, null);
}
}
/**
* Returns the decoration area type of the specified component. The
* component and its ancestor hierarchy are scanned for the registered
* decoration area type. If
* {@link #setDecorationType(JComponent, DecorationAreaType)} has been
* called on the specified component, the matching decoration type is
* returned. Otherwise, the component hierarchy is scanned to find the
* closest ancestor that was passed to
* {@link #setDecorationType(JComponent, DecorationAreaType)} - and its
* decoration type is returned. If neither the component, nor any one of its
* parent components has been passed to the setter method,
* {@link DecorationAreaType#NONE} is returned.
*
* @param comp
* Component.
* @return Decoration area type of the component.
*/
public static DecorationAreaType getDecorationType(Component comp) {
JPopupMenu popupMenu = null;
JComponent popupInvoker = null;
Component c = comp;
while (c != null) {
if (c instanceof JComponent) {
JComponent jc = (JComponent) c;
Object prop = jc.getClientProperty(DECORATION_AREA_TYPE);
if (prop instanceof DecorationAreaType) {
return (DecorationAreaType) prop;
}
Object invokerProp = jc.getClientProperty(POPUP_INVOKER_LINK);
if (invokerProp instanceof PopupInvokerLink) {
popupInvoker = ((PopupInvokerLink) invokerProp).getPopupInvoker();
}
}
if (c instanceof JPopupMenu) {
popupMenu = (JPopupMenu) c;
}
c = c.getParent();
}
if (popupMenu != null) {
Component invoker = popupMenu.getInvoker();
if (popupMenu != invoker) {
return getDecorationType(popupMenu.getInvoker());
}
}
if (popupInvoker != null) {
return getDecorationType(popupInvoker);
}
return DecorationAreaType.NONE;
}
/**
* Returns the immediate decoration area type of the specified component.
* The component is checked for the registered decoration area type. If
* {@link #setDecorationType(JComponent, DecorationAreaType, boolean)} was
* not called on this component, this method returns <code>null</code>.
*
* @param comp
* Component.
* @return Immediate decoration area type of the component.
*/
public static DecorationAreaType getImmediateDecorationType(Component comp) {
Component c = comp;
if (c instanceof JComponent) {
JComponent jc = (JComponent) c;
Object prop = jc.getClientProperty(DECORATION_AREA_TYPE);
if (prop instanceof DecorationAreaType)
return (DecorationAreaType) prop;
}
return null;
}
/**
* Paints the decoration background on the specified component. The
* decoration background is not painted when the <code>force</code>
* parameter is <code>false</code> and at least one of the following
* conditions holds:
* <ul>
* <li>The component is in a cell renderer.</li>
* <li>The component is not showing on the screen.</li>
* <li>The component is in the preview mode.</li>
* </ul>
*
* @param g
* Graphics context.
* @param c
* Component.
* @param force
* If <code>true</code>, the painting of decoration background is
* enforced.
*/
public static void paintDecorationBackground(Graphics g, Component c,
boolean force) {
DecorationAreaType decorationType = SubstanceLookAndFeel
.getDecorationType(c);
paintDecorationBackground(g, c, decorationType, force);
}
/**
* Paints the decoration background on the specified component. See comments
* on {@link #paintDecorationBackground(Graphics, Component, boolean)} for
* the cases when the decoration background painting is skipped.
*
* @param g
* Graphics context.
* @param c
* Component.
* @param decorationType
* Decoration area type of the component.
* @param force
* If <code>true</code>, the painting of decoration background is
* enforced. #see
* {@link #paintDecorationBackground(Graphics, Component, boolean)}
*/
private static void paintDecorationBackground(Graphics g, Component c,
DecorationAreaType decorationType, boolean force) {
// System.out.println("Painting " + c.getClass().getSimpleName());
boolean isInCellRenderer = (SwingUtilities.getAncestorOfClass(
CellRendererPane.class, c) != null);
boolean isPreviewMode = false;
if (c instanceof JComponent) {
isPreviewMode = (Boolean.TRUE.equals(((JComponent) c)
.getClientProperty(LafWidgetUtilities.PREVIEW_MODE)));
}
if (!force && !isPreviewMode && !c.isShowing() && !isInCellRenderer) {
return;
}
if ((c.getHeight() == 0) || (c.getWidth() == 0))
return;
SubstanceSkin skin = SubstanceCoreUtilities.getSkin(c);
SubstanceDecorationPainter painter = skin.getDecorationPainter();
Graphics2D g2d = (Graphics2D) g.create();
painter.paintDecorationArea(g2d, c, decorationType, c.getWidth(), c
.getHeight(), skin);
SubstanceWatermark watermark = SubstanceCoreUtilities.getSkin(c)
.getWatermark();
if ((watermark != null) && !isPreviewMode && !isInCellRenderer
&& c.isShowing() && SubstanceCoreUtilities.toDrawWatermark(c)) {
// paint the watermark over the component
watermark.drawWatermarkImage(g2d, c, 0, 0, c.getWidth(), c
.getHeight());
// paint the background second time with 50%
// translucency, making the watermark' bleed' through.
g2d.setComposite(LafWidgetUtilities.getAlphaComposite(c, 0.5f, g));
painter.paintDecorationArea(g2d, c, decorationType, c.getWidth(), c
.getHeight(), skin);
}
g2d.dispose();
}
}