/* * Copyright (c) 2009 Kathryn Huxtable and Kenneth Orr. * * This file is part of the SeaGlass Pluggable Look and Feel. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * $Id$ */ package com.seaglasslookandfeel; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.plaf.synth.Region; import javax.swing.plaf.synth.SynthConstants; import javax.swing.plaf.synth.SynthContext; import javax.swing.plaf.synth.SynthPainter; import javax.swing.plaf.synth.SynthStyle; /** * An immutable transient object containing contextual information about a * <code>Region</code>. A <code>SeaGlassContext</code> should only be considered * valid for the duration of the method it is passed to. In other words you * should not cache a <code>SeaGlassContext</code> that is passed to you and * expect it to remain valid. * * <p>Extends SynthContext by Scott Violet and reimplements most of its methods * because too much is package local.</p> * * @see javax.swing.plaf.synth.SynthContext */ public class SeaGlassContext extends SynthContext { private static final SynthPainter EMPTY_PAINTER = new SynthPainter() { }; /** This button *must* not have a UI or we end up throwing an NPE. */ private static final JButton FAKE_BUTTON = new JButton() { public void updateUI() { // Do nothing. } }; // Fake items are created because there's no public empty constructor for // SynthContext and we need a way to determine when our own empty // constructor was called. We pass these to SynthContext's constructor and // then test for them later. private static final JButton fakeComponent = FAKE_BUTTON; private static Region fakeRegion = Region.BUTTON; private static SynthStyle fakeStyle = new SeaGlassStyle(null, null); // From SynthContext. private static final Map contextMap; static { contextMap = new HashMap(); } private JComponent component; private Region region; private SynthStyle style; private int state; /** * Creates a new SeaGlassContext object. */ public SeaGlassContext() { super(fakeComponent, fakeRegion, fakeStyle, 0); } /** * Creates a SeaGlassContext with the specified values. This is meant for * subclasses and custom UI implementors. You very rarely need to construct * a SeaGlassContext, though some methods will take one. * * @param component JComponent * @param region Identifies the portion of the JComponent * @param style Style associated with the component * @param state State of the component as defined in SynthConstants. */ public SeaGlassContext(JComponent component, Region region, SynthStyle style, int state) { super(component, region, style, state); if (component == fakeComponent) { this.component = null; this.region = null; this.style = null; return; } if (component == null || region == null || style == null) { throw new NullPointerException("You must supply a non-null component, region and style"); } reset(component, region, style, state); } /** * The method used to get a context. * * @param type the class of the context. * @param component the component. * @param region the region. * @param style the style. * @param state the state. * * @return the newly constructed context, corresponding to the arguments. */ public static SeaGlassContext getContext(Class type, JComponent component, Region region, SynthStyle style, int state) { SeaGlassContext context = null; synchronized (contextMap) { List instances = (List) contextMap.get(type); if (instances != null) { int size = instances.size(); if (size > 0) { context = (SeaGlassContext) instances.remove(size - 1); } } } if (context == null) { try { context = (SeaGlassContext) type.newInstance(); } catch (IllegalAccessException iae) { iae.printStackTrace(); } catch (InstantiationException ie) { ie.printStackTrace(); } } context.reset(component, region, style, state); return context; } /** * Release a context for re-use. * * @param context the context to release. */ static void releaseContext(SeaGlassContext context) { synchronized (contextMap) { List instances = (List) contextMap.get(context.getClass()); if (instances == null) { instances = new ArrayList(5); contextMap.put(context.getClass(), instances); } instances.add(context); } } /** * Returns the hosting component containing the region. * * @return Hosting Component */ public JComponent getComponent() { return component; } /** * Returns the Region identifying this state. * * @return Region of the hosting component */ public Region getRegion() { return region; } /** * A convenience method for <code>getRegion().isSubregion()</code>. * * @return {@code true} if the context represents a subregion, {@code false} * otherwise. */ @SuppressWarnings("all") public boolean isSubregion() { return getRegion().isSubregion(); } /** * Sets the current style for the context. * * @param style the new style. */ @SuppressWarnings("all") public void setStyle(SynthStyle style) { this.style = style; } /** * Returns the style associated with this Region. * * @return SynthStyle associated with the region. */ public SynthStyle getStyle() { return style; } /** * Sets the current state for a component/region. * * @param state the new state. */ @SuppressWarnings("all") public void setComponentState(int state) { this.state = state; } /** * Returns the state of the widget, which is a bitmask of the values defined * in <code>SynthConstants</code>. A region will at least be in one of * <code>ENABLED</code>, <code>MOUSE_OVER</code>, <code>PRESSED</code> or * <code>DISABLED</code>. * * @see SynthConstants * * @return State of Component */ public int getComponentState() { return state; } /** * Resets the state of the Context. * * @param component the new component. * @param region the new region. * @param style the new style. * @param state the new state. */ @SuppressWarnings("all") public void reset(JComponent component, Region region, SynthStyle style, int state) { this.component = component; this.region = region; this.style = style; this.state = state; } /** * Release a context for re-use after nulling the component and style. */ @SuppressWarnings("all") public void dispose() { this.component = null; this.style = null; releaseContext(this); } /** * Convenience method to get the Painter from the current SynthStyle. This * will NEVER return null. * * @return the painter for the style and context, otherwise the empty * painter. */ @SuppressWarnings("all") public SynthPainter getPainter() { SynthPainter painter = getStyle().getPainter(this); if (painter != null) { return painter; } return EMPTY_PAINTER; } }