All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jpac.fx.Dashboard Maven / Gradle / Ivy

The newest version!
/**
 * PROJECT   : Elbfisch - java process automation controller (jPac)
 * MODULE    : Dashboard.java
 * VERSION   : -
 * DATE      : -
 * PURPOSE   : 
 * AUTHOR    : Bernd Schuster, MSK Gesellschaft fuer Automatisierung mbH, Schenefeld
 * REMARKS   : -
 * CHANGES   : CH#n   
 *
 * This file is part of the jPac process automation controller.
 * jPac is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * jPac 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the jPac If not, see .
 */
package org.jpac.fx;

import com.sun.javafx.tk.Toolkit;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.geometry.Dimension2D;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.WindowEvent;

import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import org.jpac.SignalNotRegisteredException;

/**
 *
 * @author berndschuster
 */
public class Dashboard extends Stage {
    static  Logger Log = LoggerFactory.getLogger("jpac.fx"); 
    private static final int  SLIDERWIDTH         = 20;
    private static final int  DEFAULTMAXNUMOFROWS = 20;
    private static final int  STAGETITLEBARHEIGHT = 40;//estimate
    private static final int  TABLETITLEBARHEIGHT = 60;//estimate
    private static final int  CONTROLPANELHEIGHT  = 45;//estimate
    
//    static  HashMap displayedDashboards;
    
    private DashboardData     dashboardData;
    private boolean           dashboardSaved;
    private boolean           failedToSaveDashboardSaved;
    private boolean           someSignalsNotFound;
    private Scene             scene;
    private VBox              contentPanel;
    private HBox              bottomControlPanel;
    private Button            btnClose;
    private ToggleButton      btnConnect;
    private Button            btnSaveDashboard;
    private HBox              buttonControlPanel;
    private HBox              infoTextPanel;
    private Label             lbHideHierarchyLevel;
    private Label             lbInfoText;
    private SignalTable       signalTable;
    private Spinner  spnHierarchyLevel;
    private DashboardLauncher dashboardLauncher;
    
