com.alee.painter.PainterSupport Maven / Gradle / Ivy
Show all versions of weblaf-ui Show documentation
/*
* This file is part of WebLookAndFeel library.
*
* WebLookAndFeel library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* WebLookAndFeel library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WebLookAndFeel library. If not, see .
*/
package com.alee.painter;
import com.alee.laf.WebLookAndFeel;
import com.alee.managers.style.Bounds;
import com.alee.managers.style.PainterShapeProvider;
import com.alee.managers.style.StyleManager;
import com.alee.managers.style.data.ComponentStyle;
import com.alee.painter.decoration.AbstractDecorationPainter;
import com.alee.painter.decoration.AbstractSectionDecorationPainter;
import com.alee.utils.LafUtils;
import com.alee.utils.ReflectUtils;
import com.alee.utils.SwingUtils;
import com.alee.utils.swing.BorderMethods;
import com.alee.utils.swing.DataRunnable;
import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import java.awt.*;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.WeakHashMap;
/**
* This special class provides basic methods to link painter with components.
*
* @author Mikle Garin
*/
public final class PainterSupport
{
/**
* Installed painters map.
*/
private static final Map> installedPainters =
new WeakHashMap> ();
/**
* Returns the specified painter if it can be assigned to proper painter type.
* Otherwise returns newly created adapter painter that wraps the specified painter.
* Used by component UIs to adapt general-type painters for their specific-type needs.
*
* @param painter processed painter
* @param properClass proper painter class
* @param adapterClass adapter painter class
* @param proper painter type
* @return specified painter if it can be assigned to proper painter type, new painter adapter if it cannot be assigned
*/
public static T getProperPainter ( final Painter painter, final Class properClass,
final Class extends T> adapterClass )
{
return painter == null ? null : ReflectUtils.isAssignable ( properClass, painter.getClass () ) ? ( T ) painter :
( T ) ReflectUtils.createInstanceSafely ( adapterClass, painter );
}
/**
* Returns either the specified painter if it is not an adapted painter or the adapted painter.
* Used by component UIs to retrieve painters adapted for their specific needs.
*
* @param painter painter to process
* @param desired painter type
* @return either the specified painter if it is not an adapted painter or the adapted painter
*/
public static T getAdaptedPainter ( final Painter painter )
{
return ( T ) ( painter != null && painter instanceof AdaptivePainter ? ( ( AdaptivePainter ) painter ).getPainter () : painter );
}
/**
* Sets component painter.
* {@code null} can be provided to uninstall painter.
*
* @param component component painter should be installed into
* @param setter runnable that updates actual painter field
* @param oldPainter previously installed painter
* @param painter painter to install
* @param specificClass specific painter class
* @param specificAdapterClass specific painter adapter class
* @param specific painter class type
*/
public static
void setPainter ( final JComponent component, final DataRunnable
setter,
final P oldPainter, final Painter painter, final Class
specificClass,
final Class extends P> specificAdapterClass )
{
// Creating adaptive painter if required
final P properPainter = getProperPainter ( painter, specificClass, specificAdapterClass );
// Properly updating painter
uninstallPainter ( component, oldPainter );
setter.run ( properPainter );
installPainter ( component, properPainter );
// Firing painter change event
SwingUtils.firePropertyChanged ( component, WebLookAndFeel.PAINTER_PROPERTY, oldPainter, properPainter );
}
/**
* Installs painter into the specified component.
* It is highly recommended to call this method only from EDT.
*
* @param component component painter is applied to
* @param painter painter to install
*/
public static void installPainter ( final JComponent component, final Painter painter )
{
// Simply ignore this call if empty painter is set or component doesn't exist
if ( component == null || painter == null )
{
return;
}
// Installing painter
Map listeners = installedPainters.get ( component );
if ( listeners == null )
{
listeners = new WeakHashMap ( 1 );
installedPainters.put ( component, listeners );
}
if ( !installedPainters.containsKey ( painter ) )
{
// Installing painter
painter.install ( component, LafUtils.getUI ( component ) );
// Applying initial component settings
final Boolean opaque = painter.isOpaque ();
if ( opaque != null )
{
LookAndFeel.installProperty ( component, WebLookAndFeel.OPAQUE_PROPERTY, opaque ? Boolean.TRUE : Boolean.FALSE );
}
// Creating weak references to use them inside the listener
// Otherwise we will force it to keep strong reference to component and painter if we use them directly
final WeakReference c = new WeakReference ( component );
final WeakReference p = new WeakReference ( painter );
// Adding painter listener
final PainterListener listener = new PainterListener ()
{
@Override
public void repaint ()
{
// Forcing component to be repainted
c.get ().repaint ();
}
@Override
public void repaint ( final int x, final int y, final int width, final int height )
{
// Forcing component to be repainted
c.get ().repaint ( x, y, width, height );
}
@Override
public void revalidate ()
{
// Forcing layout updates
c.get ().revalidate ();
}
@Override
public void updateOpacity ()
{
// Updating component opacity according to painter
final Painter painter = p.get ();
if ( painter != null )
{
final Boolean opaque = painter.isOpaque ();
if ( opaque != null )
{
c.get ().setOpaque ( opaque );
}
}
}
};
painter.addPainterListener ( listener );
listeners.put ( painter, listener );
}
}
/**
* Uninstalls painter from the specified component.
* It is highly recommended to call this method only from EDT.
*
* @param component component painter is uninstalled from
* @param painter painter to uninstall
*/
public static void uninstallPainter ( final JComponent component, final Painter painter )
{
if ( component == null || painter == null )
{
return;
}
final Map listeners = installedPainters.get ( component );
if ( listeners != null )
{
// Uninstalling painter
painter.uninstall ( component, LafUtils.getUI ( component ) );
// Removing painter listener
listeners.remove ( painter );
}
}
/**
* Installs section painter into the specified component.
* It is highly recommended to call this method only from EDT.
*
* @param origin origin painter
* @param painter section painter to install
* @param old previously installed section painter
* @param component component painter should be installed into
* @param ui component UI
* @param section painter type
* @return installed sub-painter
*/
public static T installSectionPainter ( final Painter origin, final T painter, final Painter old,
final JComponent component, final ComponentUI ui )
{
if ( component != null && ui != null )
{
if ( old != null )
{
old.uninstall ( component, ui );
if ( old instanceof AbstractSectionDecorationPainter )
{
( ( AbstractSectionDecorationPainter ) old ).setOrigin ( null );
}
}
if ( painter != null )
{
if ( painter instanceof AbstractSectionDecorationPainter )
{
( ( AbstractSectionDecorationPainter ) painter ).setOrigin ( origin );
}
painter.install ( component, ui );
}
}
return painter;
}
/**
* Uninstalls section painter from the specified component.
* It is highly recommended to call this method only from EDT.
*
* @param painter section painter to uninstall
* @param component component painter should be uninstalled from
* @param ui component UI
* @param section painter type
* @return {@code null}
*/
public static T uninstallSectionPainter ( final T painter, final JComponent component, final ComponentUI ui )
{
if ( component != null && ui != null )
{
if ( painter != null )
{
painter.uninstall ( component, ui );
if ( painter instanceof AbstractSectionDecorationPainter )
{
( ( AbstractSectionDecorationPainter ) painter ).clearOrigin ();
}
}
}
return null;
}
/**
* Force painter to update border of the component it is attached to.
*
* @param painter painter to ask for border update
*/
public static void updateBorder ( final Painter painter )
{
if ( painter instanceof BorderMethods )
{
( ( BorderMethods ) painter ).updateBorder ();
}
}
/**
* Returns component shape according to its painter.
*
* @param component component painter is applied to
* @param painter component painter
* @return component shape according to its painter
*/
public static Shape getShape ( final JComponent component, final Painter painter )
{
if ( painter != null && painter instanceof PainterShapeProvider )
{
return ( ( PainterShapeProvider ) painter ).provideShape ( component, Bounds.margin.of ( component ) );
}
else
{
return Bounds.margin.of ( component );
}
}
/**
* Returns component preferred size or {@code null} if there is no preferred size.
*
* @param component component painter is applied to
* @param painter component painter
* @return component preferred size or {@code null} if there is no preferred size
*/
public static Dimension getPreferredSize ( final JComponent component, final Painter painter )
{
return getPreferredSize ( component, null, painter );
}
/**
* Returns component preferred size or {@code null} if there is no preferred size.
* todo Probably get rid of this method and force painters to determine full preferred size?
*
* @param component component painter is applied to
* @param preferred component preferred size
* @param painter component painter
* @return component preferred size or {@code null} if there is no preferred size
*/
public static Dimension getPreferredSize ( final JComponent component, final Dimension preferred, final Painter painter )
{
return getPreferredSize ( component, preferred, painter, false );
}
/**
* Returns component preferred size or {@code null} if there is no preferred size.
*
* @param component component painter is applied to
* @param preferred component preferred size
* @param painter component painter
* @param ignoreLayoutSize whether or not layout preferred size should be ignored
* @return component preferred size or {@code null} if there is no preferred size
*/
public static Dimension getPreferredSize ( final JComponent component, final Dimension preferred, final Painter painter,
final boolean ignoreLayoutSize )
{
// Painter's preferred size
Dimension ps = SwingUtils.max ( preferred, painter != null ? painter.getPreferredSize () : null );
// Layout preferred size
if ( !ignoreLayoutSize )
{
synchronized ( component.getTreeLock () )
{
final LayoutManager layout = component.getLayout ();
if ( layout != null )
{
ps = SwingUtils.max ( ps, layout.preferredLayoutSize ( component ) );
}
}
}
return ps;
}
/**
* Returns whether or not component uses decoratable painter.
*
* @param component component to process
* @return true if component uses decoratable painter, false otherwise
*/
public static boolean isDecoratable ( final Component component )
{
if ( component instanceof JComponent )
{
final JComponent jComponent = ( JComponent ) component;
final ComponentStyle style = StyleManager.getSkin ( jComponent ).getComponentStyle ( jComponent );
final Painter painter = style != null ? style.getPainter ( jComponent ) : null;
return painter != null && painter instanceof AbstractDecorationPainter;
}
else
{
return false;
}
}
}