Merge pull request #93 from informatik-ag-ngl/f/component_list
Component List
This commit is contained in:
commit
bd61936fde
@ -28,11 +28,5 @@
|
|||||||
<attribute name="maven.pomderived" value="true"/>
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
|
|
||||||
<attributes>
|
|
||||||
<attribute name="maven.pomderived" value="true"/>
|
|
||||||
<attribute name="test" value="true"/>
|
|
||||||
</attributes>
|
|
||||||
</classpathentry>
|
|
||||||
<classpathentry kind="output" path="target/classes"/>
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
7
pom.xml
7
pom.xml
@ -28,11 +28,16 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.informatik-ag-ngl</groupId>
|
<groupId>com.github.informatik-ag-ngl</groupId>
|
||||||
<artifactId>envoy-common</artifactId>
|
<artifactId>envoy-common</artifactId>
|
||||||
<version>e5c67b8</version>
|
<version>develop-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<finalName>envoy-client</finalName>
|
<finalName>envoy-client</finalName>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
@ -2,9 +2,9 @@ package envoy.client;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import javax.swing.DefaultListModel;
|
import envoy.client.ui.list.ComponentListModel;
|
||||||
|
|
||||||
import envoy.data.Message;
|
import envoy.data.Message;
|
||||||
|
import envoy.data.Message.MessageStatus;
|
||||||
import envoy.data.User;
|
import envoy.data.User;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +25,7 @@ public class Chat implements Serializable {
|
|||||||
private static final long serialVersionUID = -7751248474547242056L;
|
private static final long serialVersionUID = -7751248474547242056L;
|
||||||
|
|
||||||
private User recipient;
|
private User recipient;
|
||||||
private DefaultListModel<Message> model = new DefaultListModel<>();
|
private ComponentListModel<Message> model = new ComponentListModel<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the list of messages that the recipient receives.<br>
|
* Provides the list of messages that the recipient receives.<br>
|
||||||
@ -37,22 +37,34 @@ public class Chat implements Serializable {
|
|||||||
public Chat(User recipient) { this.recipient = recipient; }
|
public Chat(User recipient) { this.recipient = recipient; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the recipient of a message
|
* Appends a message to the bottom of this chat
|
||||||
|
*
|
||||||
|
* @param message the message to append
|
||||||
* @since Envoy v0.1-alpha
|
* @since Envoy v0.1-alpha
|
||||||
*/
|
*/
|
||||||
public User getRecipient() { return recipient; }
|
public void appendMessage(Message message) { model.add(message); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the received message at the current Point in the current chat
|
* Sets the status of all chat messages to {@code READ} starting from the bottom
|
||||||
|
* and stopping once a read message is found.
|
||||||
*
|
*
|
||||||
* @param message the message to add in said chat
|
* @since Envoy v0.3-alpha
|
||||||
* @since Envoy v0.1-alpha
|
|
||||||
*/
|
*/
|
||||||
public void appendMessage(Message message) { model.addElement(message); }
|
public void read() {
|
||||||
|
for (int i = model.size() - 1; i >= 0; --i)
|
||||||
|
if (model.get(i).getStatus() == MessageStatus.READ) break;
|
||||||
|
else model.get(i).setStatus(MessageStatus.READ);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return all messages in the current chat
|
* @return all messages in the current chat
|
||||||
* @since Envoy v0.1-alpha
|
* @since Envoy v0.1-alpha
|
||||||
*/
|
*/
|
||||||
public DefaultListModel<Message> getModel() { return model; }
|
public ComponentListModel<Message> getModel() { return model; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the recipient of a message
|
||||||
|
* @since Envoy v0.1-alpha
|
||||||
|
*/
|
||||||
|
public User getRecipient() { return recipient; }
|
||||||
}
|
}
|
@ -11,6 +11,7 @@ import javax.naming.TimeLimitExceededException;
|
|||||||
|
|
||||||
import envoy.client.util.EnvoyLog;
|
import envoy.client.util.EnvoyLog;
|
||||||
import envoy.data.*;
|
import envoy.data.*;
|
||||||
|
import envoy.event.IdGeneratorRequest;
|
||||||
import envoy.util.SerializationUtils;
|
import envoy.util.SerializationUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,11 +46,13 @@ public class Client implements Closeable {
|
|||||||
* an exception is thrown.
|
* an exception is thrown.
|
||||||
*
|
*
|
||||||
* @param credentials the login credentials of the user
|
* @param credentials the login credentials of the user
|
||||||
|
* @param localDB the local database used to persist the current
|
||||||
|
* {@link IdGenerator}
|
||||||
* @throws Exception if the online mode could not be entered or the request
|
* @throws Exception if the online mode could not be entered or the request
|
||||||
* failed for some other reason
|
* failed for some other reason
|
||||||
* @since Envoy v0.2-alpha
|
* @since Envoy v0.2-alpha
|
||||||
*/
|
*/
|
||||||
public void onlineInit(LoginCredentials credentials) throws Exception {
|
public void onlineInit(LoginCredentials credentials, LocalDB localDB) throws Exception {
|
||||||
// Establish TCP connection
|
// Establish TCP connection
|
||||||
logger.info(String.format("Attempting connection to server %s:%d...", config.getServer(), config.getPort()));
|
logger.info(String.format("Attempting connection to server %s:%d...", config.getServer(), config.getPort()));
|
||||||
socket = new Socket(config.getServer(), config.getPort());
|
socket = new Socket(config.getServer(), config.getPort());
|
||||||
@ -84,6 +87,13 @@ public class Client implements Closeable {
|
|||||||
|
|
||||||
// Register processors for message and status handling
|
// Register processors for message and status handling
|
||||||
receiver.registerProcessor(Message.class, new ReceivedMessageProcessor());
|
receiver.registerProcessor(Message.class, new ReceivedMessageProcessor());
|
||||||
|
// TODO: Status handling
|
||||||
|
|
||||||
|
// Process message ID generation
|
||||||
|
receiver.registerProcessor(IdGenerator.class, localDB::setIdGenerator);
|
||||||
|
|
||||||
|
// Request a generator if none is present
|
||||||
|
if (!localDB.hasIdGenerator() || !localDB.getIdGenerator().hasNext()) requestIdGenerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -94,11 +104,21 @@ public class Client implements Closeable {
|
|||||||
* @since Envoy v0.3-alpha
|
* @since Envoy v0.3-alpha
|
||||||
*/
|
*/
|
||||||
public void sendMessage(Message message) throws IOException {
|
public void sendMessage(Message message) throws IOException {
|
||||||
checkOnline();
|
writeObject(message);
|
||||||
SerializationUtils.writeBytesWithLength(message, socket.getOutputStream());
|
|
||||||
message.nextStatus();
|
message.nextStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests a new {@link IdGenerator} from the server.
|
||||||
|
*
|
||||||
|
* @throws IOException if the request does not reach the server
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
public void requestIdGenerator() throws IOException {
|
||||||
|
logger.info("Requesting new id generator...");
|
||||||
|
writeObject(new IdGeneratorRequest());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a {@code Map<String, User>} of all users on the server with their
|
* @return a {@code Map<String, User>} of all users on the server with their
|
||||||
* user names as keys
|
* user names as keys
|
||||||
@ -114,6 +134,11 @@ public class Client implements Closeable {
|
|||||||
@Override
|
@Override
|
||||||
public void close() throws IOException { if (online) socket.close(); }
|
public void close() throws IOException { if (online) socket.close(); }
|
||||||
|
|
||||||
|
private void writeObject(Object obj) throws IOException {
|
||||||
|
checkOnline();
|
||||||
|
SerializationUtils.writeBytesWithLength(obj, socket.getOutputStream());
|
||||||
|
}
|
||||||
|
|
||||||
private void checkOnline() { if (!online) throw new IllegalStateException("Client is not online"); }
|
private void checkOnline() { if (!online) throw new IllegalStateException("Client is not online"); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +31,6 @@ public class Config {
|
|||||||
items.put("server", new ConfigItem<>("server", "s", (input) -> input, null));
|
items.put("server", new ConfigItem<>("server", "s", (input) -> input, null));
|
||||||
items.put("port", new ConfigItem<>("port", "p", (input) -> Integer.parseInt(input), null));
|
items.put("port", new ConfigItem<>("port", "p", (input) -> Integer.parseInt(input), null));
|
||||||
items.put("localDB", new ConfigItem<>("localDB", "db", (input) -> new File(input), new File("localDB")));
|
items.put("localDB", new ConfigItem<>("localDB", "db", (input) -> new File(input), new File("localDB")));
|
||||||
items.put("syncTimeout", new ConfigItem<>("syncTimeout", "st", (input) -> Integer.parseInt(input), 1000));
|
|
||||||
items.put("homeDirectory",
|
items.put("homeDirectory",
|
||||||
new ConfigItem<>("homeDirectory", "h", (input) -> new File(input), new File(System.getProperty("user.home"), ".envoy")));
|
new ConfigItem<>("homeDirectory", "h", (input) -> new File(input), new File(System.getProperty("user.home"), ".envoy")));
|
||||||
items.put("fileLevelBarrier", new ConfigItem<>("fileLevelBarrier", "fb", (input) -> Level.parse(input), Level.CONFIG));
|
items.put("fileLevelBarrier", new ConfigItem<>("fileLevelBarrier", "fb", (input) -> Level.parse(input), Level.CONFIG));
|
||||||
@ -112,7 +111,7 @@ public class Config {
|
|||||||
* @return the port at which the Envoy server is located on the host
|
* @return the port at which the Envoy server is located on the host
|
||||||
* @since Envoy v0.1-alpha
|
* @since Envoy v0.1-alpha
|
||||||
*/
|
*/
|
||||||
public int getPort() { return (int) items.get("port").get(); }
|
public Integer getPort() { return (Integer) items.get("port").get(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the local database specific to the client user
|
* @return the local database specific to the client user
|
||||||
@ -120,12 +119,6 @@ public class Config {
|
|||||||
*/
|
*/
|
||||||
public File getLocalDB() { return (File) items.get("localDB").get(); }
|
public File getLocalDB() { return (File) items.get("localDB").get(); }
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the current time (milliseconds) that is waited between Syncs
|
|
||||||
* @since Envoy v0.1-alpha
|
|
||||||
*/
|
|
||||||
public int getSyncTimeout() { return (int) items.get("syncTimeout").get(); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the directory in which all local files are saves
|
* @return the directory in which all local files are saves
|
||||||
* @since Envoy v0.2-alpha
|
* @since Envoy v0.2-alpha
|
||||||
|
@ -4,10 +4,15 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import envoy.data.IdGenerator;
|
||||||
import envoy.data.User;
|
import envoy.data.User;
|
||||||
import envoy.util.SerializationUtils;
|
import envoy.util.SerializationUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Stored information about the current {@link User} and their {@link Chat}s.
|
||||||
|
* For message ID generation a {@link IdGenerator} is stored as well.
|
||||||
|
* These object are persisted inside a folder of the local file system.<br>
|
||||||
|
* <br>
|
||||||
* Project: <strong>envoy-client</strong><br>
|
* Project: <strong>envoy-client</strong><br>
|
||||||
* File: <strong>LocalDB.java</strong><br>
|
* File: <strong>LocalDB.java</strong><br>
|
||||||
* Created: <strong>27.10.2019</strong><br>
|
* Created: <strong>27.10.2019</strong><br>
|
||||||
@ -18,10 +23,11 @@ import envoy.util.SerializationUtils;
|
|||||||
*/
|
*/
|
||||||
public class LocalDB {
|
public class LocalDB {
|
||||||
|
|
||||||
private File localDBDir, localDBFile, usersFile;
|
private File localDBDir, localDBFile, usersFile, idGeneratorFile;
|
||||||
private User user;
|
private User user;
|
||||||
private Map<String, User> users = new HashMap<>();
|
private Map<String, User> users = new HashMap<>();
|
||||||
private List<Chat> chats = new ArrayList<>();
|
private List<Chat> chats = new ArrayList<>();
|
||||||
|
private IdGenerator idGenerator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an empty local database. To serialize any chats to the file
|
* Constructs an empty local database. To serialize any chats to the file
|
||||||
@ -38,6 +44,7 @@ public class LocalDB {
|
|||||||
if (localDBDir.exists() && !localDBDir.isDirectory())
|
if (localDBDir.exists() && !localDBDir.isDirectory())
|
||||||
throw new IOException(String.format("LocalDBDir '%s' is not a directory!", localDBDir.getAbsolutePath()));
|
throw new IOException(String.format("LocalDBDir '%s' is not a directory!", localDBDir.getAbsolutePath()));
|
||||||
usersFile = new File(localDBDir, "users.db");
|
usersFile = new File(localDBDir, "users.db");
|
||||||
|
idGeneratorFile = new File(localDBDir, "id_generator.db");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,7 +60,8 @@ public class LocalDB {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores all users to the local database. If the client user is specified, the
|
* Stores all users to the local database. If the client user is specified, the
|
||||||
* chats related to this user are stored as well.
|
* chats related to this user are stored as well. The message id generator will
|
||||||
|
* also be saved if present.
|
||||||
*
|
*
|
||||||
* @throws IOException if something went wrong during saving
|
* @throws IOException if something went wrong during saving
|
||||||
* @since Envoy v0.1-alpha
|
* @since Envoy v0.1-alpha
|
||||||
@ -63,7 +71,10 @@ public class LocalDB {
|
|||||||
SerializationUtils.write(usersFile, users);
|
SerializationUtils.write(usersFile, users);
|
||||||
|
|
||||||
// Save chats
|
// Save chats
|
||||||
SerializationUtils.write(localDBFile, chats);
|
if (user != null) SerializationUtils.write(localDBFile, chats);
|
||||||
|
|
||||||
|
// Save id generator
|
||||||
|
if (hasIdGenerator()) SerializationUtils.write(idGeneratorFile, idGenerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,6 +95,18 @@ public class LocalDB {
|
|||||||
*/
|
*/
|
||||||
public void loadChats() throws ClassNotFoundException, IOException { chats = SerializationUtils.read(localDBFile, ArrayList.class); }
|
public void loadChats() throws ClassNotFoundException, IOException { chats = SerializationUtils.read(localDBFile, ArrayList.class); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the message ID generator that is stored in the local database. If the
|
||||||
|
* file is not found, the exception is ignored.
|
||||||
|
*
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
public void loadIdGenerator() {
|
||||||
|
try {
|
||||||
|
idGenerator = SerializationUtils.read(idGeneratorFile, IdGenerator.class);
|
||||||
|
} catch (ClassNotFoundException | IOException e) {}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a {@code Map<String, User>} of all users stored locally with their
|
* @return a {@code Map<String, User>} of all users stored locally with their
|
||||||
* user names as keys
|
* user names as keys
|
||||||
@ -119,4 +142,22 @@ public class LocalDB {
|
|||||||
* @since Envoy v0.2-alpha
|
* @since Envoy v0.2-alpha
|
||||||
*/
|
*/
|
||||||
public void setUser(User user) { this.user = user; }
|
public void setUser(User user) { this.user = user; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the message ID generator
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
public IdGenerator getIdGenerator() { return idGenerator; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param idGenerator the message ID generator to set
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
public void setIdGenerator(IdGenerator idGenerator) { this.idGenerator = idGenerator; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@code true} if an {@link IdGenerator} is present
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
public boolean hasIdGenerator() { return idGenerator != null; }
|
||||||
}
|
}
|
@ -135,7 +135,7 @@ public class Settings {
|
|||||||
* {@code Control} key.
|
* {@code Control} key.
|
||||||
* @since Envoy v0.2-alpha
|
* @since Envoy v0.2-alpha
|
||||||
*/
|
*/
|
||||||
public boolean isEnterToSend() { return (boolean) items.get("enterToSend").get(); }
|
public Boolean isEnterToSend() { return (Boolean) items.get("enterToSend").get(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the keystrokes performed by the user to send a message.
|
* Changes the keystrokes performed by the user to send a message.
|
||||||
@ -152,7 +152,7 @@ public class Settings {
|
|||||||
* @return the current on close mode.
|
* @return the current on close mode.
|
||||||
* @since Envoy v0.3-alpha
|
* @since Envoy v0.3-alpha
|
||||||
*/
|
*/
|
||||||
public boolean getCurrentOnCloseMode() { return (boolean) items.get("onCloseMode").get(); }
|
public Boolean getCurrentOnCloseMode() { return (Boolean) items.get("onCloseMode").get(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the current on close mode.
|
* Sets the current on close mode.
|
||||||
|
@ -10,7 +10,9 @@ import javax.swing.*;
|
|||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
|
|
||||||
import envoy.client.*;
|
import envoy.client.*;
|
||||||
|
import envoy.client.event.MessageCreationEvent;
|
||||||
import envoy.client.event.ThemeChangeEvent;
|
import envoy.client.event.ThemeChangeEvent;
|
||||||
|
import envoy.client.ui.list.ComponentList;
|
||||||
import envoy.client.ui.settings.SettingsScreen;
|
import envoy.client.ui.settings.SettingsScreen;
|
||||||
import envoy.client.util.EnvoyLog;
|
import envoy.client.util.EnvoyLog;
|
||||||
import envoy.data.Message;
|
import envoy.data.Message;
|
||||||
@ -30,8 +32,6 @@ import envoy.event.EventBus;
|
|||||||
*/
|
*/
|
||||||
public class ChatWindow extends JFrame {
|
public class ChatWindow extends JFrame {
|
||||||
|
|
||||||
private static final long serialVersionUID = 6865098428255463649L;
|
|
||||||
|
|
||||||
// User specific objects
|
// User specific objects
|
||||||
private Client client;
|
private Client client;
|
||||||
private LocalDB localDB;
|
private LocalDB localDB;
|
||||||
@ -41,16 +41,20 @@ public class ChatWindow extends JFrame {
|
|||||||
private PrimaryTextArea messageEnterTextArea = new PrimaryTextArea(space);
|
private PrimaryTextArea messageEnterTextArea = new PrimaryTextArea(space);
|
||||||
private JList<User> userList = new JList<>();
|
private JList<User> userList = new JList<>();
|
||||||
private Chat currentChat;
|
private Chat currentChat;
|
||||||
private JList<Message> messageList = new JList<>();
|
private ComponentList<Message> messageList = new ComponentList<>(new MessageListRenderer());
|
||||||
private PrimaryScrollPane scrollPane = new PrimaryScrollPane();
|
private PrimaryScrollPane scrollPane = new PrimaryScrollPane();
|
||||||
private JTextPane textPane = new JTextPane();
|
private JTextPane textPane = new JTextPane();
|
||||||
private PrimaryButton postButton = new PrimaryButton("Post");
|
private PrimaryButton postButton = new PrimaryButton("Post");
|
||||||
private PrimaryButton settingsButton = new PrimaryButton("Settings");
|
private PrimaryButton settingsButton = new PrimaryButton("Settings");
|
||||||
|
|
||||||
private static int space = 4;
|
|
||||||
|
|
||||||
private static final Logger logger = EnvoyLog.getLogger(ChatWindow.class.getSimpleName());
|
private static final Logger logger = EnvoyLog.getLogger(ChatWindow.class.getSimpleName());
|
||||||
|
|
||||||
|
// GUI component spacing
|
||||||
|
private final static int space = 4;
|
||||||
|
private static final Insets insets = new Insets(space, space, space, space);
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 6865098428255463649L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a {@link JFrame} with UI elements used to send and read messages
|
* Initializes a {@link JFrame} with UI elements used to send and read messages
|
||||||
* to different users.
|
* to different users.
|
||||||
@ -73,14 +77,11 @@ public class ChatWindow extends JFrame {
|
|||||||
gbl_contentPane.rowWeights = new double[] { 0.05, 1.0, 0.07 };
|
gbl_contentPane.rowWeights = new double[] { 0.05, 1.0, 0.07 };
|
||||||
contentPane.setLayout(gbl_contentPane);
|
contentPane.setLayout(gbl_contentPane);
|
||||||
|
|
||||||
messageList.setCellRenderer(new MessageListRenderer());
|
// TODO: messageList.setFocusTraversalKeysEnabled(false);
|
||||||
messageList.setFocusTraversalKeysEnabled(false);
|
// messageList.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
|
||||||
messageList.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
|
|
||||||
|
|
||||||
DefaultListModel<Message> messageListModel = new DefaultListModel<>();
|
// messageList.setFont(new Font("Arial", Font.PLAIN, 17));
|
||||||
messageList.setModel(messageListModel);
|
// messageList.setFixedCellHeight(60);
|
||||||
messageList.setFont(new Font("Arial", Font.PLAIN, 17));
|
|
||||||
messageList.setFixedCellHeight(60);
|
|
||||||
messageList.setBorder(new EmptyBorder(space, space, space, space));
|
messageList.setBorder(new EmptyBorder(space, space, space, space));
|
||||||
|
|
||||||
scrollPane.setViewportView(messageList);
|
scrollPane.setViewportView(messageList);
|
||||||
@ -91,7 +92,7 @@ public class ChatWindow extends JFrame {
|
|||||||
gbc_scrollPane.gridx = 1;
|
gbc_scrollPane.gridx = 1;
|
||||||
gbc_scrollPane.gridy = 1;
|
gbc_scrollPane.gridy = 1;
|
||||||
|
|
||||||
gbc_scrollPane.insets = new Insets(space, space, space, space);
|
gbc_scrollPane.insets = insets;
|
||||||
contentPane.add(scrollPane, gbc_scrollPane);
|
contentPane.add(scrollPane, gbc_scrollPane);
|
||||||
|
|
||||||
// Message enter field
|
// Message enter field
|
||||||
@ -100,8 +101,8 @@ public class ChatWindow extends JFrame {
|
|||||||
@Override
|
@Override
|
||||||
public void keyReleased(KeyEvent e) {
|
public void keyReleased(KeyEvent e) {
|
||||||
if (e.getKeyCode() == KeyEvent.VK_ENTER
|
if (e.getKeyCode() == KeyEvent.VK_ENTER
|
||||||
&& ((Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0) || (e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK)))
|
&& (Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0 || e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK))
|
||||||
postMessage(messageList);
|
postMessage();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -110,7 +111,7 @@ public class ChatWindow extends JFrame {
|
|||||||
gbc_messageEnterTextfield.gridx = 1;
|
gbc_messageEnterTextfield.gridx = 1;
|
||||||
gbc_messageEnterTextfield.gridy = 2;
|
gbc_messageEnterTextfield.gridy = 2;
|
||||||
|
|
||||||
gbc_messageEnterTextfield.insets = new Insets(space, space, space, space);
|
gbc_messageEnterTextfield.insets = insets;
|
||||||
|
|
||||||
contentPane.add(messageEnterTextArea, gbc_messageEnterTextfield);
|
contentPane.add(messageEnterTextArea, gbc_messageEnterTextfield);
|
||||||
|
|
||||||
@ -121,9 +122,9 @@ public class ChatWindow extends JFrame {
|
|||||||
gbc_moveSelectionPostButton.gridx = 2;
|
gbc_moveSelectionPostButton.gridx = 2;
|
||||||
gbc_moveSelectionPostButton.gridy = 2;
|
gbc_moveSelectionPostButton.gridy = 2;
|
||||||
|
|
||||||
gbc_moveSelectionPostButton.insets = new Insets(space, space, space, space);
|
gbc_moveSelectionPostButton.insets = insets;
|
||||||
|
|
||||||
postButton.addActionListener((evt) -> { postMessage(messageList); });
|
postButton.addActionListener((evt) -> { postMessage(); });
|
||||||
contentPane.add(postButton, gbc_moveSelectionPostButton);
|
contentPane.add(postButton, gbc_moveSelectionPostButton);
|
||||||
|
|
||||||
// Settings Button
|
// Settings Button
|
||||||
@ -133,7 +134,7 @@ public class ChatWindow extends JFrame {
|
|||||||
gbc_moveSelectionSettingsButton.gridx = 2;
|
gbc_moveSelectionSettingsButton.gridx = 2;
|
||||||
gbc_moveSelectionSettingsButton.gridy = 0;
|
gbc_moveSelectionSettingsButton.gridy = 0;
|
||||||
|
|
||||||
gbc_moveSelectionSettingsButton.insets = new Insets(space, space, space, space);
|
gbc_moveSelectionSettingsButton.insets = insets;
|
||||||
|
|
||||||
settingsButton.addActionListener((evt) -> {
|
settingsButton.addActionListener((evt) -> {
|
||||||
try {
|
try {
|
||||||
@ -154,7 +155,7 @@ public class ChatWindow extends JFrame {
|
|||||||
gbc_partnerName.gridx = 1;
|
gbc_partnerName.gridx = 1;
|
||||||
gbc_partnerName.gridy = 0;
|
gbc_partnerName.gridy = 0;
|
||||||
|
|
||||||
gbc_partnerName.insets = new Insets(space, space, space, space);
|
gbc_partnerName.insets = insets;
|
||||||
contentPane.add(textPane, gbc_partnerName);
|
contentPane.add(textPane, gbc_partnerName);
|
||||||
|
|
||||||
userList.setCellRenderer(new UserListRenderer());
|
userList.setCellRenderer(new UserListRenderer());
|
||||||
@ -165,17 +166,22 @@ public class ChatWindow extends JFrame {
|
|||||||
final JList<User> selectedUserList = (JList<User>) listSelectionEvent.getSource();
|
final JList<User> selectedUserList = (JList<User>) listSelectionEvent.getSource();
|
||||||
final User user = selectedUserList.getSelectedValue();
|
final User user = selectedUserList.getSelectedValue();
|
||||||
|
|
||||||
|
// Select current chat
|
||||||
currentChat = localDB.getChats().stream().filter(chat -> chat.getRecipient().getId() == user.getId()).findFirst().get();
|
currentChat = localDB.getChats().stream().filter(chat -> chat.getRecipient().getId() == user.getId()).findFirst().get();
|
||||||
|
|
||||||
// Set all unread messages in the chat to read
|
// Read current Chat
|
||||||
readCurrentChat();
|
currentChat.read();
|
||||||
|
|
||||||
|
// Set recipient in client and chat title
|
||||||
client.setRecipient(user);
|
client.setRecipient(user);
|
||||||
textPane.setText(currentChat.getRecipient().getName());
|
textPane.setText(currentChat.getRecipient().getName());
|
||||||
|
|
||||||
|
// Update model and scroll down
|
||||||
messageList.setModel(currentChat.getModel());
|
messageList.setModel(currentChat.getModel());
|
||||||
scrollPane.setChatOpened(true);
|
scrollPane.setChatOpened(true);
|
||||||
contentPane.revalidate();
|
|
||||||
|
revalidate();
|
||||||
|
repaint();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -187,20 +193,29 @@ public class ChatWindow extends JFrame {
|
|||||||
gbc_userList.gridx = 0;
|
gbc_userList.gridx = 0;
|
||||||
gbc_userList.gridy = 1;
|
gbc_userList.gridy = 1;
|
||||||
gbc_userList.anchor = GridBagConstraints.PAGE_START;
|
gbc_userList.anchor = GridBagConstraints.PAGE_START;
|
||||||
gbc_userList.insets = new Insets(space, space, space, space);
|
gbc_userList.insets = insets;
|
||||||
|
|
||||||
applyTheme(Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()));
|
applyTheme(Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()));
|
||||||
|
|
||||||
contentPane.add(userList, gbc_userList);
|
contentPane.add(userList, gbc_userList);
|
||||||
contentPane.revalidate();
|
contentPane.revalidate();
|
||||||
|
|
||||||
|
// Listen to theme changes
|
||||||
EventBus.getInstance().register(ThemeChangeEvent.class, (evt) -> applyTheme((Theme) evt.get()));
|
EventBus.getInstance().register(ThemeChangeEvent.class, (evt) -> applyTheme((Theme) evt.get()));
|
||||||
|
|
||||||
contentPane.revalidate();
|
// Listen to received messages
|
||||||
|
EventBus.getInstance().register(MessageCreationEvent.class, (evt) -> {
|
||||||
|
Message message = ((MessageCreationEvent) evt).get();
|
||||||
|
localDB.getChats().stream().filter(c -> c.getRecipient().getId() == message.getSenderId()).findFirst().get().appendMessage(message);
|
||||||
|
revalidate();
|
||||||
|
repaint();
|
||||||
|
});
|
||||||
|
|
||||||
|
revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to immediately reload the ChatWindow when settings were changed.
|
* Used to immediately reload the {@link ChatWindow} when settings were changed.
|
||||||
*
|
*
|
||||||
* @param theme the theme to change colors into
|
* @param theme the theme to change colors into
|
||||||
* @since Envoy v0.2-alpha
|
* @since Envoy v0.2-alpha
|
||||||
@ -210,8 +225,8 @@ public class ChatWindow extends JFrame {
|
|||||||
contentPane.setBackground(theme.getBackgroundColor());
|
contentPane.setBackground(theme.getBackgroundColor());
|
||||||
contentPane.setForeground(theme.getUserNameColor());
|
contentPane.setForeground(theme.getUserNameColor());
|
||||||
// messageList
|
// messageList
|
||||||
messageList.setSelectionForeground(theme.getUserNameColor());
|
// messageList.setSelectionForeground(theme.getUserNameColor());
|
||||||
messageList.setSelectionBackground(theme.getSelectionColor());
|
// messageList.setSelectionBackground(theme.getSelectionColor());
|
||||||
messageList.setForeground(theme.getMessageColorChat());
|
messageList.setForeground(theme.getMessageColorChat());
|
||||||
messageList.setBackground(theme.getCellColor());
|
messageList.setBackground(theme.getCellColor());
|
||||||
// scrollPane
|
// scrollPane
|
||||||
@ -238,7 +253,7 @@ public class ChatWindow extends JFrame {
|
|||||||
userList.setBackground(theme.getCellColor());
|
userList.setBackground(theme.getCellColor());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void postMessage(JList<Message> messageList) {
|
private void postMessage() {
|
||||||
if (!client.hasRecipient()) {
|
if (!client.hasRecipient()) {
|
||||||
JOptionPane.showMessageDialog(this, "Please select a recipient!", "Cannot send message", JOptionPane.INFORMATION_MESSAGE);
|
JOptionPane.showMessageDialog(this, "Please select a recipient!", "Cannot send message", JOptionPane.INFORMATION_MESSAGE);
|
||||||
return;
|
return;
|
||||||
@ -247,24 +262,32 @@ public class ChatWindow extends JFrame {
|
|||||||
if (!messageEnterTextArea.getText().isEmpty()) try {
|
if (!messageEnterTextArea.getText().isEmpty()) try {
|
||||||
|
|
||||||
// Create message
|
// Create message
|
||||||
final Message message = new MessageBuilder(localDB.getUser().getId(), currentChat.getRecipient().getId())
|
final Message message = new MessageBuilder(localDB.getUser().getId(), currentChat.getRecipient().getId(), localDB.getIdGenerator())
|
||||||
.setText(messageEnterTextArea.getText())
|
.setText(messageEnterTextArea.getText())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Send message
|
// Send message
|
||||||
|
// TODO: Store offline messages
|
||||||
client.sendMessage(message);
|
client.sendMessage(message);
|
||||||
|
|
||||||
// Add message to LocalDB and update UI
|
// Add message to LocalDB and update UI
|
||||||
currentChat.appendMessage(message);
|
currentChat.appendMessage(message);
|
||||||
messageList.setModel(currentChat.getModel());
|
// messageList.setModel(currentChat.getModel());
|
||||||
|
|
||||||
// Clear text field
|
// Clear text field
|
||||||
messageEnterTextArea.setText("");
|
messageEnterTextArea.setText("");
|
||||||
contentPane.revalidate();
|
|
||||||
|
// Update UI
|
||||||
|
revalidate();
|
||||||
|
repaint();
|
||||||
|
|
||||||
|
// Request a new id generator if all ids were used
|
||||||
|
if (!localDB.getIdGenerator().hasNext()) client.requestIdGenerator();
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
JOptionPane.showMessageDialog(this,
|
JOptionPane.showMessageDialog(this,
|
||||||
"An exception occured while sending a message. See the log for more details.",
|
"Error sending message:\n" + e.toString(),
|
||||||
"Exception occured",
|
"Message sending error",
|
||||||
JOptionPane.ERROR_MESSAGE);
|
JOptionPane.ERROR_MESSAGE);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -291,64 +314,13 @@ public class ChatWindow extends JFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the data model and the UI repeatedly after a certain amount of
|
* Sets the {@link Client} used by this {@link ChatWindow}.
|
||||||
* time.
|
|
||||||
*
|
|
||||||
* @param timeout the amount of time that passes between two requests sent to
|
|
||||||
* the server
|
|
||||||
* @since Envoy v0.1-alpha
|
|
||||||
*/
|
|
||||||
private void startSyncThread(int timeout) {
|
|
||||||
new Timer(timeout, (evt) -> {
|
|
||||||
new Thread(() -> {
|
|
||||||
|
|
||||||
// Synchronize
|
|
||||||
try {
|
|
||||||
// localDB.applySync(client.sendSync(client.getSender().getId(),
|
|
||||||
// localDB.fillSync(client.getSender().getId())));
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.log(Level.SEVERE, "Could not perform sync", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Process unread messages
|
|
||||||
// localDB.addUnreadMessagesToLocalDB();
|
|
||||||
// localDB.clearUnreadMessagesSync();
|
|
||||||
|
|
||||||
// Mark unread messages as read when they are in the current chat
|
|
||||||
readCurrentChat();
|
|
||||||
|
|
||||||
// Update UI
|
|
||||||
SwingUtilities.invokeLater(() -> { updateUserStates(); contentPane.revalidate(); contentPane.repaint(); });
|
|
||||||
}).start();
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateUserStates() {
|
|
||||||
for (int i = 0; i < userList.getModel().getSize(); i++)
|
|
||||||
for (int j = 0; j < localDB.getChats().size(); j++)
|
|
||||||
if (userList.getModel().getElementAt(i).getId() == localDB.getChats().get(j).getRecipient().getId())
|
|
||||||
userList.getModel().getElementAt(i).setStatus(localDB.getChats().get(j).getRecipient().getStatus());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks messages in the current chat as {@code READ}.
|
|
||||||
*/
|
|
||||||
private void readCurrentChat() {
|
|
||||||
if (currentChat != null) {
|
|
||||||
// TODO: localDB.setMessagesToRead(currentChat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the {@link Client} used by this {@link ChatWindow}. If the client is
|
|
||||||
* online, the sync thread is started.
|
|
||||||
*
|
*
|
||||||
* @param client the {@link Client} used to send and receive messages
|
* @param client the {@link Client} used to send and receive messages
|
||||||
* @since Envoy v0.2-alpha
|
* @since Envoy v0.2-alpha
|
||||||
*/
|
*/
|
||||||
public void setClient(Client client) {
|
public void setClient(Client client) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
if (client.isOnline() && localDB != null) startSyncThread(Config.getInstance().getSyncTimeout());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -361,6 +333,5 @@ public class ChatWindow extends JFrame {
|
|||||||
public void setLocalDB(LocalDB localDB) {
|
public void setLocalDB(LocalDB localDB) {
|
||||||
this.localDB = localDB;
|
this.localDB = localDB;
|
||||||
loadUsersAndChats();
|
loadUsersAndChats();
|
||||||
if (client != null && client.isOnline()) startSyncThread(Config.getInstance().getSyncTimeout());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package envoy.client.ui;
|
package envoy.client.ui;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Dimension;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
|
||||||
import javax.swing.JLabel;
|
import javax.swing.*;
|
||||||
import javax.swing.JList;
|
|
||||||
import javax.swing.ListCellRenderer;
|
|
||||||
|
|
||||||
import envoy.client.Settings;
|
import envoy.client.Settings;
|
||||||
|
import envoy.client.ui.list.ComponentList;
|
||||||
|
import envoy.client.ui.list.ComponentListCellRenderer;
|
||||||
import envoy.data.Message;
|
import envoy.data.Message;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,22 +21,22 @@ import envoy.data.Message;
|
|||||||
* @author Maximilian Käfer
|
* @author Maximilian Käfer
|
||||||
* @since Envoy v0.1-alpha
|
* @since Envoy v0.1-alpha
|
||||||
*/
|
*/
|
||||||
public class MessageListRenderer extends JLabel implements ListCellRenderer<Message> {
|
public class MessageListRenderer implements ComponentListCellRenderer<Message> {
|
||||||
|
|
||||||
private static final long serialVersionUID = 5164417379767181198L;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getListCellRendererComponent(JList<? extends Message> list, Message value, int index, boolean isSelected, boolean cellHasFocus) {
|
public JComponent getListCellComponent(ComponentList<? extends Message> list, Message value, boolean isSelected) {
|
||||||
|
final JPanel panel = new JPanel();
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
setBackground(list.getSelectionBackground());
|
panel.setBackground(Color.DARK_GRAY);
|
||||||
setForeground(list.getSelectionForeground());
|
panel.setForeground(Color.RED);
|
||||||
|
// TODO: Selection
|
||||||
|
// setBackground(list.getSelectionBackground());
|
||||||
|
// setForeground(list.getSelectionForeground());
|
||||||
} else {
|
} else {
|
||||||
setBackground(list.getBackground());
|
panel.setBackground(list.getBackground());
|
||||||
setForeground(list.getForeground());
|
panel.setForeground(list.getForeground());
|
||||||
}
|
}
|
||||||
|
|
||||||
setOpaque(true);
|
|
||||||
|
|
||||||
// TODO: Handle message attachments
|
// TODO: Handle message attachments
|
||||||
|
|
||||||
final String text = value.getText();
|
final String text = value.getText();
|
||||||
@ -49,12 +49,22 @@ public class MessageListRenderer extends JLabel implements ListCellRenderer<Mess
|
|||||||
// Getting the DateColor in the Chat of the current theme
|
// Getting the DateColor in the Chat of the current theme
|
||||||
String dateColor = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getDateColorChat().toHex();
|
String dateColor = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()).getDateColorChat().toHex();
|
||||||
|
|
||||||
setText(String.format("<html><p style=\"color:%s\"><b><small>%s</b></small><br><p style=\"color:%s\">%s :%s</html>",
|
panel.add(new JLabel(String.format("<html><p style=\"color:%s\"><b><small>%s</b></small><br><p style=\"color:%s\">%s :%s</html>",
|
||||||
dateColor,
|
dateColor,
|
||||||
date,
|
date,
|
||||||
textColor,
|
textColor,
|
||||||
text,
|
text,
|
||||||
state));
|
state)));
|
||||||
return this;
|
|
||||||
|
// Define some space to the messages below
|
||||||
|
panel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 0, 15, 0), BorderFactory.createEtchedBorder()));
|
||||||
|
|
||||||
|
// Define a maximum height of 50px
|
||||||
|
Dimension size = new Dimension(list.getWidth() - 25, 50);
|
||||||
|
panel.setMaximumSize(size);
|
||||||
|
panel.setMinimumSize(size);
|
||||||
|
panel.setPreferredSize(size);
|
||||||
|
|
||||||
|
return panel;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -97,7 +97,8 @@ public class Startup {
|
|||||||
Client client = new Client();
|
Client client = new Client();
|
||||||
try {
|
try {
|
||||||
// Try entering online mode first
|
// Try entering online mode first
|
||||||
client.onlineInit(credentials);
|
localDB.loadIdGenerator();
|
||||||
|
client.onlineInit(credentials, localDB);
|
||||||
} catch (Exception e1) {
|
} catch (Exception e1) {
|
||||||
logger.warning("Could not connect to server. Trying offline mode...");
|
logger.warning("Could not connect to server. Trying offline mode...");
|
||||||
e1.printStackTrace();
|
e1.printStackTrace();
|
||||||
@ -152,7 +153,7 @@ public class Startup {
|
|||||||
.getItems()
|
.getItems()
|
||||||
.get("onCloseMode")
|
.get("onCloseMode")
|
||||||
.setChangeHandler((onCloseMode) -> chatWindow
|
.setChangeHandler((onCloseMode) -> chatWindow
|
||||||
.setDefaultCloseOperation((boolean) onCloseMode ? JFrame.HIDE_ON_CLOSE : JFrame.EXIT_ON_CLOSE));
|
.setDefaultCloseOperation((Boolean) onCloseMode ? JFrame.HIDE_ON_CLOSE : JFrame.EXIT_ON_CLOSE));
|
||||||
} catch (EnvoyException e) {
|
} catch (EnvoyException e) {
|
||||||
logger.warning("The StatusTrayIcon is not supported on this platform!");
|
logger.warning("The StatusTrayIcon is not supported on this platform!");
|
||||||
}
|
}
|
||||||
|
91
src/main/java/envoy/client/ui/list/ComponentList.java
Normal file
91
src/main/java/envoy/client/ui/list/ComponentList.java
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package envoy.client.ui.list;
|
||||||
|
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a vertical list layout of components provided in a
|
||||||
|
* {@link ComponentListModel}. Similar to {@link javax.swing.JList} but capable
|
||||||
|
* of rendering {@link JPanel}s.<br>
|
||||||
|
* <br>
|
||||||
|
* Project: <strong>envoy-client</strong><br>
|
||||||
|
* File: <strong>ComponentList.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
|
||||||
|
*/
|
||||||
|
public class ComponentList<E> extends JPanel {
|
||||||
|
|
||||||
|
private ComponentListModel<E> model;
|
||||||
|
private ComponentListCellRenderer<E> renderer;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1759644503942876737L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link ComponentList}.
|
||||||
|
*
|
||||||
|
* @param renderer the list cell renderer used to display elements provided by
|
||||||
|
* the {@link ComponentListModel}
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
public ComponentList(ComponentListCellRenderer<E> renderer) {
|
||||||
|
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
||||||
|
this.renderer = renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link ComponentList}.
|
||||||
|
*
|
||||||
|
* @param model the list model providing the list elements to render
|
||||||
|
* @param renderer the list cell renderer used to display elements provided by
|
||||||
|
* the {@link ComponentListModel}
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
public ComponentList(ComponentListModel<E> model, ComponentListCellRenderer<E> renderer) {
|
||||||
|
this(renderer);
|
||||||
|
this.model = model;
|
||||||
|
setModel(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the list model providing the list elements to render
|
||||||
|
*
|
||||||
|
* @param model the list model to set
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
public void setModel(ComponentListModel<E> model) {
|
||||||
|
// Remove old model
|
||||||
|
if (this.model != null)
|
||||||
|
this.model.setComponentList(null);
|
||||||
|
|
||||||
|
// Synchronize with new model
|
||||||
|
this.model = model;
|
||||||
|
this.model.setComponentList(this);
|
||||||
|
synchronizeModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an object to the list by rendering it with the current
|
||||||
|
* {@link ComponentListCellRenderer}.
|
||||||
|
*
|
||||||
|
* @param elem the element to add
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
void add(E elem) {
|
||||||
|
add(renderer.getListCellComponent(this, elem, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all child components and then adds all components representing the
|
||||||
|
* elements of the {@link ComponentListModel}.
|
||||||
|
*
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
void synchronizeModel() {
|
||||||
|
removeAll();
|
||||||
|
if (model != null) for (E elem : model)
|
||||||
|
add(elem);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package envoy.client.ui.list;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows a {@link ComponentList} convert its elements into Swing components
|
||||||
|
* that can be rendered.<br>
|
||||||
|
* <br>
|
||||||
|
* Project: <strong>envoy-client</strong><br>
|
||||||
|
* File: <strong>ComponentListCellRenderer.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
|
||||||
|
*/
|
||||||
|
public interface ComponentListCellRenderer<E> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a Swing component representing a list element.
|
||||||
|
*
|
||||||
|
* @param list the list in which the component will be displayed
|
||||||
|
* @param value the list element that will be converted
|
||||||
|
* @param isSelected {@code true} if the user has selected the list cell in
|
||||||
|
* which the list element is rendered
|
||||||
|
* @return the component representing the list element
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
JComponent getListCellComponent(ComponentList<? extends E> list, E value, boolean isSelected);
|
||||||
|
}
|
111
src/main/java/envoy/client/ui/list/ComponentListModel.java
Normal file
111
src/main/java/envoy/client/ui/list/ComponentListModel.java
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
package envoy.client.ui.list;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
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>
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
public final class ComponentListModel<E> implements Iterable<E>, Serializable {
|
||||||
|
|
||||||
|
private List<E> elements = new ArrayList<>();
|
||||||
|
transient private ComponentList<E> componentList;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 4815005915255497331L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an element to this model and notifies the associated
|
||||||
|
* {@link ComponentList} to add the corresponding component.
|
||||||
|
*
|
||||||
|
* @param e the element to add
|
||||||
|
* @return {@code true}
|
||||||
|
* @see java.util.List#add(java.lang.Object)
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
public boolean add(E e) {
|
||||||
|
if (componentList != null) componentList.add(e);
|
||||||
|
return elements.add(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all elements from this model and clears the associated
|
||||||
|
* {@link ComponentList}.
|
||||||
|
*
|
||||||
|
* @see java.util.List#clear()
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
elements.clear();
|
||||||
|
if (componentList != null) componentList.removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param index the index to retrieve the element from
|
||||||
|
* @return the element located at the index
|
||||||
|
* @see java.util.List#get(int)
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
public E get(int index) { return elements.get(index); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the element at a specific index from this model and the corresponding
|
||||||
|
* component from the {@link ComponentList}.
|
||||||
|
*
|
||||||
|
* @param index the index of the element to remove
|
||||||
|
* @return the removed element
|
||||||
|
* @see java.util.List#remove(int)
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
public E remove(int index) {
|
||||||
|
if (componentList != null) componentList.remove(index);
|
||||||
|
return elements.remove(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the amount of elements in this list model
|
||||||
|
* @see java.util.List#size()
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
public int size() { return elements.size(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return an iterator over the elements of this list model
|
||||||
|
* @see java.util.List#iterator()
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Iterator<E> iterator() {
|
||||||
|
return new Iterator<E>() {
|
||||||
|
|
||||||
|
Iterator<E> iter = elements.iterator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() { return iter.hasNext(); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E next() { return iter.next(); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the component list displaying the elements of this model and triggers a
|
||||||
|
* synchronization.
|
||||||
|
*
|
||||||
|
* @param componentList the component list to set
|
||||||
|
* @since Envoy v0.3-alpha
|
||||||
|
*/
|
||||||
|
void setComponentList(ComponentList<E> componentList) {
|
||||||
|
this.componentList = componentList;
|
||||||
|
if (componentList != null) componentList.synchronizeModel();
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user