001/*
002 * $Id: CalendarCellContext.java 3286 2009-03-10 12:13:43Z 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 */
021package org.jdesktop.swingx.plaf.basic;
022
023import java.awt.Color;
024import java.util.Calendar;
025
026import javax.swing.BorderFactory;
027import javax.swing.Icon;
028import javax.swing.SwingConstants;
029import javax.swing.UIManager;
030import javax.swing.border.Border;
031
032import org.jdesktop.swingx.JXMonthView;
033import org.jdesktop.swingx.border.IconBorder;
034import org.jdesktop.swingx.plaf.UIManagerExt;
035import org.jdesktop.swingx.renderer.CellContext;
036
037/**
038 * MonthView specific CellContext. This is internally used by BasisMonthViewUI rendering.
039 * 
040 * @author Jeanette Winzenburg
041 */
042class CalendarCellContext extends CellContext {
043
044    /**
045     * The padding for month traversal icons.
046     * PENDING JW: decouple rendering and hit-detection. As is, these are 
047     * hard-coded "magic numbers" which must be the same in both 
048     * the ui-delegate (which does the hit-detection) and here (which
049     * returns the default title border)
050     * 
051     * Added as preliminary fix for #1028-swingx: title border incorrect if box-padding 0
052     */
053    private int arrowPaddingX = 3;
054    private int arrowPaddingY = 3;
055
056    private CalendarState dayState;
057
058    public void installContext(JXMonthView component, Calendar value,
059            boolean selected, boolean focused, CalendarState dayState) {
060        this.component = component;
061        this.dayState = dayState;
062        installState(value, -1, -1, selected, focused, true, true);
063    }
064
065    
066    @Override
067    public JXMonthView getComponent() {
068        return (JXMonthView) super.getComponent();
069    }
070
071
072    public CalendarState getCalendarState() {
073        return dayState;
074    }
075    
076    
077    public Calendar getCalendar() {
078        return (getValue() instanceof Calendar) ? (Calendar) getValue() : null;
079    }
080
081    @Override
082    protected Color getForeground() {
083        if (CalendarState.LEADING == dayState) {
084            return getUIColor("leadingDayForeground");
085        }
086        if (CalendarState.TRAILING == dayState) {
087            return getUIColor("trailingDayForeground");
088        }
089        if ((CalendarState.TITLE == dayState) && (getComponent() != null)) {
090            return getComponent().getMonthStringForeground();
091        }
092        if (CalendarState.WEEK_OF_YEAR == dayState) {
093            Color weekOfTheYearForeground = getUIColor("weekOfTheYearForeground");
094            if (weekOfTheYearForeground != null) {
095                return weekOfTheYearForeground;
096            }
097        }
098        if (CalendarState.DAY_OF_WEEK == dayState) {
099            Color daysOfTheWeekForeground = getComponent() != null 
100                ? getComponent().getDaysOfTheWeekForeground() : null;
101            if (daysOfTheWeekForeground != null) {
102                return daysOfTheWeekForeground;
103            }
104        }
105
106        Color flaggedOrPerDayForeground = getFlaggedOrPerDayForeground();
107        return flaggedOrPerDayForeground != null ? flaggedOrPerDayForeground : super.getForeground();
108    }
109
110    /**
111     * @param key
112     * @return
113     */
114    private Color getUIColor(String key) {
115        return UIManagerExt.getColor(getUIPrefix() + key);
116    }
117
118    /**
119     * Returns the special color used for flagged days or per weekday or null if none is
120     * set, the component or the calendar are null.
121     * 
122     * @return the special foreground color for flagged days or per dayOfWeek.
123     */
124    protected Color getFlaggedOrPerDayForeground() {
125        
126        if (getComponent() != null && (getCalendar() != null)) {
127            if (getComponent().isFlaggedDate(getCalendar().getTime())) {
128                return getComponent().getFlaggedDayForeground();
129            } else {
130                Color perDay = getComponent().getPerDayOfWeekForeground(getCalendar().get(Calendar.DAY_OF_WEEK));
131                if (perDay != null) {
132                    return perDay;
133                }
134                
135            }
136        }
137        return null;
138    }
139
140    @Override
141    protected Color getBackground() {
142        if ((CalendarState.TITLE == dayState) && (getComponent() != null)) {
143            return getComponent().getMonthStringBackground();
144        }
145        return super.getBackground();
146    }
147
148    @Override
149    protected Color getSelectionBackground() {
150        if (CalendarState.LEADING == dayState || CalendarState.TRAILING == dayState) return getBackground();
151        return getComponent() != null ? getComponent().getSelectionBackground() : null;
152    }
153
154    @Override
155    protected Color getSelectionForeground() {
156        if (CalendarState.LEADING == dayState || CalendarState.TRAILING == dayState) return getForeground();
157        Color flaggedOrPerDayForeground = getFlaggedOrPerDayForeground();
158        if (flaggedOrPerDayForeground != null) {
159            return flaggedOrPerDayForeground;
160        }
161        return getComponent() != null ? getComponent().getSelectionForeground() : null;
162    }
163
164    
165    
166    @Override
167    protected Border getBorder() {
168        if (getComponent() == null) {
169            return super.getBorder();
170        }
171        if (CalendarState.TITLE == dayState) {
172            return getTitleBorder();
173        }
174        if (isToday()) {
175            int x = getComponent().getBoxPaddingX();
176            int y = getComponent().getBoxPaddingY();
177           Border todayBorder = BorderFactory.createLineBorder(getComponent().getTodayBackground());
178           Border empty = BorderFactory.createEmptyBorder(y - 1, x - 1, y - 1, x -1);
179           return BorderFactory.createCompoundBorder(todayBorder, empty);
180        }
181        return BorderFactory.createEmptyBorder(getComponent().getBoxPaddingY(), getComponent().getBoxPaddingX(), getComponent().getBoxPaddingY(), getComponent().getBoxPaddingX());
182    }
183
184    /**
185     * @return
186     */
187    private Border getTitleBorder() {
188        if (getComponent().isTraversable()) {
189            Icon downIcon = UIManager.getIcon("JXMonthView.monthDownFileName");
190            Icon upIcon = UIManager.getIcon("JXMonthView.monthUpFileName");
191
192            // fix for #1028-swingx: title border whacky for boxpadding 0
193            // in fact there had been a deeper issue - without using the arrowPadding here
194            // the hit-detection of the buttons is slightly off target
195            IconBorder up = new IconBorder(upIcon, SwingConstants.EAST, arrowPaddingX); 
196            IconBorder down = new IconBorder(downIcon, SwingConstants.WEST, arrowPaddingX); 
197            Border compound = BorderFactory.createCompoundBorder(up, down);
198            Border empty = BorderFactory.createEmptyBorder(2* arrowPaddingY, 0, 2*arrowPaddingY, 0);
199            return BorderFactory.createCompoundBorder(compound, empty);
200        }
201        
202        return BorderFactory.createEmptyBorder(getComponent().getBoxPaddingY(), getComponent().getBoxPaddingX(), getComponent().getBoxPaddingY(), getComponent().getBoxPaddingX());
203    }
204
205    /**
206     * @return
207     */
208    protected boolean isToday() {
209        return CalendarState.TODAY == dayState;
210    }
211
212    @Override
213    protected String getUIPrefix() {
214        return "JXMonthView.";
215    }
216
217    
218}