<commit_before>/*******************************************************************************
 * Copyright (c) 2015-2018 Oak Ridge National Laboratory.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.csstudio.display.builder.representation.javafx.widgets;

import static org.csstudio.display.builder.representation.ToolkitRepresentation.logger;

import java.util.logging.Level;

import org.csstudio.display.builder.model.DirtyFlag;
import org.csstudio.display.builder.model.UntypedWidgetPropertyListener;
import org.csstudio.display.builder.model.WidgetProperty;
import org.csstudio.display.builder.model.util.FormatOptionHandler;
import org.csstudio.display.builder.model.widgets.SpinnerWidget;
import org.csstudio.display.builder.representation.javafx.JFXUtil;
import org.csstudio.javafx.Styles;
import org.diirt.vtype.Display;
import org.diirt.vtype.VNumber;
import org.diirt.vtype.VType;
import org.diirt.vtype.ValueUtil;

import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectPropertyBase;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import javafx.scene.input.ContextMenuEvent;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.util.StringConverter;

/** Creates JavaFX item for model widget
 *  @author <NAME>
 */
@SuppressWarnings("nls")
public class SpinnerRepresentation extends RegionBaseRepresentation<Spinner<String>, SpinnerWidget>
{
    /** Is user actively editing the content, so updates should be suppressed? */
    private volatile boolean active = false;

    private final DirtyFlag dirty_style = new DirtyFlag();
    private final DirtyFlag dirty_content = new DirtyFlag();
    private final UntypedWidgetPropertyListener behaviorChangedListener = this::behaviorChanged;
    private final UntypedWidgetPropertyListener contentChangedListener = this::contentChanged;
    private final UntypedWidgetPropertyListener styleChangedListener = this::styleChanged;

    private Node pressedButton = null;

    protected volatile String value_text = "<?>";
    protected volatile VType  value      = null;
    private volatile double   value_max  = 100.0;
    private volatile double   value_min  = 0.0;

    @Override
    protected final Spinner<String> createJFXNode() throws Exception
    {
        final Spinner<String> spinner = new Spinner<String>();
        spinner.setValueFactory(createSVF());
        styleChanged(null, null, null);
        spinner.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
        spinner.focusedProperty().addListener((property, oldval, newval)->
        {
            if (!spinner.isFocused())
                restore();
            active = false;
        });
        spinner.getEditor().setOnKeyPressed((final KeyEvent event) ->
        {
            switch (event.getCode())
            {
            case ESCAPE: //TODO: fix: escape key event not sensed
                // Revert original value, leave active state
                restore();
                active = false;
                break;
            case ENTER:
                // Submit value, leave active state
                submit();
                active = false;
                break;
            //incrementing by keyboard
            case UP:
            case PAGE_UP:
                if (!active)
                    jfx_node.getValueFactory().increment(1);
                break;
            case DOWN:
            case PAGE_DOWN:
                if (!active)
                    jfx_node.getValueFactory().decrement(1);
                break;
            default:
                // Any other key results in active state
                active = true;
            }
        });

        // While context menu is handled by SWT, there is a problem
        // when the primary button is held down to increment/decrement the spinner,
        // and _then_ the secondary button is used to open the context menu.
        // Releasing the primary button will in that case NOT stop the value changes
        // because SWT has the focus.
        // Fix is to trace the pressedButton,
        // and suppress context menu while button is held down,
        // and stopping incrementing/decrementing when exiting the spinner.
        spinner.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> {
            if ( e.getButton() == MouseButton.PRIMARY ) {

                Node node = e.getPickResult().getIntersectedNode();

                if ( node.getStyleClass().contains("increment-arrow-button")
                  || node.getStyleClass().contains("decrement-arrow-button") ) {
                    pressedButton = node;
                }

            } else if ( e.getButton() == MouseButton.SECONDARY ) {
                // Disable the contemporary triggering of a value change and of the
                // opening of contextual menu when right-clicking on the spinner's
                // buttons.
                e.consume();
            }
        });

        spinner.addEventFilter(MouseEvent.MOUSE_RELEASED, e -> {
            if ( e.getButton() == MouseButton.PRIMARY ) {
                pressedButton = null;
            }
        });

        spinner.addEventFilter(ContextMenuEvent.CONTEXT_MENU_REQUESTED, e -> {

            Node node = e.getPickResult().getIntersectedNode();

            // Contextual menu is forbidden inside the spinner's buttons, otherwise
            // if a button has focus (i.e. previously clicked), right-clicking on it
            // and, after the context menu id shown, double-clicking outside the
            // the menu and the spinner's boundary will close the menu and press
            // the button (probably because SWT remember the JFX cursor's position
            // when the menu was triggered).
            // This implementation will limit the context menu only inside the text area.
            if ( node.getStyleClass().contains("increment-arrow-button")
              || node.getStyleClass().contains("decrement-arrow-button") ) {
               e.consume();
            } else {
                // Allow SVW context-menu be opened on top of the JavaFX one
                // when right-clicking on the text area.
                spinner.getParent().fireEvent(e);
            }

        });

        spinner.addEventFilter(MouseEvent.MOUSE_EXITED, e -> {<commit_msg>JavaFX context-menu disabled. Only the SWT will appear.<commit_after>/*******************************************************************************
 * Copyright (c) 2015-2018 Oak Ridge National Laboratory.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.csstudio.display.builder.representation.javafx.widgets;

import static org.csstudio.display.builder.representation.ToolkitRepresentation.logger;

import java.util.logging.Level;

import org.csstudio.display.builder.model.DirtyFlag;
import org.csstudio.display.builder.model.UntypedWidgetPropertyListener;
import org.csstudio.display.builder.model.WidgetProperty;
import org.csstudio.display.builder.model.util.FormatOptionHandler;
import org.csstudio.display.builder.model.widgets.SpinnerWidget;
import org.csstudio.display.builder.representation.javafx.JFXUtil;
import org.csstudio.javafx.Styles;
import org.diirt.vtype.Display;
import org.diirt.vtype.VNumber;
import org.diirt.vtype.VType;
import org.diirt.vtype.ValueUtil;

import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectPropertyBase;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.Event;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import javafx.scene.input.ContextMenuEvent;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.util.StringConverter;

/** Creates JavaFX item for model widget
 *  @author <NAME>
 */
@SuppressWarnings("nls")
public class SpinnerRepresentation extends RegionBaseRepresentation<Spinner<String>, SpinnerWidget>
{
    /** Is user actively editing the content, so updates should be suppressed? */
    private volatile boolean active = false;

    private final DirtyFlag dirty_style = new DirtyFlag();
    private final DirtyFlag dirty_content = new DirtyFlag();
    private final UntypedWidgetPropertyListener behaviorChangedListener = this::behaviorChanged;
    private final UntypedWidgetPropertyListener contentChangedListener = this::contentChanged;
    private final UntypedWidgetPropertyListener styleChangedListener = this::styleChanged;

    private Node pressedButton = null;

    protected volatile String value_text = "<?>";
    protected volatile VType  value      = null;
    private volatile double   value_max  = 100.0;
    private volatile double   value_min  = 0.0;

    @Override
    protected final Spinner<String> createJFXNode() throws Exception
    {
        final Spinner<String> spinner = new Spinner<String>();
        spinner.setValueFactory(createSVF());
        styleChanged(null, null, null);
        spinner.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
        spinner.focusedProperty().addListener((property, oldval, newval)->
        {
            if (!spinner.isFocused())
                restore();
            active = false;
        });
        spinner.getEditor().setOnKeyPressed((final KeyEvent event) ->
        {
            switch (event.getCode())
            {
            case ESCAPE: //TODO: fix: escape key event not sensed
                // Revert original value, leave active state
                restore();
                active = false;
                break;
            case ENTER:
                // Submit value, leave active state
                submit();
                active = false;
                break;
            //incrementing by keyboard
            case UP:
            case PAGE_UP:
                if (!active)
                    jfx_node.getValueFactory().increment(1);
                break;
            case DOWN:
            case PAGE_DOWN:
                if (!active)
                    jfx_node.getValueFactory().decrement(1);
                break;
            default:
                // Any other key results in active state
                active = true;
            }
        });

        // While context menu is handled by SWT, there is a problem
        // when the primary button is held down to increment/decrement the spinner,
        // and _then_ the secondary button is used to open the context menu.
        // Releasing the primary button will in that case NOT stop the value changes
        // because SWT has the focus.
        // Fix is to trace the pressedButton,
        // and suppress context menu while button is held down,
        // and stopping incrementing/decrementing when exiting the spinner.
        spinner.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> {
            if ( e.getButton() == MouseButton.PRIMARY ) {

                Node node = e.getPickResult().getIntersectedNode();

                if ( node.getStyleClass().contains("increment-arrow-button")
                  || node.getStyleClass().contains("decrement-arrow-button") ) {
                    pressedButton = node;
                }

            } else if ( e.getButton() == MouseButton.SECONDARY ) {
                // Disable the contemporary triggering of a value change and of the
                // opening of contextual menu when right-clicking on the spinner's
                // buttons.
                e.consume();
            }
        });

        spinner.addEventFilter(MouseEvent.MOUSE_RELEASED, e -> {
            if ( e.getButton() == MouseButton.PRIMARY ) {
                pressedButton = null;
            }
        });

        spinner.addEventFilter(ContextMenuEvent.CONTEXT_MENU_REQUESTED, e -> {

            Node node = e.getPickResult().getIntersectedNode();

            // Contextual menu is forbidden inside the spinner's buttons, otherwise
            // if a button has focus (i.e. previously clicked), right-clicking on it
            // and, after the context menu id shown, double-clicking outside the
            // the menu and the spinner's boundary will close the menu and press
            // the button (probably because SWT remember the JFX cursor's position
            // when the menu was triggered).
            // This implementation will limit the context menu only inside the text area.
            if ( !node.getStyleClass().contains("increment-arrow-button")
              && !node.getStyleClass().contains("decrement-arrow-button") ) {
                spinner.getParent().fireEvent((Event) e.clone());
            }

            e.consume();

        });

        spinner.addEventFilter(MouseEvent.MOUSE_EXITED, e -> {
{'fraction_non_alphanumeric': 0.10893653763131386, 'fraction_numerical': 0.002878111958555188, 'mean_word_length': 2.4939668174962293, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3963328', 'n_tokens_mistral': 3651, 'n_tokens_neox': 3477, 'n_words': 974}