From 8d41a2230a60bcde0dd9032cb97ac71791b772c9 Mon Sep 17 00:00:00 2001 From: kske Date: Sat, 25 Jan 2020 11:33:28 +0100 Subject: [PATCH 01/11] Added component list classes (unfinished) --- .../envoy/client/ui/list/ComponentList.java | 32 +++++++ .../ui/list/ComponentListCellRenderer.java | 16 ++++ .../client/ui/list/ComponentListModel.java | 83 +++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 src/main/java/envoy/client/ui/list/ComponentList.java create mode 100644 src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java create mode 100644 src/main/java/envoy/client/ui/list/ComponentListModel.java diff --git a/src/main/java/envoy/client/ui/list/ComponentList.java b/src/main/java/envoy/client/ui/list/ComponentList.java new file mode 100644 index 0000000..869721c --- /dev/null +++ b/src/main/java/envoy/client/ui/list/ComponentList.java @@ -0,0 +1,32 @@ +package envoy.client.ui.list; + +import javax.swing.BoxLayout; +import javax.swing.JPanel; + +/** + * Project: envoy-client
+ * File: ComponentList.java
+ * Created: 25.01.2020
+ * + * @author Kai S. K. Engelbart + * @since Envoy v0.3-alpha + */ +public class ComponentList extends JPanel { + + private ComponentListCellRenderer renderer; + + private static final long serialVersionUID = 1759644503942876737L; + + public ComponentList(ComponentListModel model, ComponentListCellRenderer renderer) { + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + model.setComponentList(this); + this.renderer = renderer; + + for (E elem : model) + add(renderer.getListCellComponent(this, elem, false)); + } + + void add(E elem) { + add(renderer.getListCellComponent(this, elem, false)); + } +} diff --git a/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java b/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java new file mode 100644 index 0000000..6a3b383 --- /dev/null +++ b/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java @@ -0,0 +1,16 @@ +package envoy.client.ui.list; + +import java.awt.Component; + +/** + * Project: envoy-client
+ * File: ComponentListCellRenderer.java
+ * Created: 25.01.2020
+ * + * @author Kai S. K. Engelbart + * @since Envoy v0.3-alpha + */ +public interface ComponentListCellRenderer { + + Component getListCellComponent(ComponentList list, E value, boolean isSelected); +} diff --git a/src/main/java/envoy/client/ui/list/ComponentListModel.java b/src/main/java/envoy/client/ui/list/ComponentListModel.java new file mode 100644 index 0000000..e5ea6fe --- /dev/null +++ b/src/main/java/envoy/client/ui/list/ComponentListModel.java @@ -0,0 +1,83 @@ +package envoy.client.ui.list; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * Project: envoy-client
+ * File: ComponentListModel.java
+ * Created: 25.01.2020
+ * + * @author Kai S. K. Engelbart + * @since Envoy v0.3-alpha + */ +public final class ComponentListModel implements Iterable { + + private List elements = new ArrayList<>(); + private ComponentList componentList; + + /** + * Adds an element to this model and notifies the associated + * {@link ComponentList} to add the corresponding component. + * + * @param e the element to add + * @return {@code true} + * @see java.util.List#add(java.lang.Object) + */ + public boolean add(E e) { + componentList.add(e); + return elements.add(e); + } + + /** + * Removes all elements from this model and clears the associated + * {@link ComponentList}. + * + * @see java.util.List#clear() + * @since Envoy v0.3-alpha + */ + public void clear() { + elements.clear(); + componentList.removeAll(); + } + + /** + * @param index + * @return + * @see java.util.List#get(int) + */ + public E get(int index) { return elements.get(index); } + + /** + * Removes the element at a specific index from this model and the corresponding + * component from the {@link ComponentList}. + * + * @param index + * @return the removed element + * @see java.util.List#remove(int) + */ + public E remove(int index) { + componentList.remove(index); + return elements.remove(index); + } + + /** + * @return + * @see java.util.List#iterator() + */ + public Iterator iterator() { + return new Iterator() { + + Iterator iter = elements.iterator(); + + @Override + public boolean hasNext() { return iter.hasNext(); } + + @Override + public E next() { return iter.next(); } + }; + } + + void setComponentList(ComponentList componentList) { this.componentList = componentList; } +} From 43851d98934424b712bf865919f9f5f137d77f33 Mon Sep 17 00:00:00 2001 From: kske Date: Sun, 26 Jan 2020 22:10:15 +0100 Subject: [PATCH 02/11] Started ComponentList integration for message list --- src/main/java/envoy/client/Chat.java | 9 +++---- src/main/java/envoy/client/ui/ChatWindow.java | 26 +++++++++---------- .../envoy/client/ui/MessageListRenderer.java | 14 +++++----- .../envoy/client/ui/list/ComponentList.java | 24 ++++++++++++++--- .../ui/list/ComponentListCellRenderer.java | 2 +- .../client/ui/list/ComponentListModel.java | 17 +++++++----- 6 files changed, 56 insertions(+), 36 deletions(-) diff --git a/src/main/java/envoy/client/Chat.java b/src/main/java/envoy/client/Chat.java index ef762c4..20513e4 100644 --- a/src/main/java/envoy/client/Chat.java +++ b/src/main/java/envoy/client/Chat.java @@ -2,8 +2,7 @@ package envoy.client; import java.io.Serializable; -import javax.swing.DefaultListModel; - +import envoy.client.ui.list.ComponentListModel; import envoy.data.Message; import envoy.data.User; @@ -25,7 +24,7 @@ public class Chat implements Serializable { private static final long serialVersionUID = -7751248474547242056L; private User recipient; - private DefaultListModel model = new DefaultListModel<>(); + private ComponentListModel model = new ComponentListModel<>(); /** * Provides the list of messages that the recipient receives.
@@ -48,11 +47,11 @@ public class Chat implements Serializable { * @param message the message to add in said chat * @since Envoy v0.1-alpha */ - public void appendMessage(Message message) { model.addElement(message); } + public void appendMessage(Message message) { model.add(message); } /** * @return all messages in the current chat * @since Envoy v0.1-alpha */ - public DefaultListModel getModel() { return model; } + public ComponentListModel getModel() { return model; } } \ No newline at end of file diff --git a/src/main/java/envoy/client/ui/ChatWindow.java b/src/main/java/envoy/client/ui/ChatWindow.java index 4f8b0dc..671deda 100644 --- a/src/main/java/envoy/client/ui/ChatWindow.java +++ b/src/main/java/envoy/client/ui/ChatWindow.java @@ -11,6 +11,8 @@ import javax.swing.border.EmptyBorder; import envoy.client.*; import envoy.client.event.ThemeChangeEvent; +import envoy.client.ui.list.ComponentList; +import envoy.client.ui.list.ComponentListModel; import envoy.client.ui.settings.SettingsScreen; import envoy.client.util.EnvoyLog; import envoy.data.Message; @@ -41,7 +43,7 @@ public class ChatWindow extends JFrame { private PrimaryTextArea messageEnterTextArea = new PrimaryTextArea(space); private JList userList = new JList<>(); private Chat currentChat; - private JList messageList = new JList<>(); + private ComponentList messageList; private PrimaryScrollPane scrollPane = new PrimaryScrollPane(); private JTextPane textPane = new JTextPane(); private PrimaryButton postButton = new PrimaryButton("Post"); @@ -73,14 +75,12 @@ public class ChatWindow extends JFrame { gbl_contentPane.rowWeights = new double[] { 0.05, 1.0, 0.07 }; contentPane.setLayout(gbl_contentPane); - messageList.setCellRenderer(new MessageListRenderer()); - messageList.setFocusTraversalKeysEnabled(false); - messageList.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); + messageList = new ComponentList<>(new ComponentListModel<>(), new MessageListRenderer()); + // TODO: messageList.setFocusTraversalKeysEnabled(false); + // messageList.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); - DefaultListModel messageListModel = new DefaultListModel<>(); - messageList.setModel(messageListModel); - messageList.setFont(new Font("Arial", Font.PLAIN, 17)); - messageList.setFixedCellHeight(60); + // messageList.setFont(new Font("Arial", Font.PLAIN, 17)); + // messageList.setFixedCellHeight(60); messageList.setBorder(new EmptyBorder(space, space, space, space)); scrollPane.setViewportView(messageList); @@ -101,7 +101,7 @@ public class ChatWindow extends JFrame { public void keyReleased(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER && ((Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0) || (e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK))) - postMessage(messageList); + postMessage(); } }); @@ -123,7 +123,7 @@ public class ChatWindow extends JFrame { gbc_moveSelectionPostButton.insets = new Insets(space, space, space, space); - postButton.addActionListener((evt) -> { postMessage(messageList); }); + postButton.addActionListener((evt) -> { postMessage(); }); contentPane.add(postButton, gbc_moveSelectionPostButton); // Settings Button @@ -210,8 +210,8 @@ public class ChatWindow extends JFrame { contentPane.setBackground(theme.getBackgroundColor()); contentPane.setForeground(theme.getUserNameColor()); // messageList - messageList.setSelectionForeground(theme.getUserNameColor()); - messageList.setSelectionBackground(theme.getSelectionColor()); + // messageList.setSelectionForeground(theme.getUserNameColor()); + // messageList.setSelectionBackground(theme.getSelectionColor()); messageList.setForeground(theme.getMessageColorChat()); messageList.setBackground(theme.getCellColor()); // scrollPane @@ -238,7 +238,7 @@ public class ChatWindow extends JFrame { userList.setBackground(theme.getCellColor()); } - private void postMessage(JList messageList) { + private void postMessage() { if (!client.hasRecipient()) { JOptionPane.showMessageDialog(this, "Please select a recipient!", "Cannot send message", JOptionPane.INFORMATION_MESSAGE); return; diff --git a/src/main/java/envoy/client/ui/MessageListRenderer.java b/src/main/java/envoy/client/ui/MessageListRenderer.java index ed62854..c3bebac 100644 --- a/src/main/java/envoy/client/ui/MessageListRenderer.java +++ b/src/main/java/envoy/client/ui/MessageListRenderer.java @@ -4,10 +4,10 @@ import java.awt.Component; import java.text.SimpleDateFormat; import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.ListCellRenderer; import envoy.client.Settings; +import envoy.client.ui.list.ComponentList; +import envoy.client.ui.list.ComponentListCellRenderer; import envoy.data.Message; /** @@ -21,15 +21,17 @@ import envoy.data.Message; * @author Maximilian Käfer * @since Envoy v0.1-alpha */ -public class MessageListRenderer extends JLabel implements ListCellRenderer { +public class MessageListRenderer extends JLabel implements ComponentListCellRenderer { private static final long serialVersionUID = 5164417379767181198L; @Override - public Component getListCellRendererComponent(JList list, Message value, int index, boolean isSelected, boolean cellHasFocus) { + public Component getListCellComponent(ComponentList list, Message value, boolean isSelected) { if (isSelected) { - setBackground(list.getSelectionBackground()); - setForeground(list.getSelectionForeground()); + setBackground(Color.DARK_GRAY); + setForeground(Color.RED); +// setBackground(list.getSelectionBackground()); +// setForeground(list.getSelectionForeground()); } else { setBackground(list.getBackground()); setForeground(list.getForeground()); diff --git a/src/main/java/envoy/client/ui/list/ComponentList.java b/src/main/java/envoy/client/ui/list/ComponentList.java index 869721c..313838d 100644 --- a/src/main/java/envoy/client/ui/list/ComponentList.java +++ b/src/main/java/envoy/client/ui/list/ComponentList.java @@ -13,20 +13,36 @@ import javax.swing.JPanel; */ public class ComponentList extends JPanel { + private ComponentListModel model; private ComponentListCellRenderer renderer; private static final long serialVersionUID = 1759644503942876737L; public ComponentList(ComponentListModel model, ComponentListCellRenderer renderer) { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); - model.setComponentList(this); - this.renderer = renderer; + this.model = model; + this.renderer = renderer; + setModel(model); + } - for (E elem : model) - add(renderer.getListCellComponent(this, elem, false)); + public void setModel(ComponentListModel model) { + // Remove old model + this.model.clear(); + this.model.setComponentList(null); + + // Synchronize with new model + this.model = model; + this.model.setComponentList(this); + synchronizeModel(); } void add(E elem) { add(renderer.getListCellComponent(this, elem, false)); } + + private void synchronizeModel() { + removeAll(); + for (E elem : model) + add(renderer.getListCellComponent(this, elem, false)); + } } diff --git a/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java b/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java index 6a3b383..95d196d 100644 --- a/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java +++ b/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java @@ -12,5 +12,5 @@ import java.awt.Component; */ public interface ComponentListCellRenderer { - Component getListCellComponent(ComponentList list, E value, boolean isSelected); + Component getListCellComponent(ComponentList list, E value, boolean isSelected); } diff --git a/src/main/java/envoy/client/ui/list/ComponentListModel.java b/src/main/java/envoy/client/ui/list/ComponentListModel.java index e5ea6fe..27db590 100644 --- a/src/main/java/envoy/client/ui/list/ComponentListModel.java +++ b/src/main/java/envoy/client/ui/list/ComponentListModel.java @@ -14,8 +14,8 @@ import java.util.List; */ public final class ComponentListModel implements Iterable { - private List elements = new ArrayList<>(); - private ComponentList componentList; + private List elements = new ArrayList<>(); + private ComponentList componentList; /** * Adds an element to this model and notifies the associated @@ -26,7 +26,7 @@ public final class ComponentListModel implements Iterable { * @see java.util.List#add(java.lang.Object) */ public boolean add(E e) { - componentList.add(e); + if (componentList != null) componentList.add(e); return elements.add(e); } @@ -39,7 +39,7 @@ public final class ComponentListModel implements Iterable { */ public void clear() { elements.clear(); - componentList.removeAll(); + if (componentList != null) componentList.removeAll(); } /** @@ -58,7 +58,7 @@ public final class ComponentListModel implements Iterable { * @see java.util.List#remove(int) */ public E remove(int index) { - componentList.remove(index); + if (componentList != null) componentList.remove(index); return elements.remove(index); } @@ -70,7 +70,7 @@ public final class ComponentListModel implements Iterable { return new Iterator() { Iterator iter = elements.iterator(); - + @Override public boolean hasNext() { return iter.hasNext(); } @@ -79,5 +79,8 @@ public final class ComponentListModel implements Iterable { }; } - void setComponentList(ComponentList componentList) { this.componentList = componentList; } + void setComponentList(ComponentList componentList) { + this.componentList = componentList; + if (componentList != null) elements.forEach(componentList::add); + } } From 0c1c3f6fce94e63ecbf8bd70dfa0bb21f8c7b8e7 Mon Sep 17 00:00:00 2001 From: kske Date: Mon, 27 Jan 2020 07:06:38 +0100 Subject: [PATCH 03/11] Removed src/test/resources folder --- .classpath | 6 ------ pom.xml | 5 +++++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.classpath b/.classpath index 1a0c560..a5d9509 100644 --- a/.classpath +++ b/.classpath @@ -28,11 +28,5 @@ - - - - - - diff --git a/pom.xml b/pom.xml index 01fa149..bf35cbb 100644 --- a/pom.xml +++ b/pom.xml @@ -34,5 +34,10 @@ envoy-client + + + src/main/resources + + \ No newline at end of file From 063f5798dc48659a6f8f06052707e64d6243f846 Mon Sep 17 00:00:00 2001 From: kske Date: Mon, 27 Jan 2020 20:11:47 +0100 Subject: [PATCH 04/11] Implemented ComponentList rendering and integration of message list --- src/main/java/envoy/client/ui/ChatWindow.java | 51 ++++++++++--------- .../envoy/client/ui/MessageListRenderer.java | 32 +++++++----- .../envoy/client/ui/list/ComponentList.java | 27 ++++++---- .../ui/list/ComponentListCellRenderer.java | 6 +-- .../client/ui/list/ComponentListModel.java | 20 +++++--- 5 files changed, 77 insertions(+), 59 deletions(-) diff --git a/src/main/java/envoy/client/ui/ChatWindow.java b/src/main/java/envoy/client/ui/ChatWindow.java index 671deda..3c4cdee 100644 --- a/src/main/java/envoy/client/ui/ChatWindow.java +++ b/src/main/java/envoy/client/ui/ChatWindow.java @@ -12,7 +12,6 @@ import javax.swing.border.EmptyBorder; import envoy.client.*; import envoy.client.event.ThemeChangeEvent; import envoy.client.ui.list.ComponentList; -import envoy.client.ui.list.ComponentListModel; import envoy.client.ui.settings.SettingsScreen; import envoy.client.util.EnvoyLog; import envoy.data.Message; @@ -32,27 +31,29 @@ import envoy.event.EventBus; */ public class ChatWindow extends JFrame { - private static final long serialVersionUID = 6865098428255463649L; - // User specific objects private Client client; private LocalDB localDB; // GUI components - private JPanel contentPane = new JPanel(); - private PrimaryTextArea messageEnterTextArea = new PrimaryTextArea(space); - private JList userList = new JList<>(); - private Chat currentChat; - private ComponentList messageList; - private PrimaryScrollPane scrollPane = new PrimaryScrollPane(); - private JTextPane textPane = new JTextPane(); - private PrimaryButton postButton = new PrimaryButton("Post"); - private PrimaryButton settingsButton = new PrimaryButton("Settings"); - - private static int space = 4; + private JPanel contentPane = new JPanel(); + private PrimaryTextArea messageEnterTextArea = new PrimaryTextArea(space); + private JList userList = new JList<>(); + private Chat currentChat; + private ComponentList messageList = new ComponentList<>(new MessageListRenderer()); + private PrimaryScrollPane scrollPane = new PrimaryScrollPane(); + private JTextPane textPane = new JTextPane(); + private PrimaryButton postButton = new PrimaryButton("Post"); + private PrimaryButton settingsButton = new PrimaryButton("Settings"); private static final Logger logger = EnvoyLog.getLogger(ChatWindow.class.getSimpleName()); + // GUI component spacing + private final static int space = 4; + private static final Insets insets = new Insets(space, space, space, space); + + private static final long serialVersionUID = 6865098428255463649L; + /** * Initializes a {@link JFrame} with UI elements used to send and read messages * to different users. @@ -75,7 +76,6 @@ public class ChatWindow extends JFrame { gbl_contentPane.rowWeights = new double[] { 0.05, 1.0, 0.07 }; contentPane.setLayout(gbl_contentPane); - messageList = new ComponentList<>(new ComponentListModel<>(), new MessageListRenderer()); // TODO: messageList.setFocusTraversalKeysEnabled(false); // messageList.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); @@ -91,7 +91,7 @@ public class ChatWindow extends JFrame { gbc_scrollPane.gridx = 1; gbc_scrollPane.gridy = 1; - gbc_scrollPane.insets = new Insets(space, space, space, space); + gbc_scrollPane.insets = insets; contentPane.add(scrollPane, gbc_scrollPane); // Message enter field @@ -100,7 +100,7 @@ public class ChatWindow extends JFrame { @Override public void keyReleased(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER - && ((Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0) || (e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK))) + && (Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0 || e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK)) postMessage(); } }); @@ -110,7 +110,7 @@ public class ChatWindow extends JFrame { gbc_messageEnterTextfield.gridx = 1; gbc_messageEnterTextfield.gridy = 2; - gbc_messageEnterTextfield.insets = new Insets(space, space, space, space); + gbc_messageEnterTextfield.insets = insets; contentPane.add(messageEnterTextArea, gbc_messageEnterTextfield); @@ -121,7 +121,7 @@ public class ChatWindow extends JFrame { gbc_moveSelectionPostButton.gridx = 2; gbc_moveSelectionPostButton.gridy = 2; - gbc_moveSelectionPostButton.insets = new Insets(space, space, space, space); + gbc_moveSelectionPostButton.insets = insets; postButton.addActionListener((evt) -> { postMessage(); }); contentPane.add(postButton, gbc_moveSelectionPostButton); @@ -133,7 +133,7 @@ public class ChatWindow extends JFrame { gbc_moveSelectionSettingsButton.gridx = 2; gbc_moveSelectionSettingsButton.gridy = 0; - gbc_moveSelectionSettingsButton.insets = new Insets(space, space, space, space); + gbc_moveSelectionSettingsButton.insets = insets; settingsButton.addActionListener((evt) -> { try { @@ -154,7 +154,7 @@ public class ChatWindow extends JFrame { gbc_partnerName.gridx = 1; gbc_partnerName.gridy = 0; - gbc_partnerName.insets = new Insets(space, space, space, space); + gbc_partnerName.insets = insets; contentPane.add(textPane, gbc_partnerName); userList.setCellRenderer(new UserListRenderer()); @@ -187,7 +187,7 @@ public class ChatWindow extends JFrame { gbc_userList.gridx = 0; gbc_userList.gridy = 1; gbc_userList.anchor = GridBagConstraints.PAGE_START; - gbc_userList.insets = new Insets(space, space, space, space); + gbc_userList.insets = insets; applyTheme(Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme())); @@ -252,15 +252,18 @@ public class ChatWindow extends JFrame { .build(); // Send message + // TODO: Store offline messages client.sendMessage(message); // Add message to LocalDB and update UI currentChat.appendMessage(message); - messageList.setModel(currentChat.getModel()); + // messageList.setModel(currentChat.getModel()); // Clear text field messageEnterTextArea.setText(""); - contentPane.revalidate(); + + revalidate(); + repaint(); } catch (Exception e) { JOptionPane.showMessageDialog(this, "An exception occured while sending a message. See the log for more details.", diff --git a/src/main/java/envoy/client/ui/MessageListRenderer.java b/src/main/java/envoy/client/ui/MessageListRenderer.java index c3bebac..51cbbd5 100644 --- a/src/main/java/envoy/client/ui/MessageListRenderer.java +++ b/src/main/java/envoy/client/ui/MessageListRenderer.java @@ -1,9 +1,10 @@ package envoy.client.ui; -import java.awt.Component; import java.text.SimpleDateFormat; +import javax.swing.JComponent; import javax.swing.JLabel; +import javax.swing.border.EmptyBorder; import envoy.client.Settings; import envoy.client.ui.list.ComponentList; @@ -21,23 +22,22 @@ import envoy.data.Message; * @author Maximilian Käfer * @since Envoy v0.1-alpha */ -public class MessageListRenderer extends JLabel implements ComponentListCellRenderer { - - private static final long serialVersionUID = 5164417379767181198L; +public class MessageListRenderer implements ComponentListCellRenderer { @Override - public Component getListCellComponent(ComponentList list, Message value, boolean isSelected) { + public JComponent getListCellComponent(ComponentList list, Message value, boolean isSelected) { + final JLabel label = new JLabel(); if (isSelected) { - setBackground(Color.DARK_GRAY); - setForeground(Color.RED); -// setBackground(list.getSelectionBackground()); -// setForeground(list.getSelectionForeground()); + label.setBackground(Color.DARK_GRAY); + label.setForeground(Color.RED); + // setBackground(list.getSelectionBackground()); + // setForeground(list.getSelectionForeground()); } else { - setBackground(list.getBackground()); - setForeground(list.getForeground()); + label.setBackground(list.getBackground()); + label.setForeground(list.getForeground()); } - setOpaque(true); + label.setOpaque(true); // TODO: Handle message attachments @@ -51,12 +51,16 @@ public class MessageListRenderer extends JLabel implements ComponentListCellRend // Getting the DateColor in the Chat of the current theme String dateColor = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getDateColorChat().toHex(); - setText(String.format("

%s

%s :%s", + label.setText(String.format("

%s

%s :%s", dateColor, date, textColor, text, state)); - return this; + + // Define some space to the components above and below + label.setBorder(new EmptyBorder(0, 0, 15, 0)); + + return label; } } \ No newline at end of file diff --git a/src/main/java/envoy/client/ui/list/ComponentList.java b/src/main/java/envoy/client/ui/list/ComponentList.java index 313838d..7cca24a 100644 --- a/src/main/java/envoy/client/ui/list/ComponentList.java +++ b/src/main/java/envoy/client/ui/list/ComponentList.java @@ -7,28 +7,34 @@ import javax.swing.JPanel; * Project: envoy-client
* File: ComponentList.java
* Created: 25.01.2020
- * + * * @author Kai S. K. Engelbart * @since Envoy v0.3-alpha */ public class ComponentList extends JPanel { private ComponentListModel model; - private ComponentListCellRenderer renderer; + private ComponentListCellRenderer renderer; private static final long serialVersionUID = 1759644503942876737L; - public ComponentList(ComponentListModel model, ComponentListCellRenderer renderer) { + public ComponentList(ComponentListCellRenderer renderer) { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); - this.model = model; - this.renderer = renderer; + this.renderer = renderer; + } + + public ComponentList(ComponentListModel model, ComponentListCellRenderer renderer) { + this(renderer); + this.model = model; setModel(model); } public void setModel(ComponentListModel model) { // Remove old model - this.model.clear(); - this.model.setComponentList(null); + if (this.model != null) { + this.model.clear(); + this.model.setComponentList(null); + } // Synchronize with new model this.model = model; @@ -40,9 +46,10 @@ public class ComponentList extends JPanel { add(renderer.getListCellComponent(this, elem, false)); } - private void synchronizeModel() { + void synchronizeModel() { removeAll(); - for (E elem : model) - add(renderer.getListCellComponent(this, elem, false)); + if (model != null) + for (E elem : model) + add(renderer.getListCellComponent(this, elem, false)); } } diff --git a/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java b/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java index 95d196d..7fdab1b 100644 --- a/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java +++ b/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java @@ -1,16 +1,16 @@ package envoy.client.ui.list; -import java.awt.Component; +import javax.swing.JComponent; /** * Project: envoy-client
* File: ComponentListCellRenderer.java
* Created: 25.01.2020
- * + * * @author Kai S. K. Engelbart * @since Envoy v0.3-alpha */ public interface ComponentListCellRenderer { - Component getListCellComponent(ComponentList list, E value, boolean isSelected); + JComponent getListCellComponent(ComponentList list, E value, boolean isSelected); } diff --git a/src/main/java/envoy/client/ui/list/ComponentListModel.java b/src/main/java/envoy/client/ui/list/ComponentListModel.java index 27db590..e4b0594 100644 --- a/src/main/java/envoy/client/ui/list/ComponentListModel.java +++ b/src/main/java/envoy/client/ui/list/ComponentListModel.java @@ -1,5 +1,6 @@ package envoy.client.ui.list; +import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -8,19 +9,21 @@ import java.util.List; * Project: envoy-client
* File: ComponentListModel.java
* Created: 25.01.2020
- * + * * @author Kai S. K. Engelbart * @since Envoy v0.3-alpha */ -public final class ComponentListModel implements Iterable { +public final class ComponentListModel implements Iterable, Serializable { - private List elements = new ArrayList<>(); - private ComponentList componentList; + private List elements = new ArrayList<>(); + transient private ComponentList componentList; + + private static final long serialVersionUID = 4815005915255497331L; /** * Adds an element to this model and notifies the associated * {@link ComponentList} to add the corresponding component. - * + * * @param e the element to add * @return {@code true} * @see java.util.List#add(java.lang.Object) @@ -33,7 +36,7 @@ public final class ComponentListModel implements Iterable { /** * Removes all elements from this model and clears the associated * {@link ComponentList}. - * + * * @see java.util.List#clear() * @since Envoy v0.3-alpha */ @@ -52,7 +55,7 @@ public final class ComponentListModel implements Iterable { /** * Removes the element at a specific index from this model and the corresponding * component from the {@link ComponentList}. - * + * * @param index * @return the removed element * @see java.util.List#remove(int) @@ -66,6 +69,7 @@ public final class ComponentListModel implements Iterable { * @return * @see java.util.List#iterator() */ + @Override public Iterator iterator() { return new Iterator() { @@ -81,6 +85,6 @@ public final class ComponentListModel implements Iterable { void setComponentList(ComponentList componentList) { this.componentList = componentList; - if (componentList != null) elements.forEach(componentList::add); + if (componentList != null) componentList.synchronizeModel(); } } From 5ecda78cf12edda2997e226369b9c748c7805acd Mon Sep 17 00:00:00 2001 From: kske Date: Mon, 27 Jan 2020 20:23:30 +0100 Subject: [PATCH 05/11] Changed message list components to JPanels --- .../envoy/client/ui/MessageListRenderer.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/java/envoy/client/ui/MessageListRenderer.java b/src/main/java/envoy/client/ui/MessageListRenderer.java index 51cbbd5..11312d1 100644 --- a/src/main/java/envoy/client/ui/MessageListRenderer.java +++ b/src/main/java/envoy/client/ui/MessageListRenderer.java @@ -4,6 +4,7 @@ import java.text.SimpleDateFormat; import javax.swing.JComponent; import javax.swing.JLabel; +import javax.swing.JPanel; import javax.swing.border.EmptyBorder; import envoy.client.Settings; @@ -26,19 +27,17 @@ public class MessageListRenderer implements ComponentListCellRenderer { @Override public JComponent getListCellComponent(ComponentList list, Message value, boolean isSelected) { - final JLabel label = new JLabel(); + final JPanel panel = new JPanel(); if (isSelected) { - label.setBackground(Color.DARK_GRAY); - label.setForeground(Color.RED); + panel.setBackground(Color.DARK_GRAY); + panel.setForeground(Color.RED); // setBackground(list.getSelectionBackground()); // setForeground(list.getSelectionForeground()); } else { - label.setBackground(list.getBackground()); - label.setForeground(list.getForeground()); + panel.setBackground(list.getBackground()); + panel.setForeground(list.getForeground()); } - label.setOpaque(true); - // TODO: Handle message attachments final String text = value.getText(); @@ -51,16 +50,18 @@ public class MessageListRenderer implements ComponentListCellRenderer { // Getting the DateColor in the Chat of the current theme String dateColor = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getDateColorChat().toHex(); - label.setText(String.format("

%s

%s :%s", + //JLabel textLabel = new JLabel(text + ": " + state); + + panel.add(new JLabel(String.format("

%s

%s :%s", dateColor, date, textColor, text, - state)); + state))); // Define some space to the components above and below - label.setBorder(new EmptyBorder(0, 0, 15, 0)); + panel.setBorder(new EmptyBorder(0, 0, 15, 0)); - return label; + return panel; } } \ No newline at end of file From cd5030735a71dac1ea1384cc29e394ce078d1991 Mon Sep 17 00:00:00 2001 From: kske Date: Mon, 27 Jan 2020 21:01:38 +0100 Subject: [PATCH 06/11] Added Javadoc for the ui.list package --- .../envoy/client/ui/list/ComponentList.java | 48 ++++++++++++++++--- .../ui/list/ComponentListCellRenderer.java | 14 ++++++ .../client/ui/list/ComponentListModel.java | 22 +++++++-- 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/src/main/java/envoy/client/ui/list/ComponentList.java b/src/main/java/envoy/client/ui/list/ComponentList.java index 7cca24a..47f8c31 100644 --- a/src/main/java/envoy/client/ui/list/ComponentList.java +++ b/src/main/java/envoy/client/ui/list/ComponentList.java @@ -4,10 +4,15 @@ import javax.swing.BoxLayout; import javax.swing.JPanel; /** + * Provides a vertical list layout of components provided in a + * {@link ComponentListModel}. Similar to {@link javax.swing.JList} but capable + * of rendering {@link JPanel}s.
+ *
* Project: envoy-client
* File: ComponentList.java
* Created: 25.01.2020
* + * @param the type of object displayed in this list * @author Kai S. K. Engelbart * @since Envoy v0.3-alpha */ @@ -18,17 +23,38 @@ public class ComponentList extends JPanel { private static final long serialVersionUID = 1759644503942876737L; + /** + * Creates an instance of {@link ComponentList}. + * + * @param renderer the list cell renderer used to display elements provided by + * the {@link ComponentListModel} + * @since Envoy v0.3-alpha + */ public ComponentList(ComponentListCellRenderer renderer) { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); this.renderer = renderer; } + /** + * Creates an instance of {@link ComponentList}. + * + * @param model the list model providing the list elements to render + * @param renderer the list cell renderer used to display elements provided by + * the {@link ComponentListModel} + * @since Envoy v0.3-alpha + */ public ComponentList(ComponentListModel model, ComponentListCellRenderer renderer) { this(renderer); this.model = model; setModel(model); } + /** + * Sets the list model providing the list elements to render + * + * @param model the list model to set + * @since Envoy v0.3-alpha + */ public void setModel(ComponentListModel model) { // Remove old model if (this.model != null) { @@ -42,14 +68,24 @@ public class ComponentList extends JPanel { synchronizeModel(); } - void add(E elem) { - add(renderer.getListCellComponent(this, elem, false)); - } + /** + * Adds an object to the list by rendering it with the current + * {@link ComponentListCellRenderer}. + * + * @param elem the element to add + * @since Envoy v0.3-alpha + */ + void add(E elem) { add(renderer.getListCellComponent(this, elem, false)); } + /** + * Removes all child components and then adds all components representing the + * elements of the {@link ComponentListModel}. + * + * @since Envoy v0.3-alpha + */ void synchronizeModel() { removeAll(); - if (model != null) - for (E elem : model) - add(renderer.getListCellComponent(this, elem, false)); + if (model != null) for (E elem : model) + add(renderer.getListCellComponent(this, elem, false)); } } diff --git a/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java b/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java index 7fdab1b..a8bdda6 100644 --- a/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java +++ b/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java @@ -3,14 +3,28 @@ package envoy.client.ui.list; import javax.swing.JComponent; /** + * Allows a {@link ComponentList} convert its elements into Swing components + * that can be rendered.
+ *
* Project: envoy-client
* File: ComponentListCellRenderer.java
* Created: 25.01.2020
* + * @param the type of object displayed in this list * @author Kai S. K. Engelbart * @since Envoy v0.3-alpha */ public interface ComponentListCellRenderer { + /** + * Provides a Swing component representing a list element. + * + * @param list the list in which the component will be displayed + * @param value the list element that will be converted + * @param isSelected {@code true} if the user has selected the list cell in + * which the list element is rendered + * @return the component representing the list element + * @since Envoy v0.3-alpha + */ JComponent getListCellComponent(ComponentList list, E value, boolean isSelected); } diff --git a/src/main/java/envoy/client/ui/list/ComponentListModel.java b/src/main/java/envoy/client/ui/list/ComponentListModel.java index e4b0594..1845651 100644 --- a/src/main/java/envoy/client/ui/list/ComponentListModel.java +++ b/src/main/java/envoy/client/ui/list/ComponentListModel.java @@ -6,10 +6,13 @@ import java.util.Iterator; import java.util.List; /** + * Stores objects that will be displayed in a {@link ComponentList}.
+ *
* Project: envoy-client
* File: ComponentListModel.java
* Created: 25.01.2020
* + * @param the type of object displayed in this list * @author Kai S. K. Engelbart * @since Envoy v0.3-alpha */ @@ -27,6 +30,7 @@ public final class ComponentListModel implements Iterable, Serializable { * @param e the element to add * @return {@code true} * @see java.util.List#add(java.lang.Object) + * @since Envoy v0.3-alpha */ public boolean add(E e) { if (componentList != null) componentList.add(e); @@ -46,9 +50,10 @@ public final class ComponentListModel implements Iterable, Serializable { } /** - * @param index - * @return + * @param index the index to retrieve the element from + * @return the element located at the index * @see java.util.List#get(int) + * @since Envoy v0.3-alpha */ public E get(int index) { return elements.get(index); } @@ -56,9 +61,10 @@ public final class ComponentListModel implements Iterable, Serializable { * Removes the element at a specific index from this model and the corresponding * component from the {@link ComponentList}. * - * @param index + * @param index the index of the element to remove * @return the removed element * @see java.util.List#remove(int) + * @since Envoy v0.3-alpha */ public E remove(int index) { if (componentList != null) componentList.remove(index); @@ -66,8 +72,9 @@ public final class ComponentListModel implements Iterable, Serializable { } /** - * @return + * @return an iterator over the elements of this list model * @see java.util.List#iterator() + * @since Envoy v0.3-alpha */ @Override public Iterator iterator() { @@ -83,6 +90,13 @@ public final class ComponentListModel implements Iterable, Serializable { }; } + /** + * Sets the component list displaying the elements of this model and triggers a + * synchronization. + * + * @param componentList the component list to set + * @since Envoy v0.3-alpha + */ void setComponentList(ComponentList componentList) { this.componentList = componentList; if (componentList != null) componentList.synchronizeModel(); From c0f4e5d270ad29c3d52bb01bbc9c826e7cd7d7ba Mon Sep 17 00:00:00 2001 From: kske Date: Wed, 29 Jan 2020 07:44:25 +0100 Subject: [PATCH 07/11] Implemented message ID generation and generator requests * Persisting IdGenerator in LocalDB * Added Client#requestIdGenerator() * Listening to MessageCreationEvents in ChatWindow --- pom.xml | 2 +- src/main/java/envoy/client/Client.java | 33 ++++++++++-- src/main/java/envoy/client/LocalDB.java | 53 ++++++++++++++++--- src/main/java/envoy/client/ui/ChatWindow.java | 25 +++++++-- src/main/java/envoy/client/ui/Startup.java | 5 +- 5 files changed, 100 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index bf35cbb..0b5487d 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ com.github.informatik-ag-ngl envoy-common - e5c67b8 + develop-SNAPSHOT diff --git a/src/main/java/envoy/client/Client.java b/src/main/java/envoy/client/Client.java index 4885a92..935669f 100644 --- a/src/main/java/envoy/client/Client.java +++ b/src/main/java/envoy/client/Client.java @@ -11,6 +11,7 @@ import javax.naming.TimeLimitExceededException; import envoy.client.util.EnvoyLog; import envoy.data.*; +import envoy.event.IdGeneratorRequest; import envoy.util.SerializationUtils; /** @@ -45,11 +46,13 @@ public class Client implements Closeable { * an exception is thrown. * * @param credentials the login credentials of the user + * @param localDB the local database used to persist the current + * {@link IdGenerator} * @throws Exception if the online mode could not be entered or the request * failed for some other reason * @since Envoy v0.2-alpha */ - public void onlineInit(LoginCredentials credentials) throws Exception { + public void onlineInit(LoginCredentials credentials, LocalDB localDB) throws Exception { // Establish TCP connection logger.info(String.format("Attempting connection to server %s:%d...", config.getServer(), config.getPort())); socket = new Socket(config.getServer(), config.getPort()); @@ -84,21 +87,38 @@ public class Client implements Closeable { // Register processors for message and status handling receiver.registerProcessor(Message.class, new ReceivedMessageProcessor()); + // TODO: Status handling + + // Process message ID generation + receiver.registerProcessor(IdGenerator.class, localDB::setIdGenerator); + + // Request a generator if none is present + if (!localDB.hasIdGenerator() || !localDB.getIdGenerator().hasNext()) requestIdGenerator(); } /** * Sends a message to the server. - * + * * @param message the message to send * @throws IOException if the message does not reach the server * @since Envoy v0.3-alpha */ public void sendMessage(Message message) throws IOException { - checkOnline(); - SerializationUtils.writeBytesWithLength(message, socket.getOutputStream()); + writeObject(message); message.nextStatus(); } + /** + * Requests a new {@link IdGenerator} from the server. + * + * @throws IOException if the request does not reach the server + * @since Envoy v0.3-alpha + */ + public void requestIdGenerator() throws IOException { + logger.info("Requesting new id generator..."); + writeObject(new IdGeneratorRequest()); + } + /** * @return a {@code Map} of all users on the server with their * user names as keys @@ -114,6 +134,11 @@ public class Client implements Closeable { @Override public void close() throws IOException { if (online) socket.close(); } + private void writeObject(Object obj) throws IOException { + checkOnline(); + SerializationUtils.writeBytesWithLength(obj, socket.getOutputStream()); + } + private void checkOnline() { if (!online) throw new IllegalStateException("Client is not online"); } /** diff --git a/src/main/java/envoy/client/LocalDB.java b/src/main/java/envoy/client/LocalDB.java index 2c40e65..7511d41 100644 --- a/src/main/java/envoy/client/LocalDB.java +++ b/src/main/java/envoy/client/LocalDB.java @@ -4,10 +4,15 @@ import java.io.File; import java.io.IOException; import java.util.*; +import envoy.data.IdGenerator; import envoy.data.User; import envoy.util.SerializationUtils; /** + * Stored information about the current {@link User} and their {@link Chat}s. + * For message ID generation a {@link IdGenerator} is stored as well. + * These object are persisted inside a folder of the local file system.
+ *
* Project: envoy-client
* File: LocalDB.java
* Created: 27.10.2019
@@ -18,10 +23,11 @@ import envoy.util.SerializationUtils; */ public class LocalDB { - private File localDBDir, localDBFile, usersFile; + private File localDBDir, localDBFile, usersFile, idGeneratorFile; private User user; private Map users = new HashMap<>(); private List chats = new ArrayList<>(); + private IdGenerator idGenerator; /** * Constructs an empty local database. To serialize any chats to the file @@ -37,7 +43,8 @@ public class LocalDB { // Initialize local database directory if (localDBDir.exists() && !localDBDir.isDirectory()) throw new IOException(String.format("LocalDBDir '%s' is not a directory!", localDBDir.getAbsolutePath())); - usersFile = new File(localDBDir, "users.db"); + usersFile = new File(localDBDir, "users.db"); + idGeneratorFile = new File(localDBDir, "id_generator.db"); } /** @@ -53,7 +60,8 @@ public class LocalDB { /** * Stores all users to the local database. If the client user is specified, the - * chats related to this user are stored as well. + * chats related to this user are stored as well. The message id generator will + * also be saved if present. * * @throws IOException if something went wrong during saving * @since Envoy v0.1-alpha @@ -63,12 +71,15 @@ public class LocalDB { SerializationUtils.write(usersFile, users); // Save chats - SerializationUtils.write(localDBFile, chats); + if (user != null) SerializationUtils.write(localDBFile, chats); + + // Save id generator + if (hasIdGenerator()) SerializationUtils.write(idGeneratorFile, idGenerator); } /** * Loads all users that are stored in the local database. - * + * * @throws IOException if the loading process failed * @throws ClassNotFoundException if the loading process failed * @since Envoy v0.2-alpha @@ -77,13 +88,25 @@ public class LocalDB { /** * Loads all chats saved by Envoy for the client user. - * + * * @throws IOException if the loading process failed * @throws ClassNotFoundException if the loading process failed * @since Envoy v0.1-alpha */ public void loadChats() throws ClassNotFoundException, IOException { chats = SerializationUtils.read(localDBFile, ArrayList.class); } + /** + * Loads the message ID generator that is stored in the local database. If the + * file is not found, the exception is ignored. + * + * @since Envoy v0.3-alpha + */ + public void loadIdGenerator() { + try { + idGenerator = SerializationUtils.read(idGeneratorFile, IdGenerator.class); + } catch (ClassNotFoundException | IOException e) {} + } + /** * @return a {@code Map} of all users stored locally with their * user names as keys @@ -119,4 +142,22 @@ public class LocalDB { * @since Envoy v0.2-alpha */ public void setUser(User user) { this.user = user; } + + /** + * @return the message ID generator + * @since Envoy v0.3-alpha + */ + public IdGenerator getIdGenerator() { return idGenerator; } + + /** + * @param idGenerator the message ID generator to set + * @since Envoy v0.3-alpha + */ + public void setIdGenerator(IdGenerator idGenerator) { this.idGenerator = idGenerator; } + + /** + * @return {@code true} if an {@link IdGenerator} is present + * @since Envoy v0.3-alpha + */ + public boolean hasIdGenerator() { return idGenerator != null; } } \ No newline at end of file diff --git a/src/main/java/envoy/client/ui/ChatWindow.java b/src/main/java/envoy/client/ui/ChatWindow.java index 3c4cdee..51c7d7f 100644 --- a/src/main/java/envoy/client/ui/ChatWindow.java +++ b/src/main/java/envoy/client/ui/ChatWindow.java @@ -10,6 +10,7 @@ import javax.swing.*; import javax.swing.border.EmptyBorder; import envoy.client.*; +import envoy.client.event.MessageCreationEvent; import envoy.client.event.ThemeChangeEvent; import envoy.client.ui.list.ComponentList; import envoy.client.ui.settings.SettingsScreen; @@ -175,7 +176,9 @@ public class ChatWindow extends JFrame { messageList.setModel(currentChat.getModel()); scrollPane.setChatOpened(true); - contentPane.revalidate(); + + revalidate(); + repaint(); } }); @@ -194,13 +197,20 @@ public class ChatWindow extends JFrame { contentPane.add(userList, gbc_userList); contentPane.revalidate(); + // Listen to theme changes EventBus.getInstance().register(ThemeChangeEvent.class, (evt) -> applyTheme((Theme) evt.get())); + // Listen to received messages + EventBus.getInstance().register(MessageCreationEvent.class, (evt) -> { + Message message = ((MessageCreationEvent) evt).get(); + localDB.getChats().stream().filter(c -> c.getRecipient().getId() == message.getRecipientId()).findFirst().get().appendMessage(message); + }); + contentPane.revalidate(); } /** - * Used to immediately reload the ChatWindow when settings were changed. + * Used to immediately reload the {@link ChatWindow} when settings were changed. * * @param theme the theme to change colors into * @since Envoy v0.2-alpha @@ -247,7 +257,7 @@ public class ChatWindow extends JFrame { if (!messageEnterTextArea.getText().isEmpty()) try { // Create message - final Message message = new MessageBuilder(localDB.getUser().getId(), currentChat.getRecipient().getId()) + final Message message = new MessageBuilder(localDB.getUser().getId(), currentChat.getRecipient().getId(), localDB.getIdGenerator()) .setText(messageEnterTextArea.getText()) .build(); @@ -262,12 +272,17 @@ public class ChatWindow extends JFrame { // Clear text field messageEnterTextArea.setText(""); + // Update UI revalidate(); repaint(); + + // Request a new id generator if all ids were used + if (!localDB.getIdGenerator().hasNext()) client.requestIdGenerator(); + } catch (Exception e) { JOptionPane.showMessageDialog(this, - "An exception occured while sending a message. See the log for more details.", - "Exception occured", + "Error sending message:\n" + e.toString(), + "Message sending error", JOptionPane.ERROR_MESSAGE); e.printStackTrace(); } diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/ui/Startup.java index 3e4692d..2fb9a3a 100644 --- a/src/main/java/envoy/client/ui/Startup.java +++ b/src/main/java/envoy/client/ui/Startup.java @@ -62,7 +62,7 @@ public class Startup { if (!config.isInitialized()) throw new EnvoyException("Server or port are not defined"); } catch (Exception e) { JOptionPane - .showMessageDialog(null, "Error loading configuration values: \n" + e.toString(), "Configuration error", JOptionPane.ERROR_MESSAGE); + .showMessageDialog(null, "Error loading configuration values:\n" + e.toString(), "Configuration error", JOptionPane.ERROR_MESSAGE); System.exit(1); e.printStackTrace(); } @@ -97,7 +97,8 @@ public class Startup { Client client = new Client(); try { // Try entering online mode first - client.onlineInit(credentials); + localDB.loadIdGenerator(); + client.onlineInit(credentials, localDB); } catch (Exception e1) { logger.warning("Could not connect to server. Trying offline mode..."); e1.printStackTrace(); From 8e449d150b08d2bc94b9f260f12cafb2abb5a013 Mon Sep 17 00:00:00 2001 From: kske Date: Sat, 1 Feb 2020 10:20:06 +0100 Subject: [PATCH 08/11] Removed old sync thread, implemented chat reading --- src/main/java/envoy/client/Chat.java | 29 ++++++--- src/main/java/envoy/client/Config.java | 9 +-- src/main/java/envoy/client/Settings.java | 4 +- src/main/java/envoy/client/ui/ChatWindow.java | 60 ++----------------- src/main/java/envoy/client/ui/Startup.java | 2 +- .../client/ui/list/ComponentListModel.java | 7 +++ 6 files changed, 37 insertions(+), 74 deletions(-) diff --git a/src/main/java/envoy/client/Chat.java b/src/main/java/envoy/client/Chat.java index 20513e4..a00e46d 100644 --- a/src/main/java/envoy/client/Chat.java +++ b/src/main/java/envoy/client/Chat.java @@ -4,6 +4,7 @@ import java.io.Serializable; import envoy.client.ui.list.ComponentListModel; import envoy.data.Message; +import envoy.data.Message.MessageStatus; import envoy.data.User; /** @@ -36,22 +37,34 @@ public class Chat implements Serializable { public Chat(User recipient) { this.recipient = recipient; } /** - * @return the recipient of a message - * @since Envoy v0.1-alpha - */ - public User getRecipient() { return recipient; } - - /** - * Adds the received message at the current Point in the current chat + * Appends a message to the bottom of this chat * - * @param message the message to add in said chat + * @param message the message to append * @since Envoy v0.1-alpha */ public void appendMessage(Message message) { model.add(message); } + /** + * Sets the status of all chat messages to {@code READ} starting from the bottom + * and stopping once a read message is found. + * + * @since Envoy v0.3-alpha + */ + public void read() { + for (int i = model.size() - 1; i >= 0; --i) + if (model.get(i).getStatus() == MessageStatus.READ) break; + else model.get(i).setStatus(MessageStatus.READ); + } + /** * @return all messages in the current chat * @since Envoy v0.1-alpha */ public ComponentListModel getModel() { return model; } + + /** + * @return the recipient of a message + * @since Envoy v0.1-alpha + */ + public User getRecipient() { return recipient; } } \ 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 9a2c08b..fc93a08 100644 --- a/src/main/java/envoy/client/Config.java +++ b/src/main/java/envoy/client/Config.java @@ -31,7 +31,6 @@ public class Config { items.put("server", new ConfigItem<>("server", "s", (input) -> input, null)); items.put("port", new ConfigItem<>("port", "p", (input) -> Integer.parseInt(input), null)); items.put("localDB", new ConfigItem<>("localDB", "db", (input) -> new File(input), new File("localDB"))); - items.put("syncTimeout", new ConfigItem<>("syncTimeout", "st", (input) -> Integer.parseInt(input), 1000)); items.put("homeDirectory", new ConfigItem<>("homeDirectory", "h", (input) -> new File(input), new File(System.getProperty("user.home"), ".envoy"))); items.put("fileLevelBarrier", new ConfigItem<>("fileLevelBarrier", "fb", (input) -> Level.parse(input), Level.CONFIG)); @@ -112,7 +111,7 @@ public class Config { * @return the port at which the Envoy server is located on the host * @since Envoy v0.1-alpha */ - public int getPort() { return (int) items.get("port").get(); } + public Integer getPort() { return (Integer) items.get("port").get(); } /** * @return the local database specific to the client user @@ -120,12 +119,6 @@ public class Config { */ public File getLocalDB() { return (File) items.get("localDB").get(); } - /** - * @return the current time (milliseconds) that is waited between Syncs - * @since Envoy v0.1-alpha - */ - public int getSyncTimeout() { return (int) items.get("syncTimeout").get(); } - /** * @return the directory in which all local files are saves * @since Envoy v0.2-alpha diff --git a/src/main/java/envoy/client/Settings.java b/src/main/java/envoy/client/Settings.java index ef451d0..02b6c88 100644 --- a/src/main/java/envoy/client/Settings.java +++ b/src/main/java/envoy/client/Settings.java @@ -135,7 +135,7 @@ public class Settings { * {@code Control} key. * @since Envoy v0.2-alpha */ - public boolean isEnterToSend() { return (boolean) items.get("enterToSend").get(); } + public Boolean isEnterToSend() { return (Boolean) items.get("enterToSend").get(); } /** * Changes the keystrokes performed by the user to send a message. @@ -152,7 +152,7 @@ public class Settings { * @return the current on close mode. * @since Envoy v0.3-alpha */ - public boolean getCurrentOnCloseMode() { return (boolean) items.get("onCloseMode").get(); } + public Boolean getCurrentOnCloseMode() { return (Boolean) items.get("onCloseMode").get(); } /** * Sets the current on close mode. diff --git a/src/main/java/envoy/client/ui/ChatWindow.java b/src/main/java/envoy/client/ui/ChatWindow.java index 51c7d7f..30ea1ea 100644 --- a/src/main/java/envoy/client/ui/ChatWindow.java +++ b/src/main/java/envoy/client/ui/ChatWindow.java @@ -169,7 +169,7 @@ public class ChatWindow extends JFrame { currentChat = localDB.getChats().stream().filter(chat -> chat.getRecipient().getId() == user.getId()).findFirst().get(); // Set all unread messages in the chat to read - readCurrentChat(); + currentChat.read(); client.setRecipient(user); textPane.setText(currentChat.getRecipient().getName()); @@ -204,9 +204,11 @@ public class ChatWindow extends JFrame { EventBus.getInstance().register(MessageCreationEvent.class, (evt) -> { Message message = ((MessageCreationEvent) evt).get(); localDB.getChats().stream().filter(c -> c.getRecipient().getId() == message.getRecipientId()).findFirst().get().appendMessage(message); + revalidate(); + repaint(); }); - contentPane.revalidate(); + revalidate(); } /** @@ -309,64 +311,13 @@ public class ChatWindow extends JFrame { } /** - * Updates the data model and the UI repeatedly after a certain amount of - * time. - * - * @param timeout the amount of time that passes between two requests sent to - * the server - * @since Envoy v0.1-alpha - */ - private void startSyncThread(int timeout) { - new Timer(timeout, (evt) -> { - new Thread(() -> { - - // Synchronize - try { - // localDB.applySync(client.sendSync(client.getSender().getId(), - // localDB.fillSync(client.getSender().getId()))); - } catch (Exception e) { - logger.log(Level.SEVERE, "Could not perform sync", e); - } - - // TODO: Process unread messages - // localDB.addUnreadMessagesToLocalDB(); - // localDB.clearUnreadMessagesSync(); - - // Mark unread messages as read when they are in the current chat - readCurrentChat(); - - // Update UI - SwingUtilities.invokeLater(() -> { updateUserStates(); contentPane.revalidate(); contentPane.repaint(); }); - }).start(); - }).start(); - } - - private void updateUserStates() { - for (int i = 0; i < userList.getModel().getSize(); i++) - for (int j = 0; j < localDB.getChats().size(); j++) - if (userList.getModel().getElementAt(i).getId() == localDB.getChats().get(j).getRecipient().getId()) - userList.getModel().getElementAt(i).setStatus(localDB.getChats().get(j).getRecipient().getStatus()); - } - - /** - * Marks messages in the current chat as {@code READ}. - */ - private void readCurrentChat() { - if (currentChat != null) { - // TODO: localDB.setMessagesToRead(currentChat); - } - } - - /** - * Sets the {@link Client} used by this {@link ChatWindow}. If the client is - * online, the sync thread is started. + * Sets the {@link Client} used by this {@link ChatWindow}. * * @param client the {@link Client} used to send and receive messages * @since Envoy v0.2-alpha */ public void setClient(Client client) { this.client = client; - if (client.isOnline() && localDB != null) startSyncThread(Config.getInstance().getSyncTimeout()); } /** @@ -379,6 +330,5 @@ public class ChatWindow extends JFrame { public void setLocalDB(LocalDB localDB) { this.localDB = localDB; loadUsersAndChats(); - if (client != null && client.isOnline()) startSyncThread(Config.getInstance().getSyncTimeout()); } } diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/ui/Startup.java index 2fb9a3a..949b464 100644 --- a/src/main/java/envoy/client/ui/Startup.java +++ b/src/main/java/envoy/client/ui/Startup.java @@ -153,7 +153,7 @@ public class Startup { .getItems() .get("onCloseMode") .setChangeHandler((onCloseMode) -> chatWindow - .setDefaultCloseOperation((boolean) onCloseMode ? JFrame.HIDE_ON_CLOSE : JFrame.EXIT_ON_CLOSE)); + .setDefaultCloseOperation((Boolean) onCloseMode ? JFrame.HIDE_ON_CLOSE : JFrame.EXIT_ON_CLOSE)); } catch (EnvoyException e) { logger.warning("The StatusTrayIcon is not supported on this platform!"); } diff --git a/src/main/java/envoy/client/ui/list/ComponentListModel.java b/src/main/java/envoy/client/ui/list/ComponentListModel.java index 1845651..8fb1d68 100644 --- a/src/main/java/envoy/client/ui/list/ComponentListModel.java +++ b/src/main/java/envoy/client/ui/list/ComponentListModel.java @@ -71,6 +71,13 @@ public final class ComponentListModel implements Iterable, Serializable { return elements.remove(index); } + /** + * @return the amount of elements in this list model + * @see java.util.List#size() + * @since Envoy v0.3-alpha + */ + public int size() { return elements.size(); } + /** * @return an iterator over the elements of this list model * @see java.util.List#iterator() From 2fb419b0e7577351af3666e2ad8f56bb3adca6ce Mon Sep 17 00:00:00 2001 From: kske Date: Sat, 1 Feb 2020 10:41:59 +0100 Subject: [PATCH 09/11] Fixed receiving messages --- src/main/java/envoy/client/ui/ChatWindow.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/envoy/client/ui/ChatWindow.java b/src/main/java/envoy/client/ui/ChatWindow.java index 30ea1ea..da64632 100644 --- a/src/main/java/envoy/client/ui/ChatWindow.java +++ b/src/main/java/envoy/client/ui/ChatWindow.java @@ -203,7 +203,7 @@ public class ChatWindow extends JFrame { // Listen to received messages EventBus.getInstance().register(MessageCreationEvent.class, (evt) -> { Message message = ((MessageCreationEvent) evt).get(); - localDB.getChats().stream().filter(c -> c.getRecipient().getId() == message.getRecipientId()).findFirst().get().appendMessage(message); + localDB.getChats().stream().filter(c -> c.getRecipient().getId() == message.getSenderId()).findFirst().get().appendMessage(message); revalidate(); repaint(); }); From 07b5ee841a31c8995623b32502910b7ab695d312 Mon Sep 17 00:00:00 2001 From: kske Date: Sat, 1 Feb 2020 11:34:57 +0100 Subject: [PATCH 10/11] Fixed spacing in ComponentList --- src/main/java/envoy/client/ui/ChatWindow.java | 5 ++++- .../envoy/client/ui/list/ComponentList.java | 18 +++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/main/java/envoy/client/ui/ChatWindow.java b/src/main/java/envoy/client/ui/ChatWindow.java index da64632..ab9e968 100644 --- a/src/main/java/envoy/client/ui/ChatWindow.java +++ b/src/main/java/envoy/client/ui/ChatWindow.java @@ -166,14 +166,17 @@ public class ChatWindow extends JFrame { final JList selectedUserList = (JList) listSelectionEvent.getSource(); final User user = selectedUserList.getSelectedValue(); + // Select current chat currentChat = localDB.getChats().stream().filter(chat -> chat.getRecipient().getId() == user.getId()).findFirst().get(); - // Set all unread messages in the chat to read + // Read current Chat currentChat.read(); + // Set recipient in client and chat title client.setRecipient(user); textPane.setText(currentChat.getRecipient().getName()); + // Update model and scroll down messageList.setModel(currentChat.getModel()); scrollPane.setChatOpened(true); diff --git a/src/main/java/envoy/client/ui/list/ComponentList.java b/src/main/java/envoy/client/ui/list/ComponentList.java index 47f8c31..5b2ea45 100644 --- a/src/main/java/envoy/client/ui/list/ComponentList.java +++ b/src/main/java/envoy/client/ui/list/ComponentList.java @@ -1,6 +1,9 @@ package envoy.client.ui.list; +import java.awt.Dimension; + import javax.swing.BoxLayout; +import javax.swing.JComponent; import javax.swing.JPanel; /** @@ -57,10 +60,8 @@ public class ComponentList extends JPanel { */ public void setModel(ComponentListModel model) { // Remove old model - if (this.model != null) { - this.model.clear(); + if (this.model != null) this.model.setComponentList(null); - } // Synchronize with new model this.model = model; @@ -75,7 +76,14 @@ public class ComponentList extends JPanel { * @param elem the element to add * @since Envoy v0.3-alpha */ - void add(E elem) { add(renderer.getListCellComponent(this, elem, false)); } + void add(E elem) { + JComponent c = renderer.getListCellComponent(this, elem, false); + Dimension size = new Dimension(getWidth(), 50); + c.setMaximumSize(size); + c.setMinimumSize(size); + c.setPreferredSize(size); + add(c); + } /** * Removes all child components and then adds all components representing the @@ -86,6 +94,6 @@ public class ComponentList extends JPanel { void synchronizeModel() { removeAll(); if (model != null) for (E elem : model) - add(renderer.getListCellComponent(this, elem, false)); + add(elem); } } From 82fd57d19c2c39a0b99c4045e7a772971031422e Mon Sep 17 00:00:00 2001 From: kske Date: Sun, 2 Feb 2020 11:37:46 +0100 Subject: [PATCH 11/11] Moved dimension and spacing calculation to MessageListRenderer --- .../envoy/client/ui/MessageListRenderer.java | 19 +++++++++++-------- .../envoy/client/ui/list/ComponentList.java | 10 +--------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/main/java/envoy/client/ui/MessageListRenderer.java b/src/main/java/envoy/client/ui/MessageListRenderer.java index 11312d1..c038e39 100644 --- a/src/main/java/envoy/client/ui/MessageListRenderer.java +++ b/src/main/java/envoy/client/ui/MessageListRenderer.java @@ -1,11 +1,9 @@ package envoy.client.ui; +import java.awt.Dimension; import java.text.SimpleDateFormat; -import javax.swing.JComponent; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.border.EmptyBorder; +import javax.swing.*; import envoy.client.Settings; import envoy.client.ui.list.ComponentList; @@ -31,6 +29,7 @@ public class MessageListRenderer implements ComponentListCellRenderer { if (isSelected) { panel.setBackground(Color.DARK_GRAY); panel.setForeground(Color.RED); + // TODO: Selection // setBackground(list.getSelectionBackground()); // setForeground(list.getSelectionForeground()); } else { @@ -50,8 +49,6 @@ public class MessageListRenderer implements ComponentListCellRenderer { // Getting the DateColor in the Chat of the current theme String dateColor = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getDateColorChat().toHex(); - //JLabel textLabel = new JLabel(text + ": " + state); - panel.add(new JLabel(String.format("

%s

%s :%s", dateColor, date, @@ -59,8 +56,14 @@ public class MessageListRenderer implements ComponentListCellRenderer { text, state))); - // Define some space to the components above and below - panel.setBorder(new EmptyBorder(0, 0, 15, 0)); + // Define some space to the messages below + panel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 0, 15, 0), BorderFactory.createEtchedBorder())); + + // Define a maximum height of 50px + Dimension size = new Dimension(list.getWidth() - 25, 50); + panel.setMaximumSize(size); + panel.setMinimumSize(size); + panel.setPreferredSize(size); return panel; } diff --git a/src/main/java/envoy/client/ui/list/ComponentList.java b/src/main/java/envoy/client/ui/list/ComponentList.java index 5b2ea45..8cbf2db 100644 --- a/src/main/java/envoy/client/ui/list/ComponentList.java +++ b/src/main/java/envoy/client/ui/list/ComponentList.java @@ -1,9 +1,6 @@ package envoy.client.ui.list; -import java.awt.Dimension; - import javax.swing.BoxLayout; -import javax.swing.JComponent; import javax.swing.JPanel; /** @@ -77,12 +74,7 @@ public class ComponentList extends JPanel { * @since Envoy v0.3-alpha */ void add(E elem) { - JComponent c = renderer.getListCellComponent(this, elem, false); - Dimension size = new Dimension(getWidth(), 50); - c.setMaximumSize(size); - c.setMinimumSize(size); - c.setPreferredSize(size); - add(c); + add(renderer.getListCellComponent(this, elem, false)); } /**