Merge pull request #123 from informatik-ag-ngl/f/forward_messages

Added message forwarding capability
This commit is contained in:
delvh 2020-03-23 22:07:43 +01:00 committed by GitHub
commit 2b501f0329
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
65 changed files with 2021 additions and 1014 deletions

View File

@ -1,4 +1,4 @@
package envoy.client.ui;
package envoy.client;
import java.awt.EventQueue;
import java.io.File;
@ -15,6 +15,9 @@ import javax.swing.SwingUtilities;
import envoy.client.data.*;
import envoy.client.net.Client;
import envoy.client.net.WriteProxy;
import envoy.client.ui.StatusTrayIcon;
import envoy.client.ui.container.ChatWindow;
import envoy.client.ui.container.LoginDialog;
import envoy.data.Config;
import envoy.data.Message;
import envoy.data.User.UserStatus;
@ -31,7 +34,7 @@ import envoy.util.EnvoyLog;
* @author Leon Hofmeister
* @author Maximilian Käfer
* @author Kai S. K. Engelbart
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public class Startup {
@ -48,7 +51,7 @@ public class Startup {
*
* @param args the command line arguments may contain configuration parameters
* and are parsed by the {@link Config} class
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public static void main(String[] args) {
ClientConfig config = ClientConfig.getInstance();
@ -127,11 +130,9 @@ public class Startup {
// Save all users to the local database and flush cache
localDb.setUsers(client.getUsers());
writeProxy.flushCache();
} else {
} else
// Set all contacts to offline mode
localDb.getUsers().values().stream().filter(u -> u != localDb.getUser()).forEach(u -> u.setStatus(UserStatus.OFFLINE));
}
// Display ChatWindow and StatusTrayIcon
EventQueue.invokeLater(() -> {

View File

@ -17,7 +17,7 @@ import envoy.util.EnvoyLog;
*
* @param <T> the type of cached elements
* @author Kai S. K. Engelbart
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public class Cache<T> implements Consumer<T>, Serializable {
@ -31,7 +31,7 @@ public class Cache<T> implements Consumer<T>, Serializable {
* Adds an element to the cache.
*
* @param element the element to add
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
@Override
public void accept(T element) {
@ -43,7 +43,7 @@ public class Cache<T> implements Consumer<T>, Serializable {
* Sets the processor to which cached elements are relayed.
*
* @param processor the processor to set
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void setProcessor(Consumer<T> processor) { this.processor = processor; }
@ -51,7 +51,7 @@ public class Cache<T> implements Consumer<T>, Serializable {
* Relays all cached elements to the processor.
*
* @throws IllegalStateException if the processor is not initialized
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void relay() {
if (processor == null) throw new IllegalStateException("Processor is not defined");

View File

@ -4,7 +4,7 @@ import java.io.IOException;
import java.io.Serializable;
import envoy.client.net.WriteProxy;
import envoy.client.ui.list.ComponentListModel;
import envoy.client.ui.list.Model;
import envoy.data.Message;
import envoy.data.Message.MessageStatus;
import envoy.data.User;
@ -21,21 +21,21 @@ import envoy.event.MessageStatusChangeEvent;
* @author Maximilian K&auml;fer
* @author Leon Hofmeister
* @author Kai S. K. Engelbart
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public class Chat implements Serializable {
private static final long serialVersionUID = -7751248474547242056L;
private final User recipient;
private final ComponentListModel<Message> model = new ComponentListModel<>();
private final Model<Message> model = new Model<>();
/**
* Provides the list of messages that the recipient receives.<br>
* Saves the Messages in the corresponding chat at that Point.
*
* @param recipient the user who receives the messages
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public Chat(User recipient) { this.recipient = recipient; }
@ -43,7 +43,7 @@ public class Chat implements Serializable {
* Appends a message to the bottom of this chat
*
* @param message the message to append
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public void appendMessage(Message message) { model.add(message); }
@ -56,37 +56,35 @@ public class Chat implements Serializable {
* the message status changes
* @throws IOException if a {@link MessageStatusChangeEvent} could not be
* delivered to the server
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void read(WriteProxy writeProxy) throws IOException {
for (int i = model.size() - 1; i >= 0; --i) {
final Message m = model.get(i);
if (m.getSenderId() == recipient.getId()) {
if (m.getStatus() == MessageStatus.READ) break;
if (m.getSenderId() == recipient.getId()) if (m.getStatus() == MessageStatus.READ) break;
else {
m.setStatus(MessageStatus.READ);
writeProxy.writeMessageStatusChangeEvent(new MessageStatusChangeEvent(m));
}
}
}
}
/**
* @return {@code true} if the newest message received in the chat doesn't have
* the status {@code READ}
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public boolean isUnread() { return !model.isEmpty() && model.get(model.size() - 1).getStatus() != MessageStatus.READ; }
/**
* @return all messages in the current chat
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public ComponentListModel<Message> getModel() { return model; }
public Model<Message> getModel() { return model; }
/**
* @return the recipient of a message
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public User getRecipient() { return recipient; }
}

View File

@ -18,7 +18,7 @@ import envoy.data.LoginCredentials;
* Created: <strong>01.03.2020</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy v0.1-beta
* @since Envoy Client v0.1-beta
*/
public class ClientConfig extends Config {
@ -26,7 +26,7 @@ public class ClientConfig extends Config {
/**
* @return the singleton instance of the client config
* @since Envoy v0.1-beta
* @since Envoy Client v0.1-beta
*/
public static ClientConfig getInstance() {
if (config == null) config = new ClientConfig();
@ -47,68 +47,68 @@ public class ClientConfig extends Config {
/**
* @return the host name of the Envoy server
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public String getServer() { return (String) items.get("server").get(); }
/**
* @return the port at which the Envoy server is located on the host
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public Integer getPort() { return (Integer) items.get("port").get(); }
/**
* @return the local database specific to the client user
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public File getLocalDB() { return (File) items.get("localDB").get(); }
/**
* @return {@code true} if the local database is to be ignored
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public Boolean isIgnoreLocalDB() { return (Boolean) items.get("ignoreLocalDB").get(); }
/**
* @return the directory in which all local files are saves
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public File getHomeDirectory() { return (File) items.get("homeDirectory").get(); }
/**
* @return the minimal {@link Level} to log inside the log file
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Level getFileLevelBarrier() { return (Level) items.get("fileLevelBarrier").get(); }
/**
* @return the minimal {@link Level} to log inside the console
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Level getConsoleLevelBarrier() { return (Level) items.get("consoleLevelBarrier").get(); }
/**
* @return the user name
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public String getUser() { return (String) items.get("user").get(); }
/**
* @return the password
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public char[] getPassword() { return (char[]) items.get("password").get(); }
/**
* @return {@code true} if user name and password are set
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public boolean hasLoginCredentials() { return getUser() != null && getPassword() != null; }
/**
* @return login credentials for the specified user name and password, without
* the registration option
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public LoginCredentials getLoginCredentials() {
try {

View File

@ -16,7 +16,7 @@ import envoy.event.MessageStatusChangeEvent;
* Created: <strong>3 Feb 2020</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public abstract class LocalDb {
@ -30,7 +30,7 @@ public abstract class LocalDb {
/**
* Initializes a storage space for a user-specific list of chats.
*
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void initializeUserStorage() {}
@ -39,7 +39,7 @@ public abstract class LocalDb {
* as well. The message id generator will also be saved if present.
*
* @throws Exception if the saving process failed
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void save() throws Exception {}
@ -47,7 +47,7 @@ public abstract class LocalDb {
* Loads all user data.
*
* @throws Exception if the loading process failed
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void loadUsers() throws Exception {}
@ -55,21 +55,21 @@ public abstract class LocalDb {
* Loads all data of the client user.
*
* @throws Exception if the loading process failed
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void loadUserData() throws Exception {}
/**
* Loads the ID generator. Any exception thrown during this process is ignored.
*
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void loadIdGenerator() {}
/**
* @return a {@code Map<String, User>} of all users stored locally with their
* user names as keys
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Map<String, User> getUsers() { return users; }
@ -81,7 +81,7 @@ public abstract class LocalDb {
/**
* @return all saved {@link Chat} objects that list the client user as the
* sender
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
**/
public List<Chat> getChats() { return chats; }
@ -92,55 +92,55 @@ public abstract class LocalDb {
/**
* @return the {@link User} who initialized the local database
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public User getUser() { return user; }
/**
* @param user the user to set
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public void setUser(User user) { this.user = user; }
/**
* @return the message ID generator
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public IdGenerator getIdGenerator() { return idGenerator; }
/**
* @param idGenerator the message ID generator to set
* @since Envoy v0.3-alpha
* @since Envoy Client 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
* @since Envoy Client v0.3-alpha
*/
public boolean hasIdGenerator() { return idGenerator != null; }
/**
* @return the offline message cache
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public Cache<Message> getMessageCache() { return messageCache; }
/**
* @param messageCache the offline message cache to set
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void setMessageCache(Cache<Message> messageCache) { this.messageCache = messageCache; }
/**
* @return the offline status cache
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public Cache<MessageStatusChangeEvent> getStatusCache() { return statusCache; }
/**
* @param statusCache the offline status cache to set
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void setStatusCache(Cache<MessageStatusChangeEvent> statusCache) { this.statusCache = statusCache; }
@ -150,7 +150,7 @@ public abstract class LocalDb {
* @param id the ID of the message to search for
* @return the message with the corresponding ID, or {@code null} if no message
* has been found
* @since Envoy v0.1-beta
* @since Envoy Client v0.1-beta
*/
public Message getMessage(long id) {
for (Chat c : chats)

View File

@ -19,7 +19,7 @@ import envoy.util.SerializationUtils;
*
* @author Kai S. K. Engelbart
* @author Maximilian K&auml;fer
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public class PersistentLocalDb extends LocalDb {
@ -32,7 +32,7 @@ public class PersistentLocalDb extends LocalDb {
* This constructor shall be used in conjunction with the {@code ignoreLocalDB}
* {@link ConfigItem}.
*
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public PersistentLocalDb() {}
@ -42,7 +42,7 @@ public class PersistentLocalDb extends LocalDb {
*
* @param localDbDir the directory in which to store users and chats
* @throws IOException if the PersistentLocalDb could not be initialized
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public PersistentLocalDb(File localDbDir) throws IOException {
localDBDir = localDbDir;
@ -58,7 +58,7 @@ public class PersistentLocalDb extends LocalDb {
* Creates a database file for a user-specific list of chats.
*
* @throws NullPointerException if the client user is not yet specified
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
@Override
public void initializeUserStorage() {

View File

@ -22,7 +22,7 @@ import envoy.util.SerializationUtils;
* @author Leon Hofmeister
* @author Maximilian K&auml;fer
* @author Kai S. K. Engelbart
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public class Settings {
@ -49,7 +49,7 @@ public class Settings {
* The way to instantiate the settings.
* Is set to private to deny other instances of that object.
*
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
private Settings() {
// Load settings from settings file
@ -81,7 +81,7 @@ public class Settings {
* This method is used to ensure that there is only one instance of Settings.
*
* @return the instance of Settings
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public static Settings getInstance() { return settings; }
@ -90,7 +90,7 @@ public class Settings {
*
* @throws IOException if an error occurs while saving the themes to the theme
* file
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public void save() throws IOException {
// Save settings to settings file
@ -110,21 +110,27 @@ public class Settings {
* Adds new theme to the theme map.
*
* @param theme the {@link Theme} to add
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public void addNewThemeToMap(Theme theme) { settings.getThemes().put(theme.getThemeName(), theme); }
public void addNewThemeToMap(Theme theme) { getThemes().put(theme.getThemeName(), theme); }
/**
* @return the name of the currently active {@link Theme}
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public String getCurrentTheme() { return (String) items.get("currentTheme").get(); }
public String getCurrentThemeName() { return (String) items.get("currentTheme").get(); }
/**
* @return the currently active {@link Theme}
* @since Envoy Client v0.1-beta
*/
public Theme getCurrentTheme() { return getTheme(getCurrentThemeName()); }
/**
* Sets the name of the current {@link Theme}.
*
* @param themeName the name to set
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public void setCurrentTheme(String themeName) { ((SettingsItem<String>) items.get("currentTheme")).set(themeName); }
@ -132,7 +138,7 @@ public class Settings {
* @return {@code true}, if pressing the {@code Enter} key suffices to send a
* message. Otherwise it has to be pressed in conjunction with the
* {@code Control} key.
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Boolean isEnterToSend() { return (Boolean) items.get("enterToSend").get(); }
@ -142,13 +148,13 @@ public class Settings {
* @param enterToSend If set to {@code true} a message can be sent by pressing
* the {@code Enter} key. Otherwise it has to be pressed in
* conjunction with the {@code Control} key.
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public void setEnterToSend(boolean enterToSend) { ((SettingsItem<Boolean>) items.get("enterToSend")).set(enterToSend); }
/**
* @return the current on close mode.
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public Boolean getCurrentOnCloseMode() { return (Boolean) items.get("onCloseMode").get(); }
@ -156,7 +162,7 @@ public class Settings {
* Sets the current on close mode.
*
* @param currentOnCloseMode the on close mode that should be set.
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void setCurrentOnCloseMode(boolean currentOnCloseMode) { ((SettingsItem<Boolean>) items.get("onCloseMode")).set(currentOnCloseMode); }
@ -172,7 +178,7 @@ public class Settings {
/**
* @return a {@code Map<String, Theme>} of all themes with their names as keys
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Map<String, Theme> getThemes() { return themes; }
@ -180,14 +186,14 @@ public class Settings {
* Sets the {@code Map<String, Theme>} of all themes with their names as keys
*
* @param themes the theme map to set
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public void setThemes(Map<String, Theme> themes) { this.themes = themes; }
/**
* @param themeName the name of the {@link Theme} to get
* @return the {@link Theme} with the specified name
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public Theme getTheme(String themeName) { return themes.get(themeName); }
}

View File

@ -7,7 +7,7 @@ import java.util.function.Consumer;
import javax.swing.JComponent;
import envoy.client.ui.PrimaryToggleSwitch;
import envoy.client.ui.primary.PrimaryToggleSwitch;
/**
* Encapsulates a persistent value that is directly or indirectly mutable by the
@ -19,7 +19,7 @@ import envoy.client.ui.PrimaryToggleSwitch;
*
* @param <T> the type of this {@link SettingsItem}'s value
* @author Kai S. K. Engelbart
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public class SettingsItem<T> implements Serializable {
@ -45,7 +45,7 @@ public class SettingsItem<T> implements Serializable {
* @param value the default value
* @param userFriendlyName the user friendly name (short)
* @param description the description (long)
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public SettingsItem(T value, String userFriendlyName, String description) {
this(value, componentClasses.get(value.getClass()));
@ -61,7 +61,7 @@ public class SettingsItem<T> implements Serializable {
*
* @param value the default value
* @param componentClass the class of the {@link JComponent} to represent this {@link SettingsItem} with
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public SettingsItem(T value, Class<? extends JComponent> componentClass) {
this.value = value;
@ -72,7 +72,7 @@ public class SettingsItem<T> implements Serializable {
* @return an instance of the {@link JComponent} that represents this {@link SettingsItem}
* @throws ReflectiveOperationException if the component initialization failed
* @throws SecurityException if the component initialization failed
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public JComponent getComponent() throws ReflectiveOperationException, SecurityException {
if (componentClass == null) throw new NullPointerException("Component class is null");
@ -81,7 +81,7 @@ public class SettingsItem<T> implements Serializable {
/**
* @return the value
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public T get() { return value; }
@ -90,7 +90,7 @@ public class SettingsItem<T> implements Serializable {
* defined, it will be invoked with this value.
*
* @param value the value to set
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void set(T value) {
if (changeHandler != null && value != this.value) changeHandler.accept(value);
@ -99,37 +99,37 @@ public class SettingsItem<T> implements Serializable {
/**
* @return the componentClass
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public Class<? extends JComponent> getComponentClass() { return componentClass; }
/**
* @param componentClass the componentClass to set
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void setComponentClass(Class<? extends JComponent> componentClass) { this.componentClass = componentClass; }
/**
* @return the userFriendlyName
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public String getUserFriendlyName() { return userFriendlyName; }
/**
* @param userFriendlyName the userFriendlyName to set
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void setUserFriendlyName(String userFriendlyName) { this.userFriendlyName = userFriendlyName; }
/**
* @return the description
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public String getDescription() { return description; }
/**
* @param description the description to set
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void setDescription(String description) { this.description = description; }
@ -139,7 +139,7 @@ public class SettingsItem<T> implements Serializable {
* when the value changes.
*
* @param changeHandler the changeHandler to set
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void setChangeHandler(Consumer<T> changeHandler) {
this.changeHandler = changeHandler;

View File

@ -9,7 +9,7 @@ package envoy.client.data;
* Created: <strong>3 Feb 2020</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public class TransientLocalDb extends LocalDb {
}

View File

@ -4,6 +4,6 @@
* @author Kai S. K. Engelbart
* @author Leon Hofmeister
* @author Maximilian K&auml;fer
* @since Envoy v0.1-beta
* @since Envoy Client v0.1-beta
*/
package envoy.client.data;

View File

@ -10,7 +10,7 @@ import envoy.event.Event;
* Created: <strong>8 Feb 2020</strong><br>
*
* @author Leon Hofmeister
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public class HandshakeSuccessfulEvent extends Event.Valueless {

View File

@ -9,7 +9,7 @@ import envoy.event.Event;
* Created: <strong>4 Dec 2019</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public class MessageCreationEvent extends Event<Message> {

View File

@ -9,7 +9,7 @@ import envoy.event.Event;
* Created: <strong>4 Dec 2019</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public class MessageModificationEvent extends Event<Message> {

View File

@ -9,7 +9,7 @@ import envoy.event.Event;
*
* @author: Maximilian K&aumlfer
*
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public class SendEvent extends Event<Event<?>> {

View File

@ -9,7 +9,7 @@ import envoy.event.Event;
* Created: <strong>15 Dec 2019</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public class ThemeChangeEvent extends Event<Theme> {
@ -20,7 +20,7 @@ public class ThemeChangeEvent extends Event<Theme> {
* of the {@link Theme} currently in use
*
* @param theme the new currently used {@link Theme} object
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public ThemeChangeEvent(Theme theme) { super(theme); }
}

View File

@ -4,6 +4,6 @@
* @author Kai S. K. Engelbart
* @author Leon Hofmeister
* @author Maximilian K&auml;fer
* @since Envoy v0.1-beta
* @since Envoy Client v0.1-beta
*/
package envoy.client.event;

View File

@ -30,7 +30,7 @@ import envoy.util.SerializationUtils;
* @author Kai S. K. Engelbart
* @author Maximilian K&auml;fer
* @author Leon Hofmeister
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public class Client implements Closeable {
@ -124,7 +124,7 @@ public class Client implements Closeable {
* initialization
* @throws IOException if no {@link IdGenerator} is present and none could be
* requested from the server
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public void initReceiver(LocalDb localDb, Cache<Message> receivedMessageCache) throws IOException {
checkOnline();
@ -181,7 +181,7 @@ public class Client implements Closeable {
*
* @param message the message to send
* @throws IOException if the message does not reach the server
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void sendMessage(Message message) throws IOException {
writeObject(message);
@ -200,7 +200,7 @@ public class Client implements Closeable {
* Requests a new {@link IdGenerator} from the server.
*
* @throws IOException if the request does not reach the server
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void requestIdGenerator() throws IOException {
logger.info("Requesting new id generator...");
@ -210,7 +210,7 @@ public class Client implements Closeable {
/**
* @return a {@code Map<String, User>} of all users on the server with their
* user names as keys
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Map<String, User> getUsers() {
checkOnline();
@ -232,7 +232,7 @@ public class Client implements Closeable {
/**
* @return the sender object that represents this client.
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public User getSender() { return sender; }
@ -240,7 +240,7 @@ public class Client implements Closeable {
* Sets the client user which is used to send messages.
*
* @param sender the client user to set
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public void setSender(User sender) { this.sender = sender; }
@ -251,19 +251,19 @@ public class Client implements Closeable {
/**
* @return {@code true} if a connection to the server could be established
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public boolean isOnline() { return online; }
/**
* @return the contacts of this {@link Client}
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public Contacts getContacts() { return contacts; }
/**
* @param contacts the contacts to set
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void setContacts(Contacts contacts) { this.contacts = contacts; }
}

View File

@ -14,7 +14,7 @@ import envoy.util.EnvoyLog;
* Created: <strong>4 Feb 2020</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public class MessageStatusChangeEventProcessor implements Consumer<MessageStatusChangeEvent> {
@ -25,7 +25,7 @@ public class MessageStatusChangeEventProcessor implements Consumer<MessageStatus
* {@code RECEIVED} or {@code READ}.
*
* @param evt the status change event
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
@Override
public void accept(MessageStatusChangeEvent evt) {

View File

@ -15,7 +15,7 @@ import envoy.util.EnvoyLog;
* Created: <strong>31.12.2019</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public class ReceivedMessageProcessor implements Consumer<Message> {

View File

@ -19,7 +19,7 @@ import envoy.util.SerializationUtils;
* Created: <strong>30.12.2019</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public class Receiver extends Thread {

View File

@ -12,7 +12,7 @@ import envoy.event.UserStatusChangeEvent;
* Created: <strong>2 Feb 2020</strong><br>
*
* @author Leon Hofmeister
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public class UserStatusChangeProcessor implements Consumer<UserStatusChangeEvent> {
@ -20,7 +20,7 @@ public class UserStatusChangeProcessor implements Consumer<UserStatusChangeEvent
/**
* @param localDb the local database in which status updates will by applied
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public UserStatusChangeProcessor(LocalDb localDb) { this.localDb = localDb; }

View File

@ -19,7 +19,7 @@ import envoy.util.EnvoyLog;
* Created: <strong>6 Feb 2020</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public class WriteProxy {
@ -36,7 +36,7 @@ public class WriteProxy {
* events
* @param localDb the local database used to cache messages and message status
* change events
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public WriteProxy(Client client, LocalDb localDb) {
this.client = client;
@ -68,7 +68,7 @@ public class WriteProxy {
* Sends cached {@link Message}s and {@link MessageStatusChangeEvent}s to the
* server.
*
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void flushCache() {
// Send messages
@ -84,7 +84,7 @@ public class WriteProxy {
*
* @param message the message to send
* @throws IOException if the message could not be sent
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void writeMessage(Message message) throws IOException {
if (client.isOnline()) client.sendMessage(message);
@ -97,7 +97,7 @@ public class WriteProxy {
*
* @param evt the event to send
* @throws IOException if the event could not be sent
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void writeMessageStatusChangeEvent(MessageStatusChangeEvent evt) throws IOException {
if (client.isOnline()) client.sendEvent(evt);

View File

@ -4,6 +4,6 @@
* @author Kai S. K. Engelbart
* @author Leon Hofmeister
* @author Maximilian K&auml;fer
* @since Envoy v0.1-beta
* @since Envoy Client v0.1-beta
*/
package envoy.client.net;

View File

@ -11,7 +11,7 @@ import java.awt.color.ColorSpace;
* Created: <strong>23.12.2019</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
@SuppressWarnings("javadoc")
public class Color extends java.awt.Color {
@ -102,13 +102,13 @@ public class Color extends java.awt.Color {
/**
* @return the inversion of this {@link Color} by replacing the red, green and
* blue values by subtracting them form 255
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public Color invert() { return new Color(255 - getRed(), 255 - getGreen(), 255 - getBlue()); }
/**
* @return the hex value of this {@link Color}
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public String toHex() { return String.format("#%02x%02x%02x", getRed(), getGreen(), getBlue()); }
}

View File

@ -0,0 +1,200 @@
package envoy.client.ui;
import java.awt.Component;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;
import javax.swing.*;
/**
* This class defines a menu that will be automatically called if
* {@link MouseEvent#isPopupTrigger()} returns true for the parent component.
* The user has the possibility to directly add actions to be performed when
* clicking on the element with the selected String. Additionally, for each
* element an {@link Icon} can be added, but it must not be.
* If the key(text) of an element starts with one of the predefined values, a
* special component will be called: either a {@link JRadioButtonMenuItem}, a
* {@link JCheckBoxMenuItem} or a {@link JMenu} will be created.<br>
* <br>
* Project: <strong>envoy-client</strong><br>
* File: <strong>ContextMenu.java</strong><br>
* Created: <strong>17 Mar 2020</strong><br>
*
* @author Leon Hofmeister
* @since Envoy Client v0.1-beta
*/
public class ContextMenu extends JPopupMenu {
private static final long serialVersionUID = 2177146471226992104L;
/**
* If a key starts with this String, a {@link JCheckBoxMenuItem} will be created
*/
public static final String checkboxMenuItem = "ChBoMI";
/**
* If a key starts with this String, a {@link JRadioButtonMenuItem} will be
* created
*/
public static final String radioButtonMenuItem = "RaBuMI";
/**
* If a key starts with this String, a {@link JMenu} will be created
*/
public static final String subMenuItem = "SubMI";
private Map<String, ActionListener> items = new HashMap<>();
private Map<String, Icon> icons = new HashMap<>();
private Map<String, Integer> mnemonics = new HashMap<>();
private ButtonGroup radioButtonGroup = new ButtonGroup();
private boolean built = false;
/**
* @param parent the component which will call this
* {@link ContextMenu}
* @since Envoy Client v0.1-beta
*/
public ContextMenu(Component parent) { setInvoker(parent); }
/**
* @param label the string that a UI may use to display as a title
* for the pop-up menu
* @param parent the component which will call this
* {@link ContextMenu}
* @param itemsWithActions a map of all strings to be displayed with according
* actions
* @param itemIcons the icons to be displayed before a name, if wanted.
* Only keys in here will have an Icon displayed. More
* precisely, all keys here not included in the first
* map will be thrown out.
* @param itemMnemonics the keyboard shortcuts that need to be pressed to
* automatically execute the {@link JMenuItem} with the
* given text
* @since Envoy Client v0.1-beta
*/
public ContextMenu(String label, Component parent, Map<String, ActionListener> itemsWithActions, Map<String, Icon> itemIcons,
Map<String, Integer> itemMnemonics) {
super(label);
setInvoker(parent);
this.items = (itemsWithActions != null) ? itemsWithActions : items;
this.icons = (itemIcons != null) ? itemIcons : icons;
this.mnemonics = (itemMnemonics != null) ? itemMnemonics : mnemonics;
}
/**
* Prepares the PopupMenu to be displayed. Should only be used once all map
* values have been set.
*
* @return this instance of {@link ContextMenu} to allow chaining behind the
* constructor
* @since Envoy Client v0.1-beta
*/
public ContextMenu build() {
items.forEach((text, action) -> {
// case radio button wanted
AbstractButton item;
if (text.startsWith(radioButtonMenuItem)) {
item = new JRadioButtonMenuItem(text.substring(radioButtonMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
radioButtonGroup.add(item);
// case check box wanted
} else if (text.startsWith(checkboxMenuItem))
item = new JCheckBoxMenuItem(text.substring(checkboxMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
// case sub-menu wanted
else if (text.startsWith(subMenuItem)) item = new JMenu(text.substring(subMenuItem.length()));
else // normal JMenuItem wanted
item = new JMenuItem(text, icons.containsKey(text) ? icons.get(text) : null);
item.addActionListener(action);
if (mnemonics.containsKey(text)) item.setMnemonic(mnemonics.get(text));
add(item);
});
if (getInvoker() != null) {
getInvoker().addMouseListener(getShowingListener());
built = true;
}
return this;
}
/**
* @param label the string that a UI may use to display as a title for the
* pop-up menu.
* @since Envoy Client v0.1-beta
*/
public ContextMenu(String label) { super(label); }
private MouseAdapter getShowingListener() {
return new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) { action(e); }
@Override
public void mousePressed(MouseEvent e) { action(e); }
@Override
public void mouseReleased(MouseEvent e) { action(e); }
private void action(MouseEvent e) {
if (!built) build();
if (e.isPopupTrigger()) {
// hides the menu if already visible
if (!isVisible()) show(e.getComponent(), e.getX(), e.getY());
else setVisible(false);
}
}
};
}
/**
* Removes all subcomponents of this menu.
*
* @since Envoy Client v0.1-beta
*/
public void clear() {
removeAll();
items = new HashMap<>();
icons = new HashMap<>();
mnemonics = new HashMap<>();
}
/**
* @return the items
* @since Envoy Client v0.1-beta
*/
public Map<String, ActionListener> getItems() { return items; }
/**
* @param items the items with the displayed text and the according action to
* take once called
* @since Envoy Client v0.1-beta
*/
public void setItems(Map<String, ActionListener> items) { this.items = items; }
/**
* @return the icons
* @since Envoy Client v0.1-beta
*/
public Map<String, Icon> getIcons() { return icons; }
/**
* @param icons the icons to set
* @since Envoy Client v0.1-beta
*/
public void setIcons(Map<String, Icon> icons) { this.icons = icons; }
/**
* @return the mnemonics (the keyboard shortcuts that automatically execute the
* command for a {@link JMenuItem} with corresponding text)
* @since Envoy Client v0.1-beta
*/
public Map<String, Integer> getMnemonics() { return mnemonics; }
/**
* @param mnemonics the keyboard shortcuts that need to be pressed to
* automatically execute the {@link JMenuItem} with the given
* text
* @since Envoy Client v0.1-beta
*/
public void setMnemonics(Map<String, Integer> mnemonics) { this.mnemonics = mnemonics; }
}

View File

@ -0,0 +1,61 @@
package envoy.client.ui;
import java.awt.Image;
import java.io.IOException;
import java.util.EnumMap;
import java.util.EnumSet;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
/**
* Provides static utility methods for loading icons from the resource
* folder.<br>
* <br>
* Project: <strong>envoy-client</strong>
* File: <strong>IconUtil.java</strong>
* Created: <strong>16.03.2020</strong>
*
* @author Kai S. K. Engelbart
* @since Envoy Client v0.1-beta
*/
public class IconUtil {
private IconUtil() {}
/**
* Loads an icon from resource folder and scales it to a given size.
*
* @param path the path to the icon inside the resource folder
* @param size the size to scale the icon to
* @return the scaled icon
* @throws IOException if the loading process failed
* @since Envoy Client v0.1-beta
*/
public static ImageIcon load(String path, int size) throws IOException {
return new ImageIcon(ImageIO.read(IconUtil.class.getResourceAsStream(path)).getScaledInstance(size, size, Image.SCALE_SMOOTH));
}
/**
*
* Loads icons specified by an enum. The images have to be named like the
* lowercase enum constants with {@code .png} extension and be located inside a
* folder with the lowercase name of the enum, which must be contained inside
* the {@code /icons} folder.
*
* @param <T> the enum that specifies the icons to load
* @param enumClass the class of the enum
* @param size the size to scale the icons to
* @return a map containing the loaded icons with the corresponding enum
* constants as keys
* @throws IOException if the loading process failed
* @since Envoy Client v0.1-beta
*/
public static <T extends Enum<T>> EnumMap<T, ImageIcon> loadByEnum(Class<T> enumClass, int size) throws IOException {
var icons = new EnumMap<T, ImageIcon>(enumClass);
var path = "/icons/" + enumClass.getSimpleName().toLowerCase() + "/";
for (var e : EnumSet.allOf(enumClass))
icons.put(e, load(path + e.toString().toLowerCase() + ".png", size));
return icons;
}
}

View File

@ -1,93 +0,0 @@
package envoy.client.ui;
import java.awt.BorderLayout;
import java.awt.Font;
import java.text.SimpleDateFormat;
import javax.swing.*;
import envoy.client.data.Settings;
import envoy.client.ui.list.ComponentList;
import envoy.client.ui.list.ComponentListCellRenderer;
import envoy.data.Message;
/**
* Defines how a message is displayed.<br>
* <br>
* Project: <strong>envoy-client</strong><br>
* File: <strong>MessageListRenderer.java</strong><br>
* Created: <strong>19 Oct 2019</strong><br>
*
* @author Kai S. K. Engelbart
* @author Maximilian K&auml;fer
* @author Leon Hofmeister
* @since Envoy v0.1-alpha
*/
public class MessageListRenderer implements ComponentListCellRenderer<Message> {
private JTextArea messageTextArea;
@Override
public JPanel getListCellComponent(ComponentList<? extends Message> list, Message value, boolean isSelected) {
final JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
final Theme theme = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme());
// Panel background
panel.setBackground(isSelected ? theme.getSelectionColor() : theme.getCellColor());
// TODO: Handle message attachments
final String state = value.getStatus().toString();
final String date = new SimpleDateFormat("dd.MM.yyyy HH:mm").format(value.getCreationDate());
final String text = value.getText();
// The Label that displays the creation date of a message
JLabel dateLabel = new JLabel(date);
// Set the date color to be the value of DateColorChat
dateLabel.setForeground(theme.getDateColor());
panel.add(dateLabel, BorderLayout.NORTH);
// The JTextArea that displays the text content of a message and its status
messageTextArea = new JTextArea(text + System.getProperty("line.separator"));
messageTextArea.setLineWrap(true);
messageTextArea.setWrapStyleWord(true);
messageTextArea.setAlignmentX(0.5f);
messageTextArea.setForeground(theme.getMessageTextColor());
messageTextArea.setBackground(panel.getBackground());
messageTextArea.setEditable(false);
panel.add(messageTextArea, BorderLayout.CENTER);
JLabel statusLabel = new JLabel(state);
statusLabel.setFont(new Font("Arial", Font.BOLD, 14));
Color statusColor;
switch (value.getStatus()) {
case WAITING:
statusColor = Color.gray;
break;
case SENT:
statusColor = Color.blue;
break;
case RECEIVED:
statusColor = Color.yellow;
break;
case READ:
statusColor = Color.green;
break;
default:
statusColor = theme.getMessageTextColor();
break;
}
statusLabel.setForeground(statusColor);
statusLabel.setBackground(panel.getBackground());
panel.add(statusLabel, BorderLayout.SOUTH);
// Define some space to the messages below
panel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(), BorderFactory.createEtchedBorder()));
return panel;
}
}

View File

@ -16,7 +16,7 @@ import envoy.exception.EnvoyException;
* Created: <strong>3 Dec 2019</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public class StatusTrayIcon {
@ -41,7 +41,7 @@ public class StatusTrayIcon {
* notifications are displayed
* @throws EnvoyException if the currently used OS does not support the System
* Tray API
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public StatusTrayIcon(Window focusTarget) throws EnvoyException {
if (!SystemTray.isSupported()) throw new EnvoyException("The Envoy tray icon is not supported.");
@ -85,7 +85,7 @@ public class StatusTrayIcon {
*
* @throws EnvoyException if the status icon could not be attaches to the system
* tray for system-internal reasons
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public void show() throws EnvoyException {
try {

37
src/main/java/envoy/client/ui/Theme.java Normal file → Executable file
View File

@ -10,7 +10,7 @@ import java.util.Map;
* Created: <strong>23 Nov 2019</strong><br>
*
* @author Maximilian K&auml;fer
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public class Theme implements Serializable {
@ -29,15 +29,16 @@ public class Theme implements Serializable {
* elements
* @param interactableBackgroundColor the color of interactable background UI
* elements
* @param messageColorChat the color of chat messages
* @param textColor the color normal text should be displayed
* in
* @param dateColorChat the color of chat message metadata
* @param selectionColor the section color
* @param typingMessageColor the color of currently typed messages
* @param userNameColor the color of user names
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Theme(String themeName, Color backgroundColor, Color cellColor, Color interactableForegroundColor, Color interactableBackgroundColor,
Color messageColorChat, Color dateColorChat, Color selectionColor, Color typingMessageColor, Color userNameColor) {
Color textColor, Color dateColorChat, Color selectionColor, Color typingMessageColor, Color userNameColor) {
this.themeName = themeName;
@ -45,7 +46,7 @@ public class Theme implements Serializable {
colors.put("cellColor", cellColor);
colors.put("interactableForegroundColor", interactableForegroundColor);
colors.put("interactableBackgroundColor", interactableBackgroundColor);
colors.put("messageColorChat", messageColorChat);
colors.put("textColor", textColor);
colors.put("dateColorChat", dateColorChat);
colors.put("selectionColor", selectionColor);
colors.put("typingMessageColor", typingMessageColor);
@ -58,7 +59,7 @@ public class Theme implements Serializable {
*
* @param name the name of the {@link Theme}
* @param other the {@link Theme} to copy
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Theme(String name, Theme other) {
themeName = name;
@ -68,69 +69,69 @@ public class Theme implements Serializable {
/**
* @return a {@code Map<String, Color>} of all colors defined for this theme
* with their names as keys
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Map<String, Color> getColors() { return colors; }
/**
* @return name of the theme
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public String getThemeName() { return themeName; }
/**
* @return interactableForegroundColor
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Color getInteractableForegroundColor() { return colors.get("interactableForegroundColor"); }
/**
* @return the {@link Color} in which the text content of a message should be
* displayed
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Color getMessageTextColor() { return colors.get("messageColorChat"); }
public Color getTextColor() { return colors.get("textColor"); }
/**
* @return the {@link Color} in which the creation date of a message should be
* displayed
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Color getDateColor() { return colors.get("dateColorChat"); }
/**
* @return selectionColor
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Color getSelectionColor() { return colors.get("selectionColor"); }
/**
* @return typingMessageColor
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Color getTypingMessageColor() { return colors.get("typingMessageColor"); }
/**
* @return backgroundColor
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Color getBackgroundColor() { return colors.get("backgroundColor"); }
/**
* @return cellColor
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Color getCellColor() { return colors.get("cellColor"); }
/**
* @return interactableBackgroundColor
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Color getInteractableBackgroundColor() { return colors.get("interactableBackgroundColor"); }
/**
* @return userNameColor
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public Color getUserNameColor() { return colors.get("userNameColor"); }

View File

@ -1,8 +1,13 @@
package envoy.client.ui;
package envoy.client.ui.container;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.awt.event.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -18,8 +23,16 @@ import envoy.client.event.MessageCreationEvent;
import envoy.client.event.ThemeChangeEvent;
import envoy.client.net.Client;
import envoy.client.net.WriteProxy;
import envoy.client.ui.Theme;
import envoy.client.ui.list.ComponentList;
import envoy.client.ui.list.ComponentListModel;
import envoy.client.ui.list.ComponentList.SelectionMode;
import envoy.client.ui.list.Model;
import envoy.client.ui.list_component.ContactSearchComponent;
import envoy.client.ui.list_component.MessageComponent;
import envoy.client.ui.primary.PrimaryButton;
import envoy.client.ui.primary.PrimaryScrollPane;
import envoy.client.ui.primary.PrimaryTextArea;
import envoy.client.ui.renderer.UserListRenderer;
import envoy.client.ui.settings.SettingsScreen;
import envoy.data.Message;
import envoy.data.Message.MessageStatus;
@ -36,13 +49,12 @@ import envoy.util.EnvoyLog;
* @author Kai S. K. Engelbart
* @author Maximilian K&auml;fer
* @author Leon Hofmeister
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public class ChatWindow extends JFrame {
/**
* This int defines the maximum amount of chars allowed per message. Currently
* set at 200.
* This integer defines the maximum amount of chars allowed per message.
*
* @since Envoy 0.1-beta
*/
@ -59,11 +71,12 @@ public class ChatWindow extends JFrame {
private PrimaryTextArea messageEnterTextArea = new PrimaryTextArea(space);
private JList<User> userList = new JList<>();
private DefaultListModel<User> userListModel = new DefaultListModel<>();
private ComponentList<Message> messageList = new ComponentList<>(new MessageListRenderer());
private ComponentList<Message> messageList = new ComponentList<>();
private PrimaryScrollPane scrollPane = new PrimaryScrollPane();
private JTextPane textPane = new JTextPane();
private PrimaryButton postButton = new PrimaryButton("Post");
private PrimaryButton settingsButton = new PrimaryButton("Settings");
private JPopupMenu contextMenu;
// Contacts Header
private JPanel contactsHeader = new JPanel();
@ -75,9 +88,8 @@ public class ChatWindow extends JFrame {
private final PrimaryButton cancelButton = new PrimaryButton("x");
private final PrimaryTextArea searchField = new PrimaryTextArea(space);
private final PrimaryScrollPane scrollForPossibleContacts = new PrimaryScrollPane();
private final ContactsSearchRenderer contactRenderer = new ContactsSearchRenderer();
private final ComponentListModel<User> contactsModel = new ComponentListModel<>();
private final ComponentList<User> contactList = new ComponentList<>(contactRenderer);
private final Model<User> contactsModel = new Model<>();
private final ComponentList<User> contactList = new ComponentList<User>().setRenderer(ContactSearchComponent::new);
private static final Logger logger = EnvoyLog.getLogger(ChatWindow.class);
@ -91,11 +103,12 @@ public class ChatWindow extends JFrame {
* Initializes a {@link JFrame} with UI elements used to send and read messages
* to different users.
*
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public ChatWindow() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 600, 800);
setMinimumSize(new Dimension(400, 300));
setTitle("Envoy");
setLocationRelativeTo(null);
setIconImage(Toolkit.getDefaultToolkit().createImage(getClass().getClassLoader().getResource("envoy_logo.png")));
@ -106,19 +119,46 @@ public class ChatWindow extends JFrame {
gbl_contentPane.columnWidths = new int[] { 1, 1, 1 };
gbl_contentPane.rowHeights = new int[] { 1, 1, 1, 1 };
gbl_contentPane.columnWeights = new double[] { 0.03, 1.0, 0.1 };
gbl_contentPane.rowWeights = new double[] { 0.03, 0.001, 1.0, 0.005 };
gbl_contentPane.rowWeights = new double[] { 0.03, 0.001, 1.0, 0.001 };
contentPane.setLayout(gbl_contentPane);
messageList.setBorder(new EmptyBorder(space, space, space, space));
messageList.setSelectionMode(SelectionMode.SINGLE);
messageList.setSelectionHandler((message, comp, isSelected) -> {
final var theme = Settings.getInstance().getCurrentTheme();
comp.setBackground(isSelected ? theme.getSelectionColor() : theme.getCellColor());
// ContextMenu
Map<String, ActionListener> commands = Map.of("forward selected message", evt -> {
final Message selectedMessage = messageList.getSingleSelectedElement();
List<User> chosenContacts = ContactsChooserDialog
.showForwardingDialog("Forward selected message to", null, selectedMessage, localDb.getUsers().values());
if (chosenContacts != null && chosenContacts.size() > 0) forwardMessage(selectedMessage, chosenContacts.toArray(new User[0]));
}, "copy", evt -> {
// TODO should be enhanced to allow also copying of message attachments,
// especially pictures
StringSelection copy = new StringSelection(messageList.getSingleSelectedElement().getText());
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(copy, copy);
// TODO insert implementation to edit and delete messages
}, "delete", evt -> {}, "edit", evt -> {}, "quote", evt -> {});
if (isSelected) {
contextMenu = new ContextMenu(null, comp, commands, null, null).build();
contextMenu.show(comp, 0, 0);
}
});
scrollPane.setViewportView(messageList);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.addComponentListener(new ComponentAdapter() {
// updates list elements when list is resized
// Update list elements when scroll pane (and thus list) is resized
@Override
public void componentResized(ComponentEvent e) { messageList.synchronizeModel(); }
public void componentResized(ComponentEvent e) {
messageList.setMaximumSize(new Dimension(scrollPane.getWidth(), Integer.MAX_VALUE));
messageList.synchronizeModel();
}
});
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
GridBagConstraints gbc_scrollPane = new GridBagConstraints();
gbc_scrollPane.fill = GridBagConstraints.BOTH;
@ -135,7 +175,10 @@ public class ChatWindow extends JFrame {
messageEnterTextArea.addInputMethodListener(new InputMethodListener() {
@Override
public void inputMethodTextChanged(InputMethodEvent event) { checkMessageTextLength(); }
public void inputMethodTextChanged(InputMethodEvent event) {
checkMessageTextLength();
checkPostButton(messageEnterTextArea.getText());
}
@Override
public void caretPositionChanged(InputMethodEvent event) {}
@ -146,32 +189,30 @@ 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)
&& postButton.isEnabled())
postMessage();
// Checking if text is too long
checkMessageTextLength();
checkPostButton(messageEnterTextArea.getText());
}
});
GridBagConstraints gbc_scrollPaneForTextInput = new GridBagConstraints();
gbc_scrollPaneForTextInput.fill = GridBagConstraints.BOTH;
gbc_scrollPaneForTextInput.gridx = 1;
gbc_scrollPaneForTextInput.gridy = 3;
gbc_scrollPaneForTextInput.insets = insets;
contentPane.add(messageEnterTextArea, gbc_scrollPaneForTextInput);
GridBagConstraints gbc_messageEnterTextArea = new GridBagConstraints();
gbc_messageEnterTextArea.fill = GridBagConstraints.BOTH;
gbc_messageEnterTextArea.gridx = 1;
gbc_messageEnterTextArea.gridy = 3;
gbc_messageEnterTextArea.insets = insets;
contentPane.add(messageEnterTextArea, gbc_messageEnterTextArea);
// Post Button
GridBagConstraints gbc_postButton = new GridBagConstraints();
gbc_postButton.fill = GridBagConstraints.BOTH;
gbc_postButton.gridx = 2;
gbc_postButton.gridy = 3;
gbc_postButton.insets = insets;
postButton.addActionListener((evt) -> { postMessage(); });
postButton.setEnabled(false);
contentPane.add(postButton, gbc_postButton);
// Settings Button
@ -353,11 +394,11 @@ public class ChatWindow extends JFrame {
gbc_addContact.gridy = 0;
gbc_addContact.insets = insets;
addContact.addActionListener((evt) -> { drawContactSearch(gbc_searchPane); });
addContact.addActionListener(evt -> drawContactSearch(gbc_searchPane));
contactsHeader.add(addContact, gbc_addContact);
applyTheme(Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()));
applyTheme(Settings.getInstance().getCurrentTheme());
contentPane.add(contactsHeader, gbc_contactsHeader);
contentPane.revalidate();
@ -440,15 +481,16 @@ public class ChatWindow extends JFrame {
* Used to immediately reload the {@link ChatWindow} when settings were changed.
*
* @param theme the theme to change colors into
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
private void applyTheme(Theme theme) {
// contentPane
contentPane.setBackground(theme.getBackgroundColor());
contentPane.setForeground(theme.getUserNameColor());
// messageList
messageList.setForeground(theme.getMessageTextColor());
messageList.setForeground(theme.getTextColor());
messageList.setBackground(theme.getCellColor());
messageList.synchronizeModel();
// scrollPane
scrollPane.applyTheme(theme);
scrollPane.autoscroll();
@ -482,33 +524,68 @@ public class ChatWindow extends JFrame {
searchField.setForeground(theme.getUserNameColor());
cancelButton.setBackground(theme.getInteractableBackgroundColor());
cancelButton.setForeground(theme.getInteractableForegroundColor());
contactList.setForeground(theme.getMessageTextColor());
contactList.setForeground(theme.getTextColor());
contactList.setBackground(theme.getCellColor());
scrollForPossibleContacts.applyTheme(theme);
}
/**
* Sends a new message to the server based on the text entered in the textArea.
*
* @since Envoy Client v0.1-beta
*/
private void postMessage() {
if (userList.isSelectionEmpty()) {
JOptionPane.showMessageDialog(this, "Please select a recipient!", "Cannot send message", JOptionPane.INFORMATION_MESSAGE);
return;
}
String text = messageEnterTextArea.getText().trim();
if (!text.isEmpty()) checkMessageTextLength();
if (!messageEnterTextArea.getText().isEmpty()) try {
checkMessageTextLength();
// Create message
final Message message = new MessageBuilder(localDb.getUser().getId(), currentChat.getRecipient().getId(), localDb.getIdGenerator())
.setText(messageEnterTextArea.getText())
.setText(text)
.build();
sendMessage(message);
// Clear text field
messageEnterTextArea.setText("");
postButton.setEnabled(false);
}
/**
* Forwards a message.
*
* @param message the message to forward
* @param recipient the new recipient of the message
* @since Envoy Client v0.1-beta
*/
private void forwardMessage(Message message, User... recipients) {
Arrays.stream(recipients).forEach(recipient -> {
if (message != null && recipients != null) sendMessage(new MessageBuilder(message, recipient.getId(), localDb.getIdGenerator()).build());
else throw new NullPointerException("No recipient or no message selected");
});
}
@SuppressWarnings("unused")
private void forwardMessages(Collection<Message> messages, User... recipients) {
messages.forEach(message -> { forwardMessage(message, recipients); });
}
/**
* Sends a {@link Message} to the server.
*
* @param message the message to send
* @since Envoy Client v0.1-beta
*/
private void sendMessage(final Message message) {
try {
// Send message
writeProxy.writeMessage(message);
// Add message to PersistentLocalDb and update UI
currentChat.appendMessage(message);
// Clear text field
messageEnterTextArea.setText("");
// Update UI
revalidate();
repaint();
@ -525,7 +602,7 @@ public class ChatWindow extends JFrame {
private void readCurrentChat() {
try {
currentChat.read(writeProxy);
messageList.synchronizeModel();
if (messageList.getRenderer() != null) messageList.synchronizeModel();
} catch (IOException e) {
e.printStackTrace();
logger.log(Level.WARNING, "Couldn't notify server about message status change", e);
@ -562,13 +639,15 @@ public class ChatWindow extends JFrame {
* @param writeProxy the write proxy used to send messages and status change
* events to the server or cache them inside the local
* database
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void initContent(Client client, LocalDb localDb, WriteProxy writeProxy) {
this.client = client;
this.localDb = localDb;
this.writeProxy = writeProxy;
messageList.setRenderer((list, message) -> new MessageComponent(list, message, client.getSender().getId()));
// Load users and chats
new Thread(() -> {
localDb.getUsers().values().forEach(user -> {
@ -589,7 +668,7 @@ public class ChatWindow extends JFrame {
* {@link ChatWindow#MAX_MESSAGE_LENGTH}
* and splits the text into the allowed part, if that is the case.
*
* @since Envoy v0.1-beta
* @since Envoy Client v0.1-beta
*/
private void checkMessageTextLength() {
String input = messageEnterTextArea.getText();
@ -603,4 +682,6 @@ public class ChatWindow extends JFrame {
JOptionPane.WARNING_MESSAGE);
}
}
private void checkPostButton(String text) { postButton.setEnabled(!text.trim().isBlank()); }
}

View File

@ -0,0 +1,148 @@
package envoy.client.ui.container;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import envoy.client.data.Settings;
import envoy.client.ui.Theme;
import envoy.client.ui.list.ComponentList;
import envoy.client.ui.list.ComponentList.SelectionMode;
import envoy.client.ui.list.Model;
import envoy.client.ui.list_component.UserComponent;
import envoy.data.Message;
import envoy.data.User;
/**
* This class defines a dialog to choose contacts from.<br>
* <br>
* Project: <strong>envoy-client</strong><br>
* File: <strong>ContactsChooserDialog.java</strong><br>
* Created: <strong>15 Mar 2020</strong><br>
*
* @author Leon Hofmeister
* @since Envoy Client v0.1-beta
*/
public class ContactsChooserDialog extends JDialog {
private static final long serialVersionUID = -5774558118579032256L;
private ComponentList<User> contactList = new ComponentList<User>().setModel(new Model<User>())
.setRenderer((list, user) -> new UserComponent(user));
private JButton okButton = new JButton("Ok");
private JButton cancelButton = new JButton("Cancel");
private final Theme theme = Settings.getInstance().getCurrentTheme();
private final JPanel contentPanel = new JPanel();
/**
* Shows a modal contacts-chooser dialog and blocks until the
* dialog is hidden. If the user presses the "OK" button, then
* this method hides/disposes the dialog and returns the selected element (has
* yet
* to be casted back to its original type due to the limitations of Generics in
* Java).
* If the user presses the "Cancel" button or closes the dialog without
* pressing "OK", then this method disposes the dialog and returns an empty
* <code>ArrayList</code>.
*
* @param title the title of the dialog
* @param parent this @{@link Component} will be parsed to
* {@link java.awt.Window#setLocationRelativeTo(Component)} in
* order to change the location of the dialog
* @param message the {@link Message} to display on top of the Dialog
* @param users the users that should be displayed
* @return the selected Element (yet has to be casted to the wanted type due to
* the Generics limitations in Java)
* @since Envoy Client v0.1-beta
*/
public static List<User> showForwardingDialog(String title, Component parent, Message message, Collection<User> users) {
ContactsChooserDialog dialog = new ContactsChooserDialog(parent);
dialog.setTitle(title);
dialog.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
dialog.addCancelButtonActionListener(e -> dialog.dispose());
List<User> results = new ArrayList<>();
dialog.addOkButtonActionListener(e -> {
results.addAll(dialog.getContactList().getSelectedElements());
dialog.dispose();
});
Model<User> contactListModel = dialog.getContactList().getModel();
users.forEach(contactListModel::add);
dialog.setModalityType(ModalityType.APPLICATION_MODAL);
dialog.setVisible(true);
return results;
}
/**
* @param parent this @{@link Component} will be parsed to
* {@link java.awt.Window#setLocationRelativeTo(Component)}
* @since Envoy Client v0.1-beta
*/
private ContactsChooserDialog(Component parent) {
contactList.setSelectionMode(SelectionMode.MULTIPLE);
contactList.setSelectionHandler((user, comp, isSelected) -> {
final var theme = Settings.getInstance().getCurrentTheme();
comp.setBackground(isSelected ? theme.getSelectionColor() : theme.getCellColor());
});
setLocationRelativeTo(parent);
getContentPane().setLayout(new BorderLayout());
setBackground(theme.getBackgroundColor());
setForeground(theme.getTextColor());
setSize(400, 400);
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
getContentPane().add(contentPanel, BorderLayout.CENTER);
contentPanel.setLayout(new BorderLayout(0, 0));
contentPanel.add(contactList, BorderLayout.CENTER);
{
JPanel buttonPane = new JPanel();
getContentPane().add(buttonPane, BorderLayout.SOUTH);
{
okButton = new JButton("OK");
okButton.setMnemonic(KeyEvent.VK_ENTER);
okButton.setActionCommand("OK");
buttonPane.setLayout(new BorderLayout(0, 0));
buttonPane.add(okButton, BorderLayout.EAST);
getRootPane().setDefaultButton(okButton);
}
{
cancelButton = new JButton("Cancel");
cancelButton.setActionCommand("Cancel");
buttonPane.add(cancelButton, BorderLayout.WEST);
}
}
applyTheme(Settings.getInstance().getCurrentTheme());
}
private void applyTheme(Theme theme) {
contentPanel.setBackground(theme.getBackgroundColor());
contentPanel.setForeground(theme.getTextColor());
contactList.setBackground(theme.getCellColor());
okButton.setBackground(theme.getInteractableBackgroundColor());
okButton.setForeground(theme.getTextColor());
cancelButton.setBackground(theme.getInteractableBackgroundColor());
cancelButton.setForeground(theme.getTextColor());
}
/**
* @return the underlying {@link ComponentList}
* @since Envoy Client v0.1-beta
*/
private ComponentList<User> getContactList() { return contactList; }
private void addOkButtonActionListener(ActionListener l) { okButton.addActionListener(l); }
private void addCancelButtonActionListener(ActionListener l) { cancelButton.addActionListener(l); }
}

View File

@ -0,0 +1,255 @@
package envoy.client.ui.container;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;
import javax.swing.*;
import envoy.client.data.Settings;
import envoy.client.ui.Theme;
/**
* This class defines a menu that will be automatically called if
* {@link MouseEvent#isPopupTrigger()} returns true for the parent component.
* The user has the possibility to directly add actions to be performed when
* clicking on the element with the selected String. Additionally, for each
* element an {@link Icon} can be added, but it must not be.
* If the key(text) of an element starts with one of the predefined values, a
* special component will be called: either a {@link JRadioButtonMenuItem}, a
* {@link JCheckBoxMenuItem} or a {@link JMenu} will be created.<br>
* <br>
* Project: <strong>envoy-client</strong><br>
* File: <strong>ContextMenu.java</strong><br>
* Created: <strong>17 Mar 2020</strong><br>
*
* @author Leon Hofmeister
* @since Envoy Client v0.1-beta
*/
public class ContextMenu extends JPopupMenu {
private static final long serialVersionUID = 2177146471226992104L;
/**
* If a key starts with this String, a {@link JCheckBoxMenuItem} will be created
*/
public static final String checkboxMenuItem = "ChBoMI";
/**
* If a key starts with this String, a {@link JRadioButtonMenuItem} will be
* created
*/
public static final String radioButtonMenuItem = "RaBuMI";
/**
* If a key starts with this String, a {@link JMenu} will be created
*/
public static final String subMenuItem = "SubMI";
private Map<String, ActionListener> items = new HashMap<>();
private Map<String, Icon> icons = new HashMap<>();
private Map<String, Integer> mnemonics = new HashMap<>();
private ButtonGroup radioButtonGroup = new ButtonGroup();
private boolean built = false;
private boolean visible = false;
/**
* @param parent the component which will call this
* {@link ContextMenu}
* @since Envoy Client v0.1-beta
*/
public ContextMenu(Component parent) {
setInvoker(parent);
setOpaque(true);
}
/**
* @param label the string that a UI may use to display as a title
* for the pop-up menu
* @param parent the component which will call this
* {@link ContextMenu}
* @param itemsWithActions a map of all strings to be displayed with according
* actions
* @param itemIcons the icons to be displayed before a name, if wanted.
* Only keys in here will have an Icon displayed. More
* precisely, all keys here not included in the first
* map will be thrown out.
* @param itemMnemonics the keyboard shortcuts that need to be pressed to
* automatically execute the {@link JMenuItem} with the
* given text
* @since Envoy Client v0.1-beta
*/
public ContextMenu(String label, Component parent, Map<String, ActionListener> itemsWithActions, Map<String, Icon> itemIcons,
Map<String, Integer> itemMnemonics) {
this(label);
setInvoker(parent);
this.items = (itemsWithActions != null) ? itemsWithActions : items;
this.icons = (itemIcons != null) ? itemIcons : icons;
this.mnemonics = (itemMnemonics != null) ? itemMnemonics : mnemonics;
}
/**
* @param label the string that a UI may use to display as a title for the
* pop-up menu.
* @since Envoy Client v0.1-beta
*/
public ContextMenu(String label) {
super(label);
setOpaque(true);
}
/**
* Prepares the PopupMenu to be displayed. Should only be used once all map
* values have been set.
*
* @return this instance of {@link ContextMenu} to allow chaining behind the
* constructor
* @since Envoy Client v0.1-beta
*/
public ContextMenu build() {
items.forEach((text, action) -> {
// case radio button wanted
AbstractButton item;
if (text.startsWith(radioButtonMenuItem)) {
item = new JRadioButtonMenuItem(text.substring(radioButtonMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
radioButtonGroup.add(item);
// case check box wanted
} else if (text.startsWith(checkboxMenuItem))
item = new JCheckBoxMenuItem(text.substring(checkboxMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
// case sub-menu wanted
else if (text.startsWith(subMenuItem)) item = new JMenu(text.substring(subMenuItem.length()));
else // normal JMenuItem wanted
item = new JMenuItem(text, icons.containsKey(text) ? icons.get(text) : null);
item.addActionListener(action);
item.setOpaque(true);
if (mnemonics.containsKey(text)) item.setMnemonic(mnemonics.get(text));
add(item);
});
getInvoker().addMouseListener(getShowingListener());
applyTheme(Settings.getInstance().getCurrentTheme());
built = true;
return this;
}
private MouseAdapter getShowingListener() {
return new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) { action(e); }
@Override
public void mousePressed(MouseEvent e) { action(e); }
@Override
public void mouseReleased(MouseEvent e) { action(e); }
private void action(MouseEvent e) {
if (!built) build();
if (e.isPopupTrigger()) {
// hides the menu if already visible
visible = !visible;
if (visible) show(e.getComponent(), e.getX(), e.getY());
else setVisible(false);
}
}
};
}
/**
* Removes all subcomponents of this menu.
*
* @since Envoy Client v0.1-beta
*/
public void clear() {
removeAll();
items = new HashMap<>();
icons = new HashMap<>();
mnemonics = new HashMap<>();
}
/**
* @return the items
* @since Envoy Client v0.1-beta
*/
public Map<String, ActionListener> getItems() { return items; }
/**
* @param items the items with the displayed text and the according action to
* take once called
* @since Envoy Client v0.1-beta
*/
public void setItems(Map<String, ActionListener> items) { this.items = items; }
/**
* @return the icons
* @since Envoy Client v0.1-beta
*/
public Map<String, Icon> getIcons() { return icons; }
/**
* @param icons the icons to set
* @since Envoy Client v0.1-beta
*/
public void setIcons(Map<String, Icon> icons) { this.icons = icons; }
/**
* @return the mnemonics (the keyboard shortcuts that automatically execute the
* command for a {@link JMenuItem} with corresponding text)
* @since Envoy Client v0.1-beta
*/
public Map<String, Integer> getMnemonics() { return mnemonics; }
/**
* @param mnemonics the keyboard shortcuts that need to be pressed to
* automatically execute the {@link JMenuItem} with the given
* text
* @since Envoy Client v0.1-beta
*/
public void setMnemonics(Map<String, Integer> mnemonics) { this.mnemonics = mnemonics; }
/**
* {@inheritDoc}<br>
* Additionally sets the foreground of all subcomponents of this
* {@link ContextMenu}.
*
* @since Envoy Client v0.1-beta
*/
@Override
public void setForeground(Color color) {
super.setForeground(color);
for (MenuElement element : getSubElements())
((Component) element).setForeground(color);
}
/**
* {@inheritDoc}<br>
* Additionally sets the background of all subcomponents of this
* {@link ContextMenu}.
*
* @since Envoy Client v0.1-beta
*/
@Override
public void setBackground(Color color) {
super.setBackground(color);
for (MenuElement element : getSubElements())
((Component) element).setBackground(color);
}
/**
* Sets the fore- and background of all elements contained in this
* {@link ContextMenu}
* This method is to be only used by Envoy as {@link Theme} is an
* Envoy-exclusive object.
*
* @param theme the theme to use
* @since Envoy Client v0.1-beta
*/
protected void applyTheme(Theme theme) {
setBackground(theme.getCellColor());
setForeground(theme.getTextColor());
}
}

View File

@ -1,4 +1,4 @@
package envoy.client.ui;
package envoy.client.ui.container;
import java.awt.*;
import java.awt.event.ItemEvent;
@ -16,6 +16,8 @@ import javax.swing.border.EmptyBorder;
import envoy.client.data.*;
import envoy.client.event.HandshakeSuccessfulEvent;
import envoy.client.net.Client;
import envoy.client.ui.Theme;
import envoy.client.ui.primary.PrimaryButton;
import envoy.data.LoginCredentials;
import envoy.data.Message;
import envoy.data.User;
@ -31,7 +33,7 @@ import envoy.util.EnvoyLog;
*
* @author Kai S. K. Engelbart
* @author Maximilian K&auml;fer
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public class LoginDialog extends JDialog {
@ -72,7 +74,7 @@ public class LoginDialog extends JDialog {
* @param localDb the local database in which data is persisted
* @param receivedMessageCache the cache that stored messages received during
* the handshake
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public LoginDialog(Client client, LocalDb localDb, Cache<Message> receivedMessageCache) {
this.client = client;
@ -281,7 +283,7 @@ public class LoginDialog extends JDialog {
/**
* Resets the text stored in the password fields.
*
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
private void clearPasswordFields() {
passwordField.setText(null);
@ -289,7 +291,7 @@ public class LoginDialog extends JDialog {
}
private void setTheme() {
Theme theme = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme());
Theme theme = Settings.getInstance().getCurrentTheme();
// Panels
contentPanel.setBackground(theme.getBackgroundColor());
@ -335,7 +337,7 @@ public class LoginDialog extends JDialog {
/**
* Shuts the system down properly if the login was aborted.
*
* @since Envoy v0.1-beta
* @since Envoy Client v0.1-beta
*/
private void abortLogin() {
logger.info("The login process has been cancelled. Exiting...");

View File

@ -0,0 +1,13 @@
/**
* This package contains all graphical Containers, like Dialogs and Frames.<br>
* <br>
* Project: <strong>envoy-client</strong><br>
* File: <strong>package-info.java</strong><br>
* Created: <strong>16 Mar 2020</strong><br>
*
* @author Leon Hofmeister
* @author Kai S. K. Engelbart
* @author Maximilian K&auml;fer
* @since Envoy Client v0.1-beta
*/
package envoy.client.ui.container;

View File

@ -3,12 +3,14 @@ package envoy.client.ui.list;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashSet;
import java.util.Set;
import javax.swing.*;
/**
* Provides a vertical list layout of components provided in a
* {@link ComponentListModel}. Similar to {@link javax.swing.JList} but capable
* {@link Model}. Similar to {@link javax.swing.JList} but capable
* of rendering {@link JPanel}s.<br>
* <br>
* Project: <strong>envoy-client</strong><br>
@ -17,141 +19,90 @@ import javax.swing.*;
*
* @param <E> the type of object displayed in this list
* @author Kai S. K. Engelbart
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public class ComponentList<E> extends JPanel {
private ComponentListModel<E> model;
private ComponentListCellRenderer<E> renderer;
private int currentSelection = -1;
private Model<E> model;
private Renderer<E> renderer;
private SelectionHandler<E> selectionHandler;
private SelectionMode selectionMode = SelectionMode.NONE;
private Set<Integer> selection = new HashSet<>();
private static final long serialVersionUID = 1759644503942876737L;
/**
* Creates an instance of {@link ComponentList}.
* Defines the possible modes of selection that can be performed by the user
*
* @param renderer the list cell renderer used to display elements provided by
* the {@link ComponentListModel}
* @since Envoy v0.3-alpha
* @since Envoy Client v0.1-beta
*/
public ComponentList(ComponentListCellRenderer<E> renderer) {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
this.renderer = renderer;
public static enum SelectionMode {
/**
* Selection is completely ignored.
*/
NONE,
/**
* Only a single element can be selected.
*/
SINGLE,
/**
* Multiple elements can be selected regardless of their position.
*/
MULTIPLE
}
/**
* 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
* @since Envoy Client v0.3-alpha
*/
public ComponentList(ComponentListModel<E> model, ComponentListCellRenderer<E> renderer) {
this(renderer);
this.model = model;
setModel(model);
}
/**
* Sets the list model providing the list elements to render. The rendered
* components will be synchronized with the contents of the new model or removed
* if the new model is {@code null}.
*
* @param model the list model to set
* @since Envoy v0.3-alpha
*/
public void setModel(ComponentListModel<E> model) {
// Remove old model
if (this.model != null) this.model.setComponentList(null);
// Synchronize with new model
this.model = model;
if (model != null) this.model.setComponentList(this);
synchronizeModel();
}
public ComponentList() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); }
/**
* Removes all child components and then adds all components representing the
* elements of the {@link ComponentListModel}.
* elements of the {@link Model}.
*
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void synchronizeModel() {
if (model != null) {
removeAll();
if (model != null) model.forEach(this::add);
model.forEach(this::addElement);
revalidate();
}
/**
* 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(elem, getComponentCount(), false); }
/**
* Adds an object to the list by rendering it with the current
* {@link ComponentListRenderer}.
*
* @param elem the element to add
* @param index the index at which to add the element
* @param isSelected the selection state of the element
* @since Envoy v0.1-beta
*/
private void add(E elem, int index, boolean isSelected) {
final JComponent component = renderer.getListCellComponent(this, elem, isSelected);
component.addMouseListener(getSelectionListener(index));
add(component, index);
}
/**
* @param componentIndex the index of the list component to which the mouse
* listener will be added
* @return a mouse listener calling the
* {@link ComponentList#componentSelected(int)} method with the
* component's index when a left click is performed by the user
* @since Envoy v0.1-beta
*/
private MouseListener getSelectionListener(int componentIndex) {
return new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) componentSelected(componentIndex); }
};
}
/**
* Gets called when a list component has been clicked on by the user. Any
* previous selections are then removed and the selected component gets
* redrawn.<br>
* <br>
* If the currently selected component gets selected again, the selection is
* removed.
* Selects a list element by index. If the element is already selected, it is
* removed from the selection.
*
* @param index the index of the selected component
* @since Envoy v0.1-beta
* @since Envoy Client v0.1-beta
*/
private void componentSelected(int index) {
if (index == currentSelection) {
public void selectElement(int index) {
final JComponent element = getComponent(index);
if (selection.contains(index)) {
// Deselect if clicked again
if (selectionHandler != null) selectionHandler.selectionChanged(model.get(index), element, true);
selection.remove(index);
// Clear selection
update(currentSelection, false);
currentSelection = -1;
} else {
// Remove old selection
if (currentSelection >= 0) update(currentSelection, false);
// Remove old selection if single selection is enabled
if (selectionMode == SelectionMode.SINGLE) clearSelection();
// Select item
if (selectionMode != SelectionMode.NONE) {
// Assign new selection
currentSelection = index;
selection.add(index);
// Update current selection
update(currentSelection, true);
// Update element
if (selectionHandler != null) selectionHandler.selectionChanged(model.get(index), element, true);
}
}
revalidate();
@ -159,14 +110,152 @@ public class ComponentList<E> extends JPanel {
}
/**
* Replaces a list element with a newly rendered instance of its contents.
* Removes the current selection.
*
* @param index the index of the element to update
* @param isSelected the selection state passed to the {@link ListCellRenderer}
* @since Envoy v0.1-beta
* @since Envoy Client v0.1-alpha
*/
private void update(int index, boolean isSelected) {
remove(index);
add(model.get(index), index, isSelected);
public void clearSelection() {
if (selectionHandler != null) selection.forEach(i -> selectionHandler.selectionChanged(model.get(i), getComponent(i), false));
selection.clear();
}
/**
* Adds an object to the list by rendering it with the current
* {@link Renderer}.
*
* @param elem the element to add
* @since Envoy Client v0.3-alpha
*/
void addElement(E elem) {
if (renderer != null) {
final JComponent component = renderer.getListCellComponent(this, elem);
component.addMouseListener(getSelectionListener(getComponentCount()));
add(component, getComponentCount());
}
}
/**
* @param componentIndex the index of the list component to which the mouse
* listener will be added
* @return a mouse listener calling the
* {@link ComponentList#selectElement(int)} method with the
* component's index when a left click is performed by the user
* @since Envoy Client v0.1-beta
*/
private MouseListener getSelectionListener(int componentIndex) {
return new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) selectElement(componentIndex); }
};
}
@Override
public JComponent getComponent(int n) { return (JComponent) super.getComponent(n); }
/**
* @return a set of all selected indices
* @since Envoy Client v0.1-beta
*/
public Set<Integer> getSelection() { return selection; }
/**
* @return a set of all selected elements
* @since Envoy Client v0.1-beta
*/
public Set<E> getSelectedElements() {
var selectedElements = new HashSet<E>();
selection.forEach(i -> selectedElements.add(model.get(i)));
return selectedElements;
}
/**
* @return the index of an arbitrary selected element
* @throws java.util.NoSuchElementException if no selection is present
* @since Envoy Client v0.1-beta
*/
public int getSingleSelection() { return selection.stream().findAny().get(); }
/**
* @return an arbitrary selected element
* @throws java.util.NoSuchElementException if no selection is present
* @since Envoy Client v0.1-beta
*/
public E getSingleSelectedElement() { return model.get(getSingleSelection()); }
/**
* @return the model
* @since Envoy Client v0.1-beta
*/
public Model<E> getModel() { return model; }
/**
* Sets the list model providing the list elements to render. The rendered
* components will be synchronized with the contents of the new model or removed
* if the new model is {@code null}.
*
* @param model the list model to set
* @return this component list
* @since Envoy Client v0.3-alpha
*/
public ComponentList<E> setModel(Model<E> model) {
// Remove old model
if (this.model != null) this.model.setComponentList(null);
// Synchronize with new model
this.model = model;
if (model != null) model.setComponentList(this);
synchronizeModel();
return this;
}
/**
* @return the renderer
* @since Envoy Client v0.1-beta
*/
public Renderer<E> getRenderer() { return renderer; }
/**
* @param renderer the renderer to set
* @return this component list
* @since Envoy Client v0.1-beta
*/
public ComponentList<E> setRenderer(Renderer<E> renderer) {
this.renderer = renderer;
return this;
}
/**
* @return the selection mode
* @since Envoy Client v0.1-beta
*/
public SelectionMode getSelectionMode() { return selectionMode; }
/**
* Sets a new selection mode. The current selection will be cleared during this
* action.
*
* @param selectionMode the selection mode to set
* @return this component list
* @since Envoy Client v0.1-beta
*/
public ComponentList<E> setSelectionMode(SelectionMode selectionMode) {
this.selectionMode = selectionMode;
clearSelection();
return this;
}
/**
* @return the selection handler
* @since Envoy Client v0.1-beta
*/
public SelectionHandler<E> getSelectionHandler() { return selectionHandler; }
/**
* @param selectionHandler the selection handler to set
* @since Envoy Client v0.1-beta
*/
public void setSelectionHandler(SelectionHandler<E> selectionHandler) { this.selectionHandler = selectionHandler; }
}

View File

@ -9,19 +9,19 @@ import java.util.List;
* Stores objects that will be displayed in a {@link ComponentList}.<br>
* <br>
* Project: <strong>envoy-client</strong><br>
* File: <strong>ComponentListModel.java</strong><br>
* File: <strong>Model.java</strong><br>
* Created: <strong>25.01.2020</strong><br>
*
* @param <E> the type of object displayed in this list
* @author Kai S. K. Engelbart
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public final class ComponentListModel<E> implements Iterable<E>, Serializable {
public final class Model<E> implements Iterable<E>, Serializable {
private List<E> elements = new ArrayList<>();
transient private ComponentList<E> componentList;
private static final long serialVersionUID = 4815005915255497331L;
private static final long serialVersionUID = 0L;
/**
* Adds an element to this model and notifies the associated
@ -30,11 +30,11 @@ public final class ComponentListModel<E> implements Iterable<E>, Serializable {
* @param e the element to add
* @return {@code true}
* @see java.util.List#add(java.lang.Object)
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public boolean add(E e) {
if (componentList != null) {
componentList.add(e);
componentList.addElement(e);
componentList.revalidate();
}
return elements.add(e);
@ -45,7 +45,7 @@ public final class ComponentListModel<E> implements Iterable<E>, Serializable {
* {@link ComponentList}.
*
* @see java.util.List#clear()
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public void clear() {
elements.clear();
@ -56,7 +56,7 @@ public final class ComponentListModel<E> implements Iterable<E>, Serializable {
* @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
* @since Envoy Client v0.3-alpha
*/
public E get(int index) { return elements.get(index); }
@ -67,7 +67,7 @@ public final class ComponentListModel<E> implements Iterable<E>, Serializable {
* @param index the index of the element to remove
* @return the removed element
* @see java.util.List#remove(int)
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public E remove(int index) {
if (componentList != null) componentList.remove(index);
@ -77,7 +77,7 @@ public final class ComponentListModel<E> implements Iterable<E>, Serializable {
/**
* @return the amount of elements in this list model
* @see java.util.List#size()
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public int size() { return elements.size(); }
@ -90,7 +90,7 @@ public final class ComponentListModel<E> implements Iterable<E>, Serializable {
/**
* @return an iterator over the elements of this list model
* @see java.util.List#iterator()
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
@Override
public Iterator<E> iterator() {
@ -111,10 +111,10 @@ public final class ComponentListModel<E> implements Iterable<E>, Serializable {
* synchronization.
*
* @param componentList the component list to set
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
void setComponentList(ComponentList<E> componentList) {
this.componentList = componentList;
if (componentList != null) componentList.synchronizeModel();
if (componentList != null && componentList.getRenderer() != null) componentList.synchronizeModel();
}
}

View File

@ -7,14 +7,15 @@ import javax.swing.JComponent;
* that can be rendered.<br>
* <br>
* Project: <strong>envoy-client</strong><br>
* File: <strong>ComponentListCellRenderer.java</strong><br>
* File: <strong>Renderer.java</strong><br>
* Created: <strong>25.01.2020</strong><br>
*
* @param <E> the type of object displayed in this list
* @author Kai S. K. Engelbart
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public interface ComponentListCellRenderer<E> {
@FunctionalInterface
public interface Renderer<E> {
/**
* Provides a Swing component representing a list element.
@ -24,7 +25,7 @@ public interface ComponentListCellRenderer<E> {
* @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
* @since Envoy Client v0.3-alpha
*/
JComponent getListCellComponent(ComponentList<? extends E> list, E value, boolean isSelected);
JComponent getListCellComponent(ComponentList<? extends E> list, E value);
}

View File

@ -0,0 +1,28 @@
package envoy.client.ui.list;
import javax.swing.JComponent;
/**
* Handles the selection of elements in a {@link ComponentList}.<br>
* <br>
* Project: <strong>envoy-client</strong>
* File: <strong>SelectionHandler.java</strong>
* Created: <strong>21.03.2020</strong>
*
* @author Kai S. K. Engelbart
* @param <E> the type of the underlying {@link ComponentList}
* @since Envoy Client v0.1-beta
*/
@FunctionalInterface
public interface SelectionHandler<E> {
/**
* Notifies the handler about a selection.
*
* @param element the selected element
* @param component the selected component
* @param isSelected contains the selection state
* @since Envoy Client v0.1-beta
*/
void selectionChanged(E element, JComponent component, boolean isSelected);
}

View File

@ -5,6 +5,6 @@
* @author Kai S. K. Engelbart
* @author Leon Hofmeister
* @author Maximilian K&auml;fer
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
package envoy.client.ui.list;

View File

@ -1,4 +1,4 @@
package envoy.client.ui;
package envoy.client.ui.list_component;
import java.awt.Component;
import java.awt.Dimension;
@ -9,42 +9,40 @@ import javax.swing.*;
import envoy.client.data.Settings;
import envoy.client.event.SendEvent;
import envoy.client.ui.list.ComponentList;
import envoy.client.ui.list.ComponentListCellRenderer;
import envoy.client.ui.primary.PrimaryButton;
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>
* Project: <strong>envoy-client</strong>
* File: <strong>ContactSearchComponent.java</strong>
* Created: <strong>21.03.2020</strong>
*
* @author Maximilian K&auml;fer
* @author Kai S. K. Engelbart
* @since Envoy v0.3-alpha
* @since Envoy Client v0.1-beta
*/
public class ContactsSearchRenderer implements ComponentListCellRenderer<User> {
public class ContactSearchComponent extends JComponent {
@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());
}
private static final long serialVersionUID = 3166795412575239455L;
/**
* @param list the {@link ComponentList} that is used to display search results
* @param user the {@link User} that appears as a search result
* @since Envoy Client v0.1-beta
*/
public ContactSearchComponent(ComponentList<? extends User> list, User user) {
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
setBackground(list.getBackground());
setForeground(list.getForeground());
JLabel display = new JLabel(user.getName());
display.setForeground(Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getMessageTextColor());
display.setForeground(Settings.getInstance().getCurrentTheme().getTextColor());
display.setAlignmentX(Component.LEFT_ALIGNMENT);
display.setAlignmentY(Component.CENTER_ALIGNMENT);
display.setFont(new Font("Arial", Font.PLAIN, 16));
panel.add(display);
add(display);
PrimaryButton add = new PrimaryButton("+");
add.setFont(new Font("Arial", Font.PLAIN, 19));
@ -61,17 +59,15 @@ public class ContactsSearchRenderer implements ComponentListCellRenderer<User> {
EventBus.getInstance().dispatch(new SendEvent(contactsOperationEvent));
});
panel.add(add);
add(add);
// Define some space to the messages below
panel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 0, 15, 0), BorderFactory.createEtchedBorder()));
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;
setMaximumSize(size);
setMinimumSize(size);
setPreferredSize(size);
}
}

View File

@ -0,0 +1,127 @@
package envoy.client.ui.list_component;
import java.awt.*;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.EnumMap;
import javax.swing.*;
import envoy.client.data.Chat;
import envoy.client.data.Settings;
import envoy.client.ui.Color;
import envoy.client.ui.IconUtil;
import envoy.client.ui.list.ComponentList;
import envoy.data.Message;
import envoy.data.Message.MessageStatus;
import envoy.data.User;
/**
* Project: <strong>envoy-client</strong>
* File: <strong>MessageComponent.java</strong>
* Created: <strong>21.03.2020</strong>
*
* @author Kai S. K. Engelbart
* @since Envoy Client v0.1-beta
*/
public class MessageComponent extends JPanel {
private static final long serialVersionUID = 103920706139926996L;
private static EnumMap<MessageStatus, ImageIcon> statusIcons;
private static ImageIcon forwardIcon;
static {
try {
statusIcons = IconUtil.loadByEnum(MessageStatus.class, 16);
forwardIcon = IconUtil.load("/icons/forward.png", 16);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @param list the {@link ComponentList} that displays this {@link Chat}
* @param message the {@link Message} to display
* @param senderId the id of the {@link User} who sends messages from this
* account
* @since Envoy Client v0.1-beta
*/
public MessageComponent(ComponentList<? extends Message> list, Message message, long senderId) {
var width = list.getMaximumSize().width;
final var theme = Settings.getInstance().getCurrentTheme();
final int padding = (int) (width * 0.35);
GridBagLayout gbl_panel = new GridBagLayout();
gbl_panel.columnWidths = new int[] { 1, 1 };
gbl_panel.rowHeights = new int[] { 1, 1 };
gbl_panel.columnWeights = new double[] { 1, 1 };
gbl_panel.rowWeights = new double[] { 1, 1 };
setLayout(gbl_panel);
setBackground(theme.getCellColor());
// Date Label - The Label that displays the creation date of a message
var dateLabel = new JLabel(new SimpleDateFormat("dd.MM.yyyy HH:mm").format(message.getCreationDate()));
dateLabel.setForeground(theme.getDateColor());
dateLabel.setAlignmentX(1f);
dateLabel.setFont(new Font("Arial", Font.PLAIN, 12));
dateLabel.setPreferredSize(dateLabel.getPreferredSize());
var gbc_dateLabel = new GridBagConstraints();
gbc_dateLabel.fill = GridBagConstraints.BOTH;
gbc_dateLabel.gridx = 0;
gbc_dateLabel.gridy = 0;
add(dateLabel, gbc_dateLabel);
// Message area - The JTextArea that displays the text content of a message.
var messageTextArea = new JTextArea(message.getText());
messageTextArea.setLineWrap(true);
messageTextArea.setWrapStyleWord(true);
messageTextArea.setForeground(theme.getTextColor());
messageTextArea.setAlignmentX(0.5f);
messageTextArea.setBackground(theme.getCellColor());
messageTextArea.setEditable(false);
var font = new Font("Arial", Font.PLAIN, 14);
messageTextArea.setFont(font);
messageTextArea.setSize(width - padding - 16, 10);
var gbc_messageTextArea = new GridBagConstraints();
gbc_messageTextArea.fill = GridBagConstraints.HORIZONTAL;
gbc_messageTextArea.gridx = 0;
gbc_messageTextArea.gridy = 1;
add(messageTextArea, gbc_messageTextArea);
// Status Label - displays the status of the message
var statusLabel = new JLabel(statusIcons.get(message.getStatus()));
var gbc_statusLabel = new GridBagConstraints();
gbc_statusLabel.gridx = 1;
gbc_statusLabel.gridy = 1;
add(statusLabel, gbc_statusLabel);
// Forwarding
if (message.isForwarded()) {
var forwardLabel = new JLabel("Forwarded", forwardIcon, SwingConstants.CENTER);
forwardLabel.setBackground(getBackground());
forwardLabel.setForeground(Color.lightGray);
var gbc_forwardLabel = new GridBagConstraints();
gbc_forwardLabel.fill = GridBagConstraints.BOTH;
gbc_forwardLabel.gridx = 1;
gbc_forwardLabel.gridy = 0;
add(forwardLabel, gbc_forwardLabel);
}
// Define an etched border and some space to the messages below
var ours = senderId == message.getSenderId();
setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, ours ? padding : 10, 10, ours ? 0 : padding),
BorderFactory.createEtchedBorder()));
var size = new Dimension(width - 50, getPreferredSize().height);
setPreferredSize(size);
setMinimumSize(size);
setMaximumSize(size);
}
}

View File

@ -0,0 +1,69 @@
package envoy.client.ui.list_component;
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JLabel;
import javax.swing.JPanel;
import envoy.client.data.Settings;
import envoy.client.ui.Color;
import envoy.client.ui.Theme;
import envoy.data.User;
import envoy.data.User.UserStatus;
/**
* Displays a {@link User}.<br>
* <br>
* Project: <strong>envoy-client</strong>
* File: <strong>UserComponent.java</strong>
* Created: <strong>21.03.2020</strong>
*
* @author Kai S. K. Engelbart
* @since Envoy Client v0.1-beta
*/
public class UserComponent extends JPanel {
private static final long serialVersionUID = 8450602172939729585L;
/**
* @param user the {@link User} whose information is displayed
* @since Envoy Client v0.1-beta
*/
public UserComponent(User user) {
final Theme theme = Settings.getInstance().getCurrentTheme();
setLayout(new BorderLayout());
// Panel background
setBackground(theme.getCellColor());
setOpaque(true);
setPreferredSize(new Dimension(100, 35));
// TODO add profile picture support in BorderLayout.West
JLabel username = new JLabel(user.getName());
username.setForeground(theme.getUserNameColor());
add(username, BorderLayout.CENTER);
final UserStatus status = user.getStatus();
JLabel statusLabel = new JLabel(status.toString());
Color foreground;
switch (status) {
case AWAY:
foreground = Color.yellow;
break;
case BUSY:
foreground = Color.blue;
break;
case ONLINE:
foreground = Color.green;
break;
default:
foreground = Color.lightGray;
break;
}
statusLabel.setForeground(foreground);
add(statusLabel, BorderLayout.NORTH);
}
}

View File

@ -0,0 +1,14 @@
/**
* This package contains swing components that can be displayed by
* {@link envoy.client.ui.list.ComponentList}.<br>
* <br>
* Project: <strong>envoy-client</strong><br>
* File: <strong>package-info.java</strong><br>
* Created: <strong>21 Mar 2020</strong><br>
*
* @author Leon Hofmeister
* @author Kai S. K. Engelbart
* @author Maximilian K&auml;fer
* @since Envoy Client v0.1-beta
*/
package envoy.client.ui.list_component;

View File

@ -4,6 +4,6 @@
* @author Kai S. K. Engelbart
* @author Leon Hofmeister
* @author Maximilian K&auml;fer
* @since Envoy v0.1-beta
* @since Envoy Client v0.1-beta
*/
package envoy.client.ui;

View File

@ -1,4 +1,4 @@
package envoy.client.ui;
package envoy.client.ui.primary;
import java.awt.Graphics;
@ -11,7 +11,7 @@ import javax.swing.JButton;
*
* @author Kai S. K. Engelbart
* @author Maximilian K&auml;fer
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public class PrimaryButton extends JButton {

View File

@ -1,11 +1,6 @@
package envoy.client.ui;
package envoy.client.ui.primary;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.*;
import javax.swing.JButton;
import javax.swing.JComponent;
@ -13,6 +8,7 @@ import javax.swing.JScrollBar;
import javax.swing.plaf.basic.BasicScrollBarUI;
import envoy.client.data.Settings;
import envoy.client.ui.Theme;
/**
* Project: <strong>envoy-client</strong><br>
@ -20,7 +16,7 @@ import envoy.client.data.Settings;
* Created: <strong>14.12.2019</strong><br>
*
* @author Maximilian K&auml;fer
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public class PrimaryScrollBar extends BasicScrollBarUI {
@ -97,11 +93,11 @@ public class PrimaryScrollBar extends BasicScrollBarUI {
g2.setPaint(color);
if (isVertical) {
g2.fillRoundRect(r.x - 9, r.y, r.width, r.height, arcSize, arcSize);
g2.setPaint(Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getCellColor());
g2.setPaint(Settings.getInstance().getCurrentTheme().getCellColor());
g2.drawRoundRect(r.x - 9, r.y, r.width, r.height, arcSize, arcSize);
} else {
g2.fillRoundRect(r.x, r.y + 9, r.width, r.height - 10, arcSize, arcSize);
g2.setPaint(Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getCellColor());
g2.setPaint(Settings.getInstance().getCurrentTheme().getCellColor());
g2.drawRoundRect(r.x, r.y + 9, r.width, r.height - 10, arcSize, arcSize);
}
g2.dispose();

View File

@ -1,7 +1,9 @@
package envoy.client.ui;
package envoy.client.ui.primary;
import javax.swing.JScrollPane;
import envoy.client.ui.Theme;
/**
* Project: <strong>envoy-client</strong><br>
* File: <strong>PrimaryScrollPane.java</strong><br>
@ -20,7 +22,7 @@ public class PrimaryScrollPane extends JScrollPane {
/**
* Initializes a {@link JScrollPane} with the primary Envoy design scheme
*
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public PrimaryScrollPane() { setBorder(null); }
@ -28,7 +30,7 @@ public class PrimaryScrollPane extends JScrollPane {
* Styles the vertical and horizontal scroll bars.
*
* @param theme the color set used to color the component
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public void applyTheme(Theme theme) {
setForeground(theme.getBackgroundColor());
@ -52,7 +54,7 @@ public class PrimaryScrollPane extends JScrollPane {
* When rereading messages, the chat doesn't scroll down if new messages </br>
* are added. (Besides see first point)
*
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public void autoscroll() {
// Automatic scrolling to the bottom
@ -77,7 +79,7 @@ public class PrimaryScrollPane extends JScrollPane {
* triggering it to automatically scroll down.
*
* @param chatOpened indicates the chat opening status
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public void setChatOpened(boolean chatOpened) { this.chatOpened = chatOpened; }
}

View File

@ -1,4 +1,4 @@
package envoy.client.ui;
package envoy.client.ui.primary;
import java.awt.Font;
import java.awt.Graphics;
@ -12,7 +12,7 @@ import javax.swing.border.EmptyBorder;
* Created: <strong>07.12.2019</strong><br>
*
* @author Maximilian K&auml;fer
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public class PrimaryTextArea extends JTextArea {

View File

@ -1,4 +1,4 @@
package envoy.client.ui;
package envoy.client.ui.primary;
import java.awt.Dimension;
import java.awt.Graphics;
@ -7,6 +7,7 @@ import javax.swing.JButton;
import envoy.client.data.Settings;
import envoy.client.data.SettingsItem;
import envoy.client.ui.Color;
/**
* This component can be used to toggle between two options. This will change
@ -18,7 +19,7 @@ import envoy.client.data.SettingsItem;
*
* @author Maximilian K&auml;fer
* @author Kai S. K. Engelbart
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public class PrimaryToggleSwitch extends JButton {
@ -31,7 +32,7 @@ public class PrimaryToggleSwitch extends JButton {
*
* @param settingsItem the {@link SettingsItem} that is controlled by this
* {@link PrimaryToggleSwitch}
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public PrimaryToggleSwitch(SettingsItem<Boolean> settingsItem) {
setPreferredSize(new Dimension(50, 25));
@ -51,7 +52,7 @@ public class PrimaryToggleSwitch extends JButton {
g.setColor(state ? Color.GREEN : Color.LIGHT_GRAY);
g.fillRoundRect(0, 0, getWidth(), getHeight(), 25, 25);
g.setColor(Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getInteractableBackgroundColor());
g.setColor(Settings.getInstance().getCurrentTheme().getInteractableBackgroundColor());
g.fillRoundRect(state ? 25 : 0, 0, 25, 25, 25, 25);
}
}

View File

@ -0,0 +1,17 @@
/**
* This package defines all "primary" components that were defined specifically
* for the visual improvement of Envoy. However, they can still be used in
* general for other projects.<br>
* Primary elements are supposed to provide the main functionality of a UI
* component.<br>
* <br>
* Project: <strong>envoy-client</strong><br>
* File: <strong>package-info.java</strong><br>
* Created: <strong>14 Mar 2020</strong><br>
*
* @author Leon Hofmeister
* @author Kai S. K. Engelbart
* @author Maximilian K&auml;fer
* @since Envoy Client v0.1-beta
*/
package envoy.client.ui.primary;

View File

@ -1,4 +1,4 @@
package envoy.client.ui;
package envoy.client.ui.renderer;
import java.awt.Component;
import java.awt.Dimension;
@ -20,7 +20,7 @@ import envoy.data.User.UserStatus;
*
* @author Kai S. K. Engelbart
* @author Maximilian K&auml;fer
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public class UserListRenderer extends JLabel implements ListCellRenderer<User> {
@ -46,7 +46,7 @@ public class UserListRenderer extends JLabel implements ListCellRenderer<User> {
// Getting the UserNameColor of the current theme
String textColor = null;
textColor = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getUserNameColor().toHex();
textColor = Settings.getInstance().getCurrentTheme().getUserNameColor().toHex();
switch (status) {
case ONLINE:
setText(String

View File

@ -0,0 +1,14 @@
/**
* This package contains all Envoy-specific renderers for lists that store an
* arbitrary number of JComponents.<br>
* <br>
* Project: <strong>envoy-client</strong><br>
* File: <strong>package-info.java</strong><br>
* Created: <strong>14 Mar 2020</strong><br>
*
* @author Leon Hofmeister
* @author Kai S. K. Engelbart
* @author Maximilian K&auml;fer
* @since Envoy Client v0.1-beta
*/
package envoy.client.ui.renderer;

View File

@ -3,7 +3,6 @@ package envoy.client.ui.settings;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -23,7 +22,7 @@ import envoy.util.EnvoyLog;
* Created: <strong>21 Dec 2019</strong><br>
*
* @author Maximilian K&auml;fer
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public class GeneralSettingsPanel extends SettingsPanel {
@ -39,11 +38,11 @@ public class GeneralSettingsPanel extends SettingsPanel {
*
* @param parent the {@link SettingsScreen} as a part of which this
* {@link SettingsPanel} is displayed
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public GeneralSettingsPanel(SettingsScreen parent) {
super(parent);
theme = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme());
theme = Settings.getInstance().getCurrentTheme();
setBackground(theme.getCellColor());
@ -87,7 +86,4 @@ public class GeneralSettingsPanel extends SettingsPanel {
add(descriptionText, gbc_descriptionText);
}
@Override
public ActionListener getOkButtonAction() { return evt -> {}; }
}

View File

@ -8,9 +8,9 @@ import javax.swing.JPanel;
import javax.swing.JTextPane;
import envoy.client.data.Settings;
import envoy.client.ui.PrimaryButton;
import envoy.client.ui.PrimaryTextArea;
import envoy.client.ui.Theme;
import envoy.client.ui.primary.PrimaryButton;
import envoy.client.ui.primary.PrimaryTextArea;
/**
* Displays window where you can choose a name for the new {@link Theme}.
@ -20,7 +20,7 @@ import envoy.client.ui.Theme;
* Created: <strong>26 Dec 2019</strong><br>
*
* @author Maximilian K&auml;fer
* @since Envoy v0.3-alpha
* @since Envoy Client v0.3-alpha
*/
public class NewThemeScreen extends JDialog {
@ -43,10 +43,12 @@ public class NewThemeScreen extends JDialog {
* There are two versions of this Window. The first one is responsible for
* choosing the name, the second one appears, if the name already exists.
*
* @param parent the dialog is launched with its location relative to this {@link SettingsScreen}
* @param parent the dialog is launched with its location relative to
* this {@link SettingsScreen}
* @param newThemeAction is executed when a new theme name is entered
* @param modifyThemeAction is executed when an existing theme name is entered and confirmed
* @since Envoy v0.3-alpha
* @param modifyThemeAction is executed when an existing theme name is entered
* and confirmed
* @since Envoy Client v0.3-alpha
*/
public NewThemeScreen(SettingsScreen parent, Consumer<String> newThemeAction, Consumer<String> modifyThemeAction) {
this.newThemeAction = newThemeAction;
@ -59,7 +61,7 @@ public class NewThemeScreen extends JDialog {
setDimensions(true);
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
Theme theme = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme());
Theme theme = Settings.getInstance().getCurrentTheme();
getContentPane().setLayout(new BorderLayout());
standardPanel.setBackground(theme.getBackgroundColor());

View File

@ -1,7 +1,5 @@
package envoy.client.ui.settings;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
/**
@ -14,7 +12,7 @@ import javax.swing.JPanel;
* Created: <strong>20 Dec 2019</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public abstract class SettingsPanel extends JPanel {
@ -29,11 +27,4 @@ public abstract class SettingsPanel extends JPanel {
* {@link SettingsPanel} is displayed
*/
public SettingsPanel(SettingsScreen parent) { this.parent = parent; }
/**
* @return an {@link ActionListener} that should be invoked when the OK button
* is pressed in the {@link SettingsScreen}
* @since Envoy v0.2-alpha
*/
public abstract ActionListener getOkButtonAction();
}

View File

@ -11,8 +11,8 @@ import javax.swing.*;
import envoy.client.data.Settings;
import envoy.client.event.ThemeChangeEvent;
import envoy.client.ui.PrimaryButton;
import envoy.client.ui.Theme;
import envoy.client.ui.primary.PrimaryButton;
import envoy.event.EventBus;
import envoy.util.EnvoyLog;
@ -26,7 +26,7 @@ import envoy.util.EnvoyLog;
* @author Leon Hofmeister
* @author Maximilian K&auml;fer
* @author Kai S. K. Engelbart
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public class SettingsScreen extends JDialog {
@ -40,7 +40,6 @@ public class SettingsScreen extends JDialog {
// OK and cancel buttons
private final JPanel buttonPane = new JPanel();
private final PrimaryButton okButton = new PrimaryButton("Save");
private final PrimaryButton cancelButton = new PrimaryButton("Cancel");
private final Insets insets = new Insets(5, 5, 5, 5);
@ -52,7 +51,7 @@ public class SettingsScreen extends JDialog {
/**
* Initializes the settings screen.
*
* @since Envoy v0.1-alpha
* @since Envoy Client v0.1-alpha
*/
public SettingsScreen() {
// Initialize settings pages
@ -138,25 +137,10 @@ public class SettingsScreen extends JDialog {
cancelButton.addActionListener((evt) -> { dispose(); });
}
{
okButton.setActionCommand("OK");
okButton.setBorderPainted(false);
GridBagConstraints gbc_okButton = new GridBagConstraints();
gbc_okButton.anchor = GridBagConstraints.NORTHEAST;
gbc_okButton.fill = GridBagConstraints.EAST;
gbc_okButton.insets = insets;
gbc_okButton.gridx = 2;
gbc_okButton.gridy = 0;
buttonPane.add(okButton, gbc_okButton);
getRootPane().setDefaultButton(okButton);
// Invoke settings panel action on button press
okButton.addActionListener((evt) -> { if (settingsPanel != null) settingsPanel.getOkButtonAction().actionPerformed(evt); });
}
}
// Apply current theme
applyTheme(Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()));
applyTheme(Settings.getInstance().getCurrentTheme());
// Respond to theme changes
EventBus.getInstance().register(ThemeChangeEvent.class, evt -> applyTheme(evt.get()));
@ -179,10 +163,6 @@ public class SettingsScreen extends JDialog {
cancelButton.setBackground(theme.getInteractableBackgroundColor());
cancelButton.setForeground(theme.getInteractableForegroundColor());
// okButton
okButton.setBackground(theme.getInteractableBackgroundColor());
okButton.setForeground(theme.getInteractableForegroundColor());
// options
options.setSelectionForeground(theme.getUserNameColor());
options.setSelectionBackground(theme.getSelectionColor());

View File

@ -1,9 +1,6 @@
package envoy.client.ui.settings;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -13,6 +10,7 @@ import envoy.client.data.Settings;
import envoy.client.event.ThemeChangeEvent;
import envoy.client.ui.Color;
import envoy.client.ui.Theme;
import envoy.client.ui.primary.PrimaryButton;
import envoy.event.EventBus;
import envoy.util.EnvoyLog;
@ -26,18 +24,18 @@ import envoy.util.EnvoyLog;
*
* @author Kai S. K. Engelbart
* @author Maximilian K&auml;fer
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public class ThemeCustomizationPanel extends SettingsPanel {
private JPanel colorsPanel = new JPanel();
private DefaultComboBoxModel<String> themesModel = new DefaultComboBoxModel<>(
Settings.getInstance().getThemes().keySet().toArray(new String[0]));
private JComboBox<String> themes = new JComboBox<>(themesModel);
private DefaultComboBoxModel<String> themesModel;
private JComboBox<String> themes;
private Theme temporaryTheme;
private boolean themeChanged;
private PrimaryButton createThemeButton = new PrimaryButton("Create Theme");
private boolean themeChanged;
private final Insets insets = new Insets(5, 5, 5, 5);
private static final Logger logger = EnvoyLog.getLogger(ThemeCustomizationPanel.class);
@ -50,18 +48,28 @@ public class ThemeCustomizationPanel extends SettingsPanel {
*
* @param parent the {@link SettingsScreen} as a part of which this
* {@link SettingsPanel} is displayed
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
public ThemeCustomizationPanel(SettingsScreen parent) {
super(parent);
temporaryTheme = new Theme("temporaryTheme", Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()));
temporaryTheme = new Theme("temporaryTheme", Settings.getInstance().getCurrentTheme());
var themeNames = Settings.getInstance().getThemes().keySet().toArray(new String[0]);
String currentThemeName = Settings.getInstance().getCurrentThemeName();
for (int i = 0; i < themeNames.length; i++)
if (currentThemeName.equals(themeNames[i])) {
themeNames[i] = themeNames[0];
themeNames[0] = currentThemeName;
break;
}
themesModel = new DefaultComboBoxModel<>(themeNames);
themes = new JComboBox<>(themesModel);
GridBagLayout gbl_themeLayout = new GridBagLayout();
gbl_themeLayout.columnWidths = new int[] { 1, 1 };
gbl_themeLayout.rowHeights = new int[] { 1, 1 };
gbl_themeLayout.rowHeights = new int[] { 1, 1, 1 };
gbl_themeLayout.columnWeights = new double[] { 1.0, 1.0 };
gbl_themeLayout.rowWeights = new double[] { 0.01, 1.0 };
gbl_themeLayout.rowWeights = new double[] { 0.01, 1.0, 0.01 };
setLayout(gbl_themeLayout);
@ -85,7 +93,7 @@ public class ThemeCustomizationPanel extends SettingsPanel {
colorsPanel.setLayout(gbl_colorCustomizations);
Theme theme = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme());
Theme theme = Settings.getInstance().getCurrentTheme();
buildCustomizeElements(theme);
GridBagConstraints gbc_colorsPanel = new GridBagConstraints();
@ -97,20 +105,47 @@ public class ThemeCustomizationPanel extends SettingsPanel {
gbc_colorsPanel.insets = insets;
add(colorsPanel, gbc_colorsPanel);
createThemeButton.addActionListener((evt) -> {
if (themeChanged) {
new NewThemeScreen(parent, name -> {
// Create new theme
logger.log(Level.FINEST, name);
Settings.getInstance().addNewThemeToMap(new Theme(name, temporaryTheme));
// Add new theme name to combo box
themesModel.addElement(name);
// Select new theme name
themes.setSelectedIndex(themesModel.getSize() - 1);
}, name -> {
// Modify theme
Settings.getInstance().getThemes().replace(name, new Theme(name, temporaryTheme));
if (themes.getSelectedItem().equals(name))
EventBus.getInstance().dispatch(new ThemeChangeEvent(Settings.getInstance().getTheme(name)));
else themes.setSelectedItem(name);
}).setVisible(true);
themeChanged = false;
}
});
GridBagConstraints gbc_createThemeButton = new GridBagConstraints();
gbc_createThemeButton.fill = GridBagConstraints.HORIZONTAL;
gbc_createThemeButton.gridx = 0;
gbc_createThemeButton.gridy = 2;
gbc_createThemeButton.anchor = GridBagConstraints.CENTER;
gbc_createThemeButton.insets = insets;
add(createThemeButton, gbc_createThemeButton);
colorsPanel.setBackground(theme.getCellColor());
// Apply theme upon selection
themes.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
themes.addItemListener(e -> {
String selectedValue = (String) themes.getSelectedItem();
logger.log(Level.FINEST, "Selected theme: " + selectedValue);
final Theme currentTheme = Settings.getInstance().getTheme(selectedValue);
Settings.getInstance().setCurrentTheme(selectedValue);
EventBus.getInstance().dispatch(new ThemeChangeEvent(currentTheme));
}
});
// Apply current theme
@ -126,39 +161,15 @@ public class ThemeCustomizationPanel extends SettingsPanel {
});
}
@Override
public ActionListener getOkButtonAction() {
return (evt) -> {
if (themeChanged) {
new NewThemeScreen(parent, name -> {
// Create new theme
logger.log(Level.FINEST, name);
Settings.getInstance().addNewThemeToMap(new Theme(name, temporaryTheme));
// Add new theme name to combo box
themesModel.addElement(name);
// Select new theme name
themes.setSelectedIndex(themesModel.getSize() - 1);
}, name -> {
// Modify theme
Settings.getInstance().getThemes().replace(name, new Theme(name, temporaryTheme));
if (themes.getSelectedItem().equals(name)) {
EventBus.getInstance().dispatch(new ThemeChangeEvent(Settings.getInstance().getTheme(name)));
} else {
themes.setSelectedItem(name);
}
}).setVisible(true);
themeChanged = false;
}
};
}
private void applyTheme(Theme theme) {
// themeContent
setForeground(theme.getUserNameColor());
setBackground(theme.getCellColor());
// createThemeButton
createThemeButton.setForeground(theme.getInteractableForegroundColor());
createThemeButton.setBackground(theme.getInteractableBackgroundColor());
// themes
themes.setBackground(theme.getInteractableBackgroundColor());
themes.setForeground(theme.getInteractableForegroundColor());
@ -181,7 +192,7 @@ public class ThemeCustomizationPanel extends SettingsPanel {
buildCustomizeElement(theme, theme.getCellColor(), "Cells", "cellColor", 2);
buildCustomizeElement(theme, theme.getInteractableForegroundColor(), "Interactable Foreground", "interactableForegroundColor", 3);
buildCustomizeElement(theme, theme.getInteractableBackgroundColor(), "Interactable Background", "interactableBackgroundColor", 4);
buildCustomizeElement(theme, theme.getMessageTextColor(), "Messages Chat", "messageColorChat", 5);
buildCustomizeElement(theme, theme.getTextColor(), "Text Color", "textColor", 5);
buildCustomizeElement(theme, theme.getDateColor(), "Date Chat", "dateColorChat", 6);
buildCustomizeElement(theme, theme.getSelectionColor(), "Selection", "selectionColor", 7);
buildCustomizeElement(theme, theme.getTypingMessageColor(), "Typing Message", "typingMessageColor", 8);

View File

@ -4,6 +4,6 @@
* @author Kai S. K. Engelbart
* @author Leon Hofmeister
* @author Maximilian K&auml;fer
* @since Envoy v0.2-alpha
* @since Envoy Client v0.2-alpha
*/
package envoy.client.ui.settings;

View File

@ -5,7 +5,7 @@
* @author Kai S. K. Engelbart
* @author Leon Hofmeister
* @author Maximilian K&auml;fer
* @since Envoy v0.1-beta
* @since Envoy Client v0.1-beta
*/
module envoy {

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB