Merge pull request #123 from informatik-ag-ngl/f/forward_messages
Added message forwarding capability
This commit is contained in:
		| @@ -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 { | ||||
| 			} | ||||
| 		})); | ||||
| 	} | ||||
| } | ||||
| } | ||||
| @@ -17,7 +17,7 @@ import envoy.util.EnvoyLog; | ||||
|  * | ||||
|  * @param <T> the type of cached elements | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.3-alpha | ||||
|  * @since Envoy Client v0.3-alpha | ||||
|  */ | ||||
| public class Cache<T> implements Consumer<T>, Serializable { | ||||
|  | ||||
| @@ -31,7 +31,7 @@ public class Cache<T> implements Consumer<T>, Serializable { | ||||
| 	 * Adds an element to the cache. | ||||
| 	 * | ||||
| 	 * @param element the element to add | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	@Override | ||||
| 	public void accept(T element) { | ||||
| @@ -43,7 +43,7 @@ public class Cache<T> implements Consumer<T>, Serializable { | ||||
| 	 * Sets the processor to which cached elements are relayed. | ||||
| 	 * | ||||
| 	 * @param processor the processor to set | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void setProcessor(Consumer<T> processor) { this.processor = processor; } | ||||
|  | ||||
| @@ -51,7 +51,7 @@ public class Cache<T> implements Consumer<T>, Serializable { | ||||
| 	 * Relays all cached elements to the processor. | ||||
| 	 * | ||||
| 	 * @throws IllegalStateException if the processor is not initialized | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void relay() { | ||||
| 		if (processor == null) throw new IllegalStateException("Processor is not defined"); | ||||
|   | ||||
| @@ -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<Message>	model	= new ComponentListModel<>(); | ||||
| 	private final Model<Message>	model	= new Model<>(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Provides the list of messages that the recipient receives.<br> | ||||
| 	 * Saves the Messages in the corresponding chat at that Point. | ||||
| 	 * | ||||
| 	 * @param recipient the user who receives the messages | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 * @since Envoy Client v0.1-alpha | ||||
| 	 */ | ||||
| 	public Chat(User recipient) { this.recipient = recipient; } | ||||
|  | ||||
| @@ -43,7 +43,7 @@ public class Chat implements Serializable { | ||||
| 	 * Appends a message to the bottom of this chat | ||||
| 	 * | ||||
| 	 * @param message the message to append | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 * @since Envoy Client v0.1-alpha | ||||
| 	 */ | ||||
| 	public void appendMessage(Message message) { model.add(message); } | ||||
|  | ||||
| @@ -56,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<Message> getModel() { return model; } | ||||
| 	public Model<Message> getModel() { return model; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the recipient of a message | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 * @since Envoy Client v0.1-alpha | ||||
| 	 */ | ||||
| 	public User getRecipient() { return recipient; } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -18,7 +18,7 @@ import envoy.data.LoginCredentials; | ||||
|  * Created: <strong>01.03.2020</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.1-beta | ||||
|  * @since Envoy Client v0.1-beta | ||||
|  */ | ||||
| public class ClientConfig extends Config { | ||||
|  | ||||
| @@ -26,7 +26,7 @@ public class ClientConfig extends Config { | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the singleton instance of the client config | ||||
| 	 * @since Envoy v0.1-beta | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public static ClientConfig getInstance() { | ||||
| 		if (config == null) config = new ClientConfig(); | ||||
| @@ -47,68 +47,68 @@ public class ClientConfig extends Config { | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the host name of the Envoy server | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 * @since Envoy Client v0.1-alpha | ||||
| 	 */ | ||||
| 	public String getServer() { return (String) items.get("server").get(); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the port at which the Envoy server is located on the host | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 * @since Envoy Client v0.1-alpha | ||||
| 	 */ | ||||
| 	public Integer getPort() { return (Integer) items.get("port").get(); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the local database specific to the client user | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 * @since Envoy Client v0.1-alpha | ||||
| 	 */ | ||||
| 	public File getLocalDB() { return (File) items.get("localDB").get(); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return {@code true} if the local database is to be ignored | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public Boolean isIgnoreLocalDB() { return (Boolean) items.get("ignoreLocalDB").get(); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the directory in which all local files are saves | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public File getHomeDirectory() { return (File) items.get("homeDirectory").get(); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the minimal {@link Level} to log inside the log file | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public Level getFileLevelBarrier() { return (Level) items.get("fileLevelBarrier").get(); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the minimal {@link Level} to log inside the console | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public Level getConsoleLevelBarrier() { return (Level) items.get("consoleLevelBarrier").get(); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the user name | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public String getUser() { return (String) items.get("user").get(); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the password | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public char[] getPassword() { return (char[]) items.get("password").get(); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return {@code true} if user name and password are set | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public boolean hasLoginCredentials() { return getUser() != null && getPassword() != null; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return login credentials for the specified user name and password, without | ||||
| 	 *         the registration option | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public LoginCredentials getLoginCredentials() { | ||||
| 		try { | ||||
|   | ||||
| @@ -16,7 +16,7 @@ import envoy.event.MessageStatusChangeEvent; | ||||
|  * Created: <strong>3 Feb 2020</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.3-alpha | ||||
|  * @since Envoy Client v0.3-alpha | ||||
|  */ | ||||
| public abstract class LocalDb { | ||||
|  | ||||
| @@ -30,7 +30,7 @@ public abstract class LocalDb { | ||||
| 	/** | ||||
| 	 * Initializes a storage space for a user-specific list of chats. | ||||
| 	 * | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void initializeUserStorage() {} | ||||
|  | ||||
| @@ -39,7 +39,7 @@ public abstract class LocalDb { | ||||
| 	 * as well. The message id generator will also be saved if present. | ||||
| 	 * | ||||
| 	 * @throws Exception if the saving process failed | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void save() throws Exception {} | ||||
|  | ||||
| @@ -47,7 +47,7 @@ public abstract class LocalDb { | ||||
| 	 * Loads all user data. | ||||
| 	 * | ||||
| 	 * @throws Exception if the loading process failed | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void loadUsers() throws Exception {} | ||||
|  | ||||
| @@ -55,21 +55,21 @@ public abstract class LocalDb { | ||||
| 	 * Loads all data of the client user. | ||||
| 	 * | ||||
| 	 * @throws Exception if the loading process failed | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void loadUserData() throws Exception {} | ||||
|  | ||||
| 	/** | ||||
| 	 * Loads the ID generator. Any exception thrown during this process is ignored. | ||||
| 	 * | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void loadIdGenerator() {} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return a {@code Map<String, User>} of all users stored locally with their | ||||
| 	 *         user names as keys | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public Map<String, User> getUsers() { return users; } | ||||
|  | ||||
| @@ -81,7 +81,7 @@ public abstract class LocalDb { | ||||
| 	/** | ||||
| 	 * @return all saved {@link Chat} objects that list the client user as the | ||||
| 	 *         sender | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 * @since Envoy Client v0.1-alpha | ||||
| 	 **/ | ||||
| 	public List<Chat> getChats() { return chats; } | ||||
|  | ||||
| @@ -92,55 +92,55 @@ public abstract class LocalDb { | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the {@link User} who initialized the local database | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public User getUser() { return user; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param user the user to set | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public void setUser(User user) { this.user = user; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the message ID generator | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public IdGenerator getIdGenerator() { return idGenerator; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param idGenerator the message ID generator to set | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void setIdGenerator(IdGenerator idGenerator) { this.idGenerator = idGenerator; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return {@code true} if an {@link IdGenerator} is present | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public boolean hasIdGenerator() { return idGenerator != null; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the offline message cache | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public Cache<Message> getMessageCache() { return messageCache; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param messageCache the offline message cache to set | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void setMessageCache(Cache<Message> messageCache) { this.messageCache = messageCache; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the offline status cache | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public Cache<MessageStatusChangeEvent> getStatusCache() { return statusCache; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param statusCache the offline status cache to set | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void setStatusCache(Cache<MessageStatusChangeEvent> statusCache) { this.statusCache = statusCache; } | ||||
|  | ||||
| @@ -150,7 +150,7 @@ public abstract class LocalDb { | ||||
| 	 * @param id the ID of the message to search for | ||||
| 	 * @return the message with the corresponding ID, or {@code null} if no message | ||||
| 	 *         has been found | ||||
| 	 * @since Envoy v0.1-beta | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public Message getMessage(long id) { | ||||
| 		for (Chat c : chats) | ||||
|   | ||||
| @@ -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) {} | ||||
| 	} | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -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<String>) items.get("currentTheme")).set(themeName); } | ||||
|  | ||||
| @@ -132,7 +138,7 @@ public class Settings { | ||||
| 	 * @return {@code true}, if pressing the {@code Enter} key suffices to send a | ||||
| 	 *         message. Otherwise it has to be pressed in conjunction with the | ||||
| 	 *         {@code Control} key. | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public Boolean isEnterToSend() { return (Boolean) items.get("enterToSend").get(); } | ||||
|  | ||||
| @@ -142,13 +148,13 @@ public class Settings { | ||||
| 	 * @param enterToSend If set to {@code true} a message can be sent by pressing | ||||
| 	 *                    the {@code Enter} key. Otherwise it has to be pressed in | ||||
| 	 *                    conjunction with the {@code Control} key. | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public void setEnterToSend(boolean enterToSend) { ((SettingsItem<Boolean>) items.get("enterToSend")).set(enterToSend); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the current on close mode. | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public Boolean getCurrentOnCloseMode() { return (Boolean) items.get("onCloseMode").get(); } | ||||
|  | ||||
| @@ -156,7 +162,7 @@ public class Settings { | ||||
| 	 * Sets the current on close mode. | ||||
| 	 * | ||||
| 	 * @param currentOnCloseMode the on close mode that should be set. | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void setCurrentOnCloseMode(boolean currentOnCloseMode) { ((SettingsItem<Boolean>) items.get("onCloseMode")).set(currentOnCloseMode); } | ||||
|  | ||||
| @@ -172,7 +178,7 @@ public class Settings { | ||||
|  | ||||
| 	/** | ||||
| 	 * @return a {@code Map<String, Theme>} of all themes with their names as keys | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public Map<String, Theme> getThemes() { return themes; } | ||||
|  | ||||
| @@ -180,14 +186,14 @@ public class Settings { | ||||
| 	 * Sets the {@code Map<String, Theme>} of all themes with their names as keys | ||||
| 	 * | ||||
| 	 * @param themes the theme map to set | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public void setThemes(Map<String, Theme> themes) { this.themes = themes; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param themeName the name of the {@link Theme} to get | ||||
| 	 * @return the {@link Theme} with the specified name | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public Theme getTheme(String themeName) { return themes.get(themeName); } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ import java.util.function.Consumer; | ||||
|  | ||||
| import javax.swing.JComponent; | ||||
|  | ||||
| import envoy.client.ui.PrimaryToggleSwitch; | ||||
| import envoy.client.ui.primary.PrimaryToggleSwitch; | ||||
|  | ||||
| /** | ||||
|  * Encapsulates a persistent value that is directly or indirectly mutable by the | ||||
| @@ -19,7 +19,7 @@ import envoy.client.ui.PrimaryToggleSwitch; | ||||
|  *  | ||||
|  * @param <T> the type of this {@link SettingsItem}'s value | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.3-alpha | ||||
|  * @since Envoy Client v0.3-alpha | ||||
|  */ | ||||
| public class SettingsItem<T> implements Serializable { | ||||
|  | ||||
| @@ -45,7 +45,7 @@ public class SettingsItem<T> implements Serializable { | ||||
| 	 * @param value            the default value | ||||
| 	 * @param userFriendlyName the user friendly name (short) | ||||
| 	 * @param description      the description (long) | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public SettingsItem(T value, String userFriendlyName, String description) { | ||||
| 		this(value, componentClasses.get(value.getClass())); | ||||
| @@ -61,7 +61,7 @@ public class SettingsItem<T> implements Serializable { | ||||
| 	 *  | ||||
| 	 * @param value the default value | ||||
| 	 * @param componentClass the class of the {@link JComponent} to represent this {@link SettingsItem} with | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public SettingsItem(T value, Class<? extends JComponent> componentClass) { | ||||
| 		this.value			= value; | ||||
| @@ -72,7 +72,7 @@ public class SettingsItem<T> implements Serializable { | ||||
| 	 * @return an instance of the {@link JComponent} that represents this {@link SettingsItem} | ||||
| 	 * @throws ReflectiveOperationException if the component initialization failed | ||||
| 	 * @throws SecurityException if the component initialization failed | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public JComponent getComponent() throws ReflectiveOperationException, SecurityException { | ||||
| 		if (componentClass == null) throw new NullPointerException("Component class is null"); | ||||
| @@ -81,7 +81,7 @@ public class SettingsItem<T> implements Serializable { | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the value | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public T get() { return value; } | ||||
|  | ||||
| @@ -90,7 +90,7 @@ public class SettingsItem<T> implements Serializable { | ||||
| 	 * defined, it will be invoked with this value. | ||||
| 	 *  | ||||
| 	 * @param value the value to set | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void set(T value) { | ||||
| 		if (changeHandler != null && value != this.value) changeHandler.accept(value); | ||||
| @@ -99,37 +99,37 @@ public class SettingsItem<T> implements Serializable { | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the componentClass | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public Class<? extends JComponent> getComponentClass() { return componentClass; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param componentClass the componentClass to set | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void setComponentClass(Class<? extends JComponent> componentClass) { this.componentClass = componentClass; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the userFriendlyName | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public String getUserFriendlyName() { return userFriendlyName; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param userFriendlyName the userFriendlyName to set | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void setUserFriendlyName(String userFriendlyName) { this.userFriendlyName = userFriendlyName; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the description | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public String getDescription() { return description; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param description the description to set | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void setDescription(String description) { this.description = description; } | ||||
|  | ||||
| @@ -139,7 +139,7 @@ public class SettingsItem<T> implements Serializable { | ||||
| 	 * when the value changes. | ||||
| 	 *  | ||||
| 	 * @param changeHandler the changeHandler to set | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void setChangeHandler(Consumer<T> changeHandler) { | ||||
| 		this.changeHandler = changeHandler; | ||||
|   | ||||
| @@ -9,7 +9,7 @@ package envoy.client.data; | ||||
|  * Created: <strong>3 Feb 2020</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.3-alpha | ||||
|  * @since Envoy Client v0.3-alpha | ||||
|  */ | ||||
| public class TransientLocalDb extends LocalDb { | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -10,7 +10,7 @@ import envoy.event.Event; | ||||
|  * Created: <strong>8 Feb 2020</strong><br> | ||||
|  * | ||||
|  * @author Leon Hofmeister | ||||
|  * @since Envoy v0.3-alpha | ||||
|  * @since Envoy Client v0.3-alpha | ||||
|  */ | ||||
| public class HandshakeSuccessfulEvent extends Event.Valueless { | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ import envoy.event.Event; | ||||
|  * Created: <strong>4 Dec 2019</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.2-alpha | ||||
|  * @since Envoy Client v0.2-alpha | ||||
|  */ | ||||
| public class MessageCreationEvent extends Event<Message> { | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ import envoy.event.Event; | ||||
|  * Created: <strong>4 Dec 2019</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.2-alpha | ||||
|  * @since Envoy Client v0.2-alpha | ||||
|  */ | ||||
| public class MessageModificationEvent extends Event<Message> { | ||||
|  | ||||
|   | ||||
| @@ -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<Event<?>> { | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ import envoy.event.Event; | ||||
|  * Created: <strong>15 Dec 2019</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.2-alpha | ||||
|  * @since Envoy Client v0.2-alpha | ||||
|  */ | ||||
| public class ThemeChangeEvent extends Event<Theme> { | ||||
|  | ||||
| @@ -20,7 +20,7 @@ public class ThemeChangeEvent extends Event<Theme> { | ||||
| 	 * of the {@link Theme} currently in use | ||||
| 	 * | ||||
| 	 * @param theme the new currently used {@link Theme} object | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public ThemeChangeEvent(Theme theme) { super(theme); } | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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<Message> receivedMessageCache) throws IOException { | ||||
| 		checkOnline(); | ||||
| @@ -181,7 +181,7 @@ public class Client implements Closeable { | ||||
| 	 * | ||||
| 	 * @param message the message to send | ||||
| 	 * @throws IOException if the message does not reach the server | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void sendMessage(Message message) throws IOException { | ||||
| 		writeObject(message); | ||||
| @@ -200,7 +200,7 @@ public class Client implements Closeable { | ||||
| 	 * Requests a new {@link IdGenerator} from the server. | ||||
| 	 * | ||||
| 	 * @throws IOException if the request does not reach the server | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void requestIdGenerator() throws IOException { | ||||
| 		logger.info("Requesting new id generator..."); | ||||
| @@ -210,7 +210,7 @@ public class Client implements Closeable { | ||||
| 	/** | ||||
| 	 * @return a {@code Map<String, User>} of all users on the server with their | ||||
| 	 *         user names as keys | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public Map<String, User> getUsers() { | ||||
| 		checkOnline(); | ||||
| @@ -232,7 +232,7 @@ public class Client implements Closeable { | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the sender object that represents this client. | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 * @since Envoy Client v0.1-alpha | ||||
| 	 */ | ||||
| 	public User getSender() { return sender; } | ||||
|  | ||||
| @@ -240,7 +240,7 @@ public class Client implements Closeable { | ||||
| 	 * Sets the client user which is used to send messages. | ||||
| 	 * | ||||
| 	 * @param sender the client user to set | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public void setSender(User sender) { this.sender = sender; } | ||||
|  | ||||
| @@ -251,19 +251,19 @@ public class Client implements Closeable { | ||||
|  | ||||
| 	/** | ||||
| 	 * @return {@code true} if a connection to the server could be established | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public boolean isOnline() { return online; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the contacts of this {@link Client} | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public Contacts getContacts() { return contacts; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param contacts the contacts to set | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void setContacts(Contacts contacts) { this.contacts = contacts; } | ||||
| } | ||||
|   | ||||
| @@ -14,7 +14,7 @@ import envoy.util.EnvoyLog; | ||||
|  * Created: <strong>4 Feb 2020</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.3-alpha | ||||
|  * @since Envoy Client v0.3-alpha | ||||
|  */ | ||||
| public class MessageStatusChangeEventProcessor implements Consumer<MessageStatusChangeEvent> { | ||||
|  | ||||
| @@ -25,7 +25,7 @@ public class MessageStatusChangeEventProcessor implements Consumer<MessageStatus | ||||
| 	 * {@code RECEIVED} or {@code READ}. | ||||
| 	 * | ||||
| 	 * @param evt the status change event | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	@Override | ||||
| 	public void accept(MessageStatusChangeEvent evt) { | ||||
|   | ||||
| @@ -15,7 +15,7 @@ import envoy.util.EnvoyLog; | ||||
|  * Created: <strong>31.12.2019</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.3-alpha | ||||
|  * @since Envoy Client v0.3-alpha | ||||
|  */ | ||||
| public class ReceivedMessageProcessor implements Consumer<Message> { | ||||
|  | ||||
|   | ||||
| @@ -19,7 +19,7 @@ import envoy.util.SerializationUtils; | ||||
|  * Created: <strong>30.12.2019</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.3-alpha | ||||
|  * @since Envoy Client v0.3-alpha | ||||
|  */ | ||||
| public class Receiver extends Thread { | ||||
|  | ||||
| @@ -85,4 +85,4 @@ public class Receiver extends Thread { | ||||
| 	 * Removes all object processors registered at this {@link Receiver}. | ||||
| 	 */ | ||||
| 	public void removeAllProcessors() { processors.clear(); } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -12,7 +12,7 @@ import envoy.event.UserStatusChangeEvent; | ||||
|  * Created: <strong>2 Feb 2020</strong><br> | ||||
|  * | ||||
|  * @author Leon Hofmeister | ||||
|  * @since Envoy v0.3-alpha | ||||
|  * @since Envoy Client v0.3-alpha | ||||
|  */ | ||||
| public class UserStatusChangeProcessor implements Consumer<UserStatusChangeEvent> { | ||||
|  | ||||
| @@ -20,7 +20,7 @@ public class UserStatusChangeProcessor implements Consumer<UserStatusChangeEvent | ||||
|  | ||||
| 	/** | ||||
| 	 * @param localDb the local database in which status updates will by applied | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public UserStatusChangeProcessor(LocalDb localDb) { this.localDb = localDb; } | ||||
|  | ||||
|   | ||||
| @@ -19,7 +19,7 @@ import envoy.util.EnvoyLog; | ||||
|  * Created: <strong>6 Feb 2020</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.3-alpha | ||||
|  * @since Envoy Client v0.3-alpha | ||||
|  */ | ||||
| public class WriteProxy { | ||||
|  | ||||
| @@ -36,7 +36,7 @@ public class WriteProxy { | ||||
| 	 *                events | ||||
| 	 * @param localDb the local database used to cache messages and message status | ||||
| 	 *                change events | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public WriteProxy(Client client, LocalDb localDb) { | ||||
| 		this.client		= client; | ||||
| @@ -68,7 +68,7 @@ public class WriteProxy { | ||||
| 	 * Sends cached {@link Message}s and {@link MessageStatusChangeEvent}s to the | ||||
| 	 * server. | ||||
| 	 * | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void flushCache() { | ||||
| 		// Send messages | ||||
| @@ -84,7 +84,7 @@ public class WriteProxy { | ||||
| 	 * | ||||
| 	 * @param message the message to send | ||||
| 	 * @throws IOException if the message could not be sent | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void writeMessage(Message message) throws IOException { | ||||
| 		if (client.isOnline()) client.sendMessage(message); | ||||
| @@ -97,7 +97,7 @@ public class WriteProxy { | ||||
| 	 * | ||||
| 	 * @param evt the event to send | ||||
| 	 * @throws IOException if the event could not be sent | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void writeMessageStatusChangeEvent(MessageStatusChangeEvent evt) throws IOException { | ||||
| 		if (client.isOnline()) client.sendEvent(evt); | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -11,7 +11,7 @@ import java.awt.color.ColorSpace; | ||||
|  * Created: <strong>23.12.2019</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.3-alpha | ||||
|  * @since Envoy Client v0.3-alpha | ||||
|  */ | ||||
| @SuppressWarnings("javadoc") | ||||
| public class Color extends java.awt.Color { | ||||
| @@ -102,13 +102,13 @@ public class Color extends java.awt.Color { | ||||
| 	/** | ||||
| 	 * @return the inversion of this {@link Color} by replacing the red, green and | ||||
| 	 *         blue values by subtracting them form 255 | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public Color invert() { return new Color(255 - getRed(), 255 - getGreen(), 255 - getBlue()); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the hex value of this {@link Color} | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public String toHex() { return String.format("#%02x%02x%02x", getRed(), getGreen(), getBlue()); } | ||||
| } | ||||
|   | ||||
							
								
								
									
										200
									
								
								src/main/java/envoy/client/ui/ContextMenu.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								src/main/java/envoy/client/ui/ContextMenu.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,200 @@ | ||||
| package envoy.client.ui; | ||||
|  | ||||
| import java.awt.Component; | ||||
| import java.awt.event.ActionListener; | ||||
| import java.awt.event.MouseAdapter; | ||||
| import java.awt.event.MouseEvent; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
|  | ||||
| import javax.swing.*; | ||||
|  | ||||
| /** | ||||
|  * This class defines a menu that will be automatically called if | ||||
|  * {@link MouseEvent#isPopupTrigger()} returns true for the parent component. | ||||
|  * The user has the possibility to directly add actions to be performed when | ||||
|  * clicking on the element with the selected String. Additionally, for each | ||||
|  * element an {@link Icon} can be added, but it must not be. | ||||
|  * If the key(text) of an element starts with one of the predefined values, a | ||||
|  * special component will be called: either a {@link JRadioButtonMenuItem}, a | ||||
|  * {@link JCheckBoxMenuItem} or a {@link JMenu} will be created.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>ContextMenu.java</strong><br> | ||||
|  * Created: <strong>17 Mar 2020</strong><br> | ||||
|  * | ||||
|  * @author Leon Hofmeister | ||||
|  * @since Envoy Client v0.1-beta | ||||
|  */ | ||||
| public class ContextMenu extends JPopupMenu { | ||||
|  | ||||
| 	private static final long serialVersionUID = 2177146471226992104L; | ||||
|  | ||||
| 	/** | ||||
| 	 * If a key starts with this String, a {@link JCheckBoxMenuItem} will be created | ||||
| 	 */ | ||||
| 	public static final String	checkboxMenuItem	= "ChBoMI"; | ||||
| 	/** | ||||
| 	 * If a key starts with this String, a {@link JRadioButtonMenuItem} will be | ||||
| 	 * created | ||||
| 	 */ | ||||
| 	public static final String	radioButtonMenuItem	= "RaBuMI"; | ||||
| 	/** | ||||
| 	 * If a key starts with this String, a {@link JMenu} will be created | ||||
| 	 */ | ||||
| 	public static final String	subMenuItem			= "SubMI"; | ||||
|  | ||||
| 	private Map<String, ActionListener>	items		= new HashMap<>(); | ||||
| 	private Map<String, Icon>			icons		= new HashMap<>(); | ||||
| 	private Map<String, Integer>		mnemonics	= new HashMap<>(); | ||||
|  | ||||
| 	private ButtonGroup	radioButtonGroup	= new ButtonGroup(); | ||||
| 	private boolean		built				= false; | ||||
|  | ||||
| 	/** | ||||
| 	 * @param parent the component which will call this | ||||
| 	 *               {@link ContextMenu} | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public ContextMenu(Component parent) { setInvoker(parent); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param label            the string that a UI may use to display as a title | ||||
| 	 *                         for the pop-up menu | ||||
| 	 * @param parent           the component which will call this | ||||
| 	 *                         {@link ContextMenu} | ||||
| 	 * @param itemsWithActions a map of all strings to be displayed with according | ||||
| 	 *                         actions | ||||
| 	 * @param itemIcons        the icons to be displayed before a name, if wanted. | ||||
| 	 *                         Only keys in here will have an Icon displayed. More | ||||
| 	 *                         precisely, all keys here not included in the first | ||||
| 	 *                         map will be thrown out. | ||||
| 	 * @param itemMnemonics    the keyboard shortcuts that need to be pressed to | ||||
| 	 *                         automatically execute the {@link JMenuItem} with the | ||||
| 	 *                         given text | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public ContextMenu(String label, Component parent, Map<String, ActionListener> itemsWithActions, Map<String, Icon> itemIcons, | ||||
| 			Map<String, Integer> itemMnemonics) { | ||||
| 		super(label); | ||||
| 		setInvoker(parent); | ||||
| 		this.items		= (itemsWithActions != null) ? itemsWithActions : items; | ||||
| 		this.icons		= (itemIcons != null) ? itemIcons : icons; | ||||
| 		this.mnemonics	= (itemMnemonics != null) ? itemMnemonics : mnemonics; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Prepares the PopupMenu to be displayed. Should only be used once all map | ||||
| 	 * values have been set. | ||||
| 	 * | ||||
| 	 * @return this instance of {@link ContextMenu} to allow chaining behind the | ||||
| 	 *         constructor | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public ContextMenu build() { | ||||
| 		items.forEach((text, action) -> { | ||||
| 			// case radio button wanted | ||||
| 			AbstractButton item; | ||||
| 			if (text.startsWith(radioButtonMenuItem)) { | ||||
| 				item = new JRadioButtonMenuItem(text.substring(radioButtonMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null); | ||||
| 				radioButtonGroup.add(item); | ||||
| 				// case check box wanted | ||||
| 			} else if (text.startsWith(checkboxMenuItem)) | ||||
| 				item = new JCheckBoxMenuItem(text.substring(checkboxMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null); | ||||
| 			// case sub-menu wanted | ||||
| 			else if (text.startsWith(subMenuItem)) item = new JMenu(text.substring(subMenuItem.length())); | ||||
| 			else // normal JMenuItem wanted | ||||
| 				item = new JMenuItem(text, icons.containsKey(text) ? icons.get(text) : null); | ||||
| 			item.addActionListener(action); | ||||
| 			if (mnemonics.containsKey(text)) item.setMnemonic(mnemonics.get(text)); | ||||
| 			add(item); | ||||
| 		}); | ||||
| 		if (getInvoker() != null) { | ||||
| 			getInvoker().addMouseListener(getShowingListener()); | ||||
| 			built = true; | ||||
| 		} | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param label the string that a UI may use to display as a title for the | ||||
| 	 *              pop-up menu. | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public ContextMenu(String label) { super(label); } | ||||
|  | ||||
| 	private MouseAdapter getShowingListener() { | ||||
| 		return new MouseAdapter() { | ||||
|  | ||||
| 			@Override | ||||
| 			public void mouseClicked(MouseEvent e) { action(e); } | ||||
|  | ||||
| 			@Override | ||||
| 			public void mousePressed(MouseEvent e) { action(e); } | ||||
|  | ||||
| 			@Override | ||||
| 			public void mouseReleased(MouseEvent e) { action(e); } | ||||
|  | ||||
| 			private void action(MouseEvent e) { | ||||
| 				if (!built) build(); | ||||
| 				if (e.isPopupTrigger()) { | ||||
| 					// hides the menu if already visible | ||||
| 					if (!isVisible()) show(e.getComponent(), e.getX(), e.getY()); | ||||
| 					else setVisible(false); | ||||
| 				} | ||||
| 			} | ||||
| 		}; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Removes all subcomponents of this menu. | ||||
| 	 * | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public void clear() { | ||||
| 		removeAll(); | ||||
| 		items		= new HashMap<>(); | ||||
| 		icons		= new HashMap<>(); | ||||
| 		mnemonics	= new HashMap<>(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the items | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public Map<String, ActionListener> getItems() { return items; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param items the items with the displayed text and the according action to | ||||
| 	 *              take once called | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public void setItems(Map<String, ActionListener> items) { this.items = items; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the icons | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public Map<String, Icon> getIcons() { return icons; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param icons the icons to set | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public void setIcons(Map<String, Icon> icons) { this.icons = icons; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the mnemonics (the keyboard shortcuts that automatically execute the | ||||
| 	 *         command for a {@link JMenuItem} with corresponding text) | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public Map<String, Integer> getMnemonics() { return mnemonics; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param mnemonics the keyboard shortcuts that need to be pressed to | ||||
| 	 *                  automatically execute the {@link JMenuItem} with the given | ||||
| 	 *                  text | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public void setMnemonics(Map<String, Integer> mnemonics) { this.mnemonics = mnemonics; } | ||||
| } | ||||
							
								
								
									
										61
									
								
								src/main/java/envoy/client/ui/IconUtil.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/main/java/envoy/client/ui/IconUtil.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| package envoy.client.ui; | ||||
|  | ||||
| import java.awt.Image; | ||||
| import java.io.IOException; | ||||
| import java.util.EnumMap; | ||||
| import java.util.EnumSet; | ||||
|  | ||||
| import javax.imageio.ImageIO; | ||||
| import javax.swing.ImageIcon; | ||||
|  | ||||
| /** | ||||
|  * Provides static utility methods for loading icons from the resource | ||||
|  * folder.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong> | ||||
|  * File: <strong>IconUtil.java</strong> | ||||
|  * Created: <strong>16.03.2020</strong> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy Client v0.1-beta | ||||
|  */ | ||||
| public class IconUtil { | ||||
|  | ||||
| 	private IconUtil() {} | ||||
|  | ||||
| 	/** | ||||
| 	 * Loads an icon from resource folder and scales it to a given size. | ||||
| 	 *  | ||||
| 	 * @param path the path to the icon inside the resource folder | ||||
| 	 * @param size the size to scale the icon to | ||||
| 	 * @return the scaled icon | ||||
| 	 * @throws IOException if the loading process failed | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public static ImageIcon load(String path, int size) throws IOException { | ||||
| 		return new ImageIcon(ImageIO.read(IconUtil.class.getResourceAsStream(path)).getScaledInstance(size, size, Image.SCALE_SMOOTH)); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 *  | ||||
| 	 * Loads icons specified by an enum. The images have to be named like the | ||||
| 	 * lowercase enum constants with {@code .png} extension and be located inside a | ||||
| 	 * folder with the lowercase name of the enum, which must be contained inside | ||||
| 	 * the {@code /icons} folder. | ||||
| 	 *  | ||||
| 	 * @param <T>       the enum that specifies the icons to load | ||||
| 	 * @param enumClass the class of the enum | ||||
| 	 * @param size      the size to scale the icons to | ||||
| 	 * @return a map containing the loaded icons with the corresponding enum | ||||
| 	 *         constants as keys | ||||
| 	 * @throws IOException if the loading process failed | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public static <T extends Enum<T>> EnumMap<T, ImageIcon> loadByEnum(Class<T> enumClass, int size) throws IOException { | ||||
| 		var	icons	= new EnumMap<T, ImageIcon>(enumClass); | ||||
| 		var	path	= "/icons/" + enumClass.getSimpleName().toLowerCase() + "/"; | ||||
| 		for (var e : EnumSet.allOf(enumClass)) | ||||
| 			icons.put(e, load(path + e.toString().toLowerCase() + ".png", size)); | ||||
| 		return icons; | ||||
| 	} | ||||
| } | ||||
| @@ -1,93 +0,0 @@ | ||||
| package envoy.client.ui; | ||||
|  | ||||
| import java.awt.BorderLayout; | ||||
| import java.awt.Font; | ||||
| import java.text.SimpleDateFormat; | ||||
|  | ||||
| import javax.swing.*; | ||||
|  | ||||
| import envoy.client.data.Settings; | ||||
| import envoy.client.ui.list.ComponentList; | ||||
| import envoy.client.ui.list.ComponentListCellRenderer; | ||||
| import envoy.data.Message; | ||||
|  | ||||
| /** | ||||
|  * Defines how a message is displayed.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>MessageListRenderer.java</strong><br> | ||||
|  * Created: <strong>19 Oct 2019</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @author Maximilian Käfer | ||||
|  * @author Leon Hofmeister | ||||
|  * @since Envoy v0.1-alpha | ||||
|  */ | ||||
| public class MessageListRenderer implements ComponentListCellRenderer<Message> { | ||||
|  | ||||
| 	private JTextArea messageTextArea; | ||||
|  | ||||
| 	@Override | ||||
| 	public JPanel getListCellComponent(ComponentList<? extends Message> list, Message value, boolean isSelected) { | ||||
| 		final JPanel panel = new JPanel(); | ||||
| 		panel.setLayout(new BorderLayout()); | ||||
| 		final Theme theme = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()); | ||||
|  | ||||
| 		// Panel background | ||||
| 		panel.setBackground(isSelected ? theme.getSelectionColor() : theme.getCellColor()); | ||||
|  | ||||
| 		// TODO: Handle message attachments | ||||
|  | ||||
| 		final String	state	= value.getStatus().toString(); | ||||
| 		final String	date	= new SimpleDateFormat("dd.MM.yyyy HH:mm").format(value.getCreationDate()); | ||||
| 		final String	text	= value.getText(); | ||||
|  | ||||
| 		// The Label that displays the creation date of a message | ||||
| 		JLabel dateLabel = new JLabel(date); | ||||
| 		// Set the date color to be the value of DateColorChat | ||||
| 		dateLabel.setForeground(theme.getDateColor()); | ||||
|  | ||||
| 		panel.add(dateLabel, BorderLayout.NORTH); | ||||
|  | ||||
| 		// The JTextArea that displays the text content of a message and its status | ||||
| 		messageTextArea = new JTextArea(text + System.getProperty("line.separator")); | ||||
| 		messageTextArea.setLineWrap(true); | ||||
| 		messageTextArea.setWrapStyleWord(true); | ||||
| 		messageTextArea.setAlignmentX(0.5f); | ||||
| 		messageTextArea.setForeground(theme.getMessageTextColor()); | ||||
| 		messageTextArea.setBackground(panel.getBackground()); | ||||
| 		messageTextArea.setEditable(false); | ||||
|  | ||||
| 		panel.add(messageTextArea, BorderLayout.CENTER); | ||||
|  | ||||
| 		JLabel statusLabel = new JLabel(state); | ||||
| 		statusLabel.setFont(new Font("Arial", Font.BOLD, 14)); | ||||
| 		Color statusColor; | ||||
| 		switch (value.getStatus()) { | ||||
| 			case WAITING: | ||||
| 				statusColor = Color.gray; | ||||
| 				break; | ||||
| 			case SENT: | ||||
| 				statusColor = Color.blue; | ||||
| 				break; | ||||
| 			case RECEIVED: | ||||
| 				statusColor = Color.yellow; | ||||
| 				break; | ||||
| 			case READ: | ||||
| 				statusColor = Color.green; | ||||
| 				break; | ||||
| 			default: | ||||
| 				statusColor = theme.getMessageTextColor(); | ||||
| 				break; | ||||
| 		} | ||||
| 		statusLabel.setForeground(statusColor); | ||||
| 		statusLabel.setBackground(panel.getBackground()); | ||||
|  | ||||
| 		panel.add(statusLabel, BorderLayout.SOUTH); | ||||
|  | ||||
| 		// Define some space to the messages below | ||||
| 		panel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(), BorderFactory.createEtchedBorder())); | ||||
|  | ||||
| 		return panel; | ||||
| 	} | ||||
| } | ||||
| @@ -16,7 +16,7 @@ import envoy.exception.EnvoyException; | ||||
|  * Created: <strong>3 Dec 2019</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.2-alpha | ||||
|  * @since Envoy Client v0.2-alpha | ||||
|  */ | ||||
| public class StatusTrayIcon { | ||||
|  | ||||
| @@ -41,7 +41,7 @@ public class StatusTrayIcon { | ||||
| 	 *                    notifications are displayed | ||||
| 	 * @throws EnvoyException if the currently used OS does not support the System | ||||
| 	 *                        Tray API | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public StatusTrayIcon(Window focusTarget) throws EnvoyException { | ||||
| 		if (!SystemTray.isSupported()) throw new EnvoyException("The Envoy tray icon is not supported."); | ||||
| @@ -85,7 +85,7 @@ public class StatusTrayIcon { | ||||
| 	 * | ||||
| 	 * @throws EnvoyException if the status icon could not be attaches to the system | ||||
| 	 *                        tray for system-internal reasons | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public void show() throws EnvoyException { | ||||
| 		try { | ||||
| @@ -94,4 +94,4 @@ public class StatusTrayIcon { | ||||
| 			throw new EnvoyException("Could not attach Envoy tray icon to system tray.", e); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| } | ||||
|   | ||||
							
								
								
									
										37
									
								
								src/main/java/envoy/client/ui/Theme.java
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										37
									
								
								src/main/java/envoy/client/ui/Theme.java
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -10,7 +10,7 @@ import java.util.Map; | ||||
|  * Created: <strong>23 Nov 2019</strong><br> | ||||
|  * | ||||
|  * @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<String, Color>} of all colors defined for this theme | ||||
| 	 *         with their names as keys | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public Map<String, Color> getColors() { return colors; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return name of the theme | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public String getThemeName() { return themeName; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return interactableForegroundColor | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public Color getInteractableForegroundColor() { return colors.get("interactableForegroundColor"); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the {@link Color} in which the text content of a message should be | ||||
| 	 *         displayed | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public Color getMessageTextColor() { return colors.get("messageColorChat"); } | ||||
| 	public Color getTextColor() { return colors.get("textColor"); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the {@link Color} in which the creation date of a message should be | ||||
| 	 *         displayed | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public Color getDateColor() { return colors.get("dateColorChat"); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return selectionColor | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public Color getSelectionColor() { return colors.get("selectionColor"); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return typingMessageColor | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public Color getTypingMessageColor() { return colors.get("typingMessageColor"); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return backgroundColor | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public Color getBackgroundColor() { return colors.get("backgroundColor"); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return cellColor | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public Color getCellColor() { return colors.get("cellColor"); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return interactableBackgroundColor | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public Color getInteractableBackgroundColor() { return colors.get("interactableBackgroundColor"); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return userNameColor | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public Color getUserNameColor() { return colors.get("userNameColor"); } | ||||
|  | ||||
|   | ||||
| @@ -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<User>				userList				= new JList<>(); | ||||
| 	private DefaultListModel<User>	userListModel			= new DefaultListModel<>(); | ||||
| 	private ComponentList<Message>	messageList				= new ComponentList<>(new MessageListRenderer()); | ||||
| 	private ComponentList<Message>	messageList				= new ComponentList<>(); | ||||
| 	private PrimaryScrollPane		scrollPane				= new PrimaryScrollPane(); | ||||
| 	private JTextPane				textPane				= new JTextPane(); | ||||
| 	private PrimaryButton			postButton				= new PrimaryButton("Post"); | ||||
| 	private PrimaryButton			settingsButton			= new PrimaryButton("Settings"); | ||||
| 	private JPopupMenu				contextMenu; | ||||
| 
 | ||||
| 	// Contacts Header | ||||
| 	private JPanel			contactsHeader	= new JPanel(); | ||||
| @@ -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<User>	contactsModel				= new ComponentListModel<>(); | ||||
| 	private final ComponentList<User>		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<User>			contactsModel				= new Model<>(); | ||||
| 	private final ComponentList<User>	contactList					= new ComponentList<User>().setRenderer(ContactSearchComponent::new); | ||||
| 
 | ||||
| 	private static final Logger logger = EnvoyLog.getLogger(ChatWindow.class); | ||||
| 
 | ||||
| @@ -91,11 +103,12 @@ public class ChatWindow extends JFrame { | ||||
| 	 * Initializes a {@link JFrame} with UI elements used to send and read messages | ||||
| 	 * to different users. | ||||
| 	 * | ||||
| 	 * @since Envoy v0.1-alpha | ||||
| 	 * @since Envoy Client v0.1-alpha | ||||
| 	 */ | ||||
| 	public ChatWindow() { | ||||
| 		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | ||||
| 		setBounds(100, 100, 600, 800); | ||||
| 		setMinimumSize(new Dimension(400, 300)); | ||||
| 		setTitle("Envoy"); | ||||
| 		setLocationRelativeTo(null); | ||||
| 		setIconImage(Toolkit.getDefaultToolkit().createImage(getClass().getClassLoader().getResource("envoy_logo.png"))); | ||||
| @@ -106,19 +119,46 @@ public class ChatWindow extends JFrame { | ||||
| 		gbl_contentPane.columnWidths	= new int[] { 1, 1, 1 }; | ||||
| 		gbl_contentPane.rowHeights		= new int[] { 1, 1, 1, 1 }; | ||||
| 		gbl_contentPane.columnWeights	= new double[] { 0.03, 1.0, 0.1 }; | ||||
| 		gbl_contentPane.rowWeights		= new double[] { 0.03, 0.001, 1.0, 0.005 }; | ||||
| 		gbl_contentPane.rowWeights		= new double[] { 0.03, 0.001, 1.0, 0.001 }; | ||||
| 		contentPane.setLayout(gbl_contentPane); | ||||
| 
 | ||||
| 		messageList.setBorder(new EmptyBorder(space, space, space, space)); | ||||
| 		messageList.setSelectionMode(SelectionMode.SINGLE); | ||||
| 		messageList.setSelectionHandler((message, comp, isSelected) -> { | ||||
| 			final var theme = Settings.getInstance().getCurrentTheme(); | ||||
| 			comp.setBackground(isSelected ? theme.getSelectionColor() : theme.getCellColor()); | ||||
| 
 | ||||
| 			// ContextMenu | ||||
| 			Map<String, ActionListener> commands = Map.of("forward selected message", evt -> { | ||||
| 				final Message	selectedMessage	= messageList.getSingleSelectedElement(); | ||||
| 				List<User>		chosenContacts	= ContactsChooserDialog | ||||
| 					.showForwardingDialog("Forward selected message to", null, selectedMessage, localDb.getUsers().values()); | ||||
| 				if (chosenContacts != null && chosenContacts.size() > 0) forwardMessage(selectedMessage, chosenContacts.toArray(new User[0])); | ||||
| 			}, "copy", evt -> { | ||||
| 				// TODO should be enhanced to allow also copying of message attachments, | ||||
| 				// especially pictures | ||||
| 				StringSelection copy = new StringSelection(messageList.getSingleSelectedElement().getText()); | ||||
| 				Toolkit.getDefaultToolkit().getSystemClipboard().setContents(copy, copy); | ||||
| 				// TODO insert implementation to edit and delete messages | ||||
| 			}, "delete", evt -> {}, "edit", evt -> {}, "quote", evt -> {}); | ||||
| 
 | ||||
| 			if (isSelected) { | ||||
| 				contextMenu = new ContextMenu(null, comp, commands, null, null).build(); | ||||
| 				contextMenu.show(comp, 0, 0); | ||||
| 			} | ||||
| 		}); | ||||
| 
 | ||||
| 		scrollPane.setViewportView(messageList); | ||||
| 		scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); | ||||
| 		scrollPane.addComponentListener(new ComponentAdapter() { | ||||
| 
 | ||||
| 			// updates list elements when list is resized | ||||
| 			// Update list elements when scroll pane (and thus list) is resized | ||||
| 			@Override | ||||
| 			public void componentResized(ComponentEvent e) { messageList.synchronizeModel(); } | ||||
| 			public void componentResized(ComponentEvent e) { | ||||
| 				messageList.setMaximumSize(new Dimension(scrollPane.getWidth(), Integer.MAX_VALUE)); | ||||
| 				messageList.synchronizeModel(); | ||||
| 			} | ||||
| 		}); | ||||
| 		scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); | ||||
| 
 | ||||
| 		GridBagConstraints gbc_scrollPane = new GridBagConstraints(); | ||||
| 		gbc_scrollPane.fill			= GridBagConstraints.BOTH; | ||||
| @@ -135,7 +175,10 @@ public class ChatWindow extends JFrame { | ||||
| 		messageEnterTextArea.addInputMethodListener(new InputMethodListener() { | ||||
| 
 | ||||
| 			@Override | ||||
| 			public void inputMethodTextChanged(InputMethodEvent event) { checkMessageTextLength(); } | ||||
| 			public void inputMethodTextChanged(InputMethodEvent event) { | ||||
| 				checkMessageTextLength(); | ||||
| 				checkPostButton(messageEnterTextArea.getText()); | ||||
| 			} | ||||
| 
 | ||||
| 			@Override | ||||
| 			public void caretPositionChanged(InputMethodEvent event) {} | ||||
| @@ -146,32 +189,30 @@ public class ChatWindow extends JFrame { | ||||
| 			@Override | ||||
| 			public void keyReleased(KeyEvent e) { | ||||
| 				if (e.getKeyCode() == KeyEvent.VK_ENTER | ||||
| 						&& (Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0 || e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK)) | ||||
| 						&& (Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0 || e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK) | ||||
| 						&& postButton.isEnabled()) | ||||
| 					postMessage(); | ||||
| 				// Checking if text is too long | ||||
| 				checkMessageTextLength(); | ||||
| 				checkPostButton(messageEnterTextArea.getText()); | ||||
| 			} | ||||
| 		}); | ||||
| 
 | ||||
| 		GridBagConstraints gbc_scrollPaneForTextInput = new GridBagConstraints(); | ||||
| 		gbc_scrollPaneForTextInput.fill		= GridBagConstraints.BOTH; | ||||
| 		gbc_scrollPaneForTextInput.gridx	= 1; | ||||
| 		gbc_scrollPaneForTextInput.gridy	= 3; | ||||
| 
 | ||||
| 		gbc_scrollPaneForTextInput.insets = insets; | ||||
| 
 | ||||
| 		contentPane.add(messageEnterTextArea, gbc_scrollPaneForTextInput); | ||||
| 		GridBagConstraints gbc_messageEnterTextArea = new GridBagConstraints(); | ||||
| 		gbc_messageEnterTextArea.fill	= GridBagConstraints.BOTH; | ||||
| 		gbc_messageEnterTextArea.gridx	= 1; | ||||
| 		gbc_messageEnterTextArea.gridy	= 3; | ||||
| 		gbc_messageEnterTextArea.insets	= insets; | ||||
| 		contentPane.add(messageEnterTextArea, gbc_messageEnterTextArea); | ||||
| 
 | ||||
| 		// Post Button | ||||
| 		GridBagConstraints gbc_postButton = new GridBagConstraints(); | ||||
| 
 | ||||
| 		gbc_postButton.fill		= GridBagConstraints.BOTH; | ||||
| 		gbc_postButton.gridx	= 2; | ||||
| 		gbc_postButton.gridy	= 3; | ||||
| 
 | ||||
| 		gbc_postButton.insets = insets; | ||||
| 
 | ||||
| 		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<Message> messages, User... recipients) { | ||||
| 		messages.forEach(message -> { forwardMessage(message, recipients); }); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Sends a {@link Message} to the server. | ||||
| 	 * | ||||
| 	 * @param message the message to send | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	private void sendMessage(final Message message) { | ||||
| 		try { | ||||
| 			// Send message | ||||
| 			writeProxy.writeMessage(message); | ||||
| 
 | ||||
| 			// Add message to PersistentLocalDb and update UI | ||||
| 			currentChat.appendMessage(message); | ||||
| 
 | ||||
| 			// Clear text field | ||||
| 			messageEnterTextArea.setText(""); | ||||
| 
 | ||||
| 			// Update UI | ||||
| 			revalidate(); | ||||
| 			repaint(); | ||||
| @@ -525,7 +602,7 @@ public class ChatWindow extends JFrame { | ||||
| 	private void readCurrentChat() { | ||||
| 		try { | ||||
| 			currentChat.read(writeProxy); | ||||
| 			messageList.synchronizeModel(); | ||||
| 			if (messageList.getRenderer() != null) messageList.synchronizeModel(); | ||||
| 		} catch (IOException e) { | ||||
| 			e.printStackTrace(); | ||||
| 			logger.log(Level.WARNING, "Couldn't notify server about message status change", e); | ||||
| @@ -562,13 +639,15 @@ public class ChatWindow extends JFrame { | ||||
| 	 * @param writeProxy the write proxy used to send messages and status change | ||||
| 	 *                   events to the server or cache them inside the local | ||||
| 	 *                   database | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void initContent(Client client, LocalDb localDb, WriteProxy writeProxy) { | ||||
| 		this.client		= client; | ||||
| 		this.localDb	= localDb; | ||||
| 		this.writeProxy	= writeProxy; | ||||
| 
 | ||||
| 		messageList.setRenderer((list, message) -> new MessageComponent(list, message, client.getSender().getId())); | ||||
| 
 | ||||
| 		// Load users and chats | ||||
| 		new Thread(() -> { | ||||
| 			localDb.getUsers().values().forEach(user -> { | ||||
| @@ -589,7 +668,7 @@ public class ChatWindow extends JFrame { | ||||
| 	 * {@link ChatWindow#MAX_MESSAGE_LENGTH} | ||||
| 	 * and splits the text into the allowed part, if that is the case. | ||||
| 	 * | ||||
| 	 * @since Envoy v0.1-beta | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	private void checkMessageTextLength() { | ||||
| 		String input = messageEnterTextArea.getText(); | ||||
| @@ -603,4 +682,6 @@ public class ChatWindow extends JFrame { | ||||
| 					JOptionPane.WARNING_MESSAGE); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private void checkPostButton(String text) { postButton.setEnabled(!text.trim().isBlank()); } | ||||
| } | ||||
							
								
								
									
										148
									
								
								src/main/java/envoy/client/ui/container/ContactsChooserDialog.java
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										148
									
								
								src/main/java/envoy/client/ui/container/ContactsChooserDialog.java
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,148 @@ | ||||
| package envoy.client.ui.container; | ||||
|  | ||||
| import java.awt.BorderLayout; | ||||
| import java.awt.Component; | ||||
| import java.awt.event.ActionListener; | ||||
| import java.awt.event.KeyEvent; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collection; | ||||
| import java.util.List; | ||||
|  | ||||
| import javax.swing.JButton; | ||||
| import javax.swing.JDialog; | ||||
| import javax.swing.JPanel; | ||||
| import javax.swing.border.EmptyBorder; | ||||
|  | ||||
| import envoy.client.data.Settings; | ||||
| import envoy.client.ui.Theme; | ||||
| import envoy.client.ui.list.ComponentList; | ||||
| import envoy.client.ui.list.ComponentList.SelectionMode; | ||||
| import envoy.client.ui.list.Model; | ||||
| import envoy.client.ui.list_component.UserComponent; | ||||
| import envoy.data.Message; | ||||
| import envoy.data.User; | ||||
|  | ||||
| /** | ||||
|  * This class defines a dialog to choose contacts from.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>ContactsChooserDialog.java</strong><br> | ||||
|  * Created: <strong>15 Mar 2020</strong><br> | ||||
|  * | ||||
|  * @author Leon Hofmeister | ||||
|  * @since Envoy Client v0.1-beta | ||||
|  */ | ||||
| public class ContactsChooserDialog extends JDialog { | ||||
|  | ||||
| 	private static final long serialVersionUID = -5774558118579032256L; | ||||
|  | ||||
| 	private ComponentList<User>	contactList		= new ComponentList<User>().setModel(new Model<User>()) | ||||
| 		.setRenderer((list, user) -> new UserComponent(user)); | ||||
| 	private JButton				okButton		= new JButton("Ok"); | ||||
| 	private JButton				cancelButton	= new JButton("Cancel"); | ||||
|  | ||||
| 	private final Theme theme = Settings.getInstance().getCurrentTheme(); | ||||
|  | ||||
| 	private final JPanel contentPanel = new JPanel(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Shows a modal contacts-chooser dialog and blocks until the | ||||
| 	 * dialog is hidden. If the user presses the "OK" button, then | ||||
| 	 * this method hides/disposes the dialog and returns the selected element (has | ||||
| 	 * yet | ||||
| 	 * to be casted back to its original type due to the limitations of Generics in | ||||
| 	 * Java). | ||||
| 	 * If the user presses the "Cancel" button or closes the dialog without | ||||
| 	 * pressing "OK", then this method disposes the dialog and returns an empty | ||||
| 	 * <code>ArrayList</code>. | ||||
| 	 * | ||||
| 	 * @param title   the title of the dialog | ||||
| 	 * @param parent  this @{@link Component} will be parsed to | ||||
| 	 *                {@link java.awt.Window#setLocationRelativeTo(Component)} in | ||||
| 	 *                order to change the location of the dialog | ||||
| 	 * @param message the {@link Message} to display on top of the Dialog | ||||
| 	 * @param users   the users that should be displayed | ||||
| 	 * @return the selected Element (yet has to be casted to the wanted type due to | ||||
| 	 *         the Generics limitations in Java) | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public static List<User> showForwardingDialog(String title, Component parent, Message message, Collection<User> users) { | ||||
| 		ContactsChooserDialog dialog = new ContactsChooserDialog(parent); | ||||
| 		dialog.setTitle(title); | ||||
| 		dialog.setDefaultCloseOperation(DISPOSE_ON_CLOSE); | ||||
| 		dialog.addCancelButtonActionListener(e -> dialog.dispose()); | ||||
|  | ||||
| 		List<User> results = new ArrayList<>(); | ||||
| 		dialog.addOkButtonActionListener(e -> {  | ||||
| 			results.addAll(dialog.getContactList().getSelectedElements()); | ||||
| 			dialog.dispose(); | ||||
| 		}); | ||||
| 		Model<User> contactListModel = dialog.getContactList().getModel(); | ||||
| 		users.forEach(contactListModel::add); | ||||
|  | ||||
| 		dialog.setModalityType(ModalityType.APPLICATION_MODAL); | ||||
| 		dialog.setVisible(true); | ||||
|  | ||||
| 		return results; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param parent this @{@link Component} will be parsed to | ||||
| 	 *               {@link java.awt.Window#setLocationRelativeTo(Component)} | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	private ContactsChooserDialog(Component parent) { | ||||
| 		contactList.setSelectionMode(SelectionMode.MULTIPLE); | ||||
| 		contactList.setSelectionHandler((user, comp, isSelected) -> { | ||||
| 			final var theme = Settings.getInstance().getCurrentTheme(); | ||||
| 			comp.setBackground(isSelected ? theme.getSelectionColor() : theme.getCellColor()); | ||||
| 		}); | ||||
| 		setLocationRelativeTo(parent); | ||||
| 		getContentPane().setLayout(new BorderLayout()); | ||||
| 		setBackground(theme.getBackgroundColor()); | ||||
| 		setForeground(theme.getTextColor()); | ||||
| 		setSize(400, 400); | ||||
| 		contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); | ||||
| 		getContentPane().add(contentPanel, BorderLayout.CENTER); | ||||
| 		contentPanel.setLayout(new BorderLayout(0, 0)); | ||||
| 		contentPanel.add(contactList, BorderLayout.CENTER); | ||||
| 		{ | ||||
| 			JPanel buttonPane = new JPanel(); | ||||
| 			getContentPane().add(buttonPane, BorderLayout.SOUTH); | ||||
| 			{ | ||||
| 				okButton = new JButton("OK"); | ||||
| 				okButton.setMnemonic(KeyEvent.VK_ENTER); | ||||
| 				okButton.setActionCommand("OK"); | ||||
| 				buttonPane.setLayout(new BorderLayout(0, 0)); | ||||
| 				buttonPane.add(okButton, BorderLayout.EAST); | ||||
| 				getRootPane().setDefaultButton(okButton); | ||||
| 			} | ||||
| 			{ | ||||
| 				cancelButton = new JButton("Cancel"); | ||||
| 				cancelButton.setActionCommand("Cancel"); | ||||
| 				buttonPane.add(cancelButton, BorderLayout.WEST); | ||||
| 			} | ||||
| 		} | ||||
| 		applyTheme(Settings.getInstance().getCurrentTheme()); | ||||
| 	} | ||||
|  | ||||
| 	private void applyTheme(Theme theme) { | ||||
| 		contentPanel.setBackground(theme.getBackgroundColor()); | ||||
| 		contentPanel.setForeground(theme.getTextColor()); | ||||
| 		contactList.setBackground(theme.getCellColor()); | ||||
| 		okButton.setBackground(theme.getInteractableBackgroundColor()); | ||||
| 		okButton.setForeground(theme.getTextColor()); | ||||
| 		cancelButton.setBackground(theme.getInteractableBackgroundColor()); | ||||
| 		cancelButton.setForeground(theme.getTextColor()); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the underlying {@link ComponentList} | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	private ComponentList<User> getContactList() { return contactList; } | ||||
|  | ||||
| 	private void addOkButtonActionListener(ActionListener l) { okButton.addActionListener(l); } | ||||
|  | ||||
| 	private void addCancelButtonActionListener(ActionListener l) { cancelButton.addActionListener(l); } | ||||
| } | ||||
							
								
								
									
										255
									
								
								src/main/java/envoy/client/ui/container/ContextMenu.java
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										255
									
								
								src/main/java/envoy/client/ui/container/ContextMenu.java
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,255 @@ | ||||
| package envoy.client.ui.container; | ||||
|  | ||||
| import java.awt.Color; | ||||
| import java.awt.Component; | ||||
| import java.awt.event.ActionListener; | ||||
| import java.awt.event.MouseAdapter; | ||||
| import java.awt.event.MouseEvent; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
|  | ||||
| import javax.swing.*; | ||||
|  | ||||
| import envoy.client.data.Settings; | ||||
| import envoy.client.ui.Theme; | ||||
|  | ||||
| /** | ||||
|  * This class defines a menu that will be automatically called if | ||||
|  * {@link MouseEvent#isPopupTrigger()} returns true for the parent component. | ||||
|  * The user has the possibility to directly add actions to be performed when | ||||
|  * clicking on the element with the selected String. Additionally, for each | ||||
|  * element an {@link Icon} can be added, but it must not be. | ||||
|  * If the key(text) of an element starts with one of the predefined values, a | ||||
|  * special component will be called: either a {@link JRadioButtonMenuItem}, a | ||||
|  * {@link JCheckBoxMenuItem} or a {@link JMenu} will be created.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>ContextMenu.java</strong><br> | ||||
|  * Created: <strong>17 Mar 2020</strong><br> | ||||
|  * | ||||
|  * @author Leon Hofmeister | ||||
|  * @since Envoy Client v0.1-beta | ||||
|  */ | ||||
| public class ContextMenu extends JPopupMenu { | ||||
|  | ||||
| 	private static final long serialVersionUID = 2177146471226992104L; | ||||
|  | ||||
| 	/** | ||||
| 	 * If a key starts with this String, a {@link JCheckBoxMenuItem} will be created | ||||
| 	 */ | ||||
| 	public static final String	checkboxMenuItem	= "ChBoMI"; | ||||
| 	/** | ||||
| 	 * If a key starts with this String, a {@link JRadioButtonMenuItem} will be | ||||
| 	 * created | ||||
| 	 */ | ||||
| 	public static final String	radioButtonMenuItem	= "RaBuMI"; | ||||
| 	/** | ||||
| 	 * If a key starts with this String, a {@link JMenu} will be created | ||||
| 	 */ | ||||
| 	public static final String	subMenuItem			= "SubMI"; | ||||
|  | ||||
| 	private Map<String, ActionListener>	items		= new HashMap<>(); | ||||
| 	private Map<String, Icon>			icons		= new HashMap<>(); | ||||
| 	private Map<String, Integer>		mnemonics	= new HashMap<>(); | ||||
|  | ||||
| 	private ButtonGroup	radioButtonGroup	= new ButtonGroup(); | ||||
| 	private boolean		built				= false; | ||||
| 	private boolean		visible				= false; | ||||
|  | ||||
| 	/** | ||||
| 	 * @param parent the component which will call this | ||||
| 	 *               {@link ContextMenu} | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public ContextMenu(Component parent) { | ||||
| 		setInvoker(parent); | ||||
| 		setOpaque(true); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param label            the string that a UI may use to display as a title | ||||
| 	 *                         for the pop-up menu | ||||
| 	 * @param parent           the component which will call this | ||||
| 	 *                         {@link ContextMenu} | ||||
| 	 * @param itemsWithActions a map of all strings to be displayed with according | ||||
| 	 *                         actions | ||||
| 	 * @param itemIcons        the icons to be displayed before a name, if wanted. | ||||
| 	 *                         Only keys in here will have an Icon displayed. More | ||||
| 	 *                         precisely, all keys here not included in the first | ||||
| 	 *                         map will be thrown out. | ||||
| 	 * @param itemMnemonics    the keyboard shortcuts that need to be pressed to | ||||
| 	 *                         automatically execute the {@link JMenuItem} with the | ||||
| 	 *                         given text | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public ContextMenu(String label, Component parent, Map<String, ActionListener> itemsWithActions, Map<String, Icon> itemIcons, | ||||
| 			Map<String, Integer> itemMnemonics) { | ||||
| 		this(label); | ||||
| 		setInvoker(parent); | ||||
| 		this.items		= (itemsWithActions != null) ? itemsWithActions : items; | ||||
| 		this.icons		= (itemIcons != null) ? itemIcons : icons; | ||||
| 		this.mnemonics	= (itemMnemonics != null) ? itemMnemonics : mnemonics; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param label the string that a UI may use to display as a title for the | ||||
| 	 *              pop-up menu. | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public ContextMenu(String label) { | ||||
| 		super(label); | ||||
| 		setOpaque(true); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Prepares the PopupMenu to be displayed. Should only be used once all map | ||||
| 	 * values have been set. | ||||
| 	 * | ||||
| 	 * @return this instance of {@link ContextMenu} to allow chaining behind the | ||||
| 	 *         constructor | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public ContextMenu build() { | ||||
| 		items.forEach((text, action) -> { | ||||
| 			// case radio button wanted | ||||
| 			AbstractButton item; | ||||
| 			if (text.startsWith(radioButtonMenuItem)) { | ||||
| 				item = new JRadioButtonMenuItem(text.substring(radioButtonMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null); | ||||
| 				radioButtonGroup.add(item); | ||||
| 				// case check box wanted | ||||
| 			} else if (text.startsWith(checkboxMenuItem)) | ||||
| 				item = new JCheckBoxMenuItem(text.substring(checkboxMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null); | ||||
| 			// case sub-menu wanted | ||||
| 			else if (text.startsWith(subMenuItem)) item = new JMenu(text.substring(subMenuItem.length())); | ||||
| 			else // normal JMenuItem wanted | ||||
| 				item = new JMenuItem(text, icons.containsKey(text) ? icons.get(text) : null); | ||||
| 			item.addActionListener(action); | ||||
| 			item.setOpaque(true); | ||||
| 			if (mnemonics.containsKey(text)) item.setMnemonic(mnemonics.get(text)); | ||||
| 			add(item); | ||||
| 		}); | ||||
| 		getInvoker().addMouseListener(getShowingListener()); | ||||
| 		applyTheme(Settings.getInstance().getCurrentTheme()); | ||||
| 		built = true; | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	private MouseAdapter getShowingListener() { | ||||
| 		return new MouseAdapter() { | ||||
|  | ||||
| 			@Override | ||||
| 			public void mouseClicked(MouseEvent e) { action(e); } | ||||
|  | ||||
| 			@Override | ||||
| 			public void mousePressed(MouseEvent e) { action(e); } | ||||
|  | ||||
| 			@Override | ||||
| 			public void mouseReleased(MouseEvent e) { action(e); } | ||||
|  | ||||
| 			private void action(MouseEvent e) { | ||||
| 				if (!built) build(); | ||||
| 				if (e.isPopupTrigger()) { | ||||
| 					// hides the menu if already visible | ||||
| 					visible = !visible; | ||||
| 					if (visible) show(e.getComponent(), e.getX(), e.getY()); | ||||
| 					else setVisible(false); | ||||
|  | ||||
| 				} | ||||
| 			} | ||||
| 		}; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Removes all subcomponents of this menu. | ||||
| 	 * | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public void clear() { | ||||
| 		removeAll(); | ||||
| 		items		= new HashMap<>(); | ||||
| 		icons		= new HashMap<>(); | ||||
| 		mnemonics	= new HashMap<>(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the items | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public Map<String, ActionListener> getItems() { return items; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param items the items with the displayed text and the according action to | ||||
| 	 *              take once called | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public void setItems(Map<String, ActionListener> items) { this.items = items; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the icons | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public Map<String, Icon> getIcons() { return icons; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param icons the icons to set | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public void setIcons(Map<String, Icon> icons) { this.icons = icons; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the mnemonics (the keyboard shortcuts that automatically execute the | ||||
| 	 *         command for a {@link JMenuItem} with corresponding text) | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public Map<String, Integer> getMnemonics() { return mnemonics; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param mnemonics the keyboard shortcuts that need to be pressed to | ||||
| 	 *                  automatically execute the {@link JMenuItem} with the given | ||||
| 	 *                  text | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public void setMnemonics(Map<String, Integer> mnemonics) { this.mnemonics = mnemonics; } | ||||
|  | ||||
| 	/** | ||||
| 	 * {@inheritDoc}<br> | ||||
| 	 * Additionally sets the foreground of all subcomponents of this | ||||
| 	 * {@link ContextMenu}. | ||||
| 	 * | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	@Override | ||||
| 	public void setForeground(Color color) { | ||||
| 		super.setForeground(color); | ||||
| 		for (MenuElement element : getSubElements()) | ||||
| 			((Component) element).setForeground(color); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * {@inheritDoc}<br> | ||||
| 	 * Additionally sets the background of all subcomponents of this | ||||
| 	 * {@link ContextMenu}. | ||||
| 	 * | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	@Override | ||||
| 	public void setBackground(Color color) { | ||||
| 		super.setBackground(color); | ||||
| 		for (MenuElement element : getSubElements()) | ||||
| 			((Component) element).setBackground(color); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Sets the fore- and background of all elements contained in this | ||||
| 	 * {@link ContextMenu} | ||||
| 	 * This method is to be only used by Envoy as {@link Theme} is an | ||||
| 	 * Envoy-exclusive object. | ||||
| 	 * | ||||
| 	 * @param theme the theme to use | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	protected void applyTheme(Theme theme) { | ||||
| 		setBackground(theme.getCellColor()); | ||||
| 		setForeground(theme.getTextColor()); | ||||
| 	} | ||||
| } | ||||
| @@ -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<Message> receivedMessageCache) { | ||||
| 		this.client					= client; | ||||
| @@ -281,7 +283,7 @@ public class LoginDialog extends JDialog { | ||||
| 	/** | ||||
| 	 * Resets the text stored in the password fields. | ||||
| 	 * | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	private void clearPasswordFields() { | ||||
| 		passwordField.setText(null); | ||||
| @@ -289,7 +291,7 @@ public class LoginDialog extends JDialog { | ||||
| 	} | ||||
| 
 | ||||
| 	private void setTheme() { | ||||
| 		Theme theme = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()); | ||||
| 		Theme theme = Settings.getInstance().getCurrentTheme(); | ||||
| 
 | ||||
| 		// Panels | ||||
| 		contentPanel.setBackground(theme.getBackgroundColor()); | ||||
| @@ -335,7 +337,7 @@ public class LoginDialog extends JDialog { | ||||
| 	/** | ||||
| 	 * Shuts the system down properly if the login was aborted. | ||||
| 	 * | ||||
| 	 * @since Envoy v0.1-beta | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	private void abortLogin() { | ||||
| 		logger.info("The login process has been cancelled. Exiting..."); | ||||
							
								
								
									
										13
									
								
								src/main/java/envoy/client/ui/container/package-info.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/main/java/envoy/client/ui/container/package-info.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| /** | ||||
|  * This package contains all graphical Containers, like Dialogs and Frames.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>package-info.java</strong><br> | ||||
|  * Created: <strong>16 Mar 2020</strong><br> | ||||
|  * | ||||
|  * @author Leon Hofmeister | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @author Maximilian Käfer | ||||
|  * @since Envoy Client v0.1-beta | ||||
|  */ | ||||
| package envoy.client.ui.container; | ||||
| @@ -3,12 +3,14 @@ package envoy.client.ui.list; | ||||
| import java.awt.event.MouseAdapter; | ||||
| import java.awt.event.MouseEvent; | ||||
| import java.awt.event.MouseListener; | ||||
| import java.util.HashSet; | ||||
| import java.util.Set; | ||||
|  | ||||
| import javax.swing.*; | ||||
|  | ||||
| /** | ||||
|  * Provides a vertical list layout of components provided in a | ||||
|  * {@link ComponentListModel}. Similar to {@link javax.swing.JList} but capable | ||||
|  * {@link Model}. Similar to {@link javax.swing.JList} but capable | ||||
|  * of rendering {@link JPanel}s.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
| @@ -17,141 +19,90 @@ import javax.swing.*; | ||||
|  * | ||||
|  * @param <E> the type of object displayed in this list | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.3-alpha | ||||
|  * @since Envoy Client v0.3-alpha | ||||
|  */ | ||||
| public class ComponentList<E> extends JPanel { | ||||
|  | ||||
| 	private ComponentListModel<E>			model; | ||||
| 	private ComponentListCellRenderer<E>	renderer; | ||||
|  | ||||
| 	private int currentSelection = -1; | ||||
| 	private Model<E>			model; | ||||
| 	private Renderer<E>			renderer; | ||||
| 	private SelectionHandler<E>	selectionHandler; | ||||
| 	private SelectionMode		selectionMode	= SelectionMode.NONE; | ||||
| 	private Set<Integer>		selection		= new HashSet<>(); | ||||
|  | ||||
| 	private static final long serialVersionUID = 1759644503942876737L; | ||||
|  | ||||
| 	/** | ||||
| 	 * Creates an instance of {@link ComponentList}. | ||||
| 	 * Defines the possible modes of selection that can be performed by the user | ||||
| 	 * | ||||
| 	 * @param renderer the list cell renderer used to display elements provided by | ||||
| 	 *                 the {@link ComponentListModel} | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public ComponentList(ComponentListCellRenderer<E> renderer) { | ||||
| 		setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); | ||||
| 		this.renderer = renderer; | ||||
| 	public static enum SelectionMode { | ||||
| 		/** | ||||
| 		 * Selection is completely ignored. | ||||
| 		 */ | ||||
| 		NONE, | ||||
|  | ||||
| 		/** | ||||
| 		 * Only a single element can be selected. | ||||
| 		 */ | ||||
| 		SINGLE, | ||||
|  | ||||
| 		/** | ||||
| 		 * Multiple elements can be selected regardless of their position. | ||||
| 		 */ | ||||
| 		MULTIPLE | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Creates an instance of {@link ComponentList}. | ||||
| 	 * | ||||
| 	 * @param model    the list model providing the list elements to render | ||||
| 	 * @param renderer the list cell renderer used to display elements provided by | ||||
| 	 *                 the {@link ComponentListModel} | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public ComponentList(ComponentListModel<E> model, ComponentListCellRenderer<E> renderer) { | ||||
| 		this(renderer); | ||||
| 		this.model = model; | ||||
| 		setModel(model); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Sets the list model providing the list elements to render. The rendered | ||||
| 	 * components will be synchronized with the contents of the new model or removed | ||||
| 	 * if the new model is {@code null}. | ||||
| 	 * | ||||
| 	 * @param model the list model to set | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 */ | ||||
| 	public void setModel(ComponentListModel<E> model) { | ||||
|  | ||||
| 		// Remove old model | ||||
| 		if (this.model != null) this.model.setComponentList(null); | ||||
|  | ||||
| 		// Synchronize with new model | ||||
| 		this.model = model; | ||||
| 		if (model != null) this.model.setComponentList(this); | ||||
| 		synchronizeModel(); | ||||
| 	} | ||||
| 	public ComponentList() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); } | ||||
|  | ||||
| 	/** | ||||
| 	 * Removes all child components and then adds all components representing the | ||||
| 	 * elements of the {@link ComponentListModel}. | ||||
| 	 * elements of the {@link Model}. | ||||
| 	 * | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void synchronizeModel() { | ||||
| 		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.<br> | ||||
| 	 * <br> | ||||
| 	 * If the currently selected component gets selected again, the selection is | ||||
| 	 * removed. | ||||
| 	 * Selects a list element by index. If the element is already selected, it is | ||||
| 	 * removed from the selection. | ||||
| 	 * | ||||
| 	 * @param index the index of the selected component | ||||
| 	 * @since Envoy v0.1-beta | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	private void componentSelected(int index) { | ||||
| 		if (index == currentSelection) { | ||||
| 	public void selectElement(int index) { | ||||
| 		final JComponent element = getComponent(index); | ||||
| 		if (selection.contains(index)) { | ||||
|  | ||||
| 			// Deselect if clicked again | ||||
| 			if (selectionHandler != null) selectionHandler.selectionChanged(model.get(index), element, true); | ||||
| 			selection.remove(index); | ||||
|  | ||||
| 			// Clear selection | ||||
| 			update(currentSelection, false); | ||||
| 			currentSelection = -1; | ||||
| 		} else { | ||||
|  | ||||
| 			// Remove old selection | ||||
| 			if (currentSelection >= 0) update(currentSelection, false); | ||||
| 			// Remove old selection if single selection is enabled | ||||
| 			if (selectionMode == SelectionMode.SINGLE) clearSelection(); | ||||
|  | ||||
| 			// 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<E> extends JPanel { | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Replaces a list element with a newly rendered instance of its contents. | ||||
| 	 * Removes the current selection. | ||||
| 	 * | ||||
| 	 * @param index      the index of the element to update | ||||
| 	 * @param isSelected the selection state passed to the {@link ListCellRenderer} | ||||
| 	 * @since Envoy v0.1-beta | ||||
| 	 * @since Envoy Client v0.1-alpha | ||||
| 	 */ | ||||
| 	private void update(int index, boolean isSelected) { | ||||
| 		remove(index); | ||||
| 		add(model.get(index), index, isSelected); | ||||
| 	public void clearSelection() { | ||||
| 		if (selectionHandler != null) selection.forEach(i -> selectionHandler.selectionChanged(model.get(i), getComponent(i), false)); | ||||
| 		selection.clear(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Adds an object to the list by rendering it with the current | ||||
| 	 * {@link Renderer}. | ||||
| 	 * | ||||
| 	 * @param elem the element to add | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	void addElement(E elem) { | ||||
| 		if (renderer != null) { | ||||
| 			final JComponent component = renderer.getListCellComponent(this, elem); | ||||
| 			component.addMouseListener(getSelectionListener(getComponentCount())); | ||||
| 			add(component, getComponentCount()); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param componentIndex the index of the list component to which the mouse | ||||
| 	 *                       listener will be added | ||||
| 	 * @return a mouse listener calling the | ||||
| 	 *         {@link ComponentList#selectElement(int)} method with the | ||||
| 	 *         component's index when a left click is performed by the user | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	private MouseListener getSelectionListener(int componentIndex) { | ||||
| 		return new MouseAdapter() { | ||||
|  | ||||
| 			@Override | ||||
| 			public void mouseClicked(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) selectElement(componentIndex); } | ||||
| 		}; | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public JComponent getComponent(int n) { return (JComponent) super.getComponent(n); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return a set of all selected indices | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public Set<Integer> getSelection() { return selection; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return a set of all selected elements | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public Set<E> getSelectedElements() { | ||||
| 		var selectedElements = new HashSet<E>(); | ||||
| 		selection.forEach(i -> selectedElements.add(model.get(i))); | ||||
| 		return selectedElements; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the index of an arbitrary selected element | ||||
| 	 * @throws java.util.NoSuchElementException if no selection is present | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public int getSingleSelection() { return selection.stream().findAny().get(); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return an arbitrary selected element | ||||
| 	 * @throws java.util.NoSuchElementException if no selection is present | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public E getSingleSelectedElement() { return model.get(getSingleSelection()); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the model | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public Model<E> getModel() { return model; } | ||||
|  | ||||
| 	/** | ||||
| 	 * Sets the list model providing the list elements to render. The rendered | ||||
| 	 * components will be synchronized with the contents of the new model or removed | ||||
| 	 * if the new model is {@code null}. | ||||
| 	 * | ||||
| 	 * @param model the list model to set | ||||
| 	 * @return this component list | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public ComponentList<E> setModel(Model<E> model) { | ||||
|  | ||||
| 		// Remove old model | ||||
| 		if (this.model != null) this.model.setComponentList(null); | ||||
|  | ||||
| 		// Synchronize with new model | ||||
| 		this.model = model; | ||||
| 		if (model != null) model.setComponentList(this); | ||||
| 		synchronizeModel(); | ||||
|  | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the renderer | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public Renderer<E> getRenderer() { return renderer; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param renderer the renderer to set | ||||
| 	 * @return this component list | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public ComponentList<E> setRenderer(Renderer<E> renderer) { | ||||
| 		this.renderer = renderer; | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the selection mode | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public SelectionMode getSelectionMode() { return selectionMode; } | ||||
|  | ||||
| 	/** | ||||
| 	 * Sets a new selection mode. The current selection will be cleared during this | ||||
| 	 * action. | ||||
| 	 * | ||||
| 	 * @param selectionMode the selection mode to set | ||||
| 	 * @return this component list | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public ComponentList<E> setSelectionMode(SelectionMode selectionMode) { | ||||
| 		this.selectionMode = selectionMode; | ||||
| 		clearSelection(); | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the selection handler | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public SelectionHandler<E> getSelectionHandler() { return selectionHandler; } | ||||
|  | ||||
| 	/** | ||||
| 	 * @param selectionHandler the selection handler to set | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public void setSelectionHandler(SelectionHandler<E> selectionHandler) { this.selectionHandler = selectionHandler; } | ||||
| } | ||||
|   | ||||
| @@ -9,19 +9,19 @@ import java.util.List; | ||||
|  * Stores objects that will be displayed in a {@link ComponentList}.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>ComponentListModel.java</strong><br> | ||||
|  * File: <strong>Model.java</strong><br> | ||||
|  * Created: <strong>25.01.2020</strong><br> | ||||
|  * | ||||
|  * @param <E> the type of object displayed in this list | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.3-alpha | ||||
|  * @since Envoy Client v0.3-alpha | ||||
|  */ | ||||
| public final class ComponentListModel<E> implements Iterable<E>, Serializable { | ||||
| public final class Model<E> implements Iterable<E>, Serializable { | ||||
| 
 | ||||
| 	private List<E>						elements	= new ArrayList<>(); | ||||
| 	transient private ComponentList<E>	componentList; | ||||
| 
 | ||||
| 	private static final long serialVersionUID = 4815005915255497331L; | ||||
| 	private static final long serialVersionUID = 0L; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Adds an element to this model and notifies the associated | ||||
| @@ -30,11 +30,11 @@ public final class ComponentListModel<E> implements Iterable<E>, Serializable { | ||||
| 	 * @param e the element to add | ||||
| 	 * @return {@code true} | ||||
| 	 * @see java.util.List#add(java.lang.Object) | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public boolean add(E e) { | ||||
| 		if (componentList != null) { | ||||
| 			componentList.add(e); | ||||
| 			componentList.addElement(e); | ||||
| 			componentList.revalidate(); | ||||
| 		} | ||||
| 		return elements.add(e); | ||||
| @@ -45,7 +45,7 @@ public final class ComponentListModel<E> implements Iterable<E>, Serializable { | ||||
| 	 * {@link ComponentList}. | ||||
| 	 * | ||||
| 	 * @see java.util.List#clear() | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public void clear() { | ||||
| 		elements.clear(); | ||||
| @@ -56,7 +56,7 @@ public final class ComponentListModel<E> implements Iterable<E>, Serializable { | ||||
| 	 * @param index the index to retrieve the element from | ||||
| 	 * @return the element located at the index | ||||
| 	 * @see java.util.List#get(int) | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public E get(int index) { return elements.get(index); } | ||||
| 
 | ||||
| @@ -67,7 +67,7 @@ public final class ComponentListModel<E> implements Iterable<E>, Serializable { | ||||
| 	 * @param index the index of the element to remove | ||||
| 	 * @return the removed element | ||||
| 	 * @see java.util.List#remove(int) | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public E remove(int index) { | ||||
| 		if (componentList != null) componentList.remove(index); | ||||
| @@ -77,7 +77,7 @@ public final class ComponentListModel<E> implements Iterable<E>, Serializable { | ||||
| 	/** | ||||
| 	 * @return the amount of elements in this list model | ||||
| 	 * @see java.util.List#size() | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	public int size() { return elements.size(); } | ||||
| 
 | ||||
| @@ -90,7 +90,7 @@ public final class ComponentListModel<E> implements Iterable<E>, Serializable { | ||||
| 	/** | ||||
| 	 * @return an iterator over the elements of this list model | ||||
| 	 * @see java.util.List#iterator() | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	@Override | ||||
| 	public Iterator<E> iterator() { | ||||
| @@ -111,10 +111,10 @@ public final class ComponentListModel<E> implements Iterable<E>, Serializable { | ||||
| 	 * synchronization. | ||||
| 	 * | ||||
| 	 * @param componentList the component list to set | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	void setComponentList(ComponentList<E> componentList) { | ||||
| 		this.componentList = componentList; | ||||
| 		if (componentList != null) componentList.synchronizeModel(); | ||||
| 		if (componentList != null && componentList.getRenderer() != null) componentList.synchronizeModel(); | ||||
| 	} | ||||
| } | ||||
| @@ -7,14 +7,15 @@ import javax.swing.JComponent; | ||||
|  * that can be rendered.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>ComponentListCellRenderer.java</strong><br> | ||||
|  * File: <strong>Renderer.java</strong><br> | ||||
|  * Created: <strong>25.01.2020</strong><br> | ||||
|  * | ||||
|  * @param <E> the type of object displayed in this list | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.3-alpha | ||||
|  * @since Envoy Client v0.3-alpha | ||||
|  */ | ||||
| public interface ComponentListCellRenderer<E> { | ||||
| @FunctionalInterface | ||||
| public interface Renderer<E> { | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Provides a Swing component representing a list element. | ||||
| @@ -24,7 +25,7 @@ public interface ComponentListCellRenderer<E> { | ||||
| 	 * @param isSelected {@code true} if the user has selected the list cell in | ||||
| 	 *                   which the list element is rendered | ||||
| 	 * @return the component representing the list element | ||||
| 	 * @since Envoy v0.3-alpha | ||||
| 	 * @since Envoy Client v0.3-alpha | ||||
| 	 */ | ||||
| 	JComponent getListCellComponent(ComponentList<? extends E> list, E value, boolean isSelected); | ||||
| 	JComponent getListCellComponent(ComponentList<? extends E> list, E value); | ||||
| } | ||||
							
								
								
									
										28
									
								
								src/main/java/envoy/client/ui/list/SelectionHandler.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/main/java/envoy/client/ui/list/SelectionHandler.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| package envoy.client.ui.list; | ||||
|  | ||||
| import javax.swing.JComponent; | ||||
|  | ||||
| /** | ||||
|  * Handles the selection of elements in a {@link ComponentList}.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong> | ||||
|  * File: <strong>SelectionHandler.java</strong> | ||||
|  * Created: <strong>21.03.2020</strong> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @param <E> the type of the underlying {@link ComponentList} | ||||
|  * @since Envoy Client v0.1-beta | ||||
|  */ | ||||
| @FunctionalInterface | ||||
| public interface SelectionHandler<E> { | ||||
|  | ||||
| 	/** | ||||
| 	 * Notifies the handler about a selection. | ||||
| 	 * | ||||
| 	 * @param element    the selected element | ||||
| 	 * @param component  the selected component | ||||
| 	 * @param isSelected contains the selection state | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	void selectionChanged(E element, JComponent component, boolean isSelected); | ||||
| } | ||||
| @@ -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; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package envoy.client.ui; | ||||
| package envoy.client.ui.list_component; | ||||
| 
 | ||||
| import java.awt.Component; | ||||
| import java.awt.Dimension; | ||||
| @@ -9,42 +9,40 @@ import javax.swing.*; | ||||
| import envoy.client.data.Settings; | ||||
| import envoy.client.event.SendEvent; | ||||
| import envoy.client.ui.list.ComponentList; | ||||
| import envoy.client.ui.list.ComponentListCellRenderer; | ||||
| import envoy.client.ui.primary.PrimaryButton; | ||||
| import envoy.data.User; | ||||
| import envoy.event.ContactOperationEvent; | ||||
| import envoy.event.EventBus; | ||||
| 
 | ||||
| /** | ||||
|  * Defines how a contact is displayed.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>ContactsSearchRenderer.java</strong><br> | ||||
|  * Created: <strong>08.02.2020</strong><br> | ||||
|  * Project: <strong>envoy-client</strong> | ||||
|  * File: <strong>ContactSearchComponent.java</strong> | ||||
|  * Created: <strong>21.03.2020</strong> | ||||
|  * | ||||
|  * @author Maximilian Käfer | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.3-alpha | ||||
|  * @since Envoy Client v0.1-beta | ||||
|  */ | ||||
| public class ContactsSearchRenderer implements ComponentListCellRenderer<User> { | ||||
| public class ContactSearchComponent extends JComponent { | ||||
| 
 | ||||
| 	@Override | ||||
| 	public JComponent getListCellComponent(ComponentList<? extends User> list, User user, boolean isSelected) { | ||||
| 		final JPanel panel = new JPanel(); | ||||
| 		panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); | ||||
| 		if (isSelected) { | ||||
| 			panel.setBackground(Color.DARK_GRAY); | ||||
| 			panel.setForeground(Color.RED); | ||||
| 		} else { | ||||
| 			panel.setBackground(list.getBackground()); | ||||
| 			panel.setForeground(list.getForeground()); | ||||
| 		} | ||||
| 	private static final long serialVersionUID = 3166795412575239455L; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * @param list the {@link ComponentList} that is used to display search results | ||||
| 	 * @param user the {@link User} that appears as a search result | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public ContactSearchComponent(ComponentList<? extends User> list, User user) { | ||||
| 		setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); | ||||
| 
 | ||||
| 		setBackground(list.getBackground()); | ||||
| 		setForeground(list.getForeground()); | ||||
| 
 | ||||
| 		JLabel display = new JLabel(user.getName()); | ||||
| 		display.setForeground(Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getMessageTextColor()); | ||||
| 		display.setForeground(Settings.getInstance().getCurrentTheme().getTextColor()); | ||||
| 		display.setAlignmentX(Component.LEFT_ALIGNMENT); | ||||
| 		display.setAlignmentY(Component.CENTER_ALIGNMENT); | ||||
| 		display.setFont(new Font("Arial", Font.PLAIN, 16)); | ||||
| 		panel.add(display); | ||||
| 		add(display); | ||||
| 
 | ||||
| 		PrimaryButton add = new PrimaryButton("+"); | ||||
| 		add.setFont(new Font("Arial", Font.PLAIN, 19)); | ||||
| @@ -61,17 +59,15 @@ public class ContactsSearchRenderer implements ComponentListCellRenderer<User> { | ||||
| 			EventBus.getInstance().dispatch(new SendEvent(contactsOperationEvent)); | ||||
| 		}); | ||||
| 
 | ||||
| 		panel.add(add); | ||||
| 		add(add); | ||||
| 
 | ||||
| 		// Define some space to the messages below | ||||
| 		panel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 0, 15, 0), BorderFactory.createEtchedBorder())); | ||||
| 		setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 0, 15, 0), BorderFactory.createEtchedBorder())); | ||||
| 
 | ||||
| 		// Define a maximum height of 50px | ||||
| 		Dimension size = new Dimension(435, 50); | ||||
| 		panel.setMaximumSize(size); | ||||
| 		panel.setMinimumSize(size); | ||||
| 		panel.setPreferredSize(size); | ||||
| 
 | ||||
| 		return panel; | ||||
| 		setMaximumSize(size); | ||||
| 		setMinimumSize(size); | ||||
| 		setPreferredSize(size); | ||||
| 	} | ||||
| } | ||||
| } | ||||
| @@ -0,0 +1,127 @@ | ||||
| package envoy.client.ui.list_component; | ||||
|  | ||||
| import java.awt.*; | ||||
| import java.io.IOException; | ||||
| import java.text.SimpleDateFormat; | ||||
| import java.util.EnumMap; | ||||
|  | ||||
| import javax.swing.*; | ||||
|  | ||||
| import envoy.client.data.Chat; | ||||
| import envoy.client.data.Settings; | ||||
| import envoy.client.ui.Color; | ||||
| import envoy.client.ui.IconUtil; | ||||
| import envoy.client.ui.list.ComponentList; | ||||
| import envoy.data.Message; | ||||
| import envoy.data.Message.MessageStatus; | ||||
| import envoy.data.User; | ||||
|  | ||||
| /** | ||||
|  * Project: <strong>envoy-client</strong> | ||||
|  * File: <strong>MessageComponent.java</strong> | ||||
|  * Created: <strong>21.03.2020</strong> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy Client v0.1-beta | ||||
|  */ | ||||
| public class MessageComponent extends JPanel { | ||||
|  | ||||
| 	private static final long serialVersionUID = 103920706139926996L; | ||||
|  | ||||
| 	private static EnumMap<MessageStatus, ImageIcon>	statusIcons; | ||||
| 	private static ImageIcon							forwardIcon; | ||||
|  | ||||
| 	static { | ||||
| 		try { | ||||
| 			statusIcons	= IconUtil.loadByEnum(MessageStatus.class, 16); | ||||
| 			forwardIcon	= IconUtil.load("/icons/forward.png", 16); | ||||
| 		} catch (IOException e) { | ||||
| 			e.printStackTrace(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param list     the {@link ComponentList} that displays this {@link Chat} | ||||
| 	 * @param message  the {@link Message} to display | ||||
| 	 * @param senderId the id of the {@link User} who sends messages from this | ||||
| 	 *                 account | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public MessageComponent(ComponentList<? extends Message> list, Message message, long senderId) { | ||||
| 		var			width	= list.getMaximumSize().width; | ||||
| 		final var	theme	= Settings.getInstance().getCurrentTheme(); | ||||
| 		final int	padding	= (int) (width * 0.35); | ||||
|  | ||||
| 		GridBagLayout gbl_panel = new GridBagLayout(); | ||||
| 		gbl_panel.columnWidths	= new int[] { 1, 1 }; | ||||
| 		gbl_panel.rowHeights	= new int[] { 1, 1 }; | ||||
| 		gbl_panel.columnWeights	= new double[] { 1, 1 }; | ||||
| 		gbl_panel.rowWeights	= new double[] { 1, 1 }; | ||||
|  | ||||
| 		setLayout(gbl_panel); | ||||
| 		setBackground(theme.getCellColor()); | ||||
|  | ||||
| 		// Date Label - The Label that displays the creation date of a message | ||||
| 		var dateLabel = new JLabel(new SimpleDateFormat("dd.MM.yyyy HH:mm").format(message.getCreationDate())); | ||||
| 		dateLabel.setForeground(theme.getDateColor()); | ||||
| 		dateLabel.setAlignmentX(1f); | ||||
| 		dateLabel.setFont(new Font("Arial", Font.PLAIN, 12)); | ||||
| 		dateLabel.setPreferredSize(dateLabel.getPreferredSize()); | ||||
|  | ||||
| 		var gbc_dateLabel = new GridBagConstraints(); | ||||
| 		gbc_dateLabel.fill	= GridBagConstraints.BOTH; | ||||
| 		gbc_dateLabel.gridx	= 0; | ||||
| 		gbc_dateLabel.gridy	= 0; | ||||
| 		add(dateLabel, gbc_dateLabel); | ||||
|  | ||||
| 		// Message area - The JTextArea that displays the text content of a message. | ||||
| 		var messageTextArea = new JTextArea(message.getText()); | ||||
| 		messageTextArea.setLineWrap(true); | ||||
| 		messageTextArea.setWrapStyleWord(true); | ||||
| 		messageTextArea.setForeground(theme.getTextColor()); | ||||
| 		messageTextArea.setAlignmentX(0.5f); | ||||
| 		messageTextArea.setBackground(theme.getCellColor()); | ||||
| 		messageTextArea.setEditable(false); | ||||
| 		var font = new Font("Arial", Font.PLAIN, 14); | ||||
| 		messageTextArea.setFont(font); | ||||
| 		messageTextArea.setSize(width - padding - 16, 10); | ||||
|  | ||||
| 		var gbc_messageTextArea = new GridBagConstraints(); | ||||
| 		gbc_messageTextArea.fill	= GridBagConstraints.HORIZONTAL; | ||||
| 		gbc_messageTextArea.gridx	= 0; | ||||
| 		gbc_messageTextArea.gridy	= 1; | ||||
| 		add(messageTextArea, gbc_messageTextArea); | ||||
|  | ||||
| 		// Status Label - displays the status of the message | ||||
| 		var statusLabel = new JLabel(statusIcons.get(message.getStatus())); | ||||
|  | ||||
| 		var gbc_statusLabel = new GridBagConstraints(); | ||||
| 		gbc_statusLabel.gridx	= 1; | ||||
| 		gbc_statusLabel.gridy	= 1; | ||||
| 		add(statusLabel, gbc_statusLabel); | ||||
|  | ||||
| 		// Forwarding | ||||
| 		if (message.isForwarded()) { | ||||
| 			var forwardLabel = new JLabel("Forwarded", forwardIcon, SwingConstants.CENTER); | ||||
| 			forwardLabel.setBackground(getBackground()); | ||||
| 			forwardLabel.setForeground(Color.lightGray); | ||||
|  | ||||
| 			var gbc_forwardLabel = new GridBagConstraints(); | ||||
| 			gbc_forwardLabel.fill	= GridBagConstraints.BOTH; | ||||
| 			gbc_forwardLabel.gridx	= 1; | ||||
| 			gbc_forwardLabel.gridy	= 0; | ||||
| 			add(forwardLabel, gbc_forwardLabel); | ||||
| 		} | ||||
|  | ||||
| 		// Define an etched border and some space to the messages below | ||||
| 		var ours = senderId == message.getSenderId(); | ||||
| 		setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, ours ? padding : 10, 10, ours ? 0 : padding), | ||||
| 				BorderFactory.createEtchedBorder())); | ||||
|  | ||||
| 		var size = new Dimension(width - 50, getPreferredSize().height); | ||||
|  | ||||
| 		setPreferredSize(size); | ||||
| 		setMinimumSize(size); | ||||
| 		setMaximumSize(size); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,69 @@ | ||||
| package envoy.client.ui.list_component; | ||||
|  | ||||
| import java.awt.BorderLayout; | ||||
| import java.awt.Dimension; | ||||
|  | ||||
| import javax.swing.JLabel; | ||||
| import javax.swing.JPanel; | ||||
|  | ||||
| import envoy.client.data.Settings; | ||||
| import envoy.client.ui.Color; | ||||
| import envoy.client.ui.Theme; | ||||
| import envoy.data.User; | ||||
| import envoy.data.User.UserStatus; | ||||
|  | ||||
| /** | ||||
|  * Displays a {@link User}.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong> | ||||
|  * File: <strong>UserComponent.java</strong> | ||||
|  * Created: <strong>21.03.2020</strong> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy Client v0.1-beta | ||||
|  */ | ||||
| public class UserComponent extends JPanel { | ||||
|  | ||||
| 	private static final long serialVersionUID = 8450602172939729585L; | ||||
|  | ||||
| 	/** | ||||
| 	 * @param user the {@link User} whose information is displayed | ||||
| 	 * @since Envoy Client v0.1-beta | ||||
| 	 */ | ||||
| 	public UserComponent(User user) { | ||||
| 		final Theme theme = Settings.getInstance().getCurrentTheme(); | ||||
|  | ||||
| 		setLayout(new BorderLayout()); | ||||
|  | ||||
| 		// Panel background | ||||
| 		setBackground(theme.getCellColor()); | ||||
| 		setOpaque(true); | ||||
| 		setPreferredSize(new Dimension(100, 35)); | ||||
|  | ||||
| 		// TODO add profile picture support in BorderLayout.West | ||||
|  | ||||
| 		JLabel username = new JLabel(user.getName()); | ||||
| 		username.setForeground(theme.getUserNameColor()); | ||||
| 		add(username, BorderLayout.CENTER); | ||||
|  | ||||
| 		final UserStatus	status		= user.getStatus(); | ||||
| 		JLabel				statusLabel	= new JLabel(status.toString()); | ||||
| 		Color				foreground; | ||||
| 		switch (status) { | ||||
| 			case AWAY: | ||||
| 				foreground = Color.yellow; | ||||
| 				break; | ||||
| 			case BUSY: | ||||
| 				foreground = Color.blue; | ||||
| 				break; | ||||
| 			case ONLINE: | ||||
| 				foreground = Color.green; | ||||
| 				break; | ||||
| 			default: | ||||
| 				foreground = Color.lightGray; | ||||
| 				break; | ||||
| 		} | ||||
| 		statusLabel.setForeground(foreground); | ||||
| 		add(statusLabel, BorderLayout.NORTH); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,14 @@ | ||||
| /** | ||||
|  * This package contains swing components that can be displayed by | ||||
|  * {@link envoy.client.ui.list.ComponentList}.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>package-info.java</strong><br> | ||||
|  * Created: <strong>21 Mar 2020</strong><br> | ||||
|  * | ||||
|  * @author Leon Hofmeister | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @author Maximilian Käfer | ||||
|  * @since Envoy Client v0.1-beta | ||||
|  */ | ||||
| package envoy.client.ui.list_component; | ||||
| @@ -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; | ||||
|   | ||||
| @@ -1,63 +1,63 @@ | ||||
| package envoy.client.ui; | ||||
| 
 | ||||
| import java.awt.Graphics; | ||||
| 
 | ||||
| import javax.swing.JButton; | ||||
| 
 | ||||
| /** | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>PrimaryButton.javaEvent.java</strong><br> | ||||
|  * Created: <strong>07.12.2019</strong><br> | ||||
|  * | ||||
|  * @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; } | ||||
| } | ||||
| package envoy.client.ui.primary; | ||||
| 
 | ||||
| import java.awt.Graphics; | ||||
| 
 | ||||
| import javax.swing.JButton; | ||||
| 
 | ||||
| /** | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>PrimaryButton.javaEvent.java</strong><br> | ||||
|  * Created: <strong>07.12.2019</strong><br> | ||||
|  * | ||||
|  * @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; } | ||||
| } | ||||
| @@ -1,11 +1,6 @@ | ||||
| package envoy.client.ui; | ||||
| package envoy.client.ui.primary; | ||||
| 
 | ||||
| import java.awt.Color; | ||||
| import java.awt.Dimension; | ||||
| import java.awt.Graphics; | ||||
| import java.awt.Graphics2D; | ||||
| import java.awt.Rectangle; | ||||
| import java.awt.RenderingHints; | ||||
| import java.awt.*; | ||||
| 
 | ||||
| import javax.swing.JButton; | ||||
| import javax.swing.JComponent; | ||||
| @@ -13,6 +8,7 @@ import javax.swing.JScrollBar; | ||||
| import javax.swing.plaf.basic.BasicScrollBarUI; | ||||
| 
 | ||||
| import envoy.client.data.Settings; | ||||
| import envoy.client.ui.Theme; | ||||
| 
 | ||||
| /** | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
| @@ -20,7 +16,7 @@ import envoy.client.data.Settings; | ||||
|  * Created: <strong>14.12.2019</strong><br> | ||||
|  * | ||||
|  * @author Maximilian Kä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(); | ||||
| @@ -1,7 +1,9 @@ | ||||
| package envoy.client.ui; | ||||
| package envoy.client.ui.primary; | ||||
| 
 | ||||
| import javax.swing.JScrollPane; | ||||
| 
 | ||||
| import envoy.client.ui.Theme; | ||||
| 
 | ||||
| /** | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>PrimaryScrollPane.java</strong><br> | ||||
| @@ -20,7 +22,7 @@ public class PrimaryScrollPane extends JScrollPane { | ||||
| 	/** | ||||
| 	 * Initializes a {@link JScrollPane} with the primary Envoy design scheme | ||||
| 	 * | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public PrimaryScrollPane() { setBorder(null); } | ||||
| 
 | ||||
| @@ -28,7 +30,7 @@ public class PrimaryScrollPane extends JScrollPane { | ||||
| 	 * Styles the vertical and horizontal scroll bars. | ||||
| 	 * | ||||
| 	 * @param theme the color set used to color the component | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public void applyTheme(Theme theme) { | ||||
| 		setForeground(theme.getBackgroundColor()); | ||||
| @@ -52,7 +54,7 @@ public class PrimaryScrollPane extends JScrollPane { | ||||
| 	 * When rereading messages, the chat doesn't scroll down if new messages </br> | ||||
| 	 * are added. (Besides see first point) | ||||
| 	 * | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public void autoscroll() { | ||||
| 		// Automatic scrolling to the bottom | ||||
| @@ -77,7 +79,7 @@ public class PrimaryScrollPane extends JScrollPane { | ||||
| 	 * triggering it to automatically scroll down. | ||||
| 	 * | ||||
| 	 * @param chatOpened indicates the chat opening status | ||||
| 	 * @since Envoy v0.2-alpha | ||||
| 	 * @since Envoy Client v0.2-alpha | ||||
| 	 */ | ||||
| 	public void setChatOpened(boolean chatOpened) { this.chatOpened = chatOpened; } | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| package envoy.client.ui; | ||||
| package envoy.client.ui.primary; | ||||
| 
 | ||||
| import java.awt.Font; | ||||
| import java.awt.Graphics; | ||||
| @@ -12,7 +12,7 @@ import javax.swing.border.EmptyBorder; | ||||
|  * Created: <strong>07.12.2019</strong><br> | ||||
|  *  | ||||
|  * @author Maximilian Käfer | ||||
|  * @since Envoy v0.2-alpha | ||||
|  * @since Envoy Client v0.2-alpha | ||||
|  */ | ||||
| public class PrimaryTextArea extends JTextArea { | ||||
| 
 | ||||
| @@ -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}.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>PrimaryToggleSwitch.java</strong><br> | ||||
|  * Created: <strong>21 Dec 2019</strong><br> | ||||
|  *  | ||||
|  * @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<Boolean> 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); | ||||
| 	} | ||||
| } | ||||
| 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}.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>PrimaryToggleSwitch.java</strong><br> | ||||
|  * Created: <strong>21 Dec 2019</strong><br> | ||||
|  * | ||||
|  * @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<Boolean> 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); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										17
									
								
								src/main/java/envoy/client/ui/primary/package-info.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/main/java/envoy/client/ui/primary/package-info.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| /** | ||||
|  * This package defines all "primary" components that were defined specifically | ||||
|  * for the visual improvement of Envoy. However, they can still be used in | ||||
|  * general for other projects.<br> | ||||
|  * Primary elements are supposed to provide the main functionality of a UI | ||||
|  * component.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>package-info.java</strong><br> | ||||
|  * Created: <strong>14 Mar 2020</strong><br> | ||||
|  * | ||||
|  * @author Leon Hofmeister | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @author Maximilian Käfer | ||||
|  * @since Envoy Client v0.1-beta | ||||
|  */ | ||||
| package envoy.client.ui.primary; | ||||
| @@ -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<User> { | ||||
| 
 | ||||
| @@ -46,7 +46,7 @@ public class UserListRenderer extends JLabel implements ListCellRenderer<User> { | ||||
| 
 | ||||
| 		// Getting the UserNameColor of the current theme | ||||
| 		String textColor = null; | ||||
| 		textColor = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getUserNameColor().toHex(); | ||||
| 		textColor = Settings.getInstance().getCurrentTheme().getUserNameColor().toHex(); | ||||
| 		switch (status) { | ||||
| 			case ONLINE: | ||||
| 				setText(String | ||||
| @@ -59,4 +59,4 @@ public class UserListRenderer extends JLabel implements ListCellRenderer<User> { | ||||
| 		} | ||||
| 		return this; | ||||
| 	} | ||||
| } | ||||
| } | ||||
							
								
								
									
										14
									
								
								src/main/java/envoy/client/ui/renderer/package-info.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/main/java/envoy/client/ui/renderer/package-info.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| /** | ||||
|  * This package contains all Envoy-specific renderers for lists that store an | ||||
|  * arbitrary number of JComponents.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>package-info.java</strong><br> | ||||
|  * Created: <strong>14 Mar 2020</strong><br> | ||||
|  * | ||||
|  * @author Leon Hofmeister | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @author Maximilian Käfer | ||||
|  * @since Envoy Client v0.1-beta | ||||
|  */ | ||||
| package envoy.client.ui.renderer; | ||||
| @@ -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.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>GeneralSettingsPanel.java</strong><br> | ||||
|  * Created: <strong>21 Dec 2019</strong><br> | ||||
|  * | ||||
|  * @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 -> {}; } | ||||
| } | ||||
| 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.<br> | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>GeneralSettingsPanel.java</strong><br> | ||||
|  * Created: <strong>21 Dec 2019</strong><br> | ||||
|  * | ||||
|  * @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); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -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}. | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>NewThemeScreen.java</strong><br> | ||||
|  * Created: <strong>26 Dec 2019</strong><br> | ||||
|  * | ||||
|  * @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<String> newThemeAction, modifyThemeAction; | ||||
|  | ||||
| 	private static final long serialVersionUID = 2369985550946300976L; | ||||
|  | ||||
| 	/** | ||||
| 	 * Creates a window, where you can choose a name for a new {@link Theme}. <br> | ||||
| 	 * 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<String> newThemeAction, Consumer<String> 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(); }); | ||||
| 	} | ||||
| } | ||||
| 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}. | ||||
|  * <br> | ||||
|  * Project: <strong>envoy-client</strong><br> | ||||
|  * File: <strong>NewThemeScreen.java</strong><br> | ||||
|  * Created: <strong>26 Dec 2019</strong><br> | ||||
|  * | ||||
|  * @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<String> newThemeAction, modifyThemeAction; | ||||
|  | ||||
| 	private static final long serialVersionUID = 2369985550946300976L; | ||||
|  | ||||
| 	/** | ||||
| 	 * Creates a window, where you can choose a name for a new {@link Theme}. <br> | ||||
| 	 * 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<String> newThemeAction, Consumer<String> 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(); }); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,7 +1,5 @@ | ||||
| package envoy.client.ui.settings; | ||||
|  | ||||
| import java.awt.event.ActionListener; | ||||
|  | ||||
| import javax.swing.JPanel; | ||||
|  | ||||
| /** | ||||
| @@ -14,7 +12,7 @@ import javax.swing.JPanel; | ||||
|  * Created: <strong>20 Dec 2019</strong><br> | ||||
|  * | ||||
|  * @author Kai S. K. Engelbart | ||||
|  * @since Envoy v0.2-alpha | ||||
|  * @since Envoy Client v0.2-alpha | ||||
|  */ | ||||
| public abstract class SettingsPanel extends JPanel { | ||||
|  | ||||
| @@ -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(); | ||||
| } | ||||
|   | ||||
| @@ -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()); | ||||
| 	} | ||||
| } | ||||
| } | ||||
|   | ||||
							
								
								
									
										119
									
								
								src/main/java/envoy/client/ui/settings/ThemeCustomizationPanel.java
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										119
									
								
								src/main/java/envoy/client/ui/settings/ThemeCustomizationPanel.java
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,9 +1,6 @@ | ||||
| package envoy.client.ui.settings; | ||||
|  | ||||
| import java.awt.*; | ||||
| import java.awt.event.ActionListener; | ||||
| import java.awt.event.ItemEvent; | ||||
| import java.awt.event.ItemListener; | ||||
| import java.util.logging.Level; | ||||
| import java.util.logging.Logger; | ||||
|  | ||||
| @@ -13,6 +10,7 @@ import envoy.client.data.Settings; | ||||
| import envoy.client.event.ThemeChangeEvent; | ||||
| import envoy.client.ui.Color; | ||||
| import envoy.client.ui.Theme; | ||||
| import envoy.client.ui.primary.PrimaryButton; | ||||
| import envoy.event.EventBus; | ||||
| import envoy.util.EnvoyLog; | ||||
|  | ||||
| @@ -26,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<String>	themesModel	= new DefaultComboBoxModel<>( | ||||
| 			Settings.getInstance().getThemes().keySet().toArray(new String[0])); | ||||
| 	private JComboBox<String>				themes		= new JComboBox<>(themesModel); | ||||
| 	private DefaultComboBoxModel<String>	themesModel; | ||||
| 	private JComboBox<String>				themes; | ||||
| 	private Theme							temporaryTheme; | ||||
| 	private boolean							themeChanged; | ||||
| 	private PrimaryButton					createThemeButton	= new PrimaryButton("Create Theme"); | ||||
|  | ||||
| 	private 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); | ||||
| 	} | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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 { | ||||
|  | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/icons/forward.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/icons/forward.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 25 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/icons/messagestatus/read.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/icons/messagestatus/read.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 29 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/icons/messagestatus/received.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/icons/messagestatus/received.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 29 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/icons/messagestatus/sent.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/icons/messagestatus/sent.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 26 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/icons/messagestatus/waiting.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/icons/messagestatus/waiting.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 26 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/icons/settings.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/icons/settings.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 28 KiB | 
		Reference in New Issue
	
	Block a user
	 delvh
					delvh