From 4adc23d9025b08837b9f9f6f9dc2baf118487e31 Mon Sep 17 00:00:00 2001 From: delvh Date: Thu, 31 Oct 2019 22:11:50 +0100 Subject: [PATCH] Added automatic line separation and Keylistener for posting Improvements yet to be made (help wanted!): * automatic line separation does not work * automatic line separation needs a case for insertion of a String * Keylistener for Posting does not work * Method for listening to multiple keys needed (ctrl+enter) * A template Settings screen was added, has yet to be polished --- src/main/java/envoy/client/Chat.java | 2 +- src/main/java/envoy/client/Client.java | 37 +++-- src/main/java/envoy/client/Config.java | 4 +- src/main/java/envoy/client/LocalDB.java | 20 ++- src/main/java/envoy/client/ui/ChatWindow.java | 113 +++++++++++-- .../java/envoy/client/ui/SettingsScreen.java | 156 ++++++++++++++++++ 6 files changed, 301 insertions(+), 31 deletions(-) create mode 100644 src/main/java/envoy/client/ui/SettingsScreen.java diff --git a/src/main/java/envoy/client/Chat.java b/src/main/java/envoy/client/Chat.java index abeb226..da35bd3 100644 --- a/src/main/java/envoy/client/Chat.java +++ b/src/main/java/envoy/client/Chat.java @@ -10,7 +10,7 @@ import envoy.schema.User; public class Chat implements Serializable { private static final long serialVersionUID = -7751248474547242056L; - + private User recipient; private DefaultListModel model = new DefaultListModel<>(); diff --git a/src/main/java/envoy/client/Client.java b/src/main/java/envoy/client/Client.java index 0ebce3c..120d7dc 100644 --- a/src/main/java/envoy/client/Client.java +++ b/src/main/java/envoy/client/Client.java @@ -28,9 +28,8 @@ import envoy.schema.Users; * @author Kai S. K. Engelbart * @author Maximilian Käfer * @author Leon Hofmeister - * @since Envoy 0.1 + * @since Envoy v0.1-alpha */ - public class Client { private ObjectFactory objectFactory = new ObjectFactory(); @@ -53,9 +52,8 @@ public class Client { * Because sending a request is a blocking operation, it is executed * asynchronously. * - * @param sender name of the sender - * @param recipient name of the recipient - * @param textContent content (text) of the message + * @param message the {@link Message} we want to send + * @since Envoy v0.1-alpha */ public void sendMessage(Message message) { new Thread(() -> { @@ -90,6 +88,7 @@ public class Client { * * @param textContent The content (text) of the message * @return prepared {@link Message} object + * @since Envoy v0.1-alpha */ public Message createMessage(String textContent) { Message.MetaData metaData = objectFactory.createMessageMetaData(); @@ -109,22 +108,28 @@ public class Client { return message; } - public Users getUsersListXml() { return get(String.format("%s:%d/envoy-server/rest/user", config.getServer(), config.getPort()), Users.class); } + public Users getUsersListXml() { + return get(String.format("%s:%d/envoy-server/rest/user", config.getServer(), config.getPort()), Users.class); + } public Messages getUnreadMessages(long userId) { - return get(String.format("%s:%d/envoy-server/rest/message/receive?userId=%d", config.getServer(), config.getPort(), userId), Messages.class); + return get(String + .format("%s:%d/envoy-server/rest/message/receive?userId=%d", config.getServer(), config.getPort(), userId), + Messages.class); } /** - * Returns a {@link User} with a specific name by name. + * Returns a {@link User} with a specific id by name. * * @param name - the name of the {@link User} * @return a {@link User} with the specified name * @since Envoy v0.1-alpha */ private User getUser(String name) { - return get(String.format("%s:%d/envoy-server/rest/user/sender?name=%s", config.getServer(), config.getPort(), name), Users.class).getUser() - .get(0); + return get( + String + .format("%s:%d/envoy-server/rest/user/sender?name=%s", config.getServer(), config.getPort(), name), + Users.class).getUser().get(0); } /** @@ -166,9 +171,21 @@ public class Client { */ public User getSender() { return sender; } + /** + * @return the recipient of a message. + * @since Envoy v0.1-alpha + */ public User getRecipient() { return recipient; } + /** + * @param recipient the recipient to set + * @since Envoy v0.1-alpha + */ public void setRecipient(User recipient) { this.recipient = recipient; } + /** + * @return true, if a recipient is selected + * @since Envoy v0.1-alpha + */ public boolean hasRecipient() { return recipient != null; } } \ No newline at end of file diff --git a/src/main/java/envoy/client/Config.java b/src/main/java/envoy/client/Config.java index 1d107ca..bfa1e57 100644 --- a/src/main/java/envoy/client/Config.java +++ b/src/main/java/envoy/client/Config.java @@ -59,7 +59,9 @@ public class Config { * @return {@code true} if server, port and localDB directory are known. * @since Envoy v0.1-alpha */ - public boolean isInitialized() { return server != null && !server.isEmpty() && port > 0 && port < 65566 && localDB != null; } + public boolean isInitialized() { + return server != null && !server.isEmpty() && port > 0 && port < 65566 && localDB != null; + } /** * @return the host name of the Envoy server diff --git a/src/main/java/envoy/client/LocalDB.java b/src/main/java/envoy/client/LocalDB.java index d7b3409..1bab251 100644 --- a/src/main/java/envoy/client/LocalDB.java +++ b/src/main/java/envoy/client/LocalDB.java @@ -23,16 +23,16 @@ import envoy.schema.User; public class LocalDB { private File localDB; - private User sender; + private User client; private List chats = new ArrayList<>(); /** * Constructs an empty local database. * - * @param sender the user that is logged in with this client + * @param client the user that is logged in with this client * @since Envoy v0.1-alpha **/ - public LocalDB(User sender) { this.sender = sender; } + public LocalDB(User sender) { this.client = sender; } /** * Initializes the local database and fills it with values @@ -43,9 +43,9 @@ public class LocalDB { * @since Envoy v0.1-alpha **/ public void initializeDBFile(File localDBDir) throws EnvoyException { - if (localDBDir.exists() && !localDBDir.isDirectory()) - throw new EnvoyException(String.format("LocalDBDir '%s' is not a directory!", localDBDir.getAbsolutePath())); - localDB = new File(localDBDir, sender.getID() + ".db"); + if (localDBDir.exists() && !localDBDir.isDirectory()) throw new EnvoyException( + String.format("LocalDBDir '%s' is not a directory!", localDBDir.getAbsolutePath())); + localDB = new File(localDBDir, client.getID() + ".db"); if (localDB.exists()) loadFromLocalDB(); } @@ -89,8 +89,14 @@ public class LocalDB { /** * @return all saves {@link Chat} objects that list the client user as the - * sender + * client * @since Envoy v0.1-alpha **/ public List getChats() { return chats; } + + /** + * @return the User who initialised the local Database + * @since Envoy v0.1-alpha + */ + public User getUser() { return client; } } diff --git a/src/main/java/envoy/client/ui/ChatWindow.java b/src/main/java/envoy/client/ui/ChatWindow.java index 5f79b13..f6625b5 100644 --- a/src/main/java/envoy/client/ui/ChatWindow.java +++ b/src/main/java/envoy/client/ui/ChatWindow.java @@ -6,6 +6,9 @@ import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; +import java.awt.event.InputMethodEvent; +import java.awt.event.InputMethodListener; +import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; @@ -39,7 +42,7 @@ import envoy.schema.Users; * @author Kai S. K. Engelbart * @author Maximilian Käfer * @author Leon Hofmeister - * @since Envoy 0.1 + * @since Envoy v0.1-alpha */ public class ChatWindow extends JFrame { @@ -53,6 +56,8 @@ public class ChatWindow extends JFrame { private JList userList = new JList<>(); private Chat currentChat; + private String messageText; + public ChatWindow(Client client, LocalDB localDB) { this.client = client; this.localDB = localDB; @@ -77,7 +82,7 @@ public class ChatWindow extends JFrame { gbl_contentPane.columnWidths = new int[] { 1, 1, 1 }; gbl_contentPane.rowHeights = new int[] { 1, 1, 1 }; gbl_contentPane.columnWeights = new double[] { 0.3, 1.0, 0.1 }; - gbl_contentPane.rowWeights = new double[] { 0.05, 1, 0.07 }; + gbl_contentPane.rowWeights = new double[] { 0.05, 1.0, 0.07 }; contentPane.setLayout(gbl_contentPane); JList messageList = new JList<>(); @@ -114,6 +119,30 @@ public class ChatWindow extends JFrame { // Message enter field JTextArea messageEnterTextfield = new JTextArea(); + // checks for changed Message + messageEnterTextfield.addInputMethodListener(new InputMethodListener() { + + public void caretPositionChanged(InputMethodEvent arg0) {} + + public void inputMethodTextChanged(InputMethodEvent arg0) { + String message = messageEnterTextfield.getText(); + int messageSize = message.length(); + int lineSize = 45; + String[] tempString = message.split(System.getProperty("line.separator")); + int currentLineAmount = tempString.length; + int wantedLineAmount = Math.floorDiv(messageSize, lineSize); + if (currentLineAmount != wantedLineAmount) { + if (Math.abs(messageText.length() - messageSize) == 1 + && currentLineAmount == wantedLineAmount - 1) {// Check for "normal" Keystroke/Backspace + messageEnterTextfield.setText(transformLastSpace(message, wantedLineAmount, lineSize)); + } + } else {// That's the case if a group of chars was a)inserted or b)deleted + // TODO + } + messageText = message; + } + + }); messageEnterTextfield.setCaretColor(new Color(255, 255, 255)); messageEnterTextfield.setForeground(new Color(255, 255, 255)); messageEnterTextfield.setBackground(new Color(51, 51, 51)); @@ -122,17 +151,20 @@ public class ChatWindow extends JFrame { messageEnterTextfield.setFont(new Font("Arial", Font.PLAIN, 17)); messageEnterTextfield.setBorder(new EmptyBorder(5, 5, 5, 5)); - GridBagConstraints gbc_moveSelectionMessageEnterTextfield = new GridBagConstraints(); - gbc_moveSelectionMessageEnterTextfield.fill = GridBagConstraints.BOTH; - gbc_moveSelectionMessageEnterTextfield.gridx = 1; - gbc_moveSelectionMessageEnterTextfield.gridy = 2; + GridBagConstraints gbc_messageEnterTextfield = new GridBagConstraints(); + gbc_messageEnterTextfield.fill = GridBagConstraints.BOTH; + gbc_messageEnterTextfield.gridx = 1; + gbc_messageEnterTextfield.gridy = 2; - gbc_moveSelectionMessageEnterTextfield.insets = new Insets(10, 10, 10, 10); + gbc_messageEnterTextfield.insets = new Insets(10, 10, 10, 10); - contentPane.add(messageEnterTextfield, gbc_moveSelectionMessageEnterTextfield); + contentPane.add(messageEnterTextfield, gbc_messageEnterTextfield); // Post Button JButton postButton = new JButton("Post"); + if (SettingsScreen.isEnterToSend() == true) { postButton.setMnemonic(KeyEvent.VK_ENTER); } + // TODO: Other option to send only on "ctrl"+"enter" needs to be implemented. + // TODO: Difficult. Also above statement doesn't work! postButton.setForeground(new Color(255, 255, 255)); postButton.setBackground(new Color(102, 51, 153)); postButton.setBorderPainted(false); @@ -147,7 +179,10 @@ public class ChatWindow extends JFrame { postButton.addActionListener((evt) -> { if (!client.hasRecipient()) { - JOptionPane.showMessageDialog(this, "Please select a recipient!", "Cannot send message", JOptionPane.INFORMATION_MESSAGE); + JOptionPane.showMessageDialog(this, + "Please select a recipient!", + "Cannot send message", + JOptionPane.INFORMATION_MESSAGE); return; } @@ -175,6 +210,31 @@ public class ChatWindow extends JFrame { contentPane.add(postButton, gbc_moveSelectionPostButton); + // Settings Button + JButton settingsButton = new JButton("Settings"); + settingsButton.setForeground(new Color(255, 255, 255)); + settingsButton.setBackground(new Color(102, 51, 153)); + settingsButton.setBorderPainted(false); + + GridBagConstraints gbc_moveSelectionSettingsButton = new GridBagConstraints(); + + gbc_moveSelectionSettingsButton.fill = GridBagConstraints.BOTH; + gbc_moveSelectionSettingsButton.gridx = 2; + gbc_moveSelectionSettingsButton.gridy = 0; + + gbc_moveSelectionSettingsButton.insets = new Insets(10, 10, 10, 10); + + settingsButton.addActionListener((evt) -> { + try { + SettingsScreen.open(localDB.getUser().getName()); + } catch (Exception e) { + SettingsScreen.open(); + System.err.println("An Error occured while opening the Settings screen"); + e.printStackTrace(); + } + }); + contentPane.add(settingsButton, gbc_moveSelectionSettingsButton); + // Partner name display JTextPane textPane = new JTextPane(); textPane.setBackground(new Color(0, 0, 0)); @@ -200,7 +260,11 @@ public class ChatWindow extends JFrame { final User user = selectedUserList.getSelectedValue(); client.setRecipient(user); - currentChat = localDB.getChats().stream().filter(chat -> chat.getRecipient().getID() == user.getID()).findFirst().get(); + currentChat = localDB.getChats() + .stream() + .filter(chat -> chat.getRecipient().getID() == user.getID()) + .findFirst() + .get(); client.setRecipient(user); @@ -233,9 +297,31 @@ public class ChatWindow extends JFrame { contentPane.revalidate(); } + /** + * takes care of too long or not needed lines in the message. + * + * @param message the message that is currently written in the textfield + * @param wantedLine the line at which we currently are positioned + * @param lineSize the amount of chars per line + * @return the transformed message + * @since Envoy v0.1-alpha + */ + private String transformLastSpace(String message, int wantedLine, int lineSize) { + int index = wantedLine * lineSize; + int lastSpace = message.lastIndexOf(" ", index); + if (index - lastSpace > lineSize) {// Fall Wort länger als Zeile + return message.substring(0, index) + System.getProperty("line.separator") + message.substring(index); + } else { + return message.substring(0, lastSpace - 1) + System.getProperty("line.separator") + + message.substring(lastSpace + 1); + } + } + /** * Initializes the elements of the user list by downloading them from the * server. + * + * @since Envoy v0.1-alpha */ private void loadUsersAndChats() { new Thread(() -> { @@ -257,14 +343,17 @@ public class ChatWindow extends JFrame { * * @param timeout the amount of time that passes between two requests sent to * the server + * @since Envoy v0.1-alpha */ private void startReceiverThread(int timeout) { new Timer(timeout, (evt) -> { Messages unreadMessages = client.getUnreadMessages(client.getSender().getID()); for (int i = 0; i < unreadMessages.getMessage().size(); i++) for (int j = 0; j < localDB.getChats().size(); j++) - if (localDB.getChats().get(j).getRecipient().getID() == unreadMessages.getMessage().get(i).getMetaData().getSender()) - localDB.getChats().get(j).appendMessage(unreadMessages.getMessage().get(i)); + if (localDB.getChats().get(j).getRecipient().getID() == unreadMessages.getMessage() + .get(i) + .getMetaData() + .getSender()) localDB.getChats().get(j).appendMessage(unreadMessages.getMessage().get(i)); }).start(); } } \ No newline at end of file diff --git a/src/main/java/envoy/client/ui/SettingsScreen.java b/src/main/java/envoy/client/ui/SettingsScreen.java new file mode 100644 index 0000000..7a4edab --- /dev/null +++ b/src/main/java/envoy/client/ui/SettingsScreen.java @@ -0,0 +1,156 @@ +package envoy.client.ui; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.FlowLayout; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; + +/** + * Project: envoy-client
+ * File: SettingsScreen.java
+ * Created: 31 Oct 2019
+ * + * @author Leon Hofmeister + */ +public class SettingsScreen extends JDialog { + + private static final long serialVersionUID = -4476913491263077107L; + private final JPanel contentPanel = new JPanel(); + public static boolean EnterToSend = true; + + // TODO: Add a JPanel with all the Information necessary: + // change (Picture,Username, Email, Password) and toggle(light/dark mode, + // "ctrl+enter"/"enter" + // to send a message directly) + /** + * Open the Settings screen. + * Only suited for Dev/Error mode. + * Avoid usage. + * + * @since Envoy v0.1-alpha + */ + public static void open() { + SettingsScreen dialog = new SettingsScreen(); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + dialog.setVisible(true); + } + + /** + * Opens the Settings screen.
+ * Use preferreably since everyone is already initialised.
+ * It personalises the screen more. + * + * @param Username The name of the User + * @param Email The Email that is associated with that Account + * @since Envoy v0.1-alpha + */ + public static void open(String Username) {// , String Email) {AUSKLAMMERN, WENN ANMELDUNG PER + // EMAIL IMPLEMENTIERT IST! + SettingsScreen dialog = new SettingsScreen(Username); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + dialog.setVisible(true); + } + + /** + * Builds the Settings screen.
+ * Use only as Dev/Error Mode.
+ * Avoid usage. + * + * @since Envoy v0.1-alpha + */ + public SettingsScreen() { + setBackground(Color.BLACK); + setBounds(100, 100, 450, 300); + getContentPane().setLayout(new BorderLayout()); + contentPanel.setBackground(Color.BLACK); + contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); + getContentPane().add(contentPanel, BorderLayout.CENTER); + contentPanel.setLayout(new BorderLayout(0, 0)); + { + JPanel buttonPane = new JPanel(); + buttonPane.setBackground(Color.BLACK); + getContentPane().add(buttonPane, BorderLayout.SOUTH); + buttonPane.setLayout(new BorderLayout(0, 0)); + { + JButton okButton = new JButton("Save"); + okButton.setActionCommand("OK"); + buttonPane.add(okButton, BorderLayout.EAST); + getRootPane().setDefaultButton(okButton); + okButton.addActionListener((evt) -> { + // Hier später die Daten abspeichern, wenn Datenmodell implementiert ist + dispose(); + }); + } + { + JButton cancelButton = new JButton("Cancel"); + cancelButton.setActionCommand("Cancel"); + buttonPane.add(cancelButton, BorderLayout.WEST); + + cancelButton.addActionListener((evt) -> { dispose(); }); + } + } + } + + /** + * Builds the Settings screen.
+ * Use preferreably since everyone is already initialised.
+ * It personalises the screen more. + * + * @param Username The name of the User + * @param Email The Email that is associated with that Account + * @since Envoy v0.1-alpha + */ + public SettingsScreen(String Username) {// , String Email, String hashedPwd) {AUSKLAMMERN, WENN ANMELDUNG PER EMAIL + // IMPLEMENTIERT IST! + setBackground(Color.BLACK); + setBounds(100, 100, 450, 300); + getContentPane().setLayout(new BorderLayout()); + contentPanel.setBackground(Color.BLACK); + contentPanel.setLayout(new FlowLayout()); + contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); + getContentPane().add(contentPanel, BorderLayout.CENTER); + { + JPanel buttonPane = new JPanel(); + buttonPane.setBackground(Color.BLACK); + getContentPane().add(buttonPane, BorderLayout.SOUTH); + buttonPane.setLayout(new BorderLayout(0, 0)); + { + JButton okButton = new JButton("Save"); + okButton.setActionCommand("OK"); + buttonPane.add(okButton, BorderLayout.EAST); + getRootPane().setDefaultButton(okButton); + okButton.addActionListener((evt) -> { + // Hier später die Daten abspeichern, wenn Datenmodell implementiert ist + dispose(); + }); + } + { + JButton cancelButton = new JButton("Cancel"); + cancelButton.setActionCommand("Cancel"); + buttonPane.add(cancelButton, BorderLayout.WEST); + + cancelButton.addActionListener((evt) -> { dispose(); }); + } + } + } + + /** + * @return true if Enter should be used to send a message + * @since Envoy v0.1-alpha + */ + public static boolean isEnterToSend() { return EnterToSend; } + + /** + * @param enterToSend
+ * toggles whether a message should be sent via + *
+ * buttonpress "enter" or "ctrl"+"enter" + * @since Envoy v0.1-alpha + */ + public static void setEnterToSend(boolean enterToSend) { EnterToSend = enterToSend; } + // TODO: Should be changed to private, but later to avoid warnings +}