All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jfxtras.internal.scene.control.skin.CalendarPickerMonthlySkinAbstract Maven / Gradle / Ivy

There is a newer version: 17-r1
Show newest version
/**
 * CalendarPickerMonthlySkinAbstract.java
 *
 * Copyright (c) 2011-2014, JFXtras
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the organization nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL  BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package jfxtras.internal.scene.control.skin;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import javafx.beans.InvalidationListener;
import javafx.scene.control.SkinBase;
import jfxtras.scene.control.CalendarPicker;

/**
 * This class contains common code to support skins that shows a month at once.
 * It assumes that there is a grid of clickables, one for every day of the month, and provides methods to help handle these.
 *  
 * @author Tom Eugelink
 *
 * @param  the actual skin class, so fluent methods return the correct class (see "return (S)this;")
 */
abstract public class CalendarPickerMonthlySkinAbstract extends SkinBase
{
	// ==================================================================================================================
	// CONSTRUCTOR
	
	/**
	 * 
	 */
	public CalendarPickerMonthlySkinAbstract(CalendarPicker control)
	{
		super(control);//, new CalendarPickerBehavior(control));
		construct();
	}

	/*
	 * 
	 */
	private void construct()
	{
		// react to changes in the locale
		getSkinnable().localeProperty().addListener( (InvalidationListener) observable -> {
			refreshLocale();
		});
		refreshLocale();

		// modify the calendar the way we like it
		derivedDisplayedCalendar(); // this must be done before installing the listener, otherwise the refresh will be called to early
		getSkinnable().displayedCalendar().addListener( (InvalidationListener) observable -> {
			// adapt to our needs
			Calendar lDisplayedCalendar = getSkinnable().getDisplayedCalendar();
			if ((myDisplayedCalendar == null && lDisplayedCalendar != null)
				|| myDisplayedCalendar.equals(lDisplayedCalendar) == false
				) {
				derivedDisplayedCalendar();

				// wait for this change to report itself (but then not enter this if statement)
				return;
			}

			// update
			refresh();
		});
	}
	private Calendar myDisplayedCalendar = null;

	/**
	 *
	 */
	private void derivedDisplayedCalendar() {
		// modify the calender the way we like it
		Calendar lDisplayedCalendar = getSkinnable().getDisplayedCalendar();
		Calendar lCalendar = deriveDisplayedCalendar(lDisplayedCalendar);

		// assign it
		myDisplayedCalendar = lCalendar;
		getSkinnable().displayedCalendar().set(lCalendar);

	}

	// ==================================================================================================================
	// PROPERTIES
	
	/**
	 * 
	 */
	private void refreshLocale()
	{
		// create the formatter to use
		simpleDateFormat = (SimpleDateFormat)SimpleDateFormat.getDateInstance(SimpleDateFormat.LONG, getSkinnable().getLocale());
	}
	private SimpleDateFormat simpleDateFormat = null;


	// ==================================================================================================================
	// SUPPORT

	// modify the displayed date to match our requirements (meaning same locale, etc)
	abstract protected Calendar deriveDisplayedCalendar(Calendar displayedCalendar);

	// refresh the skin
	abstract protected void refresh();

	/**
	 * 
	 */
	protected void calendarRangeCallback()
	{
		if (getSkinnable().calendarRangeCallbackProperty().get() != null) {
			// start and end
			Calendar lStartCalendar = periodStartCalendar(); 
			Calendar lEndCalendar = periodEndCalendar();
			try
			{
				calendarRangeCallbackAtomicInteger.incrementAndGet();
				getSkinnable().calendarRangeCallbackProperty().get().call(new CalendarPicker.CalendarRange(lStartCalendar, lEndCalendar));
			}
			finally
			{
				calendarRangeCallbackAtomicInteger.decrementAndGet();
			}
		}
	}
	protected final AtomicInteger calendarRangeCallbackAtomicInteger = new AtomicInteger(0);

	/**
	 * 
	 * @return
	 */
	protected Calendar periodStartCalendar()
	{
		return (Calendar)getSkinnable().getDisplayedCalendar().clone();
	}
	
	/**
	 * 
	 * @return
	 */
	protected Calendar periodEndCalendar()
	{
		Calendar lEndCalendar = (Calendar)getSkinnable().getDisplayedCalendar().clone();
		lEndCalendar.add(java.util.Calendar.MONTH, 1);
		lEndCalendar.set(java.util.Calendar.DATE, 1);
		lEndCalendar.add(java.util.Calendar.DATE, -1);
		return lEndCalendar;				
	}
	
	/**
	 * get the weekday labels starting with the weekday that is the first-day-of-the-week according to the locale in the displayed calendar
	 */
	protected List getWeekdayLabels()
	{
		// result
		List lWeekdayLabels = new ArrayList();

		// setup the dayLabels
		// Calendar.SUNDAY = 1 and Calendar.SATURDAY = 7
		simpleDateFormat.applyPattern("E");
		Calendar lCalendar = new java.util.GregorianCalendar(2009, 6, 5); // july 5th 2009 is a Sunday
		for (int i = 0; i < 7; i++)
		{
			// next
			lCalendar.set(java.util.Calendar.DATE, 4 + getSkinnable().getDisplayedCalendar().getFirstDayOfWeek() + i);

			// assign day
			lWeekdayLabels.add( simpleDateFormat.format(lCalendar.getTime()));
		}
		
		// done
		return lWeekdayLabels;
	}
	
	/**
	 * Get a list with the weeklabels
	 */
	protected List getWeeknumbers()
	{
		// result
		List lWeekLabels = new ArrayList();

		// setup the weekLabels
		Calendar lCalendar = (Calendar)getSkinnable().getDisplayedCalendar().clone();
		for (int i = 0; i <= 5; i++)
		{
			// set label
			lWeekLabels.add( lCalendar.get(java.util.Calendar.WEEK_OF_YEAR) );

			// next week
			lCalendar.add(java.util.Calendar.DATE, 7);
		}

		// done
		return lWeekLabels;
	}

	/**
	 * get the weekday labels starting with the weekday that is the first-day-of-the-week according to the locale in the displayed calendar
	 */
	protected List getMonthLabels()
	{
		// result
		List lMonthLabels = new ArrayList();

		// setup the month
		simpleDateFormat.applyPattern("MMMM");
		Calendar lCalendar = new java.util.GregorianCalendar(2009, 0, 1); 
		for (int i = 0; i < 12; i++)
		{
			// next
			lCalendar.set(java.util.Calendar.MONTH, i);

			// assign day
			lMonthLabels.add( simpleDateFormat.format(lCalendar.getTime()));
		}
		
		// done
		return lMonthLabels;
	}
	
	/**
	 * check if a certain weekday name is a certain day-of-the-week
	 */
	protected boolean isWeekday(int idx, int weekdaynr)
	{
		// setup the dayLabels
		// Calendar.SUNDAY = 1 and Calendar.SATURDAY = 7
		Calendar lCalendar = new java.util.GregorianCalendar(2009, 6, 4 + getSkinnable().getDisplayedCalendar().getFirstDayOfWeek()); // july 5th 2009 is a Sunday
		lCalendar.add(java.util.Calendar.DATE, idx);
		int lDayOfWeek = lCalendar.get(java.util.Calendar.DAY_OF_WEEK);

		// check
		return (lDayOfWeek == weekdaynr);
	}

	/**
	 * check if a certain weekday name is a certain day-of-the-week
	 */
	protected boolean isWeekdayWeekend(int idx) 
	{
		return (isWeekday(idx, java.util.Calendar.SATURDAY) || isWeekday(idx, java.util.Calendar.SUNDAY));
	}
	
	/**
	 * determine on which day of week idx the first of the months is
	 */
	protected int determineFirstOfMonthDayOfWeek()
	{
		// determine with which button to start
		int lFirstDayOfWeek = getSkinnable().getDisplayedCalendar().getFirstDayOfWeek();
		int lFirstOfMonthIdx = getSkinnable().getDisplayedCalendar().get(java.util.Calendar.DAY_OF_WEEK) - lFirstDayOfWeek;
		if (lFirstOfMonthIdx < 0) lFirstOfMonthIdx += 7;
		return lFirstOfMonthIdx;
	}
	
	/**
	 * determine the number of days in the month
	 */
	protected int determineDaysInMonth()
	{
		// determine the number of days in the month
		Calendar lCalendar = (Calendar)getSkinnable().getDisplayedCalendar().clone();
		lCalendar.add(java.util.Calendar.MONTH, 1);
		lCalendar.set(java.util.Calendar.DATE, 1);
		lCalendar.add(java.util.Calendar.DATE, -1);
		return lCalendar.get(java.util.Calendar.DAY_OF_MONTH);
	}

	/**
	 * determine if a date is today
	 */
	protected boolean isToday(Calendar calendar)
	{
		int lYear = calendar.get(java.util.Calendar.YEAR);
		int lMonth = calendar.get(java.util.Calendar.MONTH);
		int lDay = calendar.get(java.util.Calendar.DATE);
		return (lYear == iTodayYear && lMonth == iTodayMonth && lDay == iTodayDay);
	}
	private Calendar iToday = java.util.Calendar.getInstance();
	private int iTodayYear = iToday.get(java.util.Calendar.YEAR);
	private int iTodayMonth = iToday.get(java.util.Calendar.MONTH);
	private int iTodayDay = iToday.get(java.util.Calendar.DATE);
}