server
Clone or download
Modified Files
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<modelVersion>4.0.0</modelVersion>
<groupId>com.StreamPi</groupId>
<groupId>com.stream_pi</groupId>
<artifactId>Server</artifactId>
<artifactId>server</artifactId>
<version>1.0.0</version>
<version>1.0.0</version>
<properties>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.target>11</maven.compiler.target>
<client.plugin.version>0.1.35</client.plugin.version>
<client.plugin.version>0.1.35</client.plugin.version>
<JavaFXVersion>16-ea+6</JavaFXVersion>
<JavaFXVersion>16-ea+6</JavaFXVersion>
<CommonsBeanUtilsVersion>1.9.4</CommonsBeanUtilsVersion>
<CommonsBeanUtilsVersion>1.9.4</CommonsBeanUtilsVersion>
<CommonsConfigurationVersion>2.7</CommonsConfigurationVersion>
<CommonsConfigurationVersion>2.7</CommonsConfigurationVersion>
<ActionAPIVersion>1.0.0</ActionAPIVersion>
<ActionAPIVersion>1.0.0</ActionAPIVersion>
<ThemeAPIVersion>1.0.0</ThemeAPIVersion>
<ThemeAPIVersion>1.0.0</ThemeAPIVersion>
<UtilVersion>1.0.0</UtilVersion>
<UtilVersion>1.0.0</UtilVersion>
<!--CommAPIVersion>1.0.0</CommAPIVersion-->
<!--CommAPIVersion>1.0.0</CommAPIVersion-->
<Log4JVersion>2.14.0</Log4JVersion>
<Log4JVersion>2.14.0</Log4JVersion>
<IkonliVersion>11.5.0</IkonliVersion>
<IkonliVersion>11.5.0</IkonliVersion>
<IkonliFA5PackVersion>11.5.0</IkonliFA5PackVersion>
<IkonliFA5PackVersion>11.5.0</IkonliFA5PackVersion>
<MainClassName>com.StreamPi.Server.Main</MainClassName>
<MainClassName>com.stream_pi.server.Main</MainClassName>
</properties>
</properties>
<dependencies>
<dependencies>
<dependency>
<dependency>
<groupId>org.openjfx</groupId>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<artifactId>javafx-controls</artifactId>
<version>${JavaFXVersion}</version>
<version>${JavaFXVersion}</version>
</dependency>
</dependency>
<dependency>
<dependency>
<groupId>org.openjfx</groupId>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<artifactId>javafx-media</artifactId>
<version>${JavaFXVersion}</version>
<version>${JavaFXVersion}</version>
</dependency>
</dependency>
<dependency>
<dependency>
<groupId>org.kordamp.ikonli</groupId>
<groupId>org.kordamp.ikonli</groupId>
<artifactId>ikonli-fontawesome5-pack</artifactId>
<artifactId>ikonli-fontawesome5-pack</artifactId>
<version>${IkonliFA5PackVersion}</version>
<version>${IkonliFA5PackVersion}</version>
</dependency>
</dependency>
<dependency>
<dependency>
<groupId>org.kordamp.ikonli</groupId>
<groupId>org.kordamp.ikonli</groupId>
<artifactId>ikonli-javafx</artifactId>
<artifactId>ikonli-javafx</artifactId>
<version>${IkonliVersion}</version>
<version>${IkonliVersion}</version>
</dependency>
</dependency>
<dependency>
<dependency>
<groupId>org.openjfx</groupId>
<groupId>org.openjfx</groupId>
<artifactId>javafx-base</artifactId>
<artifactId>javafx-base</artifactId>
<version>${JavaFXVersion}</version>
<version>${JavaFXVersion}</version>
</dependency>
</dependency>
<dependency>
<dependency>
<groupId>com.StreamPi</groupId>
<groupId>com.stream_pi</groupId>
<artifactId>ActionAPI</artifactId>
<artifactId>action_api</artifactId>
<version>${ActionAPIVersion}</version>
<version>${ActionAPIVersion}</version>
</dependency>
</dependency>
<dependency>
<dependency>
<groupId>com.StreamPi</groupId>
<groupId>com.stream_pi</groupId>
<artifactId>Util</artifactId>
<artifactId>util</artifactId>
<version>${UtilVersion}</version>
<version>${UtilVersion}</version>
</dependency>
</dependency>
<dependency>
<dependency>
<groupId>com.StreamPi</groupId>
<groupId>com.stream_pi</groupId>
<artifactId>ThemeAPI</artifactId>
<artifactId>themeapi</artifactId>
<version>${ThemeAPIVersion}</version>
<version>${ThemeAPIVersion}</version>
</dependency>
</dependency>
<dependency>
<dependency>
<groupId>org.json</groupId>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<artifactId>json</artifactId>
<version>20201115</version>
<version>20201115</version>
</dependency>
</dependency>
</dependencies>
</dependencies>
<repositories>
<repositories>
<repository>
<repository>
<id>gluon-releases</id>
<id>gluon-releases</id>
<url>http://nexus.gluonhq.com/nexus/content/repositories/releases/</url>
<url>http://nexus.gluonhq.com/nexus/content/repositories/releases/</url>
</repository>
</repository>
</repositories>
</repositories>
<build>
<build>
<plugins>
<plugins>
<plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<version>3.8.1</version>
<configuration>
<configuration>
<release>11</release>
<release>11</release>
</configuration>
</configuration>
</plugin>
</plugin>
<plugin>
<plugin>
<groupId>org.openjfx</groupId>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.5</version>
<version>0.0.5</version>
<configuration>
<configuration>
<commandlineArgs>-DStreamPi.startupRunnerFileName=run_min -DStreamPi.startupMode=maximise</commandlineArgs>
<commandlineArgs>-DStreamPi.startupRunnerFileName=run_min -DStreamPi.startupMode=maximise</commandlineArgs>
<options>
<options>
<option>-Dglass.gtk.uiScale=1.0</option>
<option>-Dglass.gtk.uiScale=1.0</option>
</options>
</options>
<mainClass>${MainClassName}</mainClass>
<mainClass>${MainClassName}</mainClass>
</configuration>
</configuration>
</plugin>
</plugin>
<plugin>
<plugin>
<groupId>com.gluonhq</groupId>
<groupId>com.gluonhq</groupId>
<artifactId>client-maven-plugin</artifactId>
<artifactId>client-maven-plugin</artifactId>
<version>${client.plugin.version}</version>
<version>${client.plugin.version}</version>
<configuration>
<configuration>
<bundlesList>
<bundlesList>
<list>com.sun.org.apache.xerces.internal.impl.msg.XMLMessages</list>
<list>com.sun.org.apache.xerces.internal.impl.msg.XMLMessages</list>
</bundlesList>
</bundlesList>
<reflectionList>
<reflectionList>
<list>java.util.logging.FileHandler</list>
<list>java.util.logging.FileHandler</list>
</reflectionList>
</reflectionList>
<nativeImageArgs>--report-unsupported-elements-at-runtime,--initialize-at-build-time=com.sun.org.apache.xml.internal.serializer.ToXMLStream,-Dsvm.targetname=android,--enable-https,--enable-http,-H:Log=registerResource</nativeImageArgs>
<nativeImageArgs>--report-unsupported-elements-at-runtime,--initialize-at-build-time=com.sun.org.apache.xml.internal.serializer.ToXMLStream,-Dsvm.targetname=android,--enable-https,--enable-http,-H:Log=registerResource</nativeImageArgs>
<mainClass>${MainClassName}</mainClass>
<mainClass>${MainClassName}</mainClass>
</configuration>
</configuration>
</plugin>
</plugin>
<plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.2.0</version>
<version>3.2.0</version>
<configuration>
<configuration>
<includeEmptyDirs>true</includeEmptyDirs>
<includeEmptyDirs>true</includeEmptyDirs>
</configuration>
</configuration>
</plugin>
</plugin>
</plugins>
</plugins>
</build>
</build>
<pluginRepositories>
<pluginRepositories>
<pluginRepository>
<pluginRepository>
<id>gluon-releases</id>
<id>gluon-releases</id>
<url>http://nexus.gluonhq.com/nexus/content/repositories/releases/</url>
<url>http://nexus.gluonhq.com/nexus/content/repositories/releases/</url>
</pluginRepository>
</pluginRepository>
<pluginRepository>
<pluginRepository>
<id>gluon-snapshots</id>
<id>gluon-snapshots</id>
<url>https://nexus.gluonhq.com/nexus/content/repositories/public-snapshots</url>
<url>https://nexus.gluonhq.com/nexus/content/repositories/public-snapshots</url>
</pluginRepository>
</pluginRepository>
</pluginRepositories>
</pluginRepositories>
</project>
</project>
/*
Stream-Pi - Free & Open-Source Modular Cross-Platform Programmable Macropad
Copyright (C) 2019-2021 Debayan Sutradhar (rnayabed), Samuel Quiñones (SamuelQuinones)
This program 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.
This program 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.
Written by : Debayan Sutradhar (rnayabed)
*/
package com.StreamPi.Server.Action;
import com.StreamPi.ActionAPI.Action.Action;
import com.StreamPi.ActionAPI.Action.ActionType;
import com.StreamPi.ActionAPI.Action.ServerConnection;
import com.StreamPi.ActionAPI.Action.PropertySaver;
import com.StreamPi.ActionAPI.ActionProperty.Property.Property;
import com.StreamPi.ActionAPI.ActionProperty.Property.Type;
import com.StreamPi.ActionAPI.ActionProperty.ServerProperties;
import com.StreamPi.ActionAPI.NormalAction.NormalAction;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.Exception.SevereException;
import com.StreamPi.Util.Exception.StreamPiException;
import com.StreamPi.Util.Version.Version;
import com.StreamPi.Util.XMLConfigHelper.XMLConfigHelper;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.NodeList;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import java.io.File;
import java.lang.module.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ServiceLoader;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.w3c.dom.Element;
public class NormalActionPlugins
{
private static NormalActionPlugins instance = null;
private final Logger logger;
private File configFile;
private Document document;
private static String pluginsLocation = null;
/**
* Singleton class instance getter. Creates new one, when asked for the first time
*
* @return returns instance of NormalActionPlugins (one and only, always)
*/
public static synchronized NormalActionPlugins getInstance()
{
if(instance == null)
{
instance = new NormalActionPlugins();
}
return instance;
}
/**
* Sets the folder location where the plugin JARs and their dependencies are stored
*
* @param location Folder location
*/
public static void setPluginsLocation(String location)
{
pluginsLocation = location;
}
/**
* Private constructor
*/
private NormalActionPlugins()
{
logger = Logger.getLogger(NormalActionPlugins.class.getName());
normalPluginsHashmap = new HashMap<>();
}
/**
* init Method
*/
public void init() throws SevereException, MinorException
{
registerPlugins();
initPlugins();
}
/**
* Used to fetch list of all external Plugins
*
* @return List of plugins
*/
public List<NormalAction> getPlugins()
{
return normalPlugins;
}
/**
* Returns a plugin by its module name
*
* @param name Module Name
* @return The plugin. If not found, then null is returned
*/
public NormalAction getPluginByModuleName(String name)
{
logger.info("Plugin being requested : "+name);
Integer index = normalPluginsHashmap.getOrDefault(name, -1);
if(index != -1)
{
return normalPlugins.get(index);
}
return null;
}
private List<NormalAction> normalPlugins = null;
HashMap<String, Integer> normalPluginsHashmap;
/**
* Used to register plugins from plugin location
*/
public void registerPlugins() throws SevereException, MinorException
{
logger.info("Registering external plugins from "+pluginsLocation+" ...");
try
{
configFile = new File(pluginsLocation+"/config.xml");
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
document = docBuilder.parse(configFile);
}
catch (Exception e)
{
e.printStackTrace();
throw new SevereException("Plugins","Error reading plugins config.xml. Cannot continue.");
}
ArrayList<NormalAction> errorModules = new ArrayList<>();
ArrayList<String> errorModuleError = new ArrayList<>();
ArrayList<Action> pluginsConfigs = new ArrayList<>();
NodeList actionsNode = document.getElementsByTagName("actions").item(0).getChildNodes();
for(int i =0;i<actionsNode.getLength();i++)
{
Node eachActionNode = actionsNode.item(i);
if(eachActionNode.getNodeType() != Node.ELEMENT_NODE)
continue;
if(!eachActionNode.getNodeName().equals("action"))
continue;
Element eachActionElement = (Element) eachActionNode;
String name;
Version version;
try
{
name = XMLConfigHelper.getStringProperty(eachActionElement, "module-name");
version = new Version(XMLConfigHelper.getStringProperty(eachActionElement, "version"));
}
catch (Exception e)
{
logger.log(Level.WARNING, "Skipping configuration because invalid ...");
e.printStackTrace();
continue;
}
ServerProperties serverProperties = new ServerProperties();
NodeList serverPropertiesNodeList = eachActionElement.getElementsByTagName("properties").item(0).getChildNodes();
for(int j = 0;j<serverPropertiesNodeList.getLength();j++)
{
Node eachPropertyNode = serverPropertiesNodeList.item(j);
if(eachPropertyNode.getNodeType() != Node.ELEMENT_NODE)
continue;
if(!eachPropertyNode.getNodeName().equals("property"))
continue;
Element eachPropertyElement = (Element) eachPropertyNode;
try
{
Property property = new Property(XMLConfigHelper.getStringProperty(eachPropertyElement, "name"), Type.STRING);
property.setRawValue(XMLConfigHelper.getStringProperty(eachPropertyElement, "value"));
serverProperties.addProperty(property);
}
catch (Exception e)
{
logger.log(Level.WARNING, "Skipping property because invalid ...");
e.printStackTrace();
}
}
Action action = new Action(ActionType.NORMAL);
action.setModuleName(name);
action.setVersion(version);
action.getServerProperties().set(serverProperties);
pluginsConfigs.add(action);
}
logger.info("Size : "+pluginsConfigs.size());
Path pluginsDir = Paths.get(pluginsLocation); // Directory with plugins JARs
try
{
// Search for plugins in the plugins directory
ModuleFinder pluginsFinder = ModuleFinder.of(pluginsDir);
// Find all names of all found plugin modules
List<String> p = pluginsFinder
.findAll()
.stream()
.map(ModuleReference::descriptor)
.map(ModuleDescriptor::name)
.collect(Collectors.toList());
// Create configuration that will resolve plugin modules
// (verify that the graph of modules is correct)
Configuration pluginsConfiguration = ModuleLayer
.boot()
.configuration()
.resolve(pluginsFinder, ModuleFinder.of(), p);
// Create a module layer for plugins
ModuleLayer layer = ModuleLayer
.boot()
.defineModulesWithOneLoader(pluginsConfiguration, ClassLoader.getSystemClassLoader());
logger.info("Loading plugins from jar ...");
// Now you can use the new module layer to find service implementations in it
normalPlugins = ServiceLoader
.load(layer, NormalAction.class).stream()
.map(ServiceLoader.Provider::get)
.collect(Collectors.toList());
logger.info("...Done!");
}
catch (Exception e)
{
e.printStackTrace();
throw new MinorException("Error", "Error loading modules\n"+e.getMessage()+"\nPlease fix the errors. Other plugins wont be loaded.");
}
sortedPlugins = new HashMap<>();
for (NormalAction eachPlugin : normalPlugins)
{
try
{
eachPlugin.setPropertySaver(propertySaver);
eachPlugin.setServerConnection(serverConnection);
eachPlugin.initProperties();
Action foundAction = null;
for (Action action : pluginsConfigs) {
if (action.getModuleName().equals(eachPlugin.getModuleName())
&& action.getVersion().isEqual(eachPlugin.getVersion())) {
foundAction = action;
List<Property> eachPluginStoredProperties = action.getServerProperties().get();
List<Property> eachPluginCodeProperties = eachPlugin.getServerProperties().get();
for (int i =0;i< eachPluginCodeProperties.size(); i++) {
Property eachPluginCodeProperty = eachPluginCodeProperties.get(i);
Property foundProp = null;
for (Property eachPluginStoredProperty : eachPluginStoredProperties) {
if (eachPluginCodeProperty.getName().equals(eachPluginStoredProperty.getName())) {
eachPluginCodeProperty.setRawValue(eachPluginStoredProperty.getRawValue());
foundProp = eachPluginStoredProperty;
}
}
eachPluginCodeProperties.set(i, eachPluginCodeProperty);
if (foundProp != null) {
eachPluginStoredProperties.remove(foundProp);
}
}
eachPlugin.getServerProperties().set(eachPluginCodeProperties);
break;
}
}
if (foundAction != null)
pluginsConfigs.remove(foundAction);
else
{
List<Property> eachPluginStoredProperties = eachPlugin.getServerProperties().get();
for(Property property :eachPluginStoredProperties)
{
if(property.getType() == Type.STRING || property.getType() == Type.INTEGER || property.getType() == Type.DOUBLE)
property.setRawValue(property.getDefaultRawValue());
}
}
if (!sortedPlugins.containsKey(eachPlugin.getCategory())) {
ArrayList<NormalAction> actions = new ArrayList<>();
sortedPlugins.put(eachPlugin.getCategory(), actions);
}
sortedPlugins.get(eachPlugin.getCategory()).add(eachPlugin);
}
catch (Exception e)
{
e.printStackTrace();
errorModules.add(eachPlugin);
errorModuleError.add(e.getMessage());
}
}
try {
saveServerSettings();
} catch (MinorException e) {
e.printStackTrace();
}
logger.log(Level.INFO, "All plugins registered!");
if(errorModules.size() > 0)
{
StringBuilder errors = new StringBuilder("The following action modules could not be loaded:");
for(int i = 0; i<errorModules.size(); i++)
{
normalPlugins.remove(errorModules.get(i));
errors.append("\n * ").append(errorModules.get(i).getModuleName()).append("\n(")
.append(errorModuleError.get(i)).append(")");
}
throw new MinorException("Plugins", errors.toString());
}
for(int i = 0;i<normalPlugins.size();i++)
{
normalPluginsHashmap.put(normalPlugins.get(i).getModuleName(), i);
}
}
/**
* Used to init plugins
*/
public void initPlugins() throws MinorException
{
StringBuilder errors = new StringBuilder("There were errors registering the following plugins. As a result, they have been omitted : ");
boolean isError = false;
for(NormalAction eachPlugin : normalPlugins)
{
try
{
eachPlugin.initAction();
}
catch (Exception e)
{
e.printStackTrace();
isError = true;
errors.append("\n* ")
.append(eachPlugin.getName())
.append(" - ")
.append(eachPlugin.getModuleName())
.append("\n");
if(e instanceof StreamPiException)
errors.append(((MinorException) e).getShortMessage());
errors.append("\n");
}
}
if(isError)
{
throw new MinorException("Plugin init error", errors.toString());
}
}
HashMap<String, ArrayList<NormalAction>> sortedPlugins;
/**
* Gets list of sorted plugins
*
* @return Hashmap with category key, and list of plugins of each category
*/
public HashMap<String, ArrayList<NormalAction>> getSortedPlugins()
{
return sortedPlugins;
}
/**
* @return Gets actions element from the config.xml in plugins folder
*/
private Element getActionsElement()
{
return (Element) document.getElementsByTagName("actions").item(0);
}
/**
* Saves ServerProperties of every plugin in config.xml in plugins folder
*
* @throws MinorException Thrown when failed to save settings
*/
public void saveServerSettings() throws MinorException
{
XMLConfigHelper.removeChilds(getActionsElement());
for(NormalAction normalAction : normalPlugins)
{
Element actionElement = document.createElement("action");
getActionsElement().appendChild(actionElement);
Element moduleNameElement = document.createElement("module-name");
moduleNameElement.setTextContent(normalAction.getModuleName());
actionElement.appendChild(moduleNameElement);
Element versionElement = document.createElement("version");
versionElement.setTextContent(normalAction.getVersion().getText());
actionElement.appendChild(versionElement);
Element propertiesElement = document.createElement("properties");
actionElement.appendChild(propertiesElement);
for(String key : normalAction.getServerProperties().getNames())
{
for(Property eachProperty : normalAction.getServerProperties().getMultipleProperties(key))
{
Element propertyElement = document.createElement("property");
propertiesElement.appendChild(propertyElement);
Element nameElement = document.createElement("name");
nameElement.setTextContent(eachProperty.getName());
propertyElement.appendChild(nameElement);
Element valueElement = document.createElement("value");
valueElement.setTextContent(eachProperty.getRawValue());
propertyElement.appendChild(valueElement);
}
}
}
save();
}
private PropertySaver propertySaver = null;
/**
* Set PropertySaver class
* @param propertySaver instance of PropertySaver
*/
public void setPropertySaver(PropertySaver propertySaver)
{
this.propertySaver = propertySaver;
}
private ServerConnection serverConnection = null;
/**
* Set setServerConnection class
* @param serverConnection instance of ServerConnection
*/
public void setServerConnection(ServerConnection serverConnection)
{
this.serverConnection = serverConnection;
}
/**
* Get plugin from index from list
*
* @param index of plugin
* @return found plugin
*/
public NormalAction getActionFromIndex(int index)
{
return normalPlugins.get(index);
}
/**
* Calls onShutDown method in every plugin
*/
public void shutDownActions()
{
if(normalPlugins != null)
{
for(NormalAction eachPlugin : normalPlugins)
{
try
{
eachPlugin.onShutDown();
}
catch (Exception e)
{
e.printStackTrace();
}
}
normalPlugins.clear();
}
}
/**
* Saves all Server Properties of each Plugin in config.xml in Plugins folder
* @throws MinorException thrown when failed to save
*/
public void save() throws MinorException
{
try
{
Transformer transformer = TransformerFactory.newInstance().newTransformer();
Result output = new StreamResult(configFile);
Source input = new DOMSource(document);
transformer.transform(input, output);
}
catch (Exception e)
{
throw new MinorException("Config", "unable to save server plugins settings");
}
}
}
/*
Stream-Pi - Free & Open-Source Modular Cross-Platform Programmable Macropad
Copyright (C) 2019-2021 Debayan Sutradhar (rnayabed), Samuel Quiñones (SamuelQuinones)
This program 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.
This program 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.
Written by : Debayan Sutradhar (rnayabed)
*/
package com.StreamPi.Server.Client;
import com.StreamPi.Util.Platform.Platform;
import com.StreamPi.Util.Platform.ReleaseStatus;
import com.StreamPi.Util.Version.Version;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class Client {
private String nickName;
private final SocketAddress remoteSocketAddress;
private final Platform platform;
private final Version version;
private final Version commStandardVersion;
private final Version themeAPIVersion;
private final ReleaseStatus releaseStatus;
private double startupDisplayHeight, startupDisplayWidth;
private final HashMap<String,ClientProfile> profiles;
private final HashMap<String,ClientTheme> themes;
private String defaultProfileID;
private String defaultThemeFullName;
public Client(Version version, ReleaseStatus releaseStatus, Version commStandardVersion, Version themeAPIVersion, String nickName, Platform platform, SocketAddress remoteSocketAddress)
{
this.version = version;
this.releaseStatus = releaseStatus;
this.commStandardVersion = commStandardVersion;
this.themeAPIVersion = themeAPIVersion;
this.nickName = nickName;
this.remoteSocketAddress = remoteSocketAddress;
this.platform = platform;
this.profiles = new HashMap<>();
this.themes = new HashMap<>();
}
public ReleaseStatus getReleaseStatus() {
return releaseStatus;
}
public void setDefaultThemeFullName(String defaultThemeFullName) {
this.defaultThemeFullName = defaultThemeFullName;
}
public String getDefaultThemeFullName() {
return defaultThemeFullName;
}
public void setDefaultProfileID(String ID)
{
defaultProfileID = ID;
}
public void addTheme(ClientTheme clientTheme) throws CloneNotSupportedException
{
themes.put(clientTheme.getThemeFullName(), (ClientTheme) clientTheme.clone());
}
public ArrayList<ClientTheme> getThemes()
{
ArrayList<ClientTheme> clientThemes = new ArrayList<>();
for(String clientTheme : themes.keySet())
{
clientThemes.add(themes.get(clientTheme));
}
return clientThemes;
}
public ClientTheme getThemeByFullName(String fullName)
{
return themes.getOrDefault(fullName, null);
}
public String getDefaultProfileID()
{
return defaultProfileID;
}
//Client Profiles
public void setNickName(String nickName)
{
this.nickName = nickName;
}
public List<ClientProfile> getAllClientProfiles()
{
ArrayList<ClientProfile> clientProfiles = new ArrayList<>();
for(String profile : profiles.keySet())
clientProfiles.add(profiles.get(profile));
return clientProfiles;
}
public void removeProfileFromID(String ID)
{
profiles.remove(ID);
}
public void addProfile(ClientProfile clientProfile) throws CloneNotSupportedException {
profiles.put(clientProfile.getID(), (ClientProfile) clientProfile.clone());
}
public synchronized ClientProfile getProfileByID(String ID) {
return profiles.getOrDefault(ID, null);
}
public SocketAddress getRemoteSocketAddress()
{
return remoteSocketAddress;
}
public Platform getPlatform()
{
return platform;
}
public String getNickName()
{
return nickName;
}
public Version getVersion()
{
return version;
}
public Version getCommStandardVersion()
{
return commStandardVersion;
}
public Version getThemeAPIVersion()
{
return themeAPIVersion;
}
public double getStartupDisplayHeight()
{
return startupDisplayHeight;
}
public double getStartupDisplayWidth()
{
return startupDisplayWidth;
}
public void setStartupDisplayHeight(double height)
{
startupDisplayHeight = height;
}
public void setStartupDisplayWidth(double width)
{
startupDisplayWidth = width;
}
private int getMaxRows(int eachActionSize)
{
return (int) (startupDisplayHeight / eachActionSize);
}
public int getMaxCols(int eachActionSize)
{
return (int) (startupDisplayWidth / eachActionSize);
}
}
/*
Stream-Pi - Free & Open-Source Modular Cross-Platform Programmable Macropad
Copyright (C) 2019-2021 Debayan Sutradhar (rnayabed), Samuel Quiñones (SamuelQuinones)
This program 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.
This program 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.
Written by : Debayan Sutradhar (rnayabed)
*/
package com.StreamPi.Server.Client;
import com.StreamPi.ActionAPI.Action.Action;
import java.util.HashMap;
import java.util.Set;
import java.util.UUID;
public class ClientProfile implements Cloneable {
private String name, ID;
private int rows, cols, actionSize, actionGap;
private final HashMap<String, Action> actions;
public ClientProfile(String name, String ID, int rows, int cols, int actionSize, int actionGap)
{
this.actions = new HashMap<>();
this.ID = ID;
this.name = name;
this.rows = rows;
this.cols = cols;
this.actionGap = actionGap;
this.actionSize = actionSize;
}
public ClientProfile(String name, int rows, int cols, int actionSize, int actionGap)
{
this(name, UUID.randomUUID().toString(), rows, cols, actionSize, actionGap);
}
public Action getActionByID(String ID)
{
return actions.get(ID);
}
public void removeActionByID(String ID)
{
actions.remove(ID);
}
public Set<String> getActionsKeySet() {
return actions.keySet();
}
public synchronized void addAction(Action action) throws CloneNotSupportedException {
actions.put(action.getID(), action.clone());
}
public String getID()
{
return ID;
}
public String getName()
{
return name;
}
public int getRows()
{
return rows;
}
public int getCols()
{
return cols;
}
public int getActionSize()
{
return actionSize;
}
public int getActionGap()
{
return actionGap;
}
public void setRows(int rows)
{
this.rows = rows;
}
public void setCols(int cols)
{
this.cols = cols;
}
public void setID(String ID)
{
this.ID = ID;
}
public void setActionSize(int actionSize)
{
this.actionSize = actionSize;
}
public void setActionGap(int actionGap)
{
this.actionGap = actionGap;
}
public void setName(String name)
{
this.name = name;
}
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
}
package com.StreamPi.Server.Client;
public class ClientTheme implements Cloneable
{
public String themeFullName;
public String shortName;
public String author;
public String version;
public ClientTheme(String themeFullName, String shortName,
String author, String version)
{
this.themeFullName = themeFullName;
this.shortName = shortName;
this.author = author;
this.version = version;
}
public String getThemeFullName()
{
return themeFullName;
}
public String getShortName()
{
return shortName;
}
public String getAuthor()
{
return author;
}
public String getVersion() {
return version;
}
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
}
package com.StreamPi.Server.Connection;
import com.StreamPi.ActionAPI.Action.Action;
import com.StreamPi.ActionAPI.Action.ActionType;
import com.StreamPi.ActionAPI.Action.DisplayTextAlignment;
import com.StreamPi.ActionAPI.Action.Location;
import com.StreamPi.ActionAPI.ActionProperty.ClientProperties;
import com.StreamPi.ActionAPI.ActionProperty.Property.Property;
import com.StreamPi.ActionAPI.ActionProperty.Property.Type;
import com.StreamPi.ActionAPI.NormalAction.NormalAction;
import com.StreamPi.ActionAPI.OtherActions.CombineAction;
import com.StreamPi.ActionAPI.OtherActions.FolderAction;
import com.StreamPi.Server.Action.NormalActionPlugins;
import com.StreamPi.Server.Client.Client;
import com.StreamPi.Server.Client.ClientProfile;
import com.StreamPi.Server.Client.ClientTheme;
import com.StreamPi.Server.Info.ServerInfo;
import com.StreamPi.Server.Window.ExceptionAndAlertHandler;
import com.StreamPi.Util.Alert.StreamPiAlert;
import com.StreamPi.Util.Alert.StreamPiAlertType;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.Exception.SevereException;
import com.StreamPi.Util.Exception.StreamPiException;
import com.StreamPi.Util.Platform.Platform;
import com.StreamPi.Util.Platform.ReleaseStatus;
import com.StreamPi.Util.Version.Version;
import javafx.concurrent.Task;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
public class ClientConnection extends Thread{
private Socket socket;
private ServerListener serverListener;
private AtomicBoolean stop = new AtomicBoolean(false);
private DataInputStream dis;
private DataOutputStream dos;
private Logger logger;
private Client client = null;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
public ClientConnection(Socket socket, ServerListener serverListener, ExceptionAndAlertHandler exceptionAndAlertHandler)
{
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
//actionIconsToBeSent = new ArrayList__();
this.socket = socket;
this.serverListener = serverListener;
logger = Logger.getLogger(ClientConnection.class.getName());
try
{
dis = new DataInputStream(socket.getInputStream());
dos = new DataOutputStream(socket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
exceptionAndAlertHandler.handleMinorException(new MinorException("Unable to start socket streams"));
}
start();
}
public synchronized void exit()
{
if(stop.get())
return;
logger.info("Stopping ...");
try
{
if(socket !=null)
{
logger.info("Stopping connection "+socket.getRemoteSocketAddress());
disconnect();
}
}
catch (SevereException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleSevereException(e);
}
}
public synchronized void exitAndRemove()
{
exit();
removeConnection();
serverListener.clearTemp();
}
public void removeConnection()
{
ClientConnections.getInstance().removeConnection(this);
}
public void writeToStream(String text) throws SevereException
{
/*try
{
logger.debug(text);
dos.writeUTF(text);
dos.flush();
}
catch (IOException e)
{
e.printStackTrace();
throw new SevereException("Unable to write to IO Stream!");
}*/
try
{
byte[] txtBytes = text.getBytes();
Thread.sleep(50);
dos.writeUTF("string:: ::");
dos.flush();
dos.writeInt(txtBytes.length);
dos.flush();
write(txtBytes);
dos.flush();
}
catch (IOException | InterruptedException e)
{
e.printStackTrace();
throw new SevereException("Unable to write to IO Stream!");
}
}
public void sendIcon(String profileID, String actionID, byte[] icon) throws SevereException
{
try
{
logger.info("Sending action Icon...");
//Thread.sleep(50);
System.out.println("1");
dos.writeUTF("action_icon::"+profileID+"!!"+actionID+"!!::"+icon.length);
System.out.println("2");
dos.flush();
System.out.println("3");
dos.writeInt(icon.length);
System.out.println("4");
dos.flush();
System.out.println("5");
write(icon);
System.out.println("6");
dos.flush();
System.out.println("7");
}
catch (IOException e)
{
e.printStackTrace();
throw new SevereException("Unable to write to IO Stream!");
}
}
public void write(byte[] array) throws SevereException
{
try
{
dos.write(array);
}
catch (IOException e)
{
e.printStackTrace();
throw new SevereException("Unable to write to IO Stream!");
}
}
public void initAfterConnectionQueryReceive(String[] arr) throws StreamPiException
{
logger.info("Setting up Client object ...");
Version clientVersion;
Version commsStandard;
Version themesStandard;
ReleaseStatus releaseStatus;
try
{
clientVersion = new Version(arr[1]);
releaseStatus = ReleaseStatus.valueOf(arr[2]);
commsStandard = new Version(arr[3]);
themesStandard = new Version(arr[4]);
}
catch (MinorException e)
{
exitAndRemove();
throw new MinorException(e.getShortMessage()+" (Client '"+socket.getRemoteSocketAddress()+"' )");
}
if(!commsStandard.isEqual(ServerInfo.getInstance().getCommStandardVersion()))
{
String errTxt = "Server and Client Communication standards do not match. Make sure you are on the latest version of server and client.\n" +
"Server Comms. Standard : "+ServerInfo.getInstance().getCommStandardVersion().getText()+
"\nClient Comms. Standard : "+commsStandard.getText();
disconnect(errTxt);
throw new MinorException(errTxt);
}
client = new Client(clientVersion, releaseStatus, commsStandard, themesStandard, arr[5], Platform.valueOf(arr[8]), socket.getRemoteSocketAddress());
client.setStartupDisplayWidth(Double.parseDouble(arr[6]));
client.setStartupDisplayHeight(Double.parseDouble(arr[7]));
client.setDefaultProfileID(arr[9]);
client.setDefaultThemeFullName(arr[10]);
//call get profiles command.
serverListener.clearTemp();
}
public synchronized Client getClient()
{
return client;
}
@Override
public void run() {
try {
initAfterConnectionQuerySend();
} catch (SevereException e) {
e.printStackTrace();
exceptionAndAlertHandler.handleSevereException(e);
exitAndRemove();
return;
}
try
{
while(!stop.get())
{
String msg = "";
try
{
String raw = dis.readUTF();
int length = dis.readInt();
System.out.println("SIZE TO READ : "+length);
String[] precursor = raw.split("::");
String inputType = precursor[0];
String secondArg = precursor[1];
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
/*int count;
int chunkSize = 512;
while (length>0)
{
if(chunkSize > length)
chunkSize = length;
else
chunkSize = 512;
byte[] buffer = new byte[chunkSize];
count = dis.read(buffer);
System.out.println(count);
byteArrayOutputStream.write(buffer);
length-=count;
}*/
/*byte[] buffer = new byte[8192];
int read;
while((read = dis.read(buffer)) != -1){
System.out.println("READ : "+read);
byteArrayOutputStream.write(buffer, 0, read);
}
System.out.println("READ : "+byteArrayOutputStream.size());
byteArrayOutputStream.close();
byte[] bArr = byteArrayOutputStream.toByteArray();*/
byte[] bArr = new byte[length];
dis.readFully(bArr);
if(inputType.equals("string"))
{
msg = new String(bArr);
}
else if(inputType.equals("action_icon"))
{
String[] secondArgSep = secondArg.split("!!");
String profileID = secondArgSep[0];
String actionID = secondArgSep[1];
getClient().getProfileByID(profileID).getActionByID(actionID).setIcon(bArr);
//serverListener.clearTemp();
continue;
}
}
catch (IOException e)
{
logger.log(Level.SEVERE, e.getMessage());
e.printStackTrace();
serverListener.clearTemp();
if(!stop.get())
{
removeConnection();
throw new MinorException("Accidentally disconnected from "+getClient().getNickName()+".");
}
exitAndRemove();
return;
}
logger.info("Received text : '"+msg+"'");
String[] sep = msg.split("::");
String command = sep[0];
switch (command)
{
case "disconnect" : clientDisconnected(msg);
break;
case "client_details" : initAfterConnectionQueryReceive(sep);
getProfilesFromClient();
getThemesFromClient();
break;
case "profiles" : registerProfilesFromClient(sep);
break;
case "profile_details" : registerProfileDetailsFromClient(sep);
break;
case "action_details" : registerActionToProfile(sep);
break;
case "themes": registerThemes(sep);
break;
case "action_clicked": actionClicked(sep[1], sep[2]);
break;
default: logger.warning("Command '"+command+"' does not match records. Make sure client and server versions are equal.");
}
}
}
catch (StreamPiException e)
{
e.printStackTrace();
if(e instanceof MinorException)
exceptionAndAlertHandler.handleMinorException((MinorException) e);
else if (e instanceof SevereException)
exceptionAndAlertHandler.handleSevereException((SevereException) e);
}
}
// commands
public void initAfterConnectionQuerySend() throws SevereException
{
logger.info("Asking client details ...");
writeToStream("get_client_details::");
}
public void disconnect() throws SevereException {
disconnect("");
}
public void disconnect(String message) throws SevereException {
if(stop.get())
return;
stop.set(true);
logger.info("Sending client disconnect message ...");
writeToStream("disconnect::"+message+"::");
try
{
if(!socket.isClosed())
socket.close();
}
catch (IOException e)
{
e.printStackTrace();
throw new SevereException("Unable to close socket");
}
}
public void clientDisconnected(String message)
{
stop.set(true);
String txt = "Disconnected!";
if(!message.equals("disconnect::::"))
txt = "Message : "+message.split("::")[1];
new StreamPiAlert("Disconnected from "+getClient().getNickName()+".", txt, StreamPiAlertType.WARNING).show();;
exitAndRemove();
}
public void getProfilesFromClient() throws StreamPiException
{
logger.info("Asking client to send profiles ...");
writeToStream("get_profiles::");
}
public void getThemesFromClient() throws StreamPiException
{
logger.info("Asking clients to send themes ...");
writeToStream("get_themes::");
}
public void registerThemes(String[] sep)
{
for(int i =1; i<sep.length;i++)
{
String[] internal = sep[i].split("__");
ClientTheme clientTheme = new ClientTheme(
internal[0],
internal[1],
internal[2],
internal[3]
);
try
{
getClient().addTheme(clientTheme);
}
catch (CloneNotSupportedException e)
{
logger.log(Level.SEVERE, e.getMessage());
e.printStackTrace();
}
}
}
public void registerProfilesFromClient(String[] sep) throws StreamPiException
{
logger.info("Registering profiles ...");
int noOfProfiles = Integer.parseInt(sep[1]);
for(int i = 2; i<(noOfProfiles + 2); i++)
{
String profileID = sep[i];
getProfileDetailsFromClient(profileID);
}
}
public void getProfileDetailsFromClient(String ID) throws StreamPiException
{
logger.info("Asking client to send details of profile : "+ID);
writeToStream("get_profile_details::"+ID+"::");
}
public void registerProfileDetailsFromClient(String[] sep)
{
String ID = sep[1];
logger.info("Registering details for profile : "+ID);
String name = sep[2];
int rows = Integer.parseInt(sep[3]);
int cols = Integer.parseInt(sep[4]);
int actionSize = Integer.parseInt(sep[5]);
int actionGap = Integer.parseInt(sep[6]);
ClientProfile clientProfile = new ClientProfile(name, ID, rows, cols, actionSize, actionGap);
logger.info("Added client profile "+clientProfile.getName());
try
{
getClient().addProfile(clientProfile);
}
catch (CloneNotSupportedException e)
{
logger.severe(e.getMessage());
e.printStackTrace();
}
serverListener.clearTemp();
}
/*public void getActionIcon(String profileID, String actionID) throws StreamPiException
{
System.out.println("getting action icon from "+profileID+", "+actionID);
writeToStream("get_action_icon::"+profileID+"::"+actionID);
}*/
public synchronized void registerActionToProfile(String[] sep) throws StreamPiException
{
String profileID = sep[1];
String ID = sep[2];
ActionType actionType = ActionType.valueOf(sep[3]);
//4 - Version
//5 - ModuleName
//display
String bgColorHex = sep[6];
//icon
boolean isHasIcon = sep[7].equals("true");
boolean isShowIcon = sep[8].equals("true");
//text
boolean isShowDisplayText = sep[9].equals("true");
String displayFontColor = sep[10];
String displayText = sep[11];
DisplayTextAlignment displayTextAlignment = DisplayTextAlignment.valueOf(sep[12]);
//location
String row = sep[13];
String col = sep[14];
Location location = new Location(Integer.parseInt(row), Integer.parseInt(col));
Action action = new Action(ID, actionType);
action.setBgColourHex(bgColorHex);
action.setShowIcon(isShowIcon);
action.setHasIcon(isHasIcon);
action.setShowDisplayText(isShowDisplayText);
action.setDisplayTextFontColourHex(displayFontColor);
action.setDisplayText(displayText);
action.setDisplayTextAlignment(displayTextAlignment);
action.setLocation(location);
//client properties
int clientPropertiesSize = Integer.parseInt(sep[15]);
String root = sep[17];
action.setParent(root);
String[] clientPropertiesRaw = sep[16].split("!!");
ClientProperties clientProperties = new ClientProperties();
if(actionType == ActionType.FOLDER)
clientProperties.setDuplicatePropertyAllowed(true);
for(int i = 0;i<clientPropertiesSize; i++)
{
String[] clientPraw = clientPropertiesRaw[i].split("__");
Property property = new Property(clientPraw[0], Type.STRING);
if(clientPraw.length > 1)
property.setRawValue(clientPraw[1]);
clientProperties.addProperty(property);
}
action.setClientProperties(clientProperties);
action.setModuleName(sep[5]);
//set up action
//Action toBeAdded = null;
if(actionType == ActionType.NORMAL)
{
NormalAction actionCopy = NormalActionPlugins.getInstance().getPluginByModuleName(sep[5]);
if(actionCopy == null)
{
action.setInvalid(true);
}
else
{
action.setVersion(new Version(sep[4]));
//action.setHelpLink(actionCopy.getHelpLink());
if(actionCopy.getVersion().getMajor() != action.getVersion().getMajor())
{
action.setInvalid(true);
}
else
{
action.setName(actionCopy.getName());
ClientProperties finalClientProperties = new ClientProperties();
for(Property property : actionCopy.getClientProperties().get())
{
for(int i = 0;i<action.getClientProperties().getSize();i++)
{
Property property1 = action.getClientProperties().get().get(i);
if (property.getName().equals(property1.getName()))
{
property.setRawValue(property1.getRawValue());
finalClientProperties.addProperty(property);
}
}
}
action.setClientProperties(finalClientProperties);
}
}
}
try
{
for(Property prop : action.getClientProperties().get())
{
logger.info("G@@@@@ : "+prop.getRawValue());
}
getClient().getProfileByID(profileID).addAction(action);
for(String action1x : getClient().getProfileByID(profileID).getActionsKeySet())
{
Action action1 = getClient().getProfileByID(profileID).getActionByID(action1x);
logger.info("231cc : "+action1.getID());
for(Property prop : action1.getClientProperties().get())
{
logger.info("G@VVVV@@@ : "+prop.getRawValue());
}
}
}
catch (CloneNotSupportedException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleMinorException(new MinorException("Action", "Unable to clone"));
}
}
public void saveActionDetails(String profileID, Action action) throws SevereException
{
StringBuilder finalQuery = new StringBuilder("save_action_details::");
//failsafes
if(action.getDisplayText().endsWith(":"))
action.setDisplayText(action.getDisplayText()+" ");
finalQuery.append(profileID)
.append("::")
.append(action.getID())
.append("::")
.append(action.getActionType())
.append("::");
if(action.getActionType() == ActionType.NORMAL)
{
finalQuery.append(action.getVersion().getText());
System.out.println("VERSION :sdd "+action.getVersion().getText());
}
finalQuery.append("::");
if(action.getActionType() == ActionType.NORMAL)
finalQuery.append(action.getModuleName());
finalQuery.append("::");
//display
finalQuery.append(action.getBgColourHex())
.append("::");
//icon
finalQuery.append(action.isHasIcon())
.append("::")
.append(action.isShowIcon())
.append("::");
//text
finalQuery.append(action.isShowDisplayText())
.append("::")
.append(action.getDisplayTextFontColourHex())
.append("::")
.append(action.getDisplayText())
.append("::")
.append(action.getDisplayTextAlignment())
.append("::");
//location
if(action.getLocation() == null)
finalQuery.append("-1::-1::");
else
finalQuery.append(action.getLocation().getRow())
.append("::")
.append(action.getLocation().getCol())
.append("::");
//client properties
ClientProperties clientProperties = action.getClientProperties();
finalQuery.append(clientProperties.getSize())
.append("::");
for(Property property : clientProperties.get())
{
finalQuery.append(property.getName())
.append("__")
.append(property.getRawValue())
.append("__");
finalQuery.append("!!");
}
finalQuery.append("::")
.append(action.getParent())
.append("::");
writeToStream(finalQuery.toString());
}
public void deleteAction(String profileID, String actionID) throws SevereException
{
writeToStream("delete_action::"+profileID+"::"+actionID);
}
public void saveClientDetails(String clientNickname, String screenWidth, String screenHeight, String defaultProfileID,
String defaultThemeFullName) throws SevereException
{
writeToStream("save_client_details::"+
clientNickname+"::"+
screenWidth+"::"+
screenHeight+"::"+
defaultProfileID+"::"+
defaultThemeFullName+"::");
client.setNickName(clientNickname);
client.setStartupDisplayWidth(Double.parseDouble(screenWidth));
client.setStartupDisplayHeight(Double.parseDouble(screenHeight));
client.setDefaultProfileID(defaultProfileID);
client.setDefaultThemeFullName(defaultThemeFullName);
}
public void saveProfileDetails(ClientProfile clientProfile) throws SevereException, CloneNotSupportedException {
if(client.getProfileByID(clientProfile.getID()) !=null)
{
client.getProfileByID(clientProfile.getID()).setName(clientProfile.getName());
client.getProfileByID(clientProfile.getID()).setRows(clientProfile.getRows());
client.getProfileByID(clientProfile.getID()).setCols(clientProfile.getCols());
client.getProfileByID(clientProfile.getID()).setActionSize(clientProfile.getActionSize());
client.getProfileByID(clientProfile.getID()).setActionGap(clientProfile.getActionGap());
}
else
client.addProfile(clientProfile);
writeToStream("save_client_profile::"+
clientProfile.getID()+"::"+
clientProfile.getName()+"::"+
clientProfile.getRows()+"::"+
clientProfile.getCols()+"::"+
clientProfile.getActionSize()+"::"+
clientProfile.getActionGap()+"::");
}
public void deleteProfile(String ID) throws SevereException
{
writeToStream("delete_profile::"+ID+"::");
}
public void actionClicked(String profileID, String actionID) {
try
{
Action action = client.getProfileByID(profileID).getActionByID(actionID);
if(action.getActionType() == ActionType.NORMAL)
{
NormalAction original = NormalActionPlugins.getInstance().getPluginByModuleName(
action.getModuleName()
);
if(original == null)
{
throw new MinorException(
"The Action isn't installed on the server."
);
}
NormalAction normalAction = original.clone();
normalAction.setLocation(action.getLocation());
normalAction.setDisplayText(action.getDisplayText());
normalAction.setID(actionID);
normalAction.setClientProperties(action.getClientProperties());
new Thread(new Task<Void>() {
@Override
protected Void call()
{
try
{
boolean result = serverListener.onNormalActionClicked(normalAction);
if(!result)
{
sendActionFailed(profileID, actionID);
}
}
catch (SevereException e)
{
exceptionAndAlertHandler.handleSevereException(e);
}
return null;
}
}).start();
}
} catch (Exception e) {
e.printStackTrace();
exceptionAndAlertHandler.handleMinorException(new MinorException(e.getMessage()));
}
}
public void sendActionFailed(String profileID, String actionID) throws SevereException {
logger.info("Sending failed status ...");
writeToStream("action_failed::"+
profileID+"::"+
actionID+"::");
}
}
package com.StreamPi.Server.Connection;
import com.StreamPi.Server.Client.Client;
import java.net.SocketAddress;
import java.util.ArrayList;
public class ClientConnections {
private ArrayList<ClientConnection> connections;
private static ClientConnections instance = null;
private ClientConnections()
{
connections = new ArrayList<>();
}
public static synchronized ClientConnections getInstance()
{
if(instance == null)
{
instance = new ClientConnections();
}
return instance;
}
public ArrayList<ClientConnection> getConnections()
{
return connections;
}
public void clearAllConnections()
{
connections.clear(); // NOT RECOMMENDED TO USE CARELESSLY
}
public void addConnection(ClientConnection connection)
{
connections.add(connection);
}
public void removeConnection(ClientConnection clientConnection)
{
System.out.println(connections.remove(clientConnection)+" 22222222222222222222222222222222222222222");
}
public void disconnectAll()
{
new Thread(()->{
for(ClientConnection clientConnection : connections)
{
clientConnection.exit();
}
clearAllConnections();
}).start();
}
public ClientConnection getClientConnectionBySocketAddress(SocketAddress socketAddress)
{
for(ClientConnection clientConnection : connections)
{
if(clientConnection.getClient().getRemoteSocketAddress().equals(socketAddress))
return clientConnection;
}
return null;
}
}
/*package com.StreamPi.Server.Connection;
import com.StreamPi.ActionAPI.Action.Action;
import com.StreamPi.ActionAPI.Action.ActionType;
import com.StreamPi.ActionAPI.Action.Location;
import com.StreamPi.ActionAPI.ActionProperty.ClientProperties;
import com.StreamPi.ActionAPI.NormalAction.NormalAction;
import com.StreamPi.CommAPI.ConnectionGrpc;
import com.StreamPi.CommAPI.ServerGRPC;
import com.StreamPi.Server.Action.NormalActionPlugins;
import com.StreamPi.Server.Client.Client;
import com.StreamPi.Server.Client.ClientProfile;
import com.StreamPi.Server.Info.ServerInfo;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.Exception.SevereException;
import com.StreamPi.Util.Exception.StreamPiException;
import com.StreamPi.Util.Platform.Platform;
import com.StreamPi.Util.Version.Version;
import io.grpc.stub.StreamObserver;
import javafx.concurrent.Task;
import javafx.scene.control.Alert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class ConnectionService extends ConnectionGrpc.ConnectionImplBase {
private Client client = null;
private Logger logger;
ServerListener serverListener;
final HashMap<String, Boolean> actionStatuses;
public ConnectionService(ServerListener serverListener)
{
super();
isDisconnect = false;
disconnectMessage = "";
logger = LoggerFactory.getLogger(ConnectionService.class);
actionStatuses = new HashMap<>();
this.serverListener = serverListener;
}
boolean isDisconnect;
String disconnectMessage;
public void disconnect()
{
disconnect("");
}
public void disconnect(String message)
{
if(!isDisconnect)
{
this.isDisconnect = true;
this.disconnectMessage = message;
}
}
@Override
public void sendClientDetails(ServerGRPC.ClientDetails request, StreamObserver<ServerGRPC.Status> responseObserver) {
Version clientVersion;
Version commsAPIVersion;
Version themeAPIVersion;
try
{
clientVersion = new Version(request.getClientVersion());
commsAPIVersion = new Version(request.getCommAPIVersion());
themeAPIVersion = new Version(request.getThemeAPIVersion());
}
catch (MinorException e)
{
e.printStackTrace();
serverListener.onRPCError(new MinorException("versions invalid. Check stacktrace"));
disconnect();
return;
}
String nickName = request.getNickName();
Platform platform = Platform.valueOf(request.getPlatform().toString());
client = new Client(clientVersion, commsAPIVersion, themeAPIVersion, nickName, platform, null);
boolean sendActions = true;
if(!commsAPIVersion.isEqual(ServerInfo.getInstance().getCommAPIVersion()))
{
sendActions = false;
disconnect("Client CommAPI and Server CommAPI do not match!");
}
responseObserver.onNext(ServerGRPC.Status.newBuilder().setSendActions(sendActions).build());
responseObserver.onCompleted();
}
@Override
public StreamObserver<ServerGRPC.ClientProfile> sendClientProfiles(StreamObserver<ServerGRPC.Empty> responseObserver) {
client.getProfiles().clear();
ArrayList<String> notFoundActions = new ArrayList<>();
return new StreamObserver<ServerGRPC.ClientProfile>() {
@Override
public void onNext(ServerGRPC.ClientProfile clientProfile) {
String name = clientProfile.getName();
String id = clientProfile.getId();
int rows = clientProfile.getRows();
int cols = clientProfile.getCols();
int actionSize = clientProfile.getActionSize();
int actionGap = clientProfile.getActionGap();
ClientProfile finalClientProfile = new ClientProfile(name, id, rows, cols, actionSize, actionGap);
ArrayList<Action> actions = new ArrayList<>();
List<ServerGRPC.ClientAction> clientActions = clientProfile.getActionsList();
for(ServerGRPC.ClientAction clientAction : clientActions)
{
String actionID = clientAction.getId();
String actionName = clientAction.getActionName();
boolean hasIcon = clientAction.getHasIcon();
ActionType actionType = ActionType.valueOf(clientAction.getActionType().toString());
Action action = new Action(actionID, actionType);
action.setActionName(actionName);
action.setHasIcon(hasIcon);
int locationX = clientAction.getLocationX();
int locationY = clientAction.getLocationY();
action.setLocation(new Location(locationX, locationY));
if(actionType == ActionType.NORMAL)
{
action.setModuleName(clientAction.getModuleName());
ClientProperties properties = new ClientProperties();
List<ServerGRPC.ClientProperty> clientProperties = clientAction.getClientPropertiesList();
for(ServerGRPC.ClientProperty clientProperty : clientProperties)
{
String propertyName = clientProperty.getName();
String propertyValue = clientProperty.getValue();
properties.addProperty(propertyName, propertyValue);
}
action.setClientProperties(properties);
boolean isFound = false;
for(NormalAction normalAction : NormalActionPlugins.getInstance().getPlugins())
{
if(normalAction.getModuleName().equals(action.getModuleName()))
{
isFound = true;
normalAction.setClientProperties(action.getClientProperties());
normalAction.setActionName(action.getActionName());
normalAction.setHasIcon(action.isHasIcon());
normalAction.setID(action.getID());
normalAction.setLocation(action.getLocation());
normalAction.setInvalid(false);
actions.add(normalAction);
break;
}
}
if(!isFound)
{
String aName = action.getModuleName();
action.setInvalid(true);
logger.warn("Action "+aName+" not found!");
if(!notFoundActions.contains(aName))
notFoundActions.add(aName);
actions.add(action);
}
}
}
finalClientProfile.setActions(actions);
client.addProfile(finalClientProfile);
}
@Override
public void onError(Throwable throwable) {
serverListener.onRPCError(new SevereException(throwable.getMessage()));
}
@Override
public void onCompleted() {
if(!notFoundActions.isEmpty())
{
StringBuilder all = new StringBuilder("Some actions cannot be edited/used because they are not installed on the server : ");
for(String each : notFoundActions)
{
all.append("\n * ").append(each);
}
serverListener.onAlert("Warning",all.toString(), Alert.AlertType.WARNING);
}
responseObserver.onNext(ServerGRPC.Empty.newBuilder().build());
responseObserver.onCompleted();
}
};
}
@Override
public void actionClicked(ServerGRPC.ClickedActionID request, StreamObserver<ServerGRPC.Empty> responseObserver) {
try
{
Action actionClicked = client.getProfileByID(request.getProfileID()).getActionByID(request.getId());
new Thread(new Task<Void>() {
@Override
protected Void call()
{
try
{
synchronized (actionStatuses)
{
actionStatuses.put(request.getId(), serverListener.onActionClicked(actionClicked));
}
}
catch (MinorException e)
{
serverListener.onRPCError(e);
}
return null;
}
}).start();
}
catch (MinorException e)
{
e.printStackTrace();
}
finally {
responseObserver.onNext(ServerGRPC.Empty.newBuilder().build());
responseObserver.onCompleted();
}
}
@Override
public StreamObserver<ServerGRPC.Empty> actionClickedStatus(StreamObserver<ServerGRPC.ActionStatus> responseObserver) {
return new StreamObserver<ServerGRPC.Empty>() {
@Override
public void onNext(ServerGRPC.Empty empty) {
}
@Override
public void onError(Throwable throwable) {
serverListener.onRPCError(new StreamPiException(throwable.getMessage()));
}
@Override
public void onCompleted() {
String id = null;
boolean success = false;
synchronized (actionStatuses)
{
if(!actionStatuses.isEmpty())
{
for(String key : actionStatuses.keySet())
{
id = key;
success = actionStatuses.get(id);
System.out.println("SDSDSDASDASDXZXCZXCZXC");
break;
}
}
}
if(id==null)
{
responseObserver.onNext(ServerGRPC.ActionStatus.newBuilder().build());
}
else
responseObserver.onNext(ServerGRPC.ActionStatus.newBuilder()
.setId(id)
.setIsSuccess(success)
.build());
responseObserver.onCompleted();
}
};
}
@Override
public StreamObserver<ServerGRPC.DisconnectMessage> disconnect(StreamObserver<ServerGRPC.DisconnectMessage> responseObserver) {
return new StreamObserver<ServerGRPC.DisconnectMessage>() {
@Override
public void onNext(ServerGRPC.DisconnectMessage disconnectMessage) {
if(disconnectMessage.getIsDisconnect())
disconnect(disconnectMessage.getMessage());
}
@Override
public void onError(Throwable throwable) {
serverListener.onRPCError(new StreamPiException(throwable.getMessage()));
}
@Override
public void onCompleted() {
responseObserver.onNext(ServerGRPC.DisconnectMessage.newBuilder()
.setIsDisconnect(isDisconnect)
.setMessage(disconnectMessage)
.build());
responseObserver.onCompleted();
}
};
}
}
*/
package com.StreamPi.Server.Connection;
import com.StreamPi.Server.Window.ExceptionAndAlertHandler;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.Exception.SevereException;
import javafx.concurrent.Task;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
public class MainServer extends Thread{
private ServerListener serverListener;
private Logger logger = Logger.getLogger(MainServer.class.getName());
private int port;
private ServerSocket serverSocket = null;
//private Server server;
private AtomicBoolean stop = new AtomicBoolean(false);
private ExceptionAndAlertHandler exceptionAndAlertHandler;
public MainServer(ServerListener serverListener, ExceptionAndAlertHandler exceptionAndAlertHandler)
{
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
this.port = port;
this.serverListener = serverListener;
}
public void setPort(int port) {
this.port = port;
}
@Override
public synchronized void start() {
stop.set(false);
super.start();
}
public void stopListeningForConnections()
{
/*if(server !=null)
{
if(!server.isShutdown())
server.shutdown();
}*/
try
{
logger.info("Stopping listening for connections ...");
if(serverSocket!=null)
if(!serverSocket.isClosed())
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
finally {
logger.info("... Done!");
}
stop.set(true);
}
@Override
public void run() {
logger.warning("Starting main server on port "+port+" ...");
try {
logger.info("Starting server on port "+port+" ...");
/*Server server = ServerBuilder.forPort(port)
.addService(new ConnectionService(serverListener))
.build();
server.start();
logger.info("... Done!");*/
serverSocket = new ServerSocket(port);
while(!stop.get())
{
Socket s = serverSocket.accept();
ClientConnections.getInstance().addConnection(new ClientConnection(s, serverListener, exceptionAndAlertHandler));
logger.info("New Client connected ("+s.getRemoteSocketAddress()+") !");
}
}
catch (SocketException e)
{
logger.info("Main Server stopped accepting calls ...");
e.printStackTrace(); //more likely stopped listening;
} catch (IOException e) {
exceptionAndAlertHandler.handleSevereException(new SevereException("MainServer IO Exception occurred!"));
e.printStackTrace();
}
}
}
package com.StreamPi.Server.Connection;
import com.StreamPi.ActionAPI.Action.Action;
import com.StreamPi.ActionAPI.NormalAction.NormalAction;
import com.StreamPi.Server.Client.Client;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.Exception.SevereException;
import com.StreamPi.Util.Exception.StreamPiException;
import javafx.scene.control.Alert;
import java.net.Socket;
public interface ServerListener {
boolean onNormalActionClicked(NormalAction action);
void clearTemp();
void init();
void othInit();
}
package com.StreamPi.Server.Controller;
import com.StreamPi.ActionAPI.Action.ServerConnection;
import com.StreamPi.ActionAPI.Action.PropertySaver;
import com.StreamPi.ActionAPI.NormalAction.NormalAction;
import com.StreamPi.Server.Main;
import com.StreamPi.Server.Action.NormalActionPlugins;
import com.StreamPi.Server.Connection.ClientConnections;
import com.StreamPi.Server.Connection.MainServer;
import com.StreamPi.Server.IO.Config;
import com.StreamPi.Server.Info.ServerInfo;
import com.StreamPi.Server.Window.Base;
import com.StreamPi.Server.Window.Dashboard.DonatePopupContent;
import com.StreamPi.Server.Window.FirstTimeUse.FirstTimeUse;
import com.StreamPi.Util.Alert.StreamPiAlert;
import com.StreamPi.Util.Alert.StreamPiAlertListener;
import com.StreamPi.Util.Alert.StreamPiAlertType;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.Exception.SevereException;
import com.StreamPi.Util.IOHelper.IOHelper;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.awt.SystemTray;
import javafx.util.Duration;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.PopupMenu;
import java.awt.MenuItem;
import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Random;
import java.util.logging.Level;
public class Controller extends Base implements PropertySaver, ServerConnection
{
MainServer mainServer;
final SystemTray systemTray;
public void setupDashWindow() throws SevereException
{
try
{
getStage().setTitle("Stream-Pi Server - "+InetAddress.getLocalHost().getCanonicalHostName()+":"+Config.getInstance().getPort()); //Sets title
getStage().setOnCloseRequest(this::onCloseRequest);
}
catch (UnknownHostException e)
{
e.printStackTrace();
throw new SevereException(e.getMessage());
}
}
private void checkPrePathDirectory() throws SevereException
{
try {
File filex = new File(ServerInfo.getInstance().getPrePath());
System.out.println("SAX : "+filex.exists());
if(!filex.exists())
{
filex.mkdirs();
IOHelper.unzip(Main.class.getResourceAsStream("Default.obj"), ServerInfo.getInstance().getPrePath());
}
}
catch (Exception e)
{
e.printStackTrace();
throw new SevereException(e.getMessage());
}
}
@Override
public void init()
{
try {
checkPrePathDirectory();
initBase();
setupDashWindow();
setupSettingsWindowsAnimations();
NormalActionPlugins.getInstance().setPropertySaver(this);
NormalActionPlugins.getInstance().setServerConnection(this);
getDashboardPane().getPluginsPane().getSettingsButton().setOnAction(event -> {
openSettingsTimeLine.play();
});
getSettingsPane().getCloseButton().setOnAction(event -> {
closeSettingsTimeLine.play();
});
getSettingsPane().getThemesSettings().setController(this);
mainServer = new MainServer(this, this);
if(getConfig().isFirstTimeUse())
{
Stage stage = new Stage();
Scene s = new Scene(new FirstTimeUse(this, this), 512, 300);
stage.setResizable(false);
stage.setScene(s);
stage.setTitle("Stream-Pi Server Setup");
stage.initModality(Modality.APPLICATION_MODAL);
stage.setOnCloseRequest(event->Platform.exit());
stage.show();
}
else
{
if(getConfig().isAllowDonatePopup())
{
if(new Random().nextInt(5) == 3)
new DonatePopupContent(getHostServices(), this).show();
}
othInit();
}
}
catch (SevereException e)
{
handleSevereException(e);
}
}
@Override
public void othInit()
{
try
{
if(ServerInfo.getInstance().isStartMinimised())
minimiseApp();
else
getStage().show();
}
catch(MinorException e)
{
handleMinorException(e);
}
new Thread(new Task<Void>() {
@Override
protected Void call()
{
try
{
getSettingsPane().getGeneralSettings().loadDataFromConfig();
//themes
getSettingsPane().getThemesSettings().setThemes(getThemes());
getSettingsPane().getThemesSettings().setCurrentThemeFullName(getCurrentTheme().getFullName());
getSettingsPane().getThemesSettings().loadThemes();
//clients
getSettingsPane().getClientsSettings().loadData();
try
{
//Plugins
Platform.runLater(()->{
getDashboardPane().getPluginsPane().clearData();
getDashboardPane().getPluginsPane().loadOtherActions();
});
NormalActionPlugins.setPluginsLocation(getConfig().getPluginsPath());
NormalActionPlugins.getInstance().init();
Platform.runLater(()->getDashboardPane().getPluginsPane().loadData());
getSettingsPane().getPluginsSettings().loadPlugins();
}
catch (MinorException e)
{
getSettingsPane().getPluginsSettings().showPluginInitError();
handleMinorException(e);
}
//Server
mainServer.setPort(getConfig().getPort());
mainServer.start();
}
catch (SevereException e)
{
handleSevereException(e);
}
return null;
}
}).start();
}
private void setupSettingsWindowsAnimations()
{
Node settingsNode = getSettingsPane();
Node dashboardNode = getDashboardPane();
openSettingsTimeLine = new Timeline();
openSettingsTimeLine.setCycleCount(1);
openSettingsTimeLine.getKeyFrames().addAll(
new KeyFrame(Duration.millis(0.0D),
new KeyValue(settingsNode.opacityProperty(),
0.0D, Interpolator.EASE_IN),
new KeyValue(settingsNode.scaleXProperty(),
1.1D, Interpolator.EASE_IN),
new KeyValue(settingsNode.scaleYProperty(),
1.1D, Interpolator.EASE_IN),
new KeyValue(settingsNode.scaleZProperty(),
1.1D, Interpolator.EASE_IN)),
new KeyFrame(Duration.millis(90.0D),
new KeyValue(settingsNode.opacityProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleXProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleYProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleZProperty(),
1.0D, Interpolator.LINEAR)),
new KeyFrame(Duration.millis(0.0D),
new KeyValue(dashboardNode.opacityProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleXProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleYProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleZProperty(),
1.0D, Interpolator.LINEAR)),
new KeyFrame(Duration.millis(90.0D),
new KeyValue(dashboardNode.opacityProperty(),
0.0D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleXProperty(),
0.9D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleYProperty(),
0.9D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleZProperty(),
0.9D, Interpolator.LINEAR))
);
openSettingsTimeLine.setOnFinished(event1 -> {
settingsNode.toFront();
});
closeSettingsTimeLine = new Timeline();
closeSettingsTimeLine.setCycleCount(1);
closeSettingsTimeLine.getKeyFrames().addAll(
new KeyFrame(Duration.millis(0.0D),
new KeyValue(settingsNode.opacityProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleXProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleYProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleZProperty(),
1.0D, Interpolator.LINEAR)),
new KeyFrame(Duration.millis(90.0D),
new KeyValue(settingsNode.opacityProperty(),
0.0D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleXProperty(),
1.1D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleYProperty(),
1.1D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleZProperty(),
1.1D, Interpolator.LINEAR)),
new KeyFrame(Duration.millis(0.0D),
new KeyValue(dashboardNode.opacityProperty(),
0.0D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleXProperty(),
0.9D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleYProperty(),
0.9D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleZProperty(),
0.9D, Interpolator.LINEAR)),
new KeyFrame(Duration.millis(90.0D),
new KeyValue(dashboardNode.opacityProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleXProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleYProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleZProperty(),
1.0D, Interpolator.LINEAR))
);
closeSettingsTimeLine.setOnFinished(event1 -> {
dashboardNode.toFront();
new Thread(new Task<Void>() {
@Override
protected Void call() {
try {
getSettingsPane().getClientsSettings().loadData();
getSettingsPane().getGeneralSettings().loadDataFromConfig();
getSettingsPane().getPluginsSettings().loadPlugins();
getSettingsPane().getThemesSettings().setThemes(getThemes());
getSettingsPane().getThemesSettings().setCurrentThemeFullName(getCurrentTheme().getFullName());
getSettingsPane().getThemesSettings().loadThemes();
getSettingsPane().setDefaultTabToGeneral();
}
catch (SevereException e)
{
handleSevereException(e);
}
catch (MinorException e)
{
handleMinorException(e);
}
return null;
}
}).start();
});
}
private Timeline openSettingsTimeLine;
private Timeline closeSettingsTimeLine;
public Controller(){
systemTray = SystemTray.getSystemTray();
mainServer = null;
}
public void onCloseRequest(WindowEvent event)
{
try
{
if(Config.getInstance().getCloseOnX())
{
getConfig().setStartupWindowSize(
getWidth(),
getHeight()
);
getConfig().save();
onQuitApp();
NormalActionPlugins.getInstance().shutDownActions();
Platform.exit();
}
else
{
minimiseApp();
event.consume();
}
}
catch (SevereException e)
{
handleSevereException(e);
}
catch (MinorException e)
{
handleMinorException(e);
}
finally
{
closeLogger();
}
}
public void onQuitApp()
{
if(mainServer!=null)
mainServer.stopListeningForConnections();
ClientConnections.getInstance().disconnectAll();
getLogger().info("Shutting down ...");
}
public void minimiseApp() throws MinorException
{
try
{
if(SystemTray.isSupported())
{
if(getTrayIcon() == null)
initIconTray();
systemTray.add(getTrayIcon());
//getStage().setIconified(true);
getStage().hide();
}
else
{
new StreamPiAlert("System Tray Error", "Your System does not support System Tray", StreamPiAlertType.ERROR).show();
}
}
catch(Exception e)
{
throw new MinorException(e.getMessage());
}
}
public void initIconTray()
{
Platform.setImplicitExit(false);
PopupMenu popup = new PopupMenu();
MenuItem showItem = new MenuItem("Show");
showItem.addActionListener(l->{
systemTray.remove(getTrayIcon());
Platform.runLater(()->{
//getStage().setIconified(false);
getStage().show();
});
});
MenuItem exitItem = new MenuItem("Exit");
exitItem.addActionListener(l->{
systemTray.remove(getTrayIcon());
onQuitApp();
Platform.exit();
});
popup.add(showItem);
popup.addSeparator();
popup.add(exitItem);
TrayIcon trayIcon = new TrayIcon(
Toolkit.getDefaultToolkit().getImage(Main.class.getResource("app_icon.png")),
"Stream-Pi Server",
popup
);
trayIcon.setImageAutoSize(true);
this.trayIcon = trayIcon;
}
private TrayIcon trayIcon = null;
public TrayIcon getTrayIcon()
{
return trayIcon;
}
@Override
public void handleMinorException(MinorException e) {
getLogger().log(Level.SEVERE, e.getMessage());
e.printStackTrace();
Platform.runLater(()-> new StreamPiAlert(e.getTitle(), e.getShortMessage(), StreamPiAlertType.WARNING).show());
}
@Override
public void handleSevereException(SevereException e) {
getLogger().log(Level.SEVERE, e.getMessage());
e.printStackTrace();
Platform.runLater(()->{
StreamPiAlert alert = new StreamPiAlert(e.getTitle(), e.getShortMessage(), StreamPiAlertType.ERROR);
alert.setOnClicked(new StreamPiAlertListener()
{
@Override
public void onClick(String txt)
{
onQuitApp();
Platform.exit();
}
});
alert.show();
});
}
@Override
public synchronized boolean onNormalActionClicked(NormalAction action) {
try{
getLogger().info("Action "+action.getID()+" clicked!");
action.onActionClicked();
return true;
}
catch (Exception e)
{
handleMinorException(new MinorException(
"Action Execution Failed!",
"Error running Action at ["+action.getLocation().getRow()+","+action.getLocation().getCol()+"] ("+action.getDisplayText()+")\n"+
"Check stacktrace/log to know what exactly happened\n\nMessage : \n"+e.getMessage() )
);
return false;
}
}
@Override
public void clearTemp() {
Platform.runLater(() -> {
getDashboardPane().getClientDetailsPane().refresh();
getDashboardPane().getActionGridPane().clear();
getDashboardPane().getActionDetailsPane().clear();
getSettingsPane().getClientsSettings().loadData();
});
}
@Override
public void saveServerProperties() {
try {
NormalActionPlugins.getInstance().saveServerSettings();
getSettingsPane().getPluginsSettings().loadPlugins();
} catch (MinorException e) {
e.printStackTrace();
handleMinorException(e);
}
}
@Override
public com.StreamPi.Util.Platform.Platform getPlatform() {
return ServerInfo.getInstance().getPlatformType();
}
}
/*
Config.java
Contributor(s) : Debayan Sutradhar (@rnayabed)
handler for config.xml
*/
package com.StreamPi.Server.IO;
import java.io.File;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import com.StreamPi.Server.Info.ServerInfo;
import com.StreamPi.Util.Exception.SevereException;
import com.StreamPi.Util.XMLConfigHelper.XMLConfigHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class Config {
private static Config instance = null;
private final File configFile;
private Document document;
private Config() throws SevereException {
try {
configFile = new File(ServerInfo.getInstance().getPrePath()+"config.xml");
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
document = docBuilder.parse(configFile);
} catch (Exception e) {
e.printStackTrace();
throw new SevereException("Config", "unable to read config.xml");
}
}
public static synchronized Config getInstance() throws SevereException
{
if(instance == null)
instance = new Config();
return instance;
}
Logger logger = Logger.getLogger(Config.class.getName());
public void save() throws SevereException {
try {
logger.info("Saving config ...");
Transformer transformer = TransformerFactory.newInstance().newTransformer();
Result output = new StreamResult(configFile);
Source input = new DOMSource(document);
transformer.transform(input, output);
logger.info("... Done!");
} catch (Exception e) {
throw new SevereException("Config", "unable to save config.xml");
}
}
//Getters
//comms
private Element getCommsElement()
{
return (Element) document.getElementsByTagName("comms").item(0);
}
public String getServerName()
{
return XMLConfigHelper.getStringProperty(getCommsElement(), "name",
getDefaultServerName(), false, true, document, configFile);
}
public int getPort()
{
return XMLConfigHelper.getIntProperty(getCommsElement(), "port",
getDefaultPort(), false, true, document, configFile);
}
//default getters
public String getDefaultServerName()
{
return "StreamPi Server";
}
public int getDefaultPort()
{
return 2004;
}
private Element getServerElement()
{
return (Element) document.getElementsByTagName("server").item(0);
}
//server
private Element getActionGridElement()
{
return (Element) getServerElement().getElementsByTagName("action-grid").item(0);
}
public int getActionGridActionGap()
{
return XMLConfigHelper.getIntProperty(getActionGridElement(), "gap",
getDefaultActionGridActionGap(), false, true, document, configFile);
}
public int getActionGridActionSize()
{
return XMLConfigHelper.getIntProperty(getActionGridElement(), "size",
getDefaultActionGridSize(), false, true, document, configFile);
}
public String getCurrentThemeFullName()
{
return XMLConfigHelper.getStringProperty(getServerElement(), "current-theme-full-name",
getDefaultCurrentThemeFullName(), false, true, document, configFile);
}
public String getThemesPath()
{
return XMLConfigHelper.getStringProperty(getServerElement(), "themes-path",
getDefaultThemesPath(), false, true, document, configFile);
}
public String getPluginsPath()
{
return XMLConfigHelper.getStringProperty(getServerElement(), "plugins-path",
getDefaultPluginsPath(), false, true, document, configFile);
}
//default getters
public String getDefaultCurrentThemeFullName()
{
return "com.StreamPi.DefaultLight";
}
public String getDefaultThemesPath()
{
return "Themes/";
}
public String getDefaultPluginsPath()
{
return "Plugins/";
}
//server > startup-window-size
private Element getStartupWindowSizeElement()
{
return (Element) getServerElement().getElementsByTagName("startup-window-size").item(0);
}
public double getStartupWindowWidth()
{
return XMLConfigHelper.getDoubleProperty(getStartupWindowSizeElement(), "width",
getDefaultStartupWindowWidth(), false, true, document, configFile);
}
public double getStartupWindowHeight()
{
return XMLConfigHelper.getDoubleProperty(getStartupWindowSizeElement(), "height",
getDefaultStartupWindowHeight(), false, true, document, configFile);
}
//default getters
public int getDefaultStartupWindowWidth()
{
return 1024;
}
public int getDefaultStartupWindowHeight()
{
return 768;
}
//others
private Element getOthersElement()
{
return (Element) document.getElementsByTagName("others").item(0);
}
public boolean getStartOnBoot()
{
return XMLConfigHelper.getBooleanProperty(getOthersElement(), "start-on-boot",
getDefaultStartOnBoot(), false, true, document, configFile);
}
public boolean getCloseOnX()
{
return XMLConfigHelper.getBooleanProperty(getOthersElement(), "close-on-x",
getDefaultCloseOnX(), false, true, document, configFile);
}
public boolean isFirstTimeUse()
{
return XMLConfigHelper.getBooleanProperty(getOthersElement(), "first-time-use", true, false, true, document, configFile);
}
public boolean isAllowDonatePopup()
{
return XMLConfigHelper.getBooleanProperty(getOthersElement(), "allow-donate-popup", true, false, true, document, configFile);
}
//default getters
public boolean getDefaultStartOnBoot()
{
return false;
}
public boolean getDefaultCloseOnX()
{
return false;
}
//Setters
//comms
public void setServerName(String name)
{
getCommsElement().getElementsByTagName("name").item(0).setTextContent(name);
}
public void setServerPort(int port)
{
getCommsElement().getElementsByTagName("port").item(0).setTextContent(port+"");
}
//server
public int getDefaultActionGridActionGap()
{
return 5;
}
public int getDefaultActionGridSize()
{
return 100;
}
public void setActionGridSize(int size)
{
getActionGridElement().getElementsByTagName("size").item(0).setTextContent(size+"");
}
public void setActionGridGap(int size)
{
getActionGridElement().getElementsByTagName("gap").item(0).setTextContent(size+"");
}
public void setPluginsPath(String path)
{
getServerElement().getElementsByTagName("plugins-path").item(0).setTextContent(path);
}
public void setThemesPath(String path)
{
getServerElement().getElementsByTagName("themes-path").item(0).setTextContent(path);
}
public void setCurrentThemeFullName(String themeName)
{
getServerElement().getElementsByTagName("current-theme-full-name").item(0).setTextContent(themeName);
}
//server > startup-window-size
public void setStartupWindowSize(double width, double height)
{
setStartupWindowWidth(width);
setStartupWindowHeight(height);
}
public void setStartupWindowWidth(double width)
{
getStartupWindowSizeElement().getElementsByTagName("width").item(0).setTextContent(width+"");
}
public void setStartupWindowHeight(double height)
{
getStartupWindowSizeElement().getElementsByTagName("height").item(0).setTextContent(height+"");
}
//others
public void setStartupOnBoot(boolean value)
{
getOthersElement().getElementsByTagName("start-on-boot").item(0).setTextContent(value+"");
}
public void setCloseOnX(boolean value)
{
getOthersElement().getElementsByTagName("close-on-x").item(0).setTextContent(value+"");
}
public void setFirstTimeUse(boolean value)
{
getOthersElement().getElementsByTagName("first-time-use").item(0).setTextContent(value+"");
}
public void setAllowDonatePopup(boolean value)
{
getOthersElement().getElementsByTagName("allow-donate-popup").item(0).setTextContent(value+"");
}
}
/*
Stream-Pi - Free & Open-Source Modular Cross-Platform Programmable Macropad
Copyright (C) 2019-2021 Debayan Sutradhar (rnayabed), Samuel Quiñones (SamuelQuinones)
This program 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.
This program 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.
Written by : Debayan Sutradhar (rnayabed)
*/
package com.StreamPi.Server.Info;
public class License {
/**
* @return Formatted String containing the Stream-Pi License an also licenses of other opensource libraries used.
*/
public static String getLicense()
{
return "Stream-Pi - Free & Open-Source Modular Cross-Platform Programmable Macro Pad\n" +
"Copyright (C) 2019-2021 Debayan Sutradhar (rnayabed), Samuel Quiñones (SamuelQuinones)\n" +
"\n" +
"This program is free software: you can redistribute it and/or modify\n" +
"it under the terms of the GNU General Public License as published by\n" +
"the Free Software Foundation, either version 3 of the License, or\n" +
"(at your option) any later version.\n" +
"\n" +
"This program is distributed in the hope that it will be useful,\n" +
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +
"GNU General Public License for more details.\n" +
"\n\n"+
"Opensource Libraries used :\n"+
"1. JavaFX - GNU General Public License with Classpath Exception\nhttp://openjdk.java.net/legal/gplv2+ce.html\n\n"+
"2. JSON - The JSON License\nhttps://www.json.org/license.html\n\n"+
"3. Ikonli - Apache License\nhttps://github.com/kordamp/ikonli/blob/master/LICENSE\n\n";
}
}
/*
ServerInfo.java
Stores basic information about the server - name, platform type
Contributors: Debayan Sutradhar (@dubbadhar)
*/
package com.StreamPi.Server.Info;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.Platform.Platform;
import com.StreamPi.Util.Platform.ReleaseStatus;
import com.StreamPi.Util.Version.Version;
public class ServerInfo {
private Version version;
private final ReleaseStatus releaseStatus;
private final Platform platformType;
private String prePath;
private Version minThemeSupportVersion;
private Version minPluginSupportVersion;
private Version commStandardVersion;
private static ServerInfo instance = null;
private String runnerFileName = null;
private boolean startMinimised = false;
private ServerInfo(){
version = new Version(1,0,0);
minThemeSupportVersion = new Version(1,0,0);
minPluginSupportVersion = new Version(1,0,0);
commStandardVersion = new Version(1,0,0);
releaseStatus = ReleaseStatus.EA;
prePath = "data/";
String osName = System.getProperty("os.name").toLowerCase();
if(osName.contains("windows"))
platformType = Platform.WINDOWS;
else if (osName.contains("linux"))
platformType = Platform.LINUX;
else if (osName.contains("mac"))
platformType = Platform.MAC;
else
platformType = Platform.UNKNOWN;
}
public String getPrePath() {
return prePath;
}
public void setStartMinimised(boolean startMinimised)
{
this.startMinimised = startMinimised;
}
public boolean isStartMinimised()
{
return startMinimised;
}
public void setRunnerFileName(String runnerFileName)
{
this.runnerFileName = runnerFileName;
}
public String getRunnerFileName()
{
return runnerFileName;
}
public static synchronized ServerInfo getInstance(){
if(instance == null)
{
instance = new ServerInfo();
}
return instance;
}
public Platform getPlatformType()
{
return platformType;
}
public Version getVersion() {
return version;
}
public ReleaseStatus getReleaseStatus()
{
return releaseStatus;
}
public Version getMinThemeSupportVersion()
{
return minThemeSupportVersion;
}
public Version getMinPluginSupportVersion()
{
return minPluginSupportVersion;
}
public Version getCommStandardVersion()
{
return commStandardVersion;
}
}
/*
Stream-Pi - Free & Open-Source Modular Cross-Platform Programmable Macropad
Copyright (C) 2019-2021 Debayan Sutradhar (rnayabed), Samuel Quiñones (SamuelQuinones)
This program 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.
This program 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.
Written by : Debayan Sutradhar (rnayabed)
*/
package com.StreamPi.Server;
import com.StreamPi.Server.Controller.Controller;
import com.StreamPi.Server.Info.ServerInfo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
/**
* First method to be called
* This method first parses all the available command line arguments passed.
* Then a new instance of Controller is created, and then initialised.
*/
public void start(Stage stage) {
for(String eachArg : getParameters().getRaw())
{
String[] r = eachArg.split("=");
if(r[0].equals("-DStreamPi.startupRunnerFileName"))
ServerInfo.getInstance().setRunnerFileName(r[1]);
else if(r[0].equals("-DStreamPi.startupMode"))
ServerInfo.getInstance().setStartMinimised(r[1].equals("min"));
}
Controller d = new Controller();
Scene s = new Scene(d);
stage.setScene(s);
d.setHostServices(getHostServices());
d.init();
}
/**
* This is a fallback. Called in some JVMs.
* This method just sends the command line arguments to JavaFX Application
*/
public static void main(String[] args)
{
launch(args);
}
}
package com.StreamPi.Server.UIPropertyBox;
import com.StreamPi.ActionAPI.ActionProperty.Property.ControlType;
import com.StreamPi.ActionAPI.ActionProperty.Property.Type;
import javafx.scene.Node;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleButton;
public class UIPropertyBox
{
private Node controlNode;
private boolean canBeBlank;
private String displayName;
public String getDisplayName() {
return displayName;
}
public boolean isCanBeBlank() {
return canBeBlank;
}
private int index;
private ControlType controlType;
private Type type;
public UIPropertyBox(int index, String displayName, Node controlNode, ControlType controlType, Type type, boolean canBeBlank)
{
this.index = index;
this.displayName = displayName;
this.controlNode = controlNode;
this.controlType = controlType;
this.type = type;
this.canBeBlank = canBeBlank;
}
public ControlType getControlType()
{
return controlType;
}
public Node getControlNode()
{
return controlNode;
}
public int getIndex() {
return index;
}
public Type getType()
{
return type;
}
public String getRawValue()
{
String rawValue = null;
if (controlType == ControlType.TEXT_FIELD)
rawValue = ((TextField) controlNode).getText();
else if (controlType == ControlType.COMBO_BOX)
rawValue = ((ComboBox<String>) controlNode).getSelectionModel().getSelectedIndex() + "";
else if (controlType == ControlType.SLIDER_DOUBLE)
rawValue = ((Slider) controlNode).getValue() + "";
else if (controlType == ControlType.SLIDER_INTEGER)
rawValue = Math.round(((Slider) controlNode).getValue()) + "";
else if (controlType == ControlType.TOGGLE) {
ToggleButton toggleButton = ((ToggleButton) controlNode);
if (toggleButton.isSelected())
rawValue = "true";
else
rawValue = "false";
}
return rawValue;
}
}
package com.StreamPi.Server.Window;
import com.StreamPi.Server.Connection.ServerListener;
import com.StreamPi.Server.IO.Config;
import com.StreamPi.Server.Info.ServerInfo;
import com.StreamPi.Server.Main;
import com.StreamPi.Server.Window.Dashboard.*;
import com.StreamPi.Server.Window.Settings.SettingsBase;
import com.StreamPi.ThemeAPI.Theme;
import com.StreamPi.ThemeAPI.Themes;
import com.StreamPi.Util.Alert.StreamPiAlert;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.Exception.SevereException;
import com.StreamPi.Util.LoggerHelper.StreamPiLogFileHandler;
import javafx.application.HostServices;
import javafx.scene.image.Image;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.io.IOException;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.awt.PopupMenu;
import java.awt.MenuItem;
public abstract class Base extends StackPane implements ExceptionAndAlertHandler, ServerListener {
private Config config;
private ServerInfo serverInfo;
private Stage stage;
private HostServices hostServices;
public Logger getLogger(){
return logger;
}
private SettingsBase settingsBase;
private DashboardBase dashboardBase;
private StackPane alertStackPane;
public void setHostServices(HostServices hostServices)
{
this.hostServices = hostServices;
}
public HostServices getHostServices()
{
return hostServices;
}
private Logger logger = null;
private StreamPiLogFileHandler logFileHandler = null;
public void initLogger() throws SevereException
{
try
{
if(logger != null)
return;
logger = Logger.getLogger("");
logFileHandler = new StreamPiLogFileHandler(ServerInfo.getInstance().getPrePath()+"../streampi.log");
logger.addHandler(logFileHandler);
}
catch(Exception e)
{
e.printStackTrace();
throw new SevereException("Cant get logger started!");
}
}
public void closeLogger()
{
if(logFileHandler != null)
logFileHandler.close();
}
public void initBase() throws SevereException {
initLogger();
getChildren().clear();
stage = (Stage) getScene().getWindow();
getStage().getIcons().add(new Image(Main.class.getResourceAsStream("app_icon.png")));
getStage().setMinWidth(500);
getStage().setMinHeight(500);
config = Config.getInstance();
stage.setWidth(config.getStartupWindowWidth());
stage.setHeight(config.getStartupWindowHeight());
stage.centerOnScreen();
serverInfo = ServerInfo.getInstance();
initThemes();
settingsBase = new SettingsBase(getHostServices(), this, this);
settingsBase.prefWidthProperty().bind(widthProperty());
settingsBase.prefHeightProperty().bind(heightProperty());
dashboardBase = new DashboardBase(this, getHostServices());
dashboardBase.prefWidthProperty().bind(widthProperty());
dashboardBase.prefHeightProperty().bind(heightProperty());
alertStackPane = new StackPane();
alertStackPane.setVisible(false);
StreamPiAlert.setParent(alertStackPane);
getChildren().addAll(settingsBase, dashboardBase, alertStackPane);
dashboardBase.toFront();
}
public void initThemes() throws SevereException {
clearStylesheets();
registerThemes();
applyDefaultStylesheet();
applyDefaultTheme();
}
public Stage getStage()
{
return stage;
}
public void applyDefaultStylesheet()
{
logger.info("Applying default stylesheet ...");
Font.loadFont(Main.class.getResourceAsStream("Roboto.ttf"), 13);
getStylesheets().add(Main.class.getResource("style.css").toExternalForm());
logger.info("... Done!");
}
public DashboardBase getDashboardPane()
{
return dashboardBase;
}
public SettingsBase getSettingsPane()
{
return settingsBase;
}
public Config getConfig()
{
return config;
}
public ServerInfo getServerInfo()
{
return serverInfo;
}
private Theme currentTheme;
public Theme getCurrentTheme()
{
return currentTheme;
}
public void applyTheme(Theme t)
{
logger.info("Applying theme '"+t.getFullName()+"' ...");
if(t.getFonts() != null)
{
for(String fontFile : t.getFonts())
{
Font.loadFont(fontFile.replace("%20",""), 13);
}
}
currentTheme = t;
getStylesheets().addAll(t.getStylesheets());
logger.info("... Done!");
}
public void clearStylesheets()
{
getStylesheets().clear();
}
Themes themes;
public void registerThemes() throws SevereException
{
logger.info("Loading themes ...");
themes = new Themes(getConfig().getThemesPath(), getConfig().getCurrentThemeFullName(), serverInfo.getMinThemeSupportVersion());
if(themes.getErrors().size()>0)
{
StringBuilder themeErrors = new StringBuilder();
for(MinorException eachException : themes.getErrors())
{
themeErrors.append("\n * ").append(eachException.getShortMessage());
}
if(themes.getIsBadThemeTheCurrentOne())
{
themeErrors.append("\n\nReverted to default theme! (").append(getConfig().getDefaultCurrentThemeFullName()).append(")");
getConfig().setCurrentThemeFullName(getConfig().getDefaultCurrentThemeFullName());
getConfig().save();
}
handleMinorException(new MinorException("Theme Loading issues", themeErrors.toString()));
}
logger.info("... Done!");
}
public Themes getThemes()
{
return themes;
}
public void applyDefaultTheme()
{
logger.info("Applying default theme ...");
boolean foundTheme = false;
for(Theme t: themes.getThemeList())
{
if(t.getFullName().equals(config.getCurrentThemeFullName()))
{
foundTheme = true;
applyTheme(t);
break;
}
}
if(foundTheme)
logger.info("... Done!");
else
{
logger.info("Theme not found. reverting to light theme ...");
try {
Config.getInstance().setCurrentThemeFullName("com.StreamPi.DefaultLight");
Config.getInstance().save();
applyDefaultTheme();
}
catch (SevereException e)
{
handleSevereException(e);
}
}
}
}
package com.StreamPi.Server.Window.Dashboard.ActionGridPane;
import com.StreamPi.ActionAPI.Action.Action;
import com.StreamPi.ActionAPI.Action.ActionType;
import com.StreamPi.ActionAPI.Action.DisplayTextAlignment;
import com.StreamPi.ActionAPI.Action.Location;
import com.StreamPi.ActionAPI.OtherActions.CombineAction;
import com.StreamPi.ActionAPI.OtherActions.FolderAction;
import com.StreamPi.Server.Window.ExceptionAndAlertHandler;
import com.StreamPi.Server.Window.Dashboard.ActionsDetailPane.ActionDetailsPaneListener;
import com.StreamPi.Util.Exception.MinorException;
import javafx.application.Platform;
import javafx.css.CssMetaData;
import javafx.css.Styleable;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.CacheHint;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseButton;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.BackgroundImage;
import javafx.scene.layout.BackgroundPosition;
import javafx.scene.layout.BackgroundRepeat;
import javafx.scene.layout.BackgroundSize;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.ImagePattern;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.TextAlignment;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.nio.ByteBuffer;
import java.util.function.Consumer;
public class ActionBox extends StackPane{
private Label displayTextLabel;
private int row;
private int col;
public void setRow(int row) {
this.row = row;
}
public void setCol(int col) {
this.col = col;
}
public int getRow() {
return row;
}
public int getCol() {
return col;
}
private ActionDetailsPaneListener actionDetailsPaneListener;
public void clear()
{
setAction(null);
setInvalid(false);
setBackground(Background.EMPTY);
setStyle(null);
getChildren().clear();
}
public void baseInit()
{
displayTextLabel = new Label();
displayTextLabel.setWrapText(true);
displayTextLabel.setTextAlignment(TextAlignment.CENTER);
displayTextLabel.getStyleClass().add("action_box_display_text_label");
displayTextLabel.prefHeightProperty().bind(heightProperty());
displayTextLabel.prefWidthProperty().bind(widthProperty());
getChildren().addAll(displayTextLabel);
setMinSize(size, size);
setMaxSize(size, size);
getStyleClass().add("action_box");
getStyleClass().add("action_box_icon_not_present");
getStyleClass().add("action_box_valid");
setOnDragOver(dragEvent ->
{
if(dragEvent.getDragboard().hasContent(Action.getDataFormat()))
{
dragEvent.acceptTransferModes(TransferMode.ANY);
dragEvent.consume();
}
});
setOnDragDropped(dragEvent ->
{
try
{
if(action == null)
{
Action action = (Action) dragEvent.getDragboard().getContent(Action.getDataFormat());
action.setLocation(new Location(getRow(),
getCol()));
action.setParent(getStreamPiParent());
action.setIDRandom();
actionGridPaneListener.addActionToCurrentClientProfile(action);
setAction(action);
init();
actionDetailsPaneListener.onActionClicked(action, this);
if(action.isHasIcon())
actionDetailsPaneListener.setSendIcon(true);
actionDetailsPaneListener.saveAction();
}
}
catch (MinorException e)
{
exceptionAndAlertHandler.handleMinorException(e);
e.printStackTrace();
}
});
setOnDragDetected(mouseEvent -> {
try {
if(action!=null)
{
if(action.getActionType() == ActionType.NORMAL)
{
Dragboard db = startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
Action newAction = (Action) action.clone();
newAction.setIDRandom();
newAction.setParent(getStreamPiParent());
content.put(Action.getDataFormat(), newAction);
db.setContent(content);
mouseEvent.consume();
}
}
}
catch (CloneNotSupportedException e)
{
e.printStackTrace();
}
});
setOnMouseClicked(mouseEvent -> {
if(action != null && mouseEvent.getButton().equals(MouseButton.PRIMARY))
{
if(mouseEvent.getClickCount() == 2 && action.getActionType() == ActionType.FOLDER)
{
getActionDetailsPaneListener().onOpenFolderButtonClicked();
}
else
{
try
{
actionDetailsPaneListener.onActionClicked(action, this);
}
catch (MinorException e)
{
exceptionAndAlertHandler.handleMinorException(e);
e.printStackTrace();
}
}
}
});
setCache(true);
setCacheHint(CacheHint.SPEED);
}
public void setInvalid(boolean invalid)
{
if(invalid)
{
getStyleClass().remove("action_box_valid");
getStyleClass().add("action_box_invalid");
}
else
{
getStyleClass().remove("action_box_invalid");
getStyleClass().add("action_box_valid");
}
}
public ActionDetailsPaneListener getActionDetailsPaneListener() {
return actionDetailsPaneListener;
}
public ActionGridPaneListener getActionGridPaneListener() {
return actionGridPaneListener;
}
private int size;
private ActionGridPaneListener actionGridPaneListener;
public ActionBox(int size, ActionDetailsPaneListener actionDetailsPaneListener, ActionGridPaneListener actionGridPaneListener)
{
this.actionGridPaneListener = actionGridPaneListener;
this.actionDetailsPaneListener = actionDetailsPaneListener;
this.size = size;
baseInit();
}
public static Action deserialize(ByteBuffer buffer) {
try {
ByteArrayInputStream is = new ByteArrayInputStream(buffer.array());
ObjectInputStream ois = new ObjectInputStream(is);
Action obj = (Action) ois.readObject();
return obj;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public void setIcon(byte[] iconByteArray)
{
if(iconByteArray == null)
{
getStyleClass().remove("action_box_icon_present");
getStyleClass().add("action_box_icon_not_present");
setBackground(null);
return;
}
else
{
getStyleClass().add("action_box_icon_present");
getStyleClass().remove("action_box_icon_not_present");
setBackground(
new Background(
new BackgroundImage(new Image(
new ByteArrayInputStream(iconByteArray), size, size, false, true
), BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.CENTER,
new BackgroundSize(100, 100, true, true, true, false))
)
);
}
}
private Action action;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
private String parent;
public String getStreamPiParent() {
return parent;
}
public void setStreamPiParent(String parent) {
this.parent = parent;
}
public ActionBox(int size, Action action, ActionDetailsPaneListener actionDetailsPaneListener, ExceptionAndAlertHandler exceptionAndAlertHandler, ActionGridPaneListener actionGridPaneListener)
{
this.actionGridPaneListener = actionGridPaneListener;
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
this.action = action;
this.actionDetailsPaneListener = actionDetailsPaneListener;
this.size = size;
baseInit();
init();
}
public void setAction(Action action)
{
this.action = action;
}
public void init()
{
if(action.isShowDisplayText())
setDisplayTextLabel(action.getDisplayText());
else
setDisplayTextLabel("");
setDisplayTextAlignment(action.getDisplayTextAlignment());
setBackgroundColour(action.getBgColourHex());
setDisplayTextFontColour(action.getDisplayTextFontColourHex());
setInvalid(action.isInvalid());
Platform.runLater(()->{
if(action.isHasIcon() && action.isShowIcon())
{
setIcon(action.getIconAsByteArray());
}
else
{
setIcon(null);
}
});
}
public void setDisplayTextLabel(String text)
{
displayTextLabel.setText(text);
}
public void setDisplayTextAlignment(DisplayTextAlignment displayTextAlignment)
{
if(displayTextAlignment == DisplayTextAlignment.CENTER)
displayTextLabel.setAlignment(Pos.CENTER);
else if (displayTextAlignment == DisplayTextAlignment.BOTTOM)
displayTextLabel.setAlignment(Pos.BOTTOM_CENTER);
else if (displayTextAlignment == DisplayTextAlignment.TOP)
displayTextLabel.setAlignment(Pos.TOP_CENTER);
}
public void setDisplayTextFontColour(String colour)
{
System.out.println("COLOr : "+colour);
if(!colour.isEmpty())
displayTextLabel.setStyle("-fx-text-fill : "+colour+";");
}
public void setBackgroundColour(String colour)
{
System.out.println("COLOr : "+colour);
if(!colour.isEmpty() && action.getIconAsByteArray() == null)
setStyle("-fx-background-color : "+colour);
}
}
package com.StreamPi.Server.Window.Dashboard.ActionGridPane;
import com.StreamPi.ActionAPI.Action.Action;
import com.StreamPi.ActionAPI.Action.Location;
import com.StreamPi.ActionAPI.ActionProperty.Property.Property;
import com.StreamPi.ActionAPI.OtherActions.FolderAction;
import com.StreamPi.Server.Client.Client;
import com.StreamPi.Server.Client.ClientProfile;
import com.StreamPi.Server.Connection.ServerListener;
import com.StreamPi.Server.IO.Config;
import com.StreamPi.Server.Window.ExceptionAndAlertHandler;
import com.StreamPi.Server.Window.Dashboard.ActionsDetailPane.ActionDetailsPaneListener;
import com.StreamPi.ThemeAPI.Themes;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.Exception.SevereException;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.CacheHint;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Paint;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.ArrayList;
import java.util.logging.Logger;
public class ActionGridPane extends ScrollPane implements ActionGridPaneListener {
private ExceptionAndAlertHandler exceptionAndAlertHandler;
private ActionDetailsPaneListener actionDetailsPaneListener;
public ActionGridPane(ExceptionAndAlertHandler exceptionAndAlertHandler)
{
logger = Logger.getLogger(ActionGridPane.class.getName());
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
getStyleClass().add("action_grid_pane_scroll_pane");
VBox.setVgrow(this, Priority.ALWAYS);
actionsGridPane = new GridPane();
actionsGridPane.setPadding(new Insets(5.0));
actionsGridPane.getStyleClass().add("action_grid_pane");
actionsGridPane.setPrefSize(USE_COMPUTED_SIZE, USE_COMPUTED_SIZE);
setContent(actionsGridPane);
}
public void setActionDetailsPaneListener(ActionDetailsPaneListener actionDetailsPaneListener) {
this.actionDetailsPaneListener = actionDetailsPaneListener;
}
private String currentParent;
public void setCurrentParent(String currentParent) {
this.currentParent = currentParent;
}
public ClientProfile getClientProfile() {
return clientProfile;
}
private Client client;
public void setClient(Client client) {
this.client = client;
}
public Client getClient() {
return client;
}
private int rows, cols;
private GridPane actionsGridPane;
private ClientProfile clientProfile;
public void setClientProfile(ClientProfile clientProfile)
{
this.clientProfile = clientProfile;
setCurrentParent("root");
setRows(clientProfile.getRows());
setCols(clientProfile.getCols());
}
public String getCurrentParent() {
return currentParent;
}
public StackPane getFolderBackButton() throws SevereException
{
StackPane stackPane = new StackPane();
stackPane.getStyleClass().add("action_box");
stackPane.getStyleClass().add("action_box_valid");
stackPane.setPrefSize(
Config.getInstance().getActionGridActionSize(),
Config.getInstance().getActionGridActionSize()
);
FontIcon fontIcon = new FontIcon("fas-chevron-left");
fontIcon.setIconSize(Config.getInstance().getActionGridActionSize() - 30);
stackPane.setAlignment(Pos.CENTER);
stackPane.getChildren().add(fontIcon);
stackPane.setOnMouseClicked(e->returnToPreviousParent());
return stackPane;
}
public void renderGrid() throws SevereException {
clear();
actionsGridPane.setHgap(Config.getInstance().getActionGridActionGap());
actionsGridPane.setVgap(Config.getInstance().getActionGridActionGap());
boolean isFolder = false;
if(!getCurrentParent().equals("root"))
{
isFolder = true;
actionsGridPane.add(getFolderBackButton(), 0,0);
}
for(int row = 0; row<rows; row++)
{
for(int col = 0; col<cols; col++)
{
if(row == 0 && col == 0 && isFolder)
continue;
ActionBox actionBox = new ActionBox(Config.getInstance().getActionGridActionSize(), actionDetailsPaneListener, this);
actionBox.setStreamPiParent(currentParent);
actionBox.setRow(row);
actionBox.setCol(col);
actionsGridPane.add(actionBox, row, col);
}
}
}
public void renderActions()
{
StringBuilder errors = new StringBuilder();
for(String action1x : getClientProfile().getActionsKeySet())
{
Action eachAction = getClientProfile().getActionByID(action1x);
logger.info("Action ID : "+eachAction.getID()+
"\nInvalid : "+eachAction.isInvalid());
try {
renderAction(eachAction);
}
catch (SevereException e)
{
exceptionAndAlertHandler.handleSevereException(e);
}
catch (MinorException e)
{
errors.append("*").append(e.getShortMessage()).append("\n");
}
}
if(!errors.toString().isEmpty())
{
exceptionAndAlertHandler.handleMinorException(new MinorException("Error while rendering following actions", errors.toString()));
}
}
public void clear()
{
actionsGridPane.getChildren().clear();
}
private Logger logger;
public void renderAction(Action action) throws SevereException, MinorException
{
if(!action.getParent().equals(currentParent))
{
logger.info("Skipping action "+action.getID()+", not current parent!");
return;
}
if(action.getLocation().getRow()==-1)
{
logger.info("Action has -1 rowIndex. Probably Combine Action. Skipping ...");
return;
}
if(action.getLocation().getRow() >= rows || action.getLocation().getCol() >= cols)
{
throw new MinorException("Action "+action.getDisplayText()+" ("+action.getID()+") falls outside bounds.\n" +
" Consider increasing rows/cols from client settings and relocating/deleting it.");
}
ActionBox actionBox = new ActionBox(Config.getInstance().getActionGridActionSize(), action, actionDetailsPaneListener, exceptionAndAlertHandler, this);
Location location = action.getLocation();
actionBox.setStreamPiParent(currentParent);
actionBox.setRow(location.getRow());
actionBox.setCol(location.getCol());
for(Node node : actionsGridPane.getChildren())
{
if(GridPane.getColumnIndex(node) == location.getRow() &&
GridPane.getRowIndex(node) == location.getCol())
{
actionsGridPane.getChildren().remove(node);
break;
}
}
System.out.println(location.getCol()+","+location.getRow());
actionsGridPane.add(actionBox, location.getRow(), location.getCol());
}
public void setRows(int rows)
{
this.rows = rows;
}
public void setCols(int cols)
{
this.cols = cols;
}
public int getRows()
{
return rows;
}
public int getCols()
{
return cols;
}
@Override
public void addActionToCurrentClientProfile(Action newAction) {
try {
getClientProfile().addAction(newAction);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
private String previousParent;
public void setPreviousParent(String previousParent) {
this.previousParent = previousParent;
}
public String getPreviousParent() {
return previousParent;
}
@Override
public void renderFolder(FolderAction action) {
setCurrentParent(action.getID());
setPreviousParent(action.getParent());
try {
renderGrid();
renderActions();
} catch (SevereException e) {
e.printStackTrace();
}
}
public void returnToPreviousParent()
{
setCurrentParent(getPreviousParent());
if(!getPreviousParent().equals("root"))
{
System.out.println("parent : "+getPreviousParent());
setPreviousParent(getClientProfile().getActionByID(
getPreviousParent()
).getParent());
}
try {
renderGrid();
renderActions();
} catch (SevereException e) {
e.printStackTrace();
}
}
}
D
src/main/java/com/StreamPi/Server/Window/Dashboard/ActionGridPane/ActionGridPaneListener.java
+0
−10
package com.StreamPi.Server.Window.Dashboard.ActionGridPane;
import com.StreamPi.ActionAPI.Action.Action;
import com.StreamPi.ActionAPI.OtherActions.FolderAction;
public interface ActionGridPaneListener {
void addActionToCurrentClientProfile(Action newAction);
void renderFolder(FolderAction action);
}
package com.StreamPi.Server.Window.Dashboard.ActionsDetailPane;
import com.StreamPi.Server.UIPropertyBox.UIPropertyBox;
import com.StreamPi.ActionAPI.Action.Action;
import com.StreamPi.ActionAPI.Action.ActionType;
import com.StreamPi.ActionAPI.Action.DisplayTextAlignment;
import com.StreamPi.ActionAPI.Action.Location;
import com.StreamPi.ActionAPI.ActionProperty.ClientProperties;
import com.StreamPi.ActionAPI.ActionProperty.Property.ControlType;
import com.StreamPi.ActionAPI.ActionProperty.Property.Property;
import com.StreamPi.ActionAPI.ActionProperty.Property.Type;
import com.StreamPi.ActionAPI.OtherActions.CombineAction;
import com.StreamPi.ActionAPI.OtherActions.FolderAction;
import com.StreamPi.Server.Client.Client;
import com.StreamPi.Server.Client.ClientProfile;
import com.StreamPi.Server.Connection.ClientConnection;
import com.StreamPi.Server.Connection.ClientConnections;
import com.StreamPi.Server.Window.Dashboard.ActionGridPane.ActionBox;
import com.StreamPi.Server.Window.ExceptionAndAlertHandler;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.Exception.SevereException;
import com.StreamPi.Util.FormHelper.HBoxInputBox;
import com.StreamPi.Util.FormHelper.HBoxInputBoxWithFileChooser;
import com.StreamPi.Util.FormHelper.SpaceFiller;
import javafx.application.HostServices;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.stage.FileChooser;
import javafx.stage.Window;
import org.kordamp.ikonli.javafx.FontIcon;
import java.io.File;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;
public class ActionDetailsPane extends VBox implements ActionDetailsPaneListener {
private ScrollPane scrollPane;
private VBox vbox;
private VBox clientPropertiesVBox;
private Button saveButton;
private Button deleteButton;
private Button openFolderButton;
private HBox buttonBar;
private Label actionHeadingLabel;
private Logger logger;
private Button returnButtonForCombineActionChild;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
private HostServices hostServices;
public ActionDetailsPane(ExceptionAndAlertHandler exceptionAndAlertHandler, HostServices hostServices) {
this.hostServices = hostServices;
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
logger = Logger.getLogger(ActionDetailsPane.class.getName());
setSpacing(10.0);
// VBox.setVgrow(this, Priority.SOMETIMES);
clientPropertiesVBox = new VBox();
clientPropertiesVBox.setSpacing(10.0);
vbox = new VBox();
vbox.setPadding(new Insets(0, 25, 0, 5));
vbox.getStyleClass().add("action_details_pane_vbox");
vbox.setSpacing(10.0);
getStyleClass().add("action_details_pane");
// setPadding(new Insets(5,50,5,50));
// setMinHeight(245);
scrollPane = new ScrollPane();
VBox.setMargin(scrollPane, new Insets(0, 0, 0, 10));
scrollPane.getStyleClass().add("action_details_pane_scroll_pane");
setMinHeight(310);
scrollPane.setContent(vbox);
vbox.prefWidthProperty().bind(scrollPane.widthProperty());
scrollPane.prefWidthProperty().bind(widthProperty());
VBox.setVgrow(scrollPane, Priority.ALWAYS);
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
openFolderButton = new Button("Open Folder");
FontIcon folderOpenIcon = new FontIcon("far-folder-open");
openFolderButton.setGraphic(folderOpenIcon);
openFolderButton.setOnAction(event -> onOpenFolderButtonClicked());
saveButton = new Button("Apply Changes");
FontIcon syncIcon = new FontIcon("fas-sync-alt");
saveButton.setGraphic(syncIcon);
saveButton.setOnAction(event -> onSaveButtonClicked());
deleteButton = new Button("Delete Action");
FontIcon deleteIcon = new FontIcon("fas-trash");
deleteIcon.setIconColor(Paint.valueOf("#FF0000"));
deleteButton.setTextFill(Paint.valueOf("#FF0000"));
deleteButton.setGraphic(deleteIcon);
deleteButton.setOnAction(event -> onDeleteButtonClicked());
returnButtonForCombineActionChild = new Button("Return");
returnButtonForCombineActionChild.setGraphic(new FontIcon("fas-caret-left"));
returnButtonForCombineActionChild.managedProperty().bind(returnButtonForCombineActionChild.visibleProperty());
returnButtonForCombineActionChild.setOnAction(event -> {
try {
logger.info("@@## : " + action.getParent());
onActionClicked(getClientProfile().getActionByID(action.getParent()), getActionBox());
} catch (MinorException e) {
e.printStackTrace();
}
});
buttonBar = new HBox(openFolderButton, returnButtonForCombineActionChild, saveButton, deleteButton);
buttonBar.setPadding(new Insets(10, 10, 10, 0));
buttonBar.setAlignment(Pos.CENTER_RIGHT);
buttonBar.setVisible(false);
buttonBar.setSpacing(10.0);
actionHeadingLabel = new Label();
actionHeadingLabel.getStyleClass().add("action_details_pane_action_heading_label");
HBox headingHBox = new HBox(actionHeadingLabel);
headingHBox.setPadding(new Insets(5, 10, 0, 10));
getChildren().addAll(headingHBox, scrollPane, buttonBar);
displayTextAlignmentComboBox = new ComboBox<>(FXCollections.observableArrayList(DisplayTextAlignment.TOP,
DisplayTextAlignment.CENTER, DisplayTextAlignment.BOTTOM));
displayTextAlignmentComboBox.managedProperty().bind(displayTextAlignmentComboBox.visibleProperty());
actionClientProperties = new ArrayList<>();
displayNameTextField = new TextField();
displayNameTextField.managedProperty().bind(displayNameTextField.visibleProperty());
iconFileTextField = new TextField();
iconFileTextField.managedProperty().bind(iconFileTextField.visibleProperty());
iconFileTextField.textProperty().addListener((observableValue, s, t1) -> {
try {
if (!s.equals(t1) && t1.length() > 0) {
byte[] iconFileByteArray = Files.readAllBytes(new File(t1).toPath());
hideIconCheckBox.setDisable(false);
hideIconCheckBox.setSelected(false);
clearIconButton.setDisable(false);
System.out.println("ABABABABABBABABBABABABCCCCCCCCCCCCCCCCCC");
action.setIcon(iconFileByteArray);
setSendIcon(true);
System.out.println(action.getIconAsByteArray().length);
}
} catch (Exception e) {
e.printStackTrace();
exceptionAndAlertHandler.handleMinorException(new MinorException(e.getMessage()));
}
});
clearIconButton = new Button("Clear Icon");
clearIconButton.managedProperty().bind(clearIconButton.visibleProperty());
clearIconButton.setOnAction(event -> {
hideIconCheckBox.setDisable(true);
hideIconCheckBox.setSelected(false);
clearIconButton.setDisable(true);
iconFileTextField.clear();
});
hideDisplayTextCheckBox = new CheckBox("Hide");
hideDisplayTextCheckBox.managedProperty().bind(hideDisplayTextCheckBox.visibleProperty());
hideIconCheckBox = new CheckBox("Hide");
hideIconCheckBox.managedProperty().bind(hideIconCheckBox.visibleProperty());
actionBackgroundColourPicker = new ColorPicker();
actionBackgroundColourPicker.managedProperty().bind(actionBackgroundColourPicker.visibleProperty());
displayTextColourPicker = new ColorPicker();
displayTextColourPicker.managedProperty().bind(displayTextColourPicker.visibleProperty());
actionBackgroundColourTransparentCheckBox = new CheckBox("Default");
actionBackgroundColourPicker.disableProperty()
.bind(actionBackgroundColourTransparentCheckBox.selectedProperty());
HBox.setMargin(actionBackgroundColourTransparentCheckBox, new Insets(0, 0, 0, 10));
displayTextColourDefaultCheckBox = new CheckBox("Default");
displayTextColourPicker.disableProperty()
.bind(displayTextColourDefaultCheckBox.selectedProperty());
HBox.setMargin(displayTextColourDefaultCheckBox, new Insets(0, 0, 0, 10));
Region r = new Region();
HBox.setHgrow(r, Priority.ALWAYS);
Region r1 = new Region();
HBox.setHgrow(r1, Priority.ALWAYS);
HBox displayTextColourHBox = new HBox(new Label("Display Text Colour"), r1, displayTextColourPicker,
displayTextColourDefaultCheckBox);
displayTextColourHBox.setAlignment(Pos.CENTER);
displayTextColourHBox.setSpacing(5.0);
HBox bgColourHBox = new HBox(new Label("Background Colour"), r, actionBackgroundColourPicker,
actionBackgroundColourTransparentCheckBox);
bgColourHBox.setAlignment(Pos.CENTER);
bgColourHBox.setSpacing(5.0);
HBox clearIconHBox = new HBox(clearIconButton);
clearIconHBox.setAlignment(Pos.CENTER_RIGHT);
displayTextFieldHBox = new HBoxInputBox("Display Name", displayNameTextField, hideDisplayTextCheckBox);
normalActionsPropsVBox = new VBox(displayTextColourHBox,
new HBox(new Label("Alignment"), new SpaceFiller(SpaceFiller.FillerType.HBox),
displayTextAlignmentComboBox),
new HBoxInputBoxWithFileChooser("Icon", iconFileTextField, hideIconCheckBox,
new FileChooser.ExtensionFilter("Images", "*.jpeg", "*.jpg", "*.png", "*.gif")),
clearIconHBox, bgColourHBox);
normalActionsPropsVBox.managedProperty().bind(normalActionsPropsVBox.visibleProperty());
normalActionsPropsVBox.setSpacing(10.0);
vbox.getChildren().addAll(displayTextFieldHBox, normalActionsPropsVBox, clientPropertiesVBox);
vbox.setVisible(false);
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
setOnDragOver(dragEvent -> {
if (dragEvent.getDragboard().hasContent(Action.getDataFormat()) && action != null) {
if (action.getActionType() == ActionType.COMBINE) {
dragEvent.acceptTransferModes(TransferMode.ANY);
dragEvent.consume();
}
}
});
setOnDragDropped(dragEvent -> {
try {
Action newAction = (Action) dragEvent.getDragboard().getContent(Action.getDataFormat());
if (newAction.getActionType() == ActionType.NORMAL) {
newAction.setLocation(new Location(-1, -1));
newAction.setParent(this.action.getID());
combineActionPropertiesPane.getCombineAction().addChild(newAction.getID());
addActionToCurrentClientProfile(newAction);
ClientConnection connection = ClientConnections.getInstance()
.getClientConnectionBySocketAddress(getClient().getRemoteSocketAddress());
connection.saveActionDetails(getClientProfile().getID(), newAction);
combineActionPropertiesPane.renderProps();
saveAction();
}
} catch (MinorException e) {
exceptionAndAlertHandler.handleMinorException(e);
e.printStackTrace();
} catch (SevereException e) {
exceptionAndAlertHandler.handleSevereException(e);
e.printStackTrace();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
});
}
private VBox normalActionsPropsVBox;
private HBox displayTextFieldHBox;
private Client client;
private ClientProfile clientProfile;
public void setClient(Client client) {
this.client = client;
}
public Client getClient() {
return client;
}
public void setClientProfile(ClientProfile clientProfile) {
this.clientProfile = clientProfile;
}
public ClientProfile getClientProfile() {
return clientProfile;
}
public void setActionHeadingLabelText(String text) {
actionHeadingLabel.setText(text);
}
private Action action;
public Action getAction() {
return action;
}
private ActionBox actionBox;
public ActionBox getActionBox() {
return actionBox;
}
@Override
public void onActionClicked(Action action, ActionBox actionBox) throws MinorException {
this.action = action;
this.actionBox = actionBox;
System.out.println(action.getActionType());
logger.info("Action Display text : "+action.getDisplayText());
clear();
renderActionProperties();
}
private TextField displayNameTextField;
private CheckBox hideDisplayTextCheckBox;
private CheckBox hideIconCheckBox;
private TextField iconFileTextField;
private Button clearIconButton;
private ColorPicker actionBackgroundColourPicker;
private ColorPicker displayTextColourPicker;
private CheckBox actionBackgroundColourTransparentCheckBox;
private CheckBox displayTextColourDefaultCheckBox;
private ComboBox<DisplayTextAlignment> displayTextAlignmentComboBox;
public void clear()
{
sendIcon = false;
actionClientProperties.clear();
displayNameTextField.clear();
iconFileTextField.clear();
clientPropertiesVBox.getChildren().clear();
vbox.setVisible(false);
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
buttonBar.setVisible(false);
setActionHeadingLabelText("");
actionBackgroundColourPicker.setValue(Color.WHITE);
displayTextColourPicker.setValue(Color.WHITE);
}
boolean isCombineChild = false;
public boolean isCombineChild() {
return isCombineChild;
}
public void renderActionProperties() throws MinorException
{
if(action.getLocation().getCol() == -1) //Combine Child action
isCombineChild = true;
else
isCombineChild = false;
displayNameTextField.setText(action.getDisplayText());
System.out.println(action.getDisplayText()+"@@@@::::"+isCombineChild);
if(isCombineChild)
{
setReturnButtonForCombineActionChildVisible(true);
normalActionsPropsVBox.setVisible(false);
hideDisplayTextCheckBox.setSelected(false);
hideDisplayTextCheckBox.setVisible(false);
}
else
{
normalActionsPropsVBox.setVisible(true);
setReturnButtonForCombineActionChildVisible(false);
hideDisplayTextCheckBox.setVisible(true);
setFolderButtonVisible(action.getActionType().equals(ActionType.FOLDER));
displayTextAlignmentComboBox.getSelectionModel().select(action.getDisplayTextAlignment());
if(!action.getBgColourHex().isEmpty())
actionBackgroundColourPicker.setValue(Color.valueOf(action.getBgColourHex()));
else
actionBackgroundColourTransparentCheckBox.setSelected(true);
if(!action.getDisplayTextFontColourHex().isEmpty())
displayTextColourPicker.setValue(Color.valueOf(action.getDisplayTextFontColourHex()));
else
displayTextColourDefaultCheckBox.setSelected(true);
hideDisplayTextCheckBox.setSelected(!action.isShowDisplayText());
hideIconCheckBox.setDisable(!action.isHasIcon());
hideIconCheckBox.setSelected(!action.isShowIcon());
if(!action.isHasIcon())
{
hideIconCheckBox.setSelected(false);
}
clearIconButton.setDisable(!action.isHasIcon());
}
buttonBar.setVisible(true);
vbox.setVisible(true);
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
if(action.getActionType() == ActionType.NORMAL)
{
if(action.isInvalid())
setActionHeadingLabelText("Invalid Action ("+action.getModuleName()+")");
else
setActionHeadingLabelText(action.getName());
}
else if(action.getActionType() == ActionType.COMBINE)
setActionHeadingLabelText("Combine Action");
else if(action.getActionType() == ActionType.FOLDER)
setActionHeadingLabelText("Folder Action");
if(!action.isInvalid())
{
if(action.getActionType() == ActionType.NORMAL)
renderClientProperties();
else if(action.getActionType() == ActionType.COMBINE)
renderCombineActionProperties();
}
}
private CombineActionPropertiesPane combineActionPropertiesPane;
public CombineActionPropertiesPane getCombineActionPropertiesPane() {
return combineActionPropertiesPane;
}
public void setReturnButtonForCombineActionChildVisible(boolean visible)
{
returnButtonForCombineActionChild.setVisible(visible);
}
public void renderCombineActionProperties()
{
try {
logger.info("@@@@@ : "+action.getClientProperties().getSize());
combineActionPropertiesPane = new CombineActionPropertiesPane(getActionAsCombineAction(action),
getClientProfile(),
this
);
clientPropertiesVBox.getChildren().add(combineActionPropertiesPane);
}
catch (MinorException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleMinorException(e);
}
}
public void setAction(Action action) {
this.action = action;
}
public FolderAction getActionAsFolderAction(Action action)
{
FolderAction folderAction = new FolderAction();
folderAction.setDisplayText(action.getDisplayText());
folderAction.setName(action.getName());
folderAction.setID(action.getID());
folderAction.setLocation(action.getLocation());
folderAction.setBgColourHex(action.getBgColourHex());
folderAction.setParent(action.getParent());
folderAction.getClientProperties().set(action.getClientProperties());
folderAction.setDisplayTextAlignment(action.getDisplayTextAlignment());
folderAction.setShowIcon(action.isShowIcon());
folderAction.setHasIcon(action.isHasIcon());
if(folderAction.isHasIcon())
folderAction.setIcon(action.getIconAsByteArray());
folderAction.setDisplayTextFontColourHex(action.getDisplayTextFontColourHex());
return folderAction;
}
public CombineAction getActionAsCombineAction(Action action)
{
CombineAction combineAction = new CombineAction();
combineAction.setDisplayText(action.getDisplayText());
combineAction.setName(action.getName());
combineAction.setID(action.getID());
combineAction.setLocation(action.getLocation());
combineAction.setBgColourHex(action.getBgColourHex());
combineAction.setParent(action.getParent());
combineAction.getClientProperties().set(action.getClientProperties());
combineAction.setDisplayTextAlignment(action.getDisplayTextAlignment());
combineAction.setShowIcon(action.isShowIcon());
combineAction.setHasIcon(action.isHasIcon());
if(combineAction.isHasIcon())
combineAction.setIcon(action.getIconAsByteArray());
combineAction.setDisplayTextFontColourHex(action.getDisplayTextFontColourHex());
for(Property prop : combineAction.getClientProperties().get())
{
System.out.println("PROP : "+prop.getName()+","+prop.getRawValue());
}
return combineAction;
}
public void onOpenFolderButtonClicked()
{
FolderAction folderAction = getActionAsFolderAction(action);
actionBox.getActionGridPaneListener().renderFolder(folderAction);
clear();
}
@Override
public Window getCurrentWindow() {
return getScene().getWindow();
}
private ArrayList<UIPropertyBox> actionClientProperties;
public void renderClientProperties() throws MinorException
{
for(int i =0;i< action.getClientProperties().getSize(); i++)
{
Property eachProperty = action.getClientProperties().get().get(i);
if(!eachProperty.isVisible())
continue;
Label label = new Label(eachProperty.getDisplayName());
HBox hBox = new HBox(label);
hBox.setSpacing(5.0);
hBox.setAlignment(Pos.CENTER_LEFT);
Node controlNode = null;
if(eachProperty.getHelpLink() != null)
{
Button helpButton = new Button();
FontIcon questionIcon = new FontIcon("fas-question");
helpButton.setGraphic(questionIcon);
helpButton.setOnAction(event -> {
hostServices.showDocument(eachProperty.getHelpLink());
});
hBox.getChildren().add(helpButton);
}
hBox.getChildren().add(new SpaceFiller(SpaceFiller.FillerType.HBox));
if(eachProperty.getControlType() == ControlType.COMBO_BOX)
{
ComboBox<String> comboBox = new ComboBox<>();
comboBox.getItems().addAll(eachProperty.getListValue());
comboBox.getSelectionModel().select(eachProperty.getSelectedIndex());
controlNode = comboBox;
}
else if(eachProperty.getControlType() == ControlType.TEXT_FIELD)
{
TextField textField = new TextField(eachProperty.getRawValue());
controlNode= textField;
}
else if(eachProperty.getControlType() == ControlType.TOGGLE)
{
ToggleButton toggleButton = new ToggleButton();
toggleButton.setSelected(eachProperty.getBoolValue());
if(eachProperty.getBoolValue())
toggleButton.setText("ON");
else
toggleButton.setText("OFF");
toggleButton.selectedProperty().addListener((observableValue, aBoolean, t1) -> {
if(t1)
toggleButton.setText("ON");
else
toggleButton.setText("OFF");
});
controlNode = toggleButton;
}
else if(eachProperty.getControlType() == ControlType.SLIDER_DOUBLE)
{
Slider slider = new Slider();
slider.setValue(eachProperty.getDoubleValue());
slider.setMax(eachProperty.getMaxDoubleValue());
slider.setMin(eachProperty.getMinDoubleValue());
controlNode = slider;
}
else if(eachProperty.getControlType() == ControlType.SLIDER_INTEGER)
{
Slider slider = new Slider();
slider.setValue(eachProperty.getIntValue());
slider.setMax(eachProperty.getMaxIntValue());
slider.setMin(eachProperty.getMinIntValue());
slider.setBlockIncrement(1.0);
slider.setSnapToTicks(true);
controlNode = slider;
}
hBox.getChildren().add(controlNode);
UIPropertyBox clientProperty = new UIPropertyBox(i, eachProperty.getDisplayName(), controlNode,
eachProperty.getControlType(), eachProperty.getType(), eachProperty.isCanBeBlank());
actionClientProperties.add(clientProperty);
clientPropertiesVBox.getChildren().add(hBox);
}
}
public void onSaveButtonClicked()
{
try {
// saveButton.setDisable(true);
// deleteButton.setDisable(true);
validateForm();
saveAction();
}
catch (MinorException e)
{
exceptionAndAlertHandler.handleMinorException(e);
}
}
private boolean sendIcon = false;
@Override
public void setSendIcon(boolean sendIcon)
{
this.sendIcon = sendIcon;
}
public void addActionToCurrentClientProfile(Action newAction) throws CloneNotSupportedException {
getClientProfile().addAction(newAction);
}
@Override
public void saveAction(Action action, boolean runAsync)
{
new OnSaveActionTask(
ClientConnections.getInstance().getClientConnectionBySocketAddress(
getClient().getRemoteSocketAddress()
),
action,
displayNameTextField.getText(),
isCombineChild(),
!hideDisplayTextCheckBox.isSelected(),
displayTextColourDefaultCheckBox.isSelected(),
"#" + displayTextColourPicker.getValue().toString().substring(2),
clearIconButton.isDisable(),
!hideIconCheckBox.isSelected(),
displayTextAlignmentComboBox.getSelectionModel().getSelectedItem(),
actionBackgroundColourTransparentCheckBox.isSelected(),
"#" + actionBackgroundColourPicker.getValue().toString().substring(2),
getCombineActionPropertiesPane(),
clientProfile, sendIcon, actionBox, actionClientProperties, exceptionAndAlertHandler,
saveButton, deleteButton, runAsync
);
}
@Override
public void saveAction()
{
saveAction(action, true);
}
public void setFolderButtonVisible(boolean visible)
{
openFolderButton.setVisible(visible);
}
public void validateForm() throws MinorException
{
String displayNameStr = displayNameTextField.getText();
StringBuilder finalErrors = new StringBuilder();
if(displayNameStr.isBlank())
{
finalErrors.append(" * Display Name cannot be blank\n");
}
if(!isCombineChild())
{
if(action.isHasIcon())
{
if(hideDisplayTextCheckBox.isSelected() && hideIconCheckBox.isSelected())
finalErrors.append(" * Both Icon and display text check box cannot be hidden.\n");
}
else
{
if(hideDisplayTextCheckBox.isSelected())
finalErrors.append(" * Display Text cannot be hidden, since there is also no icon.\n");
}
}
for (UIPropertyBox clientProperty : actionClientProperties) {
Node controlNode = clientProperty.getControlNode();
if (clientProperty.getControlType() == ControlType.TEXT_FIELD)
{
String value = ((TextField) controlNode).getText();
if(clientProperty.getType() == Type.INTEGER)
{
try
{
Integer.parseInt(value);
}
catch (NumberFormatException e)
{
finalErrors.append(" -> ").append(clientProperty.getDisplayName()).append(" must be integer.\n");
}
}
else
{
if(value.isBlank() && !clientProperty.isCanBeBlank())
finalErrors.append(" -> ").append(clientProperty.getDisplayName()).append(" cannot be blank.\n");
}
}
}
if(!finalErrors.toString().isEmpty())
{
throw new MinorException("You made mistakes",
finalErrors.toString());
}
}
public void onDeleteButtonClicked()
{
new OnDeleteActionTask(
ClientConnections.getInstance().getClientConnectionBySocketAddress(
getClient().getRemoteSocketAddress()
),
action,
isCombineChild(),
getCombineActionPropertiesPane(),
clientProfile, actionBox, this, exceptionAndAlertHandler,
!isCombineChild
);
}
}
D
src/main/java/com/StreamPi/Server/Window/Dashboard/ActionsDetailPane/ActionDetailsPaneListener.java
+0
−26
package com.StreamPi.Server.Window.Dashboard.ActionsDetailPane;
import com.StreamPi.ActionAPI.Action.Action;
import com.StreamPi.ActionAPI.Action.ActionType;
import com.StreamPi.ActionAPI.Action.Location;
import com.StreamPi.ActionAPI.OtherActions.FolderAction;
import com.StreamPi.Server.Window.Dashboard.ActionGridPane.ActionBox;
import com.StreamPi.Util.Exception.MinorException;
import javafx.stage.Window;
public interface ActionDetailsPaneListener {
void onActionClicked(Action action, ActionBox actionBox) throws MinorException;
void saveAction();
void saveAction(Action action, boolean runAsync);
void clear();
void setSendIcon(boolean sendIcon);
void onOpenFolderButtonClicked();
Window getCurrentWindow();
}
package com.StreamPi.Server.Window.Dashboard.ActionsDetailPane;
import com.StreamPi.ActionAPI.Action.Action;
import com.StreamPi.ActionAPI.Action.ActionType;
import com.StreamPi.ActionAPI.Action.Location;
import com.StreamPi.ActionAPI.ActionProperty.Property.Property;
import com.StreamPi.ActionAPI.ActionProperty.Property.Type;
import com.StreamPi.ActionAPI.OtherActions.CombineAction;
import com.StreamPi.Server.Client.ClientProfile;
import com.StreamPi.Util.Exception.MinorException;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class CombineActionPropertiesPane extends VBox {
private CombineAction combineAction;
private ClientProfile clientProfile;
private ActionDetailsPane actionDetailsPane;
public CombineAction getCombineAction() {
return combineAction;
}
public CombineActionPropertiesPane(CombineAction combineAction, ClientProfile clientProfile, ActionDetailsPane actionDetailsPane) throws MinorException {
this.combineAction = combineAction;
this.clientProfile = clientProfile;
this.actionDetailsPane = actionDetailsPane;
setSpacing(10.0);
setPadding(new Insets(0,0,10,0));
renderProps();
}
public void renderProps() throws MinorException {
getChildren().clear();
int i = 0;
for(String actionID : combineAction.getChildrenIDSequential())
{
Action action = clientProfile.getActionByID(actionID);
System.out.println("232323xxxxxxxxxxxx : "+action.getID());
Button settingsButton = new Button();
FontIcon settingsFontIcon = new FontIcon("fas-cog");
settingsButton.setGraphic(settingsFontIcon);
settingsButton.setOnAction(event -> onSettingsButtonClicked(action));
Button upButton = new Button();
FontIcon upButtonFontIcon = new FontIcon("fas-chevron-up");
upButton.setGraphic(upButtonFontIcon);
Button downButton = new Button();
FontIcon downButtonFontIcon = new FontIcon("fas-chevron-down");
downButton.setGraphic(downButtonFontIcon);
Label displayTextLabel = new Label(action.getDisplayText());
displayTextLabel.setId(action.getID());
Region r = new Region();
HBox.setHgrow(r, Priority.ALWAYS);
HBox actionHBox = new HBox(displayTextLabel, r, settingsButton, upButton, downButton);
actionHBox.setSpacing(5.0);
upButton.setUserData(i);
downButton.setUserData(i);
upButton.setOnAction(this::onUpButtonClicked);
downButton.setOnAction(this::onDownButtonClicked);
System.out.println("67878678678678");
getChildren().add(actionHBox);
System.out.println("9mhmmn");
i++;
}
}
public void onSettingsButtonClicked(Action action)
{
actionDetailsPane.clear();
actionDetailsPane.setAction(action);
try {
actionDetailsPane.renderActionProperties();
} catch (MinorException e) {
e.printStackTrace();
}
}
public void onUpButtonClicked(ActionEvent event) {
try {
Node node = (Node) event.getSource();
int currentIndex = (int) node.getUserData();
if(currentIndex > 0)
{
Property current = combineAction.getClientProperties().getSingleProperty(currentIndex+"");
Property aboveOne = combineAction.getClientProperties().getSingleProperty((currentIndex-1)+"");
combineAction.addChild(current.getRawValue(), currentIndex-1);
combineAction.addChild(aboveOne.getRawValue(), currentIndex);
actionDetailsPane.saveAction();
renderProps();
}
}
catch (MinorException e)
{
e.printStackTrace();
}
}
public void onDownButtonClicked(ActionEvent event)
{
try {
Node node = (Node) event.getSource();
int currentIndex = (int) node.getUserData();
if(currentIndex < getChildren().size() - 1)
{
Property current = combineAction.getClientProperties().getSingleProperty(currentIndex+"");
Property belowOne = combineAction.getClientProperties().getSingleProperty((currentIndex+1)+"");
combineAction.addChild(current.getRawValue(), currentIndex+1);
combineAction.addChild(belowOne.getRawValue(), currentIndex);
actionDetailsPane.saveAction();
renderProps();
}
}
catch (MinorException e)
{
e.printStackTrace();
}
}
public List<String> getFinalChildren()
{
ArrayList<String> children = new ArrayList<>();
for(int i = 0;i<getChildren().size();i++)
{
HBox hBox = (HBox) getChildren().get(i);
Label label = (Label) hBox.getChildren().get(0);
children.add(label.getId());
}
return children;
}
}
package com.StreamPi.Server.Window.Dashboard.ActionsDetailPane;
import java.util.logging.Logger;
import com.StreamPi.ActionAPI.Action.Action;
import com.StreamPi.ActionAPI.ActionProperty.Property.Property;
import com.StreamPi.Server.Client.ClientProfile;
import com.StreamPi.Server.Connection.ClientConnection;
import com.StreamPi.Server.Window.ExceptionAndAlertHandler;
import com.StreamPi.Server.Window.Dashboard.ActionGridPane.ActionBox;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.Exception.SevereException;
import javafx.application.Platform;
import javafx.concurrent.Task;
public class OnDeleteActionTask extends Task<Void>
{
private Logger logger;
public OnDeleteActionTask(ClientConnection connection, Action action, boolean isCombineChild,
CombineActionPropertiesPane combineActionPropertiesPane, ClientProfile clientProfile, ActionBox actionBox,
ActionDetailsPane actionDetailsPane,
ExceptionAndAlertHandler exceptionAndAlertHandler, boolean runAsync)
{
this.connection = connection;
this.action = action;
this.isCombineChild = isCombineChild;
this.combineActionPropertiesPane = combineActionPropertiesPane;
this.clientProfile = clientProfile;
this.actionBox = actionBox;
this.actionDetailsPane = actionDetailsPane;
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
logger = Logger.getLogger(getClass().getName());
if(runAsync)
new Thread(this).start();
else
runTask();
}
private ClientConnection connection;
private Action action;
private ActionBox actionBox;
private ActionDetailsPane actionDetailsPane;
private boolean isCombineChild;
private ClientProfile clientProfile;
private CombineActionPropertiesPane combineActionPropertiesPane;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
private void runTask()
{
try {
connection.deleteAction(clientProfile.getID(), action.getID());
clientProfile.removeActionByID(action.getID());
if(isCombineChild)
{
System.out.println("ACTION ID TO BE REMOVED : "+action.getID());
combineActionPropertiesPane.getCombineAction().removeChild(action.getID());
System.out.println("222155 "+combineActionPropertiesPane.getCombineAction().getClientProperties().getSize());
combineActionPropertiesPane.renderProps();
try {
actionDetailsPane.saveAction(combineActionPropertiesPane.getCombineAction(), false);
System.out.println(combineActionPropertiesPane.getCombineAction().getDisplayText()+"@#@#@#@#@#@#");
actionDetailsPane.onActionClicked(
combineActionPropertiesPane.getCombineAction(),
actionBox
);
} catch (MinorException e) {
e.printStackTrace();
}
}
else
{
Platform.runLater(()->{
actionBox.clear();
actionDetailsPane.clear();
});
}
// Platform.runLater(()->{
// saveButton.setDisable(false);
// deleteButton.setDisable(false);
// });
}
catch (SevereException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleSevereException(e);
}
catch (MinorException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleMinorException(e);
}
}
@Override
protected Void call() throws Exception
{
runTask();
return null;
}
}
package com.StreamPi.Server.Window.Dashboard.ActionsDetailPane;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;
import com.StreamPi.ActionAPI.Action.Action;
import com.StreamPi.ActionAPI.Action.ActionType;
import com.StreamPi.ActionAPI.Action.DisplayTextAlignment;
import com.StreamPi.ActionAPI.ActionProperty.ClientProperties;
import com.StreamPi.ActionAPI.ActionProperty.Property.Property;
import com.StreamPi.ActionAPI.ActionProperty.Property.Type;
import com.StreamPi.Server.Client.ClientProfile;
import com.StreamPi.Server.Connection.ClientConnection;
import com.StreamPi.Server.UIPropertyBox.UIPropertyBox;
import com.StreamPi.Server.Window.ExceptionAndAlertHandler;
import com.StreamPi.Server.Window.Dashboard.ActionGridPane.ActionBox;
import com.StreamPi.Util.Exception.SevereException;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.control.Button;
public class OnSaveActionTask extends Task<Void>
{
private Logger logger;
public OnSaveActionTask(ClientConnection connection, Action action, String displayNameText, boolean isCombineChild,
boolean isShowDisplayText, boolean isDefaultDisplayTextColour, String displayTextFontColour, boolean isClearIcon,
boolean isHideIcon, DisplayTextAlignment displayTextAlignment, boolean isTransparentBackground, String backgroundColour,
CombineActionPropertiesPane combineActionPropertiesPane, ClientProfile clientProfile, boolean sendIcon, ActionBox actionBox,
ArrayList<UIPropertyBox> actionClientProperties, ExceptionAndAlertHandler exceptionAndAlertHandler, Button saveButton, Button deleteButton,
boolean runAsync)
{
this.saveButton = saveButton;
this.deleteButton = deleteButton;
this.connection = connection;
this.action = action;
this.displayNameText = displayNameText;
this.isCombineChild = isCombineChild;
this.isShowDisplayText = isShowDisplayText;
this.isDefaultDisplayTextColour = isDefaultDisplayTextColour;
this.displayTextFontColour = displayTextFontColour;
this.isClearIcon = isClearIcon;
this.isHideIcon = isHideIcon;
this.displayTextAlignment = displayTextAlignment;
this.isTransparentBackground = isTransparentBackground;
this.combineActionPropertiesPane = combineActionPropertiesPane;
this.clientProfile = clientProfile;
this.sendIcon = sendIcon;
this.actionBox = actionBox;
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
this.backgroundColour = backgroundColour;
this.actionClientProperties = actionClientProperties;
logger = Logger.getLogger(getClass().getName());
if(runAsync)
new Thread(this).start();
else
runTask();
}
private Button saveButton;
private Button deleteButton;
private boolean isShowDisplayText;
private boolean isCombineChild;
private String displayNameText;
private boolean isDefaultDisplayTextColour;
private ArrayList<UIPropertyBox> actionClientProperties;
private String displayTextFontColour;
private boolean isClearIcon;
private boolean isHideIcon;
private DisplayTextAlignment displayTextAlignment;
private boolean isTransparentBackground;
private String backgroundColour;
private CombineActionPropertiesPane combineActionPropertiesPane;
private ClientProfile clientProfile;
private boolean sendIcon;
private ActionBox actionBox;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
private Action action;
private ClientConnection connection;
private void setSaveDeleteButtonState(boolean state)
{
Platform.runLater(()->{
saveButton.setDisable(state);
deleteButton.setDisable(state);
});
}
private void runTask()
{
action.setDisplayText(displayNameText);
if(!isCombineChild)
{
setSaveDeleteButtonState(true);
action.setShowDisplayText(isShowDisplayText);
if(isDefaultDisplayTextColour)
action.setDisplayTextFontColourHex("");
else
{
action.setDisplayTextFontColourHex(displayTextFontColour);
//String fontColour = "#" + displayTextColourPicker.getValue().toString().substring(2);
//action.setDisplayTextFontColourHex(fontColour);
}
if(isClearIcon)
{
action.setIcon(null);
action.setHasIcon(false);
action.setShowIcon(false);
}
if(action.isHasIcon())
action.setShowIcon(isHideIcon);
action.setDisplayTextAlignment(displayTextAlignment);
logger.info("BBBGGG : "+backgroundColour);
if(isTransparentBackground)
action.setBgColourHex("");
else
{
//String bgColour = "#" + actionBackgroundColourPicker.getValue().toString().substring(2);
action.setBgColourHex(backgroundColour);
}
}
System.out.println("parent : "+action.getParent());
if(action.getActionType() == ActionType.COMBINE)
{
List<String> finalChildren = combineActionPropertiesPane.getFinalChildren();
System.out.println("2334 "+finalChildren.size());
ClientProperties clientProperties = new ClientProperties();
for(int i = 0;i<finalChildren.size();i++)
{
Property property = new Property(i+"", Type.STRING);
property.setRawValue(finalChildren.get(i));
clientProperties.addProperty(property);
}
action.getClientProperties().set(clientProperties);
}
else
{
//properties
for (UIPropertyBox clientProperty : actionClientProperties) {
action.getClientProperties().get().get(clientProperty.getIndex()).setRawValue(clientProperty.getRawValue());
}
}
try
{
logger.info("Saving action ... "+action.isHasIcon()+"+"+sendIcon);
/*if(action.isHasIcon())
{
if(clientProfile.getActionByID(action.getID()).getIconAsByteArray() == null)
{
sendIcon = true;
}
else
{
if(!Arrays.equals(action.getIconAsByteArray(), clientProfile.getActionByID(action.getID()).getIconAsByteArray()))
{
logger.info("Sending ...");
sendIcon = true;
}
}
}*/
connection.saveActionDetails(clientProfile.getID(), action);
if(sendIcon)
{
connection.sendIcon(clientProfile.getID(), action.getID(), action.getIconAsByteArray());
}
if(!isCombineChild)
{
Platform.runLater(()->{
actionBox.clear();
actionBox.setAction(action);
actionBox.baseInit();
actionBox.init();
});
setSaveDeleteButtonState(false);
}
clientProfile.removeActionByID(action.getID());
clientProfile.addAction(action);
}
catch (SevereException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleSevereException(e);
}
catch (CloneNotSupportedException e)
{
e.printStackTrace();
}
}
@Override
protected Void call() throws Exception
{
runTask();
return null;
}
}
package com.StreamPi.Server.Window.Dashboard;
import com.StreamPi.Server.Client.Client;
import com.StreamPi.Server.Client.ClientProfile;
import com.StreamPi.Server.Connection.ClientConnection;
import com.StreamPi.Server.Connection.ClientConnections;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.scene.CacheHint;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Paint;
import javafx.util.Callback;
import org.kordamp.ikonli.javafx.FontIcon;
public class ClientDetailsPane extends HBox {
private DashboardInterface dashboard;
public ClientDetailsPane(DashboardInterface dashboard)
{
this.dashboard = dashboard;
VBox.setVgrow(this, Priority.NEVER);
getStyleClass().add("client_details_pane");
setPadding(new Insets(10));
setMinHeight(90);
initUI();
loadData();
setCache(true);
setCacheHint(CacheHint.SPEED);
}
private ComboBox<ClientConnection> clientsComboBox;
private Label noClientsConnectedLabel;
private ComboBox<ClientProfile> clientProfilesComboBox;
public void initUI()
{
noClientsConnectedLabel = new Label("No Clients Connected");
noClientsConnectedLabel.managedProperty().bind(noClientsConnectedLabel.visibleProperty());
clientsComboBox = new ComboBox<>();
clientsComboBox.setPromptText("Choose Client");
clientsComboBox.valueProperty().addListener((observableValue, oldVal, newVal) -> {
if(oldVal!=newVal && newVal!=null)
{
dashboard.newSelectedClientConnection(newVal);
clientProfilesComboBox.setItems(FXCollections.observableArrayList(newVal.getClient().getAllClientProfiles()));
clientProfilesComboBox.setVisible(true);
}
});
clientsComboBox.managedProperty().bind(clientsComboBox.visibleProperty());
Callback<ListView<ClientConnection>, ListCell<ClientConnection>> clientsComboBoxFactory = new Callback<>() {
@Override
public ListCell<ClientConnection> call(ListView<ClientConnection> clientConnectionListView) {
return new ListCell<>() {
@Override
protected void updateItem(ClientConnection clientConnection, boolean b) {
super.updateItem(clientConnection, b);
if(clientConnection == null)
{
setText("Choose Client");
}
else
{
Client client = clientConnection.getClient();
setText(client.getNickName());
}
}
};
}
};
clientsComboBox.setCellFactory(clientsComboBoxFactory);
clientsComboBox.setButtonCell(clientsComboBoxFactory.call(null));
clientProfilesComboBox = new ComboBox<>();
clientProfilesComboBox.setPromptText("Choose Profile");
clientProfilesComboBox.valueProperty().addListener((observableValue, oldVal, newVal) -> {
if(oldVal!=newVal && newVal!=null)
{
dashboard.newSelectedClientProfile(newVal);
}
});
clientProfilesComboBox.managedProperty().bind(clientProfilesComboBox.visibleProperty());
Callback<ListView<ClientProfile>, ListCell<ClientProfile>> clientProfilesComboBoxFactory = new Callback<ListView<ClientProfile>, ListCell<ClientProfile>>() {
@Override
public ListCell<ClientProfile> call(ListView<ClientProfile> clientProfileListView) {
return new ListCell<>()
{
@Override
protected void updateItem(ClientProfile profile, boolean b) {
super.updateItem(profile, b);
if(profile == null)
{
setText("Choose Profile");
}
else
{
setText(profile.getName());
}
}
};
}
};
clientProfilesComboBox.setCellFactory(clientProfilesComboBoxFactory);
clientProfilesComboBox.setButtonCell(clientProfilesComboBoxFactory.call(null));
VBox stack = new VBox(noClientsConnectedLabel, clientsComboBox, clientProfilesComboBox);
stack.setSpacing(10);
getChildren().addAll(stack);
}
private void loadData()
{
clientsComboBox.getSelectionModel().clearSelection();
clientProfilesComboBox.getSelectionModel().clearSelection();
if(ClientConnections.getInstance().getConnections().size() == 0)
{
noClientsConnectedLabel.setVisible(true);
clientsComboBox.setVisible(false);
clientProfilesComboBox.setVisible(false);
dashboard.newSelectedClientConnection(null);
}
else
{
noClientsConnectedLabel.setVisible(false);
clientsComboBox.setVisible(true);
clientProfilesComboBox.setVisible(false);
clientsComboBox.setItems(FXCollections.observableArrayList(ClientConnections.getInstance().getConnections()));
}
}
public void refresh()
{
loadData();
}
}
package com.StreamPi.Server.Window.Dashboard;
import java.util.logging.Logger;
import com.StreamPi.ActionAPI.Action.Action;
import com.StreamPi.Server.Client.ClientProfile;
import com.StreamPi.Server.Connection.ClientConnection;
import com.StreamPi.Server.Window.Dashboard.ActionGridPane.ActionGridPane;
import com.StreamPi.Server.Window.Dashboard.ActionsDetailPane.ActionDetailsPane;
import com.StreamPi.Server.Window.ExceptionAndAlertHandler;
import com.StreamPi.Util.Exception.SevereException;
import javafx.application.HostServices;
import javafx.scene.CacheHint;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
public class DashboardBase extends HBox implements DashboardInterface {
private final VBox leftPane;
private Logger logger;
public ClientProfile currentClientProfile;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
public DashboardBase(ExceptionAndAlertHandler exceptionAndAlertHandler, HostServices hostServices)
{
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
logger = Logger.getLogger(DashboardBase.class.getName());
leftPane = new VBox();
HBox.setHgrow(leftPane, Priority.ALWAYS);
getChildren().add(leftPane);
setPluginsPane(new PluginsPane(hostServices));
setClientDetailsPane(new ClientDetailsPane(this));
setActionGridPane(new ActionGridPane(exceptionAndAlertHandler));
setActionDetailsPane(new ActionDetailsPane(exceptionAndAlertHandler, hostServices));
getActionGridPane().setActionDetailsPaneListener(getActionDetailsPane());
}
private PluginsPane pluginsPane;
private void setPluginsPane(PluginsPane pluginsPane)
{
this.pluginsPane = pluginsPane;
getChildren().add(this.pluginsPane);
}
public PluginsPane getPluginsPane()
{
return pluginsPane;
}
private ClientDetailsPane clientDetailsPane;
private void setClientDetailsPane(ClientDetailsPane clientDetailsPane)
{
this.clientDetailsPane = clientDetailsPane;
leftPane.getChildren().add(this.clientDetailsPane);
}
public ClientDetailsPane getClientDetailsPane()
{
return clientDetailsPane;
}
private ActionGridPane actionGridPane;
private void setActionGridPane(ActionGridPane actionGridPane)
{
this.actionGridPane = actionGridPane;
leftPane.getChildren().add(this.actionGridPane);
}
public ActionGridPane getActionGridPane()
{
return actionGridPane;
}
private ActionDetailsPane actionDetailsPane;
private void setActionDetailsPane(ActionDetailsPane actionDetailsPane)
{
this.actionDetailsPane = actionDetailsPane;
leftPane.getChildren().add(this.actionDetailsPane);
}
public ActionDetailsPane getActionDetailsPane()
{
return actionDetailsPane;
}
public void newSelectedClientConnection(ClientConnection clientConnection)
{
if(clientConnection == null)
{
logger.info("Remove action grid");
}
else
{
getActionDetailsPane().setClient(clientConnection.getClient());
getActionGridPane().setClient(clientConnection.getClient());
}
}
public void newSelectedClientProfile(ClientProfile clientProfile)
{
this.currentClientProfile = clientProfile;
getActionDetailsPane().setClientProfile(clientProfile);
drawProfile(this.currentClientProfile);
}
public void drawProfile(ClientProfile clientProfile)
{
logger.info("Drawing ...");
getActionGridPane().setClientProfile(clientProfile);
try {
getActionGridPane().renderGrid();
getActionGridPane().renderActions();
}
catch (SevereException e)
{
exceptionAndAlertHandler.handleSevereException(e);
}
}
}
package com.StreamPi.Server.Window.Dashboard;
import com.StreamPi.Server.Client.ClientProfile;
import com.StreamPi.Server.Connection.ClientConnection;
public interface DashboardInterface {
void newSelectedClientConnection(ClientConnection clientConnection);
void newSelectedClientProfile(ClientProfile clientProfile);
}
package com.StreamPi.Server.Window.Dashboard;
import com.StreamPi.Server.IO.Config;
import com.StreamPi.Server.Window.ExceptionAndAlertHandler;
import com.StreamPi.Util.Alert.StreamPiAlert;
import com.StreamPi.Util.Alert.StreamPiAlertListener;
import com.StreamPi.Util.Alert.StreamPiAlertType;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.Exception.SevereException;
import javafx.application.HostServices;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
public class DonatePopupContent{
public DonatePopupContent(HostServices hostServices, ExceptionAndAlertHandler exceptionAndAlertHandler)
{
Label label = new Label("We are a very small team working very hard on this project for best user experience.\n\n" +
"Something like StreamPi takes time, effort and resources. But we will always keep this 100% opensource and free.\n\n" +
"If you find this project helpful, and would want to help us, please consider donating :)\n\n"+
"If you are unable to do so even a small shoutout and share across social media would be very helpful.");
label.setWrapText(true);
label.getStyleClass().add("donate_request_popup_label");
Hyperlink patreonLink = new Hyperlink("Our Patreon");
patreonLink.setOnAction(event ->{
hostServices.showDocument("https://patreon.com/streampi");
});
patreonLink.getStyleClass().add("donate_request_popup_patreon_link");
VBox pane = new VBox(label, patreonLink);
pane.setSpacing(5.0);
streamPiAlert = new StreamPiAlert("Hey!", StreamPiAlertType.INFORMATION, pane);
streamPiAlert.setOnClicked(new StreamPiAlertListener()
{
@Override
public void onClick(String buttonClicked)
{
try
{
Config.getInstance().setAllowDonatePopup(false);
Config.getInstance().save();
}
catch(SevereException e)
{
exceptionAndAlertHandler.handleSevereException(e);
}
}
});
}
private StreamPiAlert streamPiAlert;
public void show()
{
streamPiAlert.show();
}
}
package com.StreamPi.Server.Window.Dashboard;
import com.StreamPi.ActionAPI.Action.Action;
import com.StreamPi.ActionAPI.Action.ActionType;
import com.StreamPi.ActionAPI.Action.DisplayTextAlignment;
import com.StreamPi.ActionAPI.ActionProperty.Property.Property;
import com.StreamPi.ActionAPI.ActionProperty.Property.Type;
import com.StreamPi.ActionAPI.NormalAction.NormalAction;
import com.StreamPi.ActionAPI.OtherActions.CombineAction;
import com.StreamPi.ActionAPI.OtherActions.FolderAction;
import com.StreamPi.Server.Action.NormalActionPlugins;
import javafx.application.HostServices;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.CacheHint;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.*;
import javafx.scene.paint.Paint;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.ArrayList;
import java.util.HashMap;
public class PluginsPane extends VBox {
private Button settingsButton;
public PluginsPane(HostServices hostServices)
{
setMinWidth(250);
getStyleClass().add("plugins_pane");
setPadding(new Insets(10));
setSpacing(10.0);
this.hostServices = hostServices;
initUI();
}
private Accordion pluginsAccordion;
public void initUI()
{
pluginsAccordion = new Accordion();
pluginsAccordion.setCache(true);
Region r = new Region();
VBox.setVgrow(r, Priority.ALWAYS);
settingsButton = new Button();
FontIcon cog = new FontIcon("fas-cog");
settingsButton.setGraphic(cog);
HBox settingsHBox = new HBox(settingsButton);
settingsHBox.setAlignment(Pos.CENTER_RIGHT);
getChildren().addAll(new Label("Plugins"), pluginsAccordion, r, settingsHBox);
}
public Button getSettingsButton()
{
return settingsButton;
}
public void clearData()
{
pluginsAccordion.getPanes().clear();
}
public void loadData()
{
HashMap<String, ArrayList<NormalAction>> sortedPlugins = NormalActionPlugins.getInstance().getSortedPlugins();
for(String eachCategory : sortedPlugins.keySet())
{
VBox vBox = new VBox();
vBox.setSpacing(5);
TitledPane pane = new TitledPane(eachCategory, vBox);
for(NormalAction eachAction : sortedPlugins.get(eachCategory))
{
if(!eachAction.isVisibleInPluginsPane())
continue;
Button eachNormalActionPluginButton = new Button();
HBox.setHgrow(eachNormalActionPluginButton, Priority.ALWAYS);
eachNormalActionPluginButton.setMaxWidth(Double.MAX_VALUE);
eachNormalActionPluginButton.setAlignment(Pos.CENTER_LEFT);
Node graphic = eachAction.getServerButtonGraphic();
if(graphic == null)
{
FontIcon cogs = new FontIcon("fas-cogs");
cogs.getStyleClass().add("dashboard_plugins_pane_action_icon");
eachNormalActionPluginButton.setGraphic(cogs);
}
else
{
if(graphic instanceof FontIcon)
{
FontIcon fi = (FontIcon) graphic;
eachNormalActionPluginButton.setGraphic(fi);
}
else if(graphic instanceof ImageView)
{
ImageView iv = (ImageView) graphic;
iv.getStyleClass().add("dashboard_plugins_pane_action_icon_imageview");
iv.setPreserveRatio(false);
eachNormalActionPluginButton.setGraphic(iv);
}
}
eachNormalActionPluginButton.setText(eachAction.getName());
eachNormalActionPluginButton.setOnDragDetected(mouseEvent -> {
Dragboard db = eachNormalActionPluginButton.startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
content.put(Action.getDataFormat(), createFakeAction(eachAction, "Untitled Action"));
db.setContent(content);
mouseEvent.consume();
});
HBox hBox = new HBox(eachNormalActionPluginButton);
hBox.setSpacing(5.0);
hBox.setAlignment(Pos.TOP_LEFT);
HBox.setHgrow(eachNormalActionPluginButton, Priority.ALWAYS);
if(eachAction.getHelpLink() != null) {
Button helpButton = new Button();
FontIcon questionIcon = new FontIcon("fas-question");
questionIcon.getStyleClass().add("dashboard_plugins_pane_action_help_icon");
helpButton.setGraphic(questionIcon);
helpButton.setOnAction(event -> hostServices.showDocument(eachAction.getHelpLink()));
hBox.getChildren().add(helpButton);
}
vBox.getChildren().add(hBox);
}
if(vBox.getChildren().size() > 0)
pluginsAccordion.getPanes().add(pane);
}
}
private HostServices hostServices;
public Action createFakeAction(Action action, String displayText)
{
Action newAction = new Action(action.getActionType());
if(action.getActionType() == ActionType.NORMAL)
{
newAction.setModuleName(action.getModuleName());
newAction.setVersion(action.getVersion());
newAction.setName(action.getName());
}
newAction.setClientProperties(action.getClientProperties());
for(Property property : newAction.getClientProperties().get())
{
if(property.getType() == Type.STRING || property.getType() == Type.INTEGER || property.getType() == Type.DOUBLE)
property.setRawValue(property.getDefaultRawValue());
}
// newAction.setLocation(location);
newAction.setIDRandom();
newAction.setShowDisplayText(true);
newAction.setDisplayText(displayText);
newAction.setDisplayTextAlignment(DisplayTextAlignment.CENTER);
newAction.setShowIcon(false);
newAction.setHasIcon(false);
//action.setParent(root);
newAction.setBgColourHex("");
newAction.setDisplayTextFontColourHex("");
return newAction;
}
public void loadOtherActions()
{
VBox vBox = new VBox();
Button folderActionButton = new Button("Folder");
folderActionButton.setMaxWidth(Double.MAX_VALUE);
folderActionButton.setAlignment(Pos.CENTER_LEFT);
FontIcon folder = new FontIcon("fas-folder");
folderActionButton.setGraphic(folder);
folderActionButton.setOnDragDetected(mouseEvent -> {
Dragboard db = folderActionButton.startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
content.put(Action.getDataFormat(), createFakeAction(new FolderAction(), "Untitled Folder"));
db.setContent(content);
mouseEvent.consume();
});
Button combineActionButton = new Button("Combine");
combineActionButton.setMaxWidth(Double.MAX_VALUE);
combineActionButton.setAlignment(Pos.CENTER_LEFT);
FontIcon list = new FontIcon("fas-list");
combineActionButton.setGraphic(list);
combineActionButton.setOnDragDetected(mouseEvent -> {
Dragboard db = combineActionButton.startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
content.put(Action.getDataFormat(), createFakeAction(new CombineAction(), "Untitled Combine"));
db.setContent(content);
mouseEvent.consume();
});
vBox.getChildren().addAll(folderActionButton, combineActionButton);
vBox.setSpacing(5);
TitledPane pane = new TitledPane("StreamPi", vBox);
pluginsAccordion.getPanes().add(pane);
pluginsAccordion.setCache(true);
pluginsAccordion.setCacheHint(CacheHint.SPEED);
}
}
package com.StreamPi.Server.Window;
import com.StreamPi.Util.Alert.StreamPiAlertType;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.Exception.SevereException;
import javafx.scene.control.Alert;
public interface ExceptionAndAlertHandler {
void handleMinorException(MinorException e);
void handleSevereException(SevereException e);
}
package com.StreamPi.Server.Window.FirstTimeUse;
import com.StreamPi.Server.Connection.ServerListener;
import com.StreamPi.Server.IO.Config;
import com.StreamPi.Server.Window.ExceptionAndAlertHandler;
import com.StreamPi.Util.Exception.SevereException;
import com.StreamPi.Util.FormHelper.HBoxInputBox;
import com.StreamPi.Util.FormHelper.SpaceFiller;
import com.StreamPi.Util.FormHelper.SpaceFiller.FillerType;
import javafx.geometry.Pos;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class FinalConfigPane extends VBox
{
private TextField serverNicknameTextField;
private TextField serverPortTextField;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
private ServerListener serverListener;
public FinalConfigPane(ExceptionAndAlertHandler exceptionAndAlertHandler, ServerListener serverListener)
{
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
this.serverListener = serverListener;
getStyleClass().add("first_time_use_pane_final_config");
Label label = new Label("Thats it. Now just name your Stream-Pi Server, and the port where the server will run on!\n" +
"You can leave the default value of the port as it is.");
label.setWrapText(true);
label.getStyleClass().add("first_time_use_pane_final_config_label");
serverNicknameTextField = new TextField();
serverPortTextField = new TextField("2004");
HBoxInputBox serverNickNameInputBox = new HBoxInputBox("Server Nickname", serverNicknameTextField, 200);
HBoxInputBox serverPortInputBox = new HBoxInputBox("Server Port", serverPortTextField);
Button confirmButton = new Button("Confirm");
confirmButton.setOnAction(event -> onConfirmButtonClicked());
HBox bBar = new HBox(confirmButton);
bBar.setAlignment(Pos.CENTER_RIGHT);
getChildren().addAll(label, serverNickNameInputBox, serverPortInputBox, new SpaceFiller(FillerType.VBox), bBar);
setSpacing(10.0);
setVisible(false);
}
private void onConfirmButtonClicked()
{
StringBuilder errors = new StringBuilder();
String serverNameStr = serverNicknameTextField.getText();
String serverPortStr = serverPortTextField.getText();
if(serverNameStr.isBlank() || serverNameStr.isEmpty())
{
errors.append("* Server Name cannot be blank.\n");
}
int serverPort=-1;
try {
serverPort = Integer.parseInt(serverPortStr);
if (serverPort < 1024)
errors.append("* Server Port must be more than 1024");
}
catch (NumberFormatException e)
{
errors.append("* Server Port must be integer.\n");
}
if(errors.toString().isEmpty())
{
try
{
Config.getInstance().setServerName(serverNameStr);
Config.getInstance().setServerPort(serverPort);
Config.getInstance().setFirstTimeUse(false);
Config.getInstance().save();
serverListener.othInit();
((Stage) getScene().getWindow()).close();
}
catch(SevereException e)
{
exceptionAndAlertHandler.handleSevereException(e);
}
}
else
{
Alert alert = new Alert(AlertType.ERROR);
alert.setContentText("Please rectify the following errors and try again:\n"+errors.toString());
alert.show();
}
}
}
package com.StreamPi.Server.Window.FirstTimeUse;
import com.StreamPi.Server.Main;
import com.StreamPi.Server.Connection.ServerListener;
import com.StreamPi.Server.Window.ExceptionAndAlertHandler;
import com.StreamPi.Util.FormHelper.SpaceFiller;
import com.StreamPi.Util.FormHelper.SpaceFiller.FillerType;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
public class FirstTimeUse extends VBox{
public FirstTimeUse(ExceptionAndAlertHandler exceptionAndAlertHandler, ServerListener serverListener)
{
Font.loadFont(Main.class.getResourceAsStream("Roboto.ttf"), 13);
getStylesheets().add(Main.class.getResource("style.css").toExternalForm());
getStyleClass().add("first_time_use_pane");
setSpacing(10.0);
setPadding(new Insets(5));
headingLabel = new Label();
headingLabel.getStyleClass().add("first_time_use_pane_heading_label");
StackPane stackPane = new StackPane();
stackPane.getStyleClass().add("first_time_use_pane_stackpane");
VBox.setVgrow(stackPane, Priority.ALWAYS);
welcomePane = new WelcomePane();
licensePane = new LicensePane();
finalConfigPane = new FinalConfigPane(exceptionAndAlertHandler, serverListener);
stackPane.getChildren().addAll(
welcomePane,
licensePane,
finalConfigPane
);
nextButton = new Button("Next");
nextButton.setOnAction(event-> onNextButtonClicked());
previousButton = new Button("Previous");
previousButton.setOnAction(event-> onPreviousButtonClicked());
HBox buttonBar = new HBox(previousButton, new SpaceFiller(FillerType.HBox), nextButton);
buttonBar.setSpacing(10.0);
getChildren().addAll(headingLabel, stackPane, buttonBar);
setWindow(WindowName.WELCOME);
}
private Label headingLabel;
private Button nextButton;
private Button previousButton;
private WelcomePane welcomePane;
private LicensePane licensePane;
private FinalConfigPane finalConfigPane;
private WindowName windowName;
private void onNextButtonClicked()
{
if(windowName == WindowName.WELCOME)
{
setWindow(WindowName.LICENSE);
}
else if(windowName == WindowName.LICENSE)
{
setWindow(WindowName.FINAL);
}
}
private void onPreviousButtonClicked()
{
if(windowName == WindowName.FINAL)
{
setWindow(WindowName.LICENSE);
}
else if(windowName == WindowName.LICENSE)
{
setWindow(WindowName.WELCOME);
}
}
private void setWindow(WindowName windowName)
{
if (windowName == WindowName.WELCOME)
{
this.windowName = WindowName.WELCOME;
welcomePane.toFront();
welcomePane.setVisible(true);
licensePane.setVisible(false);
finalConfigPane.setVisible(false);
headingLabel.setText("");
nextButton.setDisable(false);
previousButton.setDisable(true);
}
else if (windowName == WindowName.LICENSE)
{
this.windowName = WindowName.LICENSE;
licensePane.toFront();
welcomePane.setVisible(false);
licensePane.setVisible(true);
finalConfigPane.setVisible(false);
headingLabel.setText("License Agreement");
nextButton.setDisable(false);
previousButton.setDisable(false);
}
else if (windowName == WindowName.FINAL)
{
this.windowName = WindowName.FINAL;
finalConfigPane.toFront();
welcomePane.setVisible(false);
licensePane.setVisible(false);
finalConfigPane.setVisible(true);
headingLabel.setText("Finishing up ...");
nextButton.setDisable(true);
previousButton.setDisable(false);
}
}
}
package com.StreamPi.Server.Window.FirstTimeUse;
import com.StreamPi.Server.Info.License;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
public class LicensePane extends VBox {
public LicensePane()
{
getStyleClass().add("first_time_use_pane_license");
Label label = new Label("By Clicking on 'Next', you agree with the license of StreamPi, and the license(s) of the library(s) used/included with this software.");
label.prefWidthProperty().bind(widthProperty());
label.setWrapText(true);
TextArea licenseTextArea = new TextArea(License.getLicense());
licenseTextArea.setWrapText(false);
licenseTextArea.setEditable(false);
licenseTextArea.prefWidthProperty().bind(widthProperty());
VBox.setVgrow(licenseTextArea, Priority.ALWAYS);
getChildren().addAll(label, licenseTextArea);
}
}
package com.StreamPi.Server.Window.FirstTimeUse;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
public class WelcomePane extends VBox{
public WelcomePane()
{
getStyleClass().add("first_time_use_pane_welcome");
Label welcomeLabel = new Label("Welcome!");
welcomeLabel.getStyleClass().add("first_time_use_welcome_pane_welcome_label");
Label nextToContinue = new Label("Click on Next to continue with the setup.");
nextToContinue.getStyleClass().add("first_time_use_welcome_pane_next_to_continue_label");
setAlignment(Pos.CENTER);
setSpacing(5.0);
getChildren().addAll(welcomeLabel, nextToContinue);
setVisible(false);
}
}
package com.StreamPi.Server.Window.FirstTimeUse;
public enum WindowName {
WELCOME, LICENSE, FINAL
}
package com.StreamPi.Server.Window.Settings;
import com.StreamPi.ActionAPI.ActionAPI;
import com.StreamPi.Server.Info.License;
import com.StreamPi.Server.Info.ServerInfo;
import com.StreamPi.Server.Main;
import com.StreamPi.Util.Platform.Platform;
import com.StreamPi.Util.Platform.ReleaseStatus;
import javafx.application.HostServices;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
public class About extends VBox{
private HostServices hostServices;
public About(HostServices hostServices)
{
getStyleClass().add("about");
this.hostServices = hostServices;
setAlignment(Pos.TOP_CENTER);
Image appIcon = new Image(Main.class.getResourceAsStream("app_icon.png"));
ImageView appIconImageView = new ImageView(appIcon);
appIconImageView.setFitHeight(196);
appIconImageView.setFitWidth(182);
Label licenseLabel = new Label("License");
licenseLabel.getStyleClass().add("settings_about_license_label");
VBox.setMargin(licenseLabel, new Insets(20, 0 , 10 ,0));
TextArea licenseTextArea = new TextArea(License.getLicense());
licenseTextArea.setWrapText(false);
licenseTextArea.setEditable(false);
licenseTextArea.setMaxWidth(550);
VBox.setVgrow(licenseTextArea, Priority.ALWAYS);
HBox links = new HBox();
Hyperlink github = new Hyperlink("GitHub");
github.setOnAction(event -> {
openWebpage("https://github.com/Stream-Pi");
});
Hyperlink discord = new Hyperlink("Discord");
discord.setOnAction(event -> {
openWebpage("https://discord.gg/BExqGmk");
});
Hyperlink website = new Hyperlink("Website");
website.setOnAction(event -> {
openWebpage("https://stream-pi.com");
});
Hyperlink twitter = new Hyperlink("Twitter");
twitter.setOnAction(event -> {
openWebpage("https://twitter.com/Stream_Pi");
});
Hyperlink matrix = new Hyperlink("Matrix");
matrix.setOnAction(event -> {
openWebpage("https://matrix.to/#/#stream-pi_general:matrix.org");
});
links.setSpacing(30);
links.setAlignment(Pos.CENTER);
links.getChildren().addAll(github, matrix, discord, website, twitter);
Hyperlink donateButton = new Hyperlink("DONATE");
donateButton.setOnAction(event -> {
openWebpage("https://www.patreon.com/streampi");
});
donateButton.getStyleClass().add("settings_about_donate_hyperlink");
ServerInfo serverInfo = ServerInfo.getInstance();
Label versionText = new Label(serverInfo.getVersion().getText() + " - "+ serverInfo.getPlatformType().getUIName() + " - "+ serverInfo.getReleaseStatus().getUIName());
Label commStandardLabel = new Label("Comm Standard "+serverInfo.getCommStandardVersion().getText());
Label minThemeAPILabel = new Label("Min ThemeAPI "+serverInfo.getMinThemeSupportVersion().getText());
Label minActionAPILabel = new Label("Min ActionAPI "+serverInfo.getMinPluginSupportVersion().getText());
Label currentActionAPILabel = new Label("ActionAPI "+ ActionAPI.API_VERSION.getText());
setSpacing(3);
getChildren().addAll(appIconImageView, licenseLabel, licenseTextArea, links, donateButton, versionText, commStandardLabel, minThemeAPILabel, minActionAPILabel, currentActionAPILabel);
}
public void openWebpage(String url) {
hostServices.showDocument(url);
}
}
package com.StreamPi.Server.Window.Settings;
import com.StreamPi.Server.Client.Client;
import com.StreamPi.Server.Client.ClientProfile;
import com.StreamPi.Server.Client.ClientTheme;
import com.StreamPi.Server.Connection.ClientConnection;
import com.StreamPi.Server.Connection.ClientConnections;
import com.StreamPi.Server.Connection.ServerListener;
import com.StreamPi.Server.Window.ExceptionAndAlertHandler;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.Exception.SevereException;
import com.StreamPi.Util.FormHelper.HBoxInputBox;
import com.StreamPi.Util.FormHelper.SpaceFiller;
import com.StreamPi.Util.Platform.ReleaseStatus;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.CacheHint;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.util.Callback;
import org.w3c.dom.Text;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
public class ClientsSettings extends VBox {
private VBox clientsSettingsVBox;
private Button saveButton;
private ServerListener serverListener;
private Logger logger;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
public ClientsSettings(ExceptionAndAlertHandler exceptionAndAlertHandler, ServerListener serverListener)
{
getStyleClass().add("clients_settings");
this.serverListener = serverListener;
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
clientSettingsVBoxArrayList = new ArrayList<>();
setPadding(new Insets(10.0));
logger = Logger.getLogger(ClientsSettings.class.getName());
clientsSettingsVBox = new VBox();
clientsSettingsVBox.setSpacing(20.0);
clientsSettingsVBox.setAlignment(Pos.TOP_CENTER);
setAlignment(Pos.TOP_CENTER);
ScrollPane scrollPane = new ScrollPane();
scrollPane.getStyleClass().add("clients_settings_scroll_pane");
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
VBox.setVgrow(scrollPane, Priority.ALWAYS);
scrollPane.maxWidthProperty().bind(widthProperty().multiply(0.8));
clientsSettingsVBox.prefWidthProperty().bind(scrollPane.widthProperty().subtract(25));
scrollPane.setContent(clientsSettingsVBox);
saveButton = new Button("Save");
saveButton.setOnAction(event -> onSaveButtonClicked());
HBox hBox = new HBox(saveButton);
hBox.setAlignment(Pos.CENTER_RIGHT);
getChildren().addAll(scrollPane, hBox);
setCache(true);
setCacheHint(CacheHint.SPEED);
}
public void onSaveButtonClicked()
{
new Thread(new Task<Void>() {
@Override
protected Void call()
{
try
{
Platform.runLater(()->saveButton.setDisable(true));
StringBuilder finalErrors = new StringBuilder();
for(ClientSettingsVBox clientSettingsVBox : clientSettingsVBoxArrayList)
{
StringBuilder errors = new StringBuilder();
if(clientSettingsVBox.getNickname().isBlank())
errors.append(" Cannot have blank nickname. \n");
try {
Double.parseDouble(clientSettingsVBox.getStartupWindowHeight());
}
catch (NumberFormatException e)
{
errors.append(" Must have integer display height. \n");
}
try {
Double.parseDouble(clientSettingsVBox.getStartupWindowWidth());
}
catch (NumberFormatException e)
{
errors.append(" Must have integer display width. \n");
}
for(ClientProfileVBox clientProfileVBox : clientSettingsVBox.getClientProfileVBoxes())
{
StringBuilder errors2 = new StringBuilder();
if(clientProfileVBox.getName().isBlank())
errors2.append(" cannot have blank nickname. \n");
try {
Integer.parseInt(clientProfileVBox.getActionSize());
}
catch (NumberFormatException e)
{
errors2.append(" Must have integer Action Size. \n");
}
try {
Integer.parseInt(clientProfileVBox.getActionGap());
}
catch (NumberFormatException e)
{
errors2.append(" Must have integer Action Gap. \n");
}
try {
int rows = Integer.parseInt(clientProfileVBox.getRows());
int actionsSize = Integer.parseInt(clientProfileVBox.getActionSize());
double startupWidth = Double.parseDouble(clientSettingsVBox.getStartupWindowWidth());
if((rows*actionsSize) > (startupWidth - 25) && clientSettingsVBox.getPlatform()!= com.StreamPi.Util.Platform.Platform.ANDROID)
{
errors2.append(" Rows out of bounds of screen size. \n"+startupWidth);
}
}
catch (NumberFormatException e)
{
errors2.append(" Must have integer Rows. \n");
}
try {
int cols = Integer.parseInt(clientProfileVBox.getCols());
int actionsSize = Integer.parseInt(clientProfileVBox.getActionSize());
double startupHeight = Double.parseDouble(clientSettingsVBox.getStartupWindowHeight());
if((cols*actionsSize) > (startupHeight - 25) && clientSettingsVBox.getPlatform()!= com.StreamPi.Util.Platform.Platform.ANDROID)
{
errors2.append(" Cols out of bounds of screen size. \n"+startupHeight);
}
}
catch (NumberFormatException e)
{
errors2.append(" Must have integer Columns. \n");
}
if(!errors2.toString().isEmpty())
{
errors.append(" ")
.append(clientProfileVBox.getRealName())
.append("\n")
.append(errors2.toString())
.append("\n");
}
}
if(!errors.toString().isEmpty())
{
finalErrors.append("* ")
.append(clientSettingsVBox.getRealNickName())
.append("\n")
.append(errors.toString())
.append("\n");
}
}
if(!finalErrors.toString().isEmpty())
throw new MinorException("You made form mistakes",
"Please fix the following issues : \n"+finalErrors.toString());
//save details and values
for(ClientSettingsVBox clientSettingsVBox : clientSettingsVBoxArrayList)
{
clientSettingsVBox.saveClientAndProfileDetails();
}
loadData();
serverListener.clearTemp();
}
catch (MinorException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleMinorException(e);
}
catch (SevereException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleSevereException(e);
}
catch (CloneNotSupportedException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleSevereException(new SevereException(
e.getMessage()
));
}
finally
{
Platform.runLater(()->saveButton.setDisable(false));
}
return null;
}
}).start();
}
private ArrayList<ClientSettingsVBox> clientSettingsVBoxArrayList;
public void loadData()
{
logger.info("Loading client data into ClientsSettings ...");
Platform.runLater(()-> clientsSettingsVBox.getChildren().clear());
clientSettingsVBoxArrayList.clear();
List<ClientConnection> clientConnections = ClientConnections.getInstance().getConnections();
if(clientConnections.size() == 0)
{
Platform.runLater(()->{
clientsSettingsVBox.getChildren().add(new Label("No Clients Connected."));
saveButton.setVisible(false);
});
}
else
{
Platform.runLater(()->saveButton.setVisible(true));
for (ClientConnection clientConnection : clientConnections) {
ClientSettingsVBox clientSettingsVBox = new ClientSettingsVBox(clientConnection);
clientSettingsVBoxArrayList.add(clientSettingsVBox);
Platform.runLater(()->clientsSettingsVBox.getChildren().add(clientSettingsVBox));
}
}
logger.info("... Done!");
}
public class ClientSettingsVBox extends VBox
{
private ComboBox<ClientProfile> profilesComboBox;
private ComboBox<ClientTheme> themesComboBox;
private TextField startupWindowHeightTextField;
public String getStartupWindowHeight() {
return startupWindowHeightTextField.getText();
}
private TextField startupWindowWidthTextField;
public String getStartupWindowWidth() {
return startupWindowWidthTextField.getText();
}
private TextField nicknameTextField;
public String getNickname() {
return nicknameTextField.getText();
}
private Label nickNameLabel;
private Label versionLabel;
public String getRealNickName()
{
return nickNameLabel.getText();
}
private com.StreamPi.Util.Platform.Platform platform;
public com.StreamPi.Util.Platform.Platform getPlatform() {
return platform;
}
private Label socketConnectionLabel;
private ClientConnection connection;
private Accordion profilesAccordion;
private ArrayList<ClientProfileVBox> clientProfileVBoxes;
private Label platformLabel;
private HBoxInputBox startupWindowHeightInputBox, startupWindowWidthInputBox;
public ArrayList<ClientProfileVBox> getClientProfileVBoxes() {
return clientProfileVBoxes;
}
public ClientSettingsVBox(ClientConnection connection)
{
this.connection = connection;
this.platform = connection.getClient().getPlatform();
clientProfileVBoxes = new ArrayList<>();
initUI();
loadValues();
}
public ClientConnection getConnection()
{
return connection;
}
public void saveClientAndProfileDetails() throws SevereException, CloneNotSupportedException, MinorException {
System.out.println("IIN");
getConnection().saveClientDetails(
nicknameTextField.getText(),
startupWindowWidthTextField.getText(),
startupWindowHeightTextField.getText(),
profilesComboBox.getSelectionModel().getSelectedItem().getID(),
themesComboBox.getSelectionModel().getSelectedItem().getThemeFullName()
);
System.out.println("OUT");
logger.info("Profiles : ");
for(ClientProfileVBox clientProfileVBox : clientProfileVBoxes)
{
logger.info("Name : "+clientProfileVBox.getClientProfile().getName());
getConnection().saveProfileDetails(clientProfileVBox.getClientProfile());
}
//remove deleted client profiles
for(ClientProfile clientProfile : connection.getClient().getAllClientProfiles())
{
boolean found = false;
for(ClientProfileVBox clientProfileVBox : clientProfileVBoxes)
{
if(clientProfileVBox.getClientProfile().getID().equals(clientProfile.getID()))
{
found = true;
break;
}
}
if(!found)
{
connection.getClient().removeProfileFromID(clientProfile.getID());
connection.deleteProfile(clientProfile.getID());
}
}
}
public void initUI()
{
profilesComboBox = new ComboBox<>();
Callback<ListView<ClientProfile>, ListCell<ClientProfile>> profilesComboBoxFactory = new Callback<>() {
@Override
public ListCell<ClientProfile> call(ListView<ClientProfile> clientConnectionListView) {
return new ListCell<>() {
@Override
protected void updateItem(ClientProfile clientProfile, boolean b) {
super.updateItem(clientProfile, b);
if(clientProfile == null)
{
setText(null);
}
else
{
setText(clientProfile.getName());
}
}
};
}
};
profilesComboBox.setCellFactory(profilesComboBoxFactory);
profilesComboBox.setButtonCell(profilesComboBoxFactory.call(null));
themesComboBox = new ComboBox<>();
Callback<ListView<ClientTheme>, ListCell<ClientTheme>> themesComboBoxFactory = new Callback<>() {
@Override
public ListCell<ClientTheme> call(ListView<ClientTheme> clientConnectionListView) {
return new ListCell<>() {
@Override
protected void updateItem(ClientTheme clientTheme, boolean b) {
super.updateItem(clientTheme, b);
if(clientTheme == null)
{
setText(null);
}
else
{
setText(clientTheme.getShortName());
}
}
};
}
};
themesComboBox.setCellFactory(themesComboBoxFactory);
themesComboBox.setButtonCell(themesComboBoxFactory.call(null));
startupWindowHeightTextField = new TextField();
startupWindowWidthTextField = new TextField();
platformLabel = new Label();
platformLabel.getStyleClass().add("settings_client_platform_label");
socketConnectionLabel = new Label();
socketConnectionLabel.getStyleClass().add("settings_client_socket_connection_label");
nicknameTextField = new TextField();
nickNameLabel = new Label();
nickNameLabel.getStyleClass().add("settings_client_nick_name_label");
versionLabel = new Label();
versionLabel.getStyleClass().add("settings_client_version_label");
profilesAccordion = new Accordion();
VBox.setMargin(profilesAccordion, new Insets(0,0,20,0));
Button addNewProfileButton = new Button("Add new Profile");
addNewProfileButton.setOnAction(event -> onNewProfileButtonClicked());
setSpacing(10.0);
getStyleClass().add("settings_clients_each_client");
startupWindowHeightInputBox = new HBoxInputBox("Startup Window Height", startupWindowHeightTextField);
startupWindowHeightInputBox.managedProperty().bind(startupWindowHeightInputBox.visibleProperty());
startupWindowWidthInputBox = new HBoxInputBox("Startup Window Width", startupWindowWidthTextField);
startupWindowWidthInputBox.managedProperty().bind(startupWindowWidthInputBox.visibleProperty());
this.getChildren().addAll(
nickNameLabel,
socketConnectionLabel,
platformLabel,
versionLabel,
new HBoxInputBox("Nickname",nicknameTextField),
new HBox(
new Label("Theme"),
new SpaceFiller(SpaceFiller.FillerType.HBox),
themesComboBox
),
startupWindowHeightInputBox,
startupWindowWidthInputBox,
new HBox(new Label("Startup Profile"),
new SpaceFiller(SpaceFiller.FillerType.HBox),
profilesComboBox),
addNewProfileButton,
profilesAccordion);
}
public void loadValues()
{
Client client = connection.getClient();
profilesComboBox.setItems(FXCollections.observableList(client.getAllClientProfiles()));
profilesComboBox.getSelectionModel().select(
client.getProfileByID(client.getDefaultProfileID())
);
themesComboBox.setItems(FXCollections.observableList(client.getThemes()));
themesComboBox.getSelectionModel().select(
client.getThemeByFullName(
client.getDefaultThemeFullName()
)
);
nicknameTextField.setText(client.getNickName());
if(client.getPlatform() == com.StreamPi.Util.Platform.Platform.ANDROID)
{
startupWindowHeightInputBox.setVisible(false);
startupWindowWidthInputBox.setVisible(false);
}
platformLabel.setText("Platform : "+client.getPlatform().getUIName());
startupWindowWidthTextField.setText(client.getStartupDisplayWidth()+"");
startupWindowHeightTextField.setText(client.getStartupDisplayHeight()+"");
socketConnectionLabel.setText(client.getRemoteSocketAddress().toString().substring(1)); //substring removes the `/`
nickNameLabel.setText(client.getNickName());
versionLabel.setText(client.getReleaseStatus().getUIName()+" "+client.getVersion().getText());
//add profiles
for(ClientProfile clientProfile : client.getAllClientProfiles())
{
TitledPane titledPane = new TitledPane();
titledPane.setText(clientProfile.getName());
ClientProfileVBox clientProfileVBox = new ClientProfileVBox(clientProfile);
clientProfileVBox.getRemoveButton().setOnAction(event -> onProfileDeleteButtonClicked(clientProfileVBox, titledPane));
titledPane.setContent(clientProfileVBox);
clientProfileVBoxes.add(clientProfileVBox);
profilesAccordion.getPanes().add(titledPane);
}
}
public void onNewProfileButtonClicked()
{
ClientProfile clientProfile = new ClientProfile(
"Untitled Profile",
3,
3,
100,
5
);
ClientProfileVBox clientProfileVBox = new ClientProfileVBox(clientProfile);
TitledPane titledPane = new TitledPane();
titledPane.setContent(clientProfileVBox);
titledPane.setText(clientProfile.getName());
clientProfileVBox.getRemoveButton().setOnAction(event -> onProfileDeleteButtonClicked(clientProfileVBox, titledPane));
clientProfileVBoxes.add(clientProfileVBox);
profilesAccordion.getPanes().add(titledPane);
}
public void onProfileDeleteButtonClicked(ClientProfileVBox clientProfileVBox, TitledPane titledPane)
{
if(clientProfileVBoxes.size() == 1)
{
exceptionAndAlertHandler.handleMinorException(new MinorException("Only one",
"You cannot delete all profiles"));
}
else
{
if(profilesComboBox.getSelectionModel().getSelectedItem().getID().equals(clientProfileVBox.getClientProfile().getID()))
{
exceptionAndAlertHandler.handleMinorException(new MinorException("Default",
"You cannot delete default profile. Change to another one to delete this."));
}
else
{
clientProfileVBoxes.remove(clientProfileVBox);
profilesComboBox.getItems().remove(clientProfileVBox.getClientProfile());
profilesAccordion.getPanes().remove(titledPane);
}
}
}
}
public class ClientProfileVBox extends VBox
{
private TextField nameTextField;
public String getName()
{
return nameTextField.getText();
}
private TextField rowsTextField;
public String getRows()
{
return rowsTextField.getText();
}
private TextField colsTextField;
public String getCols()
{
return colsTextField.getText();
}
private TextField actionSizeTextField;
public String getActionSize()
{
return actionSizeTextField.getText();
}
private TextField actionGapTextField;
public String getActionGap()
{
return actionGapTextField.getText();
}
private Button removeButton;
private ClientProfile clientProfile;
public String getRealName()
{
return clientProfile.getName();
}
public ClientProfileVBox(ClientProfile clientProfile)
{
this.clientProfile = clientProfile;
initUI();
loadValues(clientProfile);
}
public void initUI()
{
setPadding(new Insets(5.0));
setSpacing(10.0);
nameTextField = new TextField();
rowsTextField = new TextField();
colsTextField = new TextField();
actionSizeTextField = new TextField();
actionGapTextField = new TextField();
removeButton = new Button("Remove");
HBox hBox = new HBox(removeButton);
hBox.setAlignment(Pos.CENTER_RIGHT);
getChildren().addAll(
new HBoxInputBox("Name ", nameTextField),
new HBoxInputBox("Columns", rowsTextField),
new HBoxInputBox("Rows", colsTextField),
new HBoxInputBox("Action Size", actionSizeTextField),
new HBoxInputBox("Action Gap", actionGapTextField),
hBox
);
}
public Button getRemoveButton()
{
return removeButton;
}
public void loadValues(ClientProfile clientProfile)
{
nameTextField.setText(clientProfile.getName());
rowsTextField.setText(clientProfile.getRows()+"");
colsTextField.setText(clientProfile.getCols()+"");
actionSizeTextField.setText(clientProfile.getActionSize()+"");
actionGapTextField.setText(clientProfile.getActionGap()+"");
}
public ClientProfile getClientProfile()
{
clientProfile.setActionGap(Integer.parseInt(actionGapTextField.getText()));
clientProfile.setActionSize(Integer.parseInt(actionSizeTextField.getText()));
clientProfile.setRows(Integer.parseInt(rowsTextField.getText()));
clientProfile.setCols(Integer.parseInt(colsTextField.getText()));
clientProfile.setName(nameTextField.getText());
return clientProfile;
}
}
}
package com.StreamPi.Server.Window.Settings;
import com.StreamPi.Server.Connection.ServerListener;
import com.StreamPi.Server.IO.Config;
import com.StreamPi.Server.Controller.Controller;
import com.StreamPi.Server.Window.ExceptionAndAlertHandler;
import com.StreamPi.ThemeAPI.Theme;
import com.StreamPi.Util.Alert.StreamPiAlert;
import com.StreamPi.Util.Alert.StreamPiAlertType;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.Exception.SevereException;
import com.StreamPi.Util.StartAtBoot.SoftwareType;
import com.StreamPi.Util.StartAtBoot.StartAtBoot;
import com.StreamPi.Util.Version.Version;
import com.StreamPi.Server.Info.ServerInfo;
import javafx.application.HostServices;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.*;
import javafx.scene.paint.Paint;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import org.json.JSONObject;
import org.kordamp.ikonli.javafx.FontIcon;
import org.w3c.dom.Text;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.PopupMenu;
import java.awt.MenuItem;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.logging.Logger;
public class GeneralSettings extends ScrollPane {
private final TextField serverNameTextField;
private final TextField portTextField;
private final TextField pluginsPathTextField;
private final TextField themesPathTextField;
private final TextField actionGridPaneActionBoxSize;
private final TextField actionGridPaneActionBoxGap;
private final ToggleButton startOnBootToggleButton;
private final ToggleButton closeOnXToggleButton;
private final Button saveButton;
private final Button checkForUpdatesButton;
private Logger logger;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
private ServerListener serverListener;
private HostServices hostServices;
public GeneralSettings(ExceptionAndAlertHandler exceptionAndAlertHandler, ServerListener serverListener, HostServices hostServices)
{
this.hostServices = hostServices;
getStyleClass().add("general_settings_scroll_pane");
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
this.serverListener = serverListener;
logger = Logger.getLogger(GeneralSettings.class.getName());
setHbarPolicy(ScrollBarPolicy.NEVER);
serverNameTextField = new TextField();
portTextField = new TextField();
pluginsPathTextField = new TextField();
themesPathTextField = new TextField();
actionGridPaneActionBoxSize = new TextField();
actionGridPaneActionBoxGap = new TextField();
startOnBootToggleButton = new ToggleButton("Start on Boot");
closeOnXToggleButton = new ToggleButton("Quit On Window Close");
checkForUpdatesButton = new Button("Check for updates");
checkForUpdatesButton.setOnAction(event->checkForUpdates());
VBox vBox = new VBox();
vBox.prefWidthProperty().bind(widthProperty());
vBox.setAlignment(Pos.CENTER);
vBox.setSpacing(5);
vBox.getChildren().addAll(
getUIInputBox("Server Name", serverNameTextField),
getUIInputBox("Port", portTextField),
getUIInputBox("Action Grid Pane Action Box Size", actionGridPaneActionBoxSize),
getUIInputBox("Action Grid Pane Action Box Gap", actionGridPaneActionBoxGap),
getUIInputBoxWithDirectoryChooser("Plugins Path", pluginsPathTextField),
getUIInputBoxWithDirectoryChooser("Themes Path", themesPathTextField)
);
serverNameTextField.setPrefWidth(200);
HBox toggleButtons = new HBox(closeOnXToggleButton, startOnBootToggleButton);
toggleButtons.setSpacing(10.0);
VBox.setMargin(toggleButtons, new Insets(30, 0 , 0,0));
toggleButtons.setAlignment(Pos.CENTER);
saveButton = new Button("Save");
saveButton.setOnAction(event->save());
vBox.getChildren().addAll(toggleButtons, checkForUpdatesButton, saveButton);
vBox.setPadding(new Insets(10));
vBox.getStyleClass().add("general_settings");
setContent(vBox);
}
private HBox getUIInputBoxWithDirectoryChooser(String labelText, TextField textField)
{
HBox hBox = getUIInputBox(labelText, textField);
hBox.setSpacing(5.0);
TextField tf = (TextField) hBox.getChildren().get(2);
tf.setPrefWidth(300);
tf.setDisable(true);
Button button = new Button();
FontIcon fontIcon = new FontIcon("far-folder");
button.setGraphic(fontIcon);
button.setOnAction(event -> {
DirectoryChooser directoryChooser = new DirectoryChooser();
try {
File selectedDirectory = directoryChooser.showDialog(getScene().getWindow());
textField.setText(selectedDirectory.getAbsolutePath());
}
catch (NullPointerException e)
{
logger.info("No folder selected");
}
});
hBox.getChildren().add(button);
return hBox;
}
private HBox getUIInputBox(String labelText, TextField textField)
{
textField.setPrefWidth(100);
Label label = new Label(labelText);
Region region = new Region();
HBox.setHgrow(region, Priority.ALWAYS);
return new HBox(label, region, textField);
}
public void loadDataFromConfig() throws SevereException {
Config config = Config.getInstance();
Platform.runLater(()->
{
serverNameTextField.setText(config.getServerName());
portTextField.setText(config.getPort()+"");
pluginsPathTextField.setText(config.getPluginsPath());
themesPathTextField.setText(config.getThemesPath());
actionGridPaneActionBoxSize.setText(config.getActionGridActionSize()+"");
actionGridPaneActionBoxGap.setText(config.getActionGridActionGap()+"");
closeOnXToggleButton.setSelected(config.getCloseOnX());
startOnBootToggleButton.setSelected(config.getStartOnBoot());
});
}
public void save()
{
new Thread(new Task<Void>() {
@Override
protected Void call()
{
try {
boolean toBeReloaded = false;
boolean dashToBeReRendered = false;
Platform.runLater(()->{
saveButton.setDisable(true);
serverNameTextField.setDisable(true);
portTextField.setDisable(true);
closeOnXToggleButton.setDisable(true);
startOnBootToggleButton.setDisable(true);
});
String serverNameStr = serverNameTextField.getText();
String serverPortStr = portTextField.getText();
String pluginsPathStr = pluginsPathTextField.getText();
String themesPathStr = themesPathTextField.getText();
String actionGridActionBoxSize = actionGridPaneActionBoxSize.getText();
String actionGridActionBoxGap = actionGridPaneActionBoxGap.getText();
boolean closeOnX = closeOnXToggleButton.isSelected();
boolean startOnBoot = startOnBootToggleButton.isSelected();
Config config = Config.getInstance();
StringBuilder errors = new StringBuilder();
if(serverNameStr.isBlank())
{
errors.append("* Server Name cannot be blank.\n");
}
else
{
if(!config.getServerName().equals(serverNameStr))
{
toBeReloaded = true;
}
}
int serverPort=-1;
try {
serverPort = Integer.parseInt(serverPortStr);
if (serverPort < 1024)
errors.append("* Server Port must be more than 1024");
if(config.getPort()!=serverPort)
{
toBeReloaded = true;
}
}
catch (NumberFormatException e)
{
errors.append("* Server Port must be integer.\n");
}
int actionSize=-1;
try {
actionSize = Integer.parseInt(actionGridActionBoxSize);
if(config.getActionGridActionSize() != actionSize)
{
dashToBeReRendered = true;
}
}
catch (NumberFormatException e)
{
errors.append("* Action Size must be integer.\n");
}
int actionGap=-1;
try {
actionGap = Integer.parseInt(actionGridActionBoxGap);
if(config.getActionGridActionGap() != actionGap)
{
dashToBeReRendered = true;
}
}
catch (NumberFormatException e)
{
errors.append("* Action Gap must be integer.\n");
}
if(pluginsPathStr.isBlank())
{
errors.append("* Plugins Path must not be blank.\n");
}
else
{
if(!config.getPluginsPath().equals(pluginsPathStr))
{
toBeReloaded = true;
}
}
if(themesPathStr.isBlank())
{
errors.append("* Themes Path must not be blank.\n");
}
else
{
if(!config.getThemesPath().equals(themesPathStr))
{
toBeReloaded = true;
}
}
if(!errors.toString().isEmpty())
{
throw new MinorException("Settings", "Please rectify the following errors and try again :\n"+errors.toString());
}
if(config.getStartOnBoot() != startOnBoot)
{
if(ServerInfo.getInstance().getRunnerFileName() == null)
{
new StreamPiAlert("Uh Oh", "No Runner File Name Specified as startup arguments. Cant set run at boot.", StreamPiAlertType.ERROR).show();
startOnBoot = false;
}
else
{
StartAtBoot startAtBoot = new StartAtBoot(SoftwareType.SERVER, ServerInfo.getInstance().getPlatformType());
if(startOnBoot)
{
startAtBoot.create(new File(ServerInfo.getInstance().getRunnerFileName()));
}
else
{
boolean result = startAtBoot.delete();
if(!result)
new StreamPiAlert("Uh Oh!", "Unable to delete starter file", StreamPiAlertType.ERROR).show();
}
}
}
if(!closeOnX)
{
System.out.println("XYZ");
if(!SystemTray.isSupported())
{
StreamPiAlert alert = new StreamPiAlert("Not Supported", "Tray System not supported on this platform ", StreamPiAlertType.ERROR);
alert.show();
closeOnX = true;
}
else
{
System.out.println("YES");
}
}
config.setServerName(serverNameStr);
config.setServerPort(serverPort);
config.setActionGridGap(actionGap);
config.setActionGridSize(actionSize);
config.setPluginsPath(pluginsPathStr);
config.setThemesPath(themesPathStr);
config.setCloseOnX(closeOnX);
config.setStartupOnBoot(startOnBoot);
config.save();
loadDataFromConfig();
if(toBeReloaded)
{
new StreamPiAlert("Restart","Restart to see changes", StreamPiAlertType.INFORMATION).show();
}
if(dashToBeReRendered)
{
serverListener.clearTemp();
}
}
catch (MinorException e)
{
exceptionAndAlertHandler.handleMinorException(e);
}
catch (SevereException e)
{
exceptionAndAlertHandler.handleSevereException(e);
}
finally {
Platform.runLater(()->{
saveButton.setDisable(false);
serverNameTextField.setDisable(false);
portTextField.setDisable(false);
closeOnXToggleButton.setDisable(false);
startOnBootToggleButton.setDisable(false);
});
}
return null;
}
}).start();
}
public void checkForUpdates()
{
new Thread(new Task<Void>()
{
@Override
protected Void call() throws Exception {
try
{
Platform.runLater(()->checkForUpdatesButton.setDisable(true));
String jsonRaw = readUrl("https://stream-pi.com/API/get_latest.php?TYPE=SERVER");
System.out.println(jsonRaw);
JSONObject jsonObject = new JSONObject(jsonRaw);
String latestVersionRaw = jsonObject.getString("Version");
String releasePage = jsonObject.getString("Release Page");
Version latestVersion = new Version(latestVersionRaw);
Version currentVersion = ServerInfo.getInstance().getVersion();
if(latestVersion.isBiggerThan(currentVersion))
{
VBox vBox = new VBox();
Hyperlink urlLabel = new Hyperlink(releasePage);
urlLabel.setOnAction(event->hostServices.showDocument(releasePage));
Label label = new Label(
"New Version "+latestVersionRaw+" Available.\n" +
"Current Version "+currentVersion.getText()+".\n"+
"Changelog and install instructions are included in the release page.\n" +
"It is recommended to update to ensure maximum stability and least bugs.");
label.setWrapText(true);
vBox.setSpacing(5);
vBox.getChildren().addAll(
urlLabel,
label
);
new StreamPiAlert("New Update Available!", StreamPiAlertType.INFORMATION, vBox).show();;
}
else
{
new StreamPiAlert("Up to Date", "Server is upto date. ("+currentVersion.getText()+")", StreamPiAlertType.INFORMATION).show();;
}
}
catch (Exception e)
{
e.printStackTrace();
new StreamPiAlert("Uh Oh", "Update Check Failed. API Error/Network issue.", StreamPiAlertType.WARNING).show();;
}
finally
{
Platform.runLater(()->checkForUpdatesButton.setDisable(false));
}
return null;
}
}).start();;
}
private String readUrl(String urlString) throws Exception {
BufferedReader reader = null;
try {
URL url = new URL(urlString);
reader = new BufferedReader(new InputStreamReader(url.openStream()));
StringBuffer buffer = new StringBuffer();
int read;
char[] chars = new char[1024];
while ((read = reader.read(chars)) != -1)
buffer.append(chars, 0, read);
return buffer.toString();
} finally {
if (reader != null)
reader.close();
}
}
}
package com.StreamPi.Server.Window.Settings;
import com.StreamPi.Server.UIPropertyBox.UIPropertyBox;
import com.StreamPi.ActionAPI.ActionProperty.Property.ControlType;
import com.StreamPi.ActionAPI.ActionProperty.Property.Property;
import com.StreamPi.ActionAPI.ActionProperty.Property.Type;
import com.StreamPi.ActionAPI.NormalAction.NormalAction;
import com.StreamPi.Server.Action.NormalActionPlugins;
import com.StreamPi.Server.Connection.ServerListener;
import com.StreamPi.Server.Window.ExceptionAndAlertHandler;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.FormHelper.SpaceFiller;
import com.StreamPi.Util.FormHelper.SpaceFiller.FillerType;
import org.kordamp.ikonli.javafx.FontIcon;
import javafx.application.HostServices;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
public class PluginsSettings extends VBox {
private VBox pluginsSettingsVBox;
private ServerListener serverListener;
private Logger logger;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
private HostServices hostServices;
public PluginsSettings(ExceptionAndAlertHandler exceptionAndAlertHandler, HostServices hostServices)
{
getStyleClass().add("plugins_settings");
this.hostServices = hostServices;
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
pluginProperties = new ArrayList<>();
logger = Logger.getLogger(PluginsSettings.class.getName());
setPadding(new Insets(10));
pluginsSettingsVBox = new VBox();
pluginsSettingsVBox.setSpacing(10.0);
pluginsSettingsVBox.setAlignment(Pos.TOP_CENTER);
ScrollPane scrollPane = new ScrollPane();
scrollPane.getStyleClass().add("plugins_settings_scroll_pane");
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollPane.maxWidthProperty().bind(widthProperty().multiply(0.8));
pluginsSettingsVBox.prefWidthProperty().bind(scrollPane.widthProperty().subtract(25));
scrollPane.setContent(pluginsSettingsVBox);
VBox.setVgrow(scrollPane, Priority.ALWAYS);
setAlignment(Pos.TOP_CENTER);
saveButton = new Button("Save");
saveButton.setOnAction(event -> onSaveButtonClicked());
HBox hBox = new HBox(saveButton);
hBox.setAlignment(Pos.CENTER_RIGHT);
getChildren().addAll(scrollPane, hBox);
}
private Button saveButton;
public void onSaveButtonClicked()
{
try {
//form validation
StringBuilder finalErrors = new StringBuilder();
for (PluginProperties p : pluginProperties)
{
StringBuilder errors = new StringBuilder();
for(int j = 0; j < p.getServerPropertyUIBox().size(); j++)
{
UIPropertyBox serverProperty = p.getServerPropertyUIBox().get(j);
Node controlNode = serverProperty.getControlNode();
if (serverProperty.getControlType() == ControlType.TEXT_FIELD)
{
String value = ((TextField) controlNode).getText();
if(serverProperty.getType() == Type.INTEGER)
{
try
{
Integer.parseInt(value);
}
catch (NumberFormatException e)
{
errors.append(" -> ").append(serverProperty.getDisplayName()).append(" must be integer.\n");
}
}
else
{
if(value.isBlank() && !serverProperty.isCanBeBlank())
errors.append(" -> ").append(serverProperty.getDisplayName()).append(" cannot be blank.\n");
}
}
}
if(!errors.toString().isBlank())
{
finalErrors.append(" * ").append(p.getName()).append("\n").append(errors.toString()).append("\n");
}
}
if(!finalErrors.toString().isEmpty())
{
throw new MinorException("Form Validation Errors",
"Please rectify the following errors and try again \n"+finalErrors.toString());
}
//save
for (PluginProperties pp : pluginProperties) {
for (int j = 0; j < pp.getServerPropertyUIBox().size(); j++) {
UIPropertyBox serverProperty = pp.getServerPropertyUIBox().get(j);
String rawValue = serverProperty.getRawValue();
NormalActionPlugins.getInstance().getActionFromIndex(pp.getIndex())
.getServerProperties().get()
.get(serverProperty.getIndex()).setRawValue(rawValue);
}
}
NormalActionPlugins.getInstance().saveServerSettings();
NormalActionPlugins.getInstance().initPlugins();
}
catch (MinorException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleMinorException(e);
}
}
private ArrayList<PluginProperties> pluginProperties;
public void showPluginInitError()
{
Platform.runLater(()->{
pluginsSettingsVBox.getChildren().add(new Label("Plugin init error. Resolve issues and restart."));
saveButton.setVisible(false);
});
}
public void loadPlugins() throws MinorException {
pluginProperties.clear();
List<NormalAction> actions = NormalActionPlugins.getInstance().getPlugins();
System.out.println("asdasdasdasd"+actions.size());
Platform.runLater(()-> pluginsSettingsVBox.getChildren().clear());
if(actions.size() == 0)
{
Platform.runLater(()->{
pluginsSettingsVBox.getChildren().add(new Label("No Plugins Installed."));
saveButton.setVisible(false);
});
return;
}
else
{
Platform.runLater(()->saveButton.setVisible(true));
}
for(int i = 0; i<actions.size(); i++)
{
NormalAction action = actions.get(i);
if(!action.isVisibleInServerSettingsPane())
continue;
Label headingLabel = new Label(action.getName());
headingLabel.getStyleClass().add("settings_plugins_each_action_heading");
HBox headerHBox = new HBox(headingLabel);
if (action.getHelpLink()!=null)
{
Button helpButton = new Button();
FontIcon questionIcon = new FontIcon("fas-question");
questionIcon.getStyleClass().add("settings_plugins_plugin_help_icon");
helpButton.setGraphic(questionIcon);
helpButton.setOnAction(event -> hostServices.showDocument(action.getHelpLink()));
headerHBox.getChildren().addAll(new SpaceFiller(FillerType.HBox),helpButton);
}
Label authorLabel = new Label(action.getAuthor());
Label moduleLabel = new Label(action.getModuleName());
Label versionLabel = new Label("Version : "+action.getVersion().getText());
VBox serverPropertiesVBox = new VBox();
serverPropertiesVBox.setSpacing(10.0);
List<Property> serverProperties = action.getServerProperties().get();
ArrayList<UIPropertyBox> serverPropertyArrayList = new ArrayList<>();
for(int j =0; j<serverProperties.size(); j++)
{
Property eachProperty = serverProperties.get(j);
if(!eachProperty.isVisible())
continue;
Label label = new Label(eachProperty.getDisplayName());
Region region = new Region();
HBox.setHgrow(region, Priority.ALWAYS);
HBox hBox = new HBox(label, new SpaceFiller(SpaceFiller.FillerType.HBox));
//hBox.setId(j+"");
Node controlNode = null;
if(eachProperty.getControlType() == ControlType.COMBO_BOX)
{
ComboBox<String> comboBox = new ComboBox<>();
comboBox.getItems().addAll(eachProperty.getListValue());
comboBox.getSelectionModel().select(eachProperty.getSelectedIndex());
hBox.getChildren().add(comboBox);
controlNode = comboBox;
}
else if(eachProperty.getControlType() == ControlType.TEXT_FIELD)
{
TextField textField = new TextField(eachProperty.getRawValue());
hBox.getChildren().add(textField);
controlNode = textField;
}
else if(eachProperty.getControlType() == ControlType.TOGGLE)
{
ToggleButton toggleButton = new ToggleButton();
toggleButton.setSelected(eachProperty.getBoolValue());
if(eachProperty.getBoolValue())
toggleButton.setText("ON");
else
toggleButton.setText("OFF");
toggleButton.selectedProperty().addListener((observableValue, aBoolean, t1) -> {
if(t1)
toggleButton.setText("ON");
else
toggleButton.setText("OFF");
});
hBox.getChildren().add(toggleButton);
controlNode = toggleButton;
}
else if(eachProperty.getControlType() == ControlType.SLIDER_DOUBLE)
{
Slider slider = new Slider();
slider.setValue(eachProperty.getDoubleValue());
slider.setMax(eachProperty.getMaxDoubleValue());
slider.setMin(eachProperty.getMinDoubleValue());
hBox.getChildren().add(slider);
controlNode = slider;
}
else if(eachProperty.getControlType() == ControlType.SLIDER_INTEGER)
{
Slider slider = new Slider();
slider.setValue(eachProperty.getIntValue());
slider.setMax(eachProperty.getMaxIntValue());
slider.setMin(eachProperty.getMinIntValue());
slider.setBlockIncrement(1.0);
slider.setSnapToTicks(true);
hBox.getChildren().add(slider);
controlNode = slider;
}
UIPropertyBox serverProperty = new UIPropertyBox(j, eachProperty.getDisplayName(), controlNode, eachProperty.getControlType(), eachProperty.getType(), eachProperty.isCanBeBlank());
serverPropertyArrayList.add(serverProperty);
serverPropertiesVBox.getChildren().add(hBox);
}
PluginProperties pp = new PluginProperties(i, serverPropertyArrayList, action.getName());
pluginProperties.add(pp);
Region region1 = new Region();
region1.setPrefHeight(5);
Platform.runLater(()->{
VBox vBox = new VBox();
vBox.setSpacing(5.0);
vBox.getChildren().addAll(headerHBox, authorLabel, moduleLabel, versionLabel, serverPropertiesVBox);
if(action.getButtonBar()!=null)
vBox.getChildren().add(new HBox(new SpaceFiller(SpaceFiller.FillerType.HBox), action.getButtonBar()));
vBox.getChildren().add(region1);
//vBox.setId(i+"");
vBox.getStyleClass().add("settings_plugins_each_action");
pluginsSettingsVBox.getChildren().add(vBox);
});
}
}
public class PluginProperties
{
private int index;
private ArrayList<UIPropertyBox> serverProperty;
private String name;
public PluginProperties(int index, ArrayList<UIPropertyBox> serverProperty, String name)
{
this.index = index;
this.serverProperty = serverProperty;
this.name = name;
}
public String getName()
{
return name;
}
public int getIndex() {
return index;
}
public ArrayList<UIPropertyBox> getServerPropertyUIBox() {
return serverProperty;
}
}
}
package com.StreamPi.Server.Window.Settings;
import com.StreamPi.Server.Connection.ServerListener;
import com.StreamPi.Server.Window.ExceptionAndAlertHandler;
import com.StreamPi.ThemeAPI.Theme;
import javafx.application.HostServices;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.CacheHint;
import javafx.scene.control.*;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
public class SettingsBase extends VBox {
private TabPane tabPane;
private GeneralSettings generalSettings;
private PluginsSettings pluginsSettings;
private ThemesSettings themesSettings;
private ClientsSettings clientsSettings;
private Button closeButton;
private HostServices hostServices;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
public SettingsBase(HostServices hostServices, ExceptionAndAlertHandler exceptionAndAlertHandler,
ServerListener serverListener)
{
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
this.hostServices = hostServices;
tabPane = new TabPane();
tabPane.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE);
VBox.setVgrow(tabPane, Priority.ALWAYS);
Tab generalSettingsTab = new Tab("General");
generalSettings = new GeneralSettings(exceptionAndAlertHandler, serverListener, hostServices);
generalSettingsTab.setContent(generalSettings);
Tab pluginsSettingsTab = new Tab("Plugins");
pluginsSettings = new PluginsSettings(exceptionAndAlertHandler, hostServices);
pluginsSettingsTab.setContent(pluginsSettings);
Tab themesSettingsTab = new Tab("Themes");
themesSettings = new ThemesSettings(hostServices);
themesSettingsTab.setContent(themesSettings);
Tab clientsSettingsTab = new Tab("Clients");
clientsSettings = new ClientsSettings(exceptionAndAlertHandler, serverListener);
clientsSettingsTab.setContent(clientsSettings);
Tab aboutTab = new Tab("About");
aboutTab.setContent(new About(hostServices));
tabPane.getTabs().addAll(generalSettingsTab, pluginsSettingsTab, themesSettingsTab, clientsSettingsTab, aboutTab);
setAlignment(Pos.TOP_RIGHT);
closeButton = new Button("Close");
VBox.setMargin(closeButton, new Insets(10.0));
getChildren().addAll(tabPane, closeButton);
setCache(true);
setCacheHint(CacheHint.SPEED);
}
public void setDefaultTabToGeneral()
{
tabPane.getSelectionModel().selectFirst();
}
public Button getCloseButton()
{
return closeButton;
}
public GeneralSettings getGeneralSettings()
{
return generalSettings;
}
public PluginsSettings getPluginsSettings()
{
return pluginsSettings;
}
public ThemesSettings getThemesSettings()
{
return themesSettings;
}
public ClientsSettings getClientsSettings()
{
return clientsSettings;
}
}
package com.StreamPi.Server.Window.Settings;
import com.StreamPi.ActionAPI.Action.Action;
import com.StreamPi.ActionAPI.ActionProperty.Property.ControlType;
import com.StreamPi.ActionAPI.ActionProperty.Property.Property;
import com.StreamPi.ActionAPI.NormalAction.NormalAction;
import com.StreamPi.Server.Action.NormalActionPlugins;
import com.StreamPi.Server.Connection.ServerListener;
import com.StreamPi.Server.Controller.Controller;
import com.StreamPi.Server.IO.Config;
import com.StreamPi.ThemeAPI.Theme;
import com.StreamPi.ThemeAPI.Themes;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.Exception.SevereException;
import com.StreamPi.Util.FormHelper.SpaceFiller;
import javafx.application.HostServices;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
public class ThemesSettings extends VBox
{
private VBox themesSettingsVBox;
private Controller controller;
private Logger logger;
private HostServices hostServices;
public ThemesSettings(HostServices hostServices)
{
this.hostServices = hostServices;
getStyleClass().add("themes_settings");
logger = Logger.getLogger(ThemesSettings.class.getName());
setPadding(new Insets(10));
themesSettingsVBox = new VBox();
themesSettingsVBox.setSpacing(10.0);
themesSettingsVBox.setAlignment(Pos.TOP_CENTER);
ScrollPane scrollPane = new ScrollPane();
scrollPane.getStyleClass().add("themes_settings_scroll_pane");
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollPane.maxWidthProperty().bind(widthProperty().multiply(0.8));
themesSettingsVBox.prefWidthProperty().bind(scrollPane.widthProperty().subtract(10));
scrollPane.setContent(themesSettingsVBox);
VBox.setVgrow(scrollPane, Priority.ALWAYS);
setAlignment(Pos.TOP_CENTER);
getChildren().addAll(scrollPane);
toggleButtons = new ArrayList<>();
}
public void setController(Controller controller)
{
this.controller = controller;
}
private Themes themes;
private String currentThemeFullName;
public void setThemes(Themes themes)
{
this.themes = themes;
}
public void setCurrentThemeFullName(String currentThemeFullName)
{
this.currentThemeFullName = currentThemeFullName;
}
private ArrayList<ToggleButton> toggleButtons;
public void loadThemes()
{
toggleButtons.clear();
Platform.runLater(()-> themesSettingsVBox.getChildren().clear());
for(int i = 0; i<themes.getThemeList().size(); i++)
{
Theme theme = themes.getThemeList().get(i);
Label shortNameLabel = new Label(theme.getShortName());
shortNameLabel.getStyleClass().add("settings_themes_each_theme_heading");
Label authorLabel = new Label(theme.getAuthor());
Label fullNameLabel = new Label(theme.getFullName());
HBox topRowHBox = new HBox(shortNameLabel);
if(theme.getWebsite() != null)
{
Button helpButton = new Button();
FontIcon questionIcon = new FontIcon("fas-question");
questionIcon.getStyleClass().add("settings_themes_theme_help_icon");
helpButton.setGraphic(questionIcon);
helpButton.setOnAction(event -> hostServices.showDocument(theme.getWebsite()));
topRowHBox.getChildren().addAll(new SpaceFiller(SpaceFiller.FillerType.HBox), helpButton);
}
Label versionLabel = new Label("Version : "+theme.getVersion().getText());
ToggleButton toggleButton = new ToggleButton();
toggleButton.setSelected(theme.getFullName().equals(currentThemeFullName));
toggleButton.setId(theme.getFullName());
if(theme.getFullName().equals(currentThemeFullName))
{
toggleButton.setText("ON");
toggleButton.setSelected(true);
toggleButton.setDisable(true);
}
else
{
toggleButton.setText("OFF");
}
toggleButton.setOnAction(event -> {
ToggleButton toggleButton1 = (ToggleButton) event.getSource();
toggleButton.setText("ON");
try {
Config.getInstance().setCurrentThemeFullName(toggleButton1.getId());
Config.getInstance().save();
for(ToggleButton toggleButton2 : toggleButtons)
{
if(toggleButton2.getId().equals(Config.getInstance().getCurrentThemeFullName()))
{
toggleButton2.setDisable(true);
toggleButton2.setText("ON");
toggleButton2.setSelected(true);
}
else
{
toggleButton2.setDisable(false);
toggleButton2.setText("OFF");
toggleButton2.setSelected(false);
}
}
controller.initThemes();
}
catch (SevereException e)
{
controller.handleSevereException(e);
}
});
HBox hBox = new HBox(toggleButton);
Region region1 = new Region();
region1.setPrefHeight(5);
hBox.setAlignment(Pos.TOP_RIGHT);
VBox vBox = new VBox(topRowHBox, authorLabel, versionLabel, fullNameLabel, hBox, region1);
vBox.setSpacing(5.0);
vBox.getStyleClass().add("settings_themes_each_theme");
Platform.runLater(()->themesSettingsVBox.getChildren().add(vBox));
toggleButtons.add(toggleButton);
}
}
}
/*
Stream-Pi - Free & Open-Source Modular Cross-Platform Programmable Macropad
Copyright (C) 2019-2021 Debayan Sutradhar (rnayabed), Samuel Quiñones (SamuelQuinones)
This program 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.
This program 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.
Written by : Debayan Sutradhar (rnayabed)
*/
package com.stream_pi.server;
import com.stream_pi.server.controller.Controller;
import com.stream_pi.server.info.ServerInfo;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
/**
* First method to be called
* This method first parses all the available command line arguments passed.
* Then a new instance of controller is created, and then initialised.
*/
public void start(Stage stage) {
for(String eachArg : getParameters().getRaw())
{
String[] r = eachArg.split("=");
if(r[0].equals("-DStreamPi.startupRunnerFileName"))
ServerInfo.getInstance().setRunnerFileName(r[1]);
else if(r[0].equals("-DStreamPi.startupMode"))
ServerInfo.getInstance().setStartMinimised(r[1].equals("min"));
}
Controller d = new Controller();
Scene s = new Scene(d);
stage.setScene(s);
d.setHostServices(getHostServices());
d.init();
}
/**
* This is a fallback. Called in some JVMs.
* This method just sends the command line arguments to JavaFX Application
*/
public static void main(String[] args)
{
launch(args);
}
}
/*
Stream-Pi - Free & Open-Source Modular Cross-Platform Programmable Macropad
Copyright (C) 2019-2021 Debayan Sutradhar (rnayabed), Samuel Quiñones (SamuelQuinones)
This program 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.
This program 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.
Written by : Debayan Sutradhar (rnayabed)
*/
package com.stream_pi.server.action;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import com.stream_pi.actionapi.action.Action;
import com.stream_pi.actionapi.action.ActionType;
import com.stream_pi.actionapi.action.PropertySaver;
import com.stream_pi.actionapi.action.ServerConnection;
import com.stream_pi.actionapi.actionproperty.ServerProperties;
import com.stream_pi.actionapi.actionproperty.property.Property;
import com.stream_pi.actionapi.actionproperty.property.Type;
import com.stream_pi.actionapi.normalaction.NormalAction;
import com.stream_pi.util.exception.MinorException;
import com.stream_pi.util.exception.SevereException;
import com.stream_pi.util.exception.StreamPiException;
import com.stream_pi.util.version.Version;
import com.stream_pi.util.xmlconfighelper.XMLConfigHelper;
import org.w3c.dom.NodeList;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import java.io.File;
import java.lang.module.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ServiceLoader;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.w3c.dom.Element;
public class NormalActionPlugins
{
private static NormalActionPlugins instance = null;
private final Logger logger;
private File configFile;
private Document document;
private static String pluginsLocation = null;
/**
* Singleton class instance getter. Creates new one, when asked for the first time
*
* @return returns instance of NormalActionPlugins (one and only, always)
*/
public static synchronized NormalActionPlugins getInstance()
{
if(instance == null)
{
instance = new NormalActionPlugins();
}
return instance;
}
/**
* Sets the folder location where the plugin JARs and their dependencies are stored
*
* @param location Folder location
*/
public static void setPluginsLocation(String location)
{
pluginsLocation = location;
}
/**
* Private constructor
*/
private NormalActionPlugins()
{
logger = Logger.getLogger(NormalActionPlugins.class.getName());
normalPluginsHashmap = new HashMap<>();
}
/**
* init Method
*/
public void init() throws SevereException, MinorException
{
registerPlugins();
initPlugins();
}
/**
* Used to fetch list of all external Plugins
*
* @return List of plugins
*/
public List<NormalAction> getPlugins()
{
return normalPlugins;
}
/**
* Returns a plugin by its module name
*
* @param name Module Name
* @return The plugin. If not found, then null is returned
*/
public NormalAction getPluginByModuleName(String name)
{
logger.info("Plugin being requested : "+name);
Integer index = normalPluginsHashmap.getOrDefault(name, -1);
if(index != -1)
{
return normalPlugins.get(index);
}
return null;
}
private List<NormalAction> normalPlugins = null;
HashMap<String, Integer> normalPluginsHashmap;
/**
* Used to register plugins from plugin location
*/
public void registerPlugins() throws SevereException, MinorException
{
logger.info("Registering external plugins from "+pluginsLocation+" ...");
try
{
configFile = new File(pluginsLocation+"/config.xml");
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
document = docBuilder.parse(configFile);
}
catch (Exception e)
{
e.printStackTrace();
throw new SevereException("Plugins","Error reading plugins config.xml. Cannot continue.");
}
ArrayList<NormalAction> errorModules = new ArrayList<>();
ArrayList<String> errorModuleError = new ArrayList<>();
ArrayList<Action> pluginsConfigs = new ArrayList<>();
NodeList actionsNode = document.getElementsByTagName("actions").item(0).getChildNodes();
for(int i =0;i<actionsNode.getLength();i++)
{
Node eachActionNode = actionsNode.item(i);
if(eachActionNode.getNodeType() != Node.ELEMENT_NODE)
continue;
if(!eachActionNode.getNodeName().equals("action"))
continue;
Element eachActionElement = (Element) eachActionNode;
String name;
Version version;
try
{
name = XMLConfigHelper.getStringProperty(eachActionElement, "module-name");
version = new Version(XMLConfigHelper.getStringProperty(eachActionElement, "version"));
}
catch (Exception e)
{
logger.log(Level.WARNING, "Skipping configuration because invalid ...");
e.printStackTrace();
continue;
}
ServerProperties serverProperties = new ServerProperties();
NodeList serverPropertiesNodeList = eachActionElement.getElementsByTagName("properties").item(0).getChildNodes();
for(int j = 0;j<serverPropertiesNodeList.getLength();j++)
{
Node eachPropertyNode = serverPropertiesNodeList.item(j);
if(eachPropertyNode.getNodeType() != Node.ELEMENT_NODE)
continue;
if(!eachPropertyNode.getNodeName().equals("property"))
continue;
Element eachPropertyElement = (Element) eachPropertyNode;
try
{
Property property = new Property(XMLConfigHelper.getStringProperty(eachPropertyElement, "name"), Type.STRING);
property.setRawValue(XMLConfigHelper.getStringProperty(eachPropertyElement, "value"));
serverProperties.addProperty(property);
}
catch (Exception e)
{
logger.log(Level.WARNING, "Skipping property because invalid ...");
e.printStackTrace();
}
}
Action action = new Action(ActionType.NORMAL);
action.setModuleName(name);
action.setVersion(version);
action.getServerProperties().set(serverProperties);
pluginsConfigs.add(action);
}
logger.info("Size : "+pluginsConfigs.size());
Path pluginsDir = Paths.get(pluginsLocation); // Directory with plugins JARs
try
{
// Search for plugins in the plugins directory
ModuleFinder pluginsFinder = ModuleFinder.of(pluginsDir);
// Find all names of all found plugin modules
List<String> p = pluginsFinder
.findAll()
.stream()
.map(ModuleReference::descriptor)
.map(ModuleDescriptor::name)
.collect(Collectors.toList());
// Create configuration that will resolve plugin modules
// (verify that the graph of modules is correct)
Configuration pluginsConfiguration = ModuleLayer
.boot()
.configuration()
.resolve(pluginsFinder, ModuleFinder.of(), p);
// Create a module layer for plugins
ModuleLayer layer = ModuleLayer
.boot()
.defineModulesWithOneLoader(pluginsConfiguration, ClassLoader.getSystemClassLoader());
logger.info("Loading plugins from jar ...");
// Now you can use the new module layer to find service implementations in it
normalPlugins = ServiceLoader
.load(layer, NormalAction.class).stream()
.map(ServiceLoader.Provider::get)
.collect(Collectors.toList());
logger.info("...Done!");
}
catch (Exception e)
{
e.printStackTrace();
throw new MinorException("Error", "Error loading modules\n"+e.getMessage()+"\nPlease fix the errors. Other plugins wont be loaded.");
}
sortedPlugins = new HashMap<>();
for (NormalAction eachPlugin : normalPlugins)
{
try
{
eachPlugin.setPropertySaver(propertySaver);
eachPlugin.setServerConnection(serverConnection);
eachPlugin.initProperties();
Action foundAction = null;
for (Action action : pluginsConfigs) {
if (action.getModuleName().equals(eachPlugin.getModuleName())
&& action.getVersion().isEqual(eachPlugin.getVersion())) {
foundAction = action;
List<Property> eachPluginStoredProperties = action.getServerProperties().get();
List<Property> eachPluginCodeProperties = eachPlugin.getServerProperties().get();
for (int i =0;i< eachPluginCodeProperties.size(); i++) {
Property eachPluginCodeProperty = eachPluginCodeProperties.get(i);
Property foundProp = null;
for (Property eachPluginStoredProperty : eachPluginStoredProperties) {
if (eachPluginCodeProperty.getName().equals(eachPluginStoredProperty.getName())) {
eachPluginCodeProperty.setRawValue(eachPluginStoredProperty.getRawValue());
foundProp = eachPluginStoredProperty;
}
}
eachPluginCodeProperties.set(i, eachPluginCodeProperty);
if (foundProp != null) {
eachPluginStoredProperties.remove(foundProp);
}
}
eachPlugin.getServerProperties().set(eachPluginCodeProperties);
break;
}
}
if (foundAction != null)
pluginsConfigs.remove(foundAction);
else
{
List<Property> eachPluginStoredProperties = eachPlugin.getServerProperties().get();
for(Property property :eachPluginStoredProperties)
{
if(property.getType() == Type.STRING || property.getType() == Type.INTEGER || property.getType() == Type.DOUBLE)
property.setRawValue(property.getDefaultRawValue());
}
}
if (!sortedPlugins.containsKey(eachPlugin.getCategory())) {
ArrayList<NormalAction> actions = new ArrayList<>();
sortedPlugins.put(eachPlugin.getCategory(), actions);
}
sortedPlugins.get(eachPlugin.getCategory()).add(eachPlugin);
}
catch (Exception e)
{
e.printStackTrace();
errorModules.add(eachPlugin);
errorModuleError.add(e.getMessage());
}
}
try {
saveServerSettings();
} catch (MinorException e) {
e.printStackTrace();
}
logger.log(Level.INFO, "All plugins registered!");
if(errorModules.size() > 0)
{
StringBuilder errors = new StringBuilder("The following action modules could not be loaded:");
for(int i = 0; i<errorModules.size(); i++)
{
normalPlugins.remove(errorModules.get(i));
errors.append("\n * ").append(errorModules.get(i).getModuleName()).append("\n(")
.append(errorModuleError.get(i)).append(")");
}
throw new MinorException("Plugins", errors.toString());
}
for(int i = 0;i<normalPlugins.size();i++)
{
normalPluginsHashmap.put(normalPlugins.get(i).getModuleName(), i);
}
}
/**
* Used to init plugins
*/
public void initPlugins() throws MinorException
{
StringBuilder errors = new StringBuilder("There were errors registering the following plugins. As a result, they have been omitted : ");
boolean isError = false;
for(NormalAction eachPlugin : normalPlugins)
{
try
{
eachPlugin.initAction();
}
catch (Exception e)
{
e.printStackTrace();
isError = true;
errors.append("\n* ")
.append(eachPlugin.getName())
.append(" - ")
.append(eachPlugin.getModuleName())
.append("\n");
if(e instanceof StreamPiException)
errors.append(((MinorException) e).getShortMessage());
errors.append("\n");
}
}
if(isError)
{
throw new MinorException("Plugin init error", errors.toString());
}
}
HashMap<String, ArrayList<NormalAction>> sortedPlugins;
/**
* Gets list of sorted plugins
*
* @return Hashmap with category key, and list of plugins of each category
*/
public HashMap<String, ArrayList<NormalAction>> getSortedPlugins()
{
return sortedPlugins;
}
/**
* @return Gets actions element from the config.xml in plugins folder
*/
private Element getActionsElement()
{
return (Element) document.getElementsByTagName("actions").item(0);
}
/**
* Saves ServerProperties of every plugin in config.xml in plugins folder
*
* @throws MinorException Thrown when failed to save settings
*/
public void saveServerSettings() throws MinorException
{
XMLConfigHelper.removeChilds(getActionsElement());
for(NormalAction normalAction : normalPlugins)
{
Element actionElement = document.createElement("action");
getActionsElement().appendChild(actionElement);
Element moduleNameElement = document.createElement("module-name");
moduleNameElement.setTextContent(normalAction.getModuleName());
actionElement.appendChild(moduleNameElement);
Element versionElement = document.createElement("version");
versionElement.setTextContent(normalAction.getVersion().getText());
actionElement.appendChild(versionElement);
Element propertiesElement = document.createElement("properties");
actionElement.appendChild(propertiesElement);
for(String key : normalAction.getServerProperties().getNames())
{
for(Property eachProperty : normalAction.getServerProperties().getMultipleProperties(key))
{
Element propertyElement = document.createElement("property");
propertiesElement.appendChild(propertyElement);
Element nameElement = document.createElement("name");
nameElement.setTextContent(eachProperty.getName());
propertyElement.appendChild(nameElement);
Element valueElement = document.createElement("value");
valueElement.setTextContent(eachProperty.getRawValue());
propertyElement.appendChild(valueElement);
}
}
}
save();
}
private PropertySaver propertySaver = null;
/**
* Set PropertySaver class
* @param propertySaver instance of PropertySaver
*/
public void setPropertySaver(PropertySaver propertySaver)
{
this.propertySaver = propertySaver;
}
private ServerConnection serverConnection = null;
/**
* Set setServerConnection class
* @param serverConnection instance of ServerConnection
*/
public void setServerConnection(ServerConnection serverConnection)
{
this.serverConnection = serverConnection;
}
/**
* Get plugin from index from list
*
* @param index of plugin
* @return found plugin
*/
public NormalAction getActionFromIndex(int index)
{
return normalPlugins.get(index);
}
/**
* Calls onShutDown method in every plugin
*/
public void shutDownActions()
{
if(normalPlugins != null)
{
for(NormalAction eachPlugin : normalPlugins)
{
try
{
eachPlugin.onShutDown();
}
catch (Exception e)
{
e.printStackTrace();
}
}
normalPlugins.clear();
}
}
/**
* Saves all Server Properties of each Plugin in config.xml in Plugins folder
* @throws MinorException thrown when failed to save
*/
public void save() throws MinorException
{
try
{
Transformer transformer = TransformerFactory.newInstance().newTransformer();
Result output = new StreamResult(configFile);
Source input = new DOMSource(document);
transformer.transform(input, output);
}
catch (Exception e)
{
throw new MinorException("Config", "unable to save server plugins settings");
}
}
}
/*
Stream-Pi - Free & Open-Source Modular Cross-Platform Programmable Macropad
Copyright (C) 2019-2021 Debayan Sutradhar (rnayabed), Samuel Quiñones (SamuelQuinones)
This program 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.
This program 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.
Written by : Debayan Sutradhar (rnayabed)
*/
package com.stream_pi.server.client;
import com.stream_pi.util.platform.Platform;
import com.stream_pi.util.platform.ReleaseStatus;
import com.stream_pi.util.version.Version;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class Client {
private String nickName;
private final SocketAddress remoteSocketAddress;
private final Platform platform;
private final Version version;
private final Version commStandardVersion;
private final Version themeAPIVersion;
private final ReleaseStatus releaseStatus;
private double startupDisplayHeight, startupDisplayWidth;
private final HashMap<String,ClientProfile> profiles;
private final HashMap<String,ClientTheme> themes;
private String defaultProfileID;
private String defaultThemeFullName;
public Client(Version version, ReleaseStatus releaseStatus, Version commStandardVersion, Version themeAPIVersion, String nickName, Platform platform, SocketAddress remoteSocketAddress)
{
this.version = version;
this.releaseStatus = releaseStatus;
this.commStandardVersion = commStandardVersion;
this.themeAPIVersion = themeAPIVersion;
this.nickName = nickName;
this.remoteSocketAddress = remoteSocketAddress;
this.platform = platform;
this.profiles = new HashMap<>();
this.themes = new HashMap<>();
}
public ReleaseStatus getReleaseStatus() {
return releaseStatus;
}
public void setDefaultThemeFullName(String defaultThemeFullName) {
this.defaultThemeFullName = defaultThemeFullName;
}
public String getDefaultThemeFullName() {
return defaultThemeFullName;
}
public void setDefaultProfileID(String ID)
{
defaultProfileID = ID;
}
public void addTheme(ClientTheme clientTheme) throws CloneNotSupportedException
{
themes.put(clientTheme.getThemeFullName(), (ClientTheme) clientTheme.clone());
}
public ArrayList<ClientTheme> getThemes()
{
ArrayList<ClientTheme> clientThemes = new ArrayList<>();
for(String clientTheme : themes.keySet())
{
clientThemes.add(themes.get(clientTheme));
}
return clientThemes;
}
public ClientTheme getThemeByFullName(String fullName)
{
return themes.getOrDefault(fullName, null);
}
public String getDefaultProfileID()
{
return defaultProfileID;
}
//client Profiles
public void setNickName(String nickName)
{
this.nickName = nickName;
}
public List<ClientProfile> getAllClientProfiles()
{
ArrayList<ClientProfile> clientProfiles = new ArrayList<>();
for(String profile : profiles.keySet())
clientProfiles.add(profiles.get(profile));
return clientProfiles;
}
public void removeProfileFromID(String ID)
{
profiles.remove(ID);
}
public void addProfile(ClientProfile clientProfile) throws CloneNotSupportedException {
profiles.put(clientProfile.getID(), (ClientProfile) clientProfile.clone());
}
public synchronized ClientProfile getProfileByID(String ID) {
return profiles.getOrDefault(ID, null);
}
public SocketAddress getRemoteSocketAddress()
{
return remoteSocketAddress;
}
public Platform getPlatform()
{
return platform;
}
public String getNickName()
{
return nickName;
}
public Version getVersion()
{
return version;
}
public Version getCommStandardVersion()
{
return commStandardVersion;
}
public Version getThemeAPIVersion()
{
return themeAPIVersion;
}
public double getStartupDisplayHeight()
{
return startupDisplayHeight;
}
public double getStartupDisplayWidth()
{
return startupDisplayWidth;
}
public void setStartupDisplayHeight(double height)
{
startupDisplayHeight = height;
}
public void setStartupDisplayWidth(double width)
{
startupDisplayWidth = width;
}
private int getMaxRows(int eachActionSize)
{
return (int) (startupDisplayHeight / eachActionSize);
}
public int getMaxCols(int eachActionSize)
{
return (int) (startupDisplayWidth / eachActionSize);
}
}
/*
Stream-Pi - Free & Open-Source Modular Cross-Platform Programmable Macropad
Copyright (C) 2019-2021 Debayan Sutradhar (rnayabed), Samuel Quiñones (SamuelQuinones)
This program 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.
This program 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.
Written by : Debayan Sutradhar (rnayabed)
*/
package com.stream_pi.server.client;
import com.stream_pi.actionapi.action.Action;
import java.util.HashMap;
import java.util.Set;
import java.util.UUID;
public class ClientProfile implements Cloneable {
private String name, ID;
private int rows, cols, actionSize, actionGap;
private final HashMap<String, Action> actions;
public ClientProfile(String name, String ID, int rows, int cols, int actionSize, int actionGap)
{
this.actions = new HashMap<>();
this.ID = ID;
this.name = name;
this.rows = rows;
this.cols = cols;
this.actionGap = actionGap;
this.actionSize = actionSize;
}
public ClientProfile(String name, int rows, int cols, int actionSize, int actionGap)
{
this(name, UUID.randomUUID().toString(), rows, cols, actionSize, actionGap);
}
public Action getActionByID(String ID)
{
return actions.get(ID);
}
public void removeActionByID(String ID)
{
actions.remove(ID);
}
public Set<String> getActionsKeySet() {
return actions.keySet();
}
public synchronized void addAction(Action action) throws CloneNotSupportedException {
actions.put(action.getID(), action.clone());
}
public String getID()
{
return ID;
}
public String getName()
{
return name;
}
public int getRows()
{
return rows;
}
public int getCols()
{
return cols;
}
public int getActionSize()
{
return actionSize;
}
public int getActionGap()
{
return actionGap;
}
public void setRows(int rows)
{
this.rows = rows;
}
public void setCols(int cols)
{
this.cols = cols;
}
public void setID(String ID)
{
this.ID = ID;
}
public void setActionSize(int actionSize)
{
this.actionSize = actionSize;
}
public void setActionGap(int actionGap)
{
this.actionGap = actionGap;
}
public void setName(String name)
{
this.name = name;
}
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
}
package com.stream_pi.server.client;
public class ClientTheme implements Cloneable
{
public String themeFullName;
public String shortName;
public String author;
public String version;
public ClientTheme(String themeFullName, String shortName,
String author, String version)
{
this.themeFullName = themeFullName;
this.shortName = shortName;
this.author = author;
this.version = version;
}
public String getThemeFullName()
{
return themeFullName;
}
public String getShortName()
{
return shortName;
}
public String getAuthor()
{
return author;
}
public String getVersion() {
return version;
}
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
}
package com.stream_pi.server.connection;
import com.stream_pi.actionapi.action.Action;
import com.stream_pi.actionapi.action.ActionType;
import com.stream_pi.actionapi.action.DisplayTextAlignment;
import com.stream_pi.actionapi.action.Location;
import com.stream_pi.actionapi.actionproperty.ClientProperties;
import com.stream_pi.actionapi.actionproperty.property.Property;
import com.stream_pi.actionapi.actionproperty.property.Type;
import com.stream_pi.actionapi.normalaction.NormalAction;
import com.stream_pi.server.action.NormalActionPlugins;
import com.stream_pi.server.client.Client;
import com.stream_pi.server.client.ClientProfile;
import com.stream_pi.server.client.ClientTheme;
import com.stream_pi.server.info.ServerInfo;
import com.stream_pi.server.window.ExceptionAndAlertHandler;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertType;
import com.stream_pi.util.exception.MinorException;
import com.stream_pi.util.exception.SevereException;
import com.stream_pi.util.exception.StreamPiException;
import com.stream_pi.util.platform.Platform;
import com.stream_pi.util.platform.ReleaseStatus;
import com.stream_pi.util.version.Version;
import javafx.concurrent.Task;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ClientConnection extends Thread{
private Socket socket;
private ServerListener serverListener;
private AtomicBoolean stop = new AtomicBoolean(false);
private DataInputStream dis;
private DataOutputStream dos;
private Logger logger;
private Client client = null;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
public ClientConnection(Socket socket, ServerListener serverListener, ExceptionAndAlertHandler exceptionAndAlertHandler)
{
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
//actionIconsToBeSent = new ArrayList__();
this.socket = socket;
this.serverListener = serverListener;
logger = Logger.getLogger(ClientConnection.class.getName());
try
{
dis = new DataInputStream(socket.getInputStream());
dos = new DataOutputStream(socket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
exceptionAndAlertHandler.handleMinorException(new MinorException("Unable to start socket streams"));
}
start();
}
public synchronized void exit()
{
if(stop.get())
return;
logger.info("Stopping ...");
try
{
if(socket !=null)
{
logger.info("Stopping connection "+socket.getRemoteSocketAddress());
disconnect();
}
}
catch (SevereException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleSevereException(e);
}
}
public synchronized void exitAndRemove()
{
exit();
removeConnection();
serverListener.clearTemp();
}
public void removeConnection()
{
ClientConnections.getInstance().removeConnection(this);
}
public void writeToStream(String text) throws SevereException
{
/*try
{
logger.debug(text);
dos.writeUTF(text);
dos.flush();
}
catch (IOException e)
{
e.printStackTrace();
throw new SevereException("Unable to write to io Stream!");
}*/
try
{
byte[] txtBytes = text.getBytes();
Thread.sleep(50);
dos.writeUTF("string:: ::");
dos.flush();
dos.writeInt(txtBytes.length);
dos.flush();
write(txtBytes);
dos.flush();
}
catch (IOException | InterruptedException e)
{
e.printStackTrace();
throw new SevereException("Unable to write to io Stream!");
}
}
public void sendIcon(String profileID, String actionID, byte[] icon) throws SevereException
{
try
{
logger.info("Sending action Icon...");
//Thread.sleep(50);
System.out.println("1");
dos.writeUTF("action_icon::"+profileID+"!!"+actionID+"!!::"+icon.length);
System.out.println("2");
dos.flush();
System.out.println("3");
dos.writeInt(icon.length);
System.out.println("4");
dos.flush();
System.out.println("5");
write(icon);
System.out.println("6");
dos.flush();
System.out.println("7");
}
catch (IOException e)
{
e.printStackTrace();
throw new SevereException("Unable to write to io Stream!");
}
}
public void write(byte[] array) throws SevereException
{
try
{
dos.write(array);
}
catch (IOException e)
{
e.printStackTrace();
throw new SevereException("Unable to write to io Stream!");
}
}
public void initAfterConnectionQueryReceive(String[] arr) throws StreamPiException
{
logger.info("Setting up client object ...");
Version clientVersion;
Version commsStandard;
Version themesStandard;
ReleaseStatus releaseStatus;
try
{
clientVersion = new Version(arr[1]);
releaseStatus = ReleaseStatus.valueOf(arr[2]);
commsStandard = new Version(arr[3]);
themesStandard = new Version(arr[4]);
}
catch (MinorException e)
{
exitAndRemove();
throw new MinorException(e.getShortMessage()+" (client '"+socket.getRemoteSocketAddress()+"' )");
}
if(!commsStandard.isEqual(ServerInfo.getInstance().getCommStandardVersion()))
{
String errTxt = "Server and client Communication standards do not match. Make sure you are on the latest version of server and client.\n" +
"Server Comms. Standard : "+ServerInfo.getInstance().getCommStandardVersion().getText()+
"\nclient Comms. Standard : "+commsStandard.getText();
disconnect(errTxt);
throw new MinorException(errTxt);
}
client = new Client(clientVersion, releaseStatus, commsStandard, themesStandard, arr[5], Platform.valueOf(arr[8]), socket.getRemoteSocketAddress());
client.setStartupDisplayWidth(Double.parseDouble(arr[6]));
client.setStartupDisplayHeight(Double.parseDouble(arr[7]));
client.setDefaultProfileID(arr[9]);
client.setDefaultThemeFullName(arr[10]);
//call get profiles command.
serverListener.clearTemp();
}
public synchronized Client getClient()
{
return client;
}
@Override
public void run() {
try {
initAfterConnectionQuerySend();
} catch (SevereException e) {
e.printStackTrace();
exceptionAndAlertHandler.handleSevereException(e);
exitAndRemove();
return;
}
try
{
while(!stop.get())
{
String msg = "";
try
{
String raw = dis.readUTF();
int length = dis.readInt();
System.out.println("SIZE TO READ : "+length);
String[] precursor = raw.split("::");
String inputType = precursor[0];
String secondArg = precursor[1];
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
/*int count;
int chunkSize = 512;
while (length>0)
{
if(chunkSize > length)
chunkSize = length;
else
chunkSize = 512;
byte[] buffer = new byte[chunkSize];
count = dis.read(buffer);
System.out.println(count);
byteArrayOutputStream.write(buffer);
length-=count;
}*/
/*byte[] buffer = new byte[8192];
int read;
while((read = dis.read(buffer)) != -1){
System.out.println("READ : "+read);
byteArrayOutputStream.write(buffer, 0, read);
}
System.out.println("READ : "+byteArrayOutputStream.size());
byteArrayOutputStream.close();
byte[] bArr = byteArrayOutputStream.toByteArray();*/
byte[] bArr = new byte[length];
dis.readFully(bArr);
if(inputType.equals("string"))
{
msg = new String(bArr);
}
else if(inputType.equals("action_icon"))
{
String[] secondArgSep = secondArg.split("!!");
String profileID = secondArgSep[0];
String actionID = secondArgSep[1];
getClient().getProfileByID(profileID).getActionByID(actionID).setIcon(bArr);
//serverListener.clearTemp();
continue;
}
}
catch (IOException e)
{
logger.log(Level.SEVERE, e.getMessage());
e.printStackTrace();
serverListener.clearTemp();
if(!stop.get())
{
removeConnection();
throw new MinorException("Accidentally disconnected from "+getClient().getNickName()+".");
}
exitAndRemove();
return;
}
logger.info("Received text : '"+msg+"'");
String[] sep = msg.split("::");
String command = sep[0];
switch (command)
{
case "disconnect" : clientDisconnected(msg);
break;
case "client_details" : initAfterConnectionQueryReceive(sep);
getProfilesFromClient();
getThemesFromClient();
break;
case "profiles" : registerProfilesFromClient(sep);
break;
case "profile_details" : registerProfileDetailsFromClient(sep);
break;
case "action_details" : registerActionToProfile(sep);
break;
case "themes": registerThemes(sep);
break;
case "action_clicked": actionClicked(sep[1], sep[2]);
break;
default: logger.warning("Command '"+command+"' does not match records. Make sure client and server versions are equal.");
}
}
}
catch (StreamPiException e)
{
e.printStackTrace();
if(e instanceof MinorException)
exceptionAndAlertHandler.handleMinorException((MinorException) e);
else if (e instanceof SevereException)
exceptionAndAlertHandler.handleSevereException((SevereException) e);
}
}
// commands
public void initAfterConnectionQuerySend() throws SevereException
{
logger.info("Asking client details ...");
writeToStream("get_client_details::");
}
public void disconnect() throws SevereException {
disconnect("");
}
public void disconnect(String message) throws SevereException {
if(stop.get())
return;
stop.set(true);
logger.info("Sending client disconnect message ...");
writeToStream("disconnect::"+message+"::");
try
{
if(!socket.isClosed())
socket.close();
}
catch (IOException e)
{
e.printStackTrace();
throw new SevereException("Unable to close socket");
}
}
public void clientDisconnected(String message)
{
stop.set(true);
String txt = "Disconnected!";
if(!message.equals("disconnect::::"))
txt = "Message : "+message.split("::")[1];
new StreamPiAlert("Disconnected from "+getClient().getNickName()+".", txt, StreamPiAlertType.WARNING).show();;
exitAndRemove();
}
public void getProfilesFromClient() throws StreamPiException
{
logger.info("Asking client to send profiles ...");
writeToStream("get_profiles::");
}
public void getThemesFromClient() throws StreamPiException
{
logger.info("Asking clients to send themes ...");
writeToStream("get_themes::");
}
public void registerThemes(String[] sep)
{
for(int i =1; i<sep.length;i++)
{
String[] internal = sep[i].split("__");
ClientTheme clientTheme = new ClientTheme(
internal[0],
internal[1],
internal[2],
internal[3]
);
try
{
getClient().addTheme(clientTheme);
}
catch (CloneNotSupportedException e)
{
logger.log(Level.SEVERE, e.getMessage());
e.printStackTrace();
}
}
}
public void registerProfilesFromClient(String[] sep) throws StreamPiException
{
logger.info("Registering profiles ...");
int noOfProfiles = Integer.parseInt(sep[1]);
for(int i = 2; i<(noOfProfiles + 2); i++)
{
String profileID = sep[i];
getProfileDetailsFromClient(profileID);
}
}
public void getProfileDetailsFromClient(String ID) throws StreamPiException
{
logger.info("Asking client to send details of profile : "+ID);
writeToStream("get_profile_details::"+ID+"::");
}
public void registerProfileDetailsFromClient(String[] sep)
{
String ID = sep[1];
logger.info("Registering details for profile : "+ID);
String name = sep[2];
int rows = Integer.parseInt(sep[3]);
int cols = Integer.parseInt(sep[4]);
int actionSize = Integer.parseInt(sep[5]);
int actionGap = Integer.parseInt(sep[6]);
ClientProfile clientProfile = new ClientProfile(name, ID, rows, cols, actionSize, actionGap);
logger.info("Added client profile "+clientProfile.getName());
try
{
getClient().addProfile(clientProfile);
}
catch (CloneNotSupportedException e)
{
logger.severe(e.getMessage());
e.printStackTrace();
}
serverListener.clearTemp();
}
/*public void getActionIcon(String profileID, String actionID) throws StreamPiException
{
System.out.println("getting action icon from "+profileID+", "+actionID);
writeToStream("get_action_icon::"+profileID+"::"+actionID);
}*/
public synchronized void registerActionToProfile(String[] sep) throws StreamPiException
{
String profileID = sep[1];
String ID = sep[2];
ActionType actionType = ActionType.valueOf(sep[3]);
//4 - Version
//5 - ModuleName
//display
String bgColorHex = sep[6];
//icon
boolean isHasIcon = sep[7].equals("true");
boolean isShowIcon = sep[8].equals("true");
//text
boolean isShowDisplayText = sep[9].equals("true");
String displayFontColor = sep[10];
String displayText = sep[11];
DisplayTextAlignment displayTextAlignment = DisplayTextAlignment.valueOf(sep[12]);
//location
String row = sep[13];
String col = sep[14];
Location location = new Location(Integer.parseInt(row), Integer.parseInt(col));
Action action = new Action(ID, actionType);
action.setBgColourHex(bgColorHex);
action.setShowIcon(isShowIcon);
action.setHasIcon(isHasIcon);
action.setShowDisplayText(isShowDisplayText);
action.setDisplayTextFontColourHex(displayFontColor);
action.setDisplayText(displayText);
action.setDisplayTextAlignment(displayTextAlignment);
action.setLocation(location);
//client properties
int clientPropertiesSize = Integer.parseInt(sep[15]);
String root = sep[17];
action.setParent(root);
String[] clientPropertiesRaw = sep[16].split("!!");
ClientProperties clientProperties = new ClientProperties();
if(actionType == ActionType.FOLDER)
clientProperties.setDuplicatePropertyAllowed(true);
for(int i = 0;i<clientPropertiesSize; i++)
{
String[] clientPraw = clientPropertiesRaw[i].split("__");
Property property = new Property(clientPraw[0], Type.STRING);
if(clientPraw.length > 1)
property.setRawValue(clientPraw[1]);
clientProperties.addProperty(property);
}
action.setClientProperties(clientProperties);
action.setModuleName(sep[5]);
//set up action
//action toBeAdded = null;
if(actionType == ActionType.NORMAL)
{
NormalAction actionCopy = NormalActionPlugins.getInstance().getPluginByModuleName(sep[5]);
if(actionCopy == null)
{
action.setInvalid(true);
}
else
{
action.setVersion(new Version(sep[4]));
//action.setHelpLink(actionCopy.getHelpLink());
if(actionCopy.getVersion().getMajor() != action.getVersion().getMajor())
{
action.setInvalid(true);
}
else
{
action.setName(actionCopy.getName());
ClientProperties finalClientProperties = new ClientProperties();
for(Property property : actionCopy.getClientProperties().get())
{
for(int i = 0;i<action.getClientProperties().getSize();i++)
{
Property property1 = action.getClientProperties().get().get(i);
if (property.getName().equals(property1.getName()))
{
property.setRawValue(property1.getRawValue());
finalClientProperties.addProperty(property);
}
}
}
action.setClientProperties(finalClientProperties);
}
}
}
try
{
for(Property prop : action.getClientProperties().get())
{
logger.info("G@@@@@ : "+prop.getRawValue());
}
getClient().getProfileByID(profileID).addAction(action);
for(String action1x : getClient().getProfileByID(profileID).getActionsKeySet())
{
Action action1 = getClient().getProfileByID(profileID).getActionByID(action1x);
logger.info("231cc : "+action1.getID());
for(Property prop : action1.getClientProperties().get())
{
logger.info("G@VVVV@@@ : "+prop.getRawValue());
}
}
}
catch (CloneNotSupportedException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleMinorException(new MinorException("action", "Unable to clone"));
}
}
public void saveActionDetails(String profileID, Action action) throws SevereException
{
StringBuilder finalQuery = new StringBuilder("save_action_details::");
//failsafes
if(action.getDisplayText().endsWith(":"))
action.setDisplayText(action.getDisplayText()+" ");
finalQuery.append(profileID)
.append("::")
.append(action.getID())
.append("::")
.append(action.getActionType())
.append("::");
if(action.getActionType() == ActionType.NORMAL)
{
finalQuery.append(action.getVersion().getText());
System.out.println("VERSION :sdd "+action.getVersion().getText());
}
finalQuery.append("::");
if(action.getActionType() == ActionType.NORMAL)
finalQuery.append(action.getModuleName());
finalQuery.append("::");
//display
finalQuery.append(action.getBgColourHex())
.append("::");
//icon
finalQuery.append(action.isHasIcon())
.append("::")
.append(action.isShowIcon())
.append("::");
//text
finalQuery.append(action.isShowDisplayText())
.append("::")
.append(action.getDisplayTextFontColourHex())
.append("::")
.append(action.getDisplayText())
.append("::")
.append(action.getDisplayTextAlignment())
.append("::");
//location
if(action.getLocation() == null)
finalQuery.append("-1::-1::");
else
finalQuery.append(action.getLocation().getRow())
.append("::")
.append(action.getLocation().getCol())
.append("::");
//client properties
ClientProperties clientProperties = action.getClientProperties();
finalQuery.append(clientProperties.getSize())
.append("::");
for(Property property : clientProperties.get())
{
finalQuery.append(property.getName())
.append("__")
.append(property.getRawValue())
.append("__");
finalQuery.append("!!");
}
finalQuery.append("::")
.append(action.getParent())
.append("::");
writeToStream(finalQuery.toString());
}
public void deleteAction(String profileID, String actionID) throws SevereException
{
writeToStream("delete_action::"+profileID+"::"+actionID);
}
public void saveClientDetails(String clientNickname, String screenWidth, String screenHeight, String defaultProfileID,
String defaultThemeFullName) throws SevereException
{
writeToStream("save_client_details::"+
clientNickname+"::"+
screenWidth+"::"+
screenHeight+"::"+
defaultProfileID+"::"+
defaultThemeFullName+"::");
client.setNickName(clientNickname);
client.setStartupDisplayWidth(Double.parseDouble(screenWidth));
client.setStartupDisplayHeight(Double.parseDouble(screenHeight));
client.setDefaultProfileID(defaultProfileID);
client.setDefaultThemeFullName(defaultThemeFullName);
}
public void saveProfileDetails(ClientProfile clientProfile) throws SevereException, CloneNotSupportedException {
if(client.getProfileByID(clientProfile.getID()) !=null)
{
client.getProfileByID(clientProfile.getID()).setName(clientProfile.getName());
client.getProfileByID(clientProfile.getID()).setRows(clientProfile.getRows());
client.getProfileByID(clientProfile.getID()).setCols(clientProfile.getCols());
client.getProfileByID(clientProfile.getID()).setActionSize(clientProfile.getActionSize());
client.getProfileByID(clientProfile.getID()).setActionGap(clientProfile.getActionGap());
}
else
client.addProfile(clientProfile);
writeToStream("save_client_profile::"+
clientProfile.getID()+"::"+
clientProfile.getName()+"::"+
clientProfile.getRows()+"::"+
clientProfile.getCols()+"::"+
clientProfile.getActionSize()+"::"+
clientProfile.getActionGap()+"::");
}
public void deleteProfile(String ID) throws SevereException
{
writeToStream("delete_profile::"+ID+"::");
}
public void actionClicked(String profileID, String actionID) {
try
{
Action action = client.getProfileByID(profileID).getActionByID(actionID);
if(action.getActionType() == ActionType.NORMAL)
{
NormalAction original = NormalActionPlugins.getInstance().getPluginByModuleName(
action.getModuleName()
);
if(original == null)
{
throw new MinorException(
"The action isn't installed on the server."
);
}
NormalAction normalAction = original.clone();
normalAction.setLocation(action.getLocation());
normalAction.setDisplayText(action.getDisplayText());
normalAction.setID(actionID);
normalAction.setClientProperties(action.getClientProperties());
new Thread(new Task<Void>() {
@Override
protected Void call()
{
try
{
boolean result = serverListener.onNormalActionClicked(normalAction);
if(!result)
{
sendActionFailed(profileID, actionID);
}
}
catch (SevereException e)
{
exceptionAndAlertHandler.handleSevereException(e);
}
return null;
}
}).start();
}
} catch (Exception e) {
e.printStackTrace();
exceptionAndAlertHandler.handleMinorException(new MinorException(e.getMessage()));
}
}
public void sendActionFailed(String profileID, String actionID) throws SevereException {
logger.info("Sending failed status ...");
writeToStream("action_failed::"+
profileID+"::"+
actionID+"::");
}
}
package com.stream_pi.server.connection;
import java.net.SocketAddress;
import java.util.ArrayList;
public class ClientConnections {
private ArrayList<ClientConnection> connections;
private static ClientConnections instance = null;
private ClientConnections()
{
connections = new ArrayList<>();
}
public static synchronized ClientConnections getInstance()
{
if(instance == null)
{
instance = new ClientConnections();
}
return instance;
}
public ArrayList<ClientConnection> getConnections()
{
return connections;
}
public void clearAllConnections()
{
connections.clear(); // NOT RECOMMENDED TO USE CARELESSLY
}
public void addConnection(ClientConnection connection)
{
connections.add(connection);
}
public void removeConnection(ClientConnection clientConnection)
{
System.out.println(connections.remove(clientConnection)+" 22222222222222222222222222222222222222222");
}
public void disconnectAll()
{
new Thread(()->{
for(ClientConnection clientConnection : connections)
{
clientConnection.exit();
}
clearAllConnections();
}).start();
}
public ClientConnection getClientConnectionBySocketAddress(SocketAddress socketAddress)
{
for(ClientConnection clientConnection : connections)
{
if(clientConnection.getClient().getRemoteSocketAddress().equals(socketAddress))
return clientConnection;
}
return null;
}
}
/*package com.StreamPi.Server.connection;
import com.StreamPi.ActionAPI.action.action;
import com.StreamPi.ActionAPI.action.ActionType;
import com.StreamPi.ActionAPI.action.Location;
import com.StreamPi.ActionAPI.ActionProperty.ClientProperties;
import com.StreamPi.ActionAPI.NormalAction.NormalAction;
import com.StreamPi.CommAPI.ConnectionGrpc;
import com.StreamPi.CommAPI.ServerGRPC;
import NormalActionPlugins;
import client;
import ClientProfile;
import ServerInfo;
import com.StreamPi.Util.Exception.MinorException;
import com.StreamPi.Util.Exception.SevereException;
import com.StreamPi.Util.Exception.StreamPiException;
import com.StreamPi.Util.Platform.Platform;
import com.StreamPi.Util.Version.Version;
import io.grpc.stub.StreamObserver;
import javafx.concurrent.Task;
import javafx.scene.control.Alert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class ConnectionService extends ConnectionGrpc.ConnectionImplBase {
private client client = null;
private Logger logger;
ServerListener serverListener;
final HashMap<String, Boolean> actionStatuses;
public ConnectionService(ServerListener serverListener)
{
super();
isDisconnect = false;
disconnectMessage = "";
logger = LoggerFactory.getLogger(ConnectionService.class);
actionStatuses = new HashMap<>();
this.serverListener = serverListener;
}
boolean isDisconnect;
String disconnectMessage;
public void disconnect()
{
disconnect("");
}
public void disconnect(String message)
{
if(!isDisconnect)
{
this.isDisconnect = true;
this.disconnectMessage = message;
}
}
@Override
public void sendClientDetails(ServerGRPC.ClientDetails request, StreamObserver<ServerGRPC.Status> responseObserver) {
Version clientVersion;
Version commsAPIVersion;
Version themeAPIVersion;
try
{
clientVersion = new Version(request.getClientVersion());
commsAPIVersion = new Version(request.getCommAPIVersion());
themeAPIVersion = new Version(request.getThemeAPIVersion());
}
catch (MinorException e)
{
e.printStackTrace();
serverListener.onRPCError(new MinorException("versions invalid. Check stacktrace"));
disconnect();
return;
}
String nickName = request.getNickName();
Platform platform = Platform.valueOf(request.getPlatform().toString());
client = new client(clientVersion, commsAPIVersion, themeAPIVersion, nickName, platform, null);
boolean sendActions = true;
if(!commsAPIVersion.isEqual(ServerInfo.getInstance().getCommAPIVersion()))
{
sendActions = false;
disconnect("client CommAPI and Server CommAPI do not match!");
}
responseObserver.onNext(ServerGRPC.Status.newBuilder().setSendActions(sendActions).build());
responseObserver.onCompleted();
}
@Override
public StreamObserver<ServerGRPC.ClientProfile> sendClientProfiles(StreamObserver<ServerGRPC.Empty> responseObserver) {
client.getProfiles().clear();
ArrayList<String> notFoundActions = new ArrayList<>();
return new StreamObserver<ServerGRPC.ClientProfile>() {
@Override
public void onNext(ServerGRPC.ClientProfile clientProfile) {
String name = clientProfile.getName();
String id = clientProfile.getId();
int rows = clientProfile.getRows();
int cols = clientProfile.getCols();
int actionSize = clientProfile.getActionSize();
int actionGap = clientProfile.getActionGap();
ClientProfile finalClientProfile = new ClientProfile(name, id, rows, cols, actionSize, actionGap);
ArrayList<action> actions = new ArrayList<>();
List<ServerGRPC.ClientAction> clientActions = clientProfile.getActionsList();
for(ServerGRPC.ClientAction clientAction : clientActions)
{
String actionID = clientAction.getId();
String actionName = clientAction.getActionName();
boolean hasIcon = clientAction.getHasIcon();
ActionType actionType = ActionType.valueOf(clientAction.getActionType().toString());
action action = new action(actionID, actionType);
action.setActionName(actionName);
action.setHasIcon(hasIcon);
int locationX = clientAction.getLocationX();
int locationY = clientAction.getLocationY();
action.setLocation(new Location(locationX, locationY));
if(actionType == ActionType.NORMAL)
{
action.setModuleName(clientAction.getModuleName());
ClientProperties properties = new ClientProperties();
List<ServerGRPC.ClientProperty> clientProperties = clientAction.getClientPropertiesList();
for(ServerGRPC.ClientProperty clientProperty : clientProperties)
{
String propertyName = clientProperty.getName();
String propertyValue = clientProperty.getValue();
properties.addProperty(propertyName, propertyValue);
}
action.setClientProperties(properties);
boolean isFound = false;
for(NormalAction normalAction : NormalActionPlugins.getInstance().getPlugins())
{
if(normalAction.getModuleName().equals(action.getModuleName()))
{
isFound = true;
normalAction.setClientProperties(action.getClientProperties());
normalAction.setActionName(action.getActionName());
normalAction.setHasIcon(action.isHasIcon());
normalAction.setID(action.getID());
normalAction.setLocation(action.getLocation());
normalAction.setInvalid(false);
actions.add(normalAction);
break;
}
}
if(!isFound)
{
String aName = action.getModuleName();
action.setInvalid(true);
logger.warn("action "+aName+" not found!");
if(!notFoundActions.contains(aName))
notFoundActions.add(aName);
actions.add(action);
}
}
}
finalClientProfile.setActions(actions);
client.addProfile(finalClientProfile);
}
@Override
public void onError(Throwable throwable) {
serverListener.onRPCError(new SevereException(throwable.getMessage()));
}
@Override
public void onCompleted() {
if(!notFoundActions.isEmpty())
{
StringBuilder all = new StringBuilder("Some actions cannot be edited/used because they are not installed on the server : ");
for(String each : notFoundActions)
{
all.append("\n * ").append(each);
}
serverListener.onAlert("Warning",all.toString(), Alert.AlertType.WARNING);
}
responseObserver.onNext(ServerGRPC.Empty.newBuilder().build());
responseObserver.onCompleted();
}
};
}
@Override
public void actionClicked(ServerGRPC.ClickedActionID request, StreamObserver<ServerGRPC.Empty> responseObserver) {
try
{
action actionClicked = client.getProfileByID(request.getProfileID()).getActionByID(request.getId());
new Thread(new Task<Void>() {
@Override
protected Void call()
{
try
{
synchronized (actionStatuses)
{
actionStatuses.put(request.getId(), serverListener.onActionClicked(actionClicked));
}
}
catch (MinorException e)
{
serverListener.onRPCError(e);
}
return null;
}
}).start();
}
catch (MinorException e)
{
e.printStackTrace();
}
finally {
responseObserver.onNext(ServerGRPC.Empty.newBuilder().build());
responseObserver.onCompleted();
}
}
@Override
public StreamObserver<ServerGRPC.Empty> actionClickedStatus(StreamObserver<ServerGRPC.ActionStatus> responseObserver) {
return new StreamObserver<ServerGRPC.Empty>() {
@Override
public void onNext(ServerGRPC.Empty empty) {
}
@Override
public void onError(Throwable throwable) {
serverListener.onRPCError(new StreamPiException(throwable.getMessage()));
}
@Override
public void onCompleted() {
String id = null;
boolean success = false;
synchronized (actionStatuses)
{
if(!actionStatuses.isEmpty())
{
for(String key : actionStatuses.keySet())
{
id = key;
success = actionStatuses.get(id);
System.out.println("SDSDSDASDASDXZXCZXCZXC");
break;
}
}
}
if(id==null)
{
responseObserver.onNext(ServerGRPC.ActionStatus.newBuilder().build());
}
else
responseObserver.onNext(ServerGRPC.ActionStatus.newBuilder()
.setId(id)
.setIsSuccess(success)
.build());
responseObserver.onCompleted();
}
};
}
@Override
public StreamObserver<ServerGRPC.DisconnectMessage> disconnect(StreamObserver<ServerGRPC.DisconnectMessage> responseObserver) {
return new StreamObserver<ServerGRPC.DisconnectMessage>() {
@Override
public void onNext(ServerGRPC.DisconnectMessage disconnectMessage) {
if(disconnectMessage.getIsDisconnect())
disconnect(disconnectMessage.getMessage());
}
@Override
public void onError(Throwable throwable) {
serverListener.onRPCError(new StreamPiException(throwable.getMessage()));
}
@Override
public void onCompleted() {
responseObserver.onNext(ServerGRPC.DisconnectMessage.newBuilder()
.setIsDisconnect(isDisconnect)
.setMessage(disconnectMessage)
.build());
responseObserver.onCompleted();
}
};
}
}
*/
package com.stream_pi.server.connection;
import com.stream_pi.server.window.ExceptionAndAlertHandler;
import com.stream_pi.util.exception.MinorException;
import com.stream_pi.util.exception.SevereException;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
public class MainServer extends Thread{
private ServerListener serverListener;
private Logger logger = Logger.getLogger(MainServer.class.getName());
private int port;
private ServerSocket serverSocket = null;
//private Server server;
private AtomicBoolean stop = new AtomicBoolean(false);
private ExceptionAndAlertHandler exceptionAndAlertHandler;
public MainServer(ServerListener serverListener, ExceptionAndAlertHandler exceptionAndAlertHandler)
{
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
this.port = port;
this.serverListener = serverListener;
}
public void setPort(int port) {
this.port = port;
}
@Override
public synchronized void start() {
stop.set(false);
super.start();
}
public void stopListeningForConnections()
{
/*if(server !=null)
{
if(!server.isShutdown())
server.shutdown();
}*/
try
{
logger.info("Stopping listening for connections ...");
if(serverSocket!=null)
if(!serverSocket.isClosed())
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
finally {
logger.info("... Done!");
}
stop.set(true);
}
@Override
public void run() {
logger.warning("Starting main server on port "+port+" ...");
try {
logger.info("Starting server on port "+port+" ...");
/*Server server = ServerBuilder.forPort(port)
.addService(new ConnectionService(serverListener))
.build();
server.start();
logger.info("... Done!");*/
serverSocket = new ServerSocket(port);
while(!stop.get())
{
Socket s = serverSocket.accept();
ClientConnections.getInstance().addConnection(new ClientConnection(s, serverListener, exceptionAndAlertHandler));
logger.info("New client connected ("+s.getRemoteSocketAddress()+") !");
}
}
catch (SocketException e)
{
logger.info("Main Server stopped accepting calls ...");
e.printStackTrace(); //more likely stopped listening;
} catch (IOException e) {
exceptionAndAlertHandler.handleSevereException(new SevereException("MainServer io Exception occurred!"));
e.printStackTrace();
}
}
}
package com.stream_pi.server.connection;
import com.stream_pi.actionapi.normalaction.NormalAction;
public interface ServerListener {
boolean onNormalActionClicked(NormalAction action);
void clearTemp();
void init();
void othInit();
}
package com.stream_pi.server.controller;
import com.stream_pi.actionapi.action.ServerConnection;
import com.stream_pi.actionapi.action.PropertySaver;
import com.stream_pi.actionapi.normalaction.NormalAction;
import com.stream_pi.server.Main;
import com.stream_pi.server.action.NormalActionPlugins;
import com.stream_pi.server.connection.ClientConnections;
import com.stream_pi.server.connection.MainServer;
import com.stream_pi.server.io.Config;
import com.stream_pi.server.info.ServerInfo;
import com.stream_pi.server.window.Base;
import com.stream_pi.server.window.dashboard.DonatePopupContent;
import com.stream_pi.server.window.firsttimeuse.FirstTimeUse;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertListener;
import com.stream_pi.util.alert.StreamPiAlertType;
import com.stream_pi.util.exception.MinorException;
import com.stream_pi.util.exception.SevereException;
import com.stream_pi.util.iohelper.IOHelper;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.awt.SystemTray;
import javafx.util.Duration;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.PopupMenu;
import java.awt.MenuItem;
import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Random;
import java.util.logging.Level;
public class Controller extends Base implements PropertySaver, ServerConnection
{
MainServer mainServer;
final SystemTray systemTray;
public void setupDashWindow() throws SevereException
{
try
{
getStage().setTitle("Stream-Pi Server - "+InetAddress.getLocalHost().getCanonicalHostName()+":"+ Config.getInstance().getPort()); //Sets title
getStage().setOnCloseRequest(this::onCloseRequest);
}
catch (UnknownHostException e)
{
e.printStackTrace();
throw new SevereException(e.getMessage());
}
}
private void checkPrePathDirectory() throws SevereException
{
try {
File filex = new File(ServerInfo.getInstance().getPrePath());
System.out.println("SAX : "+filex.exists());
if(!filex.exists())
{
filex.mkdirs();
IOHelper.unzip(Main.class.getResourceAsStream("Default.obj"), ServerInfo.getInstance().getPrePath());
}
}
catch (Exception e)
{
e.printStackTrace();
throw new SevereException(e.getMessage());
}
}
@Override
public void init()
{
try {
checkPrePathDirectory();
initBase();
setupDashWindow();
setupSettingsWindowsAnimations();
NormalActionPlugins.getInstance().setPropertySaver(this);
NormalActionPlugins.getInstance().setServerConnection(this);
getDashboardPane().getPluginsPane().getSettingsButton().setOnAction(event -> {
openSettingsTimeLine.play();
});
getSettingsPane().getCloseButton().setOnAction(event -> {
closeSettingsTimeLine.play();
});
getSettingsPane().getThemesSettings().setController(this);
mainServer = new MainServer(this, this);
if(getConfig().isFirstTimeUse())
{
Stage stage = new Stage();
Scene s = new Scene(new FirstTimeUse(this, this), 512, 300);
stage.setResizable(false);
stage.setScene(s);
stage.setTitle("Stream-Pi Server Setup");
stage.initModality(Modality.APPLICATION_MODAL);
stage.setOnCloseRequest(event->Platform.exit());
stage.show();
}
else
{
if(getConfig().isAllowDonatePopup())
{
if(new Random().nextInt(5) == 3)
new DonatePopupContent(getHostServices(), this).show();
}
othInit();
}
}
catch (SevereException e)
{
handleSevereException(e);
}
}
@Override
public void othInit()
{
try
{
if(ServerInfo.getInstance().isStartMinimised())
minimiseApp();
else
getStage().show();
}
catch(MinorException e)
{
handleMinorException(e);
}
new Thread(new Task<Void>() {
@Override
protected Void call()
{
try
{
getSettingsPane().getGeneralSettings().loadDataFromConfig();
//themes
getSettingsPane().getThemesSettings().setThemes(getThemes());
getSettingsPane().getThemesSettings().setCurrentThemeFullName(getCurrentTheme().getFullName());
getSettingsPane().getThemesSettings().loadThemes();
//clients
getSettingsPane().getClientsSettings().loadData();
try
{
//Plugins
Platform.runLater(()->{
getDashboardPane().getPluginsPane().clearData();
getDashboardPane().getPluginsPane().loadOtherActions();
});
NormalActionPlugins.setPluginsLocation(getConfig().getPluginsPath());
NormalActionPlugins.getInstance().init();
Platform.runLater(()->getDashboardPane().getPluginsPane().loadData());
getSettingsPane().getPluginsSettings().loadPlugins();
}
catch (MinorException e)
{
getSettingsPane().getPluginsSettings().showPluginInitError();
handleMinorException(e);
}
//Server
mainServer.setPort(getConfig().getPort());
mainServer.start();
}
catch (SevereException e)
{
handleSevereException(e);
}
return null;
}
}).start();
}
private void setupSettingsWindowsAnimations()
{
Node settingsNode = getSettingsPane();
Node dashboardNode = getDashboardPane();
openSettingsTimeLine = new Timeline();
openSettingsTimeLine.setCycleCount(1);
openSettingsTimeLine.getKeyFrames().addAll(
new KeyFrame(Duration.millis(0.0D),
new KeyValue(settingsNode.opacityProperty(),
0.0D, Interpolator.EASE_IN),
new KeyValue(settingsNode.scaleXProperty(),
1.1D, Interpolator.EASE_IN),
new KeyValue(settingsNode.scaleYProperty(),
1.1D, Interpolator.EASE_IN),
new KeyValue(settingsNode.scaleZProperty(),
1.1D, Interpolator.EASE_IN)),
new KeyFrame(Duration.millis(90.0D),
new KeyValue(settingsNode.opacityProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleXProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleYProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleZProperty(),
1.0D, Interpolator.LINEAR)),
new KeyFrame(Duration.millis(0.0D),
new KeyValue(dashboardNode.opacityProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleXProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleYProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleZProperty(),
1.0D, Interpolator.LINEAR)),
new KeyFrame(Duration.millis(90.0D),
new KeyValue(dashboardNode.opacityProperty(),
0.0D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleXProperty(),
0.9D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleYProperty(),
0.9D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleZProperty(),
0.9D, Interpolator.LINEAR))
);
openSettingsTimeLine.setOnFinished(event1 -> {
settingsNode.toFront();
});
closeSettingsTimeLine = new Timeline();
closeSettingsTimeLine.setCycleCount(1);
closeSettingsTimeLine.getKeyFrames().addAll(
new KeyFrame(Duration.millis(0.0D),
new KeyValue(settingsNode.opacityProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleXProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleYProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleZProperty(),
1.0D, Interpolator.LINEAR)),
new KeyFrame(Duration.millis(90.0D),
new KeyValue(settingsNode.opacityProperty(),
0.0D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleXProperty(),
1.1D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleYProperty(),
1.1D, Interpolator.LINEAR),
new KeyValue(settingsNode.scaleZProperty(),
1.1D, Interpolator.LINEAR)),
new KeyFrame(Duration.millis(0.0D),
new KeyValue(dashboardNode.opacityProperty(),
0.0D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleXProperty(),
0.9D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleYProperty(),
0.9D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleZProperty(),
0.9D, Interpolator.LINEAR)),
new KeyFrame(Duration.millis(90.0D),
new KeyValue(dashboardNode.opacityProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleXProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleYProperty(),
1.0D, Interpolator.LINEAR),
new KeyValue(dashboardNode.scaleZProperty(),
1.0D, Interpolator.LINEAR))
);
closeSettingsTimeLine.setOnFinished(event1 -> {
dashboardNode.toFront();
new Thread(new Task<Void>() {
@Override
protected Void call() {
try {
getSettingsPane().getClientsSettings().loadData();
getSettingsPane().getGeneralSettings().loadDataFromConfig();
getSettingsPane().getPluginsSettings().loadPlugins();
getSettingsPane().getThemesSettings().setThemes(getThemes());
getSettingsPane().getThemesSettings().setCurrentThemeFullName(getCurrentTheme().getFullName());
getSettingsPane().getThemesSettings().loadThemes();
getSettingsPane().setDefaultTabToGeneral();
}
catch (SevereException e)
{
handleSevereException(e);
}
catch (MinorException e)
{
handleMinorException(e);
}
return null;
}
}).start();
});
}
private Timeline openSettingsTimeLine;
private Timeline closeSettingsTimeLine;
public Controller(){
systemTray = SystemTray.getSystemTray();
mainServer = null;
}
public void onCloseRequest(WindowEvent event)
{
try
{
if(Config.getInstance().getCloseOnX())
{
getConfig().setStartupWindowSize(
getWidth(),
getHeight()
);
getConfig().save();
onQuitApp();
NormalActionPlugins.getInstance().shutDownActions();
Platform.exit();
}
else
{
minimiseApp();
event.consume();
}
}
catch (SevereException e)
{
handleSevereException(e);
}
catch (MinorException e)
{
handleMinorException(e);
}
finally
{
closeLogger();
}
}
public void onQuitApp()
{
if(mainServer!=null)
mainServer.stopListeningForConnections();
ClientConnections.getInstance().disconnectAll();
getLogger().info("Shutting down ...");
}
public void minimiseApp() throws MinorException
{
try
{
if(SystemTray.isSupported())
{
if(getTrayIcon() == null)
initIconTray();
systemTray.add(getTrayIcon());
//getStage().setIconified(true);
getStage().hide();
}
else
{
new StreamPiAlert("System Tray Error", "Your System does not support System Tray", StreamPiAlertType.ERROR).show();
}
}
catch(Exception e)
{
throw new MinorException(e.getMessage());
}
}
public void initIconTray()
{
Platform.setImplicitExit(false);
PopupMenu popup = new PopupMenu();
MenuItem showItem = new MenuItem("Show");
showItem.addActionListener(l->{
systemTray.remove(getTrayIcon());
Platform.runLater(()->{
//getStage().setIconified(false);
getStage().show();
});
});
MenuItem exitItem = new MenuItem("Exit");
exitItem.addActionListener(l->{
systemTray.remove(getTrayIcon());
onQuitApp();
Platform.exit();
});
popup.add(showItem);
popup.addSeparator();
popup.add(exitItem);
TrayIcon trayIcon = new TrayIcon(
Toolkit.getDefaultToolkit().getImage(Main.class.getResource("app_icon.png")),
"Stream-Pi Server",
popup
);
trayIcon.setImageAutoSize(true);
this.trayIcon = trayIcon;
}
private TrayIcon trayIcon = null;
public TrayIcon getTrayIcon()
{
return trayIcon;
}
@Override
public void handleMinorException(MinorException e) {
getLogger().log(Level.SEVERE, e.getMessage());
e.printStackTrace();
Platform.runLater(()-> new StreamPiAlert(e.getTitle(), e.getShortMessage(), StreamPiAlertType.WARNING).show());
}
@Override
public void handleSevereException(SevereException e) {
getLogger().log(Level.SEVERE, e.getMessage());
e.printStackTrace();
Platform.runLater(()->{
StreamPiAlert alert = new StreamPiAlert(e.getTitle(), e.getShortMessage(), StreamPiAlertType.ERROR);
alert.setOnClicked(new StreamPiAlertListener()
{
@Override
public void onClick(String txt)
{
onQuitApp();
Platform.exit();
}
});
alert.show();
});
}
@Override
public synchronized boolean onNormalActionClicked(NormalAction action) {
try{
getLogger().info("action "+action.getID()+" clicked!");
action.onActionClicked();
return true;
}
catch (Exception e)
{
handleMinorException(new MinorException(
"action Execution Failed!",
"Error running action at ["+action.getLocation().getRow()+","+action.getLocation().getCol()+"] ("+action.getDisplayText()+")\n"+
"Check stacktrace/log to know what exactly happened\n\nMessage : \n"+e.getMessage() )
);
return false;
}
}
@Override
public void clearTemp() {
Platform.runLater(() -> {
getDashboardPane().getClientDetailsPane().refresh();
getDashboardPane().getActionGridPane().clear();
getDashboardPane().getActionDetailsPane().clear();
getSettingsPane().getClientsSettings().loadData();
});
}
@Override
public void saveServerProperties() {
try {
NormalActionPlugins.getInstance().saveServerSettings();
getSettingsPane().getPluginsSettings().loadPlugins();
} catch (MinorException e) {
e.printStackTrace();
handleMinorException(e);
}
}
@Override
public com.stream_pi.util.platform.Platform getPlatform() {
return ServerInfo.getInstance().getPlatformType();
}
}
/*
Stream-Pi - Free & Open-Source Modular Cross-Platform Programmable Macropad
Copyright (C) 2019-2021 Debayan Sutradhar (rnayabed), Samuel Quiñones (SamuelQuinones)
This program 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.
This program 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.
Written by : Debayan Sutradhar (rnayabed)
*/
package com.stream_pi.server.info;
public class License {
/**
* @return Formatted String containing the Stream-Pi License an also licenses of other opensource libraries used.
*/
public static String getLicense()
{
return "Stream-Pi - Free & Open-Source Modular Cross-Platform Programmable Macro Pad\n" +
"Copyright (C) 2019-2021 Debayan Sutradhar (rnayabed), Samuel Quiñones (SamuelQuinones)\n" +
"\n" +
"This program is free software: you can redistribute it and/or modify\n" +
"it under the terms of the GNU General Public License as published by\n" +
"the Free Software Foundation, either version 3 of the License, or\n" +
"(at your option) any later version.\n" +
"\n" +
"This program is distributed in the hope that it will be useful,\n" +
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +
"GNU General Public License for more details.\n" +
"\n\n"+
"Opensource Libraries used :\n"+
"1. JavaFX - GNU General Public License with Classpath Exception\nhttp://openjdk.java.net/legal/gplv2+ce.html\n\n"+
"2. JSON - The JSON License\nhttps://www.json.org/license.html\n\n"+
"3. Ikonli - Apache License\nhttps://github.com/kordamp/ikonli/blob/master/LICENSE\n\n";
}
}
/*
ServerInfo.java
Stores basic information about the server - name, platform type
Contributors: Debayan Sutradhar (@dubbadhar)
*/
package com.stream_pi.server.info;
import com.stream_pi.util.platform.Platform;
import com.stream_pi.util.platform.ReleaseStatus;
import com.stream_pi.util.version.Version;
public class ServerInfo {
private Version version;
private final ReleaseStatus releaseStatus;
private final Platform platformType;
private String prePath;
private Version minThemeSupportVersion;
private Version minPluginSupportVersion;
private Version commStandardVersion;
private static ServerInfo instance = null;
private String runnerFileName = null;
private boolean startMinimised = false;
private ServerInfo(){
version = new Version(1,0,0);
minThemeSupportVersion = new Version(1,0,0);
minPluginSupportVersion = new Version(1,0,0);
commStandardVersion = new Version(1,0,0);
releaseStatus = ReleaseStatus.EA;
prePath = "data/";
String osName = System.getProperty("os.name").toLowerCase();
if(osName.contains("windows"))
platformType = Platform.WINDOWS;
else if (osName.contains("linux"))
platformType = Platform.LINUX;
else if (osName.contains("mac"))
platformType = Platform.MAC;
else
platformType = Platform.UNKNOWN;
}
public String getPrePath() {
return prePath;
}
public void setStartMinimised(boolean startMinimised)
{
this.startMinimised = startMinimised;
}
public boolean isStartMinimised()
{
return startMinimised;
}
public void setRunnerFileName(String runnerFileName)
{
this.runnerFileName = runnerFileName;
}
public String getRunnerFileName()
{
return runnerFileName;
}
public static synchronized ServerInfo getInstance(){
if(instance == null)
{
instance = new ServerInfo();
}
return instance;
}
public Platform getPlatformType()
{
return platformType;
}
public Version getVersion() {
return version;
}
public ReleaseStatus getReleaseStatus()
{
return releaseStatus;
}
public Version getMinThemeSupportVersion()
{
return minThemeSupportVersion;
}
public Version getMinPluginSupportVersion()
{
return minPluginSupportVersion;
}
public Version getCommStandardVersion()
{
return commStandardVersion;
}
}
/*
Config.java
Contributor(s) : Debayan Sutradhar (@rnayabed)
handler for config.xml
*/
package com.stream_pi.server.io;
import java.io.File;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import com.stream_pi.server.info.ServerInfo;
import com.stream_pi.util.exception.SevereException;
import com.stream_pi.util.xmlconfighelper.XMLConfigHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class Config {
private static Config instance = null;
private final File configFile;
private Document document;
private Config() throws SevereException {
try {
configFile = new File(ServerInfo.getInstance().getPrePath()+"config.xml");
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
document = docBuilder.parse(configFile);
} catch (Exception e) {
e.printStackTrace();
throw new SevereException("Config", "unable to read config.xml");
}
}
public static synchronized Config getInstance() throws SevereException
{
if(instance == null)
instance = new Config();
return instance;
}
Logger logger = Logger.getLogger(Config.class.getName());
public void save() throws SevereException {
try {
logger.info("Saving config ...");
Transformer transformer = TransformerFactory.newInstance().newTransformer();
Result output = new StreamResult(configFile);
Source input = new DOMSource(document);
transformer.transform(input, output);
logger.info("... Done!");
} catch (Exception e) {
throw new SevereException("Config", "unable to save config.xml");
}
}
//Getters
//comms
private Element getCommsElement()
{
return (Element) document.getElementsByTagName("comms").item(0);
}
public String getServerName()
{
return XMLConfigHelper.getStringProperty(getCommsElement(), "name",
getDefaultServerName(), false, true, document, configFile);
}
public int getPort()
{
return XMLConfigHelper.getIntProperty(getCommsElement(), "port",
getDefaultPort(), false, true, document, configFile);
}
//default getters
public String getDefaultServerName()
{
return "StreamPi Server";
}
public int getDefaultPort()
{
return 2004;
}
private Element getServerElement()
{
return (Element) document.getElementsByTagName("server").item(0);
}
//server
private Element getActionGridElement()
{
return (Element) getServerElement().getElementsByTagName("action-grid").item(0);
}
public int getActionGridActionGap()
{
return XMLConfigHelper.getIntProperty(getActionGridElement(), "gap",
getDefaultActionGridActionGap(), false, true, document, configFile);
}
public int getActionGridActionSize()
{
return XMLConfigHelper.getIntProperty(getActionGridElement(), "size",
getDefaultActionGridSize(), false, true, document, configFile);
}
public String getCurrentThemeFullName()
{
return XMLConfigHelper.getStringProperty(getServerElement(), "current-theme-full-name",
getDefaultCurrentThemeFullName(), false, true, document, configFile);
}
public String getThemesPath()
{
return XMLConfigHelper.getStringProperty(getServerElement(), "themes-path",
getDefaultThemesPath(), false, true, document, configFile);
}
public String getPluginsPath()
{
return XMLConfigHelper.getStringProperty(getServerElement(), "plugins-path",
getDefaultPluginsPath(), false, true, document, configFile);
}
//default getters
public String getDefaultCurrentThemeFullName()
{
return "com.StreamPi.DefaultLight";
}
public String getDefaultThemesPath()
{
return "Themes/";
}
public String getDefaultPluginsPath()
{
return "Plugins/";
}
//server > startup-window-size
private Element getStartupWindowSizeElement()
{
return (Element) getServerElement().getElementsByTagName("startup-window-size").item(0);
}
public double getStartupWindowWidth()
{
return XMLConfigHelper.getDoubleProperty(getStartupWindowSizeElement(), "width",
getDefaultStartupWindowWidth(), false, true, document, configFile);
}
public double getStartupWindowHeight()
{
return XMLConfigHelper.getDoubleProperty(getStartupWindowSizeElement(), "height",
getDefaultStartupWindowHeight(), false, true, document, configFile);
}
//default getters
public int getDefaultStartupWindowWidth()
{
return 1024;
}
public int getDefaultStartupWindowHeight()
{
return 768;
}
//others
private Element getOthersElement()
{
return (Element) document.getElementsByTagName("others").item(0);
}
public boolean getStartOnBoot()
{
return XMLConfigHelper.getBooleanProperty(getOthersElement(), "start-on-boot",
getDefaultStartOnBoot(), false, true, document, configFile);
}
public boolean getCloseOnX()
{
return XMLConfigHelper.getBooleanProperty(getOthersElement(), "close-on-x",
getDefaultCloseOnX(), false, true, document, configFile);
}
public boolean isFirstTimeUse()
{
return XMLConfigHelper.getBooleanProperty(getOthersElement(), "first-time-use", true, false, true, document, configFile);
}
public boolean isAllowDonatePopup()
{
return XMLConfigHelper.getBooleanProperty(getOthersElement(), "allow-donate-popup", true, false, true, document, configFile);
}
//default getters
public boolean getDefaultStartOnBoot()
{
return false;
}
public boolean getDefaultCloseOnX()
{
return false;
}
//Setters
//comms
public void setServerName(String name)
{
getCommsElement().getElementsByTagName("name").item(0).setTextContent(name);
}
public void setServerPort(int port)
{
getCommsElement().getElementsByTagName("port").item(0).setTextContent(port+"");
}
//server
public int getDefaultActionGridActionGap()
{
return 5;
}
public int getDefaultActionGridSize()
{
return 100;
}
public void setActionGridSize(int size)
{
getActionGridElement().getElementsByTagName("size").item(0).setTextContent(size+"");
}
public void setActionGridGap(int size)
{
getActionGridElement().getElementsByTagName("gap").item(0).setTextContent(size+"");
}
public void setPluginsPath(String path)
{
getServerElement().getElementsByTagName("plugins-path").item(0).setTextContent(path);
}
public void setThemesPath(String path)
{
getServerElement().getElementsByTagName("themes-path").item(0).setTextContent(path);
}
public void setCurrentThemeFullName(String themeName)
{
getServerElement().getElementsByTagName("current-theme-full-name").item(0).setTextContent(themeName);
}
//server > startup-window-size
public void setStartupWindowSize(double width, double height)
{
setStartupWindowWidth(width);
setStartupWindowHeight(height);
}
public void setStartupWindowWidth(double width)
{
getStartupWindowSizeElement().getElementsByTagName("width").item(0).setTextContent(width+"");
}
public void setStartupWindowHeight(double height)
{
getStartupWindowSizeElement().getElementsByTagName("height").item(0).setTextContent(height+"");
}
//others
public void setStartupOnBoot(boolean value)
{
getOthersElement().getElementsByTagName("start-on-boot").item(0).setTextContent(value+"");
}
public void setCloseOnX(boolean value)
{
getOthersElement().getElementsByTagName("close-on-x").item(0).setTextContent(value+"");
}
public void setFirstTimeUse(boolean value)
{
getOthersElement().getElementsByTagName("first-time-use").item(0).setTextContent(value+"");
}
public void setAllowDonatePopup(boolean value)
{
getOthersElement().getElementsByTagName("allow-donate-popup").item(0).setTextContent(value+"");
}
}
package com.stream_pi.server.uipropertybox;
import com.stream_pi.actionapi.actionproperty.property.ControlType;
import com.stream_pi.actionapi.actionproperty.property.Type;
import javafx.scene.Node;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleButton;
public class UIPropertyBox
{
private Node controlNode;
private boolean canBeBlank;
private String displayName;
public String getDisplayName() {
return displayName;
}
public boolean isCanBeBlank() {
return canBeBlank;
}
private int index;
private ControlType controlType;
private Type type;
public UIPropertyBox(int index, String displayName, Node controlNode, ControlType controlType, Type type, boolean canBeBlank)
{
this.index = index;
this.displayName = displayName;
this.controlNode = controlNode;
this.controlType = controlType;
this.type = type;
this.canBeBlank = canBeBlank;
}
public ControlType getControlType()
{
return controlType;
}
public Node getControlNode()
{
return controlNode;
}
public int getIndex() {
return index;
}
public Type getType()
{
return type;
}
public String getRawValue()
{
String rawValue = null;
if (controlType == ControlType.TEXT_FIELD)
rawValue = ((TextField) controlNode).getText();
else if (controlType == ControlType.COMBO_BOX)
rawValue = ((ComboBox<String>) controlNode).getSelectionModel().getSelectedIndex() + "";
else if (controlType == ControlType.SLIDER_DOUBLE)
rawValue = ((Slider) controlNode).getValue() + "";
else if (controlType == ControlType.SLIDER_INTEGER)
rawValue = Math.round(((Slider) controlNode).getValue()) + "";
else if (controlType == ControlType.TOGGLE) {
ToggleButton toggleButton = ((ToggleButton) controlNode);
if (toggleButton.isSelected())
rawValue = "true";
else
rawValue = "false";
}
return rawValue;
}
}
package com.stream_pi.server.window;
import com.stream_pi.server.connection.ServerListener;
import com.stream_pi.server.io.Config;
import com.stream_pi.server.info.ServerInfo;
import com.stream_pi.server.Main;
import com.stream_pi.server.window.dashboard.DashboardBase;
import com.stream_pi.server.window.settings.SettingsBase;
import com.stream_pi.themeapi.Theme;
import com.stream_pi.themeapi.Themes;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.exception.MinorException;
import com.stream_pi.util.exception.SevereException;
import com.stream_pi.util.loggerhelper.StreamPiLogFileHandler;
import javafx.application.HostServices;
import javafx.scene.image.Image;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import java.util.logging.Logger;
public abstract class Base extends StackPane implements ExceptionAndAlertHandler, ServerListener {
private Config config;
private ServerInfo serverInfo;
private Stage stage;
private HostServices hostServices;
public Logger getLogger(){
return logger;
}
private SettingsBase settingsBase;
private DashboardBase dashboardBase;
private StackPane alertStackPane;
public void setHostServices(HostServices hostServices)
{
this.hostServices = hostServices;
}
public HostServices getHostServices()
{
return hostServices;
}
private Logger logger = null;
private StreamPiLogFileHandler logFileHandler = null;
public void initLogger() throws SevereException
{
try
{
if(logger != null)
return;
logger = Logger.getLogger("");
logFileHandler = new StreamPiLogFileHandler(ServerInfo.getInstance().getPrePath()+"../streampi.log");
logger.addHandler(logFileHandler);
}
catch(Exception e)
{
e.printStackTrace();
throw new SevereException("Cant get logger started!");
}
}
public void closeLogger()
{
if(logFileHandler != null)
logFileHandler.close();
}
public void initBase() throws SevereException {
initLogger();
getChildren().clear();
stage = (Stage) getScene().getWindow();
getStage().getIcons().add(new Image(Main.class.getResourceAsStream("app_icon.png")));
getStage().setMinWidth(500);
getStage().setMinHeight(500);
config = Config.getInstance();
stage.setWidth(config.getStartupWindowWidth());
stage.setHeight(config.getStartupWindowHeight());
stage.centerOnScreen();
serverInfo = ServerInfo.getInstance();
initThemes();
settingsBase = new SettingsBase(getHostServices(), this, this);
settingsBase.prefWidthProperty().bind(widthProperty());
settingsBase.prefHeightProperty().bind(heightProperty());
dashboardBase = new DashboardBase(this, getHostServices());
dashboardBase.prefWidthProperty().bind(widthProperty());
dashboardBase.prefHeightProperty().bind(heightProperty());
alertStackPane = new StackPane();
alertStackPane.setVisible(false);
StreamPiAlert.setParent(alertStackPane);
getChildren().addAll(settingsBase, dashboardBase, alertStackPane);
dashboardBase.toFront();
}
public void initThemes() throws SevereException {
clearStylesheets();
registerThemes();
applyDefaultStylesheet();
applyDefaultTheme();
}
public Stage getStage()
{
return stage;
}
public void applyDefaultStylesheet()
{
logger.info("Applying default stylesheet ...");
Font.loadFont(Main.class.getResourceAsStream("Roboto.ttf"), 13);
getStylesheets().add(Main.class.getResource("style.css").toExternalForm());
logger.info("... Done!");
}
public DashboardBase getDashboardPane()
{
return dashboardBase;
}
public SettingsBase getSettingsPane()
{
return settingsBase;
}
public Config getConfig()
{
return config;
}
public ServerInfo getServerInfo()
{
return serverInfo;
}
private Theme currentTheme;
public Theme getCurrentTheme()
{
return currentTheme;
}
public void applyTheme(Theme t)
{
logger.info("Applying theme '"+t.getFullName()+"' ...");
if(t.getFonts() != null)
{
for(String fontFile : t.getFonts())
{
Font.loadFont(fontFile.replace("%20",""), 13);
}
}
currentTheme = t;
getStylesheets().addAll(t.getStylesheets());
logger.info("... Done!");
}
public void clearStylesheets()
{
getStylesheets().clear();
}
Themes themes;
public void registerThemes() throws SevereException
{
logger.info("Loading themes ...");
themes = new Themes(getConfig().getThemesPath(), getConfig().getCurrentThemeFullName(), serverInfo.getMinThemeSupportVersion());
if(themes.getErrors().size()>0)
{
StringBuilder themeErrors = new StringBuilder();
for(MinorException eachException : themes.getErrors())
{
themeErrors.append("\n * ").append(eachException.getShortMessage());
}
if(themes.getIsBadThemeTheCurrentOne())
{
themeErrors.append("\n\nReverted to default theme! (").append(getConfig().getDefaultCurrentThemeFullName()).append(")");
getConfig().setCurrentThemeFullName(getConfig().getDefaultCurrentThemeFullName());
getConfig().save();
}
handleMinorException(new MinorException("Theme Loading issues", themeErrors.toString()));
}
logger.info("... Done!");
}
public Themes getThemes()
{
return themes;
}
public void applyDefaultTheme()
{
logger.info("Applying default theme ...");
boolean foundTheme = false;
for(Theme t: themes.getThemeList())
{
if(t.getFullName().equals(config.getCurrentThemeFullName()))
{
foundTheme = true;
applyTheme(t);
break;
}
}
if(foundTheme)
logger.info("... Done!");
else
{
logger.info("Theme not found. reverting to light theme ...");
try {
Config.getInstance().setCurrentThemeFullName("com.streampi.DefaultLight");
Config.getInstance().save();
applyDefaultTheme();
}
catch (SevereException e)
{
handleSevereException(e);
}
}
}
}
package com.stream_pi.server.window;
import com.stream_pi.util.exception.MinorException;
import com.stream_pi.util.exception.SevereException;
public interface ExceptionAndAlertHandler {
void handleMinorException(MinorException e);
void handleSevereException(SevereException e);
}
package com.stream_pi.server.window.dashboard;
import com.stream_pi.server.client.Client;
import com.stream_pi.server.client.ClientProfile;
import com.stream_pi.server.connection.ClientConnection;
import com.stream_pi.server.connection.ClientConnections;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.scene.CacheHint;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.util.Callback;
public class ClientDetailsPane extends HBox {
private DashboardInterface dashboard;
public ClientDetailsPane(DashboardInterface dashboard)
{
this.dashboard = dashboard;
VBox.setVgrow(this, Priority.NEVER);
getStyleClass().add("client_details_pane");
setPadding(new Insets(10));
setMinHeight(90);
initUI();
loadData();
setCache(true);
setCacheHint(CacheHint.SPEED);
}
private ComboBox<ClientConnection> clientsComboBox;
private Label noClientsConnectedLabel;
private ComboBox<ClientProfile> clientProfilesComboBox;
public void initUI()
{
noClientsConnectedLabel = new Label("No Clients Connected");
noClientsConnectedLabel.managedProperty().bind(noClientsConnectedLabel.visibleProperty());
clientsComboBox = new ComboBox<>();
clientsComboBox.setPromptText("Choose client");
clientsComboBox.valueProperty().addListener((observableValue, oldVal, newVal) -> {
if(oldVal!=newVal && newVal!=null)
{
dashboard.newSelectedClientConnection(newVal);
clientProfilesComboBox.setItems(FXCollections.observableArrayList(newVal.getClient().getAllClientProfiles()));
clientProfilesComboBox.setVisible(true);
}
});
clientsComboBox.managedProperty().bind(clientsComboBox.visibleProperty());
Callback<ListView<ClientConnection>, ListCell<ClientConnection>> clientsComboBoxFactory = new Callback<>() {
@Override
public ListCell<ClientConnection> call(ListView<ClientConnection> clientConnectionListView) {
return new ListCell<>() {
@Override
protected void updateItem(ClientConnection clientConnection, boolean b) {
super.updateItem(clientConnection, b);
if(clientConnection == null)
{
setText("Choose client");
}
else
{
Client client = clientConnection.getClient();
setText(client.getNickName());
}
}
};
}
};
clientsComboBox.setCellFactory(clientsComboBoxFactory);
clientsComboBox.setButtonCell(clientsComboBoxFactory.call(null));
clientProfilesComboBox = new ComboBox<>();
clientProfilesComboBox.setPromptText("Choose Profile");
clientProfilesComboBox.valueProperty().addListener((observableValue, oldVal, newVal) -> {
if(oldVal!=newVal && newVal!=null)
{
dashboard.newSelectedClientProfile(newVal);
}
});
clientProfilesComboBox.managedProperty().bind(clientProfilesComboBox.visibleProperty());
Callback<ListView<ClientProfile>, ListCell<ClientProfile>> clientProfilesComboBoxFactory = new Callback<ListView<ClientProfile>, ListCell<ClientProfile>>() {
@Override
public ListCell<ClientProfile> call(ListView<ClientProfile> clientProfileListView) {
return new ListCell<>()
{
@Override
protected void updateItem(ClientProfile profile, boolean b) {
super.updateItem(profile, b);
if(profile == null)
{
setText("Choose Profile");
}
else
{
setText(profile.getName());
}
}
};
}
};
clientProfilesComboBox.setCellFactory(clientProfilesComboBoxFactory);
clientProfilesComboBox.setButtonCell(clientProfilesComboBoxFactory.call(null));
VBox stack = new VBox(noClientsConnectedLabel, clientsComboBox, clientProfilesComboBox);
stack.setSpacing(10);
getChildren().addAll(stack);
}
private void loadData()
{
clientsComboBox.getSelectionModel().clearSelection();
clientProfilesComboBox.getSelectionModel().clearSelection();
if(ClientConnections.getInstance().getConnections().size() == 0)
{
noClientsConnectedLabel.setVisible(true);
clientsComboBox.setVisible(false);
clientProfilesComboBox.setVisible(false);
dashboard.newSelectedClientConnection(null);
}
else
{
noClientsConnectedLabel.setVisible(false);
clientsComboBox.setVisible(true);
clientProfilesComboBox.setVisible(false);
clientsComboBox.setItems(FXCollections.observableArrayList(ClientConnections.getInstance().getConnections()));
}
}
public void refresh()
{
loadData();
}
}
package com.stream_pi.server.window.dashboard;
import java.util.logging.Logger;
import com.stream_pi.server.client.ClientProfile;
import com.stream_pi.server.connection.ClientConnection;
import com.stream_pi.server.window.dashboard.actiongridpane.ActionGridPane;
import com.stream_pi.server.window.dashboard.actiondetailpane.ActionDetailsPane;
import com.stream_pi.server.window.ExceptionAndAlertHandler;
import com.stream_pi.util.exception.SevereException;
import javafx.application.HostServices;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
public class DashboardBase extends HBox implements DashboardInterface {
private final VBox leftPane;
private Logger logger;
public ClientProfile currentClientProfile;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
public DashboardBase(ExceptionAndAlertHandler exceptionAndAlertHandler, HostServices hostServices)
{
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
logger = Logger.getLogger(DashboardBase.class.getName());
leftPane = new VBox();
HBox.setHgrow(leftPane, Priority.ALWAYS);
getChildren().add(leftPane);
setPluginsPane(new PluginsPane(hostServices));
setClientDetailsPane(new ClientDetailsPane(this));
setActionGridPane(new ActionGridPane(exceptionAndAlertHandler));
setActionDetailsPane(new ActionDetailsPane(exceptionAndAlertHandler, hostServices));
getActionGridPane().setActionDetailsPaneListener(getActionDetailsPane());
}
private PluginsPane pluginsPane;
private void setPluginsPane(PluginsPane pluginsPane)
{
this.pluginsPane = pluginsPane;
getChildren().add(this.pluginsPane);
}
public PluginsPane getPluginsPane()
{
return pluginsPane;
}
private ClientDetailsPane clientDetailsPane;
private void setClientDetailsPane(ClientDetailsPane clientDetailsPane)
{
this.clientDetailsPane = clientDetailsPane;
leftPane.getChildren().add(this.clientDetailsPane);
}
public ClientDetailsPane getClientDetailsPane()
{
return clientDetailsPane;
}
private ActionGridPane actionGridPane;
private void setActionGridPane(ActionGridPane actionGridPane)
{
this.actionGridPane = actionGridPane;
leftPane.getChildren().add(this.actionGridPane);
}
public ActionGridPane getActionGridPane()
{
return actionGridPane;
}
private ActionDetailsPane actionDetailsPane;
private void setActionDetailsPane(ActionDetailsPane actionDetailsPane)
{
this.actionDetailsPane = actionDetailsPane;
leftPane.getChildren().add(this.actionDetailsPane);
}
public ActionDetailsPane getActionDetailsPane()
{
return actionDetailsPane;
}
public void newSelectedClientConnection(ClientConnection clientConnection)
{
if(clientConnection == null)
{
logger.info("Remove action grid");
}
else
{
getActionDetailsPane().setClient(clientConnection.getClient());
getActionGridPane().setClient(clientConnection.getClient());
}
}
public void newSelectedClientProfile(ClientProfile clientProfile)
{
this.currentClientProfile = clientProfile;
getActionDetailsPane().setClientProfile(clientProfile);
drawProfile(this.currentClientProfile);
}
public void drawProfile(ClientProfile clientProfile)
{
logger.info("Drawing ...");
getActionGridPane().setClientProfile(clientProfile);
try {
getActionGridPane().renderGrid();
getActionGridPane().renderActions();
}
catch (SevereException e)
{
exceptionAndAlertHandler.handleSevereException(e);
}
}
}
package com.stream_pi.server.window.dashboard;
import com.stream_pi.server.client.ClientProfile;
import com.stream_pi.server.connection.ClientConnection;
public interface DashboardInterface {
void newSelectedClientConnection(ClientConnection clientConnection);
void newSelectedClientProfile(ClientProfile clientProfile);
}
package com.stream_pi.server.window.dashboard;
import com.stream_pi.server.io.Config;
import com.stream_pi.server.window.ExceptionAndAlertHandler;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertListener;
import com.stream_pi.util.alert.StreamPiAlertType;
import com.stream_pi.util.exception.MinorException;
import com.stream_pi.util.exception.SevereException;
import javafx.application.HostServices;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
public class DonatePopupContent{
public DonatePopupContent(HostServices hostServices, ExceptionAndAlertHandler exceptionAndAlertHandler)
{
Label label = new Label("We are a very small team working very hard on this project for best user experience.\n\n" +
"Something like StreamPi takes time, effort and resources. But we will always keep this 100% opensource and free.\n\n" +
"If you find this project helpful, and would want to help us, please consider donating :)\n\n"+
"If you are unable to do so even a small shoutout and share across social media would be very helpful.");
label.setWrapText(true);
label.getStyleClass().add("donate_request_popup_label");
Hyperlink patreonLink = new Hyperlink("Our Patreon");
patreonLink.setOnAction(event ->{
hostServices.showDocument("https://patreon.com/streampi");
});
patreonLink.getStyleClass().add("donate_request_popup_patreon_link");
VBox pane = new VBox(label, patreonLink);
pane.setSpacing(5.0);
streamPiAlert = new StreamPiAlert("Hey!", StreamPiAlertType.INFORMATION, pane);
streamPiAlert.setOnClicked(new StreamPiAlertListener()
{
@Override
public void onClick(String buttonClicked)
{
try
{
Config.getInstance().setAllowDonatePopup(false);
Config.getInstance().save();
}
catch(SevereException e)
{
exceptionAndAlertHandler.handleSevereException(e);
}
}
});
}
private StreamPiAlert streamPiAlert;
public void show()
{
streamPiAlert.show();
}
}
package com.stream_pi.server.window.dashboard;
import com.stream_pi.actionapi.action.Action;
import com.stream_pi.actionapi.action.ActionType;
import com.stream_pi.actionapi.action.DisplayTextAlignment;
import com.stream_pi.actionapi.actionproperty.property.Property;
import com.stream_pi.actionapi.actionproperty.property.Type;
import com.stream_pi.actionapi.normalaction.NormalAction;
import com.stream_pi.actionapi.otheractions.CombineAction;
import com.stream_pi.actionapi.otheractions.FolderAction;
import com.stream_pi.server.action.NormalActionPlugins;
import javafx.application.HostServices;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.CacheHint;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.*;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.ArrayList;
import java.util.HashMap;
public class PluginsPane extends VBox {
private Button settingsButton;
public PluginsPane(HostServices hostServices)
{
setMinWidth(250);
getStyleClass().add("plugins_pane");
setPadding(new Insets(10));
setSpacing(10.0);
this.hostServices = hostServices;
initUI();
}
private Accordion pluginsAccordion;
public void initUI()
{
pluginsAccordion = new Accordion();
pluginsAccordion.setCache(true);
Region r = new Region();
VBox.setVgrow(r, Priority.ALWAYS);
settingsButton = new Button();
FontIcon cog = new FontIcon("fas-cog");
settingsButton.setGraphic(cog);
HBox settingsHBox = new HBox(settingsButton);
settingsHBox.setAlignment(Pos.CENTER_RIGHT);
getChildren().addAll(new Label("Plugins"), pluginsAccordion, r, settingsHBox);
}
public Button getSettingsButton()
{
return settingsButton;
}
public void clearData()
{
pluginsAccordion.getPanes().clear();
}
public void loadData()
{
HashMap<String, ArrayList<NormalAction>> sortedPlugins = NormalActionPlugins.getInstance().getSortedPlugins();
for(String eachCategory : sortedPlugins.keySet())
{
VBox vBox = new VBox();
vBox.setSpacing(5);
TitledPane pane = new TitledPane(eachCategory, vBox);
for(NormalAction eachAction : sortedPlugins.get(eachCategory))
{
if(!eachAction.isVisibleInPluginsPane())
continue;
Button eachNormalActionPluginButton = new Button();
HBox.setHgrow(eachNormalActionPluginButton, Priority.ALWAYS);
eachNormalActionPluginButton.setMaxWidth(Double.MAX_VALUE);
eachNormalActionPluginButton.setAlignment(Pos.CENTER_LEFT);
Node graphic = eachAction.getServerButtonGraphic();
if(graphic == null)
{
FontIcon cogs = new FontIcon("fas-cogs");
cogs.getStyleClass().add("dashboard_plugins_pane_action_icon");
eachNormalActionPluginButton.setGraphic(cogs);
}
else
{
if(graphic instanceof FontIcon)
{
FontIcon fi = (FontIcon) graphic;
eachNormalActionPluginButton.setGraphic(fi);
}
else if(graphic instanceof ImageView)
{
ImageView iv = (ImageView) graphic;
iv.getStyleClass().add("dashboard_plugins_pane_action_icon_imageview");
iv.setPreserveRatio(false);
eachNormalActionPluginButton.setGraphic(iv);
}
}
eachNormalActionPluginButton.setText(eachAction.getName());
eachNormalActionPluginButton.setOnDragDetected(mouseEvent -> {
Dragboard db = eachNormalActionPluginButton.startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
content.put(Action.getDataFormat(), createFakeAction(eachAction, "Untitled action"));
db.setContent(content);
mouseEvent.consume();
});
HBox hBox = new HBox(eachNormalActionPluginButton);
hBox.setSpacing(5.0);
hBox.setAlignment(Pos.TOP_LEFT);
HBox.setHgrow(eachNormalActionPluginButton, Priority.ALWAYS);
if(eachAction.getHelpLink() != null) {
Button helpButton = new Button();
FontIcon questionIcon = new FontIcon("fas-question");
questionIcon.getStyleClass().add("dashboard_plugins_pane_action_help_icon");
helpButton.setGraphic(questionIcon);
helpButton.setOnAction(event -> hostServices.showDocument(eachAction.getHelpLink()));
hBox.getChildren().add(helpButton);
}
vBox.getChildren().add(hBox);
}
if(vBox.getChildren().size() > 0)
pluginsAccordion.getPanes().add(pane);
}
}
private HostServices hostServices;
public Action createFakeAction(Action action, String displayText)
{
Action newAction = new Action(action.getActionType());
if(action.getActionType() == ActionType.NORMAL)
{
newAction.setModuleName(action.getModuleName());
newAction.setVersion(action.getVersion());
newAction.setName(action.getName());
}
newAction.setClientProperties(action.getClientProperties());
for(Property property : newAction.getClientProperties().get())
{
if(property.getType() == Type.STRING || property.getType() == Type.INTEGER || property.getType() == Type.DOUBLE)
property.setRawValue(property.getDefaultRawValue());
}
// newAction.setLocation(location);
newAction.setIDRandom();
newAction.setShowDisplayText(true);
newAction.setDisplayText(displayText);
newAction.setDisplayTextAlignment(DisplayTextAlignment.CENTER);
newAction.setShowIcon(false);
newAction.setHasIcon(false);
//action.setParent(root);
newAction.setBgColourHex("");
newAction.setDisplayTextFontColourHex("");
return newAction;
}
public void loadOtherActions()
{
VBox vBox = new VBox();
Button folderActionButton = new Button("Folder");
folderActionButton.setMaxWidth(Double.MAX_VALUE);
folderActionButton.setAlignment(Pos.CENTER_LEFT);
FontIcon folder = new FontIcon("fas-folder");
folderActionButton.setGraphic(folder);
folderActionButton.setOnDragDetected(mouseEvent -> {
Dragboard db = folderActionButton.startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
content.put(Action.getDataFormat(), createFakeAction(new FolderAction(), "Untitled Folder"));
db.setContent(content);
mouseEvent.consume();
});
Button combineActionButton = new Button("Combine");
combineActionButton.setMaxWidth(Double.MAX_VALUE);
combineActionButton.setAlignment(Pos.CENTER_LEFT);
FontIcon list = new FontIcon("fas-list");
combineActionButton.setGraphic(list);
combineActionButton.setOnDragDetected(mouseEvent -> {
Dragboard db = combineActionButton.startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
content.put(Action.getDataFormat(), createFakeAction(new CombineAction(), "Untitled Combine"));
db.setContent(content);
mouseEvent.consume();
});
vBox.getChildren().addAll(folderActionButton, combineActionButton);
vBox.setSpacing(5);
TitledPane pane = new TitledPane("StreamPi", vBox);
pluginsAccordion.getPanes().add(pane);
pluginsAccordion.setCache(true);
pluginsAccordion.setCacheHint(CacheHint.SPEED);
}
}
package com.stream_pi.server.window.dashboard.actiondetailpane;
import com.stream_pi.actionapi.action.Action;
import com.stream_pi.actionapi.action.ActionType;
import com.stream_pi.actionapi.action.DisplayTextAlignment;
import com.stream_pi.actionapi.action.Location;
import com.stream_pi.actionapi.actionproperty.property.ControlType;
import com.stream_pi.actionapi.actionproperty.property.Property;
import com.stream_pi.actionapi.actionproperty.property.Type;
import com.stream_pi.actionapi.otheractions.CombineAction;
import com.stream_pi.actionapi.otheractions.FolderAction;
import com.stream_pi.server.uipropertybox.UIPropertyBox;
import com.stream_pi.server.client.Client;
import com.stream_pi.server.client.ClientProfile;
import com.stream_pi.server.connection.ClientConnection;
import com.stream_pi.server.connection.ClientConnections;
import com.stream_pi.server.window.dashboard.actiongridpane.ActionBox;
import com.stream_pi.server.window.ExceptionAndAlertHandler;
import com.stream_pi.util.exception.MinorException;
import com.stream_pi.util.exception.SevereException;
import com.stream_pi.util.uihelper.HBoxInputBox;
import com.stream_pi.util.uihelper.HBoxInputBoxWithFileChooser;
import com.stream_pi.util.uihelper.SpaceFiller;
import javafx.application.HostServices;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.stage.FileChooser;
import javafx.stage.Window;
import org.kordamp.ikonli.javafx.FontIcon;
import java.io.File;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.logging.Logger;
public class ActionDetailsPane extends VBox implements ActionDetailsPaneListener {
private ScrollPane scrollPane;
private VBox vbox;
private VBox clientPropertiesVBox;
private Button saveButton;
private Button deleteButton;
private Button openFolderButton;
private HBox buttonBar;
private Label actionHeadingLabel;
private Logger logger;
private Button returnButtonForCombineActionChild;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
private HostServices hostServices;
public ActionDetailsPane(ExceptionAndAlertHandler exceptionAndAlertHandler, HostServices hostServices) {
this.hostServices = hostServices;
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
logger = Logger.getLogger(ActionDetailsPane.class.getName());
setSpacing(10.0);
// VBox.setVgrow(this, Priority.SOMETIMES);
clientPropertiesVBox = new VBox();
clientPropertiesVBox.setSpacing(10.0);
vbox = new VBox();
vbox.setPadding(new Insets(0, 25, 0, 5));
vbox.getStyleClass().add("action_details_pane_vbox");
vbox.setSpacing(10.0);
getStyleClass().add("action_details_pane");
// setPadding(new Insets(5,50,5,50));
// setMinHeight(245);
scrollPane = new ScrollPane();
VBox.setMargin(scrollPane, new Insets(0, 0, 0, 10));
scrollPane.getStyleClass().add("action_details_pane_scroll_pane");
setMinHeight(310);
scrollPane.setContent(vbox);
vbox.prefWidthProperty().bind(scrollPane.widthProperty());
scrollPane.prefWidthProperty().bind(widthProperty());
VBox.setVgrow(scrollPane, Priority.ALWAYS);
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
openFolderButton = new Button("Open Folder");
FontIcon folderOpenIcon = new FontIcon("far-folder-open");
openFolderButton.setGraphic(folderOpenIcon);
openFolderButton.setOnAction(event -> onOpenFolderButtonClicked());
saveButton = new Button("Apply Changes");
FontIcon syncIcon = new FontIcon("fas-sync-alt");
saveButton.setGraphic(syncIcon);
saveButton.setOnAction(event -> onSaveButtonClicked());
deleteButton = new Button("Delete action");
FontIcon deleteIcon = new FontIcon("fas-trash");
deleteIcon.setIconColor(Paint.valueOf("#FF0000"));
deleteButton.setTextFill(Paint.valueOf("#FF0000"));
deleteButton.setGraphic(deleteIcon);
deleteButton.setOnAction(event -> onDeleteButtonClicked());
returnButtonForCombineActionChild = new Button("Return");
returnButtonForCombineActionChild.setGraphic(new FontIcon("fas-caret-left"));
returnButtonForCombineActionChild.managedProperty().bind(returnButtonForCombineActionChild.visibleProperty());
returnButtonForCombineActionChild.setOnAction(event -> {
try {
logger.info("@@## : " + action.getParent());
onActionClicked(getClientProfile().getActionByID(action.getParent()), getActionBox());
} catch (MinorException e) {
e.printStackTrace();
}
});
buttonBar = new HBox(openFolderButton, returnButtonForCombineActionChild, saveButton, deleteButton);
buttonBar.setPadding(new Insets(10, 10, 10, 0));
buttonBar.setAlignment(Pos.CENTER_RIGHT);
buttonBar.setVisible(false);
buttonBar.setSpacing(10.0);
actionHeadingLabel = new Label();
actionHeadingLabel.getStyleClass().add("action_details_pane_action_heading_label");
HBox headingHBox = new HBox(actionHeadingLabel);
headingHBox.setPadding(new Insets(5, 10, 0, 10));
getChildren().addAll(headingHBox, scrollPane, buttonBar);
displayTextAlignmentComboBox = new ComboBox<>(FXCollections.observableArrayList(DisplayTextAlignment.TOP,
DisplayTextAlignment.CENTER, DisplayTextAlignment.BOTTOM));
displayTextAlignmentComboBox.managedProperty().bind(displayTextAlignmentComboBox.visibleProperty());
actionClientProperties = new ArrayList<>();
displayNameTextField = new TextField();
displayNameTextField.managedProperty().bind(displayNameTextField.visibleProperty());
iconFileTextField = new TextField();
iconFileTextField.managedProperty().bind(iconFileTextField.visibleProperty());
iconFileTextField.textProperty().addListener((observableValue, s, t1) -> {
try {
if (!s.equals(t1) && t1.length() > 0) {
byte[] iconFileByteArray = Files.readAllBytes(new File(t1).toPath());
hideIconCheckBox.setDisable(false);
hideIconCheckBox.setSelected(false);
clearIconButton.setDisable(false);
System.out.println("ABABABABABBABABBABABABCCCCCCCCCCCCCCCCCC");
action.setIcon(iconFileByteArray);
setSendIcon(true);
System.out.println(action.getIconAsByteArray().length);
}
} catch (Exception e) {
e.printStackTrace();
exceptionAndAlertHandler.handleMinorException(new MinorException(e.getMessage()));
}
});
clearIconButton = new Button("Clear Icon");
clearIconButton.managedProperty().bind(clearIconButton.visibleProperty());
clearIconButton.setOnAction(event -> {
hideIconCheckBox.setDisable(true);
hideIconCheckBox.setSelected(false);
clearIconButton.setDisable(true);
iconFileTextField.clear();
});
hideDisplayTextCheckBox = new CheckBox("Hide");
hideDisplayTextCheckBox.managedProperty().bind(hideDisplayTextCheckBox.visibleProperty());
hideIconCheckBox = new CheckBox("Hide");
hideIconCheckBox.managedProperty().bind(hideIconCheckBox.visibleProperty());
actionBackgroundColourPicker = new ColorPicker();
actionBackgroundColourPicker.managedProperty().bind(actionBackgroundColourPicker.visibleProperty());
displayTextColourPicker = new ColorPicker();
displayTextColourPicker.managedProperty().bind(displayTextColourPicker.visibleProperty());
actionBackgroundColourTransparentCheckBox = new CheckBox("Default");
actionBackgroundColourPicker.disableProperty()
.bind(actionBackgroundColourTransparentCheckBox.selectedProperty());
HBox.setMargin(actionBackgroundColourTransparentCheckBox, new Insets(0, 0, 0, 10));
displayTextColourDefaultCheckBox = new CheckBox("Default");
displayTextColourPicker.disableProperty()
.bind(displayTextColourDefaultCheckBox.selectedProperty());
HBox.setMargin(displayTextColourDefaultCheckBox, new Insets(0, 0, 0, 10));
Region r = new Region();
HBox.setHgrow(r, Priority.ALWAYS);
Region r1 = new Region();
HBox.setHgrow(r1, Priority.ALWAYS);
HBox displayTextColourHBox = new HBox(new Label("Display Text Colour"), r1, displayTextColourPicker,
displayTextColourDefaultCheckBox);
displayTextColourHBox.setAlignment(Pos.CENTER);
displayTextColourHBox.setSpacing(5.0);
HBox bgColourHBox = new HBox(new Label("Background Colour"), r, actionBackgroundColourPicker,
actionBackgroundColourTransparentCheckBox);
bgColourHBox.setAlignment(Pos.CENTER);
bgColourHBox.setSpacing(5.0);
HBox clearIconHBox = new HBox(clearIconButton);
clearIconHBox.setAlignment(Pos.CENTER_RIGHT);
displayTextFieldHBox = new HBoxInputBox("Display Name", displayNameTextField, hideDisplayTextCheckBox);
normalActionsPropsVBox = new VBox(displayTextColourHBox,
new HBox(new Label("Alignment"), new SpaceFiller(SpaceFiller.FillerType.HBox),
displayTextAlignmentComboBox),
new HBoxInputBoxWithFileChooser("Icon", iconFileTextField, hideIconCheckBox,
new FileChooser.ExtensionFilter("Images", "*.jpeg", "*.jpg", "*.png", "*.gif")),
clearIconHBox, bgColourHBox);
normalActionsPropsVBox.managedProperty().bind(normalActionsPropsVBox.visibleProperty());
normalActionsPropsVBox.setSpacing(10.0);
vbox.getChildren().addAll(displayTextFieldHBox, normalActionsPropsVBox, clientPropertiesVBox);
vbox.setVisible(false);
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
setOnDragOver(dragEvent -> {
if (dragEvent.getDragboard().hasContent(Action.getDataFormat()) && action != null) {
if (action.getActionType() == ActionType.COMBINE) {
dragEvent.acceptTransferModes(TransferMode.ANY);
dragEvent.consume();
}
}
});
setOnDragDropped(dragEvent -> {
try {
Action newAction = (Action) dragEvent.getDragboard().getContent(Action.getDataFormat());
if (newAction.getActionType() == ActionType.NORMAL) {
newAction.setLocation(new Location(-1, -1));
newAction.setParent(this.action.getID());
combineActionPropertiesPane.getCombineAction().addChild(newAction.getID());
addActionToCurrentClientProfile(newAction);
ClientConnection connection = ClientConnections.getInstance()
.getClientConnectionBySocketAddress(getClient().getRemoteSocketAddress());
connection.saveActionDetails(getClientProfile().getID(), newAction);
combineActionPropertiesPane.renderProps();
saveAction();
}
} catch (MinorException e) {
exceptionAndAlertHandler.handleMinorException(e);
e.printStackTrace();
} catch (SevereException e) {
exceptionAndAlertHandler.handleSevereException(e);
e.printStackTrace();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
});
}
private VBox normalActionsPropsVBox;
private HBox displayTextFieldHBox;
private Client client;
private ClientProfile clientProfile;
public void setClient(Client client) {
this.client = client;
}
public Client getClient() {
return client;
}
public void setClientProfile(ClientProfile clientProfile) {
this.clientProfile = clientProfile;
}
public ClientProfile getClientProfile() {
return clientProfile;
}
public void setActionHeadingLabelText(String text) {
actionHeadingLabel.setText(text);
}
private Action action;
public Action getAction() {
return action;
}
private ActionBox actionBox;
public ActionBox getActionBox() {
return actionBox;
}
@Override
public void onActionClicked(Action action, ActionBox actionBox) throws MinorException {
this.action = action;
this.actionBox = actionBox;
System.out.println(action.getActionType());
logger.info("action Display text : "+action.getDisplayText());
clear();
renderActionProperties();
}
private TextField displayNameTextField;
private CheckBox hideDisplayTextCheckBox;
private CheckBox hideIconCheckBox;
private TextField iconFileTextField;
private Button clearIconButton;
private ColorPicker actionBackgroundColourPicker;
private ColorPicker displayTextColourPicker;
private CheckBox actionBackgroundColourTransparentCheckBox;
private CheckBox displayTextColourDefaultCheckBox;
private ComboBox<DisplayTextAlignment> displayTextAlignmentComboBox;
public void clear()
{
sendIcon = false;
actionClientProperties.clear();
displayNameTextField.clear();
iconFileTextField.clear();
clientPropertiesVBox.getChildren().clear();
vbox.setVisible(false);
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
buttonBar.setVisible(false);
setActionHeadingLabelText("");
actionBackgroundColourPicker.setValue(Color.WHITE);
displayTextColourPicker.setValue(Color.WHITE);
}
boolean isCombineChild = false;
public boolean isCombineChild() {
return isCombineChild;
}
public void renderActionProperties() throws MinorException
{
if(action.getLocation().getCol() == -1) //Combine Child action
isCombineChild = true;
else
isCombineChild = false;
displayNameTextField.setText(action.getDisplayText());
System.out.println(action.getDisplayText()+"@@@@::::"+isCombineChild);
if(isCombineChild)
{
setReturnButtonForCombineActionChildVisible(true);
normalActionsPropsVBox.setVisible(false);
hideDisplayTextCheckBox.setSelected(false);
hideDisplayTextCheckBox.setVisible(false);
}
else
{
normalActionsPropsVBox.setVisible(true);
setReturnButtonForCombineActionChildVisible(false);
hideDisplayTextCheckBox.setVisible(true);
setFolderButtonVisible(action.getActionType().equals(ActionType.FOLDER));
displayTextAlignmentComboBox.getSelectionModel().select(action.getDisplayTextAlignment());
if(!action.getBgColourHex().isEmpty())
actionBackgroundColourPicker.setValue(Color.valueOf(action.getBgColourHex()));
else
actionBackgroundColourTransparentCheckBox.setSelected(true);
if(!action.getDisplayTextFontColourHex().isEmpty())
displayTextColourPicker.setValue(Color.valueOf(action.getDisplayTextFontColourHex()));
else
displayTextColourDefaultCheckBox.setSelected(true);
hideDisplayTextCheckBox.setSelected(!action.isShowDisplayText());
hideIconCheckBox.setDisable(!action.isHasIcon());
hideIconCheckBox.setSelected(!action.isShowIcon());
if(!action.isHasIcon())
{
hideIconCheckBox.setSelected(false);
}
clearIconButton.setDisable(!action.isHasIcon());
}
buttonBar.setVisible(true);
vbox.setVisible(true);
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
if(action.getActionType() == ActionType.NORMAL)
{
if(action.isInvalid())
setActionHeadingLabelText("Invalid action ("+action.getModuleName()+")");
else
setActionHeadingLabelText(action.getName());
}
else if(action.getActionType() == ActionType.COMBINE)
setActionHeadingLabelText("Combine action");
else if(action.getActionType() == ActionType.FOLDER)
setActionHeadingLabelText("Folder action");
if(!action.isInvalid())
{
if(action.getActionType() == ActionType.NORMAL)
renderClientProperties();
else if(action.getActionType() == ActionType.COMBINE)
renderCombineActionProperties();
}
}
private CombineActionPropertiesPane combineActionPropertiesPane;
public CombineActionPropertiesPane getCombineActionPropertiesPane() {
return combineActionPropertiesPane;
}
public void setReturnButtonForCombineActionChildVisible(boolean visible)
{
returnButtonForCombineActionChild.setVisible(visible);
}
public void renderCombineActionProperties()
{
try {
logger.info("@@@@@ : "+action.getClientProperties().getSize());
combineActionPropertiesPane = new CombineActionPropertiesPane(getActionAsCombineAction(action),
getClientProfile(),
this
);
clientPropertiesVBox.getChildren().add(combineActionPropertiesPane);
}
catch (MinorException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleMinorException(e);
}
}
public void setAction(Action action) {
this.action = action;
}
public FolderAction getActionAsFolderAction(Action action)
{
FolderAction folderAction = new FolderAction();
folderAction.setDisplayText(action.getDisplayText());
folderAction.setName(action.getName());
folderAction.setID(action.getID());
folderAction.setLocation(action.getLocation());
folderAction.setBgColourHex(action.getBgColourHex());
folderAction.setParent(action.getParent());
folderAction.getClientProperties().set(action.getClientProperties());
folderAction.setDisplayTextAlignment(action.getDisplayTextAlignment());
folderAction.setShowIcon(action.isShowIcon());
folderAction.setHasIcon(action.isHasIcon());
if(folderAction.isHasIcon())
folderAction.setIcon(action.getIconAsByteArray());
folderAction.setDisplayTextFontColourHex(action.getDisplayTextFontColourHex());
return folderAction;
}
public CombineAction getActionAsCombineAction(Action action)
{
CombineAction combineAction = new CombineAction();
combineAction.setDisplayText(action.getDisplayText());
combineAction.setName(action.getName());
combineAction.setID(action.getID());
combineAction.setLocation(action.getLocation());
combineAction.setBgColourHex(action.getBgColourHex());
combineAction.setParent(action.getParent());
combineAction.getClientProperties().set(action.getClientProperties());
combineAction.setDisplayTextAlignment(action.getDisplayTextAlignment());
combineAction.setShowIcon(action.isShowIcon());
combineAction.setHasIcon(action.isHasIcon());
if(combineAction.isHasIcon())
combineAction.setIcon(action.getIconAsByteArray());
combineAction.setDisplayTextFontColourHex(action.getDisplayTextFontColourHex());
for(Property prop : combineAction.getClientProperties().get())
{
System.out.println("PROP : "+prop.getName()+","+prop.getRawValue());
}
return combineAction;
}
public void onOpenFolderButtonClicked()
{
FolderAction folderAction = getActionAsFolderAction(action);
actionBox.getActionGridPaneListener().renderFolder(folderAction);
clear();
}
@Override
public Window getCurrentWindow() {
return getScene().getWindow();
}
private ArrayList<UIPropertyBox> actionClientProperties;
public void renderClientProperties() throws MinorException
{
for(int i =0;i< action.getClientProperties().getSize(); i++)
{
Property eachProperty = action.getClientProperties().get().get(i);
if(!eachProperty.isVisible())
continue;
Label label = new Label(eachProperty.getDisplayName());
HBox hBox = new HBox(label);
hBox.setSpacing(5.0);
hBox.setAlignment(Pos.CENTER_LEFT);
Node controlNode = null;
if(eachProperty.getHelpLink() != null)
{
Button helpButton = new Button();
FontIcon questionIcon = new FontIcon("fas-question");
helpButton.setGraphic(questionIcon);
helpButton.setOnAction(event -> {
hostServices.showDocument(eachProperty.getHelpLink());
});
hBox.getChildren().add(helpButton);
}
hBox.getChildren().add(new SpaceFiller(SpaceFiller.FillerType.HBox));
if(eachProperty.getControlType() == ControlType.COMBO_BOX)
{
ComboBox<String> comboBox = new ComboBox<>();
comboBox.getItems().addAll(eachProperty.getListValue());
comboBox.getSelectionModel().select(eachProperty.getSelectedIndex());
controlNode = comboBox;
}
else if(eachProperty.getControlType() == ControlType.TEXT_FIELD)
{
TextField textField = new TextField(eachProperty.getRawValue());
controlNode= textField;
}
else if(eachProperty.getControlType() == ControlType.TOGGLE)
{
ToggleButton toggleButton = new ToggleButton();
toggleButton.setSelected(eachProperty.getBoolValue());
if(eachProperty.getBoolValue())
toggleButton.setText("ON");
else
toggleButton.setText("OFF");
toggleButton.selectedProperty().addListener((observableValue, aBoolean, t1) -> {
if(t1)
toggleButton.setText("ON");
else
toggleButton.setText("OFF");
});
controlNode = toggleButton;
}
else if(eachProperty.getControlType() == ControlType.SLIDER_DOUBLE)
{
Slider slider = new Slider();
slider.setValue(eachProperty.getDoubleValue());
slider.setMax(eachProperty.getMaxDoubleValue());
slider.setMin(eachProperty.getMinDoubleValue());
controlNode = slider;
}
else if(eachProperty.getControlType() == ControlType.SLIDER_INTEGER)
{
Slider slider = new Slider();
slider.setValue(eachProperty.getIntValue());
slider.setMax(eachProperty.getMaxIntValue());
slider.setMin(eachProperty.getMinIntValue());
slider.setBlockIncrement(1.0);
slider.setSnapToTicks(true);
controlNode = slider;
}
hBox.getChildren().add(controlNode);
UIPropertyBox clientProperty = new UIPropertyBox(i, eachProperty.getDisplayName(), controlNode,
eachProperty.getControlType(), eachProperty.getType(), eachProperty.isCanBeBlank());
actionClientProperties.add(clientProperty);
clientPropertiesVBox.getChildren().add(hBox);
}
}
public void onSaveButtonClicked()
{
try {
// saveButton.setDisable(true);
// deleteButton.setDisable(true);
validateForm();
saveAction();
}
catch (MinorException e)
{
exceptionAndAlertHandler.handleMinorException(e);
}
}
private boolean sendIcon = false;
@Override
public void setSendIcon(boolean sendIcon)
{
this.sendIcon = sendIcon;
}
public void addActionToCurrentClientProfile(Action newAction) throws CloneNotSupportedException {
getClientProfile().addAction(newAction);
}
@Override
public void saveAction(Action action, boolean runAsync)
{
new OnSaveActionTask(
ClientConnections.getInstance().getClientConnectionBySocketAddress(
getClient().getRemoteSocketAddress()
),
action,
displayNameTextField.getText(),
isCombineChild(),
!hideDisplayTextCheckBox.isSelected(),
displayTextColourDefaultCheckBox.isSelected(),
"#" + displayTextColourPicker.getValue().toString().substring(2),
clearIconButton.isDisable(),
!hideIconCheckBox.isSelected(),
displayTextAlignmentComboBox.getSelectionModel().getSelectedItem(),
actionBackgroundColourTransparentCheckBox.isSelected(),
"#" + actionBackgroundColourPicker.getValue().toString().substring(2),
getCombineActionPropertiesPane(),
clientProfile, sendIcon, actionBox, actionClientProperties, exceptionAndAlertHandler,
saveButton, deleteButton, runAsync
);
}
@Override
public void saveAction()
{
saveAction(action, true);
}
public void setFolderButtonVisible(boolean visible)
{
openFolderButton.setVisible(visible);
}
public void validateForm() throws MinorException
{
String displayNameStr = displayNameTextField.getText();
StringBuilder finalErrors = new StringBuilder();
if(displayNameStr.isBlank())
{
finalErrors.append(" * Display Name cannot be blank\n");
}
if(!isCombineChild())
{
if(action.isHasIcon())
{
if(hideDisplayTextCheckBox.isSelected() && hideIconCheckBox.isSelected())
finalErrors.append(" * Both Icon and display text check box cannot be hidden.\n");
}
else
{
if(hideDisplayTextCheckBox.isSelected())
finalErrors.append(" * Display Text cannot be hidden, since there is also no icon.\n");
}
}
for (UIPropertyBox clientProperty : actionClientProperties) {
Node controlNode = clientProperty.getControlNode();
if (clientProperty.getControlType() == ControlType.TEXT_FIELD)
{
String value = ((TextField) controlNode).getText();
if(clientProperty.getType() == Type.INTEGER)
{
try
{
Integer.parseInt(value);
}
catch (NumberFormatException e)
{
finalErrors.append(" -> ").append(clientProperty.getDisplayName()).append(" must be integer.\n");
}
}
else
{
if(value.isBlank() && !clientProperty.isCanBeBlank())
finalErrors.append(" -> ").append(clientProperty.getDisplayName()).append(" cannot be blank.\n");
}
}
}
if(!finalErrors.toString().isEmpty())
{
throw new MinorException("You made mistakes",
finalErrors.toString());
}
}
public void onDeleteButtonClicked()
{
new OnDeleteActionTask(
ClientConnections.getInstance().getClientConnectionBySocketAddress(
getClient().getRemoteSocketAddress()
),
action,
isCombineChild(),
getCombineActionPropertiesPane(),
clientProfile, actionBox, this, exceptionAndAlertHandler,
!isCombineChild
);
}
}
A
src/main/java/com/stream_pi/server/window/dashboard/actiondetailpane/ActionDetailsPaneListener.java
+23
−0
package com.stream_pi.server.window.dashboard.actiondetailpane;
import com.stream_pi.actionapi.action.Action;
import com.stream_pi.server.window.dashboard.actiongridpane.ActionBox;
import com.stream_pi.util.exception.MinorException;
import javafx.stage.Window;
public interface ActionDetailsPaneListener {
void onActionClicked(Action action, ActionBox actionBox) throws MinorException;
void saveAction();
void saveAction(Action action, boolean runAsync);
void clear();
void setSendIcon(boolean sendIcon);
void onOpenFolderButtonClicked();
Window getCurrentWindow();
}
package com.stream_pi.server.window.dashboard.actiondetailpane;
import com.stream_pi.actionapi.action.Action;
import com.stream_pi.actionapi.actionproperty.property.Property;
import com.stream_pi.actionapi.otheractions.CombineAction;
import com.stream_pi.server.client.ClientProfile;
import com.stream_pi.util.exception.MinorException;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.ArrayList;
import java.util.List;
public class CombineActionPropertiesPane extends VBox {
private CombineAction combineAction;
private ClientProfile clientProfile;
private ActionDetailsPane actionDetailsPane;
public CombineAction getCombineAction() {
return combineAction;
}
public CombineActionPropertiesPane(CombineAction combineAction, ClientProfile clientProfile, ActionDetailsPane actionDetailsPane) throws MinorException {
this.combineAction = combineAction;
this.clientProfile = clientProfile;
this.actionDetailsPane = actionDetailsPane;
setSpacing(10.0);
setPadding(new Insets(0,0,10,0));
renderProps();
}
public void renderProps() throws MinorException {
getChildren().clear();
int i = 0;
for(String actionID : combineAction.getChildrenIDSequential())
{
Action action = clientProfile.getActionByID(actionID);
System.out.println("232323xxxxxxxxxxxx : "+action.getID());
Button settingsButton = new Button();
FontIcon settingsFontIcon = new FontIcon("fas-cog");
settingsButton.setGraphic(settingsFontIcon);
settingsButton.setOnAction(event -> onSettingsButtonClicked(action));
Button upButton = new Button();
FontIcon upButtonFontIcon = new FontIcon("fas-chevron-up");
upButton.setGraphic(upButtonFontIcon);
Button downButton = new Button();
FontIcon downButtonFontIcon = new FontIcon("fas-chevron-down");
downButton.setGraphic(downButtonFontIcon);
Label displayTextLabel = new Label(action.getDisplayText());
displayTextLabel.setId(action.getID());
Region r = new Region();
HBox.setHgrow(r, Priority.ALWAYS);
HBox actionHBox = new HBox(displayTextLabel, r, settingsButton, upButton, downButton);
actionHBox.setSpacing(5.0);
upButton.setUserData(i);
downButton.setUserData(i);
upButton.setOnAction(this::onUpButtonClicked);
downButton.setOnAction(this::onDownButtonClicked);
System.out.println("67878678678678");
getChildren().add(actionHBox);
System.out.println("9mhmmn");
i++;
}
}
public void onSettingsButtonClicked(Action action)
{
actionDetailsPane.clear();
actionDetailsPane.setAction(action);
try {
actionDetailsPane.renderActionProperties();
} catch (MinorException e) {
e.printStackTrace();
}
}
public void onUpButtonClicked(ActionEvent event) {
try {
Node node = (Node) event.getSource();
int currentIndex = (int) node.getUserData();
if(currentIndex > 0)
{
Property current = combineAction.getClientProperties().getSingleProperty(currentIndex+"");
Property aboveOne = combineAction.getClientProperties().getSingleProperty((currentIndex-1)+"");
combineAction.addChild(current.getRawValue(), currentIndex-1);
combineAction.addChild(aboveOne.getRawValue(), currentIndex);
actionDetailsPane.saveAction();
renderProps();
}
}
catch (MinorException e)
{
e.printStackTrace();
}
}
public void onDownButtonClicked(ActionEvent event)
{
try {
Node node = (Node) event.getSource();
int currentIndex = (int) node.getUserData();
if(currentIndex < getChildren().size() - 1)
{
Property current = combineAction.getClientProperties().getSingleProperty(currentIndex+"");
Property belowOne = combineAction.getClientProperties().getSingleProperty((currentIndex+1)+"");
combineAction.addChild(current.getRawValue(), currentIndex+1);
combineAction.addChild(belowOne.getRawValue(), currentIndex);
actionDetailsPane.saveAction();
renderProps();
}
}
catch (MinorException e)
{
e.printStackTrace();
}
}
public List<String> getFinalChildren()
{
ArrayList<String> children = new ArrayList<>();
for(int i = 0;i<getChildren().size();i++)
{
HBox hBox = (HBox) getChildren().get(i);
Label label = (Label) hBox.getChildren().get(0);
children.add(label.getId());
}
return children;
}
}
package com.stream_pi.server.window.dashboard.actiondetailpane;
import java.util.logging.Logger;
import com.stream_pi.actionapi.action.Action;
import com.stream_pi.server.client.ClientProfile;
import com.stream_pi.server.connection.ClientConnection;
import com.stream_pi.server.window.ExceptionAndAlertHandler;
import com.stream_pi.server.window.dashboard.actiongridpane.ActionBox;
import com.stream_pi.util.exception.MinorException;
import com.stream_pi.util.exception.SevereException;
import javafx.application.Platform;
import javafx.concurrent.Task;
public class OnDeleteActionTask extends Task<Void>
{
private Logger logger;
public OnDeleteActionTask(ClientConnection connection, Action action, boolean isCombineChild,
CombineActionPropertiesPane combineActionPropertiesPane, ClientProfile clientProfile, ActionBox actionBox,
ActionDetailsPane actionDetailsPane,
ExceptionAndAlertHandler exceptionAndAlertHandler, boolean runAsync)
{
this.connection = connection;
this.action = action;
this.isCombineChild = isCombineChild;
this.combineActionPropertiesPane = combineActionPropertiesPane;
this.clientProfile = clientProfile;
this.actionBox = actionBox;
this.actionDetailsPane = actionDetailsPane;
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
logger = Logger.getLogger(getClass().getName());
if(runAsync)
new Thread(this).start();
else
runTask();
}
private ClientConnection connection;
private Action action;
private ActionBox actionBox;
private ActionDetailsPane actionDetailsPane;
private boolean isCombineChild;
private ClientProfile clientProfile;
private CombineActionPropertiesPane combineActionPropertiesPane;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
private void runTask()
{
try {
connection.deleteAction(clientProfile.getID(), action.getID());
clientProfile.removeActionByID(action.getID());
if(isCombineChild)
{
System.out.println("ACTION ID TO BE REMOVED : "+action.getID());
combineActionPropertiesPane.getCombineAction().removeChild(action.getID());
System.out.println("222155 "+combineActionPropertiesPane.getCombineAction().getClientProperties().getSize());
combineActionPropertiesPane.renderProps();
try {
actionDetailsPane.saveAction(combineActionPropertiesPane.getCombineAction(), false);
System.out.println(combineActionPropertiesPane.getCombineAction().getDisplayText()+"@#@#@#@#@#@#");
actionDetailsPane.onActionClicked(
combineActionPropertiesPane.getCombineAction(),
actionBox
);
} catch (MinorException e) {
e.printStackTrace();
}
}
else
{
Platform.runLater(()->{
actionBox.clear();
actionDetailsPane.clear();
});
}
// Platform.runLater(()->{
// saveButton.setDisable(false);
// deleteButton.setDisable(false);
// });
}
catch (SevereException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleSevereException(e);
}
catch (MinorException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleMinorException(e);
}
}
@Override
protected Void call() throws Exception
{
runTask();
return null;
}
}
package com.stream_pi.server.window.dashboard.actiondetailpane;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import com.stream_pi.actionapi.action.Action;
import com.stream_pi.actionapi.action.ActionType;
import com.stream_pi.actionapi.action.DisplayTextAlignment;
import com.stream_pi.actionapi.actionproperty.ClientProperties;
import com.stream_pi.actionapi.actionproperty.property.Property;
import com.stream_pi.actionapi.actionproperty.property.Type;
import com.stream_pi.server.client.ClientProfile;
import com.stream_pi.server.connection.ClientConnection;
import com.stream_pi.server.uipropertybox.UIPropertyBox;
import com.stream_pi.server.window.ExceptionAndAlertHandler;
import com.stream_pi.server.window.dashboard.actiongridpane.ActionBox;
import com.stream_pi.util.exception.SevereException;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.control.Button;
public class OnSaveActionTask extends Task<Void>
{
private Logger logger;
public OnSaveActionTask(ClientConnection connection, Action action, String displayNameText, boolean isCombineChild,
boolean isShowDisplayText, boolean isDefaultDisplayTextColour, String displayTextFontColour, boolean isClearIcon,
boolean isHideIcon, DisplayTextAlignment displayTextAlignment, boolean isTransparentBackground, String backgroundColour,
CombineActionPropertiesPane combineActionPropertiesPane, ClientProfile clientProfile, boolean sendIcon, ActionBox actionBox,
ArrayList<UIPropertyBox> actionClientProperties, ExceptionAndAlertHandler exceptionAndAlertHandler, Button saveButton, Button deleteButton,
boolean runAsync)
{
this.saveButton = saveButton;
this.deleteButton = deleteButton;
this.connection = connection;
this.action = action;
this.displayNameText = displayNameText;
this.isCombineChild = isCombineChild;
this.isShowDisplayText = isShowDisplayText;
this.isDefaultDisplayTextColour = isDefaultDisplayTextColour;
this.displayTextFontColour = displayTextFontColour;
this.isClearIcon = isClearIcon;
this.isHideIcon = isHideIcon;
this.displayTextAlignment = displayTextAlignment;
this.isTransparentBackground = isTransparentBackground;
this.combineActionPropertiesPane = combineActionPropertiesPane;
this.clientProfile = clientProfile;
this.sendIcon = sendIcon;
this.actionBox = actionBox;
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
this.backgroundColour = backgroundColour;
this.actionClientProperties = actionClientProperties;
logger = Logger.getLogger(getClass().getName());
if(runAsync)
new Thread(this).start();
else
runTask();
}
private Button saveButton;
private Button deleteButton;
private boolean isShowDisplayText;
private boolean isCombineChild;
private String displayNameText;
private boolean isDefaultDisplayTextColour;
private ArrayList<UIPropertyBox> actionClientProperties;
private String displayTextFontColour;
private boolean isClearIcon;
private boolean isHideIcon;
private DisplayTextAlignment displayTextAlignment;
private boolean isTransparentBackground;
private String backgroundColour;
private CombineActionPropertiesPane combineActionPropertiesPane;
private ClientProfile clientProfile;
private boolean sendIcon;
private ActionBox actionBox;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
private Action action;
private ClientConnection connection;
private void setSaveDeleteButtonState(boolean state)
{
Platform.runLater(()->{
saveButton.setDisable(state);
deleteButton.setDisable(state);
});
}
private void runTask()
{
action.setDisplayText(displayNameText);
if(!isCombineChild)
{
setSaveDeleteButtonState(true);
action.setShowDisplayText(isShowDisplayText);
if(isDefaultDisplayTextColour)
action.setDisplayTextFontColourHex("");
else
{
action.setDisplayTextFontColourHex(displayTextFontColour);
//String fontColour = "#" + displayTextColourPicker.getValue().toString().substring(2);
//action.setDisplayTextFontColourHex(fontColour);
}
if(isClearIcon)
{
action.setIcon(null);
action.setHasIcon(false);
action.setShowIcon(false);
}
if(action.isHasIcon())
action.setShowIcon(isHideIcon);
action.setDisplayTextAlignment(displayTextAlignment);
logger.info("BBBGGG : "+backgroundColour);
if(isTransparentBackground)
action.setBgColourHex("");
else
{
//String bgColour = "#" + actionBackgroundColourPicker.getValue().toString().substring(2);
action.setBgColourHex(backgroundColour);
}
}
System.out.println("parent : "+action.getParent());
if(action.getActionType() == ActionType.COMBINE)
{
List<String> finalChildren = combineActionPropertiesPane.getFinalChildren();
System.out.println("2334 "+finalChildren.size());
ClientProperties clientProperties = new ClientProperties();
for(int i = 0;i<finalChildren.size();i++)
{
Property property = new Property(i+"", Type.STRING);
property.setRawValue(finalChildren.get(i));
clientProperties.addProperty(property);
}
action.getClientProperties().set(clientProperties);
}
else
{
//properties
for (UIPropertyBox clientProperty : actionClientProperties) {
action.getClientProperties().get().get(clientProperty.getIndex()).setRawValue(clientProperty.getRawValue());
}
}
try
{
logger.info("Saving action ... "+action.isHasIcon()+"+"+sendIcon);
/*if(action.isHasIcon())
{
if(clientProfile.getActionByID(action.getID()).getIconAsByteArray() == null)
{
sendIcon = true;
}
else
{
if(!Arrays.equals(action.getIconAsByteArray(), clientProfile.getActionByID(action.getID()).getIconAsByteArray()))
{
logger.info("Sending ...");
sendIcon = true;
}
}
}*/
connection.saveActionDetails(clientProfile.getID(), action);
if(sendIcon)
{
connection.sendIcon(clientProfile.getID(), action.getID(), action.getIconAsByteArray());
}
if(!isCombineChild)
{
Platform.runLater(()->{
actionBox.clear();
actionBox.setAction(action);
actionBox.baseInit();
actionBox.init();
});
setSaveDeleteButtonState(false);
}
clientProfile.removeActionByID(action.getID());
clientProfile.addAction(action);
}
catch (SevereException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleSevereException(e);
}
catch (CloneNotSupportedException e)
{
e.printStackTrace();
}
}
@Override
protected Void call() throws Exception
{
runTask();
return null;
}
}
package com.stream_pi.server.window.dashboard.actiongridpane;
import com.stream_pi.actionapi.action.Action;
import com.stream_pi.actionapi.action.ActionType;
import com.stream_pi.actionapi.action.DisplayTextAlignment;
import com.stream_pi.actionapi.action.Location;
import com.stream_pi.server.window.ExceptionAndAlertHandler;
import com.stream_pi.server.window.dashboard.actiondetailpane.ActionDetailsPaneListener;
import com.stream_pi.util.exception.MinorException;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.CacheHint;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseButton;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundImage;
import javafx.scene.layout.BackgroundPosition;
import javafx.scene.layout.BackgroundRepeat;
import javafx.scene.layout.BackgroundSize;
import javafx.scene.layout.StackPane;
import javafx.scene.text.TextAlignment;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.nio.ByteBuffer;
public class ActionBox extends StackPane{
private Label displayTextLabel;
private int row;
private int col;
public void setRow(int row) {
this.row = row;
}
public void setCol(int col) {
this.col = col;
}
public int getRow() {
return row;
}
public int getCol() {
return col;
}
private ActionDetailsPaneListener actionDetailsPaneListener;
public void clear()
{
setAction(null);
setInvalid(false);
setBackground(Background.EMPTY);
setStyle(null);
getChildren().clear();
}
public void baseInit()
{
displayTextLabel = new Label();
displayTextLabel.setWrapText(true);
displayTextLabel.setTextAlignment(TextAlignment.CENTER);
displayTextLabel.getStyleClass().add("action_box_display_text_label");
displayTextLabel.prefHeightProperty().bind(heightProperty());
displayTextLabel.prefWidthProperty().bind(widthProperty());
getChildren().addAll(displayTextLabel);
setMinSize(size, size);
setMaxSize(size, size);
getStyleClass().add("action_box");
getStyleClass().add("action_box_icon_not_present");
getStyleClass().add("action_box_valid");
setOnDragOver(dragEvent ->
{
if(dragEvent.getDragboard().hasContent(Action.getDataFormat()))
{
dragEvent.acceptTransferModes(TransferMode.ANY);
dragEvent.consume();
}
});
setOnDragDropped(dragEvent ->
{
try
{
if(action == null)
{
Action action = (Action) dragEvent.getDragboard().getContent(Action.getDataFormat());
action.setLocation(new Location(getRow(),
getCol()));
action.setParent(getStreamPiParent());
action.setIDRandom();
actionGridPaneListener.addActionToCurrentClientProfile(action);
setAction(action);
init();
actionDetailsPaneListener.onActionClicked(action, this);
if(action.isHasIcon())
actionDetailsPaneListener.setSendIcon(true);
actionDetailsPaneListener.saveAction();
}
}
catch (MinorException e)
{
exceptionAndAlertHandler.handleMinorException(e);
e.printStackTrace();
}
});
setOnDragDetected(mouseEvent -> {
try {
if(action!=null)
{
if(action.getActionType() == ActionType.NORMAL)
{
Dragboard db = startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
Action newAction = (Action) action.clone();
newAction.setIDRandom();
newAction.setParent(getStreamPiParent());
content.put(Action.getDataFormat(), newAction);
db.setContent(content);
mouseEvent.consume();
}
}
}
catch (CloneNotSupportedException e)
{
e.printStackTrace();
}
});
setOnMouseClicked(mouseEvent -> {
if(action != null && mouseEvent.getButton().equals(MouseButton.PRIMARY))
{
if(mouseEvent.getClickCount() == 2 && action.getActionType() == ActionType.FOLDER)
{
getActionDetailsPaneListener().onOpenFolderButtonClicked();
}
else
{
try
{
actionDetailsPaneListener.onActionClicked(action, this);
}
catch (MinorException e)
{
exceptionAndAlertHandler.handleMinorException(e);
e.printStackTrace();
}
}
}
});
setCache(true);
setCacheHint(CacheHint.SPEED);
}
public void setInvalid(boolean invalid)
{
if(invalid)
{
getStyleClass().remove("action_box_valid");
getStyleClass().add("action_box_invalid");
}
else
{
getStyleClass().remove("action_box_invalid");
getStyleClass().add("action_box_valid");
}
}
public ActionDetailsPaneListener getActionDetailsPaneListener() {
return actionDetailsPaneListener;
}
public ActionGridPaneListener getActionGridPaneListener() {
return actionGridPaneListener;
}
private int size;
private ActionGridPaneListener actionGridPaneListener;
public ActionBox(int size, ActionDetailsPaneListener actionDetailsPaneListener, ActionGridPaneListener actionGridPaneListener)
{
this.actionGridPaneListener = actionGridPaneListener;
this.actionDetailsPaneListener = actionDetailsPaneListener;
this.size = size;
baseInit();
}
public static Action deserialize(ByteBuffer buffer) {
try {
ByteArrayInputStream is = new ByteArrayInputStream(buffer.array());
ObjectInputStream ois = new ObjectInputStream(is);
Action obj = (Action) ois.readObject();
return obj;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public void setIcon(byte[] iconByteArray)
{
if(iconByteArray == null)
{
getStyleClass().remove("action_box_icon_present");
getStyleClass().add("action_box_icon_not_present");
setBackground(null);
return;
}
else
{
getStyleClass().add("action_box_icon_present");
getStyleClass().remove("action_box_icon_not_present");
setBackground(
new Background(
new BackgroundImage(new Image(
new ByteArrayInputStream(iconByteArray), size, size, false, true
), BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.CENTER,
new BackgroundSize(100, 100, true, true, true, false))
)
);
}
}
private Action action;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
private String parent;
public String getStreamPiParent() {
return parent;
}
public void setStreamPiParent(String parent) {
this.parent = parent;
}
public ActionBox(int size, Action action, ActionDetailsPaneListener actionDetailsPaneListener, ExceptionAndAlertHandler exceptionAndAlertHandler, ActionGridPaneListener actionGridPaneListener)
{
this.actionGridPaneListener = actionGridPaneListener;
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
this.action = action;
this.actionDetailsPaneListener = actionDetailsPaneListener;
this.size = size;
baseInit();
init();
}
public void setAction(Action action)
{
this.action = action;
}
public void init()
{
if(action.isShowDisplayText())
setDisplayTextLabel(action.getDisplayText());
else
setDisplayTextLabel("");
setDisplayTextAlignment(action.getDisplayTextAlignment());
setBackgroundColour(action.getBgColourHex());
setDisplayTextFontColour(action.getDisplayTextFontColourHex());
setInvalid(action.isInvalid());
Platform.runLater(()->{
if(action.isHasIcon() && action.isShowIcon())
{
setIcon(action.getIconAsByteArray());
}
else
{
setIcon(null);
}
});
}
public void setDisplayTextLabel(String text)
{
displayTextLabel.setText(text);
}
public void setDisplayTextAlignment(DisplayTextAlignment displayTextAlignment)
{
if(displayTextAlignment == DisplayTextAlignment.CENTER)
displayTextLabel.setAlignment(Pos.CENTER);
else if (displayTextAlignment == DisplayTextAlignment.BOTTOM)
displayTextLabel.setAlignment(Pos.BOTTOM_CENTER);
else if (displayTextAlignment == DisplayTextAlignment.TOP)
displayTextLabel.setAlignment(Pos.TOP_CENTER);
}
public void setDisplayTextFontColour(String colour)
{
System.out.println("COLOr : "+colour);
if(!colour.isEmpty())
displayTextLabel.setStyle("-fx-text-fill : "+colour+";");
}
public void setBackgroundColour(String colour)
{
System.out.println("COLOr : "+colour);
if(!colour.isEmpty() && action.getIconAsByteArray() == null)
setStyle("-fx-background-color : "+colour);
}
}
package com.stream_pi.server.window.dashboard.actiongridpane;
import com.stream_pi.actionapi.action.Action;
import com.stream_pi.actionapi.action.Location;
import com.stream_pi.actionapi.otheractions.FolderAction;
import com.stream_pi.server.client.Client;
import com.stream_pi.server.client.ClientProfile;
import com.stream_pi.server.io.Config;
import com.stream_pi.server.window.ExceptionAndAlertHandler;
import com.stream_pi.server.window.dashboard.actiondetailpane.ActionDetailsPaneListener;
import com.stream_pi.util.exception.MinorException;
import com.stream_pi.util.exception.SevereException;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.*;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.logging.Logger;
public class ActionGridPane extends ScrollPane implements ActionGridPaneListener {
private ExceptionAndAlertHandler exceptionAndAlertHandler;
private ActionDetailsPaneListener actionDetailsPaneListener;
public ActionGridPane(ExceptionAndAlertHandler exceptionAndAlertHandler)
{
logger = Logger.getLogger(ActionGridPane.class.getName());
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
getStyleClass().add("action_grid_pane_scroll_pane");
VBox.setVgrow(this, Priority.ALWAYS);
actionsGridPane = new GridPane();
actionsGridPane.setPadding(new Insets(5.0));
actionsGridPane.getStyleClass().add("action_grid_pane");
actionsGridPane.setPrefSize(USE_COMPUTED_SIZE, USE_COMPUTED_SIZE);
setContent(actionsGridPane);
}
public void setActionDetailsPaneListener(ActionDetailsPaneListener actionDetailsPaneListener) {
this.actionDetailsPaneListener = actionDetailsPaneListener;
}
private String currentParent;
public void setCurrentParent(String currentParent) {
this.currentParent = currentParent;
}
public ClientProfile getClientProfile() {
return clientProfile;
}
private Client client;
public void setClient(Client client) {
this.client = client;
}
public Client getClient() {
return client;
}
private int rows, cols;
private GridPane actionsGridPane;
private ClientProfile clientProfile;
public void setClientProfile(ClientProfile clientProfile)
{
this.clientProfile = clientProfile;
setCurrentParent("root");
setRows(clientProfile.getRows());
setCols(clientProfile.getCols());
}
public String getCurrentParent() {
return currentParent;
}
public StackPane getFolderBackButton() throws SevereException
{
StackPane stackPane = new StackPane();
stackPane.getStyleClass().add("action_box");
stackPane.getStyleClass().add("action_box_valid");
stackPane.setPrefSize(
Config.getInstance().getActionGridActionSize(),
Config.getInstance().getActionGridActionSize()
);
FontIcon fontIcon = new FontIcon("fas-chevron-left");
fontIcon.setIconSize(Config.getInstance().getActionGridActionSize() - 30);
stackPane.setAlignment(Pos.CENTER);
stackPane.getChildren().add(fontIcon);
stackPane.setOnMouseClicked(e->returnToPreviousParent());
return stackPane;
}
public void renderGrid() throws SevereException {
clear();
actionsGridPane.setHgap(Config.getInstance().getActionGridActionGap());
actionsGridPane.setVgap(Config.getInstance().getActionGridActionGap());
boolean isFolder = false;
if(!getCurrentParent().equals("root"))
{
isFolder = true;
actionsGridPane.add(getFolderBackButton(), 0,0);
}
for(int row = 0; row<rows; row++)
{
for(int col = 0; col<cols; col++)
{
if(row == 0 && col == 0 && isFolder)
continue;
ActionBox actionBox = new ActionBox(Config.getInstance().getActionGridActionSize(), actionDetailsPaneListener, this);
actionBox.setStreamPiParent(currentParent);
actionBox.setRow(row);
actionBox.setCol(col);
actionsGridPane.add(actionBox, row, col);
}
}
}
public void renderActions()
{
StringBuilder errors = new StringBuilder();
for(String action1x : getClientProfile().getActionsKeySet())
{
Action eachAction = getClientProfile().getActionByID(action1x);
logger.info("action ID : "+eachAction.getID()+
"\nInvalid : "+eachAction.isInvalid());
try {
renderAction(eachAction);
}
catch (SevereException e)
{
exceptionAndAlertHandler.handleSevereException(e);
}
catch (MinorException e)
{
errors.append("*").append(e.getShortMessage()).append("\n");
}
}
if(!errors.toString().isEmpty())
{
exceptionAndAlertHandler.handleMinorException(new MinorException("Error while rendering following actions", errors.toString()));
}
}
public void clear()
{
actionsGridPane.getChildren().clear();
}
private Logger logger;
public void renderAction(Action action) throws SevereException, MinorException
{
if(!action.getParent().equals(currentParent))
{
logger.info("Skipping action "+action.getID()+", not current parent!");
return;
}
if(action.getLocation().getRow()==-1)
{
logger.info("action has -1 rowIndex. Probably Combine action. Skipping ...");
return;
}
if(action.getLocation().getRow() >= rows || action.getLocation().getCol() >= cols)
{
throw new MinorException("action "+action.getDisplayText()+" ("+action.getID()+") falls outside bounds.\n" +
" Consider increasing rows/cols from client settings and relocating/deleting it.");
}
ActionBox actionBox = new ActionBox(Config.getInstance().getActionGridActionSize(), action, actionDetailsPaneListener, exceptionAndAlertHandler, this);
Location location = action.getLocation();
actionBox.setStreamPiParent(currentParent);
actionBox.setRow(location.getRow());
actionBox.setCol(location.getCol());
for(Node node : actionsGridPane.getChildren())
{
if(GridPane.getColumnIndex(node) == location.getRow() &&
GridPane.getRowIndex(node) == location.getCol())
{
actionsGridPane.getChildren().remove(node);
break;
}
}
System.out.println(location.getCol()+","+location.getRow());
actionsGridPane.add(actionBox, location.getRow(), location.getCol());
}
public void setRows(int rows)
{
this.rows = rows;
}
public void setCols(int cols)
{
this.cols = cols;
}
public int getRows()
{
return rows;
}
public int getCols()
{
return cols;
}
@Override
public void addActionToCurrentClientProfile(Action newAction) {
try {
getClientProfile().addAction(newAction);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
private String previousParent;
public void setPreviousParent(String previousParent) {
this.previousParent = previousParent;
}
public String getPreviousParent() {
return previousParent;
}
@Override
public void renderFolder(FolderAction action) {
setCurrentParent(action.getID());
setPreviousParent(action.getParent());
try {
renderGrid();
renderActions();
} catch (SevereException e) {
e.printStackTrace();
}
}
public void returnToPreviousParent()
{
setCurrentParent(getPreviousParent());
if(!getPreviousParent().equals("root"))
{
System.out.println("parent : "+getPreviousParent());
setPreviousParent(getClientProfile().getActionByID(
getPreviousParent()
).getParent());
}
try {
renderGrid();
renderActions();
} catch (SevereException e) {
e.printStackTrace();
}
}
}
A
src/main/java/com/stream_pi/server/window/dashboard/actiongridpane/ActionGridPaneListener.java
+10
−0
package com.stream_pi.server.window.dashboard.actiongridpane;
import com.stream_pi.actionapi.action.Action;
import com.stream_pi.actionapi.otheractions.FolderAction;
public interface ActionGridPaneListener {
void addActionToCurrentClientProfile(Action newAction);
void renderFolder(FolderAction action);
}
package com.stream_pi.server.window.firsttimeuse;
import com.stream_pi.server.connection.ServerListener;
import com.stream_pi.server.io.Config;
import com.stream_pi.server.window.ExceptionAndAlertHandler;
import com.stream_pi.util.exception.SevereException;
import com.stream_pi.util.uihelper.HBoxInputBox;
import com.stream_pi.util.uihelper.SpaceFiller;
import com.stream_pi.util.uihelper.SpaceFiller.FillerType;
import javafx.geometry.Pos;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class FinalConfigPane extends VBox
{
private TextField serverNicknameTextField;
private TextField serverPortTextField;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
private ServerListener serverListener;
public FinalConfigPane(ExceptionAndAlertHandler exceptionAndAlertHandler, ServerListener serverListener)
{
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
this.serverListener = serverListener;
getStyleClass().add("first_time_use_pane_final_config");
Label label = new Label("Thats it. Now just name your Stream-Pi Server, and the port where the server will run on!\n" +
"You can leave the default value of the port as it is.");
label.setWrapText(true);
label.getStyleClass().add("first_time_use_pane_final_config_label");
serverNicknameTextField = new TextField();
serverPortTextField = new TextField("2004");
HBoxInputBox serverNickNameInputBox = new HBoxInputBox("Server Nickname", serverNicknameTextField, 200);
HBoxInputBox serverPortInputBox = new HBoxInputBox("Server Port", serverPortTextField);
Button confirmButton = new Button("Confirm");
confirmButton.setOnAction(event -> onConfirmButtonClicked());
HBox bBar = new HBox(confirmButton);
bBar.setAlignment(Pos.CENTER_RIGHT);
getChildren().addAll(label, serverNickNameInputBox, serverPortInputBox, new SpaceFiller(FillerType.VBox), bBar);
setSpacing(10.0);
setVisible(false);
}
private void onConfirmButtonClicked()
{
StringBuilder errors = new StringBuilder();
String serverNameStr = serverNicknameTextField.getText();
String serverPortStr = serverPortTextField.getText();
if(serverNameStr.isBlank() || serverNameStr.isEmpty())
{
errors.append("* Server Name cannot be blank.\n");
}
int serverPort=-1;
try {
serverPort = Integer.parseInt(serverPortStr);
if (serverPort < 1024)
errors.append("* Server Port must be more than 1024");
}
catch (NumberFormatException e)
{
errors.append("* Server Port must be integer.\n");
}
if(errors.toString().isEmpty())
{
try
{
Config.getInstance().setServerName(serverNameStr);
Config.getInstance().setServerPort(serverPort);
Config.getInstance().setFirstTimeUse(false);
Config.getInstance().save();
serverListener.othInit();
((Stage) getScene().getWindow()).close();
}
catch(SevereException e)
{
exceptionAndAlertHandler.handleSevereException(e);
}
}
else
{
Alert alert = new Alert(AlertType.ERROR);
alert.setContentText("Please rectify the following errors and try again:\n"+errors.toString());
alert.show();
}
}
}
package com.stream_pi.server.window.firsttimeuse;
import com.stream_pi.server.Main;
import com.stream_pi.server.connection.ServerListener;
import com.stream_pi.server.window.ExceptionAndAlertHandler;
import com.stream_pi.util.uihelper.SpaceFiller;
import com.stream_pi.util.uihelper.SpaceFiller.FillerType;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
public class FirstTimeUse extends VBox{
public FirstTimeUse(ExceptionAndAlertHandler exceptionAndAlertHandler, ServerListener serverListener)
{
Font.loadFont(Main.class.getResourceAsStream("Roboto.ttf"), 13);
getStylesheets().add(Main.class.getResource("style.css").toExternalForm());
getStyleClass().add("first_time_use_pane");
setSpacing(10.0);
setPadding(new Insets(5));
headingLabel = new Label();
headingLabel.getStyleClass().add("first_time_use_pane_heading_label");
StackPane stackPane = new StackPane();
stackPane.getStyleClass().add("first_time_use_pane_stackpane");
VBox.setVgrow(stackPane, Priority.ALWAYS);
welcomePane = new WelcomePane();
licensePane = new LicensePane();
finalConfigPane = new FinalConfigPane(exceptionAndAlertHandler, serverListener);
stackPane.getChildren().addAll(
welcomePane,
licensePane,
finalConfigPane
);
nextButton = new Button("Next");
nextButton.setOnAction(event-> onNextButtonClicked());
previousButton = new Button("Previous");
previousButton.setOnAction(event-> onPreviousButtonClicked());
HBox buttonBar = new HBox(previousButton, new SpaceFiller(FillerType.HBox), nextButton);
buttonBar.setSpacing(10.0);
getChildren().addAll(headingLabel, stackPane, buttonBar);
setWindow(WindowName.WELCOME);
}
private Label headingLabel;
private Button nextButton;
private Button previousButton;
private WelcomePane welcomePane;
private LicensePane licensePane;
private FinalConfigPane finalConfigPane;
private WindowName windowName;
private void onNextButtonClicked()
{
if(windowName == WindowName.WELCOME)
{
setWindow(WindowName.LICENSE);
}
else if(windowName == WindowName.LICENSE)
{
setWindow(WindowName.FINAL);
}
}
private void onPreviousButtonClicked()
{
if(windowName == WindowName.FINAL)
{
setWindow(WindowName.LICENSE);
}
else if(windowName == WindowName.LICENSE)
{
setWindow(WindowName.WELCOME);
}
}
private void setWindow(WindowName windowName)
{
if (windowName == WindowName.WELCOME)
{
this.windowName = WindowName.WELCOME;
welcomePane.toFront();
welcomePane.setVisible(true);
licensePane.setVisible(false);
finalConfigPane.setVisible(false);
headingLabel.setText("");
nextButton.setDisable(false);
previousButton.setDisable(true);
}
else if (windowName == WindowName.LICENSE)
{
this.windowName = WindowName.LICENSE;
licensePane.toFront();
welcomePane.setVisible(false);
licensePane.setVisible(true);
finalConfigPane.setVisible(false);
headingLabel.setText("License Agreement");
nextButton.setDisable(false);
previousButton.setDisable(false);
}
else if (windowName == WindowName.FINAL)
{
this.windowName = WindowName.FINAL;
finalConfigPane.toFront();
welcomePane.setVisible(false);
licensePane.setVisible(false);
finalConfigPane.setVisible(true);
headingLabel.setText("Finishing up ...");
nextButton.setDisable(true);
previousButton.setDisable(false);
}
}
}
package com.stream_pi.server.window.firsttimeuse;
import com.stream_pi.server.info.License;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
public class LicensePane extends VBox {
public LicensePane()
{
getStyleClass().add("first_time_use_pane_license");
Label label = new Label("By Clicking on 'Next', you agree with the license of StreamPi, and the license(s) of the library(s) used/included with this software.");
label.prefWidthProperty().bind(widthProperty());
label.setWrapText(true);
TextArea licenseTextArea = new TextArea(License.getLicense());
licenseTextArea.setWrapText(false);
licenseTextArea.setEditable(false);
licenseTextArea.prefWidthProperty().bind(widthProperty());
VBox.setVgrow(licenseTextArea, Priority.ALWAYS);
getChildren().addAll(label, licenseTextArea);
}
}
package com.stream_pi.server.window.firsttimeuse;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
public class WelcomePane extends VBox{
public WelcomePane()
{
getStyleClass().add("first_time_use_pane_welcome");
Label welcomeLabel = new Label("Welcome!");
welcomeLabel.getStyleClass().add("first_time_use_welcome_pane_welcome_label");
Label nextToContinue = new Label("Click on Next to continue with the setup.");
nextToContinue.getStyleClass().add("first_time_use_welcome_pane_next_to_continue_label");
setAlignment(Pos.CENTER);
setSpacing(5.0);
getChildren().addAll(welcomeLabel, nextToContinue);
setVisible(false);
}
}
package com.stream_pi.server.window.firsttimeuse;
public enum WindowName {
WELCOME, LICENSE, FINAL
}
package com.stream_pi.server.window.settings;
import com.stream_pi.actionapi.ActionAPI;
import com.stream_pi.server.info.License;
import com.stream_pi.server.info.ServerInfo;
import com.stream_pi.server.Main;
import javafx.application.HostServices;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
public class About extends VBox{
private HostServices hostServices;
public About(HostServices hostServices)
{
getStyleClass().add("about");
this.hostServices = hostServices;
setAlignment(Pos.TOP_CENTER);
Image appIcon = new Image(Main.class.getResourceAsStream("app_icon.png"));
ImageView appIconImageView = new ImageView(appIcon);
appIconImageView.setFitHeight(196);
appIconImageView.setFitWidth(182);
Label licenseLabel = new Label("License");
licenseLabel.getStyleClass().add("settings_about_license_label");
VBox.setMargin(licenseLabel, new Insets(20, 0 , 10 ,0));
TextArea licenseTextArea = new TextArea(License.getLicense());
licenseTextArea.setWrapText(false);
licenseTextArea.setEditable(false);
licenseTextArea.setMaxWidth(550);
VBox.setVgrow(licenseTextArea, Priority.ALWAYS);
HBox links = new HBox();
Hyperlink github = new Hyperlink("GitHub");
github.setOnAction(event -> {
openWebpage("https://github.com/Stream-Pi");
});
Hyperlink discord = new Hyperlink("Discord");
discord.setOnAction(event -> {
openWebpage("https://discord.gg/BExqGmk");
});
Hyperlink website = new Hyperlink("Website");
website.setOnAction(event -> {
openWebpage("https://stream-pi.com");
});
Hyperlink twitter = new Hyperlink("Twitter");
twitter.setOnAction(event -> {
openWebpage("https://twitter.com/Stream_Pi");
});
Hyperlink matrix = new Hyperlink("Matrix");
matrix.setOnAction(event -> {
openWebpage("https://matrix.to/#/#stream-pi_general:matrix.org");
});
links.setSpacing(30);
links.setAlignment(Pos.CENTER);
links.getChildren().addAll(github, matrix, discord, website, twitter);
Hyperlink donateButton = new Hyperlink("DONATE");
donateButton.setOnAction(event -> {
openWebpage("https://www.patreon.com/streampi");
});
donateButton.getStyleClass().add("settings_about_donate_hyperlink");
ServerInfo serverInfo = ServerInfo.getInstance();
Label versionText = new Label(serverInfo.getVersion().getText() + " - "+ serverInfo.getPlatformType().getUIName() + " - "+ serverInfo.getReleaseStatus().getUIName());
Label commStandardLabel = new Label("Comm Standard "+serverInfo.getCommStandardVersion().getText());
Label minThemeAPILabel = new Label("Min ThemeAPI "+serverInfo.getMinThemeSupportVersion().getText());
Label minActionAPILabel = new Label("Min ActionAPI "+serverInfo.getMinPluginSupportVersion().getText());
Label currentActionAPILabel = new Label("ActionAPI "+ ActionAPI.API_VERSION.getText());
setSpacing(3);
getChildren().addAll(appIconImageView, licenseLabel, licenseTextArea, links, donateButton, versionText, commStandardLabel, minThemeAPILabel, minActionAPILabel, currentActionAPILabel);
}
public void openWebpage(String url) {
hostServices.showDocument(url);
}
}
package com.stream_pi.server.window.settings;
import com.stream_pi.server.client.Client;
import com.stream_pi.server.client.ClientProfile;
import com.stream_pi.server.client.ClientTheme;
import com.stream_pi.server.connection.ClientConnection;
import com.stream_pi.server.connection.ClientConnections;
import com.stream_pi.server.connection.ServerListener;
import com.stream_pi.server.window.ExceptionAndAlertHandler;
import com.stream_pi.util.exception.MinorException;
import com.stream_pi.util.exception.SevereException;
import com.stream_pi.util.uihelper.HBoxInputBox;
import com.stream_pi.util.uihelper.SpaceFiller;
import com.stream_pi.util.platform.ReleaseStatus;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.CacheHint;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.util.Callback;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
public class ClientsSettings extends VBox {
private VBox clientsSettingsVBox;
private Button saveButton;
private ServerListener serverListener;
private Logger logger;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
public ClientsSettings(ExceptionAndAlertHandler exceptionAndAlertHandler, ServerListener serverListener)
{
getStyleClass().add("clients_settings");
this.serverListener = serverListener;
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
clientSettingsVBoxArrayList = new ArrayList<>();
setPadding(new Insets(10.0));
logger = Logger.getLogger(ClientsSettings.class.getName());
clientsSettingsVBox = new VBox();
clientsSettingsVBox.setSpacing(20.0);
clientsSettingsVBox.setAlignment(Pos.TOP_CENTER);
setAlignment(Pos.TOP_CENTER);
ScrollPane scrollPane = new ScrollPane();
scrollPane.getStyleClass().add("clients_settings_scroll_pane");
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
VBox.setVgrow(scrollPane, Priority.ALWAYS);
scrollPane.maxWidthProperty().bind(widthProperty().multiply(0.8));
clientsSettingsVBox.prefWidthProperty().bind(scrollPane.widthProperty().subtract(25));
scrollPane.setContent(clientsSettingsVBox);
saveButton = new Button("Save");
saveButton.setOnAction(event -> onSaveButtonClicked());
HBox hBox = new HBox(saveButton);
hBox.setAlignment(Pos.CENTER_RIGHT);
getChildren().addAll(scrollPane, hBox);
setCache(true);
setCacheHint(CacheHint.SPEED);
}
public void onSaveButtonClicked()
{
new Thread(new Task<Void>() {
@Override
protected Void call()
{
try
{
Platform.runLater(()->saveButton.setDisable(true));
StringBuilder finalErrors = new StringBuilder();
for(ClientSettingsVBox clientSettingsVBox : clientSettingsVBoxArrayList)
{
StringBuilder errors = new StringBuilder();
if(clientSettingsVBox.getNickname().isBlank())
errors.append(" Cannot have blank nickname. \n");
try {
Double.parseDouble(clientSettingsVBox.getStartupWindowHeight());
}
catch (NumberFormatException e)
{
errors.append(" Must have integer display height. \n");
}
try {
Double.parseDouble(clientSettingsVBox.getStartupWindowWidth());
}
catch (NumberFormatException e)
{
errors.append(" Must have integer display width. \n");
}
for(ClientProfileVBox clientProfileVBox : clientSettingsVBox.getClientProfileVBoxes())
{
StringBuilder errors2 = new StringBuilder();
if(clientProfileVBox.getName().isBlank())
errors2.append(" cannot have blank nickname. \n");
try {
Integer.parseInt(clientProfileVBox.getActionSize());
}
catch (NumberFormatException e)
{
errors2.append(" Must have integer action Size. \n");
}
try {
Integer.parseInt(clientProfileVBox.getActionGap());
}
catch (NumberFormatException e)
{
errors2.append(" Must have integer action Gap. \n");
}
try {
int rows = Integer.parseInt(clientProfileVBox.getRows());
int actionsSize = Integer.parseInt(clientProfileVBox.getActionSize());
double startupWidth = Double.parseDouble(clientSettingsVBox.getStartupWindowWidth());
if((rows*actionsSize) > (startupWidth - 25) && clientSettingsVBox.getPlatform()!= com.stream_pi.util.platform.Platform.ANDROID)
{
errors2.append(" Rows out of bounds of screen size. \n"+startupWidth);
}
}
catch (NumberFormatException e)
{
errors2.append(" Must have integer Rows. \n");
}
try {
int cols = Integer.parseInt(clientProfileVBox.getCols());
int actionsSize = Integer.parseInt(clientProfileVBox.getActionSize());
double startupHeight = Double.parseDouble(clientSettingsVBox.getStartupWindowHeight());
if((cols*actionsSize) > (startupHeight - 25) && clientSettingsVBox.getPlatform()!= com.stream_pi.util.platform.Platform.ANDROID)
{
errors2.append(" Cols out of bounds of screen size. \n"+startupHeight);
}
}
catch (NumberFormatException e)
{
errors2.append(" Must have integer Columns. \n");
}
if(!errors2.toString().isEmpty())
{
errors.append(" ")
.append(clientProfileVBox.getRealName())
.append("\n")
.append(errors2.toString())
.append("\n");
}
}
if(!errors.toString().isEmpty())
{
finalErrors.append("* ")
.append(clientSettingsVBox.getRealNickName())
.append("\n")
.append(errors.toString())
.append("\n");
}
}
if(!finalErrors.toString().isEmpty())
throw new MinorException("You made form mistakes",
"Please fix the following issues : \n"+finalErrors.toString());
//save details and values
for(ClientSettingsVBox clientSettingsVBox : clientSettingsVBoxArrayList)
{
clientSettingsVBox.saveClientAndProfileDetails();
}
loadData();
serverListener.clearTemp();
}
catch (MinorException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleMinorException(e);
}
catch (SevereException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleSevereException(e);
}
catch (CloneNotSupportedException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleSevereException(new SevereException(
e.getMessage()
));
}
finally
{
Platform.runLater(()->saveButton.setDisable(false));
}
return null;
}
}).start();
}
private ArrayList<ClientSettingsVBox> clientSettingsVBoxArrayList;
public void loadData()
{
logger.info("Loading client data into ClientsSettings ...");
Platform.runLater(()-> clientsSettingsVBox.getChildren().clear());
clientSettingsVBoxArrayList.clear();
List<ClientConnection> clientConnections = ClientConnections.getInstance().getConnections();
if(clientConnections.size() == 0)
{
Platform.runLater(()->{
clientsSettingsVBox.getChildren().add(new Label("No Clients Connected."));
saveButton.setVisible(false);
});
}
else
{
Platform.runLater(()->saveButton.setVisible(true));
for (ClientConnection clientConnection : clientConnections) {
ClientSettingsVBox clientSettingsVBox = new ClientSettingsVBox(clientConnection);
clientSettingsVBoxArrayList.add(clientSettingsVBox);
Platform.runLater(()->clientsSettingsVBox.getChildren().add(clientSettingsVBox));
}
}
logger.info("... Done!");
}
public class ClientSettingsVBox extends VBox
{
private ComboBox<ClientProfile> profilesComboBox;
private ComboBox<ClientTheme> themesComboBox;
private TextField startupWindowHeightTextField;
public String getStartupWindowHeight() {
return startupWindowHeightTextField.getText();
}
private TextField startupWindowWidthTextField;
public String getStartupWindowWidth() {
return startupWindowWidthTextField.getText();
}
private TextField nicknameTextField;
public String getNickname() {
return nicknameTextField.getText();
}
private Label nickNameLabel;
private Label versionLabel;
public String getRealNickName()
{
return nickNameLabel.getText();
}
private com.stream_pi.util.platform.Platform platform;
public com.stream_pi.util.platform.Platform getPlatform() {
return platform;
}
private Label socketConnectionLabel;
private ClientConnection connection;
private Accordion profilesAccordion;
private ArrayList<ClientProfileVBox> clientProfileVBoxes;
private Label platformLabel;
private HBoxInputBox startupWindowHeightInputBox, startupWindowWidthInputBox;
public ArrayList<ClientProfileVBox> getClientProfileVBoxes() {
return clientProfileVBoxes;
}
public ClientSettingsVBox(ClientConnection connection)
{
this.connection = connection;
this.platform = connection.getClient().getPlatform();
clientProfileVBoxes = new ArrayList<>();
initUI();
loadValues();
}
public ClientConnection getConnection()
{
return connection;
}
public void saveClientAndProfileDetails() throws SevereException, CloneNotSupportedException, MinorException {
System.out.println("IIN");
getConnection().saveClientDetails(
nicknameTextField.getText(),
startupWindowWidthTextField.getText(),
startupWindowHeightTextField.getText(),
profilesComboBox.getSelectionModel().getSelectedItem().getID(),
themesComboBox.getSelectionModel().getSelectedItem().getThemeFullName()
);
System.out.println("OUT");
logger.info("Profiles : ");
for(ClientProfileVBox clientProfileVBox : clientProfileVBoxes)
{
logger.info("Name : "+clientProfileVBox.getClientProfile().getName());
getConnection().saveProfileDetails(clientProfileVBox.getClientProfile());
}
//remove deleted client profiles
for(ClientProfile clientProfile : connection.getClient().getAllClientProfiles())
{
boolean found = false;
for(ClientProfileVBox clientProfileVBox : clientProfileVBoxes)
{
if(clientProfileVBox.getClientProfile().getID().equals(clientProfile.getID()))
{
found = true;
break;
}
}
if(!found)
{
connection.getClient().removeProfileFromID(clientProfile.getID());
connection.deleteProfile(clientProfile.getID());
}
}
}
public void initUI()
{
profilesComboBox = new ComboBox<>();
Callback<ListView<ClientProfile>, ListCell<ClientProfile>> profilesComboBoxFactory = new Callback<>() {
@Override
public ListCell<ClientProfile> call(ListView<ClientProfile> clientConnectionListView) {
return new ListCell<>() {
@Override
protected void updateItem(ClientProfile clientProfile, boolean b) {
super.updateItem(clientProfile, b);
if(clientProfile == null)
{
setText(null);
}
else
{
setText(clientProfile.getName());
}
}
};
}
};
profilesComboBox.setCellFactory(profilesComboBoxFactory);
profilesComboBox.setButtonCell(profilesComboBoxFactory.call(null));
themesComboBox = new ComboBox<>();
Callback<ListView<ClientTheme>, ListCell<ClientTheme>> themesComboBoxFactory = new Callback<>() {
@Override
public ListCell<ClientTheme> call(ListView<ClientTheme> clientConnectionListView) {
return new ListCell<>() {
@Override
protected void updateItem(ClientTheme clientTheme, boolean b) {
super.updateItem(clientTheme, b);
if(clientTheme == null)
{
setText(null);
}
else
{
setText(clientTheme.getShortName());
}
}
};
}
};
themesComboBox.setCellFactory(themesComboBoxFactory);
themesComboBox.setButtonCell(themesComboBoxFactory.call(null));
startupWindowHeightTextField = new TextField();
startupWindowWidthTextField = new TextField();
platformLabel = new Label();
platformLabel.getStyleClass().add("settings_client_platform_label");
socketConnectionLabel = new Label();
socketConnectionLabel.getStyleClass().add("settings_client_socket_connection_label");
nicknameTextField = new TextField();
nickNameLabel = new Label();
nickNameLabel.getStyleClass().add("settings_client_nick_name_label");
versionLabel = new Label();
versionLabel.getStyleClass().add("settings_client_version_label");
profilesAccordion = new Accordion();
VBox.setMargin(profilesAccordion, new Insets(0,0,20,0));
Button addNewProfileButton = new Button("Add new Profile");
addNewProfileButton.setOnAction(event -> onNewProfileButtonClicked());
setSpacing(10.0);
getStyleClass().add("settings_clients_each_client");
startupWindowHeightInputBox = new HBoxInputBox("Startup window Height", startupWindowHeightTextField);
startupWindowHeightInputBox.managedProperty().bind(startupWindowHeightInputBox.visibleProperty());
startupWindowWidthInputBox = new HBoxInputBox("Startup window Width", startupWindowWidthTextField);
startupWindowWidthInputBox.managedProperty().bind(startupWindowWidthInputBox.visibleProperty());
this.getChildren().addAll(
nickNameLabel,
socketConnectionLabel,
platformLabel,
versionLabel,
new HBoxInputBox("Nickname",nicknameTextField),
new HBox(
new Label("Theme"),
new SpaceFiller(SpaceFiller.FillerType.HBox),
themesComboBox
),
startupWindowHeightInputBox,
startupWindowWidthInputBox,
new HBox(new Label("Startup Profile"),
new SpaceFiller(SpaceFiller.FillerType.HBox),
profilesComboBox),
addNewProfileButton,
profilesAccordion);
}
public void loadValues()
{
Client client = connection.getClient();
profilesComboBox.setItems(FXCollections.observableList(client.getAllClientProfiles()));
profilesComboBox.getSelectionModel().select(
client.getProfileByID(client.getDefaultProfileID())
);
themesComboBox.setItems(FXCollections.observableList(client.getThemes()));
themesComboBox.getSelectionModel().select(
client.getThemeByFullName(
client.getDefaultThemeFullName()
)
);
nicknameTextField.setText(client.getNickName());
if(client.getPlatform() == com.stream_pi.util.platform.Platform.ANDROID)
{
startupWindowHeightInputBox.setVisible(false);
startupWindowWidthInputBox.setVisible(false);
}
platformLabel.setText("Platform : "+client.getPlatform().getUIName());
startupWindowWidthTextField.setText(client.getStartupDisplayWidth()+"");
startupWindowHeightTextField.setText(client.getStartupDisplayHeight()+"");
socketConnectionLabel.setText(client.getRemoteSocketAddress().toString().substring(1)); //substring removes the `/`
nickNameLabel.setText(client.getNickName());
versionLabel.setText(client.getReleaseStatus().getUIName()+" "+client.getVersion().getText());
//add profiles
for(ClientProfile clientProfile : client.getAllClientProfiles())
{
TitledPane titledPane = new TitledPane();
titledPane.setText(clientProfile.getName());
ClientProfileVBox clientProfileVBox = new ClientProfileVBox(clientProfile);
clientProfileVBox.getRemoveButton().setOnAction(event -> onProfileDeleteButtonClicked(clientProfileVBox, titledPane));
titledPane.setContent(clientProfileVBox);
clientProfileVBoxes.add(clientProfileVBox);
profilesAccordion.getPanes().add(titledPane);
}
}
public void onNewProfileButtonClicked()
{
ClientProfile clientProfile = new ClientProfile(
"Untitled Profile",
3,
3,
100,
5
);
ClientProfileVBox clientProfileVBox = new ClientProfileVBox(clientProfile);
TitledPane titledPane = new TitledPane();
titledPane.setContent(clientProfileVBox);
titledPane.setText(clientProfile.getName());
clientProfileVBox.getRemoveButton().setOnAction(event -> onProfileDeleteButtonClicked(clientProfileVBox, titledPane));
clientProfileVBoxes.add(clientProfileVBox);
profilesAccordion.getPanes().add(titledPane);
}
public void onProfileDeleteButtonClicked(ClientProfileVBox clientProfileVBox, TitledPane titledPane)
{
if(clientProfileVBoxes.size() == 1)
{
exceptionAndAlertHandler.handleMinorException(new MinorException("Only one",
"You cannot delete all profiles"));
}
else
{
if(profilesComboBox.getSelectionModel().getSelectedItem().getID().equals(clientProfileVBox.getClientProfile().getID()))
{
exceptionAndAlertHandler.handleMinorException(new MinorException("Default",
"You cannot delete default profile. Change to another one to delete this."));
}
else
{
clientProfileVBoxes.remove(clientProfileVBox);
profilesComboBox.getItems().remove(clientProfileVBox.getClientProfile());
profilesAccordion.getPanes().remove(titledPane);
}
}
}
}
public class ClientProfileVBox extends VBox
{
private TextField nameTextField;
public String getName()
{
return nameTextField.getText();
}
private TextField rowsTextField;
public String getRows()
{
return rowsTextField.getText();
}
private TextField colsTextField;
public String getCols()
{
return colsTextField.getText();
}
private TextField actionSizeTextField;
public String getActionSize()
{
return actionSizeTextField.getText();
}
private TextField actionGapTextField;
public String getActionGap()
{
return actionGapTextField.getText();
}
private Button removeButton;
private ClientProfile clientProfile;
public String getRealName()
{
return clientProfile.getName();
}
public ClientProfileVBox(ClientProfile clientProfile)
{
this.clientProfile = clientProfile;
initUI();
loadValues(clientProfile);
}
public void initUI()
{
setPadding(new Insets(5.0));
setSpacing(10.0);
nameTextField = new TextField();
rowsTextField = new TextField();
colsTextField = new TextField();
actionSizeTextField = new TextField();
actionGapTextField = new TextField();
removeButton = new Button("Remove");
HBox hBox = new HBox(removeButton);
hBox.setAlignment(Pos.CENTER_RIGHT);
getChildren().addAll(
new HBoxInputBox("Name ", nameTextField),
new HBoxInputBox("Columns", rowsTextField),
new HBoxInputBox("Rows", colsTextField),
new HBoxInputBox("action Size", actionSizeTextField),
new HBoxInputBox("action Gap", actionGapTextField),
hBox
);
}
public Button getRemoveButton()
{
return removeButton;
}
public void loadValues(ClientProfile clientProfile)
{
nameTextField.setText(clientProfile.getName());
rowsTextField.setText(clientProfile.getRows()+"");
colsTextField.setText(clientProfile.getCols()+"");
actionSizeTextField.setText(clientProfile.getActionSize()+"");
actionGapTextField.setText(clientProfile.getActionGap()+"");
}
public ClientProfile getClientProfile()
{
clientProfile.setActionGap(Integer.parseInt(actionGapTextField.getText()));
clientProfile.setActionSize(Integer.parseInt(actionSizeTextField.getText()));
clientProfile.setRows(Integer.parseInt(rowsTextField.getText()));
clientProfile.setCols(Integer.parseInt(colsTextField.getText()));
clientProfile.setName(nameTextField.getText());
return clientProfile;
}
}
}
package com.stream_pi.server.window.settings;
import com.stream_pi.server.connection.ServerListener;
import com.stream_pi.server.io.Config;
import com.stream_pi.server.window.ExceptionAndAlertHandler;
import com.stream_pi.server.info.ServerInfo;
import com.stream_pi.util.alert.StreamPiAlert;
import com.stream_pi.util.alert.StreamPiAlertType;
import com.stream_pi.util.exception.MinorException;
import com.stream_pi.util.exception.SevereException;
import com.stream_pi.util.startatboot.SoftwareType;
import com.stream_pi.util.startatboot.StartAtBoot;
import com.stream_pi.util.version.Version;
import javafx.application.HostServices;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.*;
import javafx.stage.DirectoryChooser;
import org.json.JSONObject;
import org.kordamp.ikonli.javafx.FontIcon;
import java.awt.SystemTray;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.logging.Logger;
public class GeneralSettings extends ScrollPane {
private final TextField serverNameTextField;
private final TextField portTextField;
private final TextField pluginsPathTextField;
private final TextField themesPathTextField;
private final TextField actionGridPaneActionBoxSize;
private final TextField actionGridPaneActionBoxGap;
private final ToggleButton startOnBootToggleButton;
private final ToggleButton closeOnXToggleButton;
private final Button saveButton;
private final Button checkForUpdatesButton;
private Logger logger;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
private ServerListener serverListener;
private HostServices hostServices;
public GeneralSettings(ExceptionAndAlertHandler exceptionAndAlertHandler, ServerListener serverListener, HostServices hostServices)
{
this.hostServices = hostServices;
getStyleClass().add("general_settings_scroll_pane");
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
this.serverListener = serverListener;
logger = Logger.getLogger(GeneralSettings.class.getName());
setHbarPolicy(ScrollBarPolicy.NEVER);
serverNameTextField = new TextField();
portTextField = new TextField();
pluginsPathTextField = new TextField();
themesPathTextField = new TextField();
actionGridPaneActionBoxSize = new TextField();
actionGridPaneActionBoxGap = new TextField();
startOnBootToggleButton = new ToggleButton("Start on Boot");
closeOnXToggleButton = new ToggleButton("Quit On window Close");
checkForUpdatesButton = new Button("Check for updates");
checkForUpdatesButton.setOnAction(event->checkForUpdates());
VBox vBox = new VBox();
vBox.prefWidthProperty().bind(widthProperty());
vBox.setAlignment(Pos.CENTER);
vBox.setSpacing(5);
vBox.getChildren().addAll(
getUIInputBox("Server Name", serverNameTextField),
getUIInputBox("Port", portTextField),
getUIInputBox("action Grid Pane action Box Size", actionGridPaneActionBoxSize),
getUIInputBox("action Grid Pane action Box Gap", actionGridPaneActionBoxGap),
getUIInputBoxWithDirectoryChooser("Plugins Path", pluginsPathTextField),
getUIInputBoxWithDirectoryChooser("Themes Path", themesPathTextField)
);
serverNameTextField.setPrefWidth(200);
HBox toggleButtons = new HBox(closeOnXToggleButton, startOnBootToggleButton);
toggleButtons.setSpacing(10.0);
VBox.setMargin(toggleButtons, new Insets(30, 0 , 0,0));
toggleButtons.setAlignment(Pos.CENTER);
saveButton = new Button("Save");
saveButton.setOnAction(event->save());
vBox.getChildren().addAll(toggleButtons, checkForUpdatesButton, saveButton);
vBox.setPadding(new Insets(10));
vBox.getStyleClass().add("general_settings");
setContent(vBox);
}
private HBox getUIInputBoxWithDirectoryChooser(String labelText, TextField textField)
{
HBox hBox = getUIInputBox(labelText, textField);
hBox.setSpacing(5.0);
TextField tf = (TextField) hBox.getChildren().get(2);
tf.setPrefWidth(300);
tf.setDisable(true);
Button button = new Button();
FontIcon fontIcon = new FontIcon("far-folder");
button.setGraphic(fontIcon);
button.setOnAction(event -> {
DirectoryChooser directoryChooser = new DirectoryChooser();
try {
File selectedDirectory = directoryChooser.showDialog(getScene().getWindow());
textField.setText(selectedDirectory.getAbsolutePath());
}
catch (NullPointerException e)
{
logger.info("No folder selected");
}
});
hBox.getChildren().add(button);
return hBox;
}
private HBox getUIInputBox(String labelText, TextField textField)
{
textField.setPrefWidth(100);
Label label = new Label(labelText);
Region region = new Region();
HBox.setHgrow(region, Priority.ALWAYS);
return new HBox(label, region, textField);
}
public void loadDataFromConfig() throws SevereException {
Config config = Config.getInstance();
Platform.runLater(()->
{
serverNameTextField.setText(config.getServerName());
portTextField.setText(config.getPort()+"");
pluginsPathTextField.setText(config.getPluginsPath());
themesPathTextField.setText(config.getThemesPath());
actionGridPaneActionBoxSize.setText(config.getActionGridActionSize()+"");
actionGridPaneActionBoxGap.setText(config.getActionGridActionGap()+"");
closeOnXToggleButton.setSelected(config.getCloseOnX());
startOnBootToggleButton.setSelected(config.getStartOnBoot());
});
}
public void save()
{
new Thread(new Task<Void>() {
@Override
protected Void call()
{
try {
boolean toBeReloaded = false;
boolean dashToBeReRendered = false;
Platform.runLater(()->{
saveButton.setDisable(true);
serverNameTextField.setDisable(true);
portTextField.setDisable(true);
closeOnXToggleButton.setDisable(true);
startOnBootToggleButton.setDisable(true);
});
String serverNameStr = serverNameTextField.getText();
String serverPortStr = portTextField.getText();
String pluginsPathStr = pluginsPathTextField.getText();
String themesPathStr = themesPathTextField.getText();
String actionGridActionBoxSize = actionGridPaneActionBoxSize.getText();
String actionGridActionBoxGap = actionGridPaneActionBoxGap.getText();
boolean closeOnX = closeOnXToggleButton.isSelected();
boolean startOnBoot = startOnBootToggleButton.isSelected();
Config config = Config.getInstance();
StringBuilder errors = new StringBuilder();
if(serverNameStr.isBlank())
{
errors.append("* Server Name cannot be blank.\n");
}
else
{
if(!config.getServerName().equals(serverNameStr))
{
toBeReloaded = true;
}
}
int serverPort=-1;
try {
serverPort = Integer.parseInt(serverPortStr);
if (serverPort < 1024)
errors.append("* Server Port must be more than 1024");
if(config.getPort()!=serverPort)
{
toBeReloaded = true;
}
}
catch (NumberFormatException e)
{
errors.append("* Server Port must be integer.\n");
}
int actionSize=-1;
try {
actionSize = Integer.parseInt(actionGridActionBoxSize);
if(config.getActionGridActionSize() != actionSize)
{
dashToBeReRendered = true;
}
}
catch (NumberFormatException e)
{
errors.append("* action Size must be integer.\n");
}
int actionGap=-1;
try {
actionGap = Integer.parseInt(actionGridActionBoxGap);
if(config.getActionGridActionGap() != actionGap)
{
dashToBeReRendered = true;
}
}
catch (NumberFormatException e)
{
errors.append("* action Gap must be integer.\n");
}
if(pluginsPathStr.isBlank())
{
errors.append("* Plugins Path must not be blank.\n");
}
else
{
if(!config.getPluginsPath().equals(pluginsPathStr))
{
toBeReloaded = true;
}
}
if(themesPathStr.isBlank())
{
errors.append("* Themes Path must not be blank.\n");
}
else
{
if(!config.getThemesPath().equals(themesPathStr))
{
toBeReloaded = true;
}
}
if(!errors.toString().isEmpty())
{
throw new MinorException("settings", "Please rectify the following errors and try again :\n"+errors.toString());
}
if(config.getStartOnBoot() != startOnBoot)
{
if(ServerInfo.getInstance().getRunnerFileName() == null)
{
new StreamPiAlert("Uh Oh", "No Runner File Name Specified as startup arguments. Cant set run at boot.", StreamPiAlertType.ERROR).show();
startOnBoot = false;
}
else
{
StartAtBoot startAtBoot = new StartAtBoot(SoftwareType.SERVER, ServerInfo.getInstance().getPlatformType());
if(startOnBoot)
{
startAtBoot.create(new File(ServerInfo.getInstance().getRunnerFileName()));
}
else
{
boolean result = startAtBoot.delete();
if(!result)
new StreamPiAlert("Uh Oh!", "Unable to delete starter file", StreamPiAlertType.ERROR).show();
}
}
}
if(!closeOnX)
{
System.out.println("XYZ");
if(!SystemTray.isSupported())
{
StreamPiAlert alert = new StreamPiAlert("Not Supported", "Tray System not supported on this platform ", StreamPiAlertType.ERROR);
alert.show();
closeOnX = true;
}
else
{
System.out.println("YES");
}
}
config.setServerName(serverNameStr);
config.setServerPort(serverPort);
config.setActionGridGap(actionGap);
config.setActionGridSize(actionSize);
config.setPluginsPath(pluginsPathStr);
config.setThemesPath(themesPathStr);
config.setCloseOnX(closeOnX);
config.setStartupOnBoot(startOnBoot);
config.save();
loadDataFromConfig();
if(toBeReloaded)
{
new StreamPiAlert("Restart","Restart to see changes", StreamPiAlertType.INFORMATION).show();
}
if(dashToBeReRendered)
{
serverListener.clearTemp();
}
}
catch (MinorException e)
{
exceptionAndAlertHandler.handleMinorException(e);
}
catch (SevereException e)
{
exceptionAndAlertHandler.handleSevereException(e);
}
finally {
Platform.runLater(()->{
saveButton.setDisable(false);
serverNameTextField.setDisable(false);
portTextField.setDisable(false);
closeOnXToggleButton.setDisable(false);
startOnBootToggleButton.setDisable(false);
});
}
return null;
}
}).start();
}
public void checkForUpdates()
{
new Thread(new Task<Void>()
{
@Override
protected Void call() throws Exception {
try
{
Platform.runLater(()->checkForUpdatesButton.setDisable(true));
String jsonRaw = readUrl("https://stream-pi.com/API/get_latest.php?TYPE=SERVER");
System.out.println(jsonRaw);
JSONObject jsonObject = new JSONObject(jsonRaw);
String latestVersionRaw = jsonObject.getString("Version");
String releasePage = jsonObject.getString("Release Page");
Version latestVersion = new Version(latestVersionRaw);
Version currentVersion = ServerInfo.getInstance().getVersion();
if(latestVersion.isBiggerThan(currentVersion))
{
VBox vBox = new VBox();
Hyperlink urlLabel = new Hyperlink(releasePage);
urlLabel.setOnAction(event->hostServices.showDocument(releasePage));
Label label = new Label(
"New Version "+latestVersionRaw+" Available.\n" +
"Current Version "+currentVersion.getText()+".\n"+
"Changelog and install instructions are included in the release page.\n" +
"It is recommended to update to ensure maximum stability and least bugs.");
label.setWrapText(true);
vBox.setSpacing(5);
vBox.getChildren().addAll(
urlLabel,
label
);
new StreamPiAlert("New Update Available!", StreamPiAlertType.INFORMATION, vBox).show();;
}
else
{
new StreamPiAlert("Up to Date", "Server is upto date. ("+currentVersion.getText()+")", StreamPiAlertType.INFORMATION).show();;
}
}
catch (Exception e)
{
e.printStackTrace();
new StreamPiAlert("Uh Oh", "Update Check Failed. API Error/Network issue.", StreamPiAlertType.WARNING).show();;
}
finally
{
Platform.runLater(()->checkForUpdatesButton.setDisable(false));
}
return null;
}
}).start();;
}
private String readUrl(String urlString) throws Exception {
BufferedReader reader = null;
try {
URL url = new URL(urlString);
reader = new BufferedReader(new InputStreamReader(url.openStream()));
StringBuffer buffer = new StringBuffer();
int read;
char[] chars = new char[1024];
while ((read = reader.read(chars)) != -1)
buffer.append(chars, 0, read);
return buffer.toString();
} finally {
if (reader != null)
reader.close();
}
}
}
package com.stream_pi.server.window.settings;
import com.stream_pi.server.uipropertybox.UIPropertyBox;
import com.stream_pi.actionapi.actionproperty.property.ControlType;
import com.stream_pi.actionapi.actionproperty.property.Property;
import com.stream_pi.actionapi.actionproperty.property.Type;
import com.stream_pi.actionapi.normalaction.NormalAction;
import com.stream_pi.server.action.NormalActionPlugins;
import com.stream_pi.server.connection.ServerListener;
import com.stream_pi.server.window.ExceptionAndAlertHandler;
import com.stream_pi.util.exception.MinorException;
import com.stream_pi.util.uihelper.SpaceFiller;
import com.stream_pi.util.uihelper.SpaceFiller.FillerType;
import org.kordamp.ikonli.javafx.FontIcon;
import javafx.application.HostServices;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
public class PluginsSettings extends VBox {
private VBox pluginsSettingsVBox;
private ServerListener serverListener;
private Logger logger;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
private HostServices hostServices;
public PluginsSettings(ExceptionAndAlertHandler exceptionAndAlertHandler, HostServices hostServices)
{
getStyleClass().add("plugins_settings");
this.hostServices = hostServices;
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
pluginProperties = new ArrayList<>();
logger = Logger.getLogger(PluginsSettings.class.getName());
setPadding(new Insets(10));
pluginsSettingsVBox = new VBox();
pluginsSettingsVBox.setSpacing(10.0);
pluginsSettingsVBox.setAlignment(Pos.TOP_CENTER);
ScrollPane scrollPane = new ScrollPane();
scrollPane.getStyleClass().add("plugins_settings_scroll_pane");
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollPane.maxWidthProperty().bind(widthProperty().multiply(0.8));
pluginsSettingsVBox.prefWidthProperty().bind(scrollPane.widthProperty().subtract(25));
scrollPane.setContent(pluginsSettingsVBox);
VBox.setVgrow(scrollPane, Priority.ALWAYS);
setAlignment(Pos.TOP_CENTER);
saveButton = new Button("Save");
saveButton.setOnAction(event -> onSaveButtonClicked());
HBox hBox = new HBox(saveButton);
hBox.setAlignment(Pos.CENTER_RIGHT);
getChildren().addAll(scrollPane, hBox);
}
private Button saveButton;
public void onSaveButtonClicked()
{
try {
//form validation
StringBuilder finalErrors = new StringBuilder();
for (PluginProperties p : pluginProperties)
{
StringBuilder errors = new StringBuilder();
for(int j = 0; j < p.getServerPropertyUIBox().size(); j++)
{
UIPropertyBox serverProperty = p.getServerPropertyUIBox().get(j);
Node controlNode = serverProperty.getControlNode();
if (serverProperty.getControlType() == ControlType.TEXT_FIELD)
{
String value = ((TextField) controlNode).getText();
if(serverProperty.getType() == Type.INTEGER)
{
try
{
Integer.parseInt(value);
}
catch (NumberFormatException e)
{
errors.append(" -> ").append(serverProperty.getDisplayName()).append(" must be integer.\n");
}
}
else
{
if(value.isBlank() && !serverProperty.isCanBeBlank())
errors.append(" -> ").append(serverProperty.getDisplayName()).append(" cannot be blank.\n");
}
}
}
if(!errors.toString().isBlank())
{
finalErrors.append(" * ").append(p.getName()).append("\n").append(errors.toString()).append("\n");
}
}
if(!finalErrors.toString().isEmpty())
{
throw new MinorException("Form Validation Errors",
"Please rectify the following errors and try again \n"+finalErrors.toString());
}
//save
for (PluginProperties pp : pluginProperties) {
for (int j = 0; j < pp.getServerPropertyUIBox().size(); j++) {
UIPropertyBox serverProperty = pp.getServerPropertyUIBox().get(j);
String rawValue = serverProperty.getRawValue();
NormalActionPlugins.getInstance().getActionFromIndex(pp.getIndex())
.getServerProperties().get()
.get(serverProperty.getIndex()).setRawValue(rawValue);
}
}
NormalActionPlugins.getInstance().saveServerSettings();
NormalActionPlugins.getInstance().initPlugins();
}
catch (MinorException e)
{
e.printStackTrace();
exceptionAndAlertHandler.handleMinorException(e);
}
}
private ArrayList<PluginProperties> pluginProperties;
public void showPluginInitError()
{
Platform.runLater(()->{
pluginsSettingsVBox.getChildren().add(new Label("Plugin init error. Resolve issues and restart."));
saveButton.setVisible(false);
});
}
public void loadPlugins() throws MinorException {
pluginProperties.clear();
List<NormalAction> actions = NormalActionPlugins.getInstance().getPlugins();
System.out.println("asdasdasdasd"+actions.size());
Platform.runLater(()-> pluginsSettingsVBox.getChildren().clear());
if(actions.size() == 0)
{
Platform.runLater(()->{
pluginsSettingsVBox.getChildren().add(new Label("No Plugins Installed."));
saveButton.setVisible(false);
});
return;
}
else
{
Platform.runLater(()->saveButton.setVisible(true));
}
for(int i = 0; i<actions.size(); i++)
{
NormalAction action = actions.get(i);
if(!action.isVisibleInServerSettingsPane())
continue;
Label headingLabel = new Label(action.getName());
headingLabel.getStyleClass().add("settings_plugins_each_action_heading");
HBox headerHBox = new HBox(headingLabel);
if (action.getHelpLink()!=null)
{
Button helpButton = new Button();
FontIcon questionIcon = new FontIcon("fas-question");
questionIcon.getStyleClass().add("settings_plugins_plugin_help_icon");
helpButton.setGraphic(questionIcon);
helpButton.setOnAction(event -> hostServices.showDocument(action.getHelpLink()));
headerHBox.getChildren().addAll(new SpaceFiller(FillerType.HBox),helpButton);
}
Label authorLabel = new Label(action.getAuthor());
Label moduleLabel = new Label(action.getModuleName());
Label versionLabel = new Label("Version : "+action.getVersion().getText());
VBox serverPropertiesVBox = new VBox();
serverPropertiesVBox.setSpacing(10.0);
List<Property> serverProperties = action.getServerProperties().get();
ArrayList<UIPropertyBox> serverPropertyArrayList = new ArrayList<>();
for(int j =0; j<serverProperties.size(); j++)
{
Property eachProperty = serverProperties.get(j);
if(!eachProperty.isVisible())
continue;
Label label = new Label(eachProperty.getDisplayName());
Region region = new Region();
HBox.setHgrow(region, Priority.ALWAYS);
HBox hBox = new HBox(label, new SpaceFiller(SpaceFiller.FillerType.HBox));
//hBox.setId(j+"");
Node controlNode = null;
if(eachProperty.getControlType() == ControlType.COMBO_BOX)
{
ComboBox<String> comboBox = new ComboBox<>();
comboBox.getItems().addAll(eachProperty.getListValue());
comboBox.getSelectionModel().select(eachProperty.getSelectedIndex());
hBox.getChildren().add(comboBox);
controlNode = comboBox;
}
else if(eachProperty.getControlType() == ControlType.TEXT_FIELD)
{
TextField textField = new TextField(eachProperty.getRawValue());
hBox.getChildren().add(textField);
controlNode = textField;
}
else if(eachProperty.getControlType() == ControlType.TOGGLE)
{
ToggleButton toggleButton = new ToggleButton();
toggleButton.setSelected(eachProperty.getBoolValue());
if(eachProperty.getBoolValue())
toggleButton.setText("ON");
else
toggleButton.setText("OFF");
toggleButton.selectedProperty().addListener((observableValue, aBoolean, t1) -> {
if(t1)
toggleButton.setText("ON");
else
toggleButton.setText("OFF");
});
hBox.getChildren().add(toggleButton);
controlNode = toggleButton;
}
else if(eachProperty.getControlType() == ControlType.SLIDER_DOUBLE)
{
Slider slider = new Slider();
slider.setValue(eachProperty.getDoubleValue());
slider.setMax(eachProperty.getMaxDoubleValue());
slider.setMin(eachProperty.getMinDoubleValue());
hBox.getChildren().add(slider);
controlNode = slider;
}
else if(eachProperty.getControlType() == ControlType.SLIDER_INTEGER)
{
Slider slider = new Slider();
slider.setValue(eachProperty.getIntValue());
slider.setMax(eachProperty.getMaxIntValue());
slider.setMin(eachProperty.getMinIntValue());
slider.setBlockIncrement(1.0);
slider.setSnapToTicks(true);
hBox.getChildren().add(slider);
controlNode = slider;
}
UIPropertyBox serverProperty = new UIPropertyBox(j, eachProperty.getDisplayName(), controlNode, eachProperty.getControlType(), eachProperty.getType(), eachProperty.isCanBeBlank());
serverPropertyArrayList.add(serverProperty);
serverPropertiesVBox.getChildren().add(hBox);
}
PluginProperties pp = new PluginProperties(i, serverPropertyArrayList, action.getName());
pluginProperties.add(pp);
Region region1 = new Region();
region1.setPrefHeight(5);
Platform.runLater(()->{
VBox vBox = new VBox();
vBox.setSpacing(5.0);
vBox.getChildren().addAll(headerHBox, authorLabel, moduleLabel, versionLabel, serverPropertiesVBox);
if(action.getButtonBar()!=null)
vBox.getChildren().add(new HBox(new SpaceFiller(SpaceFiller.FillerType.HBox), action.getButtonBar()));
vBox.getChildren().add(region1);
//vBox.setId(i+"");
vBox.getStyleClass().add("settings_plugins_each_action");
pluginsSettingsVBox.getChildren().add(vBox);
});
}
}
public class PluginProperties
{
private int index;
private ArrayList<UIPropertyBox> serverProperty;
private String name;
public PluginProperties(int index, ArrayList<UIPropertyBox> serverProperty, String name)
{
this.index = index;
this.serverProperty = serverProperty;
this.name = name;
}
public String getName()
{
return name;
}
public int getIndex() {
return index;
}
public ArrayList<UIPropertyBox> getServerPropertyUIBox() {
return serverProperty;
}
}
}
package com.stream_pi.server.window.settings;
import com.stream_pi.server.connection.ServerListener;
import com.stream_pi.server.window.ExceptionAndAlertHandler;
import javafx.application.HostServices;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.CacheHint;
import javafx.scene.control.*;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
public class SettingsBase extends VBox {
private TabPane tabPane;
private GeneralSettings generalSettings;
private PluginsSettings pluginsSettings;
private ThemesSettings themesSettings;
private ClientsSettings clientsSettings;
private Button closeButton;
private HostServices hostServices;
private ExceptionAndAlertHandler exceptionAndAlertHandler;
public SettingsBase(HostServices hostServices, ExceptionAndAlertHandler exceptionAndAlertHandler,
ServerListener serverListener)
{
this.exceptionAndAlertHandler = exceptionAndAlertHandler;
this.hostServices = hostServices;
tabPane = new TabPane();
tabPane.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE);
VBox.setVgrow(tabPane, Priority.ALWAYS);
Tab generalSettingsTab = new Tab("General");
generalSettings = new GeneralSettings(exceptionAndAlertHandler, serverListener, hostServices);
generalSettingsTab.setContent(generalSettings);
Tab pluginsSettingsTab = new Tab("Plugins");
pluginsSettings = new PluginsSettings(exceptionAndAlertHandler, hostServices);
pluginsSettingsTab.setContent(pluginsSettings);
Tab themesSettingsTab = new Tab("Themes");
themesSettings = new ThemesSettings(hostServices);
themesSettingsTab.setContent(themesSettings);
Tab clientsSettingsTab = new Tab("Clients");
clientsSettings = new ClientsSettings(exceptionAndAlertHandler, serverListener);
clientsSettingsTab.setContent(clientsSettings);
Tab aboutTab = new Tab("About");
aboutTab.setContent(new About(hostServices));
tabPane.getTabs().addAll(generalSettingsTab, pluginsSettingsTab, themesSettingsTab, clientsSettingsTab, aboutTab);
setAlignment(Pos.TOP_RIGHT);
closeButton = new Button("Close");
VBox.setMargin(closeButton, new Insets(10.0));
getChildren().addAll(tabPane, closeButton);
setCache(true);
setCacheHint(CacheHint.SPEED);
}
public void setDefaultTabToGeneral()
{
tabPane.getSelectionModel().selectFirst();
}
public Button getCloseButton()
{
return closeButton;
}
public GeneralSettings getGeneralSettings()
{
return generalSettings;
}
public PluginsSettings getPluginsSettings()
{
return pluginsSettings;
}
public ThemesSettings getThemesSettings()
{
return themesSettings;
}
public ClientsSettings getClientsSettings()
{
return clientsSettings;
}
}
package com.stream_pi.server.window.settings;
import com.stream_pi.server.controller.Controller;
import com.stream_pi.server.io.Config;
import com.stream_pi.themeapi.Theme;
import com.stream_pi.themeapi.Themes;
import com.stream_pi.util.exception.SevereException;
import com.stream_pi.util.uihelper.SpaceFiller;
import javafx.application.HostServices;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.ArrayList;
import java.util.logging.Logger;
public class ThemesSettings extends VBox
{
private VBox themesSettingsVBox;
private Controller controller;
private Logger logger;
private HostServices hostServices;
public ThemesSettings(HostServices hostServices)
{
this.hostServices = hostServices;
getStyleClass().add("themes_settings");
logger = Logger.getLogger(ThemesSettings.class.getName());
setPadding(new Insets(10));
themesSettingsVBox = new VBox();
themesSettingsVBox.setSpacing(10.0);
themesSettingsVBox.setAlignment(Pos.TOP_CENTER);
ScrollPane scrollPane = new ScrollPane();
scrollPane.getStyleClass().add("themes_settings_scroll_pane");
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollPane.maxWidthProperty().bind(widthProperty().multiply(0.8));
themesSettingsVBox.prefWidthProperty().bind(scrollPane.widthProperty().subtract(10));
scrollPane.setContent(themesSettingsVBox);
VBox.setVgrow(scrollPane, Priority.ALWAYS);
setAlignment(Pos.TOP_CENTER);
getChildren().addAll(scrollPane);
toggleButtons = new ArrayList<>();
}
public void setController(Controller controller)
{
this.controller = controller;
}
private Themes themes;
private String currentThemeFullName;
public void setThemes(Themes themes)
{
this.themes = themes;
}
public void setCurrentThemeFullName(String currentThemeFullName)
{
this.currentThemeFullName = currentThemeFullName;
}
private ArrayList<ToggleButton> toggleButtons;
public void loadThemes()
{
toggleButtons.clear();
Platform.runLater(()-> themesSettingsVBox.getChildren().clear());
for(int i = 0; i<themes.getThemeList().size(); i++)
{
Theme theme = themes.getThemeList().get(i);
Label shortNameLabel = new Label(theme.getShortName());
shortNameLabel.getStyleClass().add("settings_themes_each_theme_heading");
Label authorLabel = new Label(theme.getAuthor());
Label fullNameLabel = new Label(theme.getFullName());
HBox topRowHBox = new HBox(shortNameLabel);
if(theme.getWebsite() != null)
{
Button helpButton = new Button();
FontIcon questionIcon = new FontIcon("fas-question");
questionIcon.getStyleClass().add("settings_themes_theme_help_icon");
helpButton.setGraphic(questionIcon);
helpButton.setOnAction(event -> hostServices.showDocument(theme.getWebsite()));
topRowHBox.getChildren().addAll(new SpaceFiller(SpaceFiller.FillerType.HBox), helpButton);
}
Label versionLabel = new Label("Version : "+theme.getVersion().getText());
ToggleButton toggleButton = new ToggleButton();
toggleButton.setSelected(theme.getFullName().equals(currentThemeFullName));
toggleButton.setId(theme.getFullName());
if(theme.getFullName().equals(currentThemeFullName))
{
toggleButton.setText("ON");
toggleButton.setSelected(true);
toggleButton.setDisable(true);
}
else
{
toggleButton.setText("OFF");
}
toggleButton.setOnAction(event -> {
ToggleButton toggleButton1 = (ToggleButton) event.getSource();
toggleButton.setText("ON");
try {
Config.getInstance().setCurrentThemeFullName(toggleButton1.getId());
Config.getInstance().save();
for(ToggleButton toggleButton2 : toggleButtons)
{
if(toggleButton2.getId().equals(Config.getInstance().getCurrentThemeFullName()))
{
toggleButton2.setDisable(true);
toggleButton2.setText("ON");
toggleButton2.setSelected(true);
}
else
{
toggleButton2.setDisable(false);
toggleButton2.setText("OFF");
toggleButton2.setSelected(false);
}
}
controller.initThemes();
}
catch (SevereException e)
{
controller.handleSevereException(e);
}
});
HBox hBox = new HBox(toggleButton);
Region region1 = new Region();
region1.setPrefHeight(5);
hBox.setAlignment(Pos.TOP_RIGHT);
VBox vBox = new VBox(topRowHBox, authorLabel, versionLabel, fullNameLabel, hBox, region1);
vBox.setSpacing(5.0);
vBox.getStyleClass().add("settings_themes_each_theme");
Platform.runLater(()->themesSettingsVBox.getChildren().add(vBox));
toggleButtons.add(toggleButton);
}
}
}
module com.StreamPi.Server {
module com.stream_pi.server {
uses com.StreamPi.ActionAPI.Action.Action;
uses com.stream_pi.actionapi.action.Action;
uses com.StreamPi.ActionAPI.NormalAction.NormalAction;
uses com.stream_pi.actionapi.normalaction.NormalAction;
requires com.StreamPi.ActionAPI;
requires com.stream_pi.actionapi;
requires com.StreamPi.Util;
requires com.stream_pi.util;
requires com.StreamPi.ThemeAPI;
requires com.stream_pi.themeapi;
requires org.kordamp.ikonli.javafx;
requires org.kordamp.ikonli.javafx;
requires java.xml;
requires java.xml;
requires javafx.base;
requires javafx.base;
requires javafx.graphics;
requires javafx.graphics;
requires javafx.controls;
requires javafx.controls;
requires javafx.media;
requires javafx.media;
requires java.desktop;
requires java.desktop;
requires java.sql;
requires java.sql;
requires org.json;
requires org.json;
exports com.StreamPi.Server;
exports com.stream_pi.server;
}
}
PK (R