com.lawal.client.ui.VRangeSliderBar.java Source code

Java tutorial

Introduction

Here is the source code for com.lawal.client.ui.VRangeSliderBar.java

Source

/*
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.lawal.client.ui;

import java.util.ArrayList;
import java.util.List;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.FocusPanel;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.ContainerResizedListener;
import com.vaadin.terminal.gwt.client.Paintable;
import com.vaadin.terminal.gwt.client.UIDL;
import com.vaadin.terminal.gwt.client.VConsole;
import com.vaadin.terminal.gwt.client.ui.Field;

public class VRangeSliderBar extends FocusPanel implements Paintable, Field, ContainerResizedListener {

    private static final String SLIDER_BAR_LINE = "gwt-VRangeSliderBar-line";
    private static final String KNOB_MAX_CLASSNAME = "gwt-VRangeSliderBar-knobmax";
    private static final String KNOB_MIN_CLASSNAME = "gwt-VRangeSliderBar-knobmin";

    private static final String CLASSNAME = "gwt-VRangeSliderBar-shell";
    /**
     * The timer used to continue to shift the knob if the user holds down a
     * key.
     */
    private KeyTimer keyTimer = new KeyTimer();
    /**
     * The elements used to display labels above the ticks.
     */
    private List<Element> labelElements = new ArrayList<Element>();
    /**
     * The line that the knob moves over.
     */
    private Element lineElement;
    /**
     * The offset between the edge of the shell and the line.
     */
    private int lineLeftOffset = 0;
    /**
     * The maximum slider value.
     */
    private double rangeMax;
    /**
     * The minimum slider value.
     */
    private double rangeMin;
    /**
     * The number of labels to show.
     */
    private int numLabels = 0;
    /**
     * The number of tick marks to show.
     */
    private int numTicks = 0;
    /**
     * A bit indicating whether or not we are currently sliding the slider bar
     * due to keyboard events.
     */
    private boolean slidingKeyboard = false;
    /**
     * A bit indicating whether or not we are currently sliding the slider bar
     * due to mouse events.
     */
    private boolean slidingMouse = false;
    /**
     * A bit indicating whether or not the slider is enabled
     */
    private boolean enabled = true;
    /**
     * The size of the increments between knob positions.
     */
    private double stepSize;
    /**
     * The elements used to display tick marks, which are the vertical lines
     * along the slider bar.
     */
    private List<Element> tickElements = new ArrayList<Element>();
    private LabelFormatter labelFormatter;
    private Element knobElemMin;
    private Element knobElemMax;

    private ApplicationConnection client;
    private String id;
    private boolean immediate;
    private boolean readonly;
    private Element progressElementMin;
    private Element progressElementMax;

    private boolean superImmediate = false;
    private double minCurrValue;
    private double maxCurrValue;
    private Element _target;
    private String appendString = "";

    public VRangeSliderBar() {
        super();
        this.rangeMin = 0;
        this.rangeMax = 100;
        this.setMinCurrValue(0);
        this.maxCurrValue = 100;
        this.stepSize = 10;
        setLabelFormatter(LabelFormatter.getDefault());
        // Create the outer shell
        DOM.setStyleAttribute(getElement(), "position", "relative");
        setStyleName(CLASSNAME);
        // Create the line
        lineElement = DOM.createDiv();
        DOM.appendChild(getElement(), lineElement);
        DOM.setStyleAttribute(lineElement, "position", "absolute");
        DOM.setElementProperty(lineElement, "className", SLIDER_BAR_LINE);
        // Create the knob

        // Element knobElemMin = knobImage.getElement();
        knobElemMin = DOM.createDiv();
        DOM.appendChild(getElement(), knobElemMin);
        DOM.setStyleAttribute(knobElemMin, "position", "absolute");
        DOM.setElementProperty(knobElemMin, "className", KNOB_MIN_CLASSNAME);

        knobElemMax = DOM.createDiv();
        DOM.appendChild(getElement(), knobElemMax);
        DOM.setStyleAttribute(knobElemMax, "position", "absolute");
        DOM.setElementProperty(knobElemMax, "className", KNOB_MAX_CLASSNAME);

        progressElementMin = DOM.createDiv();
        DOM.appendChild(getElement(), progressElementMin);
        DOM.setStyleAttribute(progressElementMin, "position", "absolute");
        DOM.setElementProperty(progressElementMin, "className", "gwt-VRangeSliderBar-progress");

        progressElementMax = DOM.createDiv();
        DOM.appendChild(getElement(), progressElementMax);
        DOM.setStyleAttribute(progressElementMax, "position", "absolute");
        DOM.setElementProperty(progressElementMax, "className", "gwt-VRangeSliderBar-progress");

        sinkEvents(
                Event.MOUSEEVENTS | Event.ONMOUSEWHEEL | Event.KEYEVENTS | Event.FOCUSEVENTS | Event.TOUCHEVENTS);
    }

    /**
     * Return the total range between the minimum and maximum values.
     * 
     * @return the total range
     */
    public double getTotalRange() {
        if (rangeMin > rangeMax) {
            return 0;
        }
        return rangeMax - rangeMin;
    }

    /**
     * Listen for events that will move the knob.
     * 
     * @param event
     *            the event that occurred
     */
    @Override
    public void onBrowserEvent(Event event) {
        super.onBrowserEvent(event);
        // VConsole.log("browser event" + DOM.eventGetType(event));
        if (!enabled || readonly) {
            return;
        }

        switch (DOM.eventGetType(event)) {
        // Unhighlight and cancel keyboard events
        case Event.ONBLUR:
            keyTimer.cancel();
            if (slidingMouse) {
                DOM.releaseCapture(getElement());
                slidingMouse = false;
                slideKnob(event);
                stopSliding(true, true);
            } else if (slidingKeyboard) {
                slidingKeyboard = false;
                stopSliding(true, true);
            }
            unhighlight();
            break;
        // Highlight on focus
        case Event.ONFOCUS:
            highlight();
            break;
        // Mousewheel events
        case Event.ONMOUSEWHEEL:
            int velocityY = DOM.eventGetMouseWheelVelocityY(event);
            DOM.eventPreventDefault(event);
            if (velocityY < 0) {
                //             shiftRight(1,true);
            } else {
                // shiftLeft(1,true);
            }
            break;
        // Shift left or right on key press
        case Event.ONKEYDOWN:
            processKeyDown(event);
            break;
        // Stop shifting on key up
        case Event.ONKEYUP:
            keyTimer.cancel();
            if (slidingKeyboard) {
                slidingKeyboard = false;
                stopSliding(true, true);
            }
            break;
        // Mouse Events
        case Event.ONMOUSEDOWN:
        case Event.ONTOUCHSTART:
            setFocus(true);
            slidingMouse = true;
            DOM.setCapture(getElement());
            startSliding(true, true);
            DOM.eventPreventDefault(event);
            slideKnob(event);
            break;
        case Event.ONMOUSEUP:
            // case Event.ONTOUCHEND:
            if (slidingMouse) {
                DOM.releaseCapture(getElement());
                slidingMouse = false;
                slideKnob(event);
                stopSliding(true, true);
            }
            break;
        case Event.ONMOUSEMOVE:
        case Event.ONTOUCHMOVE:
            if (slidingMouse) {
                slideKnob(event);
            }
            break;
        }
        VConsole.log("sliding mouse " + slidingMouse + " slidKey" + slidingKeyboard + " step SZ" + stepSize);
    }

    private void processKeyDown(Event event) {
        if (slidingKeyboard) {
            return;
        }
        int multiplier = 1;
        if (DOM.eventGetCtrlKey(event)) {
            multiplier = (int) (getTotalRange() / stepSize / 10);
        }
        switch (DOM.eventGetKeyCode(event)) {
        case KeyCodes.KEY_HOME:
            DOM.eventPreventDefault(event);
            setCurrentValue(rangeMin, rangeMin, true);
            break;
        case KeyCodes.KEY_END:
            DOM.eventPreventDefault(event);
            setCurrentValue(rangeMax, rangeMax, true);
            break;
        case KeyCodes.KEY_LEFT:
            DOM.eventPreventDefault(event);
            slidingKeyboard = true;
            startSliding(false, true);
            // shiftLeft(multiplier,superImmediate);
            keyTimer.schedule(400, false, multiplier);
            break;
        case KeyCodes.KEY_RIGHT:
            DOM.eventPreventDefault(event);
            slidingKeyboard = true;
            startSliding(false, true);
            // shiftRight(multiplier,superImmediate);
            keyTimer.schedule(400, true, multiplier);
            break;
        case 32:
            DOM.eventPreventDefault(event);
            double half = rangeMin + getTotalRange() / 2;
            setCurrentValue(half, half, true);
            break;
        }
    }

    /**
     * This method is called when the dimensions of the parent element change.
     * Subclasses should override this method as needed.
     * 
     * @param width
     *            the new client width of the element
     * @param height
     *            the new client height of the element
     */
    public void onResize(int width, int height) {
        // Center the line in the shell
        int lineWidth = DOM.getElementPropertyInt(lineElement, "offsetWidth");
        lineLeftOffset = (width / 2) - (lineWidth / 2);
        DOM.setStyleAttribute(lineElement, "left", lineLeftOffset + "px");
        // Draw the other components
        drawLabels();
        drawTicks();
        drawKnobAndProgress();
    }

    /**
     * Redraw the progress bar when something changes the layout.
     */
    public void redraw() {
        if (isAttached()) {
            int width = DOM.getElementPropertyInt(getElement(), "clientWidth");
            int height = DOM.getElementPropertyInt(getElement(), "clientHeight");
            onResize(width, height);
        }
    }

    // /**
    // * Set the current value and fire the onValueChange event.
    // *
    // * @param curValue
    // * the current value
    // */
    // public void setCurrentValue(double curValue) {
    // setCurrentValue(curValue, true);
    // }

    /**
     * Set the current value and optionally fire the onValueChange event.
     * 
     * @param curValue
     *            the current value
     * @param fireEvent
     *            fire the onValue change event if true
     */
    public void setCurrentValue(double minCurVal, double maxCurVal, boolean fireEvent) {
        this.setMinCurrValue(confineValue(minCurVal));
        this.maxCurrValue = confineValue(maxCurVal);
        // Redraw the knob and progress bar
        drawKnobAndProgress();
        if (fireEvent) {
            updateValueToServer();
        }
        VConsole.log(" setCurrentValue" + minCurVal + " " + maxCurVal + "" + superImmediate);
    }

    private double confineValue(double value) {
        // Confine the value to the range
        double val = Math.max(rangeMin, Math.min(rangeMax, value));
        double remainder = (val - rangeMin) % stepSize;
        val -= remainder;
        // Go to next step if more than halfway there
        if ((remainder > (stepSize / 2)) && ((val + stepSize) <= rangeMax)) {
            val += stepSize;
        }
        return val;
    }

    private void updateValueToServer() {
        client.updateVariable(id, "knobmin", getMinCurrValue(), false);
        client.updateVariable(id, "knobmax", getMaxCurrValue(), immediate);

    }

    /**
     * Sets whether this widget is enabled.
     * 
     * @param enabled
     *            true to enable the widget, false to disable it
     */
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
        if (enabled) {
            // images.slider().applyTo(knobImage);
            DOM.setElementProperty(lineElement, "className", SLIDER_BAR_LINE);
        } else {
            // images.sliderDisabled().applyTo(knobImage);
            DOM.setElementProperty(lineElement, "className",
                    "gwt-VRangeSliderBar-line gwt-VRangeSliderBar-line-disabled");
        }
        redraw();
    }

    /**
     * Set the label formatter.
     * 
     * @param labelFormatter
     *            the label formatter
     */
    public void setLabelFormatter(LabelFormatter labelFormatter) {
        this.labelFormatter = labelFormatter;
    }

    /**
     * Set the max value.
     * 
     * @param rangeMax
     *            the current value
     */
    public void setMaxValue(double maxValue) {
        this.rangeMax = maxValue;
        drawLabels();
        resetCurrentValue();
    }

    /**
     * Set the minimum value.
     * 
     * @param rangeMin
     *            the current value
     */
    public void setMinValue(double minValue) {
        this.rangeMin = minValue;
        drawLabels();
        resetCurrentValue();
    }

    /**
     * Set the number of labels to show on the line. Labels indicate the value
     * of the slider at that point. Use this method to enable labels. If you set
     * the number of labels equal to the total range divided by the step size,
     * you will get a properly aligned "jumping" effect where the knob jumps
     * between labels. Note that the number of labels displayed will be one more
     * than the number you specify, so specify 1 labels to show labels on either
     * end of the line. In other words, numLabels is really the number of slots
     * between the labels. setNumLabels(0) will disable labels.
     * 
     * @param numLabels
     *            the number of labels to show
     */
    public void setNumLabels(int numLabels) {
        this.numLabels = numLabels;
        drawLabels();
    }

    /**
     * Set the number of ticks to show on the line. A tick is a vertical line
     * that represents a division of the overall line. Use this method to enable
     * ticks. If you set the number of ticks equal to the total range divided by
     * the step size, you will get a properly aligned "jumping" effect where the
     * knob jumps between ticks. Note that the number of ticks displayed will be
     * one more than the number you specify, so specify 1 tick to show ticks on
     * either end of the line. In other words, numTicks is really the number of
     * slots between the ticks. setNumTicks(0) will disable ticks.
     * 
     * @param numTicks
     *            the number of ticks to show
     */
    public void setNumTicks(int numTicks) {
        this.numTicks = numTicks;
        drawTicks();
    }

    /**
     * Set the step size.
     * 
     * @param stepSize
     *            the current value
     */
    public void setStepSize(double stepSize) {
        if (Double.compare(0.0, stepSize) >= 0) {
            this.stepSize = 0.5;
        } else {
            this.stepSize = stepSize;
        }
        resetCurrentValue();
    }

    /**
     * Shift to the left (smaller value).
     * 
     * @param numSteps
     *            the number of steps to shift
     */
    // public void shiftLeft(int numSteps, boolean updateToserver) {
    // setCurrentValue(getCurrentValue() - numSteps * stepSize, updateToserver);
    // }
    //
    // /**
    // * Shift to the right (greater value).
    // *
    // * @param numSteps
    // * the number of steps to shift
    // */
    // public void shiftRight(int numSteps, boolean updateToserver) {
    // setCurrentValue(getCurrentValue() + numSteps * stepSize, updateToserver);
    // }

    /**
     * Format the label to display above the ticks Override this method in a
     * subclass to customize the format. By default, this method returns the
     * integer portion of the value.
     * 
     * @param value
     *            the value at the label
     * @return the text to put in the label
     */
    protected String formatLabel(double value) {
        if (labelFormatter != null) {
            return labelFormatter.formatLabel(this, value);
        }
        return (int) (10 * value) / 10.0 + "";
    }

    /**
     * Get the percentage of the knob's position relative to the size of the
     * line. The return value will be between 0.0 and 1.0.
     * 
     * @return the current percent complete
     */
    protected double getKnobPercent(double val) {
        // If we have no range
        if (rangeMax <= rangeMin) {
            return 0;
        }
        // Calculate the relative progress
        double percent = (val - rangeMin) / (rangeMax - rangeMin);
        return Math.max(0.0, Math.min(1.0, percent));
    }

    /**
     * This method is called immediately after a widget becomes attached to the
     * browser's document.
     */
    @Override
    protected void onLoad() {
        // Reset the position attribute of the parent element
        DOM.setStyleAttribute(getElement(), "position", "relative");
        // ResizableWidgetCollection.get().add(this);
        redraw();
    }

    /**
     * Draw the knob where it is supposed to be relative to the line.
     */
    private void drawKnobAndProgress() {
        // Abort if not attached
        if (!isAttached()) {
            return;
        }
        // // Move the knob to the correct position
        int lineWidth = DOM.getElementPropertyInt(lineElement, "offsetWidth");

        int knobWidth = DOM.getElementPropertyInt(knobElemMin, "offsetWidth");

        // draw min knob
        int minKnobLeft = (int) (lineLeftOffset + (getKnobPercent(getMinCurrValue()) * lineWidth)
                - (knobWidth / 2));
        minKnobLeft = Math.min(minKnobLeft, lineLeftOffset + lineWidth - (knobWidth / 2) - 1);
        DOM.setStyleAttribute(knobElemMin, "left", minKnobLeft + "px");

        knobWidth = DOM.getElementPropertyInt(knobElemMax, "offsetWidth");
        int maxknobLeft = (int) (lineLeftOffset + (getKnobPercent(maxCurrValue) * lineWidth) - (knobWidth / 2));
        maxknobLeft = Math.min(maxknobLeft, lineLeftOffset + lineWidth - (knobWidth / 2) - 1);
        DOM.setStyleAttribute(knobElemMax, "left", maxknobLeft + "px");

        //
        DOM.setStyleAttribute(progressElementMin, "left", lineLeftOffset + "px");
        DOM.setStyleAttribute(progressElementMin, "width", 95 * getKnobPercent(getMinCurrValue()) + "%");

        DOM.setStyleAttribute(progressElementMax, "right", lineLeftOffset + "px");
        double rightwidth = 95 * (rangeMax - getMaxCurrValue()) / (rangeMax - rangeMin);
        DOM.setStyleAttribute(progressElementMax, "width", rightwidth + "%");

    }

    /**
     * Draw the labels along the line.
     */
    private void drawLabels() {
        // Abort if not attached
        if (!isAttached()) {
            return;
        }
        // Draw the labels
        int lineWidth = DOM.getElementPropertyInt(lineElement, "offsetWidth");
        if (numLabels > 0) {
            // Create the labels or make them visible
            for (int i = 0; i <= numLabels; i++) {
                Element label = null;
                if (i < labelElements.size()) {
                    label = labelElements.get(i);
                } else { // Create the new label
                    label = DOM.createDiv();
                    DOM.setStyleAttribute(label, "position", "absolute");
                    DOM.setStyleAttribute(label, "display", "none");
                    if (enabled) {
                        DOM.setElementProperty(label, "className", "gwt-VRangeSliderBar-label");
                    } else {
                        DOM.setElementProperty(label, "className", "gwt-VRangeSliderBar-label-disabled");
                    }
                    DOM.appendChild(getElement(), label);
                    labelElements.add(label);
                }
                // Set the label text
                double value = rangeMin + (getTotalRange() * i / numLabels);
                DOM.setStyleAttribute(label, "visibility", "hidden");
                DOM.setStyleAttribute(label, "display", "");
                DOM.setElementProperty(label, "innerHTML", formatLabel(value));
                // Move to the left so the label width is not clipped by the
                // shell
                DOM.setStyleAttribute(label, "left", "0px");
                // Position the label and make it visible
                int labelWidth = DOM.getElementPropertyInt(label, "offsetWidth");
                int labelLeftOffset = lineLeftOffset + (lineWidth * i / numLabels) - (labelWidth / 2);
                labelLeftOffset = Math.min(labelLeftOffset, lineLeftOffset + lineWidth - labelWidth);
                labelLeftOffset = Math.max(labelLeftOffset, lineLeftOffset);
                DOM.setStyleAttribute(label, "left", labelLeftOffset + "px");
                DOM.setStyleAttribute(label, "visibility", "visible");
            }
            // Hide unused labels
            for (int i = (numLabels + 1); i < labelElements.size(); i++) {
                DOM.setStyleAttribute(labelElements.get(i), "display", "none");
            }
        } else { // Hide all labels
            for (Element elem : labelElements) {
                DOM.setStyleAttribute(elem, "display", "none");
            }
        }
    }

    /**
     * Draw the tick along the line.
     */
    private void drawTicks() {
        // Abort if not attached
        if (!isAttached()) {
            return;
        }
        // Draw the ticks
        int lineWidth = DOM.getElementPropertyInt(lineElement, "offsetWidth");
        if (numTicks > 0) {
            // Create the ticks or make them visible
            for (int i = 0; i <= numTicks; i++) {
                Element tick = null;
                if (i < tickElements.size()) {
                    tick = tickElements.get(i);
                } else {
                    // Create the new tick
                    tick = DOM.createDiv();
                    DOM.setStyleAttribute(tick, "position", "absolute");
                    DOM.setStyleAttribute(tick, "display", "none");
                    DOM.appendChild(getElement(), tick);
                    tickElements.add(tick);
                }
                if (enabled) {
                    DOM.setElementProperty(tick, "className", "gwt-VRangeSliderBar-tick");
                } else {
                    DOM.setElementProperty(tick, "className",
                            "gwt-VRangeSliderBar-tick gwt-VRangeSliderBar-tick-disabled");
                }
                // Position the tick and make it visible
                DOM.setStyleAttribute(tick, "visibility", "hidden");
                DOM.setStyleAttribute(tick, "display", "");
                int tickWidth = DOM.getElementPropertyInt(tick, "offsetWidth");
                int tickLeftOffset = lineLeftOffset + (lineWidth * i / numTicks) - (tickWidth / 2);
                tickLeftOffset = Math.min(tickLeftOffset, lineLeftOffset + lineWidth - tickWidth);
                DOM.setStyleAttribute(tick, "left", tickLeftOffset + "px");
                DOM.setStyleAttribute(tick, "visibility", "visible");
            }
            // Hide unused ticks
            for (int i = (numTicks + 1); i < tickElements.size(); i++) {
                DOM.setStyleAttribute(tickElements.get(i), "display", "none");
            }
        } else { // Hide all ticks
            for (Element elem : tickElements) {
                DOM.setStyleAttribute(elem, "display", "none");
            }
        }
    }

    /**
     * Highlight this widget.
     */
    private void highlight() {
        String styleName = getStylePrimaryName();
        DOM.setElementProperty(getElement(), "className", styleName + " " + styleName + "-focused");
    }

    /**
     * Reset the progress to constrain the progress to the current range and
     * redraw the knob as needed.
     */
    private void resetCurrentValue() {
        setCurrentValue(getMinCurrValue(), getMaxCurrValue(), true);
    }

    /**
     * Slide the knob to a new location.
     * 
     * @param event
     *            the mouse event
     */
    private void slideKnob(Event event) {

        int pointClicked = DOM.eventGetClientX(event);

        int minLeft = DOM.getAbsoluteLeft(knobElemMin);
        int maxLeft = DOM.getAbsoluteLeft(knobElemMax);

        VConsole.log("VRangeSliderBar.onBrowserEvent()" + " " + minLeft + " " + maxLeft);

        if (pointClicked <= minLeft) {
            _target = knobElemMin;
        }
        //
        else if (pointClicked >= maxLeft) {
            _target = knobElemMax;
        } else {

            int inbtw = minLeft + ((maxLeft - minLeft) / 2);
            if (pointClicked <= inbtw) {
                _target = knobElemMin;
            } else {

                _target = knobElemMax;
            }

        }

        VConsole.log("target to move is  " + _target.getClassName());
        if (pointClicked > 0) {
            int lineWidth = DOM.getElementPropertyInt(lineElement, "offsetWidth");
            int lineLeft = DOM.getAbsoluteLeft(lineElement);
            double percent = (double) (DOM.eventGetClientX(event) - lineLeft) / lineWidth * 1.0;

            if (_target == knobElemMin) {
                setCurrentValue(getTotalRange() * percent + rangeMin, this.maxCurrValue, superImmediate);
            }
            // max target
            else if (_target == knobElemMax) {
                setCurrentValue(this.minCurrValue, getTotalRange() * percent + rangeMin, superImmediate);

            }
        }

        if (minCurrValue > maxCurrValue) {
            VConsole.log(new Throwable("min cant be greater than max pos min=" + minLeft + " max is " + maxLeft));

        }

    }

    /**
     * Start sliding the knob.
     * 
     * @param highlight
     *            true to change the style
     * @param fireEvent
     *            true to fire the event
     */
    private void startSliding(boolean highlight, boolean fireEvent) {
        if (highlight) {

            if (_target == knobElemMin) {
                DOM.setElementProperty(knobElemMin, "className", KNOB_MIN_CLASSNAME);

            } else if (_target == knobElemMax) {
                DOM.setElementProperty(knobElemMax, "className", KNOB_MAX_CLASSNAME);

            }
            DOM.setElementProperty(lineElement, "className",
                    "gwt-VRangeSliderBar-line gwt-VRangeSliderBar-line-sliding");
            // DOM.setElementProperty(knobElemMin, "className",
            // "gwt-VRangeSliderBar-knob gwt-VRangeSliderBar-knob-sliding");

        }
    }

    /**
     * Stop sliding the knob.
     * 
     * @param unhighlight
     *            true to change the style
     * @param fireEvent
     *            true to fire the event
     */
    private void stopSliding(boolean unhighlight, boolean fireEvent) {
        if (unhighlight) {

            if (_target == knobElemMin) {
                DOM.setElementProperty(knobElemMin, "className", KNOB_MIN_CLASSNAME + "-inactive");

            } else if (_target == knobElemMax) {
                DOM.setElementProperty(knobElemMax, "className", KNOB_MAX_CLASSNAME + "-inactive");

            }

            DOM.setElementProperty(lineElement, "className", SLIDER_BAR_LINE);

        }

        if (fireEvent) {
            updateValueToServer();
        }

    }

    /**
     * Unhighlight this widget.
     */
    private void unhighlight() {
        DOM.setElementProperty(getElement(), "className", getStylePrimaryName());
    }

    @Override
    public void updateFromUIDL(UIDL uidl, ApplicationConnection connClient) {
        this.client = connClient;
        id = uidl.getId();
        // Ensure correct implementation
        if (connClient.updateComponent(this, uidl, true)) {
            return;
        }
        superImmediate = uidl.getBooleanAttribute("superimmediate");
        immediate = uidl.getBooleanAttribute("immediate");
        readonly = uidl.getBooleanAttribute("readonly");
        boolean disables = uidl.getBooleanAttribute("disabled");
        double min = uidl.getDoubleAttribute("rangeMin");
        double max = uidl.getDoubleAttribute("rangeMax");
        int ticks = uidl.getIntAttribute("numticks");
        int numLabel = uidl.getIntAttribute("numlabels");
        double resolution = uidl.getDoubleAttribute("stepsize");
        this.stepSize = (resolution);
        this.rangeMin = (min);
        this.rangeMax = (max);

        this.minCurrValue = uidl.getDoubleAttribute("knobmin");
        this.maxCurrValue = uidl.getDoubleAttribute("knobmax");

        appendString = uidl.getStringAttribute("append");

        setEnabled(!disables);
        setNumLabels(numLabel);
        setNumTicks(ticks);

        setCurrentValue(minCurrValue, maxCurrValue, false);

        //      VConsole.log("update from uidl range " + rangeMin + "    min =" + rangeMax);
        //      VConsole.log("update from uidl currt " + minCurrValue + " max " + maxCurrValue);

    }

    @Override
    public void iLayout() {
        // Update handle position
        setCurrentValue(getMinCurrValue(), getMaxCurrValue(), false);
    }

    // GETTERS
    /**
     * Return the current value.
     * 
     * @return the current value
     */
    // public double getCurrentValue() {
    // return curValue;
    // }

    /**
     * Return the label formatter.
     * 
     * @return the label formatter
     */
    public LabelFormatter getLabelFormatter() {
        return labelFormatter;
    }

    /**
     * Return the max value.
     * 
     * @return the max value
     */
    public double getMaxValue() {
        return rangeMax;
    }

    /**
     * Return the minimum value.
     * 
     * @return the minimum value
     */
    public double getMinValue() {
        return rangeMin;
    }

    /**
     * Return the number of labels.
     * 
     * @return the number of labels
     */
    public int getNumLabels() {
        return numLabels;
    }

    /**
     * Return the number of ticks.
     * 
     * @return the number of ticks
     */
    public int getNumTicks() {
        return numTicks;
    }

    /**
     * Return the step size.
     * 
     * @return the step size
     */
    public double getStepSize() {
        return stepSize;
    }

    /**
     * @return Gets whether this widget is enabled
     */
    public boolean isEnabled() {
        return enabled;
    }

    public void setMinCurrValue(double minCurrValue) {
        this.minCurrValue = minCurrValue;
    }

    public double getMinCurrValue() {
        return minCurrValue;
    }

    public void setMaxCurrValue(double maxCurrValue) {
        this.maxCurrValue = maxCurrValue;
    }

    public double getMaxCurrValue() {
        return maxCurrValue;
    }

    public String getAppendString() {
        return appendString;
    }

    public void setAppendString(String appendString) {
        this.appendString = appendString;
    }

    /**
     * The timer used to continue to shift the knob as the user holds down one
     * of the left/right arrow keys. Only IE auto-repeats, so we just keep
     * catching the events.
     */
    private class KeyTimer extends Timer {
        /**
         * A bit indicating that this is the run.
         */
        private boolean firstRun = true;
        /**
         * The delay between shifts, which shortens as the user holds down the
         * button.
         */
        private int repeatDelay = 30;
        /**
         * A bit indicating whether we are shifting to a higher or lower value.
         */
        private boolean shiftRight = false;
        /**
         * The number of steps to shift with each press.
         */
        private int multiplier = 1;

        /**
         * This method will be called when a timer fires. Override it to
         * implement the timer's logic.
         */
        @Override
        public void run() {
            // Highlight the knob on _min run
            if (firstRun) {
                firstRun = false;
                startSliding(true, false);
            }
            // Slide the slider bar
            if (shiftRight) {
                // setCurrentValue(curValue + multiplier * stepSize,
                // superImmediate);
            } else {
                // setCurrentValue(curValue - multiplier *
                // stepSize,superImmediate);
            }
            // Repeat this timer until cancelled by keyup event
            schedule(repeatDelay);
        }

        /**
         * Schedules a timer to elapse in the future.
         * 
         * @param delayMillis
         *            how long to wait before the timer elapses, in milliseconds
         * @param shiftRight
         *            whether to shift up or not
         * @param multiplier
         *            the number of steps to shift
         */
        public void schedule(int delayMillis, boolean shiftRight, int multiplier) {
            firstRun = true;
            this.shiftRight = shiftRight;
            this.multiplier = multiplier;
            super.schedule(delayMillis);
        }
    }
}