package org.japura.gui.calendar; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.LayoutManager; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.text.DateFormatSymbols; import java.util.ArrayList; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; import java.util.Locale; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JPanel; import org.japura.gui.GUIImages; import org.japura.gui.PopupMenuBuilder; import org.japura.gui.calendar.components.CalendarButton; import org.japura.gui.calendar.components.CalendarSlot; import org.japura.gui.calendar.components.DayOfMonthSlot; import org.japura.gui.calendar.components.DayOfWeekSlot; import org.japura.gui.calendar.components.MonthLabel; import org.japura.gui.calendar.components.TopBar; import org.japura.gui.calendar.components.WeekHeaderSlot; import org.japura.gui.calendar.components.WeekSlot; import org.japura.gui.calendar.components.YearLabel; import org.japura.gui.event.DateEvent; import org.japura.gui.event.DateListener; /** * <P> * Copyright (C) 2011-2013 Carlos Eduardo Leite de Andrade * <P> * 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 3 of the License, or (at your option) any * later version. * <P> * This program 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. * <P> * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <A * HREF="www.gnu.org/licenses/">www.gnu.org/licenses/</A> * <P> * For more information, contact: <A HREF="www.japura.org">www.japura.org</A> * <P> * * @author Carlos Eduardo Leite de Andrade */ public class Calendar extends JComponent{ private static final long serialVersionUID = -3245248937315409078L; public static final DayOfWeek SUNDAY = DayOfWeek.SUNDAY; public static final DayOfWeek MONDAY = DayOfWeek.MONDAY; public static final DayOfWeek THURSDAY = DayOfWeek.THURSDAY; public static final DayOfWeek WEDNESDAY = DayOfWeek.WEDNESDAY; public static final DayOfWeek TUESDAY = DayOfWeek.TUESDAY; public static final DayOfWeek FRIDAY = DayOfWeek.FRIDAY; public static final DayOfWeek SATURDAY = DayOfWeek.SATURDAY; private PropertiesProvider propertiesProvider; private HashMap<CalendarComponentType, List<CalendarSlot>> slots; private CalendarButton previousMonthButton; private CalendarButton previousYearButton; private CalendarButton nextMonthButton; private CalendarButton nextYearButton; private MonthLabel monthLabel; private YearLabel yearLabel; private TopBar topPanel; private JPanel slotsPanel; private PopupMenuBuilder<CalendarComponent> popupMenuBuilder; private String[] weekNames; private String[] monthNames; private Long date; private boolean weeksEnabled = false; boolean dialogMode; private JDialog modalDialog; Long selectedDialogDate; public Calendar() { this(null); } public Calendar(Locale locale) { propertiesProvider = new DefaultPropertiesProvider(); if (locale != null) { setLocale(locale); } setBorder(BorderFactory.createLineBorder(Color.BLACK)); setBackground(Color.WHITE); setOpaque(true); super.setLayout(new BorderLayout()); add(getTopPanel(), BorderLayout.NORTH); add(getSlotsPanel(), BorderLayout.CENTER); updateNames(); slots = new HashMap<CalendarComponentType, List<CalendarSlot>>(); rebuild(); setDate(System.currentTimeMillis()); } public void setPropertiesProvider(PropertiesProvider propertiesProvider) { if (propertiesProvider == null) { propertiesProvider = new DefaultPropertiesProvider(); } this.propertiesProvider = propertiesProvider; } public PropertiesProvider getPropertiesProvider() { return propertiesProvider; } @Override protected void paintComponent(Graphics g) { if (isOpaque()) { g.setColor(getBackground()); g.fillRect(0, 0, getWidth(), getHeight()); } } public PopupMenuBuilder<CalendarComponent> getPopupMenuBuilder() { return popupMenuBuilder; } public void setPopupMenuBuilder(PopupMenuBuilder<CalendarComponent> popupMenuBuilder) { this.popupMenuBuilder = popupMenuBuilder; } @Override public void setEnabled(boolean enabled) { getNextYearButton().setEnabled(enabled); getNextMonthButton().setEnabled(enabled); getPreviousMonthButton().setEnabled(enabled); getPreviousYearButton().setEnabled(enabled); getMonthLabel().setEnabled(enabled); getYearLabel().setEnabled(enabled); for (List<CalendarSlot> list : slots.values()) { for (CalendarSlot cs : list) { cs.setEnabled(enabled); } } super.setEnabled(enabled); } @Override public void setLocale(Locale l) { super.setLocale(l); updateNames(); } @Override public Dimension getMinimumSize() { return super.getPreferredSize(); } private void updateNames() { DateFormatSymbols dfs = new DateFormatSymbols(getLocale()); weekNames = dfs.getShortWeekdays(); monthNames = dfs.getMonths(); Font font = getMonthLabel().getFont(); FontMetrics fm = getMonthLabel().getFontMetrics(font); int width = 0; for (String month : monthNames) { width = Math.max(width, fm.stringWidth(month)); } Dimension dim = new Dimension(width, fm.getHeight()); getMonthLabel().setPreferredSize(dim); } protected void fireAllListeners(Date oldDate, Date newDate) { DateEvent event = new DateEvent(this, newDate, oldDate); DateListener[] listeners = listenerList.getListeners(DateListener.class); for (DateListener listener : listeners) { listener.dateChanged(event); } } public void addDateListener(DateListener listener) { listenerList.add(DateListener.class, listener); } public void removeDateListener(DateListener listener) { listenerList.remove(DateListener.class, listener); } @Override public final void setLayout(LayoutManager mgr) {} private TopBar getTopPanel() { if (topPanel == null) { topPanel = new TopBar(this); topPanel.setBackground(getPropertiesProvider().getBackground(topPanel)); topPanel.add(getPreviousYearButton()); topPanel.add(getPreviousMonthButton()); topPanel.add(getMonthLabel()); topPanel.add(getYearLabel()); topPanel.add(getNextMonthButton()); topPanel.add(getNextYearButton()); } return topPanel; } private JPanel getSlotsPanel() { if (slotsPanel == null) { slotsPanel = new JPanel(); slotsPanel.setOpaque(false); } return slotsPanel; } private CalendarButton getPreviousYearButton() { if (previousYearButton == null) { previousYearButton = new CalendarButton(this, CalendarComponentType.PREVIOUS_YEAR_BUTTON); PropertiesProvider pp = getPropertiesProvider(); previousYearButton.setForeground(pp.getForeground(previousYearButton)); previousYearButton.setDisabledForeground(pp .getDisabledForeground(previousYearButton)); previousYearButton.setMouseOverForeground(pp .getMouseOverForeground(previousYearButton)); previousYearButton.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { if (isEnabled()) { previousYear(); } } }); } return previousYearButton; } private CalendarButton getPreviousMonthButton() { if (previousMonthButton == null) { previousMonthButton = new CalendarButton(this, CalendarComponentType.PREVIOUS_MONTH_BUTTON); PropertiesProvider pp = getPropertiesProvider(); previousMonthButton.setForeground(pp.getForeground(previousMonthButton)); previousMonthButton.setDisabledForeground(pp .getDisabledForeground(previousMonthButton)); previousMonthButton.setMouseOverForeground(pp .getMouseOverForeground(previousMonthButton)); previousMonthButton.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { if (isEnabled()) { previousMonth(); } } }); } return previousMonthButton; } private CalendarButton getNextMonthButton() { if (nextMonthButton == null) { nextMonthButton = new CalendarButton(this, CalendarComponentType.NEXT_MONTH_BUTTON); PropertiesProvider pp = getPropertiesProvider(); nextMonthButton.setForeground(pp.getForeground(nextMonthButton)); nextMonthButton.setDisabledForeground(pp .getDisabledForeground(nextMonthButton)); nextMonthButton.setMouseOverForeground(pp .getMouseOverForeground(nextMonthButton)); nextMonthButton.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { if (isEnabled()) { nextMonth(); } } }); } return nextMonthButton; } private CalendarButton getNextYearButton() { if (nextYearButton == null) { nextYearButton = new CalendarButton(this, CalendarComponentType.NEXT_YEAR_BUTTON); PropertiesProvider pp = getPropertiesProvider(); nextYearButton.setForeground(pp.getForeground(nextYearButton)); nextYearButton.setDisabledForeground(pp .getDisabledForeground(nextYearButton)); nextYearButton.setMouseOverForeground(pp .getMouseOverForeground(nextYearButton)); nextYearButton.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { if (isEnabled()) { nextYear(); } } }); } return nextYearButton; } private MonthLabel getMonthLabel() { if (monthLabel == null) { monthLabel = new MonthLabel(this); monthLabel.setForeground(getPropertiesProvider() .getForeground(monthLabel)); } return monthLabel; } private YearLabel getYearLabel() { if (yearLabel == null) { yearLabel = new YearLabel(this); yearLabel.setForeground(getPropertiesProvider().getForeground(yearLabel)); } return yearLabel; } private boolean isWeeksEnabled() { return weeksEnabled; } private void addSlot(CalendarSlot cs, CalendarComponentType st) { List<CalendarSlot> list = slots.get(st); if (list == null) { list = new ArrayList<CalendarSlot>(); slots.put(st, list); } list.add(cs); getSlotsPanel().add(cs); } private void rebuild() { int columnsTotal = 7; if (isWeeksEnabled()) { columnsTotal++; } getSlotsPanel().removeAll(); getSlotsPanel().setLayout(new GridLayout(0, columnsTotal, 0, 0)); slots.clear(); if (isWeeksEnabled()) { addSlot(new WeekHeaderSlot(this), CalendarComponentType.WEEK_HEADER); } DayOfWeek week = getPropertiesProvider().getStartDayOfWeek(); for (int i = 0; i < 7; i++) { String name = weekNames[week.getJUDayOfWeek()]; addSlot(new DayOfWeekSlot(this, name, week), CalendarComponentType.DAY_WEEK_HEADER); week = week.nextDayOfWeek(); } for (int i = 0; i < columnsTotal * 6; i++) { if (isWeeksEnabled() && i % 8 == 0) { addSlot(new WeekSlot(this), CalendarComponentType.WEEK); } else { addSlot(new DayOfMonthSlot(this), CalendarComponentType.DAY_MONTH); } } getSlotsPanel().revalidate(); } public void update() { rebuild(); updateDate(); PropertiesProvider pp = getPropertiesProvider(); getPreviousYearButton().setForeground( pp.getForeground(getPreviousYearButton())); getPreviousMonthButton().setForeground( pp.getForeground(getPreviousMonthButton())); getNextMonthButton().setForeground(pp.getForeground(getNextMonthButton())); getNextYearButton().setForeground(pp.getForeground(getNextYearButton())); getPreviousYearButton().setDisabledForeground( pp.getDisabledForeground(getPreviousYearButton())); getPreviousMonthButton().setDisabledForeground( pp.getDisabledForeground(getPreviousMonthButton())); getNextMonthButton().setDisabledForeground( pp.getDisabledForeground(getNextMonthButton())); getNextYearButton().setDisabledForeground( pp.getDisabledForeground(getNextYearButton())); getPreviousYearButton().setMouseOverForeground( pp.getMouseOverForeground(getPreviousYearButton())); getPreviousMonthButton().setMouseOverForeground( pp.getMouseOverForeground(getPreviousMonthButton())); getNextMonthButton().setMouseOverForeground( pp.getMouseOverForeground(getNextMonthButton())); getNextYearButton().setMouseOverForeground( pp.getMouseOverForeground(getNextYearButton())); getMonthLabel().setForeground(pp.getForeground(getMonthLabel())); getYearLabel().setForeground(pp.getForeground(getYearLabel())); getTopPanel().setBackground(pp.getBackground(getTopPanel())); repaint(); } private void updateDate() { GregorianCalendar gc = new GregorianCalendar(); gc.setTimeInMillis(date); gc.set(GregorianCalendar.HOUR_OF_DAY, 0); gc.set(GregorianCalendar.MINUTE, 0); gc.set(GregorianCalendar.SECOND, 0); gc.set(GregorianCalendar.MILLISECOND, 0); date = gc.getTimeInMillis(); int currentDay = gc.get(GregorianCalendar.DAY_OF_MONTH); int currentMonth = gc.get(GregorianCalendar.MONTH); String monthName = monthNames[currentMonth]; getMonthLabel().setText(monthName); int currentYear = gc.get(GregorianCalendar.YEAR); getYearLabel().setText(Integer.toString(currentYear)); gc.set(GregorianCalendar.DAY_OF_MONTH, 1); int dayOfWeek = gc.get(GregorianCalendar.DAY_OF_WEEK); DayOfWeek week = DayOfWeek.getDayOfWeek(dayOfWeek); DayOfWeek s = getPropertiesProvider().getStartDayOfWeek(); int previous = 0; while (s.equals(week) == false) { s = s.nextDayOfWeek(); previous++; } gc.add(GregorianCalendar.DAY_OF_MONTH, (-1) * previous); List<CalendarSlot> list = slots.get(CalendarComponentType.DAY_MONTH); for (CalendarSlot sl : list) { DayOfMonthSlot dms = (DayOfMonthSlot) sl; int day = gc.get(GregorianCalendar.DAY_OF_MONTH); int month = gc.get(GregorianCalendar.MONTH); int year = gc.get(GregorianCalendar.YEAR); dms.setDate(day, month, year); if (month == currentMonth) { dms.setCurrentMonth(true); dms.setSelected(day == currentDay); } else { dms.setCurrentMonth(false); dms.setSelected(false); } gc.add(GregorianCalendar.DAY_OF_MONTH, 1); } repaint(); } public void previousYear() { GregorianCalendar gc = new GregorianCalendar(); gc.setTimeInMillis(date); gc.add(GregorianCalendar.YEAR, -1); setDate(gc.getTimeInMillis()); } public void nextYear() { GregorianCalendar gc = new GregorianCalendar(); gc.setTimeInMillis(date); gc.add(GregorianCalendar.YEAR, 1); setDate(gc.getTimeInMillis()); } public void nextMonth() { GregorianCalendar gc = new GregorianCalendar(); gc.setTimeInMillis(date); gc.add(GregorianCalendar.MONTH, 1); setDate(gc.getTimeInMillis()); } public void previousMonth() { GregorianCalendar gc = new GregorianCalendar(); gc.setTimeInMillis(date); gc.add(GregorianCalendar.MONTH, -1); setDate(gc.getTimeInMillis()); } public DayOfWeek getDayOfWeek(CalendarComponent cc) { if (cc instanceof DayOfMonthSlot) { DayOfMonthSlot slot = (DayOfMonthSlot) cc; return slot.getDayOfWeek(); } if (cc instanceof DayOfWeekSlot) { DayOfWeekSlot slot = (DayOfWeekSlot) cc; return slot.getDayOfWeek(); } return null; } public boolean isSelected(CalendarComponent cc) { if (cc instanceof DayOfMonthSlot) { DayOfMonthSlot slot = (DayOfMonthSlot) cc; return slot.isSelected(); } return false; } public boolean isCurrentMonth(CalendarComponent cc) { if (cc instanceof DayOfMonthSlot) { DayOfMonthSlot slot = (DayOfMonthSlot) cc; return slot.isCurrentMonth(); } return false; } public Integer getYear(CalendarComponent cc) { if (cc instanceof DayOfMonthSlot) { DayOfMonthSlot slot = (DayOfMonthSlot) cc; return slot.getYear(); } return null; } public Integer getMonth(CalendarComponent cc) { if (cc instanceof DayOfMonthSlot) { DayOfMonthSlot slot = (DayOfMonthSlot) cc; return slot.getMonth(); } return null; } public Integer getDay(CalendarComponent cc) { if (cc instanceof DayOfMonthSlot) { DayOfMonthSlot slot = (DayOfMonthSlot) cc; return slot.getDay(); } return null; } public Date getDate(CalendarComponent cc) { if (cc instanceof DayOfMonthSlot) { DayOfMonthSlot slot = (DayOfMonthSlot) cc; return slot.getDate(); } return null; } public void setDate(long date) { Date oldDate = null; if (this.date != null) { oldDate = new Date(this.date); } Date newDate = new Date(date); this.date = date; updateDate(); fireAllListeners(oldDate, newDate); } public Date getDate() { return new Date(date); } public boolean isDialogMode() { return dialogMode; } public void disposeDialog(long selectedTime) { if (isDialogMode()) { this.selectedDialogDate = selectedTime; getModalDialog().dispose(); } } JDialog getModalDialog() { if (modalDialog == null) { modalDialog = new JDialog(); ImageIcon ii = new ImageIcon(GUIImages.CALENDAR); modalDialog.setIconImage(ii.getImage()); modalDialog.add(this); modalDialog.pack(); modalDialog.setResizable(false); modalDialog.setModal(true); } return modalDialog; } public static Long showAsDialog(Locale locale, long date, String dialogTitle, Component relativeComponent) { return showAsDialog(locale, date, dialogTitle, relativeComponent, null); } public static Long showAsDialog(Locale locale, long date, String dialogTitle, Component relativeComponent, PropertiesProvider propertiesProvider) { Calendar calendar = new Calendar(locale); calendar.setDate(date); if (propertiesProvider != null) { calendar.setPropertiesProvider(propertiesProvider); } calendar.dialogMode = true; calendar.getModalDialog().setTitle(dialogTitle); calendar.getModalDialog().setLocationRelativeTo(relativeComponent); calendar.getModalDialog().setVisible(true); return calendar.selectedDialogDate; } }