diff --git a/.classpath b/.classpath index 0afa476..52645f5 100644 --- a/.classpath +++ b/.classpath @@ -1,8 +1,9 @@ - - + + + diff --git a/.externalToolBuilders/ANT Build.launch b/.externalToolBuilders/ANT Build.launch index 3f0811c..c689b92 100644 --- a/.externalToolBuilders/ANT Build.launch +++ b/.externalToolBuilders/ANT Build.launch @@ -5,6 +5,12 @@ + + + + + + diff --git a/lib/controlsfx-8.20.9.jar b/lib/controlsfx-8.20.9.jar new file mode 100644 index 0000000..babe34d Binary files /dev/null and b/lib/controlsfx-8.20.9.jar differ diff --git a/src/com/coder/client/CoderClient.java b/src/com/coder/client/CoderClient.java index 6af95f7..2763e40 100644 --- a/src/com/coder/client/CoderClient.java +++ b/src/com/coder/client/CoderClient.java @@ -7,10 +7,12 @@ import java.util.logging.Logger; import javax.swing.JOptionPane; -import com.coder.client.LoginDialog.LoginDialogAction; import com.coder.server.CoderServer; import com.coder.server.message.CoderMessage; +import com.coder.server.message.ConfigData; import com.coder.server.message.ProjectListData; +import com.coder.server.message.ProjectListReqMsg; +import com.coder.server.message.ProjectRspMsg; import zutil.log.CompactLogFormatter; import zutil.log.LogUtil; @@ -23,6 +25,7 @@ public class CoderClient extends Thread{ private int port; private Session session; private ProjectEditorWindow projectEditorWindow; + private ProjectSelectDialog projectSelectDialog; private String username = null; private char[] password = null; private int connectionRetriesLimit = DEFAULT_CONNECTION_RETRIES; //if zero, try forever @@ -32,6 +35,7 @@ public class CoderClient extends Thread{ this.port = port; this.projectEditorWindow = new ProjectEditorWindow("CoderClient"); + this.projectSelectDialog = new ProjectSelectDialog(null); LogUtil.setGlobalLevel(Level.INFO); LogUtil.setGlobalFormatter(new CompactLogFormatter()); @@ -79,7 +83,7 @@ public class CoderClient extends Thread{ LoginDialog loginDialog = new LoginDialog(username, null); loginDialog.setVisible(true); //blocking loginDialog.dispose(); - if(loginDialog.getAction() == LoginDialogAction.CANCEL){ + if(loginDialog.getAction() == LoginDialog.LoginDialogAction.CANCEL){ logger.fine("Login dialog closed or canceled by the user. terminating."); break; } @@ -106,13 +110,46 @@ public class CoderClient extends Thread{ session.addCoderMessageReceivedListener(new CoderMessageReceivedListener() { @Override public void projectListRspReceived(Map projectListRsp) { - //TODO + for(String projectName : projectListRsp.keySet()){ + ProjectListData projectData = projectListRsp.get(projectName); + projectSelectDialog.addProjectToList(projectName, projectData.type); + } + } + @Override + public void projectTypeRspReceived(Map projectTypeRsp) { + // TODO Auto-generated method stub + } + @Override + public void projectRspReceived(ProjectRspMsg projectRspMsg) { + // TODO Auto-generated method stub } }); //start receiving traffic from the server session.start(); + //clear the current local project list + this.projectSelectDialog.clearProjectList(); + + //request a new project list from the server + CoderMessage msg = new CoderMessage(); + msg.ProjectListReq = new ProjectListReqMsg(); + try { + session.send(msg); + } catch (IOException e) { + logger.log(Level.SEVERE, "Unable to send ProjectListReqMsg", e); + continue; + } + + //show the project selector dialog + this.projectSelectDialog.setVisible(true); //blocking + if(this.projectSelectDialog.getAction() == ProjectSelectDialog.ProjectSelectDialogAction.CANCEL){ + logger.fine("Project select dialog closed or canceled by the user. terminating."); + break; + } + String selectedProjectName = this.projectSelectDialog.getSelecteProjectName(); + logger.info("Project \"" +selectedProjectName+ "\" was selected."); + //add a listener to forward messages from the editor GUI to the server this.projectEditorWindow.addMessageSentListener(new GUIMessageSentListener(){ @Override @@ -152,15 +189,15 @@ public class CoderClient extends Thread{ } } - private void setPassword(char[] password) { + public void setPassword(char[] password) { this.password = password; } - private void setUsername(String username) { + public void setUsername(String username) { this.username = username; } - private void setRetryConnectForever(boolean tryForever) { + public void setRetryConnectForever(boolean tryForever) { if(tryForever){ this.connectionRetriesLimit = 0; }else{ diff --git a/src/com/coder/client/CoderMessageReceivedListener.java b/src/com/coder/client/CoderMessageReceivedListener.java index 07bc95c..bc75ba2 100644 --- a/src/com/coder/client/CoderMessageReceivedListener.java +++ b/src/com/coder/client/CoderMessageReceivedListener.java @@ -2,10 +2,15 @@ package com.coder.client; import java.util.Map; +import com.coder.server.message.ConfigData; import com.coder.server.message.ProjectListData; +import com.coder.server.message.ProjectRspMsg; public interface CoderMessageReceivedListener { - + + //project messages + void projectTypeRspReceived(Map projectTypeRsp); void projectListRspReceived(Map projectListRsp); + void projectRspReceived(ProjectRspMsg projectRspMsg); } diff --git a/src/com/coder/client/LoginCridentials.java b/src/com/coder/client/LoginCridentials.java new file mode 100644 index 0000000..e798658 --- /dev/null +++ b/src/com/coder/client/LoginCridentials.java @@ -0,0 +1,21 @@ +package com.coder.client; + +public class LoginCridentials { + + private String username; + private char[] password; + + public LoginCridentials(String username, char[] password){ + this.username = username; + this.password = password; + } + + public String getUsername(){ + return this.username; + } + + public char[] getPassword(){ + return this.password; + } + +} diff --git a/src/com/coder/client/Main.java b/src/com/coder/client/Main.java new file mode 100644 index 0000000..5268b6e --- /dev/null +++ b/src/com/coder/client/Main.java @@ -0,0 +1,69 @@ +package com.coder.client; + +import java.util.Map; +import java.util.logging.Logger; + +import com.coder.client.gui.EditorWindowController; +import com.coder.client.gui.GUIManager; +import com.coder.server.CoderServer; + +import zutil.log.LogUtil; +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class Main extends Application { + public static final Logger logger = LogUtil.getLogger(); + + public static void main(String[] args) { + Application.launch(Main.class, args); + } + + @Override + public void start(Stage mainStage) throws Exception { + + //parse program arguments + Map params = this.getParameters().getNamed(); + String username = null; + char[] password = null; + boolean reconnectForever = false; + for(String key : params.keySet()){ + String value = params.get(key); + if(key.equals("username")){ + username = value; + }else if(key.equals("password")){ + password = value.toCharArray(); + }else if(key.equals("retry-connect-forever")){ + reconnectForever = true; + } + } + + //load FXML resources + GUIManager guiManager = new GUIManager(mainStage); + + FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/coder/client/gui/EditorWindowGUI.fxml")); + Scene scene = new Scene((Parent)loader.load()); + EditorWindowController editorController = loader.getController(); + editorController.setScene(scene); + guiManager.setEditWindow(editorController); + + + mainStage.setTitle("CoderClient"); + + //create a client instance + int port = CoderServer.SERVER_PORT; + CoderClient client = new CoderClient("127.0.0.1", port, guiManager); + if(username != null){ + client.setUsername(username); + if(password != null){ + client.setPassword(password); + } + } + client.setRetryConnectForever(reconnectForever); + client.start(); + + } + +} diff --git a/src/com/coder/client/ProjectSelectDialog.java b/src/com/coder/client/ProjectSelectDialog.java new file mode 100644 index 0000000..3c85bee --- /dev/null +++ b/src/com/coder/client/ProjectSelectDialog.java @@ -0,0 +1,58 @@ +package com.coder.client; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; +import javax.swing.border.LineBorder; + +public class ProjectSelectDialog extends JDialog { + private static final long serialVersionUID = 3664648491769811261L; + + public static enum ProjectSelectDialogAction{ + PROJECT_SELECTED, + CANCEL + } + private ProjectSelectDialogAction action = ProjectSelectDialogAction.CANCEL; + + public ProjectSelectDialog(Frame parent) { + super(parent, "Select Project", true); + + //TODO + + //pack the dialog + pack(); + + setLocationRelativeTo(parent); + } + + public String getSelecteProjectName() { + return null; + } + + public ProjectSelectDialogAction getAction() { + return this.action; + } + + public void clearProjectList() { + // TODO Auto-generated method stub + + } + + public void addProjectToList(String projectName, String type) { + // TODO Auto-generated method stub + + } +} \ No newline at end of file diff --git a/src/com/coder/client/Session.java b/src/com/coder/client/Session.java index 86b129f..22afe18 100644 --- a/src/com/coder/client/Session.java +++ b/src/com/coder/client/Session.java @@ -216,6 +216,18 @@ public class Session extends Thread { listener.projectListRspReceived(msg.ProjectListRsp); } } + if(msg.ProjectRsp != null){ + logger.fine("The message contains a ProjectRspMsg"); + for(CoderMessageReceivedListener listener : messageReceivedlisteners){ + listener.projectRspReceived(msg.ProjectRsp); + } + } + if(msg.ProjectTypeRsp != null){ + logger.fine("The message contains a ProjectTypeRsp"); + for(CoderMessageReceivedListener listener : messageReceivedlisteners){ + listener.projectTypeRspReceived(msg.ProjectTypeRsp); + } + } } } diff --git a/src/com/coder/client/file/ProjectDirectory.java b/src/com/coder/client/file/ProjectDirectory.java new file mode 100644 index 0000000..ab13d52 --- /dev/null +++ b/src/com/coder/client/file/ProjectDirectory.java @@ -0,0 +1,84 @@ +package com.coder.client.file; + +import java.util.LinkedList; +import java.util.List; + +public class ProjectDirectory extends ProjectFileObject { + private LinkedList objectList; + + public ProjectDirectory(String name){ + super(name); + this.objectList = new LinkedList(); + } + + public List getAll(){ + return objectList; + } + + public boolean containsFile(String fileName){ + for(ProjectFileObject fileObject : objectList){ + if(fileObject instanceof ProjectFile){ + if(fileObject.getName().equals(fileName)){ + return true; + } + } + } + return false; + } + + public void addFile(ProjectFile file){ + this.objectList.add(file); + } + + public void deleteFile(String fileName){ + for(ProjectFileObject fileObject : objectList){ + if(fileObject instanceof ProjectFile){ + if(fileObject.getName().equals(fileName)){ + this.objectList.remove(fileObject); + } + } + } + } + + public boolean containsDirectory(String dirName){ + for(ProjectFileObject fileObject : objectList){ + if(fileObject instanceof ProjectDirectory){ + if(fileObject.getName().equals(dirName)){ + return true; + } + } + } + return false; + } + + public void addDirectory(String dirName){ + ProjectDirectory dir = new ProjectDirectory(dirName); + this.objectList.add(dir); + } + + public void deleteDirectory(String dirName){ + for(ProjectFileObject fileObject : objectList){ + if(fileObject instanceof ProjectDirectory){ + if(fileObject.getName().equals(dirName)){ + this.objectList.remove(fileObject); + } + } + } + } + + public ProjectDirectory getDirectory(String dirName) { + for(ProjectFileObject fileObject : objectList){ + if(fileObject instanceof ProjectDirectory){ + if(fileObject.getName().equals(dirName)){ + return (ProjectDirectory) fileObject; + } + } + } + return null; + } + + public String toString(){ + return this.getName(); + } + +} diff --git a/src/com/coder/client/file/ProjectFile.java b/src/com/coder/client/file/ProjectFile.java new file mode 100644 index 0000000..6a18e3f --- /dev/null +++ b/src/com/coder/client/file/ProjectFile.java @@ -0,0 +1,15 @@ +package com.coder.client.file; + +public class ProjectFile extends ProjectFileObject{ + private String filePath; + + public ProjectFile(String name, String filePath) { + super(name); + this.filePath = filePath; + } + + public String toString(){ + return this.getName() + " ("+filePath+")"; + } + +} diff --git a/src/com/coder/client/file/ProjectFileObject.java b/src/com/coder/client/file/ProjectFileObject.java new file mode 100644 index 0000000..b8360e9 --- /dev/null +++ b/src/com/coder/client/file/ProjectFileObject.java @@ -0,0 +1,14 @@ +package com.coder.client.file; + +public abstract class ProjectFileObject{ + private String name; + + public ProjectFileObject(String name){ + this.name = name; + } + + public String getName(){ + return name; + } + +} \ No newline at end of file diff --git a/src/com/coder/client/file/ProjectFileTree.java b/src/com/coder/client/file/ProjectFileTree.java new file mode 100644 index 0000000..319cf15 --- /dev/null +++ b/src/com/coder/client/file/ProjectFileTree.java @@ -0,0 +1,124 @@ +package com.coder.client.file; + +public class ProjectFileTree { + private ProjectDirectory root; + + public ProjectFileTree(){ + this.root = new ProjectDirectory("/"); + } + + public void printFileTree(){ + if(root != null){ + printDirectoryContent(root, 0); + }else{ + System.out.println("File tree not set"); + } + } + + private void printDirectoryContent(ProjectDirectory dir, int indentation){ + for(ProjectFileObject obj : dir.getAll()){ + if(obj instanceof ProjectDirectory){ + printIndentation(indentation); + System.out.println("DIR:"+obj); + printDirectoryContent((ProjectDirectory) obj, indentation+1); + } + } + for(ProjectFileObject obj : dir.getAll()){ + if(obj instanceof ProjectFile){ + printIndentation(indentation); + System.out.println("FILE:"+obj); + } + } + } + + private void printIndentation(int indentation){ + for(int i = 0; i < indentation; ++i){ + System.out.print(" "); + } + } + + public boolean parseFileList(String[] filePaths){ + ProjectDirectory root = new ProjectDirectory("/"); + for(String filePath : filePaths){ + if(filePath.endsWith("/")){ + System.out.println("SEVERE: file path is not pointing to a file: " + filePath); + this.root = null; + return false; + } + String[] tmp; + if(filePath.startsWith("/")){ + tmp = filePath.substring(1, filePath.length()).split("/"); + }else{ + tmp = filePath.split("/"); + } + int i; + ProjectDirectory tmpRoot = root; + for(i = 0; i < tmp.length-1; ++i){ + String directoryName = tmp[i]; + if(directoryName.isEmpty()){ + System.out.println("SEVERE: a directory name cannot be empty"); + this.root = null; + return false; + } + if(tmpRoot.containsDirectory(directoryName) == false){ + tmpRoot.addDirectory(directoryName); + }else{ + //directory already exist + } + tmpRoot = tmpRoot.getDirectory(directoryName); + } + String fileName = tmp[i]; + if(tmpRoot.containsDirectory(fileName)){ + //logger.sever("File list contains a directory and file with the same name"); + System.out.println("SEVERE: File list contains a directory and file with the same name"); + this.root = null; + return false; + }else if(tmpRoot.containsFile(fileName) == false){ + ProjectFile file = new ProjectFile(fileName, filePath); + tmpRoot.addFile(file); + }else{ + //file already exists + } + } + updateCurrentFileList(root, this.root); + return true; + } + + private void updateCurrentFileList(ProjectDirectory sourceRoot, ProjectDirectory targetRoot){ + + //remove files in target that does not exist in source + for(ProjectFileObject targetFileObject : targetRoot.getAll()){ + String fileObjectName = targetFileObject.getName(); + if(targetFileObject instanceof ProjectFile){ //target file is a file + if(sourceRoot.containsFile(fileObjectName) == false){ + targetRoot.deleteFile(fileObjectName); + targetFileObject = null; + } + }else if(targetFileObject instanceof ProjectDirectory){ //target file is a directory + if(sourceRoot.containsDirectory(fileObjectName) == false){ + targetRoot.deleteDirectory(fileObjectName); + targetFileObject = null; + } + } + } + + //add files to target that exist in source + for(ProjectFileObject sourceFileObject : sourceRoot.getAll()){ + String fileObjectName = sourceFileObject.getName(); + if(sourceFileObject instanceof ProjectFile){ //source file is a file + if(targetRoot.containsFile(fileObjectName) == false){ + ProjectFile file = (ProjectFile)sourceFileObject; + targetRoot.addFile(file); + } + }else if(sourceFileObject instanceof ProjectDirectory){ //source file is a directory + if(targetRoot.containsDirectory(fileObjectName) == false){ + targetRoot.addDirectory(fileObjectName); + } + //recursive + ProjectDirectory dir = (ProjectDirectory)sourceFileObject; + updateCurrentFileList(dir, targetRoot.getDirectory(fileObjectName)); + } + } + } + +} diff --git a/src/com/coder/client/file/ProjectFileTreeTest.java b/src/com/coder/client/file/ProjectFileTreeTest.java new file mode 100644 index 0000000..bca89f2 --- /dev/null +++ b/src/com/coder/client/file/ProjectFileTreeTest.java @@ -0,0 +1,37 @@ +package com.coder.client.file; + +public class ProjectFileTreeTest { + ProjectFileTree tree = new ProjectFileTree(); + + public ProjectFileTreeTest(){ + tree.printFileTree(); + + tree.parseFileList(new String[]{"/test"}); + tree.printFileTree(); + + tree.parseFileList(new String[]{"/test", "/test/test"}); + tree.printFileTree(); + + tree.parseFileList(new String[]{"/test/test/"}); + tree.printFileTree(); + + tree.parseFileList(new String[]{"/test/"}); + tree.printFileTree(); + + tree.parseFileList(new String[]{"/"}); + tree.printFileTree(); + + tree.parseFileList(new String[]{"//"}); + tree.printFileTree(); + + tree.parseFileList(new String[]{"//test"}); + tree.printFileTree(); + + + } + + public static void main(String[] args){ + new ProjectFileTreeTest(); + } + +} diff --git a/src/com/coder/client/gui/Controller.java b/src/com/coder/client/gui/Controller.java new file mode 100644 index 0000000..635af5e --- /dev/null +++ b/src/com/coder/client/gui/Controller.java @@ -0,0 +1,5 @@ +package com.coder.client.gui; + +public class Controller { + +} diff --git a/src/com/coder/client/gui/EditorWindow.java b/src/com/coder/client/gui/EditorWindow.java new file mode 100644 index 0000000..80d6f97 --- /dev/null +++ b/src/com/coder/client/gui/EditorWindow.java @@ -0,0 +1,32 @@ +package com.coder.client.gui; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class EditorWindow extends Application { + + public EditorWindow(){ + + } + + @Override + public void start(Stage stage) throws Exception { + FXMLLoader loader = new FXMLLoader(getClass().getResource("EditorWindowGUI.fxml")); + + EditorWindowController editorController = loader.getController(); + + Scene editorScene = new Scene((Parent)loader.load()); + + stage.setTitle("CoderClient"); + stage.setScene(editorScene); + stage.show(); + } + + public static void main(String[] args) { + Application.launch(EditorWindow.class, args); + } + +} diff --git a/src/com/coder/client/gui/EditorWindowController.java b/src/com/coder/client/gui/EditorWindowController.java new file mode 100644 index 0000000..16af315 --- /dev/null +++ b/src/com/coder/client/gui/EditorWindowController.java @@ -0,0 +1,113 @@ +package com.coder.client.gui; + +import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.ResourceBundle; + +import org.controlsfx.control.PropertySheet; +import org.controlsfx.property.editor.PropertyEditor; + +import com.coder.client.GUIMessageSentListener; +import com.coder.client.property.CheckBoxProperty; +import com.coder.client.property.CoderClientProperty; +import com.coder.client.property.ComboBoxProperty; +import com.coder.server.message.CoderMessage; + + +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Button; +import javafx.scene.control.TextArea; +import javafx.scene.control.TreeItem; +import javafx.scene.control.TreeView; +import javafx.util.Callback; + +public class EditorWindowController extends Controller implements Initializable { + + private HashSet GUIMessageSentListeners = new HashSet(); + + @FXML private TreeView fileTreeView; + @FXML private TextArea editTextArea; + @FXML private PropertySheet propertySheet; + @FXML private Button compileButton; + @FXML private Button runButton; + + public EditorWindowController(){ + super(); + } + + @Override + public void initialize(URL fxmlFileLocation, ResourceBundle resources) { + + setupPropertySheet(); + setupFileTreeView(); + + } + + @FXML + protected void handleRun(ActionEvent event){ + + } + + @FXML + protected void handleCompile(ActionEvent event){ + + } + + private void setupFileTreeView(){ + fileTreeView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener>() { + @Override + public void changed(ObservableValue> item, TreeItem oldValue, TreeItem newValue) { + if(newValue != fileTreeView.getRoot()){ + System.out.println("INFO: item " + newValue + " selected"); + } + } + }); + + TreeItem root = new TreeItem("root"); + root.setExpanded(true); + fileTreeView.setRoot(root); + + } + + private void setupPropertySheet(){ + //handle custom ProperySheet.Item's editor + propertySheet.setPropertyEditorFactory(new Callback>(){ + @Override + public PropertyEditor call(PropertySheet.Item param) { + //only support internal types + if(param instanceof CoderClientProperty){ + return ((CoderClientProperty)param).getEditor(); + } + //not a internal property type + return null; + } + }); + + propertySheet.getItems().clear(); + + ArrayList boards = new ArrayList(); + boards.add("UNO"); + boards.add("Mega"); + ComboBoxProperty p1 = new ComboBoxProperty("Port", null, boards); + propertySheet.getItems().add(p1); + + CheckBoxProperty p2 = new CheckBoxProperty("Melt?", false); + propertySheet.getItems().add(p2); + } + + public void addMessageSentListener(GUIMessageSentListener listener) { + this.GUIMessageSentListeners.add(listener); + } + + private void sendMessage(CoderMessage msg){ + for(GUIMessageSentListener listener : GUIMessageSentListeners){ + listener.sendMessage(msg); + } + } + +} diff --git a/src/com/coder/client/gui/EditorWindowGUI.fxml b/src/com/coder/client/gui/EditorWindowGUI.fxml new file mode 100644 index 0000000..cf1bc73 --- /dev/null +++ b/src/com/coder/client/gui/EditorWindowGUI.fxml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + +