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

jfxtras.labs.internal.scene.control.skin.CalendarTimePickerSkin 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.Calendar;

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.control.Slider;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import jfxtras.labs.internal.scene.control.behavior.CalendarTimePickerBehavior;
import jfxtras.labs.scene.control.CalendarTimePicker;

import com.sun.javafx.scene.control.skin.SkinBase;

/**
 * @author Tom Eugelink
 *
 */
public class CalendarTimePickerSkin extends SkinBase
{
	// ==================================================================================================================
	// CONSTRUCTOR
	
	/**
	 * 
	 */
	public CalendarTimePickerSkin(CalendarTimePicker control)
	{
		super(control, new CalendarTimePickerBehavior(control));
		construct();
	}

	/*
	 * construct the component
	 */
	private void construct()
	{	
		// setup component
		createNodes();
		
		// react to changes in the calendar 
		getSkinnable().calendarProperty().addListener(new InvalidationListener() 
		{
			@Override
			public void invalidated(Observable observable)
			{
				// paint
				refresh();
			} 
		});
		refresh();

		// react to changes in the minuteStep 
		getSkinnable().minuteStepProperty().addListener(new InvalidationListener() 
		{
			@Override
			public void invalidated(Observable observable)
			{
				minuteScrollSlider.setBlockIncrement(getSkinnable().getMinuteStep().doubleValue());
			} 
		});
		minuteScrollSlider.setBlockIncrement(getSkinnable().getMinuteStep().doubleValue());
		
		// react to changes in showLabels 
		getSkinnable().showLabelsProperty().addListener(new InvalidationListener() 
		{
			@Override
			public void invalidated(Observable observable)
			{
				// paint
				refreshLayout();
			} 
		});
	}
	
	// ==================================================================================================================
	// PROPERTIES
	

	// ==================================================================================================================
	// DRAW
	
    /**
	 * construct the nodes
	 */
	private void createNodes()
	{
		// two sliders
		hourScrollSlider.minProperty().set(00);
		hourScrollSlider.maxProperty().set(23);
//		hourScrollSlider.setShowTickLabels(true);
//		hourScrollSlider.setShowTickMarks(true);
		hourScrollSlider.setMajorTickUnit(12);
		hourScrollSlider.setMinorTickCount(3);
		minuteScrollSlider.minProperty().set(00);
		minuteScrollSlider.maxProperty().set(59);
//		minuteScrollSlider.setShowTickLabels(true);
//		minuteScrollSlider.setShowTickMarks(true);
		minuteScrollSlider.setMajorTickUnit(10);
		hourScrollSlider.valueProperty().addListener(new ChangeListener()
		{
			@Override
			public void changed(ObservableValue observable, Number oldValue, Number newValue)
			{
				Calendar lCalendar = (Calendar)getSkinnable().getCalendar();
				lCalendar = (lCalendar == null ? Calendar.getInstance() : (Calendar)lCalendar.clone());
				lCalendar.set(Calendar.HOUR_OF_DAY, newValue.intValue());
				getSkinnable().setCalendar(lCalendar);
			}
		});
		minuteScrollSlider.valueProperty().addListener(new ChangeListener()
		{
			@Override
			public void changed(ObservableValue observable, Number oldValue, Number newValue)
			{
				Calendar lCalendar = (Calendar)getSkinnable().getCalendar();
				lCalendar = (lCalendar == null ? Calendar.getInstance() : (Calendar)lCalendar.clone());
				
				// in order no to first set a non stepsize calendar, we step the minutes here 
				int lMinutes = newValue.intValue();
				int lMinuteStep = getSkinnable().getMinuteStep();
				if (lMinuteStep > 1)
				{
					lMinutes += getSkinnable().getMinuteStep() / 2; // add half a step, so the scroller jumps to the next tick when the mouse is half way
					if (lMinutes > 59) lMinutes -= lMinuteStep;
				}
				lCalendar.set(Calendar.MINUTE, lMinutes);
				lCalendar = blockMinutesToStep(lCalendar, getSkinnable().getMinuteStep());
				getSkinnable().setCalendar(lCalendar);
			}
		});
		
		// add label
		timeText.setDisable(true);
		timeText.getStyleClass().add("timeLabel");

		// refresh layout
		refreshLayout();
		
		// add self as CSS style
		this.getStyleClass().add(this.getClass().getSimpleName()); // always add self as style class, because CSS should relate to the skin not the control		
	}
	final private Slider hourScrollSlider = new Slider();
	final private Slider minuteScrollSlider = new Slider();
	final private Text timeText = new Text("XX:XX");
	final Pane hourLabelsPane = new Pane()
	{
		{
			prefWidthProperty().bind(hourScrollSlider.prefWidthProperty());
			layoutChildren();
			//setStyle("-fx-border-color: red; -fx-border-width:1px;");
		}
		
		protected void layoutChildren()
		{
			getChildren().clear();
			
			// get some basic numbers
			double lLabelWidth = new Text("88").prefWidth(0);
			double lWhitespace = lLabelWidth / 2;
			double lLabelWidthPlusWhitespace = lLabelWidth + lWhitespace;
			double lScrollSliderOuterPadding = 5;

			// add a dummy rectangle to make sure the are has enough height
			{
				Text lText = new Text("0");
				Rectangle lRectangle = new Rectangle(0,0, minuteScrollSlider.getWidth(), lText.prefHeight(0));
				lRectangle.setFill(Color.TRANSPARENT);
				getChildren().add(lRectangle);
			}

			// now we're going to play with some numbers
			// given the available width, how many labels cold we place (rounded down)
			int lNumberOfLabels = (int)(this.getWidth() / lLabelWidthPlusWhitespace) + 2;
			int lStep = 24;
			if (lNumberOfLabels >= 24/1) lStep = 1; 
			else if (lNumberOfLabels >= 24/2) lStep = 2;
			else if (lNumberOfLabels >= 24/3) lStep = 3;
			else if (lNumberOfLabels >= 24/4) lStep = 4;
			else if (lNumberOfLabels > 24/6) lStep = 6;			
			else if (lNumberOfLabels > 24/12) lStep = 12;			
			for (int i = 0; i < 24; i += lStep)
			{
				Text lText = new Text("" + i);
				lText.setY(lText.prefHeight(0));
				double lX = (lScrollSliderOuterPadding + ((minuteScrollSlider.getWidth() - (2*lScrollSliderOuterPadding)) / 23 * i)) - (lText.prefWidth(0) 
						  / (i == 23 ? 1 : 2) // center, except the most right 
						  * ( i == 0 ? 0 : 1)); // and the most left
				lText.setX(lX);
				getChildren().add(lText);
			}
			for (int i = 0; i < 24; i += 1)
			{
				Text lText = new Text("0");
				double lX = (lScrollSliderOuterPadding + ((minuteScrollSlider.getWidth() - (2*lScrollSliderOuterPadding)) / 23 * i));
				getChildren().add(new Line(lX, lText.prefHeight(0) + 3, lX, lText.prefHeight(0) + 3 + 3));
			}
		}
	};
	final Pane minuteLabelsPane = new Pane()
	{
		{
			layoutChildren();
			//setStyle("-fx-border-color: red; -fx-border-width:1px;");
		}
		
		protected void layoutChildren()
		{
			getChildren().clear();
			
			// get some basic numbers
			double lLabelWidth = new Text("88").prefWidth(0);
			double lWhitespace = lLabelWidth / 2;
			double lLabelWidthPlusWhitespace = lLabelWidth + lWhitespace;
			double lScrollSliderOuterPadding = 5;

			// add a dummy rectangle to make sure the are has enough height
			if (getSkinnable().showLabelsProperty().get())
			{
				Text lText = new Text("0");
				Rectangle lRectangle = new Rectangle(0,0, minuteScrollSlider.getWidth(), lText.prefHeight(0));
				lRectangle.setFill(Color.TRANSPARENT);
				getChildren().add(lRectangle);
			}

			// now we're going to play with some numbers
			// given the available width, how many labels cold we place (rounded down)
			int lNumberOfLabels = (int)(this.getWidth()  / lLabelWidthPlusWhitespace) + 2;
			int lStep = 60;
			if (lNumberOfLabels >= 60/1) lStep = 1; 
			else if (lNumberOfLabels >= 60/2) lStep = 2;
			else if (lNumberOfLabels >= 60/3) lStep = 3;
			else if (lNumberOfLabels >= 60/4) lStep = 4;
			else if (lNumberOfLabels >= 60/5) lStep = 5;
			else if (lNumberOfLabels >= 60/10) lStep = 10;
			else if (lNumberOfLabels >= 60/15) lStep = 15;			
			else if (lNumberOfLabels >= 60/30) lStep = 30;
			if (lStep < getSkinnable().getMinuteStep()) lStep = getSkinnable().getMinuteStep();
			for (int i = 0; i <= 59; i += lStep)
			{
				Text lText = new Text("" + i);
				lText.setY(lText.prefHeight(0));
				double lX = (lScrollSliderOuterPadding + ((minuteScrollSlider.getWidth() - (2*lScrollSliderOuterPadding)) / 59 * i)) - (lText.prefWidth(0) 
						  / (i == 59? 1 : 2) // center, except the most right 
						  * ( i == 0 ? 0 : 1)); // and the most left
				lText.setX(lX);
				getChildren().add(lText);
			}
			for (int i = 0; i <= 59; i += 1)
			{
				double lX = (lScrollSliderOuterPadding + ((minuteScrollSlider.getWidth() - (2*lScrollSliderOuterPadding)) / 59 * i)); 
				getChildren().add(new Line(lX, 0, lX, 3));
			}
		}
	};
	
