/*
* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
* Modified by Altug Uzunali (altug.uzunali@gmail.com) at 08/02/2012
*
* Added two thumbs functionality
*
* Modified by Jose Pereda (pereda@eii.uva.es) at 14/09/2012
*
* Added a TickLabelFormatter and a highlighted track
*
*/
package weeklyschedulerfx.doubleSlider;
import static javafx.scene.input.KeyCode.DOWN;
import static javafx.scene.input.KeyCode.END;
import static javafx.scene.input.KeyCode.F4;
import static javafx.scene.input.KeyCode.HOME;
import static javafx.scene.input.KeyCode.KP_DOWN;
import static javafx.scene.input.KeyCode.KP_LEFT;
import static javafx.scene.input.KeyCode.KP_RIGHT;
import static javafx.scene.input.KeyCode.KP_UP;
import static javafx.scene.input.KeyCode.LEFT;
import static javafx.scene.input.KeyCode.RIGHT;
import static javafx.scene.input.KeyCode.TAB;
import static javafx.scene.input.KeyCode.UP;
import static javafx.scene.input.KeyEvent.KEY_RELEASED;
import java.util.ArrayList;
import java.util.List;
import javafx.event.EventType;
import javafx.geometry.Orientation;
import javafx.scene.control.Control;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import com.sun.javafx.Utils;
import com.sun.javafx.scene.control.behavior.BehaviorBase;
import com.sun.javafx.scene.control.behavior.KeyBinding;
import com.sun.javafx.scene.control.behavior.OrientedKeyBinding;
public class DoubleSliderBehavior extends BehaviorBase<DoubleSlider>{
public DoubleSliderBehavior(DoubleSlider doubleSlider) {
super(doubleSlider);
}
/**************************************************************************
* Setup KeyBindings *
* *
* We manually specify the focus traversal keys because DoubleSlider has *
* different usage for up/down arrow keys. *
*************************************************************************/
protected static final List<KeyBinding> DOUBLESLIDER_BINDINGS = new ArrayList<KeyBinding>();
static {
DOUBLESLIDER_BINDINGS.add(new KeyBinding(TAB, "TraverseNext"));
DOUBLESLIDER_BINDINGS.add(new KeyBinding(TAB, "TraversePrevious").shift());
// TODO XXX DEBUGGING ONLY
DOUBLESLIDER_BINDINGS.add(new KeyBinding(F4, "TraverseDebug").alt().ctrl().shift());
DOUBLESLIDER_BINDINGS.add(new DoubleSliderKeyBinding(LEFT, "DecrementValue"));
DOUBLESLIDER_BINDINGS.add(new DoubleSliderKeyBinding(KP_LEFT, "DecrementValue"));
DOUBLESLIDER_BINDINGS.add(new DoubleSliderKeyBinding(UP, "IncrementValue").vertical());
DOUBLESLIDER_BINDINGS.add(new DoubleSliderKeyBinding(KP_UP, "IncrementValue").vertical());
DOUBLESLIDER_BINDINGS.add(new DoubleSliderKeyBinding(RIGHT, "IncrementValue"));
DOUBLESLIDER_BINDINGS.add(new DoubleSliderKeyBinding(KP_RIGHT, "IncrementValue"));
DOUBLESLIDER_BINDINGS.add(new DoubleSliderKeyBinding(DOWN, "DecrementValue").vertical());
DOUBLESLIDER_BINDINGS.add(new DoubleSliderKeyBinding(KP_DOWN, "DecrementValue").vertical());
DOUBLESLIDER_BINDINGS.add(new DoubleSliderKeyBinding(LEFT, "TraverseLeft").vertical());
DOUBLESLIDER_BINDINGS.add(new DoubleSliderKeyBinding(KP_LEFT, "TraverseLeft").vertical());
DOUBLESLIDER_BINDINGS.add(new DoubleSliderKeyBinding(UP, "TraverseUp"));
DOUBLESLIDER_BINDINGS.add(new DoubleSliderKeyBinding(KP_UP, "TraverseUp"));
DOUBLESLIDER_BINDINGS.add(new DoubleSliderKeyBinding(RIGHT, "TraverseRight").vertical());
DOUBLESLIDER_BINDINGS.add(new DoubleSliderKeyBinding(KP_RIGHT, "TraverseRight").vertical());
DOUBLESLIDER_BINDINGS.add(new DoubleSliderKeyBinding(DOWN, "TraverseDown"));
DOUBLESLIDER_BINDINGS.add(new DoubleSliderKeyBinding(KP_DOWN, "TraverseDown"));
DOUBLESLIDER_BINDINGS.add(new KeyBinding(HOME, KEY_RELEASED, "Home"));
DOUBLESLIDER_BINDINGS.add(new KeyBinding(END, KEY_RELEASED, "End"));
}
@Override
protected void callAction(String name){
if ("Home".equals(name)) home();
else if ("End".equals(name)) end();
else if ("IncrementValue1".equals(name)) incrementValue1();
else if ("DecrementValue1".equals(name)) decrementValue1();
else if ("IncrementValue2".equals(name)) incrementValue2();
else if ("DecrementValue2".equals(name)) decrementValue2();
else super.callAction(name);
}
@Override protected List<KeyBinding> createKeyBindings() {
return DOUBLESLIDER_BINDINGS;
}
/**
* Invoked by the Slider {@link Skin} implementation whenever a mouse press
* occurs on the "track" of the slider. This will cause the thumb to be
* moved by some amount.
*
* @param position The mouse position on track with 0.0 being beginning of
* track and 1.0 being the end
*/
public void trackPress(MouseEvent e, double position) {
// determine the percentage of the way between min and max
// represented by this mouse event
final DoubleSlider doubleSlider = getControl();
// If not already focused, request focus
if (!doubleSlider.isFocused()) doubleSlider.requestFocus();
if (doubleSlider.getOrientation().equals(Orientation.HORIZONTAL)) {
doubleSlider.adjustValue(position * (doubleSlider.getMax() - doubleSlider.getMin()) + doubleSlider.getMin());
} else {
doubleSlider.adjustValue((1-position) * (doubleSlider.getMax() - doubleSlider.getMin()) + doubleSlider.getMin());
}
}
/**
*/
public void trackRelease(MouseEvent e, double position) {
}
/**
* @param position The mouse position on track with 0.0 being beginning of
* track and 1.0 being the end
*/
public void thumbPressed(MouseEvent e, double position) {
// If not already focused, request focus
final DoubleSlider doubleSlider = getControl();
if (!doubleSlider.isFocused()) doubleSlider.requestFocus();
doubleSlider.setValueChanging(true);
}
/**
* @param position The mouse position on track with 0.0 being beginning of
* track and 1.0 being the end
*/
public void thumbDragged(DoubleSliderSkin.ThumbNumber thumbNumber, MouseEvent e, double position) {
final DoubleSlider doubleSlider = getControl();
switch(thumbNumber){
case Thumb1:
// doubleSlider.setValue1(Utils.clamp(doubleSlider.getMin(), (position * (doubleSlider.getMax() - doubleSlider.getMin())) + doubleSlider.getMin(), doubleSlider.getMax()));
doubleSlider.setValue1(Utils.clamp(doubleSlider.getMin(), (position * (doubleSlider.getMax() - doubleSlider.getMin())) + doubleSlider.getMin(), doubleSlider.getValue2()));
break;
case Thumb2:
doubleSlider.setValue2(Utils.clamp(doubleSlider.getValue1(), (position * (doubleSlider.getMax() - doubleSlider.getMin())) + doubleSlider.getMin(), doubleSlider.getMax()));
}
}
/**
* When thumb is released valueChanging should be set to false.
*/
public void thumbReleased(DoubleSliderSkin.ThumbNumber thumbNumber, MouseEvent e) {
final DoubleSlider doubleSlider = getControl();
doubleSlider.setValueChanging(false);
// RT-15207 When snapToTicks is true, slider value calculated in drag
// is then snapped to the nearest tick on mouse release.
// TODO: reconsider after tick mark implementation
if (doubleSlider.isSnapToTicks()) {
switch(thumbNumber){
case Thumb1: doubleSlider.setValue1(snapValueToTicks(doubleSlider.getValue1()));
break;
case Thumb2: doubleSlider.setValue2(snapValueToTicks(doubleSlider.getValue2()));
break;
}
}
}
private double snapValueToTicks(double val) {
final DoubleSlider doubleSlider = getControl();
double v = val;
double tickSpacing = 0;
// compute the nearest tick to this value
if (doubleSlider.getMinorTickCount() != 0) {
tickSpacing = doubleSlider.getMajorTickUnit() / (Math.max(doubleSlider.getMinorTickCount(),0)+1);
} else {
tickSpacing = doubleSlider.getMajorTickUnit();
}
int prevTick = (int)((v - doubleSlider.getMin())/ tickSpacing);
double prevTickValue = (prevTick) * tickSpacing + doubleSlider.getMin();
double nextTickValue = (prevTick + 1) * tickSpacing + doubleSlider.getMin();
v = Utils.nearest(prevTickValue, v, nextTickValue);
return Utils.clamp(doubleSlider.getMin(), v, doubleSlider.getMax());
}
private void decrementValue2() {
// TODO Auto-generated method stub
}
private void incrementValue2() {
// TODO Auto-generated method stub
}
private void decrementValue1() {
// TODO Auto-generated method stub
}
private void incrementValue1() {
// TODO Auto-generated method stub
}
private void end() {
final DoubleSlider doubleSlider = getControl();
doubleSlider.adjustValue(doubleSlider.getMax());
}
private void home() {
final DoubleSlider doubleSlider = getControl();
doubleSlider.adjustValue(doubleSlider.getMin());
}
// Used only if snapToTicks is true.
double computeIncrement() {
final DoubleSlider doubleSlider = getControl();
double tickSpacing = 0;
if (doubleSlider.getMinorTickCount() != 0) {
tickSpacing = doubleSlider.getMajorTickUnit() / (Math.max(doubleSlider.getMinorTickCount(),0)+1);
} else {
tickSpacing = doubleSlider.getMajorTickUnit();
}
if (doubleSlider.getBlockIncrement() > 0 && doubleSlider.getBlockIncrement() < tickSpacing) {
return tickSpacing;
}
return doubleSlider.getBlockIncrement();
}
public static class DoubleSliderKeyBinding extends OrientedKeyBinding {
public DoubleSliderKeyBinding(KeyCode code, String action) {
super(code, action);
}
public DoubleSliderKeyBinding(KeyCode code, EventType<KeyEvent> type, String action) {
super(code, type, action);
}
public @Override boolean getVertical(Control control) {
return ((DoubleSlider)control).getOrientation() == Orientation.VERTICAL;
}
}
}