diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/Startup.java similarity index 96% rename from src/main/java/envoy/client/ui/Startup.java rename to src/main/java/envoy/client/Startup.java index 98a8ffc..e4e4667 100644 --- a/src/main/java/envoy/client/ui/Startup.java +++ b/src/main/java/envoy/client/Startup.java @@ -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(() -> { @@ -173,4 +174,4 @@ public class Startup { } })); } -} \ No newline at end of file +} diff --git a/src/main/java/envoy/client/data/Cache.java b/src/main/java/envoy/client/data/Cache.java index 2622322..38d8d24 100644 --- a/src/main/java/envoy/client/data/Cache.java +++ b/src/main/java/envoy/client/data/Cache.java @@ -17,7 +17,7 @@ import envoy.util.EnvoyLog; * * @param the type of cached elements * @author Kai S. K. Engelbart - * @since Envoy v0.3-alpha + * @since Envoy Client v0.3-alpha */ public class Cache implements Consumer, Serializable { @@ -31,7 +31,7 @@ public class Cache implements Consumer, 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 implements Consumer, 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 processor) { this.processor = processor; } @@ -51,7 +51,7 @@ public class Cache implements Consumer, 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"); diff --git a/src/main/java/envoy/client/data/Chat.java b/src/main/java/envoy/client/data/Chat.java index 49083a7..c41b7db 100644 --- a/src/main/java/envoy/client/data/Chat.java +++ b/src/main/java/envoy/client/data/Chat.java @@ -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ä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 model = new ComponentListModel<>(); + private final Model model = new Model<>(); /** * Provides the list of messages that the recipient receives.
* 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,17 +56,15 @@ 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; - else { - m.setStatus(MessageStatus.READ); - writeProxy.writeMessageStatusChangeEvent(new MessageStatusChangeEvent(m)); - } + if (m.getSenderId() == recipient.getId()) if (m.getStatus() == MessageStatus.READ) break; + else { + m.setStatus(MessageStatus.READ); + writeProxy.writeMessageStatusChangeEvent(new MessageStatusChangeEvent(m)); } } } @@ -74,19 +72,19 @@ public class Chat implements Serializable { /** * @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 getModel() { return model; } + public Model 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; } -} \ No newline at end of file +} diff --git a/src/main/java/envoy/client/data/ClientConfig.java b/src/main/java/envoy/client/data/ClientConfig.java index 349fa54..63381a0 100644 --- a/src/main/java/envoy/client/data/ClientConfig.java +++ b/src/main/java/envoy/client/data/ClientConfig.java @@ -18,7 +18,7 @@ import envoy.data.LoginCredentials; * Created: 01.03.2020
* * @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 { diff --git a/src/main/java/envoy/client/data/LocalDb.java b/src/main/java/envoy/client/data/LocalDb.java index dd1db3a..ace19f9 100644 --- a/src/main/java/envoy/client/data/LocalDb.java +++ b/src/main/java/envoy/client/data/LocalDb.java @@ -16,7 +16,7 @@ import envoy.event.MessageStatusChangeEvent; * Created: 3 Feb 2020
* * @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} of all users stored locally with their * user names as keys - * @since Envoy v0.2-alpha + * @since Envoy Client v0.2-alpha */ public Map 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 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 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 messageCache) { this.messageCache = messageCache; } /** * @return the offline status cache - * @since Envoy v0.3-alpha + * @since Envoy Client v0.3-alpha */ public Cache 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 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) diff --git a/src/main/java/envoy/client/data/PersistentLocalDb.java b/src/main/java/envoy/client/data/PersistentLocalDb.java index f59b0f2..2bfb23e 100644 --- a/src/main/java/envoy/client/data/PersistentLocalDb.java +++ b/src/main/java/envoy/client/data/PersistentLocalDb.java @@ -19,7 +19,7 @@ import envoy.util.SerializationUtils; * * @author Kai S. K. Engelbart * @author Maximilian Kä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() { @@ -100,4 +100,4 @@ public class PersistentLocalDb extends LocalDb { idGenerator = SerializationUtils.read(idGeneratorFile, IdGenerator.class); } catch (ClassNotFoundException | IOException e) {} } -} \ No newline at end of file +} diff --git a/src/main/java/envoy/client/data/Settings.java b/src/main/java/envoy/client/data/Settings.java index c44e4f4..5bff58c 100644 --- a/src/main/java/envoy/client/data/Settings.java +++ b/src/main/java/envoy/client/data/Settings.java @@ -22,7 +22,7 @@ import envoy.util.SerializationUtils; * @author Leon Hofmeister * @author Maximilian Kä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) 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) 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) items.get("onCloseMode")).set(currentOnCloseMode); } @@ -172,7 +178,7 @@ public class Settings { /** * @return a {@code Map} of all themes with their names as keys - * @since Envoy v0.2-alpha + * @since Envoy Client v0.2-alpha */ public Map getThemes() { return themes; } @@ -180,14 +186,14 @@ public class Settings { * Sets the {@code Map} 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 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); } -} \ No newline at end of file +} diff --git a/src/main/java/envoy/client/data/SettingsItem.java b/src/main/java/envoy/client/data/SettingsItem.java index 57ba600..7b6eea6 100644 --- a/src/main/java/envoy/client/data/SettingsItem.java +++ b/src/main/java/envoy/client/data/SettingsItem.java @@ -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 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 implements Serializable { @@ -45,7 +45,7 @@ public class SettingsItem 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 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 componentClass) { this.value = value; @@ -72,7 +72,7 @@ public class SettingsItem 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 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 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 implements Serializable { /** * @return the componentClass - * @since Envoy v0.3-alpha + * @since Envoy Client v0.3-alpha */ public Class getComponentClass() { return componentClass; } /** * @param componentClass the componentClass to set - * @since Envoy v0.3-alpha + * @since Envoy Client v0.3-alpha */ public void setComponentClass(Class 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 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 changeHandler) { this.changeHandler = changeHandler; diff --git a/src/main/java/envoy/client/data/TransientLocalDb.java b/src/main/java/envoy/client/data/TransientLocalDb.java index 433488a..961dfcc 100644 --- a/src/main/java/envoy/client/data/TransientLocalDb.java +++ b/src/main/java/envoy/client/data/TransientLocalDb.java @@ -9,7 +9,7 @@ package envoy.client.data; * Created: 3 Feb 2020
* * @author Kai S. K. Engelbart - * @since Envoy v0.3-alpha + * @since Envoy Client v0.3-alpha */ public class TransientLocalDb extends LocalDb { } diff --git a/src/main/java/envoy/client/data/package-info.java b/src/main/java/envoy/client/data/package-info.java index 3129027..3455f5d 100644 --- a/src/main/java/envoy/client/data/package-info.java +++ b/src/main/java/envoy/client/data/package-info.java @@ -4,6 +4,6 @@ * @author Kai S. K. Engelbart * @author Leon Hofmeister * @author Maximilian Käfer - * @since Envoy v0.1-beta + * @since Envoy Client v0.1-beta */ package envoy.client.data; diff --git a/src/main/java/envoy/client/event/HandshakeSuccessfulEvent.java b/src/main/java/envoy/client/event/HandshakeSuccessfulEvent.java index 9cafa48..5fdb307 100644 --- a/src/main/java/envoy/client/event/HandshakeSuccessfulEvent.java +++ b/src/main/java/envoy/client/event/HandshakeSuccessfulEvent.java @@ -10,7 +10,7 @@ import envoy.event.Event; * Created: 8 Feb 2020
* * @author Leon Hofmeister - * @since Envoy v0.3-alpha + * @since Envoy Client v0.3-alpha */ public class HandshakeSuccessfulEvent extends Event.Valueless { diff --git a/src/main/java/envoy/client/event/MessageCreationEvent.java b/src/main/java/envoy/client/event/MessageCreationEvent.java index 4781943..14e95e0 100644 --- a/src/main/java/envoy/client/event/MessageCreationEvent.java +++ b/src/main/java/envoy/client/event/MessageCreationEvent.java @@ -9,7 +9,7 @@ import envoy.event.Event; * Created: 4 Dec 2019
* * @author Kai S. K. Engelbart - * @since Envoy v0.2-alpha + * @since Envoy Client v0.2-alpha */ public class MessageCreationEvent extends Event { diff --git a/src/main/java/envoy/client/event/MessageModificationEvent.java b/src/main/java/envoy/client/event/MessageModificationEvent.java index 8ddaaf0..0deb536 100644 --- a/src/main/java/envoy/client/event/MessageModificationEvent.java +++ b/src/main/java/envoy/client/event/MessageModificationEvent.java @@ -9,7 +9,7 @@ import envoy.event.Event; * Created: 4 Dec 2019
* * @author Kai S. K. Engelbart - * @since Envoy v0.2-alpha + * @since Envoy Client v0.2-alpha */ public class MessageModificationEvent extends Event { diff --git a/src/main/java/envoy/client/event/SendEvent.java b/src/main/java/envoy/client/event/SendEvent.java index 2d95fc8..5d58f3e 100644 --- a/src/main/java/envoy/client/event/SendEvent.java +++ b/src/main/java/envoy/client/event/SendEvent.java @@ -9,7 +9,7 @@ import envoy.event.Event; * * @author: Maximilian Käfer * - * @since Envoy v0.3-alpha + * @since Envoy Client v0.3-alpha */ public class SendEvent extends Event> { diff --git a/src/main/java/envoy/client/event/ThemeChangeEvent.java b/src/main/java/envoy/client/event/ThemeChangeEvent.java index c164f72..1010927 100644 --- a/src/main/java/envoy/client/event/ThemeChangeEvent.java +++ b/src/main/java/envoy/client/event/ThemeChangeEvent.java @@ -9,7 +9,7 @@ import envoy.event.Event; * Created: 15 Dec 2019
* * @author Kai S. K. Engelbart - * @since Envoy v0.2-alpha + * @since Envoy Client v0.2-alpha */ public class ThemeChangeEvent extends Event { @@ -20,7 +20,7 @@ public class ThemeChangeEvent extends Event { * 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); } } diff --git a/src/main/java/envoy/client/event/package-info.java b/src/main/java/envoy/client/event/package-info.java index 58383e0..6886d5c 100644 --- a/src/main/java/envoy/client/event/package-info.java +++ b/src/main/java/envoy/client/event/package-info.java @@ -4,6 +4,6 @@ * @author Kai S. K. Engelbart * @author Leon Hofmeister * @author Maximilian Käfer - * @since Envoy v0.1-beta + * @since Envoy Client v0.1-beta */ package envoy.client.event; diff --git a/src/main/java/envoy/client/net/Client.java b/src/main/java/envoy/client/net/Client.java index acd16e5..a877edd 100644 --- a/src/main/java/envoy/client/net/Client.java +++ b/src/main/java/envoy/client/net/Client.java @@ -30,7 +30,7 @@ import envoy.util.SerializationUtils; * @author Kai S. K. Engelbart * @author Maximilian Kä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 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} 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 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; } } diff --git a/src/main/java/envoy/client/net/MessageStatusChangeEventProcessor.java b/src/main/java/envoy/client/net/MessageStatusChangeEventProcessor.java index 834bcc8..ce53139 100644 --- a/src/main/java/envoy/client/net/MessageStatusChangeEventProcessor.java +++ b/src/main/java/envoy/client/net/MessageStatusChangeEventProcessor.java @@ -14,7 +14,7 @@ import envoy.util.EnvoyLog; * Created: 4 Feb 2020
* * @author Kai S. K. Engelbart - * @since Envoy v0.3-alpha + * @since Envoy Client v0.3-alpha */ public class MessageStatusChangeEventProcessor implements Consumer { @@ -25,7 +25,7 @@ public class MessageStatusChangeEventProcessor implements Consumer31.12.2019
* * @author Kai S. K. Engelbart - * @since Envoy v0.3-alpha + * @since Envoy Client v0.3-alpha */ public class ReceivedMessageProcessor implements Consumer { diff --git a/src/main/java/envoy/client/net/Receiver.java b/src/main/java/envoy/client/net/Receiver.java index 08ea28d..bdaf853 100644 --- a/src/main/java/envoy/client/net/Receiver.java +++ b/src/main/java/envoy/client/net/Receiver.java @@ -19,7 +19,7 @@ import envoy.util.SerializationUtils; * Created: 30.12.2019
* * @author Kai S. K. Engelbart - * @since Envoy v0.3-alpha + * @since Envoy Client v0.3-alpha */ public class Receiver extends Thread { @@ -85,4 +85,4 @@ public class Receiver extends Thread { * Removes all object processors registered at this {@link Receiver}. */ public void removeAllProcessors() { processors.clear(); } -} \ No newline at end of file +} diff --git a/src/main/java/envoy/client/net/UserStatusChangeProcessor.java b/src/main/java/envoy/client/net/UserStatusChangeProcessor.java index 69a36d4..0fffec2 100644 --- a/src/main/java/envoy/client/net/UserStatusChangeProcessor.java +++ b/src/main/java/envoy/client/net/UserStatusChangeProcessor.java @@ -12,7 +12,7 @@ import envoy.event.UserStatusChangeEvent; * Created: 2 Feb 2020
* * @author Leon Hofmeister - * @since Envoy v0.3-alpha + * @since Envoy Client v0.3-alpha */ public class UserStatusChangeProcessor implements Consumer { @@ -20,7 +20,7 @@ public class UserStatusChangeProcessor implements Consumer6 Feb 2020
* * @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); diff --git a/src/main/java/envoy/client/net/package-info.java b/src/main/java/envoy/client/net/package-info.java index 5ee0e4e..9c2a79e 100644 --- a/src/main/java/envoy/client/net/package-info.java +++ b/src/main/java/envoy/client/net/package-info.java @@ -4,6 +4,6 @@ * @author Kai S. K. Engelbart * @author Leon Hofmeister * @author Maximilian Käfer - * @since Envoy v0.1-beta + * @since Envoy Client v0.1-beta */ package envoy.client.net; diff --git a/src/main/java/envoy/client/ui/Color.java b/src/main/java/envoy/client/ui/Color.java index eabdac5..bb89943 100644 --- a/src/main/java/envoy/client/ui/Color.java +++ b/src/main/java/envoy/client/ui/Color.java @@ -11,7 +11,7 @@ import java.awt.color.ColorSpace; * Created: 23.12.2019
* * @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()); } } diff --git a/src/main/java/envoy/client/ui/ContextMenu.java b/src/main/java/envoy/client/ui/ContextMenu.java new file mode 100644 index 0000000..c184eb5 --- /dev/null +++ b/src/main/java/envoy/client/ui/ContextMenu.java @@ -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.
+ *
+ * Project: envoy-client
+ * File: ContextMenu.java
+ * Created: 17 Mar 2020
+ * + * @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 items = new HashMap<>(); + private Map icons = new HashMap<>(); + private Map 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 itemsWithActions, Map itemIcons, + Map 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 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 items) { this.items = items; } + + /** + * @return the icons + * @since Envoy Client v0.1-beta + */ + public Map getIcons() { return icons; } + + /** + * @param icons the icons to set + * @since Envoy Client v0.1-beta + */ + public void setIcons(Map 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 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 mnemonics) { this.mnemonics = mnemonics; } +} diff --git a/src/main/java/envoy/client/ui/IconUtil.java b/src/main/java/envoy/client/ui/IconUtil.java new file mode 100644 index 0000000..0092483 --- /dev/null +++ b/src/main/java/envoy/client/ui/IconUtil.java @@ -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.
+ *
+ * Project: envoy-client + * File: IconUtil.java + * Created: 16.03.2020 + * + * @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 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 > EnumMap loadByEnum(Class enumClass, int size) throws IOException { + var icons = new EnumMap(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; + } +} diff --git a/src/main/java/envoy/client/ui/MessageListRenderer.java b/src/main/java/envoy/client/ui/MessageListRenderer.java deleted file mode 100644 index 6b70e1b..0000000 --- a/src/main/java/envoy/client/ui/MessageListRenderer.java +++ /dev/null @@ -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.
- *
- * Project: envoy-client
- * File: MessageListRenderer.java
- * Created: 19 Oct 2019
- * - * @author Kai S. K. Engelbart - * @author Maximilian Käfer - * @author Leon Hofmeister - * @since Envoy v0.1-alpha - */ -public class MessageListRenderer implements ComponentListCellRenderer { - - private JTextArea messageTextArea; - - @Override - public JPanel getListCellComponent(ComponentList 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; - } -} diff --git a/src/main/java/envoy/client/ui/StatusTrayIcon.java b/src/main/java/envoy/client/ui/StatusTrayIcon.java index 4513aea..621655f 100644 --- a/src/main/java/envoy/client/ui/StatusTrayIcon.java +++ b/src/main/java/envoy/client/ui/StatusTrayIcon.java @@ -16,7 +16,7 @@ import envoy.exception.EnvoyException; * Created: 3 Dec 2019
* * @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 { @@ -94,4 +94,4 @@ public class StatusTrayIcon { throw new EnvoyException("Could not attach Envoy tray icon to system tray.", e); } } -} \ No newline at end of file +} diff --git a/src/main/java/envoy/client/ui/Theme.java b/src/main/java/envoy/client/ui/Theme.java old mode 100644 new mode 100755 index 2956882..96d8357 --- a/src/main/java/envoy/client/ui/Theme.java +++ b/src/main/java/envoy/client/ui/Theme.java @@ -10,7 +10,7 @@ import java.util.Map; * Created: 23 Nov 2019
* * @author Maximilian Kä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} 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 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"); } diff --git a/src/main/java/envoy/client/ui/ChatWindow.java b/src/main/java/envoy/client/ui/container/ChatWindow.java similarity index 76% rename from src/main/java/envoy/client/ui/ChatWindow.java rename to src/main/java/envoy/client/ui/container/ChatWindow.java index 6a5ae38..81b155c 100644 --- a/src/main/java/envoy/client/ui/ChatWindow.java +++ b/src/main/java/envoy/client/ui/container/ChatWindow.java @@ -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ä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 userList = new JList<>(); private DefaultListModel userListModel = new DefaultListModel<>(); - private ComponentList messageList = new ComponentList<>(new MessageListRenderer()); + private ComponentList 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(); @@ -71,13 +84,12 @@ public class ChatWindow extends JFrame { private PrimaryButton addContact = new PrimaryButton("+"); // Search Contacts - private final JPanel searchPane = new JPanel(); - private final PrimaryButton cancelButton = new PrimaryButton("x"); - private final PrimaryTextArea searchField = new PrimaryTextArea(space); - private final PrimaryScrollPane scrollForPossibleContacts = new PrimaryScrollPane(); - private final ContactsSearchRenderer contactRenderer = new ContactsSearchRenderer(); - private final ComponentListModel contactsModel = new ComponentListModel<>(); - private final ComponentList contactList = new ComponentList<>(contactRenderer); + private final JPanel searchPane = new JPanel(); + private final PrimaryButton cancelButton = new PrimaryButton("x"); + private final PrimaryTextArea searchField = new PrimaryTextArea(space); + private final PrimaryScrollPane scrollForPossibleContacts = new PrimaryScrollPane(); + private final Model contactsModel = new Model<>(); + private final ComponentList contactList = new ComponentList().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 commands = Map.of("forward selected message", evt -> { + final Message selectedMessage = messageList.getSingleSelectedElement(); + List 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; - + 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()) - .build(); + // Create message + final Message message = new MessageBuilder(localDb.getUser().getId(), currentChat.getRecipient().getId(), localDb.getIdGenerator()) + .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 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()); } } diff --git a/src/main/java/envoy/client/ui/container/ContactsChooserDialog.java b/src/main/java/envoy/client/ui/container/ContactsChooserDialog.java new file mode 100755 index 0000000..26a099e --- /dev/null +++ b/src/main/java/envoy/client/ui/container/ContactsChooserDialog.java @@ -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.
+ *
+ * Project: envoy-client
+ * File: ContactsChooserDialog.java
+ * Created: 15 Mar 2020
+ * + * @author Leon Hofmeister + * @since Envoy Client v0.1-beta + */ +public class ContactsChooserDialog extends JDialog { + + private static final long serialVersionUID = -5774558118579032256L; + + private ComponentList contactList = new ComponentList().setModel(new Model()) + .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 + * ArrayList. + * + * @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 showForwardingDialog(String title, Component parent, Message message, Collection users) { + ContactsChooserDialog dialog = new ContactsChooserDialog(parent); + dialog.setTitle(title); + dialog.setDefaultCloseOperation(DISPOSE_ON_CLOSE); + dialog.addCancelButtonActionListener(e -> dialog.dispose()); + + List results = new ArrayList<>(); + dialog.addOkButtonActionListener(e -> { + results.addAll(dialog.getContactList().getSelectedElements()); + dialog.dispose(); + }); + Model 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 getContactList() { return contactList; } + + private void addOkButtonActionListener(ActionListener l) { okButton.addActionListener(l); } + + private void addCancelButtonActionListener(ActionListener l) { cancelButton.addActionListener(l); } +} diff --git a/src/main/java/envoy/client/ui/container/ContextMenu.java b/src/main/java/envoy/client/ui/container/ContextMenu.java new file mode 100755 index 0000000..c52c042 --- /dev/null +++ b/src/main/java/envoy/client/ui/container/ContextMenu.java @@ -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.
+ *
+ * Project: envoy-client
+ * File: ContextMenu.java
+ * Created: 17 Mar 2020
+ * + * @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 items = new HashMap<>(); + private Map icons = new HashMap<>(); + private Map 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 itemsWithActions, Map itemIcons, + Map 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 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 items) { this.items = items; } + + /** + * @return the icons + * @since Envoy Client v0.1-beta + */ + public Map getIcons() { return icons; } + + /** + * @param icons the icons to set + * @since Envoy Client v0.1-beta + */ + public void setIcons(Map 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 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 mnemonics) { this.mnemonics = mnemonics; } + + /** + * {@inheritDoc}
+ * 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}
+ * 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()); + } +} diff --git a/src/main/java/envoy/client/ui/LoginDialog.java b/src/main/java/envoy/client/ui/container/LoginDialog.java similarity index 97% rename from src/main/java/envoy/client/ui/LoginDialog.java rename to src/main/java/envoy/client/ui/container/LoginDialog.java index 193ae8d..23c0113 100644 --- a/src/main/java/envoy/client/ui/LoginDialog.java +++ b/src/main/java/envoy/client/ui/container/LoginDialog.java @@ -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ä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 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..."); diff --git a/src/main/java/envoy/client/ui/container/package-info.java b/src/main/java/envoy/client/ui/container/package-info.java new file mode 100644 index 0000000..aab40a5 --- /dev/null +++ b/src/main/java/envoy/client/ui/container/package-info.java @@ -0,0 +1,13 @@ +/** + * This package contains all graphical Containers, like Dialogs and Frames.
+ *
+ * Project: envoy-client
+ * File: package-info.java
+ * Created: 16 Mar 2020
+ * + * @author Leon Hofmeister + * @author Kai S. K. Engelbart + * @author Maximilian Käfer + * @since Envoy Client v0.1-beta + */ +package envoy.client.ui.container; diff --git a/src/main/java/envoy/client/ui/list/ComponentList.java b/src/main/java/envoy/client/ui/list/ComponentList.java index 9b5b749..f2de2c3 100644 --- a/src/main/java/envoy/client/ui/list/ComponentList.java +++ b/src/main/java/envoy/client/ui/list/ComponentList.java @@ -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.
*
* Project: envoy-client
@@ -17,141 +19,90 @@ import javax.swing.*; * * @param 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 extends JPanel { - private ComponentListModel model; - private ComponentListCellRenderer renderer; - - private int currentSelection = -1; + private Model model; + private Renderer renderer; + private SelectionHandler selectionHandler; + private SelectionMode selectionMode = SelectionMode.NONE; + private Set 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 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 model, ComponentListCellRenderer 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 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() { - removeAll(); - if (model != null) model.forEach(this::add); - revalidate(); + if (model != null) { + removeAll(); + 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.
- *
- * 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(); - // Assign new selection - currentSelection = index; + // Select item + if (selectionMode != SelectionMode.NONE) { - // Update current selection - update(currentSelection, true); + // Assign new selection + selection.add(index); + + // Update element + if (selectionHandler != null) selectionHandler.selectionChanged(model.get(index), element, true); + } } revalidate(); @@ -159,14 +110,152 @@ public class ComponentList 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 getSelection() { return selection; } + + /** + * @return a set of all selected elements + * @since Envoy Client v0.1-beta + */ + public Set getSelectedElements() { + var selectedElements = new HashSet(); + 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 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 setModel(Model 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 getRenderer() { return renderer; } + + /** + * @param renderer the renderer to set + * @return this component list + * @since Envoy Client v0.1-beta + */ + public ComponentList setRenderer(Renderer 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 setSelectionMode(SelectionMode selectionMode) { + this.selectionMode = selectionMode; + clearSelection(); + return this; + } + + /** + * @return the selection handler + * @since Envoy Client v0.1-beta + */ + public SelectionHandler getSelectionHandler() { return selectionHandler; } + + /** + * @param selectionHandler the selection handler to set + * @since Envoy Client v0.1-beta + */ + public void setSelectionHandler(SelectionHandler selectionHandler) { this.selectionHandler = selectionHandler; } } diff --git a/src/main/java/envoy/client/ui/list/ComponentListModel.java b/src/main/java/envoy/client/ui/list/Model.java similarity index 82% rename from src/main/java/envoy/client/ui/list/ComponentListModel.java rename to src/main/java/envoy/client/ui/list/Model.java index 95775c1..4f13636 100644 --- a/src/main/java/envoy/client/ui/list/ComponentListModel.java +++ b/src/main/java/envoy/client/ui/list/Model.java @@ -9,19 +9,19 @@ import java.util.List; * Stores objects that will be displayed in a {@link ComponentList}.
*
* Project: envoy-client
- * File: ComponentListModel.java
+ * File: Model.java
* Created: 25.01.2020
* * @param 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 implements Iterable, Serializable { +public final class Model implements Iterable, Serializable { private List elements = new ArrayList<>(); transient private ComponentList 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 implements Iterable, Serializable { * @param e the element to add * @return {@code true} * @see java.util.List#add(java.lang.Object) - * @since Envoy v0.3-alpha + * @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 implements Iterable, 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 implements Iterable, 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 implements Iterable, 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 implements Iterable, 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 implements Iterable, 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 iterator() { @@ -111,10 +111,10 @@ public final class ComponentListModel implements Iterable, Serializable { * synchronization. * * @param componentList the component list to set - * @since Envoy v0.3-alpha + * @since Envoy Client v0.3-alpha */ void setComponentList(ComponentList componentList) { this.componentList = componentList; - if (componentList != null) componentList.synchronizeModel(); + if (componentList != null && componentList.getRenderer() != null) componentList.synchronizeModel(); } } diff --git a/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java b/src/main/java/envoy/client/ui/list/Renderer.java similarity index 80% rename from src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java rename to src/main/java/envoy/client/ui/list/Renderer.java index a8bdda6..f2d3ad9 100644 --- a/src/main/java/envoy/client/ui/list/ComponentListCellRenderer.java +++ b/src/main/java/envoy/client/ui/list/Renderer.java @@ -7,14 +7,15 @@ import javax.swing.JComponent; * that can be rendered.
*
* Project: envoy-client
- * File: ComponentListCellRenderer.java
+ * File: Renderer.java
* Created: 25.01.2020
* * @param 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 { +@FunctionalInterface +public interface Renderer { /** * Provides a Swing component representing a list element. @@ -24,7 +25,7 @@ public interface ComponentListCellRenderer { * @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 list, E value, boolean isSelected); + JComponent getListCellComponent(ComponentList list, E value); } diff --git a/src/main/java/envoy/client/ui/list/SelectionHandler.java b/src/main/java/envoy/client/ui/list/SelectionHandler.java new file mode 100644 index 0000000..d8c1984 --- /dev/null +++ b/src/main/java/envoy/client/ui/list/SelectionHandler.java @@ -0,0 +1,28 @@ +package envoy.client.ui.list; + +import javax.swing.JComponent; + +/** + * Handles the selection of elements in a {@link ComponentList}.
+ *
+ * Project: envoy-client + * File: SelectionHandler.java + * Created: 21.03.2020 + * + * @author Kai S. K. Engelbart + * @param the type of the underlying {@link ComponentList} + * @since Envoy Client v0.1-beta + */ +@FunctionalInterface +public interface SelectionHandler { + + /** + * 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); +} diff --git a/src/main/java/envoy/client/ui/list/package-info.java b/src/main/java/envoy/client/ui/list/package-info.java index 5f12ffd..50d553b 100644 --- a/src/main/java/envoy/client/ui/list/package-info.java +++ b/src/main/java/envoy/client/ui/list/package-info.java @@ -5,6 +5,6 @@ * @author Kai S. K. Engelbart * @author Leon Hofmeister * @author Maximilian Käfer - * @since Envoy v0.3-alpha + * @since Envoy Client v0.3-alpha */ package envoy.client.ui.list; diff --git a/src/main/java/envoy/client/ui/ContactsSearchRenderer.java b/src/main/java/envoy/client/ui/list_component/ContactSearchComponent.java similarity index 51% rename from src/main/java/envoy/client/ui/ContactsSearchRenderer.java rename to src/main/java/envoy/client/ui/list_component/ContactSearchComponent.java index 83fb81d..9f66506 100644 --- a/src/main/java/envoy/client/ui/ContactsSearchRenderer.java +++ b/src/main/java/envoy/client/ui/list_component/ContactSearchComponent.java @@ -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.
- *
- * Project: envoy-client
- * File: ContactsSearchRenderer.java
- * Created: 08.02.2020
+ * Project: envoy-client + * File: ContactSearchComponent.java + * Created: 21.03.2020 * - * @author Maximilian Käfer * @author Kai S. K. Engelbart - * @since Envoy v0.3-alpha + * @since Envoy Client v0.1-beta */ -public class ContactsSearchRenderer implements ComponentListCellRenderer { +public class ContactSearchComponent extends JComponent { - @Override - public JComponent getListCellComponent(ComponentList 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 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 { 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); } -} \ No newline at end of file +} diff --git a/src/main/java/envoy/client/ui/list_component/MessageComponent.java b/src/main/java/envoy/client/ui/list_component/MessageComponent.java new file mode 100644 index 0000000..abe436c --- /dev/null +++ b/src/main/java/envoy/client/ui/list_component/MessageComponent.java @@ -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: envoy-client + * File: MessageComponent.java + * Created: 21.03.2020 + * + * @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 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 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); + } +} diff --git a/src/main/java/envoy/client/ui/list_component/UserComponent.java b/src/main/java/envoy/client/ui/list_component/UserComponent.java new file mode 100644 index 0000000..7eebfdf --- /dev/null +++ b/src/main/java/envoy/client/ui/list_component/UserComponent.java @@ -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}.
+ *
+ * Project: envoy-client + * File: UserComponent.java + * Created: 21.03.2020 + * + * @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); + } +} diff --git a/src/main/java/envoy/client/ui/list_component/package-info.java b/src/main/java/envoy/client/ui/list_component/package-info.java new file mode 100644 index 0000000..89da45c --- /dev/null +++ b/src/main/java/envoy/client/ui/list_component/package-info.java @@ -0,0 +1,14 @@ +/** + * This package contains swing components that can be displayed by + * {@link envoy.client.ui.list.ComponentList}.
+ *
+ * Project: envoy-client
+ * File: package-info.java
+ * Created: 21 Mar 2020
+ * + * @author Leon Hofmeister + * @author Kai S. K. Engelbart + * @author Maximilian Käfer + * @since Envoy Client v0.1-beta + */ +package envoy.client.ui.list_component; diff --git a/src/main/java/envoy/client/ui/package-info.java b/src/main/java/envoy/client/ui/package-info.java index 22238af..10bf4ee 100644 --- a/src/main/java/envoy/client/ui/package-info.java +++ b/src/main/java/envoy/client/ui/package-info.java @@ -4,6 +4,6 @@ * @author Kai S. K. Engelbart * @author Leon Hofmeister * @author Maximilian Käfer - * @since Envoy v0.1-beta + * @since Envoy Client v0.1-beta */ package envoy.client.ui; diff --git a/src/main/java/envoy/client/ui/PrimaryButton.java b/src/main/java/envoy/client/ui/primary/PrimaryButton.java similarity index 92% rename from src/main/java/envoy/client/ui/PrimaryButton.java rename to src/main/java/envoy/client/ui/primary/PrimaryButton.java index 65c52a0..811939f 100644 --- a/src/main/java/envoy/client/ui/PrimaryButton.java +++ b/src/main/java/envoy/client/ui/primary/PrimaryButton.java @@ -1,63 +1,63 @@ -package envoy.client.ui; - -import java.awt.Graphics; - -import javax.swing.JButton; - -/** - * Project: envoy-client
- * File: PrimaryButton.javaEvent.java
- * Created: 07.12.2019
- * - * @author Kai S. K. Engelbart - * @author Maximilian Käfer - * @since Envoy v0.2-alpha - */ -public class PrimaryButton extends JButton { - - private static final long serialVersionUID = 3662266120667728364L; - - private int arcSize; - - /** - * Creates a primary button - * - * @param title the title of the button - * @since Envoy 0.2-alpha - */ - public PrimaryButton(String title) { this(title, 6); } - - /** - * Creates a primary button - * - * @param title the title of the button - * @param arcSize the size of the arc used to draw the round button edges - * @since Envoy 0.2-alpha - */ - public PrimaryButton(String title, int arcSize) { - super(title); - setBorderPainted(false); - setFocusPainted(false); - setContentAreaFilled(false); - this.arcSize = arcSize; - } - - @Override - protected void paintComponent(Graphics g) { - g.setColor(getBackground()); - g.fillRoundRect(0, 0, getWidth(), getHeight(), arcSize, arcSize); - super.paintComponent(g); - } - - /** - * @return the arcSize - * @since Envoy 0.2-alpha - */ - public int getArcSize() { return arcSize; } - - /** - * @param arcSize the arcSize to set - * @since Envoy 0.2-alpha - */ - public void setArcSize(int arcSize) { this.arcSize = arcSize; } -} \ No newline at end of file +package envoy.client.ui.primary; + +import java.awt.Graphics; + +import javax.swing.JButton; + +/** + * Project: envoy-client
+ * File: PrimaryButton.javaEvent.java
+ * Created: 07.12.2019
+ * + * @author Kai S. K. Engelbart + * @author Maximilian Käfer + * @since Envoy Client v0.2-alpha + */ +public class PrimaryButton extends JButton { + + private static final long serialVersionUID = 3662266120667728364L; + + private int arcSize; + + /** + * Creates a primary button + * + * @param title the title of the button + * @since Envoy 0.2-alpha + */ + public PrimaryButton(String title) { this(title, 6); } + + /** + * Creates a primary button + * + * @param title the title of the button + * @param arcSize the size of the arc used to draw the round button edges + * @since Envoy 0.2-alpha + */ + public PrimaryButton(String title, int arcSize) { + super(title); + setBorderPainted(false); + setFocusPainted(false); + setContentAreaFilled(false); + this.arcSize = arcSize; + } + + @Override + protected void paintComponent(Graphics g) { + g.setColor(getBackground()); + g.fillRoundRect(0, 0, getWidth(), getHeight(), arcSize, arcSize); + super.paintComponent(g); + } + + /** + * @return the arcSize + * @since Envoy 0.2-alpha + */ + public int getArcSize() { return arcSize; } + + /** + * @param arcSize the arcSize to set + * @since Envoy 0.2-alpha + */ + public void setArcSize(int arcSize) { this.arcSize = arcSize; } +} diff --git a/src/main/java/envoy/client/ui/PrimaryScrollBar.java b/src/main/java/envoy/client/ui/primary/PrimaryScrollBar.java similarity index 88% rename from src/main/java/envoy/client/ui/PrimaryScrollBar.java rename to src/main/java/envoy/client/ui/primary/PrimaryScrollBar.java index 8e8131c..5630525 100644 --- a/src/main/java/envoy/client/ui/PrimaryScrollBar.java +++ b/src/main/java/envoy/client/ui/primary/PrimaryScrollBar.java @@ -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: envoy-client
@@ -20,7 +16,7 @@ import envoy.client.data.Settings; * Created: 14.12.2019
* * @author Maximilian Kä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(); diff --git a/src/main/java/envoy/client/ui/PrimaryScrollPane.java b/src/main/java/envoy/client/ui/primary/PrimaryScrollPane.java similarity index 92% rename from src/main/java/envoy/client/ui/PrimaryScrollPane.java rename to src/main/java/envoy/client/ui/primary/PrimaryScrollPane.java index f8e4dbc..a27b075 100644 --- a/src/main/java/envoy/client/ui/PrimaryScrollPane.java +++ b/src/main/java/envoy/client/ui/primary/PrimaryScrollPane.java @@ -1,7 +1,9 @@ -package envoy.client.ui; +package envoy.client.ui.primary; import javax.swing.JScrollPane; +import envoy.client.ui.Theme; + /** * Project: envoy-client
* File: PrimaryScrollPane.java
@@ -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
* 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; } } diff --git a/src/main/java/envoy/client/ui/PrimaryTextArea.java b/src/main/java/envoy/client/ui/primary/PrimaryTextArea.java similarity index 96% rename from src/main/java/envoy/client/ui/PrimaryTextArea.java rename to src/main/java/envoy/client/ui/primary/PrimaryTextArea.java index 29ed2f9..280d8e9 100644 --- a/src/main/java/envoy/client/ui/PrimaryTextArea.java +++ b/src/main/java/envoy/client/ui/primary/PrimaryTextArea.java @@ -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: 07.12.2019
* * @author Maximilian Käfer - * @since Envoy v0.2-alpha + * @since Envoy Client v0.2-alpha */ public class PrimaryTextArea extends JTextArea { diff --git a/src/main/java/envoy/client/ui/PrimaryToggleSwitch.java b/src/main/java/envoy/client/ui/primary/PrimaryToggleSwitch.java similarity index 84% rename from src/main/java/envoy/client/ui/PrimaryToggleSwitch.java rename to src/main/java/envoy/client/ui/primary/PrimaryToggleSwitch.java index 3f58e2e..d6931f7 100644 --- a/src/main/java/envoy/client/ui/PrimaryToggleSwitch.java +++ b/src/main/java/envoy/client/ui/primary/PrimaryToggleSwitch.java @@ -1,57 +1,58 @@ -package envoy.client.ui; - -import java.awt.Dimension; -import java.awt.Graphics; - -import javax.swing.JButton; - -import envoy.client.data.Settings; -import envoy.client.data.SettingsItem; - -/** - * This component can be used to toggle between two options. This will change - * the state of a {@code boolean} {@link SettingsItem}.
- *
- * Project: envoy-client
- * File: PrimaryToggleSwitch.java
- * Created: 21 Dec 2019
- * - * @author Maximilian Käfer - * @author Kai S. K. Engelbart - * @since Envoy v0.3-alpha - */ -public class PrimaryToggleSwitch extends JButton { - - private boolean state; - - private static final long serialVersionUID = -721155303106833184L; - - /** - * Initializes a {@link PrimaryToggleSwitch}. - * - * @param settingsItem the {@link SettingsItem} that is controlled by this - * {@link PrimaryToggleSwitch} - * @since Envoy v0.3-alpha - */ - public PrimaryToggleSwitch(SettingsItem settingsItem) { - setPreferredSize(new Dimension(50, 25)); - setMinimumSize(new Dimension(50, 25)); - setMaximumSize(new Dimension(50, 25)); - - setBorderPainted(false); - setFocusPainted(false); - setContentAreaFilled(false); - - state = settingsItem.get(); - addActionListener((evt) -> { state = !state; settingsItem.set(state); revalidate(); repaint(); }); - } - - @Override - public void paintComponent(Graphics g) { - 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.fillRoundRect(state ? 25 : 0, 0, 25, 25, 25, 25); - } -} \ No newline at end of file +package envoy.client.ui.primary; + +import java.awt.Dimension; +import java.awt.Graphics; + +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 + * the state of a {@code boolean} {@link SettingsItem}.
+ *
+ * Project: envoy-client
+ * File: PrimaryToggleSwitch.java
+ * Created: 21 Dec 2019
+ * + * @author Maximilian Käfer + * @author Kai S. K. Engelbart + * @since Envoy Client v0.3-alpha + */ +public class PrimaryToggleSwitch extends JButton { + + private boolean state; + + private static final long serialVersionUID = -721155303106833184L; + + /** + * Initializes a {@link PrimaryToggleSwitch}. + * + * @param settingsItem the {@link SettingsItem} that is controlled by this + * {@link PrimaryToggleSwitch} + * @since Envoy Client v0.3-alpha + */ + public PrimaryToggleSwitch(SettingsItem settingsItem) { + setPreferredSize(new Dimension(50, 25)); + setMinimumSize(new Dimension(50, 25)); + setMaximumSize(new Dimension(50, 25)); + + setBorderPainted(false); + setFocusPainted(false); + setContentAreaFilled(false); + + state = settingsItem.get(); + addActionListener((evt) -> { state = !state; settingsItem.set(state); revalidate(); repaint(); }); + } + + @Override + public void paintComponent(Graphics g) { + g.setColor(state ? Color.GREEN : Color.LIGHT_GRAY); + g.fillRoundRect(0, 0, getWidth(), getHeight(), 25, 25); + + g.setColor(Settings.getInstance().getCurrentTheme().getInteractableBackgroundColor()); + g.fillRoundRect(state ? 25 : 0, 0, 25, 25, 25, 25); + } +} diff --git a/src/main/java/envoy/client/ui/primary/package-info.java b/src/main/java/envoy/client/ui/primary/package-info.java new file mode 100644 index 0000000..d4c54fa --- /dev/null +++ b/src/main/java/envoy/client/ui/primary/package-info.java @@ -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.
+ * Primary elements are supposed to provide the main functionality of a UI + * component.
+ *
+ * Project: envoy-client
+ * File: package-info.java
+ * Created: 14 Mar 2020
+ * + * @author Leon Hofmeister + * @author Kai S. K. Engelbart + * @author Maximilian Käfer + * @since Envoy Client v0.1-beta + */ +package envoy.client.ui.primary; diff --git a/src/main/java/envoy/client/ui/UserListRenderer.java b/src/main/java/envoy/client/ui/renderer/UserListRenderer.java similarity index 90% rename from src/main/java/envoy/client/ui/UserListRenderer.java rename to src/main/java/envoy/client/ui/renderer/UserListRenderer.java index c43eeb6..685c61b 100644 --- a/src/main/java/envoy/client/ui/UserListRenderer.java +++ b/src/main/java/envoy/client/ui/renderer/UserListRenderer.java @@ -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äfer - * @since Envoy v0.1-alpha + * @since Envoy Client v0.1-alpha */ public class UserListRenderer extends JLabel implements ListCellRenderer { @@ -46,7 +46,7 @@ public class UserListRenderer extends JLabel implements ListCellRenderer { // 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 @@ -59,4 +59,4 @@ public class UserListRenderer extends JLabel implements ListCellRenderer { } return this; } -} \ No newline at end of file +} diff --git a/src/main/java/envoy/client/ui/renderer/package-info.java b/src/main/java/envoy/client/ui/renderer/package-info.java new file mode 100644 index 0000000..6a6f58e --- /dev/null +++ b/src/main/java/envoy/client/ui/renderer/package-info.java @@ -0,0 +1,14 @@ +/** + * This package contains all Envoy-specific renderers for lists that store an + * arbitrary number of JComponents.
+ *
+ * Project: envoy-client
+ * File: package-info.java
+ * Created: 14 Mar 2020
+ * + * @author Leon Hofmeister + * @author Kai S. K. Engelbart + * @author Maximilian Käfer + * @since Envoy Client v0.1-beta + */ +package envoy.client.ui.renderer; diff --git a/src/main/java/envoy/client/ui/settings/GeneralSettingsPanel.java b/src/main/java/envoy/client/ui/settings/GeneralSettingsPanel.java index 13df564..b194e1c 100644 --- a/src/main/java/envoy/client/ui/settings/GeneralSettingsPanel.java +++ b/src/main/java/envoy/client/ui/settings/GeneralSettingsPanel.java @@ -1,93 +1,89 @@ -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; - -import javax.swing.JComponent; -import javax.swing.JTextPane; - -import envoy.client.data.Settings; -import envoy.client.data.SettingsItem; -import envoy.client.ui.Theme; -import envoy.util.EnvoyLog; - -/** - * Displays GUI components that allow general settings regarding the client.
- *
- * Project: envoy-client
- * File: GeneralSettingsPanel.java
- * Created: 21 Dec 2019
- * - * @author Maximilian Käfer - * @since Envoy v0.3-alpha - */ -public class GeneralSettingsPanel extends SettingsPanel { - - private Theme theme; - - private static final String[] items = { "onCloseMode", "enterToSend" }; - private static final Logger logger = EnvoyLog.getLogger(GeneralSettingsPanel.class); - private static final long serialVersionUID = -7470848775130754239L; - - /** - * This is the constructor for the General class. Here the user can set general - * settings for the client. - * - * @param parent the {@link SettingsScreen} as a part of which this - * {@link SettingsPanel} is displayed - * @since Envoy v0.3-alpha - */ - public GeneralSettingsPanel(SettingsScreen parent) { - super(parent); - theme = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()); - - setBackground(theme.getCellColor()); - - GridBagLayout gbl_general = new GridBagLayout(); - gbl_general.columnWidths = new int[] { 1, 1 }; - gbl_general.rowHeights = new int[] { 1, 1, 1 }; - gbl_general.columnWeights = new double[] { 1.0, 0.1 }; - gbl_general.rowWeights = new double[] { 0.02, 0.02, 1.0 }; - - setLayout(gbl_general); - - for (int i = 0; i < items.length; i++) - try { - createSettingElement(i, Settings.getInstance().getItems().get(items[i])); - } catch (SecurityException | ReflectiveOperationException e) { - logger.log(Level.WARNING, "Could not create settings item", e); - } - } - - private void createSettingElement(int gridy, SettingsItem settingsItem) throws SecurityException, ReflectiveOperationException { - JTextPane descriptionText = new JTextPane(); - - JComponent settingComponent = settingsItem.getComponent(); - - GridBagConstraints gbc_toggleSwitch = new GridBagConstraints(); - gbc_toggleSwitch.gridx = 1; - gbc_toggleSwitch.gridy = gridy; - - add(settingComponent, gbc_toggleSwitch); - - descriptionText.setText(settingsItem.getDescription()); - descriptionText.setBackground(theme.getBackgroundColor()); - descriptionText.setForeground(theme.getBackgroundColor().invert()); - descriptionText.setEditable(false); - - GridBagConstraints gbc_descriptionText = new GridBagConstraints(); - gbc_descriptionText.fill = GridBagConstraints.BOTH; - gbc_descriptionText.gridx = 0; - gbc_descriptionText.gridy = gridy; - gbc_descriptionText.insets = new Insets(5, 5, 5, 5); - - add(descriptionText, gbc_descriptionText); - } - - @Override - public ActionListener getOkButtonAction() { return evt -> {}; } -} \ No newline at end of file +package envoy.client.ui.settings; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.swing.JComponent; +import javax.swing.JTextPane; + +import envoy.client.data.Settings; +import envoy.client.data.SettingsItem; +import envoy.client.ui.Theme; +import envoy.util.EnvoyLog; + +/** + * Displays GUI components that allow general settings regarding the client.
+ *
+ * Project: envoy-client
+ * File: GeneralSettingsPanel.java
+ * Created: 21 Dec 2019
+ * + * @author Maximilian Käfer + * @since Envoy Client v0.3-alpha + */ +public class GeneralSettingsPanel extends SettingsPanel { + + private Theme theme; + + private static final String[] items = { "onCloseMode", "enterToSend" }; + private static final Logger logger = EnvoyLog.getLogger(GeneralSettingsPanel.class); + private static final long serialVersionUID = -7470848775130754239L; + + /** + * This is the constructor for the General class. Here the user can set general + * settings for the client. + * + * @param parent the {@link SettingsScreen} as a part of which this + * {@link SettingsPanel} is displayed + * @since Envoy Client v0.3-alpha + */ + public GeneralSettingsPanel(SettingsScreen parent) { + super(parent); + theme = Settings.getInstance().getCurrentTheme(); + + setBackground(theme.getCellColor()); + + GridBagLayout gbl_general = new GridBagLayout(); + gbl_general.columnWidths = new int[] { 1, 1 }; + gbl_general.rowHeights = new int[] { 1, 1, 1 }; + gbl_general.columnWeights = new double[] { 1.0, 0.1 }; + gbl_general.rowWeights = new double[] { 0.02, 0.02, 1.0 }; + + setLayout(gbl_general); + + for (int i = 0; i < items.length; i++) + try { + createSettingElement(i, Settings.getInstance().getItems().get(items[i])); + } catch (SecurityException | ReflectiveOperationException e) { + logger.log(Level.WARNING, "Could not create settings item", e); + } + } + + private void createSettingElement(int gridy, SettingsItem settingsItem) throws SecurityException, ReflectiveOperationException { + JTextPane descriptionText = new JTextPane(); + + JComponent settingComponent = settingsItem.getComponent(); + + GridBagConstraints gbc_toggleSwitch = new GridBagConstraints(); + gbc_toggleSwitch.gridx = 1; + gbc_toggleSwitch.gridy = gridy; + + add(settingComponent, gbc_toggleSwitch); + + descriptionText.setText(settingsItem.getDescription()); + descriptionText.setBackground(theme.getBackgroundColor()); + descriptionText.setForeground(theme.getBackgroundColor().invert()); + descriptionText.setEditable(false); + + GridBagConstraints gbc_descriptionText = new GridBagConstraints(); + gbc_descriptionText.fill = GridBagConstraints.BOTH; + gbc_descriptionText.gridx = 0; + gbc_descriptionText.gridy = gridy; + gbc_descriptionText.insets = new Insets(5, 5, 5, 5); + + add(descriptionText, gbc_descriptionText); + } +} diff --git a/src/main/java/envoy/client/ui/settings/NewThemeScreen.java b/src/main/java/envoy/client/ui/settings/NewThemeScreen.java index 02dd42a..6c6c273 100644 --- a/src/main/java/envoy/client/ui/settings/NewThemeScreen.java +++ b/src/main/java/envoy/client/ui/settings/NewThemeScreen.java @@ -1,226 +1,228 @@ -package envoy.client.ui.settings; - -import java.awt.*; -import java.util.function.Consumer; - -import javax.swing.JDialog; -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; - -/** - * Displays window where you can choose a name for the new {@link Theme}. - *
- * Project: envoy-client
- * File: NewThemeScreen.java
- * Created: 26 Dec 2019
- * - * @author Maximilian Käfer - * @since Envoy v0.3-alpha - */ -public class NewThemeScreen extends JDialog { - - private final JPanel standardPanel = new JPanel(); - private final JPanel secondaryPanel = new JPanel(); - private JTextPane text = new JTextPane(); - private PrimaryTextArea nameEnterTextArea = new PrimaryTextArea(4); - private PrimaryButton confirmButton = new PrimaryButton("Confirm"); - - private JTextPane errorText = new JTextPane(); - private PrimaryButton otherName = new PrimaryButton("Other Name"); - private PrimaryButton overwrite = new PrimaryButton("Overwrite"); - - private final Consumer newThemeAction, modifyThemeAction; - - private static final long serialVersionUID = 2369985550946300976L; - - /** - * Creates a window, where you can choose a name for a new {@link Theme}.
- * 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 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 - */ - public NewThemeScreen(SettingsScreen parent, Consumer newThemeAction, Consumer modifyThemeAction) { - this.newThemeAction = newThemeAction; - this.modifyThemeAction = modifyThemeAction; - - setLocationRelativeTo(parent); - setTitle("New Theme"); - setModal(true); - - setDimensions(true); - setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); - - Theme theme = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()); - - getContentPane().setLayout(new BorderLayout()); - standardPanel.setBackground(theme.getBackgroundColor()); - secondaryPanel.setBackground(theme.getBackgroundColor()); - loadStandardContent(theme); - } - - private void setDimensions(boolean isStandard) { - Dimension size = isStandard ? new Dimension(300, 170) : new Dimension(300, 225); - setPreferredSize(size); - setMinimumSize(size); - setMaximumSize(size); - } - - private void loadStandardContent(Theme theme) { - getContentPane().removeAll(); - - // ContentPane - GridBagLayout gbl_contentPanel = new GridBagLayout(); - - gbl_contentPanel.columnWidths = new int[] { 1, 1 }; - gbl_contentPanel.rowHeights = new int[] { 1, 1, 1 }; - gbl_contentPanel.columnWeights = new double[] { 1, 1 }; - gbl_contentPanel.rowWeights = new double[] { 1, 1, 1 }; - - getContentPane().add(standardPanel, BorderLayout.CENTER); - standardPanel.setLayout(gbl_contentPanel); - - // text.setFont(new Font()); - text.setText("Please enter a name for the new Theme"); - text.setAlignmentX(CENTER_ALIGNMENT); - text.setBackground(theme.getCellColor()); - text.setForeground(theme.getUserNameColor()); - text.setEditable(false); - - GridBagConstraints gbc_text = new GridBagConstraints(); - gbc_text.fill = GridBagConstraints.HORIZONTAL; - gbc_text.gridx = 0; - gbc_text.gridy = 0; - gbc_text.gridwidth = 2; - gbc_text.insets = new Insets(5, 5, 5, 5); - - standardPanel.add(text, gbc_text); - - nameEnterTextArea.setBackground(theme.getCellColor()); - nameEnterTextArea.setForeground(theme.getTypingMessageColor()); - nameEnterTextArea.setText(""); - nameEnterTextArea.setEditable(true); - - GridBagConstraints gbc_input = new GridBagConstraints(); - gbc_input.fill = GridBagConstraints.HORIZONTAL; - gbc_input.gridx = 0; - gbc_input.gridy = 1; - gbc_input.gridwidth = 2; - gbc_input.insets = new Insets(5, 5, 5, 5); - - standardPanel.add(nameEnterTextArea, gbc_input); - - confirmButton.setBackground(theme.getInteractableBackgroundColor()); - confirmButton.setForeground(theme.getInteractableForegroundColor()); - - GridBagConstraints gbc_confirmButton = new GridBagConstraints(); - gbc_confirmButton.gridx = 0; - gbc_confirmButton.gridy = 2; - gbc_confirmButton.gridwidth = 2; - gbc_confirmButton.insets = new Insets(5, 5, 5, 5); - - standardPanel.add(confirmButton, gbc_confirmButton); - - confirmButton.addActionListener((evt) -> { - if (!nameEnterTextArea.getText().isEmpty()) if (Settings.getInstance().getThemes().containsKey(nameEnterTextArea.getText())) { - // load other panel - setDimensions(false); - loadSecondaryPage(theme); - } else { - newThemeAction.accept(nameEnterTextArea.getText()); - dispose(); - } - }); - } - - private void loadSecondaryPage(Theme theme) { - // ContentPane - getContentPane().removeAll(); - - GridBagLayout gbl_secondaryPanel = new GridBagLayout(); - - gbl_secondaryPanel.columnWidths = new int[] { 1, 1 }; - gbl_secondaryPanel.rowHeights = new int[] { 1, 1, 1, 1 }; - gbl_secondaryPanel.columnWeights = new double[] { 1, 1 }; - gbl_secondaryPanel.rowWeights = new double[] { 1, 1, 1, 1 }; - - getContentPane().add(secondaryPanel, BorderLayout.CENTER); - secondaryPanel.setLayout(gbl_secondaryPanel); - - // text.setFont(new Font()); - text.setText("Please enter a name for the new Theme"); - text.setAlignmentX(CENTER_ALIGNMENT); - text.setBackground(theme.getCellColor()); - text.setForeground(theme.getUserNameColor()); - text.setEditable(false); - - GridBagConstraints gbc_text = new GridBagConstraints(); - gbc_text.fill = GridBagConstraints.HORIZONTAL; - gbc_text.gridx = 0; - gbc_text.gridy = 0; - gbc_text.gridwidth = 2; - gbc_text.insets = new Insets(5, 5, 5, 5); - - secondaryPanel.add(text, gbc_text); - - nameEnterTextArea.setBackground(theme.getCellColor()); - nameEnterTextArea.setForeground(theme.getTypingMessageColor()); - nameEnterTextArea.setEditable(false); - - GridBagConstraints gbc_input = new GridBagConstraints(); - gbc_input.fill = GridBagConstraints.HORIZONTAL; - gbc_input.gridx = 0; - gbc_input.gridy = 1; - gbc_input.gridwidth = 2; - gbc_input.insets = new Insets(5, 5, 5, 5); - - secondaryPanel.add(nameEnterTextArea, gbc_input); - - errorText.setText("The name does already exist. Choose another one or overwrite the old theme."); - errorText.setAlignmentX(CENTER_ALIGNMENT); - errorText.setBackground(theme.getCellColor()); - errorText.setForeground(theme.getUserNameColor()); - errorText.setEditable(false); - - GridBagConstraints gbc_errorText = new GridBagConstraints(); - gbc_errorText.fill = GridBagConstraints.HORIZONTAL; - gbc_errorText.gridx = 0; - gbc_errorText.gridy = 2; - gbc_errorText.gridwidth = 2; - gbc_errorText.insets = new Insets(5, 5, 5, 5); - - secondaryPanel.add(errorText, gbc_errorText); - - otherName.setBackground(theme.getInteractableBackgroundColor()); - otherName.setForeground(theme.getInteractableForegroundColor()); - - GridBagConstraints gbc_otherName = new GridBagConstraints(); - gbc_otherName.gridx = 0; - gbc_otherName.gridy = 3; - gbc_otherName.insets = new Insets(5, 5, 5, 5); - - secondaryPanel.add(otherName, gbc_otherName); - - overwrite.setBackground(theme.getInteractableBackgroundColor()); - overwrite.setForeground(theme.getInteractableForegroundColor()); - - GridBagConstraints gbc_overwrite = new GridBagConstraints(); - gbc_overwrite.gridx = 1; - gbc_overwrite.gridy = 3; - gbc_overwrite.insets = new Insets(5, 5, 5, 5); - - secondaryPanel.add(overwrite, gbc_overwrite); - - otherName.addActionListener((evt) -> { setDimensions(true); loadStandardContent(theme); }); - - overwrite.addActionListener((evt) -> { modifyThemeAction.accept(nameEnterTextArea.getText()); dispose(); }); - } -} \ No newline at end of file +package envoy.client.ui.settings; + +import java.awt.*; +import java.util.function.Consumer; + +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.JTextPane; + +import envoy.client.data.Settings; +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}. + *
+ * Project: envoy-client
+ * File: NewThemeScreen.java
+ * Created: 26 Dec 2019
+ * + * @author Maximilian Käfer + * @since Envoy Client v0.3-alpha + */ +public class NewThemeScreen extends JDialog { + + private final JPanel standardPanel = new JPanel(); + private final JPanel secondaryPanel = new JPanel(); + private JTextPane text = new JTextPane(); + private PrimaryTextArea nameEnterTextArea = new PrimaryTextArea(4); + private PrimaryButton confirmButton = new PrimaryButton("Confirm"); + + private JTextPane errorText = new JTextPane(); + private PrimaryButton otherName = new PrimaryButton("Other Name"); + private PrimaryButton overwrite = new PrimaryButton("Overwrite"); + + private final Consumer newThemeAction, modifyThemeAction; + + private static final long serialVersionUID = 2369985550946300976L; + + /** + * Creates a window, where you can choose a name for a new {@link Theme}.
+ * 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 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 Client v0.3-alpha + */ + public NewThemeScreen(SettingsScreen parent, Consumer newThemeAction, Consumer modifyThemeAction) { + this.newThemeAction = newThemeAction; + this.modifyThemeAction = modifyThemeAction; + + setLocationRelativeTo(parent); + setTitle("New Theme"); + setModal(true); + + setDimensions(true); + setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + + Theme theme = Settings.getInstance().getCurrentTheme(); + + getContentPane().setLayout(new BorderLayout()); + standardPanel.setBackground(theme.getBackgroundColor()); + secondaryPanel.setBackground(theme.getBackgroundColor()); + loadStandardContent(theme); + } + + private void setDimensions(boolean isStandard) { + Dimension size = isStandard ? new Dimension(300, 170) : new Dimension(300, 225); + setPreferredSize(size); + setMinimumSize(size); + setMaximumSize(size); + } + + private void loadStandardContent(Theme theme) { + getContentPane().removeAll(); + + // ContentPane + GridBagLayout gbl_contentPanel = new GridBagLayout(); + + gbl_contentPanel.columnWidths = new int[] { 1, 1 }; + gbl_contentPanel.rowHeights = new int[] { 1, 1, 1 }; + gbl_contentPanel.columnWeights = new double[] { 1, 1 }; + gbl_contentPanel.rowWeights = new double[] { 1, 1, 1 }; + + getContentPane().add(standardPanel, BorderLayout.CENTER); + standardPanel.setLayout(gbl_contentPanel); + + // text.setFont(new Font()); + text.setText("Please enter a name for the new Theme"); + text.setAlignmentX(CENTER_ALIGNMENT); + text.setBackground(theme.getCellColor()); + text.setForeground(theme.getUserNameColor()); + text.setEditable(false); + + GridBagConstraints gbc_text = new GridBagConstraints(); + gbc_text.fill = GridBagConstraints.HORIZONTAL; + gbc_text.gridx = 0; + gbc_text.gridy = 0; + gbc_text.gridwidth = 2; + gbc_text.insets = new Insets(5, 5, 5, 5); + + standardPanel.add(text, gbc_text); + + nameEnterTextArea.setBackground(theme.getCellColor()); + nameEnterTextArea.setForeground(theme.getTypingMessageColor()); + nameEnterTextArea.setText(""); + nameEnterTextArea.setEditable(true); + + GridBagConstraints gbc_input = new GridBagConstraints(); + gbc_input.fill = GridBagConstraints.HORIZONTAL; + gbc_input.gridx = 0; + gbc_input.gridy = 1; + gbc_input.gridwidth = 2; + gbc_input.insets = new Insets(5, 5, 5, 5); + + standardPanel.add(nameEnterTextArea, gbc_input); + + confirmButton.setBackground(theme.getInteractableBackgroundColor()); + confirmButton.setForeground(theme.getInteractableForegroundColor()); + + GridBagConstraints gbc_confirmButton = new GridBagConstraints(); + gbc_confirmButton.gridx = 0; + gbc_confirmButton.gridy = 2; + gbc_confirmButton.gridwidth = 2; + gbc_confirmButton.insets = new Insets(5, 5, 5, 5); + + standardPanel.add(confirmButton, gbc_confirmButton); + + confirmButton.addActionListener((evt) -> { + if (!nameEnterTextArea.getText().isEmpty()) if (Settings.getInstance().getThemes().containsKey(nameEnterTextArea.getText())) { + // load other panel + setDimensions(false); + loadSecondaryPage(theme); + } else { + newThemeAction.accept(nameEnterTextArea.getText()); + dispose(); + } + }); + } + + private void loadSecondaryPage(Theme theme) { + // ContentPane + getContentPane().removeAll(); + + GridBagLayout gbl_secondaryPanel = new GridBagLayout(); + + gbl_secondaryPanel.columnWidths = new int[] { 1, 1 }; + gbl_secondaryPanel.rowHeights = new int[] { 1, 1, 1, 1 }; + gbl_secondaryPanel.columnWeights = new double[] { 1, 1 }; + gbl_secondaryPanel.rowWeights = new double[] { 1, 1, 1, 1 }; + + getContentPane().add(secondaryPanel, BorderLayout.CENTER); + secondaryPanel.setLayout(gbl_secondaryPanel); + + // text.setFont(new Font()); + text.setText("Please enter a name for the new Theme"); + text.setAlignmentX(CENTER_ALIGNMENT); + text.setBackground(theme.getCellColor()); + text.setForeground(theme.getUserNameColor()); + text.setEditable(false); + + GridBagConstraints gbc_text = new GridBagConstraints(); + gbc_text.fill = GridBagConstraints.HORIZONTAL; + gbc_text.gridx = 0; + gbc_text.gridy = 0; + gbc_text.gridwidth = 2; + gbc_text.insets = new Insets(5, 5, 5, 5); + + secondaryPanel.add(text, gbc_text); + + nameEnterTextArea.setBackground(theme.getCellColor()); + nameEnterTextArea.setForeground(theme.getTypingMessageColor()); + nameEnterTextArea.setEditable(false); + + GridBagConstraints gbc_input = new GridBagConstraints(); + gbc_input.fill = GridBagConstraints.HORIZONTAL; + gbc_input.gridx = 0; + gbc_input.gridy = 1; + gbc_input.gridwidth = 2; + gbc_input.insets = new Insets(5, 5, 5, 5); + + secondaryPanel.add(nameEnterTextArea, gbc_input); + + errorText.setText("The name does already exist. Choose another one or overwrite the old theme."); + errorText.setAlignmentX(CENTER_ALIGNMENT); + errorText.setBackground(theme.getCellColor()); + errorText.setForeground(theme.getUserNameColor()); + errorText.setEditable(false); + + GridBagConstraints gbc_errorText = new GridBagConstraints(); + gbc_errorText.fill = GridBagConstraints.HORIZONTAL; + gbc_errorText.gridx = 0; + gbc_errorText.gridy = 2; + gbc_errorText.gridwidth = 2; + gbc_errorText.insets = new Insets(5, 5, 5, 5); + + secondaryPanel.add(errorText, gbc_errorText); + + otherName.setBackground(theme.getInteractableBackgroundColor()); + otherName.setForeground(theme.getInteractableForegroundColor()); + + GridBagConstraints gbc_otherName = new GridBagConstraints(); + gbc_otherName.gridx = 0; + gbc_otherName.gridy = 3; + gbc_otherName.insets = new Insets(5, 5, 5, 5); + + secondaryPanel.add(otherName, gbc_otherName); + + overwrite.setBackground(theme.getInteractableBackgroundColor()); + overwrite.setForeground(theme.getInteractableForegroundColor()); + + GridBagConstraints gbc_overwrite = new GridBagConstraints(); + gbc_overwrite.gridx = 1; + gbc_overwrite.gridy = 3; + gbc_overwrite.insets = new Insets(5, 5, 5, 5); + + secondaryPanel.add(overwrite, gbc_overwrite); + + otherName.addActionListener((evt) -> { setDimensions(true); loadStandardContent(theme); }); + + overwrite.addActionListener((evt) -> { modifyThemeAction.accept(nameEnterTextArea.getText()); dispose(); }); + } +} diff --git a/src/main/java/envoy/client/ui/settings/SettingsPanel.java b/src/main/java/envoy/client/ui/settings/SettingsPanel.java index 80469c5..96be74e 100644 --- a/src/main/java/envoy/client/ui/settings/SettingsPanel.java +++ b/src/main/java/envoy/client/ui/settings/SettingsPanel.java @@ -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: 20 Dec 2019
* * @author Kai S. K. Engelbart - * @since Envoy v0.2-alpha + * @since Envoy Client v0.2-alpha */ public abstract class SettingsPanel extends JPanel { @@ -24,16 +22,9 @@ public abstract class SettingsPanel extends JPanel { /** * Initializes a {@link SettingsPanel}. - * + * * @param parent the {@link SettingsScreen} as a part of which this * {@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(); } diff --git a/src/main/java/envoy/client/ui/settings/SettingsScreen.java b/src/main/java/envoy/client/ui/settings/SettingsScreen.java index c04da96..ba93270 100644 --- a/src/main/java/envoy/client/ui/settings/SettingsScreen.java +++ b/src/main/java/envoy/client/ui/settings/SettingsScreen.java @@ -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ä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,14 +163,10 @@ 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()); options.setForeground(theme.getUserNameColor()); options.setBackground(theme.getCellColor()); } -} \ No newline at end of file +} diff --git a/src/main/java/envoy/client/ui/settings/ThemeCustomizationPanel.java b/src/main/java/envoy/client/ui/settings/ThemeCustomizationPanel.java old mode 100644 new mode 100755 index a2aca9a..fce9754 --- a/src/main/java/envoy/client/ui/settings/ThemeCustomizationPanel.java +++ b/src/main/java/envoy/client/ui/settings/ThemeCustomizationPanel.java @@ -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,19 +24,19 @@ import envoy.util.EnvoyLog; * * @author Kai S. K. Engelbart * @author Maximilian Käfer - * @since Envoy v0.2-alpha + * @since Envoy Client v0.2-alpha */ public class ThemeCustomizationPanel extends SettingsPanel { private JPanel colorsPanel = new JPanel(); - private DefaultComboBoxModel themesModel = new DefaultComboBoxModel<>( - Settings.getInstance().getThemes().keySet().toArray(new String[0])); - private JComboBox themes = new JComboBox<>(themesModel); + private DefaultComboBoxModel themesModel; + private JComboBox themes; private Theme temporaryTheme; - private boolean themeChanged; + private PrimaryButton createThemeButton = new PrimaryButton("Create Theme"); - private final Insets insets = new Insets(5, 5, 5, 5); + private boolean themeChanged; + private final Insets insets = new Insets(5, 5, 5, 5); private static final Logger logger = EnvoyLog.getLogger(ThemeCustomizationPanel.class); private static final long serialVersionUID = -8697897390666456624L; @@ -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() { + themes.addItemListener(e -> { + String selectedValue = (String) themes.getSelectedItem(); + logger.log(Level.FINEST, "Selected theme: " + selectedValue); - @Override - public void itemStateChanged(ItemEvent 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)); - } + 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); @@ -232,4 +243,4 @@ public class ThemeCustomizationPanel extends SettingsPanel { colorsPanel.add(button, gbc_button); } -} \ No newline at end of file +} diff --git a/src/main/java/envoy/client/ui/settings/package-info.java b/src/main/java/envoy/client/ui/settings/package-info.java index bde6540..5325a73 100644 --- a/src/main/java/envoy/client/ui/settings/package-info.java +++ b/src/main/java/envoy/client/ui/settings/package-info.java @@ -4,6 +4,6 @@ * @author Kai S. K. Engelbart * @author Leon Hofmeister * @author Maximilian Käfer - * @since Envoy v0.2-alpha + * @since Envoy Client v0.2-alpha */ package envoy.client.ui.settings; diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 4227885..d5c969a 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -5,7 +5,7 @@ * @author Kai S. K. Engelbart * @author Leon Hofmeister * @author Maximilian Käfer - * @since Envoy v0.1-beta + * @since Envoy Client v0.1-beta */ module envoy { diff --git a/src/main/resources/icons/forward.png b/src/main/resources/icons/forward.png new file mode 100644 index 0000000..9c75854 Binary files /dev/null and b/src/main/resources/icons/forward.png differ diff --git a/src/main/resources/icons/messagestatus/read.png b/src/main/resources/icons/messagestatus/read.png new file mode 100644 index 0000000..d81f23c Binary files /dev/null and b/src/main/resources/icons/messagestatus/read.png differ diff --git a/src/main/resources/icons/messagestatus/received.png b/src/main/resources/icons/messagestatus/received.png new file mode 100644 index 0000000..3da9282 Binary files /dev/null and b/src/main/resources/icons/messagestatus/received.png differ diff --git a/src/main/resources/icons/messagestatus/sent.png b/src/main/resources/icons/messagestatus/sent.png new file mode 100644 index 0000000..2ff4cca Binary files /dev/null and b/src/main/resources/icons/messagestatus/sent.png differ diff --git a/src/main/resources/icons/messagestatus/waiting.png b/src/main/resources/icons/messagestatus/waiting.png new file mode 100644 index 0000000..e767b28 Binary files /dev/null and b/src/main/resources/icons/messagestatus/waiting.png differ diff --git a/src/main/resources/icons/settings.png b/src/main/resources/icons/settings.png new file mode 100644 index 0000000..f956615 Binary files /dev/null and b/src/main/resources/icons/settings.png differ