Merge branch 'develop' into f/invalid_login

This commit is contained in:
Kai S. K. Engelbart 2020-02-12 06:41:02 +01:00 committed by GitHub
commit c5389a6af4
23 changed files with 475 additions and 148 deletions

View File

@ -92,12 +92,13 @@ org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
org.eclipse.jdt.core.compiler.problem.suppressWarningsNotFullyAnalysed=info
org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning
org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=ignore
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
@ -126,4 +127,4 @@ org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=1.8
org.eclipse.jdt.core.compiler.source=1.8

View File

@ -127,7 +127,6 @@ public class Settings {
* @param themeName the name to set
* @since Envoy v0.2-alpha
*/
@SuppressWarnings("unchecked")
public void setCurrentTheme(String themeName) { ((SettingsItem<String>) items.get("currentTheme")).set(themeName); }
/**
@ -146,7 +145,6 @@ public class Settings {
* conjunction with the {@code Control} key.
* @since Envoy v0.2-alpha
*/
@SuppressWarnings("unchecked")
public void setEnterToSend(boolean enterToSend) { ((SettingsItem<Boolean>) items.get("enterToSend")).set(enterToSend); }
/**
@ -161,7 +159,6 @@ public class Settings {
* @param currentOnCloseMode the on close mode that should be set.
* @since Envoy v0.3-alpha
*/
@SuppressWarnings("unchecked")
public void setCurrentOnCloseMode(boolean currentOnCloseMode) { ((SettingsItem<Boolean>) items.get("onCloseMode")).set(currentOnCloseMode); }
/**

View File

@ -1,6 +1,7 @@
package envoy.client.event;
import envoy.data.Message;
import envoy.event.Event;
/**
* Project: <strong>envoy-client</strong><br>
@ -10,7 +11,7 @@ import envoy.data.Message;
* @author Kai S. K. Engelbart
* @since Envoy v0.2-alpha
*/
public class MessageCreationEvent extends MessageEvent {
public class MessageCreationEvent extends Event<Message> {
private static final long serialVersionUID = -6451021678064566774L;

View File

@ -1,34 +0,0 @@
package envoy.client.event;
import envoy.data.Message;
import envoy.event.Event;
/**
* Project: <strong>envoy-client</strong><br>
* File: <strong>MessageCreationEvent.java</strong><br>
* Created: <strong>4 Dec 2019</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy v0.2-alpha
*/
public class MessageEvent implements Event<Message> {
private static final long serialVersionUID = 7658989461923112804L;
/**
* the {@link Message} attached to this {@link MessageEvent}.
*/
protected final Message message;
/**
* Initializes a {@link MessageEvent} conveying information about a
* {@link Message} object.
*
* @param message the {@link Message} object to attach to this event
* @since Envoy v0.2-alpha
*/
public MessageEvent(Message message) { this.message = message; }
@Override
public Message get() { return message; }
}

View File

@ -1,6 +1,7 @@
package envoy.client.event;
import envoy.data.Message;
import envoy.event.Event;
/**
* Project: <strong>envoy-client</strong><br>
@ -10,7 +11,7 @@ import envoy.data.Message;
* @author Kai S. K. Engelbart
* @since Envoy v0.2-alpha
*/
public class MessageModificationEvent extends MessageEvent {
public class MessageModificationEvent extends Event<Message> {
private static final long serialVersionUID = 4650039506439563116L;

View File

@ -0,0 +1,23 @@
package envoy.client.event;
import envoy.event.Event;
/**
* Project: <strong>envoy-client</strong><br>
* File: <strong>SendEvent.java</strong><br>
* Created: <strong>11.02.2020</strong><br>
*
* @author: Maximilian K&aumlfer
*
* @since Envoy v0.3-alpha
*/
public class SendEvent extends Event<Event<?>> {
private static final long serialVersionUID = 8372746924138839060L;
/**
* @param value the event to send to the server
*/
public SendEvent(Event<?> value) { super(value); }
}

View File

@ -11,10 +11,9 @@ import envoy.event.Event;
* @author Kai S. K. Engelbart
* @since Envoy v0.2-alpha
*/
public class ThemeChangeEvent implements Event<Theme> {
public class ThemeChangeEvent extends Event<Theme> {
private static final long serialVersionUID = 6756772448803774547L;
private final Theme theme;
/**
* Initializes a {@link ThemeChangeEvent} conveying information about the change
@ -23,8 +22,5 @@ public class ThemeChangeEvent implements Event<Theme> {
* @param theme the new currently used {@link Theme} object
* @since Envoy v0.2-alpha
*/
public ThemeChangeEvent(Theme theme) { this.theme = theme; }
@Override
public Theme get() { return theme; }
public ThemeChangeEvent(Theme theme) { super(theme); }
}

View File

@ -12,9 +12,11 @@ import javax.naming.TimeLimitExceededException;
import envoy.client.data.Cache;
import envoy.client.data.Config;
import envoy.client.data.LocalDb;
import envoy.client.event.SendEvent;
import envoy.client.util.EnvoyLog;
import envoy.data.*;
import envoy.event.*;
import envoy.event.ContactOperationEvent.Operation;
import envoy.util.SerializationUtils;
/**
@ -109,11 +111,26 @@ public class Client implements Closeable {
receiver.registerProcessor(MessageStatusChangeEvent.class, new MessageStatusChangeEventProcessor());
// Process user status changes
receiver.registerProcessor(UserStatusChangeEvent.class, new UserStatusChangeProcessor(this));
receiver.registerProcessor(UserStatusChangeEvent.class, new UserStatusChangeProcessor(localDb));
// Process message ID generation
receiver.registerProcessor(IdGenerator.class, localDb::setIdGenerator);
// Process contact searches
receiver.registerProcessor(ContactSearchResult.class, EventBus.getInstance()::dispatch);
receiver.registerProcessor(Contacts.class,
contacts -> EventBus.getInstance().dispatch(new ContactOperationEvent(contacts.getContacts().get(0), Operation.ADD)));
// Send event
EventBus.getInstance().register(SendEvent.class, evt -> {
try {
sendEvent(evt.get());
} catch (IOException e) {
e.printStackTrace();
}
});
// Request a generator if none is present or the existing one is consumed
if (!localDb.hasIdGenerator() || !localDb.getIdGenerator().hasNext()) requestIdGenerator();
}

View File

@ -30,9 +30,6 @@ public class MessageStatusChangeEventProcessor implements Consumer<MessageStatus
@Override
public void accept(MessageStatusChangeEvent evt) {
if (evt.get().ordinal() < MessageStatus.RECEIVED.ordinal()) logger.info("Received invalid message status change " + evt);
else {
logger.info("Received " + evt.toString());
EventBus.getInstance().dispatch(evt);
}
else EventBus.getInstance().dispatch(evt);
}
}

View File

@ -23,7 +23,6 @@ public class ReceivedMessageProcessor implements Consumer<Message> {
@Override
public void accept(Message message) {
logger.info("Received message object " + message);
if (message.getStatus() != MessageStatus.SENT) logger.warning("The message has the unexpected status " + message.getStatus());
else {
// Update status to RECEIVED

View File

@ -35,7 +35,6 @@ public class Receiver implements Runnable {
*/
public Receiver(InputStream in) { this.in = in; }
@SuppressWarnings("unchecked")
@Override
public void run() {

View File

@ -1,9 +1,8 @@
package envoy.client.net;
import java.util.function.Consumer;
import java.util.logging.Logger;
import envoy.client.util.EnvoyLog;
import envoy.client.data.LocalDb;
import envoy.event.EventBus;
import envoy.event.UserStatusChangeEvent;
@ -17,21 +16,17 @@ import envoy.event.UserStatusChangeEvent;
*/
public class UserStatusChangeProcessor implements Consumer<UserStatusChangeEvent> {
private Client client;
private static final Logger logger = EnvoyLog.getLogger(UserStatusChangeProcessor.class.getSimpleName());
private final LocalDb localDb;
/**
* @param client the {@link Client} who receives an
* {@link UserStatusChangeEvent}
* @param localDb the local database in which status updates will by applied
* @since Envoy v0.3-alpha
*/
public UserStatusChangeProcessor(Client client) { this.client = client; }
public UserStatusChangeProcessor(LocalDb localDb) { this.localDb = localDb; }
@Override
public void accept(UserStatusChangeEvent evt) {
logger.info("Received " + evt);
client.getContacts().getContacts().stream().filter((user) -> user.getId() == evt.getId()).findFirst().get().setStatus(evt.get());
localDb.getUsers().values().stream().filter(u -> u.getId() == evt.getId()).findFirst().get().setStatus(evt.get());
EventBus.getInstance().dispatch(evt);
}
}

View File

@ -45,12 +45,14 @@ public class WriteProxy {
// Initialize cache processors for messages and message status change events
localDb.getMessageCache().setProcessor(msg -> {
try {
logger.info("Sending cached " + msg);
client.sendMessage(msg);
} catch (IOException e) {
logger.log(Level.SEVERE, "Could not send cached message", e);
}
});
localDb.getStatusCache().setProcessor(evt -> {
logger.info("Sending cached " + evt);
try {
client.sendEvent(evt);
} catch (IOException e) {
@ -66,8 +68,6 @@ public class WriteProxy {
* @since Envoy v0.3-alpha
*/
public void flushCache() {
logger.info("Sending cached messages and message status change events...");
// Send messages
localDb.getMessageCache().relay();

View File

@ -9,6 +9,8 @@ import java.util.logging.Logger;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import envoy.client.Settings;
import envoy.client.data.Chat;
@ -18,15 +20,14 @@ import envoy.client.event.ThemeChangeEvent;
import envoy.client.net.Client;
import envoy.client.net.WriteProxy;
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;
import envoy.data.Message.MessageStatus;
import envoy.data.MessageBuilder;
import envoy.data.User;
import envoy.event.EventBus;
import envoy.event.MessageStatusChangeEvent;
import envoy.event.UserStatusChangeEvent;
import envoy.event.*;
/**
* Project: <strong>envoy-client</strong><br>
@ -49,6 +50,7 @@ public class ChatWindow extends JFrame {
private JPanel contentPane = new JPanel();
private PrimaryTextArea messageEnterTextArea = new PrimaryTextArea(space);
private JList<User> userList = new JList<>();
private DefaultListModel<User> userListModel = new DefaultListModel<>();
private Chat currentChat;
private ComponentList<Message> messageList = new ComponentList<>(new MessageListRenderer());
private PrimaryScrollPane scrollPane = new PrimaryScrollPane();
@ -56,6 +58,20 @@ public class ChatWindow extends JFrame {
private PrimaryButton postButton = new PrimaryButton("Post");
private PrimaryButton settingsButton = new PrimaryButton("Settings");
// Contacts Header
private JPanel contactsHeader = new JPanel();
private JTextPane contactsDisplay = new JTextPane();
private PrimaryButton addContact = new PrimaryButton("+");
// Search Contacts
private final JPanel searchPane = new JPanel();
private final PrimaryButton cancelButton = new PrimaryButton("x");
private final PrimaryTextArea searchField = new PrimaryTextArea(space);
private final PrimaryScrollPane possibleContacts = new PrimaryScrollPane();
private final ContactsSearchRenderer contactRenderer = new ContactsSearchRenderer();
private final ComponentListModel<User> contactsModel = new ComponentListModel<>();
private final ComponentList<User> contactList = new ComponentList<>(contactRenderer);
private static final Logger logger = EnvoyLog.getLogger(ChatWindow.class.getSimpleName());
// GUI component spacing
@ -81,16 +97,11 @@ public class ChatWindow extends JFrame {
setContentPane(contentPane);
GridBagLayout gbl_contentPane = new GridBagLayout();
gbl_contentPane.columnWidths = new int[] { 1, 1, 1 };
gbl_contentPane.rowHeights = new int[] { 1, 1, 1 };
gbl_contentPane.rowHeights = new int[] { 1, 1, 1, 1 };
gbl_contentPane.columnWeights = new double[] { 0.3, 1.0, 0.1 };
gbl_contentPane.rowWeights = new double[] { 0.05, 1.0, 0.07 };
gbl_contentPane.rowWeights = new double[] { 0.03, 0.001, 1.0, 0.005 };
contentPane.setLayout(gbl_contentPane);
// TODO: messageList.setFocusTraversalKeysEnabled(false);
// messageList.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
// messageList.setFont(new Font("Arial", Font.PLAIN, 17));
// messageList.setFixedCellHeight(60);
messageList.setBorder(new EmptyBorder(space, space, space, space));
scrollPane.setViewportView(messageList);
@ -98,11 +109,13 @@ public class ChatWindow extends JFrame {
GridBagConstraints gbc_scrollPane = new GridBagConstraints();
gbc_scrollPane.fill = GridBagConstraints.BOTH;
gbc_scrollPane.gridwidth = 2;
gbc_scrollPane.gridheight = 2;
gbc_scrollPane.gridx = 1;
gbc_scrollPane.gridy = 1;
gbc_scrollPane.insets = insets;
contentPane.add(scrollPane, gbc_scrollPane);
drawChatBox(gbc_scrollPane);
// Message enter field
messageEnterTextArea.addKeyListener(new KeyAdapter() {
@ -118,7 +131,7 @@ public class ChatWindow extends JFrame {
GridBagConstraints gbc_messageEnterTextfield = new GridBagConstraints();
gbc_messageEnterTextfield.fill = GridBagConstraints.BOTH;
gbc_messageEnterTextfield.gridx = 1;
gbc_messageEnterTextfield.gridy = 2;
gbc_messageEnterTextfield.gridy = 3;
gbc_messageEnterTextfield.insets = insets;
@ -129,7 +142,7 @@ public class ChatWindow extends JFrame {
gbc_moveSelectionPostButton.fill = GridBagConstraints.BOTH;
gbc_moveSelectionPostButton.gridx = 2;
gbc_moveSelectionPostButton.gridy = 2;
gbc_moveSelectionPostButton.gridy = 3;
gbc_moveSelectionPostButton.insets = insets;
@ -145,14 +158,7 @@ public class ChatWindow extends JFrame {
gbc_moveSelectionSettingsButton.insets = insets;
settingsButton.addActionListener((evt) -> {
try {
new SettingsScreen().setVisible(true);
} catch (Exception e) {
logger.log(Level.WARNING, "An error occured while opening the settings screen", e);
e.printStackTrace();
}
});
settingsButton.addActionListener(evt -> new SettingsScreen().setVisible(true));
contentPane.add(settingsButton, gbc_moveSelectionSettingsButton);
// Partner name display
@ -171,26 +177,30 @@ public class ChatWindow extends JFrame {
userList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
userList.addListSelectionListener((listSelectionEvent) -> {
if (client != null && localDb != null && !listSelectionEvent.getValueIsAdjusting()) {
@SuppressWarnings("unchecked")
final JList<User> selectedUserList = (JList<User>) listSelectionEvent.getSource();
final User user = selectedUserList.getSelectedValue();
// Select current chat
currentChat = localDb.getChats().stream().filter(chat -> chat.getRecipient().getId() == user.getId()).findFirst().get();
for (int i = 0; i < contentPane.getComponents().length; i++) {
if (contentPane.getComponent(i).equals(searchPane)) { drawChatBox(gbc_scrollPane); }
}
if (user != null) {
// Select current chat
currentChat = localDb.getChats().stream().filter(chat -> chat.getRecipient().getId() == user.getId()).findFirst().get();
// Read current chat
readCurrentChat();
// Read current chat
readCurrentChat();
// Set chat title
textPane.setText(currentChat.getRecipient().getName());
// Set chat title
textPane.setText(currentChat.getRecipient().getName());
// Update model and scroll down
messageList.setModel(currentChat.getModel());
scrollPane.setChatOpened(true);
// Update model and scroll down
messageList.setModel(currentChat.getModel());
scrollPane.setChatOpened(true);
messageList.synchronizeModel();
revalidate();
repaint();
messageList.synchronizeModel();
revalidate();
repaint();
}
}
});
@ -198,26 +208,151 @@ public class ChatWindow extends JFrame {
userList.setBorder(new EmptyBorder(space, space, space, space));
GridBagConstraints gbc_userList = new GridBagConstraints();
gbc_userList.fill = GridBagConstraints.VERTICAL;
gbc_userList.gridx = 0;
gbc_userList.gridy = 1;
gbc_userList.anchor = GridBagConstraints.PAGE_START;
gbc_userList.insets = insets;
applyTheme(Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()));
gbc_userList.fill = GridBagConstraints.VERTICAL;
gbc_userList.gridx = 0;
gbc_userList.gridy = 2;
gbc_userList.gridheight = 2;
gbc_userList.anchor = GridBagConstraints.PAGE_START;
gbc_userList.insets = insets;
contentPane.add(userList, gbc_userList);
contentPane.revalidate();
// Contacts Search
GridBagConstraints gbc_searchPane = new GridBagConstraints();
gbc_searchPane.fill = GridBagConstraints.BOTH;
gbc_searchPane.gridwidth = 2;
gbc_searchPane.gridheight = 2;
gbc_searchPane.gridx = 1;
gbc_searchPane.gridy = 1;
gbc_searchPane.insets = insets;
GridBagLayout gbl_contactsSearch = new GridBagLayout();
gbl_contactsSearch.columnWidths = new int[] { 1, 1 };
gbl_contactsSearch.rowHeights = new int[] { 1, 1 };
gbl_contactsSearch.columnWeights = new double[] { 1, 0.1 };
gbl_contactsSearch.rowWeights = new double[] { 0.001, 1 };
searchPane.setLayout(gbl_contactsSearch);
GridBagConstraints gbc_searchField = new GridBagConstraints();
gbc_searchField.fill = GridBagConstraints.BOTH;
gbc_searchField.gridx = 0;
gbc_searchField.gridy = 0;
gbc_searchField.insets = new Insets(7, 4, 4, 4);
searchPane.add(searchField, gbc_searchField);
// Sends event to server, if input has changed
searchField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void removeUpdate(DocumentEvent evt) {
if (client.isOnline()) {
if (searchField.getText().isEmpty()) {
contactsModel.clear();
revalidate();
repaint();
} else {
try {
client.sendEvent(new ContactSearchRequest(searchField.getText()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
public void insertUpdate(DocumentEvent evt) {
if (client.isOnline()) {
try {
client.sendEvent(new ContactSearchRequest(searchField.getText()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void changedUpdate(DocumentEvent evt) {}
});
GridBagConstraints gbc_cancelButton = new GridBagConstraints();
gbc_cancelButton.fill = GridBagConstraints.BOTH;
gbc_cancelButton.gridx = 1;
gbc_cancelButton.gridy = 0;
gbc_cancelButton.insets = new Insets(7, 4, 4, 4);
cancelButton.addActionListener((evt) -> { drawChatBox(gbc_scrollPane); });
searchPane.add(cancelButton, gbc_cancelButton);
contactList.setModel(contactsModel);
possibleContacts.setBorder(new EmptyBorder(space, space, space, space));
possibleContacts.setViewportView(contactList);
GridBagConstraints gbc_possibleContacts = new GridBagConstraints();
gbc_possibleContacts.fill = GridBagConstraints.BOTH;
gbc_possibleContacts.gridwidth = 2;
gbc_possibleContacts.gridx = 0;
gbc_possibleContacts.gridy = 1;
gbc_possibleContacts.insets = insets;
searchPane.add(possibleContacts, gbc_possibleContacts);
// Contacts Header
GridBagConstraints gbc_contactsHeader = new GridBagConstraints();
gbc_contactsHeader.fill = GridBagConstraints.BOTH;
gbc_contactsHeader.gridx = 0;
gbc_contactsHeader.gridy = 1;
gbc_contactsHeader.insets = insets;
GridBagLayout gbl_contactHeader = new GridBagLayout();
gbl_contactHeader.columnWidths = new int[] { 1, 1 };
gbl_contactHeader.rowHeights = new int[] { 1 };
gbl_contactHeader.columnWeights = new double[] { 1, 1 };
gbl_contactHeader.rowWeights = new double[] { 1 };
contactsHeader.setLayout(gbl_contactHeader);
contactsDisplay.setEditable(false);
contactsDisplay.setFont(new Font("Arial", Font.PLAIN, 12));
contactsDisplay.setText("Contacts");
GridBagConstraints gbc_contactsDisplay = new GridBagConstraints();
gbc_contactsDisplay.fill = GridBagConstraints.BOTH;
gbc_contactsDisplay.gridx = 0;
gbc_contactsDisplay.gridy = 0;
contactsHeader.add(contactsDisplay, gbc_contactsDisplay);
addContact.setFont(new Font("Arial", Font.PLAIN, 15));
GridBagConstraints gbc_addContact = new GridBagConstraints();
gbc_addContact.fill = GridBagConstraints.BOTH;
gbc_addContact.gridx = 1;
gbc_addContact.gridy = 0;
gbc_addContact.insets = insets;
addContact.addActionListener((evt) -> { drawContactSearch(gbc_searchPane); });
contactsHeader.add(addContact, gbc_addContact);
applyTheme(Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()));
contentPane.add(contactsHeader, gbc_contactsHeader);
contentPane.revalidate();
// Listen to theme changes
EventBus.getInstance().register(ThemeChangeEvent.class, (evt) -> applyTheme((Theme) evt.get()));
EventBus.getInstance().register(ThemeChangeEvent.class, evt -> applyTheme(evt.get()));
// Listen to user status changes
EventBus.getInstance().register(UserStatusChangeEvent.class, (evt) -> { userList.revalidate(); userList.repaint(); });
EventBus.getInstance().register(UserStatusChangeEvent.class, evt -> { userList.revalidate(); userList.repaint(); });
// Listen to received messages
EventBus.getInstance().register(MessageCreationEvent.class, (evt) -> {
Message message = ((MessageCreationEvent) evt).get();
EventBus.getInstance().register(MessageCreationEvent.class, evt -> {
Message message = evt.get();
Chat chat = localDb.getChats().stream().filter(c -> c.getRecipient().getId() == message.getSenderId()).findFirst().get();
chat.appendMessage(message);
@ -229,9 +364,9 @@ public class ChatWindow extends JFrame {
});
// Listen to message status changes
EventBus.getInstance().register(MessageStatusChangeEvent.class, (evt) -> {
final long id = ((MessageStatusChangeEvent) evt).getId();
final MessageStatus status = (MessageStatus) evt.get();
EventBus.getInstance().register(MessageStatusChangeEvent.class, evt -> {
final long id = evt.getId();
final MessageStatus status = evt.get();
for (Chat c : localDb.getChats())
for (Message m : c.getModel())
@ -251,7 +386,35 @@ public class ChatWindow extends JFrame {
repaint();
});
// Listen to contact search results
EventBus.getInstance().register(ContactSearchResult.class, evt -> {
contactsModel.clear();
final java.util.List<User> contacts = evt.get();
logger.info("Received contact search result " + contacts);
contacts.forEach(contactsModel::add);
revalidate();
repaint();
});
// Add new contacts to the contact list
EventBus.getInstance().register(ContactOperationEvent.class, evt -> {
User contact = evt.get();
// Clearing the search field and the searchResultList
searchField.setText("");
contactsModel.clear();
// Update LocalDB
userListModel.addElement(contact);
localDb.getUsers().put(contact.getName(), contact);
localDb.getChats().add(new Chat(contact));
revalidate();
repaint();
});
revalidate();
repaint();
}
/**
@ -291,6 +454,21 @@ public class ChatWindow extends JFrame {
userList.setSelectionBackground(theme.getSelectionColor());
userList.setForeground(theme.getUserNameColor());
userList.setBackground(theme.getCellColor());
// contacts header
contactsHeader.setBackground(theme.getCellColor());
contactsDisplay.setBackground(theme.getCellColor());
contactsDisplay.setForeground(theme.getUserNameColor());
addContact.setBackground(theme.getInteractableBackgroundColor());
addContact.setForeground(theme.getInteractableForegroundColor());
// SearchPane
searchPane.setBackground(theme.getCellColor());
searchField.setBackground(theme.getBackgroundColor());
searchField.setForeground(theme.getUserNameColor());
cancelButton.setBackground(theme.getInteractableBackgroundColor());
cancelButton.setForeground(theme.getInteractableForegroundColor());
contactList.setForeground(theme.getMessageColorChat());
contactList.setBackground(theme.getCellColor());
possibleContacts.applyTheme(theme);
}
private void postMessage() {
@ -336,7 +514,6 @@ public class ChatWindow extends JFrame {
*/
private void loadUsersAndChats() {
new Thread(() -> {
DefaultListModel<User> userListModel = new DefaultListModel<>();
localDb.getUsers().values().forEach(user -> {
userListModel.addElement(user);
@ -345,6 +522,9 @@ public class ChatWindow extends JFrame {
localDb.getChats().add(new Chat(user));
});
SwingUtilities.invokeLater(() -> userList.setModel(userListModel));
revalidate();
repaint();
}).start();
}
@ -358,6 +538,24 @@ public class ChatWindow extends JFrame {
}
}
private void drawChatBox(GridBagConstraints gbc_scrollPane) {
contentPane.remove(searchPane);
contentPane.add(scrollPane, gbc_scrollPane);
contentPane.revalidate();
contentPane.repaint();
}
private void drawContactSearch(GridBagConstraints gbc_searchPane) {
currentChat = null;
userList.removeSelectionInterval(0, userList.getModel().getSize() - 1);
messageList.setModel(null);
textPane.setText("");
contentPane.remove(scrollPane);
contentPane.add(searchPane, gbc_searchPane);
contentPane.revalidate();
contentPane.repaint();
}
/**
* Initializes the components responsible server communication and
* persistence.<br>

View File

@ -0,0 +1,78 @@
package envoy.client.ui;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import javax.swing.*;
import envoy.client.Settings;
import envoy.client.event.SendEvent;
import envoy.client.ui.list.ComponentList;
import envoy.client.ui.list.ComponentListCellRenderer;
import envoy.data.User;
import envoy.event.ContactOperationEvent;
import envoy.event.EventBus;
/**
* Defines how a contact is displayed.<br>
* <br>
* Project: <strong>envoy-client</strong><br>
* File: <strong>ContactsSearchRenderer.java</strong><br>
* Created: <strong>08.02.2020</strong><br>
*
* @author Maximilian K&auml;fer
* @author Kai S. K. Engelbart
* @since Envoy v0.3-alpha
*/
public class ContactsSearchRenderer implements ComponentListCellRenderer<User> {
@Override
public JComponent getListCellComponent(ComponentList<? extends User> list, User user, boolean isSelected) {
final JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
if (isSelected) {
panel.setBackground(Color.DARK_GRAY);
panel.setForeground(Color.RED);
} else {
panel.setBackground(list.getBackground());
panel.setForeground(list.getForeground());
}
JLabel display = new JLabel(String.format("<html><p style=\"color:%s\">%s</html>",
Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getMessageColorChat().toHex(),
user.getName()));
display.setAlignmentX(Component.LEFT_ALIGNMENT);
display.setAlignmentY(Component.CENTER_ALIGNMENT);
display.setFont(new Font("Arial", Font.PLAIN, 16));
panel.add(display);
PrimaryButton add = new PrimaryButton("+");
add.setFont(new Font("Arial", Font.PLAIN, 19));
add.setPreferredSize(new Dimension(45, 45));
add.setMinimumSize(new Dimension(45, 45));
add.setMaximumSize(new Dimension(45, 45));
add.setBackground(list.getBackground());
add.setForeground(list.getForeground());
add.addActionListener(evt -> {
ContactOperationEvent contactsOperationEvent = new ContactOperationEvent(user, ContactOperationEvent.Operation.ADD);
EventBus.getInstance().dispatch(contactsOperationEvent);
EventBus.getInstance().dispatch(new SendEvent(contactsOperationEvent));
});
panel.add(add);
// 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(435, 50);
panel.setMaximumSize(size);
panel.setMinimumSize(size);
panel.setPreferredSize(size);
return panel;
}
}

View File

@ -9,6 +9,7 @@ import javax.swing.*;
import javax.swing.border.EmptyBorder;
import envoy.client.event.HandshakeSuccessfulEvent;
import envoy.client.Settings;
import envoy.data.LoginCredentials;
import envoy.event.EventBus;
import envoy.event.HandshakeRejectionEvent;
@ -24,22 +25,29 @@ import envoy.event.HandshakeRejectionEvent;
*/
public class LoginDialog extends JDialog {
private final JPanel contentPanel = new JPanel();
private static final long serialVersionUID = 352021600833907468L;
private final JPanel contentPanel;
private JTextField textField;
private JPasswordField passwordField;
private JPasswordField repeatPasswordField;
private JPasswordField repeatPasswordField;
private JLabel lblRepeatPassword;
private JLabel errorMessage;
private GridBagConstraints gbc_errorMessage;
private JLabel lblUserName;
private JLabel lblPassword;
private JLabel lblRepeatPassword;
private JLabel errorMessage;
private GridBagConstraints gbc_lblRepeatPassword;
private GridBagConstraints gbc_repeatPasswordField;
private GridBagConstraints gbc_errorMessage;
private JPanel buttonPane;
private JTextPane registerText;
private JCheckBox registerCheckBox;
private PrimaryButton okButton;
private PrimaryButton cancelButton;
private LoginCredentials credentials;
private static final long serialVersionUID = 352021600833907468L;
/**
* Displays a dialog enabling the user to enter their user name and password.
*
@ -49,6 +57,7 @@ public class LoginDialog extends JDialog {
setSize(338, 123);
setLocationRelativeTo(null);
getContentPane().setLayout(new BorderLayout());
contentPanel = new JPanel();
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
getContentPane().add(contentPanel, BorderLayout.CENTER);
GridBagLayout gbl_contentPanel = new GridBagLayout();
@ -58,7 +67,7 @@ public class LoginDialog extends JDialog {
gbl_contentPanel.rowWeights = new double[] { 0.0, 0.0, Double.MIN_VALUE };
contentPanel.setLayout(gbl_contentPanel);
{
JLabel lblUserName = new JLabel("User name:");
lblUserName = new JLabel("Username:");
GridBagConstraints gbc_lblUserName = new GridBagConstraints();
gbc_lblUserName.anchor = GridBagConstraints.EAST;
gbc_lblUserName.insets = new Insets(0, 0, 5, 5);
@ -68,6 +77,7 @@ public class LoginDialog extends JDialog {
}
{
textField = new JTextField();
textField.setBorder(null);
GridBagConstraints gbc_textField = new GridBagConstraints();
gbc_textField.insets = new Insets(0, 0, 5, 0);
gbc_textField.fill = GridBagConstraints.HORIZONTAL;
@ -77,7 +87,7 @@ public class LoginDialog extends JDialog {
textField.setColumns(10);
}
{
JLabel lblPassword = new JLabel("Password:");
lblPassword = new JLabel("Password:");
GridBagConstraints gbc_lblPassword = new GridBagConstraints();
gbc_lblPassword.anchor = GridBagConstraints.EAST;
gbc_lblPassword.insets = new Insets(0, 0, 0, 5);
@ -87,6 +97,7 @@ public class LoginDialog extends JDialog {
}
{
passwordField = new JPasswordField();
passwordField.setBorder(null);
GridBagConstraints gbc_passwordField = new GridBagConstraints();
gbc_passwordField.fill = GridBagConstraints.HORIZONTAL;
gbc_passwordField.gridx = 1;
@ -124,22 +135,23 @@ public class LoginDialog extends JDialog {
});
}
{
JPanel buttonPane = new JPanel();
buttonPane = new JPanel();
JTextPane registerText = new JTextPane();
registerText = new JTextPane();
registerText.setEditable(false);
registerText.setText("Register?");
registerText.setFont(new Font("Arial", Font.BOLD, 12));
registerText.setAlignmentX(LEFT_ALIGNMENT);
buttonPane.add(registerText);
JCheckBox registerCheckBox = new JCheckBox();
registerCheckBox = new JCheckBox();
registerCheckBox.setAlignmentX(LEFT_ALIGNMENT);
registerCheckBox.addItemListener(e -> {
switch (e.getStateChange()) {
case ItemEvent.SELECTED:
contentPanel.add(lblRepeatPassword, gbc_lblRepeatPassword);
contentPanel.add(repeatPasswordField, gbc_repeatPasswordField);
setSize(338, 160);
setSize(338, 148);
break;
case ItemEvent.DESELECTED:
@ -158,7 +170,7 @@ public class LoginDialog extends JDialog {
buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
getContentPane().add(buttonPane, BorderLayout.SOUTH);
{
JButton okButton = new JButton("OK");
okButton = new PrimaryButton("OK");
okButton.addActionListener((evt) -> {
try {
if (registerCheckBox.isSelected()) {
@ -179,12 +191,13 @@ public class LoginDialog extends JDialog {
getRootPane().setDefaultButton(okButton);
}
{
JButton cancelButton = new JButton("Cancel");
cancelButton = new PrimaryButton("Cancel");
cancelButton.addActionListener((evt) -> dispose());
cancelButton.setActionCommand("Cancel");
buttonPane.add(cancelButton);
}
}
setTheme();
setModal(true);
setVisible(true);
@ -201,6 +214,50 @@ public class LoginDialog extends JDialog {
repeatPasswordField.setText(null);
}
private void setTheme() {
Theme theme = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme());
// Panels
contentPanel.setBackground(theme.getBackgroundColor());
contentPanel.setForeground(theme.getBackgroundColor());
buttonPane.setBackground(theme.getBackgroundColor());
buttonPane.setForeground(theme.getBackgroundColor());
// Input Fields
textField.setBackground(theme.getCellColor());
textField.setForeground(theme.getUserNameColor());
passwordField.setBackground(theme.getCellColor());
passwordField.setForeground(theme.getUserNameColor());
repeatPasswordField.setBackground(theme.getCellColor());
repeatPasswordField.setForeground(theme.getUserNameColor());
// JLabels
lblUserName.setBackground(theme.getCellColor());
lblUserName.setForeground(theme.getUserNameColor());
lblPassword.setBackground(theme.getCellColor());
lblPassword.setForeground(theme.getUserNameColor());
lblRepeatPassword.setBackground(theme.getCellColor());
lblRepeatPassword.setForeground(theme.getUserNameColor());
// Register
registerText.setBackground(theme.getCellColor());
registerText.setForeground(theme.getUserNameColor());
registerCheckBox.setBackground(theme.getCellColor());
// Buttons
okButton.setBackground(theme.getInteractableBackgroundColor());
okButton.setForeground(theme.getInteractableForegroundColor());
cancelButton.setBackground(theme.getInteractableBackgroundColor());
cancelButton.setForeground(theme.getInteractableForegroundColor());
}
/**
* @return the {@link LoginCredentials} entered by the user, or {@code null} if
* the dialog has been cancelled

View File

@ -14,7 +14,7 @@ import envoy.data.Message;
* Defines how a message is displayed.<br>
* <br>
* Project: <strong>envoy-client</strong><br>
* File: <strong>UserListRenderer.java</strong><br>
* File: <strong>MessageListRenderer.java</strong><br>
* Created: <strong>19 Oct 2019</strong><br>
*
* @author Kai S. K. Engelbart

View File

@ -163,7 +163,7 @@ public class Startup {
chatWindow.initContent(client, localDb, writeProxy);
// Relay unread messages from cache
if (cache != null) cache.relay();
if (cache != null && client.isOnline()) cache.relay();
try {
new StatusTrayIcon(chatWindow).show();

View File

@ -74,13 +74,10 @@ public class StatusTrayIcon {
trayIcon.addActionListener((evt) -> { focusTarget.setVisible(true); focusTarget.requestFocus(); });
// Start processing message events
// TODO: Handle other message types
EventBus.getInstance()
.register(MessageCreationEvent.class,
(evt) -> {
// TODO: Handle other message types
if (displayMessages)
trayIcon.displayMessage("New message received", ((MessageCreationEvent) evt).get().getText(), MessageType.INFO);
});
evt -> { if (displayMessages) trayIcon.displayMessage("New message received", evt.get().getText(), MessageType.INFO); });
}
/**

View File

@ -1,6 +1,7 @@
package envoy.client.ui;
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.JLabel;
import javax.swing.JList;
@ -41,6 +42,8 @@ public class UserListRenderer extends JLabel implements ListCellRenderer<User> {
final String name = value.getName();
final UserStatus status = value.getStatus();
this.setPreferredSize(new Dimension(100, 35));
// Getting the UserNameColor of the current theme
String textColor = null;
textColor = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getUserNameColor().toHex();

View File

@ -62,8 +62,10 @@ public class ComponentList<E> extends JPanel {
// Synchronize with new model
this.model = model;
this.model.setComponentList(this);
synchronizeModel();
if (model != null) {
this.model.setComponentList(this);
synchronizeModel();
} else removeAll();
}
/**

View File

@ -159,7 +159,7 @@ public class SettingsScreen extends JDialog {
applyTheme(Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()));
// Respond to theme changes
EventBus.getInstance().register(ThemeChangeEvent.class, (evt) -> applyTheme(((ThemeChangeEvent) evt).get()));
EventBus.getInstance().register(ThemeChangeEvent.class, evt -> applyTheme(evt.get()));
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
setModal(true);

View File

@ -47,7 +47,7 @@ public class ThemeCustomizationPanel extends SettingsPanel {
* Initializes a {@link ThemeCustomizationPanel} that enables the user to change
* the current {@link Theme} and create new themes as part of the
* {@link SettingsScreen}.
*
*
* @param parent the {@link SettingsScreen} as a part of which this
* {@link SettingsPanel} is displayed
* @since Envoy v0.2-alpha
@ -119,8 +119,8 @@ public class ThemeCustomizationPanel extends SettingsPanel {
// Respond to theme changes
EventBus.getInstance()
.register(ThemeChangeEvent.class,
(evt) -> {
final Theme currentTheme = ((ThemeChangeEvent) evt).get();
evt -> {
final Theme currentTheme = evt.get();
temporaryTheme = new Theme("temporaryTheme", currentTheme);
applyTheme(currentTheme);
});
@ -130,7 +130,7 @@ public class ThemeCustomizationPanel extends SettingsPanel {
public ActionListener getOkButtonAction() {
return (evt) -> {
if (themeChanged) {
new NewThemeScreen(parent, (name) -> {
new NewThemeScreen(parent, name -> {
// Create new theme
logger.log(Level.FINEST, name);
Settings.getInstance().addNewThemeToMap(new Theme(name, temporaryTheme));
@ -140,12 +140,12 @@ public class ThemeCustomizationPanel extends SettingsPanel {
// Select new theme name
themes.setSelectedIndex(themesModel.getSize() - 1);
}, (name) -> {
}, name -> {
// Modify theme
Settings.getInstance().getThemes().replace(name, new Theme(name, temporaryTheme));
if(themes.getSelectedItem().equals(name)) {
if (themes.getSelectedItem().equals(name)) {
EventBus.getInstance().dispatch(new ThemeChangeEvent(Settings.getInstance().getTheme(name)));
}else {
} else {
themes.setSelectedItem(name);
}
}).setVisible(true);