diff --git a/lib/controlsfx-8.20.9-LICENSE.txt b/lib/controlsfx-8.20.9-LICENSE.txt
new file mode 100644
index 0000000..3985baa
--- /dev/null
+++ b/lib/controlsfx-8.20.9-LICENSE.txt
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2013, 2014, ControlsFX
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of ControlsFX, any associated website, nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
\ No newline at end of file
diff --git a/src/com/coder/client/CoderClient.java b/src/com/coder/client/CoderClient.java
index 57a9689..17a65a6 100644
--- a/src/com/coder/client/CoderClient.java
+++ b/src/com/coder/client/CoderClient.java
@@ -5,8 +5,6 @@ import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.swing.JOptionPane;
-
import com.coder.client.Session;
import com.coder.client.gui.editor.EditorWindow;
import com.coder.client.gui.editor.EditorWindowListener;
@@ -25,6 +23,9 @@ import com.coder.server.message.ProjectRspMsg;
import zutil.log.CompactLogFormatter;
import zutil.log.LogUtil;
+import zutil.net.ssdp.SSDPClient;
+import zutil.net.ssdp.SSDPClient.SSDPServiceListener;
+import zutil.net.ssdp.StandardSSDPInfo;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
@@ -45,7 +46,12 @@ public class CoderClient extends Application{
private String serverURL = null;
private int serverPort = CoderServer.SERVER_PORT;
private String username = null;
- private char[] password = null;
+ private char[] password = null; //should only be anything else than null if defined by program argument
+ private String loginErrorMessage = "";
+ private String project = null;
+
+ //services
+ SSDPClient ssdpClient;
public static void main(String[] args) {
Application.launch(args);
@@ -74,13 +80,15 @@ public class CoderClient extends Application{
}catch(NumberFormatException e){
logger.warning("port argument to program is not of a integer type. using default port.");
}
+ }else if(key.equals("project")){
+ this.project = value;
}
}
if(this.username == null){ //ignore the password if no username was set
this.password = null;
}
- //loading GUI
+ //setup GUI elements
this.mainStage = mainStage;
mainStage.setTitle("CoderClient");
try{
@@ -90,6 +98,15 @@ public class CoderClient extends Application{
setupEditWindow();
}catch(IOException e){
logger.log(Level.SEVERE, "could not load all GUI elements", e);
+ System.exit(1);
+ }
+
+ //setup SSDP client
+ try{
+ setupSSDPClient();
+ }catch(IOException e){
+ logger.log(Level.SEVERE, "could not setup SSDP client", e);
+ System.exit(1);
}
//start program logic
@@ -103,6 +120,13 @@ public class CoderClient extends Application{
@Override
public void willShow() {
closeCurrentSession();
+ ssdpClient.clearServices();
+ selectServerDialog.clearServerList();
+ if(ssdpClient != null){
+ ssdpClient.requestService("coder:discover");
+ }else{
+ logger.severe("could not send a SSDP request since the client is not setup");
+ }
selectServerDialog.setServerAddress(serverURL);
selectServerDialog.setServerPort(serverPort);
}
@@ -121,13 +145,13 @@ public class CoderClient extends Application{
if(session == null){
logger.warning("Could not setup a connection to " + serverURL + ":" + port);
serverURL = null;
+ serverPort = CoderServer.SERVER_PORT;
selectServerDialog.showOnStage(mainStage);
}else{
loginDialog.showOnStage(mainStage);
}
}
});
-
}
private void setupLoginDialog() throws IOException {
@@ -137,6 +161,7 @@ public class CoderClient extends Application{
public void willShow() {
loginDialog.setUsername(username);
loginDialog.setPassword(password);
+ loginDialog.setErrorMessage(loginErrorMessage);
}
@Override
public void cancel() {
@@ -151,14 +176,16 @@ public class CoderClient extends Application{
//authenticate session
boolean authenticated = session.authenticate(username, password);
if(!authenticated){
- JOptionPane.showMessageDialog(null, "Wrong username or password", "Authentication Failed", JOptionPane.INFORMATION_MESSAGE);
logger.severe("Authentication failed: wrong username or password");
password = null;
- loginDialog.showOnStage(mainStage);
+ loginErrorMessage = "Wrong username or password";
+ selectServerDialog.showOnStage(mainStage);
+ }else{
+ loginErrorMessage = "";
+ setupSessionListener(); //resister a message listener to the session
+ session.start(); //start receiving traffic from the server
+ selectProjectDialog.showOnStage(mainStage);
}
- setupSessionListener(); //resister a message listener to the session
- session.start(); //start receiving traffic from the server
- selectProjectDialog.showOnStage(mainStage);
}
private void setupSessionListener(){
session.addCoderMessageReceivedListener(new CoderMessageReceivedListener() {
@@ -187,17 +214,22 @@ public class CoderClient extends Application{
}
});
}
-
+
private void setupSelectProjectDialog() throws IOException {
this.selectProjectDialog = new SelectProjectDialog();
this.selectProjectDialog.addSelectProjectDialogListener(new SelectProjectDialogListener() {
@Override
public void willShow() {
selectProjectDialog.clearProjectList();
- sendProjectListReq();
+ if(project == null){
+ sendProjectListReq();
+ }else{
+ selectProjectDialog.setProject(project);
+ }
}
@Override
- public void openProject(String selectedProjectName) {
+ public void open(String selectedProjectName) {
+ project = selectedProjectName;
editorWindow.showOnStage(mainStage);
}
@Override
@@ -247,6 +279,25 @@ public class CoderClient extends Application{
});
}
+ private void setupSSDPClient() throws IOException{
+ this.ssdpClient = new SSDPClient();
+ ssdpClient.setListener(new SSDPServiceListener() {
+ @Override
+ public void newService(final StandardSSDPInfo service) {
+ if(selectServerDialog != null){
+ Platform.runLater(new Runnable() {
+ @Override
+ public void run() {
+ String ip = service.getInetAddress().getHostAddress();
+ selectServerDialog.addServerToList(ip);
+ }
+ });
+ }
+ }
+ });
+ ssdpClient.start();
+ }
+
private void closeCurrentSession(){
if(this.session != null){
logger.info("disconnecting from server");
diff --git a/src/com/coder/client/Session.java b/src/com/coder/client/Session.java
index 7059f5f..2d246d7 100644
--- a/src/com/coder/client/Session.java
+++ b/src/com/coder/client/Session.java
@@ -70,11 +70,11 @@ public class Session extends Thread {
try {
msg = in.readGenericObject();
} catch (IOException e) {
- //logger.log(Level.SEVERE, "socket is probably closed by the peer", e);
+ logger.log(Level.FINE, "socket was probably closed by the peer", e);
break;
}
if(msg == null){
- //logger.severe("socket is probably closed by the peer");
+ logger.fine("socket was probably closed by the peer");
break;
}else{
handleMessage(msg);
diff --git a/src/com/coder/client/gui/login/LoginDialog.fxml b/src/com/coder/client/gui/login/LoginDialog.fxml
index 4695360..f62f5d5 100644
--- a/src/com/coder/client/gui/login/LoginDialog.fxml
+++ b/src/com/coder/client/gui/login/LoginDialog.fxml
@@ -1,42 +1,33 @@
+
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
+
diff --git a/src/com/coder/client/gui/login/LoginDialog.java b/src/com/coder/client/gui/login/LoginDialog.java
index 318502d..68a859c 100644
--- a/src/com/coder/client/gui/login/LoginDialog.java
+++ b/src/com/coder/client/gui/login/LoginDialog.java
@@ -11,6 +11,7 @@ import zutil.log.LogUtil;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
+import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
@@ -26,6 +27,7 @@ public class LoginDialog extends GuiWindow {
@FXML private PasswordField passwordPasswordField;
@FXML private Button cancelButton;
@FXML private Button loginButton;
+ @FXML private Label errorLabel;
public LoginDialog() throws IOException {
super(LoginDialog.class.getResource("LoginDialog.fxml"));
@@ -34,7 +36,7 @@ public class LoginDialog extends GuiWindow {
@Override
public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
-
+ errorLabel.setText("");
}
@Override
@@ -42,11 +44,14 @@ public class LoginDialog extends GuiWindow {
for(LoginDialogListener listener : this.listeners){
listener.willShow();
}
- if(!usernameTextField.getText().isEmpty() && !passwordPasswordField.getText().isEmpty()){
+ if(usernameTextField.getText() != null && !usernameTextField.getText().isEmpty() && passwordPasswordField.getText() != null && !passwordPasswordField.getText().isEmpty()){
loginButton.fire();
- }
- if(!usernameTextField.getText().isEmpty()){
- passwordPasswordField.requestFocus();
+ }else{
+ if(errorLabel.getText() != null && errorLabel.getText().isEmpty() && usernameTextField.getText() != null && !usernameTextField.getText().isEmpty()){
+ passwordPasswordField.requestFocus();
+ }else{
+ usernameTextField.requestFocus();
+ }
}
}
@@ -96,4 +101,8 @@ public class LoginDialog extends GuiWindow {
return "Login";
}
+ public void setErrorMessage(String msg) {
+ errorLabel.setText(msg);
+ }
+
}
diff --git a/src/com/coder/client/gui/selectProject/ProjectListCell.fxml b/src/com/coder/client/gui/selectProject/ProjectListCell.fxml
new file mode 100644
index 0000000..b9200d1
--- /dev/null
+++ b/src/com/coder/client/gui/selectProject/ProjectListCell.fxml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/com/coder/client/gui/selectProject/ProjectListCell.java b/src/com/coder/client/gui/selectProject/ProjectListCell.java
new file mode 100644
index 0000000..e9b796b
--- /dev/null
+++ b/src/com/coder/client/gui/selectProject/ProjectListCell.java
@@ -0,0 +1,42 @@
+package com.coder.client.gui.selectProject;
+
+import java.io.IOException;
+
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.control.ListCell;
+
+class ProjectListCell extends ListCell {
+
+ @FXML private Label nameLabel;
+ @FXML private Label typeLabel;
+ @FXML private Label descriptionLabel;
+ private Node node;
+
+ public ProjectListCell() throws IOException{
+ FXMLLoader loader = new FXMLLoader(ProjectListCell.class.getResource("ProjectListCell.fxml"));
+ loader.setController(this);
+ this.node = (Node)loader.load();
+ }
+
+ @Override
+ protected void updateItem(ProjectListItem item, boolean empty){
+ super.updateItem(item, empty);
+ if(empty){
+ setText("");
+ setGraphic(null);
+ }else if(item != null){
+ setText("");
+ nameLabel.setText(item.getName());
+ typeLabel.setText(item.getType());
+ descriptionLabel.setText(item.getDescription());
+ setGraphic(node);
+ }else{
+ setText("NULL");
+ setGraphic(null);
+ }
+ }
+
+}
diff --git a/src/com/coder/client/gui/selectProject/ProjectListItem.java b/src/com/coder/client/gui/selectProject/ProjectListItem.java
index dfa5f78..b1b94e9 100644
--- a/src/com/coder/client/gui/selectProject/ProjectListItem.java
+++ b/src/com/coder/client/gui/selectProject/ProjectListItem.java
@@ -1,18 +1,30 @@
package com.coder.client.gui.selectProject;
-public class ProjectListItem {
- private String projectName;
+class ProjectListItem {
+ private String name;
private String type;
private String description;
public ProjectListItem(String projectName, String type, String description) {
- this.projectName = projectName;
+ this.name = projectName;
this.type = type;
this.description = description;
}
+ public String getName(){
+ return this.name;
+ }
+
+ public String getType(){
+ return this.type;
+ }
+
+ public String getDescription(){
+ return this.description;
+ }
+
public String toString(){
- return "ProjectName="+projectName+", type="+type+", description="+description;
+ return "PROJECT: ProjectName="+name+", type="+type+", description="+description;
}
}
diff --git a/src/com/coder/client/gui/selectProject/SelectProjectDialog.fxml b/src/com/coder/client/gui/selectProject/SelectProjectDialog.fxml
index e129cad..a71c0c2 100644
--- a/src/com/coder/client/gui/selectProject/SelectProjectDialog.fxml
+++ b/src/com/coder/client/gui/selectProject/SelectProjectDialog.fxml
@@ -6,12 +6,11 @@
-
-
+
@@ -30,7 +29,7 @@
-
diff --git a/src/com/coder/client/gui/selectServer/SelectServerDialog.java b/src/com/coder/client/gui/selectServer/SelectServerDialog.java
index 495a66e..1ec8ca4 100644
--- a/src/com/coder/client/gui/selectServer/SelectServerDialog.java
+++ b/src/com/coder/client/gui/selectServer/SelectServerDialog.java
@@ -4,11 +4,23 @@ import java.io.IOException;
import java.net.URL;
import java.util.HashSet;
import java.util.ResourceBundle;
+import java.util.logging.Level;
import java.util.logging.Logger;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
+import javafx.scene.control.ListCell;
+import javafx.scene.control.ListView;
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.input.MouseButton;
+import javafx.scene.input.MouseEvent;
+import javafx.util.Callback;
import zutil.log.LogUtil;
@@ -17,9 +29,12 @@ import com.coder.client.gui.GuiWindow;
public class SelectServerDialog extends GuiWindow {
public static final Logger logger = LogUtil.getLogger();
private HashSet listeners;
+ private ObservableList serverList;
+
private String address = null;
private int port = -1;
+ @FXML private ListView serverListView;
@FXML private Button exitButton;
@FXML private Button connectButton;
@@ -34,15 +49,60 @@ public class SelectServerDialog extends GuiWindow {
listener.willShow();
}
if(address != null && port != -1){
+ connectButton.setDisable(false);
connectButton.fire();
}
+ connectButton.setDisable(true);
}
@Override
public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
- // TODO Auto-generated method stub
-
+ serverList = FXCollections.observableArrayList();
+ serverListView.setItems(serverList);
+ serverListView.setCellFactory(new Callback, ListCell>(){
+ @Override
+ public ListCell call(ListView arg0) {
+ try {
+ return new ServerListCell();
+ } catch (IOException e) {
+ logger.log(Level.SEVERE, "could not crate a ServerListCell instance", e);
+ }
+ return null;
+ }
+ });
+ serverListView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
+ @Override
+ public void changed(ObservableValue extends ServerListItem> observable, ServerListItem oldValue, ServerListItem vewValue) {
+ if(observable.getValue() != null){
+ logger.fine("ServerListItem [" + observable.getValue() + "] selected in the server list");
+ address = observable.getValue().getIP();
+ connectButton.setDisable(false);
+ }else{
+ connectButton.setDisable(true);
+ }
+ }
+ });
}
+
+ @FXML
+ protected void mouseClickedOnList(MouseEvent event){
+ if(event.getButton().equals(MouseButton.PRIMARY)){
+ if(event.getClickCount() == 2){
+ connectButton.fire();
+ }
+ }
+ }
+
+ @FXML
+ protected void keyPressed(KeyEvent event) {
+ if(event.getCode() == KeyCode.ENTER){
+ logger.fine("User pressed the ENTER key");
+ connectButton.fire();
+ }else if(event.getCode() == KeyCode.ESCAPE){
+ logger.fine("User pressed the ESCAPE key");
+ exitButton.fire();
+ }
+ }
@FXML
protected void exit(ActionEvent event){
@@ -68,12 +128,22 @@ public class SelectServerDialog extends GuiWindow {
public void setServerPort(int serverPort) {
this.port = serverPort;
-
}
@Override
protected String getTitle() {
return "Select Server";
}
+
+ public void addServerToList(String ip) {
+ logger.fine("Adding server \"" + ip + "\" to server list");
+ ServerListItem item = new ServerListItem(ip);
+ this.serverList.add(item);
+ }
+
+ public void clearServerList(){
+ logger.fine("Clearing server list");
+ serverList.clear();
+ }
}
diff --git a/src/com/coder/client/gui/selectServer/ServerListCell.fxml b/src/com/coder/client/gui/selectServer/ServerListCell.fxml
new file mode 100644
index 0000000..932d624
--- /dev/null
+++ b/src/com/coder/client/gui/selectServer/ServerListCell.fxml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/com/coder/client/gui/selectServer/ServerListCell.java b/src/com/coder/client/gui/selectServer/ServerListCell.java
new file mode 100644
index 0000000..69b2c3b
--- /dev/null
+++ b/src/com/coder/client/gui/selectServer/ServerListCell.java
@@ -0,0 +1,37 @@
+package com.coder.client.gui.selectServer;
+
+import java.io.IOException;
+
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.control.ListCell;
+
+class ServerListCell extends ListCell {
+
+ @FXML private Label ipLabel;
+ private Node node;
+
+ public ServerListCell() throws IOException{
+ FXMLLoader loader = new FXMLLoader(ServerListCell.class.getResource("ServerListCell.fxml"));
+ loader.setController(this);
+ this.node = (Node)loader.load();
+ }
+
+ @Override
+ protected void updateItem(ServerListItem item, boolean empty){
+ super.updateItem(item, empty);
+ if(empty){
+ setText("");
+ setGraphic(null);
+ }else if(item != null){
+ setText("");
+ ipLabel.setText(item.getIP());
+ setGraphic(node);
+ }else{
+ setText("NULL");
+ setGraphic(null);
+ }
+ }
+}
diff --git a/src/com/coder/client/gui/selectServer/ServerListItem.java b/src/com/coder/client/gui/selectServer/ServerListItem.java
new file mode 100644
index 0000000..f1e7585
--- /dev/null
+++ b/src/com/coder/client/gui/selectServer/ServerListItem.java
@@ -0,0 +1,19 @@
+package com.coder.client.gui.selectServer;
+
+class ServerListItem {
+
+ private String ip = null;
+
+ public ServerListItem(String ip){
+ this.ip = ip;
+ }
+
+ public String getIP(){
+ return this.ip;
+ }
+
+ public String toString(){
+ return "SERVER: ip="+ip;
+ }
+
+}