	/**
	 * 
	 */
	private void refreshLayout()
	{
		// layout
		getChildren().clear();
		VBox lVBox = new VBox(0);
		lVBox.alignmentProperty().set(Pos.CENTER);
		if (getSkinnable().getShowLabels()) lVBox.getChildren().add(hourLabelsPane);
		lVBox.getChildren().add(hourScrollSlider);
		lVBox.getChildren().add(minuteScrollSlider);
		if (getSkinnable().getShowLabels()) lVBox.getChildren().add(minuteLabelsPane);
		getChildren().add(lVBox);
		getChildren().add(timeText);
	}
	
	/**
	 * 
	 */
	private void refresh()
	{
		Calendar lCalendar = getSkinnable().getCalendar();
		int lHour = lCalendar == null ? 0 : lCalendar.get(Calendar.HOUR_OF_DAY);
		int lMinute = lCalendar == null ? 0 : lCalendar.get(Calendar.MINUTE);
		hourScrollSlider.valueProperty().set(lHour);
		minuteScrollSlider.valueProperty().set(lMinute);
		timeText.setText( calendarTimeToText(lCalendar));
	}
	
	/**
	 * 
	 * @param calendar
	 * @return
	 */
	static public String calendarTimeToText(Calendar calendar)
	{
		if (calendar == null) return "";
		int lHour = calendar.get(Calendar.HOUR_OF_DAY);
		int lMinute = calendar.get(Calendar.MINUTE);
		String lText = (lHour < 10 ? "0" : "") + lHour + ":" + (lMinute < 10 ? "0" : "") + lMinute;
		return lText;
	}
	
	/**
	 * minutes fit in the minute steps
	 */
	static public Calendar blockMinutesToStep(Calendar calendar, Integer stepSize)
	{
		if (stepSize == null || calendar == null) return calendar;
			
		// set the minutes to match the step size
		int lMinutes = calendar.get(Calendar.MINUTE);
		if (stepSize == 1) return calendar;
		lMinutes = lMinutes / stepSize; // trunk
		lMinutes *= stepSize;
		if (calendar.get(Calendar.MINUTE) != lMinutes)
		{
			Calendar lCalendar = (Calendar)calendar.clone();
			lCalendar.set(Calendar.MINUTE, lMinutes);
			calendar = lCalendar;
		}
		return calendar;
	}


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy