swingx-master/swingx-core/src/main/java/org/jdesktop/swingx/plaf/basic/BasicCalendarHeaderHandler.java
/*
 * $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.text.DateFormatSymbols;
import java.util.Calendar;
import java.util.Locale;

import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;

import org.jdesktop.swingx.JXHyperlink;
import org.jdesktop.swingx.JXMonthView;
import org.jdesktop.swingx.JXPanel;
import org.jdesktop.swingx.hyperlink.AbstractHyperlinkAction;
import org.jdesktop.swingx.renderer.StringValue;
import org.jdesktop.swingx.renderer.StringValues;


/**
 * Custom implementation of a CalendarHeaderHandler in preparation of a vista-style 
 * calendar. Does nothing yet.
 * 
 * @author Jeanette Winzenburg
 */
public class BasicCalendarHeaderHandler extends CalendarHeaderHandler {
    
    
    @Override
    public void install(JXMonthView monthView) {
        super.install(monthView);
        getHeaderComponent().setActions(monthView.getActionMap().get("previousMonth"),
                monthView.getActionMap().get("nextMonth"),
                monthView.getActionMap().get("zoomOut"));

    }

    
    
    @Override
    protected void installNavigationActions() {
        // TODO Auto-generated method stub
        super.installNavigationActions();
        ZoomOutAction zoomOutAction = new ZoomOutAction();
        zoomOutAction.setTarget(monthView);
        monthView.getActionMap().put("zoomOut", zoomOutAction);
    }



    @Override
    public void uninstall(JXMonthView monthView) {
        getHeaderComponent().setActions(null, null, null);
        super.uninstall(monthView);
    }


    @Override
    public BasicCalendarHeader getHeaderComponent() {
        // TODO Auto-generated method stub
        return (BasicCalendarHeader) super.getHeaderComponent();
    }

    @Override
    protected BasicCalendarHeader createCalendarHeader() {
        return new BasicCalendarHeader();
    }

    /**
     * Quick fix for Issue #1046-swingx: header text not updated if zoomable.
     * 
     */
    protected static class ZoomOutAction extends AbstractHyperlinkAction<JXMonthView> {

        private PropertyChangeListener linkListener;
        // Formatters/state used by Providers. 
        /** Localized month strings used in title. */
        private String[] monthNames;
        private StringValue tsv ;

        public ZoomOutAction() {
            super();
            tsv = new StringValue() {
                
                @Override
                public String getString(Object value) {
                    if (value instanceof Calendar) {
                        String month = monthNames[((Calendar) value)
                                                  .get(Calendar.MONTH)];
                        return month + " "
                        + ((Calendar) value).get(Calendar.YEAR); 
                    }
                    return StringValues.TO_STRING.getString(value);
                }
                
            };
        }
        
        @Override
        public void actionPerformed(ActionEvent e) {
            // TODO Auto-generated method stub
            
        }

        
        /**
         * installs a propertyChangeListener on the target and
         * updates the visual properties from the target.
         */
        @Override
        protected void installTarget() {
            if (getTarget() != null) {
                getTarget().addPropertyChangeListener(getTargetListener());
            }
            updateLocale();
            updateFromTarget();
        }

        /**
         * 
         */
        private void updateLocale() {
            Locale current = getTarget() != null ? getTarget().getLocale() : Locale.getDefault();
            monthNames = DateFormatSymbols.getInstance(current).getMonths();
        }

        /**
         * removes the propertyChangeListener. <p>
         * 
         * Implementation NOTE: this does not clean-up internal state! There is
         * no need to because updateFromTarget handles both null and not-null
         * targets. Hmm...
         * 
         */
        @Override
        protected void uninstallTarget() {
            if (getTarget() == null) return;
            getTarget().removePropertyChangeListener(getTargetListener());
        }

        protected void updateFromTarget() {
            // this happens on construction with null target
            if (tsv == null) return;
            Calendar calendar = getTarget() != null ? getTarget().getCalendar() : null;
            setName(tsv.getString(calendar));
        }

        private PropertyChangeListener getTargetListener() {
            if (linkListener == null) {
             linkListener = new PropertyChangeListener() {

                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    if ("firstDisplayedDay".equals(evt.getPropertyName())) {
                        updateFromTarget();
                    } else if ("locale".equals(evt.getPropertyName())) {
                        updateLocale();
                        updateFromTarget();
                    }
                }
                
            };
            }
            return linkListener;
        }

        
    }

    
    /**
     * Active header for a JXMonthView in zoomable mode.<p>
     * 
     *  PENDING JW: very much work-in-progress.
     */
    static class BasicCalendarHeader extends JXPanel {

        protected AbstractButton prevButton;
        protected AbstractButton nextButton;
        protected JXHyperlink zoomOutLink;

        public BasicCalendarHeader() {
            setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
            prevButton = createNavigationButton();
            nextButton = createNavigationButton();
            zoomOutLink = createZoomLink();
            add(prevButton);
            add(Box.createHorizontalGlue());
            add(zoomOutLink);
            add(Box.createHorizontalGlue());
            add(nextButton);
            setBorder(BorderFactory.createEmptyBorder(2, 4, 2, 4));
        }

        /**
         * Sets the actions for backward, forward and zoom out navigation.
         * 
         * @param prev
         * @param next
         * @param zoomOut
         */
        public void setActions(Action prev, Action next, Action zoomOut) {
            prevButton.setAction(prev);
            nextButton.setAction(next);
            zoomOutLink.setAction(zoomOut);
        }
        
        
        /**
         * {@inheritDoc} <p>
         * 
         * Overridden to set the font of the zoom hyperlink.
         */
        @Override
        public void setFont(Font font) {
            super.setFont(font);
            if (zoomOutLink != null)
                zoomOutLink.setFont(font);
        }

        private JXHyperlink createZoomLink() {
            JXHyperlink zoomOutLink = new JXHyperlink();
            Color textColor = new Color(16, 66, 104);
            zoomOutLink.setUnclickedColor(textColor);
            zoomOutLink.setClickedColor(textColor);
            zoomOutLink.setFocusable(false);
            return zoomOutLink;
        }

        private AbstractButton createNavigationButton() {
            JXHyperlink b = new JXHyperlink();
            b.setContentAreaFilled(false);
            b.setBorder(BorderFactory.createEmptyBorder());
            b.setRolloverEnabled(true);
            b.setFocusable(false);
            return b;
        }
    }

}
    
