001    /*
002     * $Id: BasicCalendarHeaderHandler.java 3319 2009-03-31 15:39:29Z kleopatra $
003     *
004     * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle,
005     * Santa Clara, California 95054, U.S.A. All rights reserved.
006     *
007     * This library is free software; you can redistribute it and/or
008     * modify it under the terms of the GNU Lesser General Public
009     * License as published by the Free Software Foundation; either
010     * version 2.1 of the License, or (at your option) any later version.
011     * 
012     * This library is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015     * Lesser General Public License for more details.
016     * 
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this library; if not, write to the Free Software
019     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020     *
021     */
022    package org.jdesktop.swingx.plaf.basic;
023    
024    import java.awt.Color;
025    import java.awt.Font;
026    import java.awt.event.ActionEvent;
027    import java.beans.PropertyChangeEvent;
028    import java.beans.PropertyChangeListener;
029    import java.text.DateFormatSymbols;
030    import java.util.Calendar;
031    import java.util.Locale;
032    
033    import javax.swing.AbstractButton;
034    import javax.swing.Action;
035    import javax.swing.BorderFactory;
036    import javax.swing.Box;
037    import javax.swing.BoxLayout;
038    
039    import org.jdesktop.swingx.JXHyperlink;
040    import org.jdesktop.swingx.JXMonthView;
041    import org.jdesktop.swingx.JXPanel;
042    import org.jdesktop.swingx.hyperlink.AbstractHyperlinkAction;
043    import org.jdesktop.swingx.renderer.StringValue;
044    import org.jdesktop.swingx.renderer.StringValues;
045    
046    
047    /**
048     * Custom implementation of a CalendarHeaderHandler in preparation of a vista-style 
049     * calendar. Does nothing yet.
050     * 
051     * @author Jeanette Winzenburg
052     */
053    public class BasicCalendarHeaderHandler extends CalendarHeaderHandler {
054        
055        
056        @Override
057        public void install(JXMonthView monthView) {
058            super.install(monthView);
059            getHeaderComponent().setActions(monthView.getActionMap().get("previousMonth"),
060                    monthView.getActionMap().get("nextMonth"),
061                    monthView.getActionMap().get("zoomOut"));
062    
063        }
064    
065        
066        
067        @Override
068        protected void installNavigationActions() {
069            // TODO Auto-generated method stub
070            super.installNavigationActions();
071            ZoomOutAction zoomOutAction = new ZoomOutAction();
072            zoomOutAction.setTarget(monthView);
073            monthView.getActionMap().put("zoomOut", zoomOutAction);
074        }
075    
076    
077    
078        @Override
079        public void uninstall(JXMonthView monthView) {
080            getHeaderComponent().setActions(null, null, null);
081            super.uninstall(monthView);
082        }
083    
084    
085        @Override
086        public BasicCalendarHeader getHeaderComponent() {
087            // TODO Auto-generated method stub
088            return (BasicCalendarHeader) super.getHeaderComponent();
089        }
090    
091        @Override
092        protected BasicCalendarHeader createCalendarHeader() {
093            return new BasicCalendarHeader();
094        }
095    
096        /**
097         * Quick fix for Issue #1046-swingx: header text not updated if zoomable.
098         * 
099         */
100        protected static class ZoomOutAction extends AbstractHyperlinkAction<JXMonthView> {
101    
102            private PropertyChangeListener linkListener;
103            // Formatters/state used by Providers. 
104            /** Localized month strings used in title. */
105            private String[] monthNames;
106            private StringValue tsv ;
107    
108            public ZoomOutAction() {
109                super();
110                tsv = new StringValue() {
111                    
112                    public String getString(Object value) {
113                        if (value instanceof Calendar) {
114                            String month = monthNames[((Calendar) value)
115                                                      .get(Calendar.MONTH)];
116                            return month + " "
117                            + ((Calendar) value).get(Calendar.YEAR); 
118                        }
119                        return StringValues.TO_STRING.getString(value);
120                    }
121                    
122                };
123            }
124            
125            public void actionPerformed(ActionEvent e) {
126                // TODO Auto-generated method stub
127                
128            }
129    
130            
131            /**
132             * installs a propertyChangeListener on the target and
133             * updates the visual properties from the target.
134             */
135            @Override
136            protected void installTarget() {
137                if (getTarget() != null) {
138                    getTarget().addPropertyChangeListener(getTargetListener());
139                }
140                updateLocale();
141                updateFromTarget();
142            }
143    
144            /**
145             * 
146             */
147            private void updateLocale() {
148                Locale current = getTarget() != null ? getTarget().getLocale() : Locale.getDefault();
149                monthNames = new DateFormatSymbols(current).getMonths();
150            }
151    
152            /**
153             * removes the propertyChangeListener. <p>
154             * 
155             * Implementation NOTE: this does not clean-up internal state! There is
156             * no need to because updateFromTarget handles both null and not-null
157             * targets. Hmm...
158             * 
159             */
160            @Override
161            protected void uninstallTarget() {
162                if (getTarget() == null) return;
163                getTarget().removePropertyChangeListener(getTargetListener());
164            }
165    
166            protected void updateFromTarget() {
167                // this happens on construction with null target
168                if (tsv == null) return;
169                Calendar calendar = getTarget() != null ? getTarget().getCalendar() : null;
170                setName(tsv.getString(calendar));
171            }
172    
173            private PropertyChangeListener getTargetListener() {
174                if (linkListener == null) {
175                 linkListener = new PropertyChangeListener() {
176    
177                    public void propertyChange(PropertyChangeEvent evt) {
178                        if ("firstDisplayedDay".equals(evt.getPropertyName())) {
179                            updateFromTarget();
180                        } else if ("locale".equals(evt.getPropertyName())) {
181                            updateLocale();
182                            updateFromTarget();
183                        }
184                    }
185                    
186                };
187                }
188                return linkListener;
189            }
190    
191            
192        }
193    
194        
195        /**
196         * Active header for a JXMonthView in zoomable mode.<p>
197         * 
198         *  PENDING JW: very much work-in-progress.
199         */
200        static class BasicCalendarHeader extends JXPanel {
201    
202            protected AbstractButton prevButton;
203            protected AbstractButton nextButton;
204            protected JXHyperlink zoomOutLink;
205    
206            public BasicCalendarHeader() {
207                setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
208                prevButton = createNavigationButton();
209                nextButton = createNavigationButton();
210                zoomOutLink = createZoomLink();
211                add(prevButton);
212                add(Box.createHorizontalGlue());
213                add(zoomOutLink);
214                add(Box.createHorizontalGlue());
215                add(nextButton);
216                setBorder(BorderFactory.createEmptyBorder(2, 4, 2, 4));
217            }
218    
219            /**
220             * Sets the actions for backward, forward and zoom out navigation.
221             * 
222             * @param prev
223             * @param next
224             * @param zoomOut
225             */
226            public void setActions(Action prev, Action next, Action zoomOut) {
227                prevButton.setAction(prev);
228                nextButton.setAction(next);
229                zoomOutLink.setAction(zoomOut);
230            }
231            
232            
233            /**
234             * {@inheritDoc} <p>
235             * 
236             * Overridden to set the font of the zoom hyperlink.
237             */
238            @Override
239            public void setFont(Font font) {
240                super.setFont(font);
241                if (zoomOutLink != null)
242                    zoomOutLink.setFont(font);
243            }
244    
245            private JXHyperlink createZoomLink() {
246                JXHyperlink zoomOutLink = new JXHyperlink();
247                Color textColor = new Color(16, 66, 104);
248                zoomOutLink.setUnclickedColor(textColor);
249                zoomOutLink.setClickedColor(textColor);
250                zoomOutLink.setFocusable(false);
251                return zoomOutLink;
252            }
253    
254            private AbstractButton createNavigationButton() {
255                JXHyperlink b = new JXHyperlink();
256                b.setContentAreaFilled(false);
257                b.setBorder(BorderFactory.createEmptyBorder());
258                b.setRolloverEnabled(true);
259                b.setFocusable(false);
260                return b;
261            }
262        }
263    
264    }
265