org.jdesktop.swingx.plaf.basic.CalendarHeaderHandler Maven / Gradle / Ivy
Show all versions of swingx-all Show documentation
/*
* $Id$
*
* Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
package org.jdesktop.swingx.plaf.basic;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.logging.Logger;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.plaf.UIResource;
import org.jdesktop.swingx.JXMonthView;
import org.jdesktop.swingx.action.AbstractActionExt;
/**
* Provides and wires a component appropriate as a calendar navigation header.
* The design idea is to support a pluggable header for a zoomable (PENDING JW:
* naming!) JXMonthView. Then custom implementations can be tailored to exactly
* fit their needs.
*
*
* To install a custom implementation, register the class name of the custom
* header handler with the key CalendarHeaderHandler.uiControllerID
* , example:
*
*
*
* UIManager.put(CalendarHeaderHandler.uiControllerID, "com.foo.bar.MagicHeaderHandler")
*
*
*
* Basic navigation action should (will) be defined by the ui delegate itself (PENDING
* JW: still incomplete in BasicMonthViewUI). This handler can modify/enhance
* them as appropriate for its context.
*
*
* PENDING JW: those icons ... who's responsible? Shouldn't we use any of the
* default arrows as defined in the laf anyway (are there any?)
*
*
* Note: this is work-in-progress, be prepared to change if subclassing
* for custom requirements!
*
* @author Jeanette Winzenburg
*/
public abstract class CalendarHeaderHandler {
@SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(CalendarHeaderHandler.class.getName());
public static final String uiControllerID = "CalendarHeaderHandler";
protected JXMonthView monthView;
private JComponent calendarHeader;
protected Icon monthDownImage;
protected Icon monthUpImage;
private PropertyChangeListener monthViewPropertyChangeListener;
/**
* Installs this handler to the given month view.
*
* @param monthView the target month view to install to.
*/
public void install(JXMonthView monthView) {
this.monthView = monthView;
// PENDING JW: remove here if rendererHandler takes over control
// completely
// as is, some properties are duplicated
monthDownImage = UIManager.getIcon("JXMonthView.monthDownFileName");
monthUpImage = UIManager.getIcon("JXMonthView.monthUpFileName");
installNavigationActions();
installListeners();
componentOrientationChanged();
monthStringBackgroundChanged();
fontChanged();
}
/**
* Uninstalls this handler from the given target month view.
*
* @param monthView the target month view to install from.
*/
public void uninstall(JXMonthView monthView) {
this.monthView.remove(getHeaderComponent());
uninstallListeners();
this.monthView = null;
}
/**
* Returns a component to be used as header in a zoomable month view,
* guaranteed to be not null.
*
* @return a component to be used as header in a zoomable JXMonthView
*/
public JComponent getHeaderComponent() {
if (calendarHeader == null) {
calendarHeader = createCalendarHeader();
}
return calendarHeader;
}
/**
* Creates and registered listeners on the monthView as appropriate. This
* implementation registers a PropertyChangeListener which synchronizes
* internal state on changes of componentOrientation, font and
* monthStringBackground.
*/
protected void installListeners() {
monthView
.addPropertyChangeListener(getMonthViewPropertyChangeListener());
}
/**
* Unregisters listeners which had been installed to the monthView.
*/
protected void uninstallListeners() {
monthView.removePropertyChangeListener(monthViewPropertyChangeListener);
}
/**
* Returns the propertyChangelistener for the monthView. Lazily created.
*
* @return the propertyChangeListener for the monthView.
*/
private PropertyChangeListener getMonthViewPropertyChangeListener() {
if (monthViewPropertyChangeListener == null) {
monthViewPropertyChangeListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("componentOrientation".equals(evt.getPropertyName())) {
componentOrientationChanged();
} else if ("font".equals(evt.getPropertyName())) {
fontChanged();
} else if ("monthStringBackground".equals(evt
.getPropertyName())) {
monthStringBackgroundChanged();
}
}
};
}
return monthViewPropertyChangeListener;
}
/**
* Synchronizes internal state which depends on the month view's
* monthStringBackground.
*/
protected void monthStringBackgroundChanged() {
getHeaderComponent().setBackground(
getAsNotUIResource(monthView.getMonthStringBackground()));
}
/**
* Synchronizes internal state which depends on the month view's font.
*/
protected void fontChanged() {
getHeaderComponent().setFont(getAsNotUIResource(createDerivedFont()));
monthView.revalidate();
}
/**
* Synchronizes internal state which depends on the month view's
* componentOrientation.
*
* This implementation updates the month navigation icons and the header
* component's orientation.
*/
protected void componentOrientationChanged() {
getHeaderComponent().applyComponentOrientation(
monthView.getComponentOrientation());
if (monthView.getComponentOrientation().isLeftToRight()) {
updateMonthNavigationIcons(monthDownImage, monthUpImage);
} else {
updateMonthNavigationIcons(monthUpImage, monthDownImage);
}
}
/**
* @param previous the icon to use in the previousMonth action
* @param next the icon to use on the nextMonth action
*/
private void updateMonthNavigationIcons(Icon previous, Icon next) {
updateActionIcon("previousMonth", previous);
updateActionIcon("nextMonth", next);
}
/**
* @param previousKey
* @param previous
*/
private void updateActionIcon(String previousKey, Icon previous) {
Action action = monthView.getActionMap().get(previousKey);
if (action != null) {
action.putValue(Action.SMALL_ICON, previous);
}
}
/**
* Creates and returns the component used as header in a zoomable monthView.
*
* @return the component used as header in a zoomable monthView, guaranteed
* to be not null.
*/
protected abstract JComponent createCalendarHeader();
/**
* Installs and configures navigational actions.
*
*
* This implementation creates and installs wrappers around the
* scrollToPrevious/-NextMonth actions installed by the ui and configures
* them with the appropriate next/previous icons.
*/
protected void installNavigationActions() {
installWrapper("scrollToPreviousMonth", "previousMonth", monthView
.getComponentOrientation().isLeftToRight() ? monthDownImage
: monthUpImage);
installWrapper("scrollToNextMonth", "nextMonth", monthView
.getComponentOrientation().isLeftToRight() ? monthUpImage
: monthDownImage);
}
/**
* Creates an life action wrapper around the action registered with
* actionKey, sets its SMALL_ICON property to the given icon and installs
* itself with the newActionKey.
*
* @param actionKey the key of the action to wrap around
* @param newActionKey the key of the wrapper action
* @param icon the icon to use in the wrapper action
*/
private void installWrapper(final String actionKey, String newActionKey,
Icon icon) {
AbstractActionExt wrapper = new AbstractActionExt(null, icon) {
@Override
public void actionPerformed(ActionEvent e) {
Action action = monthView.getActionMap().get(actionKey);
if (action != null) {
action.actionPerformed(e);
}
}
};
monthView.getActionMap().put(newActionKey, wrapper);
}
/**
* Returns a Font based on the param which is not of type UIResource.
*
* @param font the base font
* @return a font not of type UIResource, may be null.
*/
private Font getAsNotUIResource(Font font) {
if (!(font instanceof UIResource))
return font;
// PENDING JW: correct way to create another font instance?
return font.deriveFont(font.getAttributes());
}
/**
* Returns a Color based on the param which is not of type UIResource.
*
* @param color the base color
* @return a color not of type UIResource, may be null.
*/
private Color getAsNotUIResource(Color color) {
if (!(color instanceof UIResource))
return color;
// PENDING JW: correct way to create another color instance?
float[] rgb = color.getRGBComponents(null);
return new Color(rgb[0], rgb[1], rgb[2], rgb[3]);
}
/**
* Create a derived font used to when painting various pieces of the month
* view component. This method will be called whenever the font on the
* component is set so a new derived font can be created.
*/
protected Font createDerivedFont() {
return monthView.getFont().deriveFont(Font.BOLD);
}
}