Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* ListSpinner.java
*
* Copyright (c) 2011-2016, 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.scene.control;
import java.util.Arrays;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.event.EventTarget;
import javafx.event.EventType;
import javafx.scene.Node;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.control.Skin;
import javafx.util.Callback;
import javafx.util.StringConverter;
/**
* This is a spinner, showing one value at a time from a list.
* This value is set and retrieved through the value property.
* Basically a spinner shows a list of values and can do a "next" or "previous".
*
* A spinner can be editable, the user can then type a value instead of selecting it.
* If the value exists in the list, the spinner will simply jump to it.
* If the value does not exist, the AddCallback is called if defined.
*
* - If the AddCallback returns null, spinner will only refresh the current index.
* - If the AddCallback returns an Integer, spinner will jump to that index (usually the index where the new value was added to the list).
*
*
* '''
*
* In the default skin you can style the text in the control using CSS like so:
* [source,css]
* --
* .ListSpinner .value {
* -fx-font-weight: bold;
* }
* --
*
* The "value" class applies to the text in both readonly and editable spinners, use the "readonly" or "editable" class to style either mode specifically.
* There is a left-arrow, right-arrow, up-arrow and down-arrow class that uses a SVG path to draw the arrow, this can be overridden with another SVG to draw a different shape.
*
* The default skin has a number of styleable properies which use the text representation of an enum for their value:
*
* [source,css]
* --
* .ListSpinner {
* -fxx-arrow-position: {LEADING, TRAILING, SPLIT}
* -fxx-arrow-direction: {VERTICAL, HORIZONTAL}
* -fxx-value-alignment: see javafx.geometry.Pos (https://docs.oracle.com/javase/8/javafx/api/javafx/geometry/Pos.html)
* }
* --
*
* @author Tom Eugelink
*/publicclassListSpinnerextendsControl{
// TODO: implement SelectionModel?// ==================================================================================================================// CONSTRUCTOR/**
*/publicListSpinner(){
construct();
}
// ------------// model/**
* @param items The item list used to populate the spinner.
*/publicListSpinner(ObservableList items){
construct();
setItems(items);
first();
}
/**
* @param items The item list used to populate the spinner.
* @param startValue The initial value of the spinner (one of the items).
*/publicListSpinner(ObservableList items, T startValue){
construct();
setItems(items);
setValue(startValue);
}
// ------------// convenience/**
* @param list
*/publicListSpinner(final java.util.List list){
this( FXCollections.observableList(list) );
}
/**
* @param list
*/publicListSpinner(T... list){
this( Arrays.asList(list) );
}
/**
* @param from
* @param to
*/publicListSpinner(int from, int to){
this( (java.util.List) new ListSpinnerIntegerList(from, to) );
}
/**
* @param from
* @param to
* @param step
*/publicListSpinner(int from, int to, int step){
this( (java.util.List) new ListSpinnerIntegerList(from, to, step) );
}
// ------------/*
*
*/privatevoidconstruct(){
// setup the CSS// the -fx-skin attribute in the CSS sets which Skin class is usedthis.getStyleClass().add(ListSpinner.class.getSimpleName());
// react to changes of the valuethis.valueObjectProperty.addListener(new ChangeListener()
{
@Overridepublicvoidchanged(ObservableValue property, T oldValue, T newValue){
// get the value of the new indexint lIdx = getItems().indexOf(newValue);
// set the valueif (ListSpinner.equals(indexObjectProperty.getValue(), lIdx) == false)
{
indexObjectProperty.setValue(lIdx);
}
}
});
// react to changes of the indexthis.indexObjectProperty.addListener(new ChangeListener()
{
@Overridepublicvoidchanged(ObservableValue property, Integer oldIndex, Integer newIndex){
// get the value of the new index
T lValue = newIndex < 0 ? null : getItems().get(newIndex);
// set the valueif (ListSpinner.equals(valueObjectProperty.getValue(), lValue) == false)
{
valueObjectProperty.setValue(lValue);
}
}
});
// react to changes of the itemsthis.itemsObjectProperty.addListener(new ChangeListener>()
{
@Overridepublicvoidchanged(ObservableValue> property, ObservableList oldList, ObservableList newList){
if (oldList != null) oldList.removeListener(listChangeListener);
if (newList != null) newList.addListener(listChangeListener);
}
});
}
/*
* react to observable list changes
* TODO: what is sticky, index or value? Now: index
*/private ListChangeListener listChangeListener = new ListChangeListener()
{
@OverridepublicvoidonChanged(javafx.collections.ListChangeListener.Change change){
// get current indexint lIndex = getIndex();
// is it still valid?if (lIndex >= getItems().size())
{
lIndex = getItems().size() - 1;
setIndex(lIndex);
return;
}
// (re)set the value of the index
valueObjectProperty.setValue( getItems().get(lIndex) );
}
};
/**
* Return the path to the CSS file so things are setup right
*/@Overridepublic String getUserAgentStylesheet(){
return ListSpinner.class.getResource("/jfxtras/internal/scene/control/" + ListSpinner.class.getSimpleName() + ".css").toExternalForm();
}
@Overridepublic Skin createDefaultSkin(){
returnnew jfxtras.internal.scene.control.skin.ListSpinnerSkin(this);
}
// ==================================================================================================================// PROPERTIES/** Id */public ListSpinnerwithId(String value){ setId(value); returnthis; }
/** Value: the currently show value of the list. */public ObjectPropertyvalueProperty(){ returnthis.valueObjectProperty; }
finalprivate ObjectProperty valueObjectProperty = new SimpleObjectProperty(this, "value", null)
{
publicvoidset(T value){
if (getItems().indexOf(value) < 0) thrownew IllegalArgumentException("Value does not exist in the list: " + value);
super.set(value);
}
};
// java bean APIpublic T getValue(){ returnthis.valueObjectProperty.getValue(); }
publicvoidsetValue(T value){ this.valueObjectProperty.setValue(value); }
public ListSpinnerwithValue(T value){ setValue(value); returnthis; }
/** Index: the currently show index in the list. */public ObjectPropertyindexProperty(){ returnthis.indexObjectProperty; }
finalprivate ObjectProperty indexObjectProperty = new SimpleObjectProperty(this, "index", null)
{
publicvoidset(Integer value){
if (value == null) thrownew NullPointerException("Null not allowed as the value for index");
if (value >= getItems().size()) thrownew IllegalArgumentException("Index out of bounds: " + value + ", valid values are 0-" + (getItems().size() - 1));
super.set(value);
}
};
public Integer getIndex(){ returnthis.indexObjectProperty.getValue(); }
publicvoidsetIndex(Integer value){ this.indexObjectProperty.setValue(value); }
public ListSpinnerwithIndex(Integer value){ setIndex(value); returnthis; }
/** Cyclic: what happens at the beginning or end of the list, stop or cycle to the other end. */public ObjectPropertycyclicProperty(){ returnthis.cyclicObjectProperty; }
finalprivate ObjectProperty cyclicObjectProperty = new SimpleObjectProperty(this, "cyclic", false)
{
publicvoidset(Boolean value){
if (value == null) thrownew NullPointerException("Null not allowed as the value for cyclic");
super.set(value);
}
};
public Boolean isCyclic(){ returnthis.cyclicObjectProperty.getValue(); }
publicvoidsetCyclic(Boolean value){ this.cyclicObjectProperty.setValue(value); }
public ListSpinnerwithCyclic(Boolean value){ setCyclic(value); returnthis; }
/** Editable: is the listspinner editable. It allows the user to type a value instead of only navigating to it, and if the AddCallback is defined, possibly also adding values. */public ObjectPropertyeditableProperty(){ returnthis.editableObjectProperty; }
finalprivate ObjectProperty editableObjectProperty = new SimpleObjectProperty(this, "editable", false)
{
publicvoidset(Boolean value){
if (value == null) thrownew NullPointerException("Null not allowed as the value for editable");
super.set(value);
}
};
public Boolean isEditable(){ returnthis.editableObjectProperty.getValue(); }
publicvoidsetEditable(Boolean value){ this.editableObjectProperty.setValue(value); }
public ListSpinnerwithEditable(Boolean value){ setEditable(value); returnthis; }
/** Postfix: a string to be placed after the value, this can for example be a unit like "kg" */public ObjectPropertypostfixProperty(){ returnthis.postfixObjectProperty; }
finalprivate ObjectProperty postfixObjectProperty = new SimpleObjectProperty(this, "postfix", "");
public String getPostfix(){ returnthis.postfixObjectProperty.getValue(); }
publicvoidsetPostfix(String value){ this.postfixObjectProperty.setValue(value); }
public ListSpinnerwithPostfix(String value){ setPostfix(value); returnthis; }
/** Prefix: a string to be placed before the list value, this can for example be a currency */public ObjectPropertyprefixProperty(){ returnthis.prefixObjectProperty; }
finalprivate ObjectProperty prefixObjectProperty = new SimpleObjectProperty(this, "prefix", "");
public String getPrefix(){ returnthis.prefixObjectProperty.getValue(); }
publicvoidsetPrefix(String value){ this.prefixObjectProperty.setValue(value); }
public ListSpinnerwithPrefix(String value){ setPrefix(value); returnthis; }
/** Items: the list. */public ObjectProperty> itemsProperty() { returnthis.itemsObjectProperty; }
finalprivate ObjectProperty> itemsObjectProperty = new SimpleObjectProperty>(this, "items", null)
{
publicvoidset(ObservableList value){
if (value == null) thrownew NullPointerException("Null not allowed as the value for items");
super.set(value);
}
};
public ObservableListgetItems(){ returnthis.itemsObjectProperty.getValue(); }
publicvoidsetItems(ObservableList value){ this.itemsObjectProperty.setValue(value); }
public ListSpinnerwithItems(ObservableList value){ setItems(value); returnthis; }
/** CellFactory: generate the cell to render a value */public ObjectProperty, Node>> cellFactoryProperty() { returnthis.cellFactoryObjectProperty; }
finalprivate ObjectProperty, Node>> cellFactoryObjectProperty = new SimpleObjectProperty, Node>>(this, "cellFactory", new DefaultCellFactory());
public Callback, Node> getCellFactory() { returnthis.cellFactoryObjectProperty.getValue(); }
publicvoidsetCellFactory(Callback, Node> value){ this.cellFactoryObjectProperty.setValue(value); }
public ListSpinnerwithCellFactory(Callback, Node> value){ setCellFactory(value); returnthis; }
/** StringConverter<T>: convert a value in the list to its string representation and (when in edit mode) vice versa. */public ObjectProperty> stringConverterProperty() { returnthis.stringConverterObjectProperty; }
finalprivate ObjectProperty> stringConverterObjectProperty = new SimpleObjectProperty>(this, "stringConverter", new DefaultStringConverter());
public StringConvertergetStringConverter(){ returnthis.stringConverterObjectProperty.getValue(); }
publicvoidsetStringConverter(StringConverter value){ this.stringConverterObjectProperty.setValue(value); }
public ListSpinnerwithStringConverter(StringConverter value){ setStringConverter(value); returnthis; }
/** AddCallback: this callback is called in editable mode when a value is entered that is not found in the list.
* It is up to the coder to added it to the list or not.
* @return the index where of the position the ListSpinner must show or null (do nothing expect refresh the currently show index)
*/public ObjectProperty> addCallbackProperty() { returnthis.addCallbackObjectProperty; }
finalprivate ObjectProperty> addCallbackObjectProperty = new SimpleObjectProperty>(this, "addCallback", null);
public CallbackgetAddCallback(){ returnthis.addCallbackObjectProperty.getValue(); }
publicvoidsetAddCallback(Callback value){ this.addCallbackObjectProperty.setValue(value); }
public ListSpinnerwithAddCallback(Callback value){ setAddCallback(value); returnthis; }
// ==================================================================================================================// StringConverter/**
* A string converter that does a simple toString, but cannot convert to an object
* @see org.jfxextras.util.StringConverterFactory
*/classDefaultStringConverterextendsStringConverter{
@Overridepublic T fromString(String string){
thrownew IllegalStateException("No StringConverter is set. An editable Spinner must have a StringConverter to be able to render and parse the value.");
}
@Overridepublic String toString(T value){
return value == null ? "" : value.toString();
}
}
// ==================================================================================================================// CellFactory/**
* Default cell factory
*/classDefaultCellFactoryimplementsCallback, Node>
{
private Label label = null;
@Overridepublic Node call(ListSpinner spinner){
// get value
T lValue = spinner.getValue();
// label not yet createdif (this.label == null)
{
this.label = new Label();
}
this.label.setText( lValue == null ? "" : spinner.getPrefix() + getStringConverter().toString(lValue) + spinner.getPostfix() );
returnthis.label;
}
};
// ==================================================================================================================// EVENTS/** OnCycle: callback for when the list cycles to the other end in cyclic mode (for example to increase a year when a month ListSpinner skips from December to January) */public ObjectProperty> onCycleProperty() { return iOnCycleObjectProperty; }
finalprivate ObjectProperty> iOnCycleObjectProperty = new SimpleObjectProperty>(null);
// java bean APIpublic EventHandlergetOnCycle(){ return iOnCycleObjectProperty.getValue(); }
publicvoidsetOnCycle(EventHandler value){ iOnCycleObjectProperty.setValue(value); }
public ListSpinnerwithOnCycle(EventHandler value){ setOnCycle(value); returnthis; }
finalstaticpublic String ONCYCLE_PROPERTY_ID = "onCycle";
/**
* CycleEvent
*/staticpublicclassCycleEventextendsEvent{
/**
* The only valid EventType for the CycleEvent.
*/publicstaticfinal EventType CYCLE = new EventType(Event.ANY, "CYCLE");
/**
*
*/publicCycleEvent(){
super(CYCLE);
}
/**
*
* @param source
* @param target
*/publicCycleEvent(Object source, EventTarget target){
super(source, target, new EventType());
}
public Object getOldIdx(){ returnthis.oldIdx; }
private Object oldIdx;
public Object getNewIdx(){ returnthis.newIdx; }
private Object newIdx;
publicbooleancycledDown(){ return cycleDirection == CycleDirection.TOP_TO_BOTTOM; }
publicbooleancycledUp(){ return cycleDirection == CycleDirection.BOTTOM_TO_TOP; }
CycleDirection cycleDirection;
}
/**
* we're cycling, fire the event
*/publicvoidfireCycleEvent(CycleDirection cycleDirection){
EventHandler lCycleEventHandler = getOnCycle();
if (lCycleEventHandler != null)
{
CycleEvent lCycleEvent = new CycleEvent();
lCycleEvent.cycleDirection = cycleDirection;
lCycleEventHandler.handle(lCycleEvent);
}
}
staticpublicenum CycleDirection { TOP_TO_BOTTOM, BOTTOM_TO_TOP }
// ==================================================================================================================// BEHAVIOR/**
*
*/publicvoidfirst(){
// nothing to doif (getItems() == null || getItems().size() == 0) return;
// set the new index (this will update the value)
indexObjectProperty.setValue(0);
}
/**
*
*/publicvoiddecrement(){
// nothing to doif (getItems() == null || getItems().size() == 0) return;
// get the current indexint lOldIdx = this.indexObjectProperty.getValue();
// get the previous index (usually current - 1)int lIdx = lOldIdx - 1;
// if endif (lIdx < 0)
{
// if we're not cyclicif (isCyclic() != null && isCyclic().booleanValue() == false)
{
// do nothingreturn;
}
// cycle to the other end: get the last value
lIdx = getItems().size() - 1;
// notify listener that we've cycled
fireCycleEvent(CycleDirection.BOTTOM_TO_TOP);
}
// set the new index (this will update the value)
indexObjectProperty.setValue(lIdx);
}
/**
*
*/publicvoidincrement(){
// nothing to doif (getItems() == null || getItems().size() == 0) return;
// get the current indexint lOldIdx = this.indexObjectProperty.getValue();
// get the next index (usually current + 1)int lIdx = lOldIdx + 1;
// if null is return, there is no next index (usually current + 1)if (lIdx >= getItems().size())
{
// if we're not cyclicif (isCyclic() != null && isCyclic().booleanValue() == false)
{
// do nothingreturn;
}
// cycle to the other end: get the first value
lIdx = 0;
// notify listener that we've cycled
fireCycleEvent(CycleDirection.TOP_TO_BOTTOM);
}
// set the new index (this will update the value)
indexObjectProperty.setValue(lIdx);
}
/**
* Get the last index; if the data provide is endless, this method mail fail!
*/publicvoidlast(){
// nothing to doif (getItems() == null || getItems().size() == 0) return;
// set the new index (this will update the value)
indexObjectProperty.setValue(getItems().size() - 1);
}
/**
* Does a o1.equals(o2) but also checks if o1 or o2 are null.
* @param o1
* @param o2
* @return True if the two values are equal, false otherwise.
*/staticpublicbooleanequals(Object o1, Object o2){
if ( o1 == null && o2 == null ) returntrue;
if ( o1 != null && o2 == null ) returnfalse;
if ( o1 == null && o2 != null ) returnfalse;
// TODO: compare arrays if (o1.getClass().isArray() && o2.getClass().isArray()) return Arrays.equals( (Object[])o1, (Object[])o2 ); return o1.equals(o2);
}
}