    public Dashboard(DashboardData dashboardData, DashboardLauncher dashboardLauncher) throws SignalNotRegisteredException {
        initComponents();
        this.initStyle(StageStyle.DECORATED);
        controlInfoText(); 
        dashboardSaved             = false;
        failedToSaveDashboardSaved = false;
        showConnectButton(true);
        showSaveDashboardButton(true);
        showCloseButton(true);
        showHierarchyLevelControls(true);
        setScene(scene);
        this.dashboardData     = dashboardData;
        this.dashboardLauncher = dashboardLauncher;
        signalTable.addEntries(dashboardData);
        someSignalsNotFound = signalTable.isSomeSignalsNotFound();
//        //replace list of items with corresponding SortedList
//        SortedList sortedList = new SortedList<>(signalTable.getItems(), (SignalListItem s1, SignalListItem s2) -> s1.getSignalIdentifier().compareTo(s2.getSignalIdentifier()));
//        sortedList.comparatorProperty().bind(signalTable.comparatorProperty());
//        signalTable.setItems(sortedList);
        
        this.setTitle("Dashboard for " + dashboardData.getName());
        if (dashboardData.getDialogSize() != null){
            this.setWidth(dashboardData.getDialogSize().getWidth());
            this.setMaxHeight(CONTROLPANELHEIGHT + TABLETITLEBARHEIGHT + signalTable.getPreferredCellHeight() * signalTable.getItems().size() + 2 * CONTROLPANELHEIGHT);
            this.setHeight(dashboardData.getDialogSize().getHeight());
        }
        else{
            this.setWidth(signalTable.getDefaultWidth() + SLIDERWIDTH);
            int numOfRows = signalTable.getItems().size() > DEFAULTMAXNUMOFROWS ? DEFAULTMAXNUMOFROWS : signalTable.getItems().size();
            double height = CONTROLPANELHEIGHT + TABLETITLEBARHEIGHT + signalTable.getPreferredCellHeight() * numOfRows + 2 * CONTROLPANELHEIGHT;
            this.setMaxHeight(CONTROLPANELHEIGHT + TABLETITLEBARHEIGHT + signalTable.getPreferredCellHeight() * signalTable.getItems().size() + 2 * CONTROLPANELHEIGHT);
            this.setHeight(height);
            dashboardData.setDialogSize(new Dimension2D(this.getWidth(), this.getHeight()));
        }
        if (dashboardData.getScreenPosition() != null){
            Rectangle2D screenDimension = Screen.getPrimary().getVisualBounds();
            int x = (int)dashboardData.getScreenPosition().getX();
            if (x + getWidth() > screenDimension.getWidth()){
                x = (int)(screenDimension.getWidth() - getWidth());
            }
            int y = (int)dashboardData.getScreenPosition().getY();
            if (y + getHeight() > screenDimension.getHeight()){
                y = (int)(screenDimension.getHeight() - getHeight());
            }
            this.setX(x);
            this.setY(y);
        } else {
            dashboardData.setScreenPosition(new Point2D(this.getX(), this.getY()));        	
        }
        if (dashboardData.getIdentifierColumnWidth() > 0){
            signalTable.identifierColumn.setPrefWidth(dashboardData.getIdentifierColumnWidth());
        } else {
        	dashboardData.setIdentifierColumnWidth((int)signalTable.identifierColumn.getPrefWidth());
        }
        if (dashboardData.getStateColumnWidth() > 0){
            signalTable.stateColumn.setPrefWidth(dashboardData.getStateColumnWidth());
        } else {
        	dashboardData.setStateColumnWidth((int)signalTable.stateColumn.getPrefWidth());
        }
        if (dashboardData.getEditColumnWidth() > 0){
            signalTable.editColumn.setPrefWidth(dashboardData.getEditColumnWidth());
        } else {
        	dashboardData.setEditColumnWidth((int)signalTable.editColumn.getPrefWidth());
        }
        
        setOnShowing(new EventHandler(){
            @Override
            public void handle(WindowEvent event) {
                if (dashboardData != null){
                    dashboardData.setDashboard(getThisDashboard());
                    if (dashboardData.isConnected()){
                        btnConnect.setSelected(true);
                        btnConnect.setText("disconnect");
                        signalTable.connect();
                    }
                }
            }
        });

        setOnCloseRequest(new EventHandler(){
            @Override
            public void handle(WindowEvent event) {
                prepareCloseOperation();
            }     
        });

        spnHierarchyLevel.getValueFactory().setValue(dashboardData.getHiddenHierarchyLevel());
        clipIdentifiers(spnHierarchyLevel.getValue());
        
        showConnectButton(dashboardData.isShowConnectButton());
        showSaveDashboardButton(dashboardData.isShowSaveDashboardButton());
        showCloseButton(dashboardData.isShowConnectButton());
        showHierarchyLevelControls(dashboardData.isShowHierarchyLevelControls());
        controlInfoText();
    }

    public Dashboard(String signalTableName) throws IOException, ClassNotFoundException, SignalNotRegisteredException {
        this(DashboardData.load(signalTableName), null);
    }

    private void initComponents() {
        setTitle("Dashboard");
        contentPanel          = new VBox();
        buttonControlPanel    = new HBox();
        signalTable           = new SignalTable();
        bottomControlPanel    = new HBox();
        infoTextPanel         = new HBox();
        signalTable.adjustDefaultStateCellSize(); 
        
        btnConnect = new ToggleButton();
        btnConnect.setText("connect");
        btnConnect.setOnAction(new EventHandler() {    
            @Override
            public void handle(ActionEvent event) {
                handleOnActionBtnConnect(event);
            }
        });        

        lbHideHierarchyLevel = new Label();
        lbHideHierarchyLevel.setText("  hide hierarchy level of identifier");
        lbHideHierarchyLevel.setAlignment(Pos.CENTER);
        
        spnHierarchyLevel = new Spinner();
        spnHierarchyLevel.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 9));
        spnHierarchyLevel.setPrefWidth(Toolkit.getToolkit().getFontLoader().getFontMetrics(Font.getDefault()).computeStringWidth("XXXX") + 20);
        spnHierarchyLevel.setOnMouseClicked(new EventHandler() {
                                                @Override
                                                public void handle(MouseEvent event) {
                                                    handleOnMouseClickedEventSpnHierarchyLevel(event);
                                                }
                                            });

        buttonControlPanel.setPadding(new Insets(5,5,5,5));
        buttonControlPanel.setSpacing(5.0);
        buttonControlPanel.getChildren().addAll(btnConnect, lbHideHierarchyLevel, spnHierarchyLevel);

        btnSaveDashboard = new Button();
        btnSaveDashboard.setText("save");
        btnSaveDashboard.setOnAction(new EventHandler() {    
            @Override
            public void handle(ActionEvent event) {
                handleOnActionBtnSave(event);
            }
        });        

//        btnClose = new Button();
//        btnClose.setText("close");
//        btnClose.setOnAction(new EventHandler() {    
//            @Override
//            public void handle(ActionEvent event) {
//                handleOnActionBtnClose(event);
//            }
//        });        
        bottomControlPanel.setPadding(new Insets(5,5,5,5));
        bottomControlPanel.setSpacing(5.0);
        bottomControlPanel.getChildren().add(btnSaveDashboard);
//        bottomControlPanel.getChildren().add(btnClose);
        
        lbInfoText = new Label();
        infoTextPanel.setPadding(new Insets(5,5,5,5));
        infoTextPanel.getChildren().addAll(lbInfoText);

        contentPanel.setPrefWidth(signalTable.getWidth());
        contentPanel.setPrefHeight(buttonControlPanel.getHeight() + signalTable.getHeight() + bottomControlPanel.getHeight() + infoTextPanel.getHeight());
        contentPanel.getChildren().addAll(buttonControlPanel, signalTable, bottomControlPanel, infoTextPanel);
        scene = new Scene(contentPanel,contentPanel.getPrefWidth(), contentPanel.getPrefHeight());
    }
    
    private Dashboard getThisDashboard(){
        return this;
    }
    
    private void handleOnActionBtnConnect(ActionEvent evt) {
        if (btnConnect.isSelected()){
            btnConnect.setText("disconnect");
            signalTable.connect();
        }
        else{
            btnConnect.setText("connect");
            signalTable.disconnect();
        }
        controlInfoText();
    }

//    private void handleOnActionBtnClose(ActionEvent evt) {
//        close();
//    }

    private void handleOnActionBtnSave(ActionEvent evt) {
        store();
        controlInfoText();
    }

    private void handleOnMouseClickedEventSpnHierarchyLevel(MouseEvent event) {
        clipIdentifiers(spnHierarchyLevel.getValue());
    }

    private void controlInfoText(){
        if (failedToSaveDashboardSaved){
            lbInfoText.setText("failed to save dashboard.");
            lbInfoText.setStyle("-fx-text-fill: red;");                        
            failedToSaveDashboardSaved = false;
        } else if (dashboardSaved){
            lbInfoText.setText("dashboard saved");
            lbInfoText.setStyle("-fx-text-fill: black;");                        
            dashboardSaved = false;
        } else if (someSignalsNotFound){
            lbInfoText.setText("some signals removed or changed. Recreate dashboard");
            lbInfoText.setStyle("-fx-text-fill: red;");                        
            dashboardSaved = false;
        }
        else{
            lbInfoText.setText("");
            lbInfoText.setStyle("-fx-text-fill: black;");                                    
        }
    }
        
    protected void clipIdentifiers(int hierarchyLevel){
        signalTable.getItems().forEach(s -> ((SignalListItem)s).clipVisibleIdentifier(hierarchyLevel));
    }
    
    protected void prepareCloseOperation(){
        if (atLeastOnePropertyChanged()){
            boolean save = askUserForSavingChanges();
            if (save){
                try{
                    store();
                }
                catch(Exception exc){
                    Log.error("Error ", exc);
                }
            }
        }
        dashboardData.setDashboard(null);
        if (dashboardLauncher != null){
            Event.fireEvent(dashboardLauncher, new DashboardClosedEvent(dashboardData));
        }
        if (signalTable.isConnected()){
            signalTable.disconnect();
        }                
    }
                
    public void showConnectButton(boolean show){
        btnConnect.setVisible(show);
    }

    public void showSaveDashboardButton(boolean show){
        btnSaveDashboard.setVisible(show);
    }
    
    public void showHierarchyLevelControls(boolean show){
        spnHierarchyLevel.setVisible(show);
        lbHideHierarchyLevel.setVisible(show);
    }
    
    public void showCloseButton(boolean show){
//        btnClose.setVisible(show);
    } 
    
    @Override
    public void close(){
        prepareCloseOperation();
        super.close();
    }
    
    public void store(){
        try{
            if (dashboardData != null){
                dashboardData.setScreenPosition(new Point2D(getX(),getY()));
                dashboardData.setDialogSize(new Dimension2D(getWidth(), getHeight()));
                dashboardData.setIdentifierColumnWidth((int)signalTable.identifierColumn.getWidth());
                dashboardData.setStateColumnWidth((int)signalTable.stateColumn.getWidth());
                dashboardData.setEditColumnWidth((int)signalTable.editColumn.getWidth());
                dashboardData.setConnected(signalTable.isConnected());
                dashboardData.setHiddenHierarchyLevel((Integer)spnHierarchyLevel.getValue());
                dashboardData.setShowConnectButton(btnConnect.isVisible());
                dashboardData.setShowSaveDashboardButton(btnSaveDashboard.isVisible());
  //              dashboardData.setShowCloseButton(btnClose.isVisible());
                //store items in the order of occurence inside the table view
                dashboardData.clear();
                signalTable.getItems().forEach(i -> dashboardData.add(i));
                if (dashboardLauncher == null){
                    //if not launched by desktop launcher
                    //save all properties to disk
                    //otherwise it is done by the launcher
                    dashboardData.store();
                }
                dashboardSaved = true;
            }
        }
        catch(Exception exc){
            Log.error("Error: ", exc);
            failedToSaveDashboardSaved = true;
            
        }

    }
    
    protected boolean atLeastOnePropertyChanged(){
        boolean changed = false;
            if (dashboardData != null){
                //changed  = !dashboardData.getScreenPosition().equals(new Point2D(getX(),getY()));
                changed = !dashboardData.getDialogSize().equals(new Dimension2D(getWidth(), getHeight()));
                changed |= dashboardData.getIdentifierColumnWidth() != (int)signalTable.identifierColumn.getWidth();
                changed |= dashboardData.getStateColumnWidth()      != (int)signalTable.stateColumn.getWidth();
                changed |= dashboardData.getEditColumnWidth()       != (int)signalTable.editColumn.getWidth();
                changed |= dashboardData.getHiddenHierarchyLevel()  != spnHierarchyLevel.getValue();
            }        
        return changed;
    }

    protected boolean askUserForSavingChanges(){
        final String SAVE     = "Save";
        final String DISCARD  = "Discard";
        Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
        alert.initStyle(StageStyle.UTILITY);
        alert.setTitle("Confirmation");
        alert.setHeaderText("you are about to drop changes to dashboard '" + dashboardData.getName() + "'");
        alert.setContentText("do you want to save changes ?");
        String[] options = new String[]{SAVE, DISCARD};

        List buttons = new ArrayList<>();
        for (String option : options) {
            buttons.add(new ButtonType(option));
        }
        alert.getButtonTypes().setAll(buttons);

        Optional result = alert.showAndWait();
        return result.get().getText().equals(SAVE);
    }
    
    static class DashboardClosedEvent extends Event{
        protected DashboardData dashboardData;
        
        public static final EventType DASHBOARD_CLOSED = new EventType<>(Event.ANY, "DASHBOARD_CLOSED");

        public DashboardClosedEvent(DashboardData dashboardData) {
            super(DASHBOARD_CLOSED);
            this.dashboardData = dashboardData;
        }   

        public DashboardData getDashboardData() {
            return dashboardData;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy