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

jfxtras.labs.internal.scene.control.skin.CalendarPickerControlSkin Maven / Gradle / Ivy

There is a newer version: 9.0-r1
Show newest version
/**
 * Copyright (c) 2011, 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  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.labs.internal.scene.control.skin;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import jfxtras.labs.scene.control.CalendarPicker;
import jfxtras.labs.scene.control.CalendarTimePicker;
import jfxtras.labs.scene.control.ListSpinner;
import jfxtras.labs.scene.control.ListSpinner.CycleEvent;
import jfxtras.labs.scene.control.ListSpinnerIntegerList;

/**
 * This skin uses regular JavaFX controls
 * @author Tom Eugelink
 *
 */
public class CalendarPickerControlSkin extends CalendarPickerMonthlySkinAbstract
{
	// ==================================================================================================================
	// CONSTRUCTOR
	
	/**
	 * 
	 */
	public CalendarPickerControlSkin(CalendarPicker control)
	{
		super(control);
		construct();
	}

	/*
	 * construct the component
	 */
	private void construct()
	{
		// setup component
		createNodes();
		
		// start listening to changes
		// if the calendar changes, the display calendar will jump to show that
		getSkinnable().calendarProperty().addListener(new InvalidationListener()
		{
			
			@Override
			public void invalidated(Observable arg0)
			{
				if (getSkinnable().getCalendar() != null) 
				{
					setDisplayedCalendar(getSkinnable().getCalendar());
				}
			} 
		});
		if (getSkinnable().getCalendar() != null) 
		{
			setDisplayedCalendar(getSkinnable().getCalendar());
		}
		
		// if the calendars change, the selection must be refreshed
		getSkinnable().calendars().addListener(new ListChangeListener()  
		{
			@Override
			public void onChanged(javafx.collections.ListChangeListener.Change arg0)
			{
				refreshDayButtonToggleState();
			} 
		});
		
		// if the displayed calendar changes, the screen must be refreshed
		displayedCalendar().addListener(new InvalidationListener()
		{
			@Override
			public void invalidated(Observable arg0)
			{
				refresh();
			} 
		});
		
		// update the data
		refresh();
	}
	
	// ==================================================================================================================
	// PROPERTIES
	

	// ==================================================================================================================
	// DRAW
	
	/**
	 * construct the nodes
	 */
	private void createNodes()
	{
		// the result
		GridPane lGridPane = new GridPane();
		lGridPane.setVgap(2.0);
		lGridPane.setHgap(2.0);
		
		// setup the grid so all weekday togglebuttons will grow, but the weeknumbers do not
		ColumnConstraints lColumnConstraintsAlwaysGrow = new ColumnConstraints();
		lColumnConstraintsAlwaysGrow.setHgrow(Priority.ALWAYS);
		ColumnConstraints lColumnConstraintsNeverGrow = new ColumnConstraints();
		lColumnConstraintsNeverGrow.setHgrow(Priority.NEVER);
		lGridPane.getColumnConstraints().addAll(lColumnConstraintsNeverGrow, lColumnConstraintsAlwaysGrow, lColumnConstraintsAlwaysGrow, lColumnConstraintsAlwaysGrow, lColumnConstraintsAlwaysGrow, lColumnConstraintsAlwaysGrow, lColumnConstraintsAlwaysGrow, lColumnConstraintsAlwaysGrow);

		// month spinner
		List lMonthLabels = getMonthLabels();
		monthListSpinner = new ListSpinner(lMonthLabels).withIndex(Calendar.getInstance().get(Calendar.MONTH)).withCyclic(Boolean.TRUE);
		// on cycle overflow to year
		monthListSpinner.setOnCycle(new EventHandler()
		{
			@Override
			public void handle(CycleEvent evt)
			{
				// if we've cycled down
				if (evt.cycledDown()) 
				{
					yearListSpinner.increment();
				}
				else 
				{
					yearListSpinner.decrement();				
				}
					 
			}
		});
		// if the value changed, update the displayed calendar
		monthListSpinner.valueProperty().addListener(new ChangeListener()
		{
			@Override
			public void changed(ObservableValue arg0, String arg1, String arg2)
			{
				setDisplayedCalendarFromSpinners();
			}
		});
		lGridPane.add(monthListSpinner, 1, 0, 4, 1); // col, row, hspan, vspan
		
		// year spinner
		yearListSpinner = new ListSpinner(new ListSpinnerIntegerList()).withValue(Calendar.getInstance().get(Calendar.YEAR));
		// if the value changed, update the displayed calendar
		yearListSpinner.valueProperty().addListener(new ChangeListener()
		{
			@Override
			public void changed(ObservableValue observableValue, Integer oldValue, Integer newValue)
			{
				setDisplayedCalendarFromSpinners();
			}
		});
		lGridPane.add(yearListSpinner, 5, 0, 3, 1); // col, row, hspan, vspan
		
		// double click here to show today
		Label lTodayLabel = new Label("   ");
		lTodayLabel.onMouseClickedProperty().set(new EventHandler()
		{
			@Override
			public void handle(MouseEvent event)
			{
				if (event.getClickCount() < 1) return;
				setDisplayedCalendarToToday();
			}
		});
		lGridPane.add(lTodayLabel, 0, 1);  // col, row
		
		// weekday labels
		for (int i = 0; i < 7; i++)
		{
			// create buttons
			Label lLabel = new Label("" + i);
			// style class is set together with the label
			lLabel.getStyleClass().add("weekday-label"); 
			lLabel.setMaxWidth(Integer.MAX_VALUE); // this is one of those times; why the @#$@#$@#$ do I need to specify this in order to make the damn label centered?
			
			// add it
			lGridPane.add(lLabel, i + 1, 1);  // col, row
			
			// remember the column it is associated with
			lLabel.setUserData(Integer.valueOf(i));
			lLabel.onMouseClickedProperty().set(weekdayLabelMouseClickedPropertyEventHandler);

			// remember it
			weekdayLabels.add(lLabel);
		}
		
		// weeknumber labels
		for (int i = 0; i < 6; i++)
		{
			// create buttons
			Label lLabel = new Label("" + i);
			lLabel.getStyleClass().add("weeknumber");
			lLabel.setAlignment(Pos.BASELINE_RIGHT);
			
			// remember it
			weeknumberLabels.add(lLabel);
			
			// remember the row it is associated with
			lLabel.setUserData(Integer.valueOf(i));
			lLabel.onMouseClickedProperty().set(weeknumerLabelMouseClickedPropertyEventHandler);

			// first of a row: add the weeknumber
			lGridPane.add(weeknumberLabels.get(i), 0, i + 2);  // col, row
		}
		
		// setup: 6 rows of 7 days per week (which is the maximum number of buttons required in the worst case layout)
		for (int i = 0; i < 6 * 7; i++)
		{
			// create buttons
			ToggleButton lToggleButton = new ToggleButton("" + i);
			lToggleButton.getStyleClass().add("day-button");
			lToggleButton.selectedProperty().addListener(toggleButtonSelectedPropertyChangeListener); // for minimal memory usage, use a single listener
			lToggleButton.onMouseReleasedProperty().set(toggleButtonMouseReleasedPropertyEventHandler); // for minimal memory usage, use a single listener
			lToggleButton.onKeyReleasedProperty().set(toggleButtonKeyReleasedPropertyEventHandler); // for minimal memory usage, use a single listener
			
			// remember which button belongs to this property
			booleanPropertyToDayToggleButtonMap.put(lToggleButton.selectedProperty(), lToggleButton);
			
			// add it
			lGridPane.add(lToggleButton, (i % 7) + 1, (i / 7) + 2);  // col, row
			lToggleButton.setMaxWidth(Double.MAX_VALUE); // make the button grow to fill a GridPane's cell
			lToggleButton.setAlignment(Pos.BASELINE_CENTER);
			
			// remember it
			dayButtons.add(lToggleButton);
		}

		// add timepicker
		// TODO: this is done upon construction, we need to make this dynamic based on Mode and showTime
		if (getSkinnable().getMode().equals(CalendarPicker.Mode.SINGLE) && getSkinnable().showTimeProperty().get() == true)
		{
			lGridPane.add(timePicker, 1, 8, 7, 1); // col, row, hspan, vspan
			Bindings.bindBidirectional(timePicker.calendarProperty(), getSkinnable().calendarProperty()); 
		}
		
		// add to self
		this.getStyleClass().add(this.getClass().getSimpleName()); // always add self as style class, because CSS should relate to the skin not the control
		getChildren().add(lGridPane);
	}
	private ListSpinner monthListSpinner = null;
	private ListSpinner yearListSpinner = null;
	final private List




© 2015 - 2024 Weber Informatics LLC | Privacy Policy