<commit_before>/**
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * Copyright (C) 2016 European Spallation Source ERIC.
 * 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;


import java.net.URL;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

import org.csstudio.display.builder.model.persist.NamedWidgetColors;
import org.csstudio.display.builder.model.persist.WidgetColorService;
import org.csstudio.display.builder.model.properties.NamedWidgetColor;
import org.csstudio.display.builder.model.properties.WidgetColor;
import org.csstudio.display.builder.model.util.ModelThreadPool;
import org.csstudio.javafx.PopOver;

import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.Slider;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;


/**
 * @author claudiorosati, European Spallation Source ERIC
 * @version 1.0.0 29 Nov 2017
 */
public class WidgetColorPopOverController implements Initializable {

    @FXML private GridPane root;

    @FXML private Label infoLabel;

    @FXML private ListView<NamedWidgetColor> colorNames;

    @FXML private ColorPicker picker;

    @FXML private Slider redSlider;
    @FXML private Slider greenSlider;
    @FXML private Slider blueSlider;
    @FXML private Slider alphaSlider;

    @FXML private Spinner<Integer> redSpinner;
    @FXML private Spinner<Integer> greenSpinner;
    @FXML private Spinner<Integer> blueSpinner;
    @FXML private Spinner<Integer> alphaSpinner;

    @FXML private Circle currentColorCircle;
    @FXML private Circle defaultColorCircle;
    @FXML private Circle originalColorCircle;

    @FXML private ButtonBar buttonBar;
    @FXML private Button defaultButton;
    @FXML private Button cancelButton;
    @FXML private Button okButton;

    private Consumer<WidgetColor>              colorChangeConsumer;
    private PopOver                            popOver;
    private final AtomicBoolean                namesLoaded   = new AtomicBoolean(false);
    private final Map<Color, NamedWidgetColor> namedColors   = Collections.synchronizedMap(new HashMap<>());
    private WidgetColor                        defaultColor  = null;
    private WidgetColor                        originalColor = null;
    private boolean                            updating      = false;

    /*
     * ---- color property -----------------------------------------------------
     */
    private final ObjectProperty<WidgetColor> color = new SimpleObjectProperty<WidgetColor>(this, "color", WidgetColorService.getColor(NamedWidgetColors.BACKGROUND)) {
        @Override
        protected void invalidated() {

            WidgetColor col = get();

            if ( col == null ) {
                set(WidgetColorService.getColor(NamedWidgetColors.BACKGROUND));
            }

        }
    };

    ObjectProperty<WidgetColor> colorProperty() {
        return color;
    }

    WidgetColor getColor() {
        return color.get();
    }

    void setColor( WidgetColor color ) {
        this.color.set(color);
    }

    /*
     * -------------------------------------------------------------------------
     */
    @Override
    public void initialize( URL location, ResourceBundle resources ) {

        updateButton(okButton, ButtonType.OK);
        updateButton(cancelButton, ButtonType.CANCEL);
        updateButton(defaultButton, new ButtonType(Messages.WidgetColorPopOver_DefaultButton, ButtonData.LEFT));

        okButton.setText(ButtonType.OK.getText());
        ButtonBar.setButtonData(okButton, ButtonType.OK.getButtonData());
        root.addEventFilter(KeyEvent.KEY_PRESSED, event ->
        {
            if (event.getCode() == KeyCode.ENTER)
                okPressed(null);
        });

        picker.valueProperty().addListener(( observable, oldColor, newColor ) -> {
            if ( !updating ) {
                setColor(JFXUtil.convert(newColor));
            }
        });
        defaultButton.disableProperty().bind(Bindings.createBooleanBinding(() -> getColor().equals(defaultColor), colorProperty()));
        okButton.disableProperty().bind(Bindings.createBooleanBinding(() -> getColor().equals(originalColor), colorProperty()));

        redSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 255));
        redSpinner.valueProperty().addListener(this::updateFromSpinner);
        greenSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 255));
        greenSpinner.valueProperty().addListener(this::updateFromSpinner);
        blueSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 255));
        blueSpinner.valueProperty().addListener(this::updateFromSpinner);
        alphaSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 255));
        alphaSpinner.valueProperty().addListener(this::updateFromSpinner);

        redSlider.valueProperty().addListener(this::updateFromSlider);
        greenSlider.valueProperty().addListener(this::updateFromSlider);
        blueSlider.valueProperty().addListener(this::updateFromSlider);
        alphaSlider.valueProperty().addListener(this::updateFromSlider);

        colorProperty().addListener(( observable, oldColor, newColor ) -> {

            updating = true;

            Color jfxColor = JFXUtil.convert(getColor());

            picker.setValue(jfxColor);
            currentColorCircle.setFill(jfxColor);

            redSlider.setValue(getRed());
            redSpinner.getValueFactory().setValue(getRed());
            greenSlider.setValue(getGreen());
            greenSpinner.getValueFactory().setValue(getGreen());
            blueSlider.setValue(getBlue());
            blueSpinner.getValueFactory().setValue(getBlue());
            alphaSlider.setValue(getAlpha());
            alphaSpinner.getValueFactory().setValue(getAlpha());

            updating = false;

        });

        colorNames.setCellFactory(view -> new NamedWidgetColorCell());<commit_msg>- On spinner's focus lost, spinner value is updated (spinners behave a bit oddly). - On spinner's focus gained, the value is selected speeding up text entry.<commit_after>/**
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * Copyright (C) 2016 European Spallation Source ERIC.
 * 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;


import java.net.URL;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

import org.csstudio.display.builder.model.persist.NamedWidgetColors;
import org.csstudio.display.builder.model.persist.WidgetColorService;
import org.csstudio.display.builder.model.properties.NamedWidgetColor;
import org.csstudio.display.builder.model.properties.WidgetColor;
import org.csstudio.display.builder.model.util.ModelThreadPool;
import org.csstudio.javafx.PopOver;

import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.Slider;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import javafx.scene.control.TextFormatter;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;


/**
 * @author claudiorosati, European Spallation Source ERIC
 * @version 1.0.0 29 Nov 2017
 */
public class WidgetColorPopOverController implements Initializable {

    @FXML private GridPane root;

    @FXML private Label infoLabel;

    @FXML private ListView<NamedWidgetColor> colorNames;

    @FXML private ColorPicker picker;

    @FXML private Slider redSlider;
    @FXML private Slider greenSlider;
    @FXML private Slider blueSlider;
    @FXML private Slider alphaSlider;

    @FXML private Spinner<Integer> redSpinner;
    @FXML private Spinner<Integer> greenSpinner;
    @FXML private Spinner<Integer> blueSpinner;
    @FXML private Spinner<Integer> alphaSpinner;

    @FXML private Circle currentColorCircle;
    @FXML private Circle defaultColorCircle;
    @FXML private Circle originalColorCircle;

    @FXML private ButtonBar buttonBar;
    @FXML private Button defaultButton;
    @FXML private Button cancelButton;
    @FXML private Button okButton;

    private Consumer<WidgetColor>              colorChangeConsumer;
    private PopOver                            popOver;
    private final AtomicBoolean                namesLoaded   = new AtomicBoolean(false);
    private final Map<Color, NamedWidgetColor> namedColors   = Collections.synchronizedMap(new HashMap<>());
    private WidgetColor                        defaultColor  = null;
    private WidgetColor                        originalColor = null;
    private boolean                            updating      = false;

    /*
     * ---- color property -----------------------------------------------------
     */
    private final ObjectProperty<WidgetColor> color = new SimpleObjectProperty<WidgetColor>(this, "color", WidgetColorService.getColor(NamedWidgetColors.BACKGROUND)) {
        @Override
        protected void invalidated() {

            WidgetColor col = get();

            if ( col == null ) {
                set(WidgetColorService.getColor(NamedWidgetColors.BACKGROUND));
            }

        }
    };

    ObjectProperty<WidgetColor> colorProperty() {
        return color;
    }

    WidgetColor getColor() {
        return color.get();
    }

    void setColor( WidgetColor color ) {
        this.color.set(color);
    }

    /*
     * -------------------------------------------------------------------------
     */
    @Override
    public void initialize( URL location, ResourceBundle resources ) {

        updateButton(okButton, ButtonType.OK);
        updateButton(cancelButton, ButtonType.CANCEL);
        updateButton(defaultButton, new ButtonType(Messages.WidgetColorPopOver_DefaultButton, ButtonData.LEFT));

        okButton.setText(ButtonType.OK.getText());
        ButtonBar.setButtonData(okButton, ButtonType.OK.getButtonData());
        root.addEventFilter(KeyEvent.KEY_PRESSED, event ->
        {
            if (event.getCode() == KeyCode.ENTER)
                okPressed(null);
        });

        picker.valueProperty().addListener(( observable, oldColor, newColor ) -> {
            if ( !updating ) {
                setColor(JFXUtil.convert(newColor));
            }
        });
        defaultButton.disableProperty().bind(Bindings.createBooleanBinding(() -> getColor().equals(defaultColor), colorProperty()));
        okButton.disableProperty().bind(Bindings.createBooleanBinding(() -> getColor().equals(originalColor), colorProperty()));

        SpinnerValueFactory<Integer> redSpinnerValueFactory = new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 255);
        TextFormatter<Integer> redSpinnerFormatter = new TextFormatter<>(redSpinnerValueFactory.getConverter(), redSpinnerValueFactory.getValue());

        redSpinnerValueFactory.valueProperty().bindBidirectional(redSpinnerFormatter.valueProperty());
        redSpinner.getEditor().setTextFormatter(redSpinnerFormatter);
        redSpinner.setValueFactory(redSpinnerValueFactory);
        redSpinner.valueProperty().addListener(this::updateFromSpinner);
        redSpinner.focusedProperty().addListener( ( s, ov, nv ) -> {
            if ( nv ) {
                Platform.runLater(() -> redSpinner.getEditor().selectAll());
            }
        });

        SpinnerValueFactory<Integer> greenSpinnerValueFactory = new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 255);
        TextFormatter<Integer> greenSpinnerFormatter = new TextFormatter<>(greenSpinnerValueFactory.getConverter(), greenSpinnerValueFactory.getValue());

        greenSpinnerValueFactory.valueProperty().bindBidirectional(greenSpinnerFormatter.valueProperty());
        greenSpinner.getEditor().setTextFormatter(greenSpinnerFormatter);
        greenSpinner.setValueFactory(greenSpinnerValueFactory);
        greenSpinner.valueProperty().addListener(this::updateFromSpinner);
        greenSpinner.focusedProperty().addListener( ( s, ov, nv ) -> {
            if ( nv ) {
                Platform.runLater(() -> greenSpinner.getEditor().selectAll());
            }
        });

        SpinnerValueFactory<Integer> blueSpinnerValueFactory = new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 255);
        TextFormatter<Integer> blueSpinnerFormatter = new TextFormatter<>(blueSpinnerValueFactory.getConverter(), blueSpinnerValueFactory.getValue());

        blueSpinnerValueFactory.valueProperty().bindBidirectional(blueSpinnerFormatter.valueProperty());
        blueSpinner.getEditor().setTextFormatter(blueSpinnerFormatter);
        blueSpinner.setValueFactory(blueSpinnerValueFactory);
        blueSpinner.valueProperty().addListener(this::updateFromSpinner);
        blueSpinner.focusedProperty().addListener( ( s, ov, nv ) -> {
            if ( nv ) {
                Platform.runLater(() -> blueSpinner.getEditor().selectAll());
            }
        });

        SpinnerValueFactory<Integer> alphaSpinnerValueFactory = new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 255);
        TextFormatter<Integer> alphaSpinnerFormatter = new TextFormatter<>(alphaSpinnerValueFactory.getConverter(), alphaSpinnerValueFactory.getValue());

        alphaSpinnerValueFactory.valueProperty().bindBidirectional(alphaSpinnerFormatter.valueProperty());
        alphaSpinner.getEditor().setTextFormatter(alphaSpinnerFormatter);
        alphaSpinner.setValueFactory(alphaSpinnerValueFactory);
        alphaSpinner.valueProperty().addListener(this::updateFromSpinner);
        alphaSpinner.focusedProperty().addListener( ( s, ov, nv ) -> {
            if ( nv ) {
                Platform.runLater(() -> alphaSpinner.getEditor().selectAll());
            }
        });

        redSlider.valueProperty().addListener(this::updateFromSlider);
        greenSlider.valueProperty().addListener(this::updateFromSlider);
        blueSlider.valueProperty().addListener(this::updateFromSlider);
        alphaSlider.valueProperty().addListener(this::updateFromSlider);

        colorProperty().addListener(( observable, oldColor, newColor ) -> {

            updating = true;

            Color jfxColor = JFXUtil.convert(getColor());

            picker.setValue(jfxColor);
            currentColorCircle.setFill(jfxColor);

            redSlider.setValue(getRed());
            redSpinner.getValueFactory().setValue(getRed());
            greenSlider.setValue(getGreen());
            greenSpinner.getValueFactory().setValue(getGreen());
            blueSlider.setValue(getBlue());
            blueSpinner.getValueFactory().setValue(getBlue());
            alphaSlider.setValue(getAlpha());
            alphaSpinner.getValueFactory().setValue(getAlpha());

            updating = false;

        });

        colorNames.setCellFactory(view -> new NamedWidgetColorCell());
{'fraction_non_alphanumeric': 0.1138456193178528, 'fraction_numerical': 0.003821877352481325, 'mean_word_length': 3.3381060035167045, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 4}, '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': '4270350', 'n_tokens_mistral': 4594, 'n_tokens_neox': 4501, 'n_words': 844}