Removed most Swing components
This commit is contained in:
parent
b5fd690f43
commit
c12eac1cbc
@ -1,64 +0,0 @@
|
||||
package envoy.client;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import envoy.client.ui.container.ChatWindow;
|
||||
import envoy.data.Config;
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* Starts the Envoy client and prompts the user to enter their name.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Startup.java</strong><br>
|
||||
* Created: <strong>12 Oct 2019</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @author Maximilian Käfer
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-alpha
|
||||
*/
|
||||
public class Startup {
|
||||
|
||||
private static final Logger logger = EnvoyLog.getLogger(Startup.class);
|
||||
|
||||
// TODO: Update Javadoc
|
||||
/**
|
||||
* Loads the application by first loading the configuration, then acquiring a
|
||||
* user name and connecting to the server. If the server cannot be reached,
|
||||
* offline mode is entered if possible. After that, a {@link ChatWindow}
|
||||
* instance is initialized and then displayed to the user. Upon application
|
||||
* exit, settings and the local database are saved.
|
||||
*
|
||||
* @param args the command line arguments may contain configuration parameters
|
||||
* and are parsed by the {@link Config} class
|
||||
* @since Envoy Client v0.1-alpha
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// Display ChatWindow and StatusTrayIcon
|
||||
// EventQueue.invokeLater(() -> {
|
||||
// try {
|
||||
// chatWindow.initContent(client, localDB, writeProxy);
|
||||
//
|
||||
//
|
||||
// try {
|
||||
// new StatusTrayIcon(chatWindow).show();
|
||||
//
|
||||
// // If the tray icon is supported and corresponding settings is set, hide the
|
||||
// // chat window on close
|
||||
// Settings.getInstance()
|
||||
// .getItems()
|
||||
// .get("onCloseMode")
|
||||
// .setChangeHandler((onCloseMode) -> chatWindow
|
||||
// .setDefaultCloseOperation((Boolean) onCloseMode ? JFrame.HIDE_ON_CLOSE :
|
||||
// JFrame.EXIT_ON_CLOSE));
|
||||
// } catch (EnvoyException e) {
|
||||
// logger.warning("The StatusTrayIcon is not supported on this platform!");
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// });
|
||||
|
||||
}
|
||||
}
|
@ -1,687 +0,0 @@
|
||||
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;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
import envoy.client.data.Chat;
|
||||
import envoy.client.data.LocalDB;
|
||||
import envoy.client.data.Settings;
|
||||
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.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;
|
||||
import envoy.data.MessageBuilder;
|
||||
import envoy.data.User;
|
||||
import envoy.event.*;
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ChatWindow.java</strong><br>
|
||||
* Created: <strong>28 Sep 2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Maximilian Käfer
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.1-alpha
|
||||
*/
|
||||
public class ChatWindow extends JFrame {
|
||||
|
||||
/**
|
||||
* This integer defines the maximum amount of chars allowed per message.
|
||||
*
|
||||
* @since Envoy 0.1-beta
|
||||
*/
|
||||
public static final int MAX_MESSAGE_LENGTH = 200;
|
||||
|
||||
// User specific objects
|
||||
private Client client;
|
||||
private WriteProxy writeProxy;
|
||||
private LocalDB localDB;
|
||||
private Chat currentChat;
|
||||
|
||||
// GUI components
|
||||
private JPanel contentPane = new JPanel();
|
||||
private PrimaryTextArea messageEnterTextArea = new PrimaryTextArea(space);
|
||||
private JList<User> userList = new JList<>();
|
||||
private DefaultListModel<User> userListModel = new DefaultListModel<>();
|
||||
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();
|
||||
private JTextPane contactsDisplay = new JTextPane();
|
||||
private PrimaryButton addContact = new PrimaryButton("+");
|
||||
|
||||
// Search Contacts
|
||||
private final JPanel searchPane = new JPanel();
|
||||
private final PrimaryButton cancelButton = new PrimaryButton("x");
|
||||
private final PrimaryTextArea searchField = new PrimaryTextArea(space);
|
||||
private final PrimaryScrollPane 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);
|
||||
|
||||
// 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 = 0L;
|
||||
|
||||
/**
|
||||
* Initializes a {@link JFrame} with UI elements used to send and read messages
|
||||
* to different users.
|
||||
*
|
||||
* @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")));
|
||||
|
||||
contentPane.setBorder(new EmptyBorder(space, space, space, space));
|
||||
setContentPane(contentPane);
|
||||
GridBagLayout gbl_contentPane = new GridBagLayout();
|
||||
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.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() {
|
||||
|
||||
// Update list elements when scroll pane (and thus list) is resized
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
messageList.setMaximumSize(new Dimension(scrollPane.getWidth(), Integer.MAX_VALUE));
|
||||
messageList.synchronizeModel();
|
||||
}
|
||||
});
|
||||
|
||||
GridBagConstraints gbc_scrollPane = new GridBagConstraints();
|
||||
gbc_scrollPane.fill = GridBagConstraints.BOTH;
|
||||
gbc_scrollPane.gridwidth = 2;
|
||||
gbc_scrollPane.gridheight = 2;
|
||||
gbc_scrollPane.gridx = 1;
|
||||
gbc_scrollPane.gridy = 1;
|
||||
|
||||
gbc_scrollPane.insets = insets;
|
||||
|
||||
drawChatBox(gbc_scrollPane);
|
||||
|
||||
// MessageEnterTextArea
|
||||
messageEnterTextArea.addInputMethodListener(new InputMethodListener() {
|
||||
|
||||
@Override
|
||||
public void inputMethodTextChanged(InputMethodEvent event) {
|
||||
checkMessageTextLength();
|
||||
checkPostButton(messageEnterTextArea.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void caretPositionChanged(InputMethodEvent event) {}
|
||||
});
|
||||
|
||||
messageEnterTextArea.addKeyListener(new KeyAdapter() {
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ENTER
|
||||
&& (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_messageEnterTextArea = new GridBagConstraints();
|
||||
gbc_messageEnterTextArea.fill = GridBagConstraints.BOTH;
|
||||
gbc_messageEnterTextArea.gridx = 1;
|
||||
gbc_messageEnterTextArea.gridy = 3;
|
||||
gbc_messageEnterTextArea.insets = insets;
|
||||
contentPane.add(messageEnterTextArea, gbc_messageEnterTextArea);
|
||||
|
||||
// Post Button
|
||||
GridBagConstraints gbc_postButton = new GridBagConstraints();
|
||||
gbc_postButton.fill = GridBagConstraints.BOTH;
|
||||
gbc_postButton.gridx = 2;
|
||||
gbc_postButton.gridy = 3;
|
||||
gbc_postButton.insets = insets;
|
||||
postButton.addActionListener((evt) -> { postMessage(); });
|
||||
postButton.setEnabled(false);
|
||||
contentPane.add(postButton, gbc_postButton);
|
||||
|
||||
// Settings Button
|
||||
GridBagConstraints gbc_moveSelectionSettingsButton = new GridBagConstraints();
|
||||
|
||||
gbc_moveSelectionSettingsButton.fill = GridBagConstraints.BOTH;
|
||||
gbc_moveSelectionSettingsButton.gridx = 2;
|
||||
gbc_moveSelectionSettingsButton.gridy = 0;
|
||||
|
||||
gbc_moveSelectionSettingsButton.insets = insets;
|
||||
|
||||
settingsButton.addActionListener(evt -> new SettingsScreen().setVisible(true));
|
||||
contentPane.add(settingsButton, gbc_moveSelectionSettingsButton);
|
||||
|
||||
// Partner name display
|
||||
textPane.setFont(new Font("Arial", Font.PLAIN, 20));
|
||||
textPane.setEditable(false);
|
||||
|
||||
GridBagConstraints gbc_partnerName = new GridBagConstraints();
|
||||
gbc_partnerName.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc_partnerName.gridx = 1;
|
||||
gbc_partnerName.gridy = 0;
|
||||
|
||||
gbc_partnerName.insets = insets;
|
||||
contentPane.add(textPane, gbc_partnerName);
|
||||
|
||||
userList.setCellRenderer(new UserListRenderer());
|
||||
userList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
userList.addListSelectionListener((listSelectionEvent) -> {
|
||||
if (client != null && localDB != null && !listSelectionEvent.getValueIsAdjusting()) {
|
||||
final JList<User> selectedUserList = (JList<User>) listSelectionEvent.getSource();
|
||||
final User user = selectedUserList.getSelectedValue();
|
||||
|
||||
for (int i = 0; i < contentPane.getComponents().length; i++)
|
||||
if (contentPane.getComponent(i).equals(searchPane)) drawChatBox(gbc_scrollPane);
|
||||
if (user != null) {
|
||||
// Select current chat
|
||||
currentChat = localDB.getChats().stream().filter(chat -> chat.getRecipient().getID() == user.getID()).findFirst().get();
|
||||
|
||||
// Read current chat
|
||||
readCurrentChat();
|
||||
|
||||
// Set chat title
|
||||
textPane.setText(currentChat.getRecipient().getName());
|
||||
|
||||
// Update model and scroll down
|
||||
// messageList.setModel(currentChat.getModel());
|
||||
scrollPane.setChatOpened(true);
|
||||
|
||||
messageList.synchronizeModel();
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
userList.setFont(new Font("Arial", Font.PLAIN, 17));
|
||||
userList.setBorder(new EmptyBorder(space, space, space, space));
|
||||
|
||||
GridBagConstraints gbc_userList = new GridBagConstraints();
|
||||
gbc_userList.fill = GridBagConstraints.VERTICAL;
|
||||
gbc_userList.gridx = 0;
|
||||
gbc_userList.gridy = 2;
|
||||
gbc_userList.gridheight = 2;
|
||||
gbc_userList.anchor = GridBagConstraints.PAGE_START;
|
||||
gbc_userList.insets = insets;
|
||||
|
||||
contentPane.add(userList, gbc_userList);
|
||||
contentPane.revalidate();
|
||||
|
||||
// Contacts Search
|
||||
GridBagConstraints gbc_searchPane = new GridBagConstraints();
|
||||
gbc_searchPane.fill = GridBagConstraints.BOTH;
|
||||
gbc_searchPane.gridwidth = 2;
|
||||
gbc_searchPane.gridheight = 2;
|
||||
gbc_searchPane.gridx = 1;
|
||||
gbc_searchPane.gridy = 1;
|
||||
|
||||
gbc_searchPane.insets = insets;
|
||||
|
||||
GridBagLayout gbl_contactsSearch = new GridBagLayout();
|
||||
gbl_contactsSearch.columnWidths = new int[] { 1, 1 };
|
||||
gbl_contactsSearch.rowHeights = new int[] { 1, 1 };
|
||||
gbl_contactsSearch.columnWeights = new double[] { 1, 0.1 };
|
||||
gbl_contactsSearch.rowWeights = new double[] { 0.001, 1 };
|
||||
searchPane.setLayout(gbl_contactsSearch);
|
||||
|
||||
GridBagConstraints gbc_searchField = new GridBagConstraints();
|
||||
gbc_searchField.fill = GridBagConstraints.BOTH;
|
||||
gbc_searchField.gridx = 0;
|
||||
gbc_searchField.gridy = 0;
|
||||
gbc_searchField.insets = new Insets(7, 4, 4, 4);
|
||||
|
||||
searchPane.add(searchField, gbc_searchField);
|
||||
|
||||
// Sends event to server, if input has changed
|
||||
searchField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent evt) {
|
||||
if (client.isOnline()) if (searchField.getText().isEmpty()) {
|
||||
contactsModel.clear();
|
||||
revalidate();
|
||||
repaint();
|
||||
} else try {
|
||||
client.sendEvent(new ContactSearchRequest(searchField.getText()));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent evt) {
|
||||
if (client.isOnline()) try {
|
||||
client.sendEvent(new ContactSearchRequest(searchField.getText()));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent evt) {}
|
||||
});
|
||||
|
||||
GridBagConstraints gbc_cancelButton = new GridBagConstraints();
|
||||
gbc_cancelButton.fill = GridBagConstraints.BOTH;
|
||||
gbc_cancelButton.gridx = 1;
|
||||
gbc_cancelButton.gridy = 0;
|
||||
gbc_cancelButton.insets = new Insets(7, 4, 4, 4);
|
||||
|
||||
cancelButton.addActionListener((evt) -> { drawChatBox(gbc_scrollPane); });
|
||||
|
||||
searchPane.add(cancelButton, gbc_cancelButton);
|
||||
|
||||
contactList.setModel(contactsModel);
|
||||
scrollForPossibleContacts.setBorder(new EmptyBorder(space, space, space, space));
|
||||
scrollForPossibleContacts.setViewportView(contactList);
|
||||
|
||||
GridBagConstraints gbc_possibleContacts = new GridBagConstraints();
|
||||
gbc_possibleContacts.fill = GridBagConstraints.BOTH;
|
||||
gbc_possibleContacts.gridwidth = 2;
|
||||
gbc_possibleContacts.gridx = 0;
|
||||
gbc_possibleContacts.gridy = 1;
|
||||
|
||||
gbc_possibleContacts.insets = insets;
|
||||
|
||||
searchPane.add(scrollForPossibleContacts, gbc_possibleContacts);
|
||||
|
||||
// Contacts Header
|
||||
GridBagConstraints gbc_contactsHeader = new GridBagConstraints();
|
||||
gbc_contactsHeader.fill = GridBagConstraints.BOTH;
|
||||
gbc_contactsHeader.gridx = 0;
|
||||
gbc_contactsHeader.gridy = 1;
|
||||
gbc_contactsHeader.insets = insets;
|
||||
|
||||
GridBagLayout gbl_contactHeader = new GridBagLayout();
|
||||
gbl_contactHeader.columnWidths = new int[] { 1, 1 };
|
||||
gbl_contactHeader.rowHeights = new int[] { 1 };
|
||||
gbl_contactHeader.columnWeights = new double[] { 1, 1 };
|
||||
gbl_contactHeader.rowWeights = new double[] { 1 };
|
||||
contactsHeader.setLayout(gbl_contactHeader);
|
||||
|
||||
contactsDisplay.setEditable(false);
|
||||
contactsDisplay.setFont(new Font("Arial", Font.PLAIN, 12));
|
||||
contactsDisplay.setText("Contacts");
|
||||
|
||||
GridBagConstraints gbc_contactsDisplay = new GridBagConstraints();
|
||||
gbc_contactsDisplay.fill = GridBagConstraints.BOTH;
|
||||
gbc_contactsDisplay.gridx = 0;
|
||||
gbc_contactsDisplay.gridy = 0;
|
||||
|
||||
contactsHeader.add(contactsDisplay, gbc_contactsDisplay);
|
||||
|
||||
addContact.setFont(new Font("Arial", Font.PLAIN, 15));
|
||||
|
||||
GridBagConstraints gbc_addContact = new GridBagConstraints();
|
||||
gbc_addContact.fill = GridBagConstraints.BOTH;
|
||||
gbc_addContact.gridx = 1;
|
||||
gbc_addContact.gridy = 0;
|
||||
gbc_addContact.insets = insets;
|
||||
|
||||
addContact.addActionListener(evt -> drawContactSearch(gbc_searchPane));
|
||||
|
||||
contactsHeader.add(addContact, gbc_addContact);
|
||||
|
||||
applyTheme(Settings.getInstance().getCurrentTheme());
|
||||
|
||||
contentPane.add(contactsHeader, gbc_contactsHeader);
|
||||
contentPane.revalidate();
|
||||
|
||||
// Listen to theme changes
|
||||
EventBus.getInstance().register(ThemeChangeEvent.class, evt -> applyTheme(evt.get()));
|
||||
|
||||
// Listen to user status changes
|
||||
EventBus.getInstance().register(UserStatusChangeEvent.class, evt -> { userList.revalidate(); userList.repaint(); });
|
||||
|
||||
// Listen to received messages
|
||||
EventBus.getInstance().register(MessageCreationEvent.class, evt -> {
|
||||
Message message = evt.get();
|
||||
Chat chat = localDB.getChats().stream().filter(c -> c.getRecipient().getID() == message.getSenderID()).findFirst().get();
|
||||
// chat.appendMessage(message);
|
||||
|
||||
// Read message and update UI if in current chat
|
||||
if (chat == currentChat) readCurrentChat();
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
});
|
||||
|
||||
// Listen to message status changes
|
||||
EventBus.getInstance().register(MessageStatusChangeEvent.class, evt -> {
|
||||
final long id = evt.getID();
|
||||
final MessageStatus status = evt.get();
|
||||
|
||||
for (Chat c : localDB.getChats())
|
||||
// for (Message m : c.getModel())
|
||||
// if (m.getID() == id) {
|
||||
//
|
||||
// // Update message status
|
||||
// m.setStatus(status);
|
||||
//
|
||||
// // Update model and scroll down if current chat
|
||||
// if (c == currentChat) {
|
||||
// messageList.setModel(currentChat.getModel());
|
||||
// scrollPane.setChatOpened(true);
|
||||
// } else messageList.synchronizeModel();
|
||||
// }
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
});
|
||||
|
||||
// Listen to contact search results
|
||||
EventBus.getInstance()
|
||||
.register(ContactSearchResult.class,
|
||||
evt -> {
|
||||
contactsModel.clear();
|
||||
final java.util.List<User> contacts = evt.get();
|
||||
contacts.forEach(contactsModel::add);
|
||||
revalidate();
|
||||
repaint();
|
||||
});
|
||||
|
||||
// Add new contacts to the contact list
|
||||
EventBus.getInstance().register(ContactOperationEvent.class, evt -> {
|
||||
User contact = evt.get();
|
||||
|
||||
// Clearing the search field and the searchResultList
|
||||
searchField.setText("");
|
||||
contactsModel.clear();
|
||||
|
||||
// Update LocalDB
|
||||
userListModel.addElement(contact);
|
||||
localDB.getUsers().put(contact.getName(), contact);
|
||||
localDB.getChats().add(new Chat(contact));
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
});
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to immediately reload the {@link ChatWindow} when settings were changed.
|
||||
*
|
||||
* @param theme the theme to change colors into
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
private void applyTheme(Theme theme) {
|
||||
// contentPane
|
||||
contentPane.setBackground(theme.getBackgroundColor());
|
||||
contentPane.setForeground(theme.getUserNameColor());
|
||||
// messageList
|
||||
messageList.setForeground(theme.getTextColor());
|
||||
messageList.setBackground(theme.getCellColor());
|
||||
messageList.synchronizeModel();
|
||||
// scrollPane
|
||||
scrollPane.applyTheme(theme);
|
||||
scrollPane.autoscroll();
|
||||
// messageEnterTextArea
|
||||
messageEnterTextArea.setCaretColor(theme.getTypingMessageColor());
|
||||
messageEnterTextArea.setForeground(theme.getTypingMessageColor());
|
||||
messageEnterTextArea.setBackground(theme.getCellColor());
|
||||
// postButton
|
||||
postButton.setForeground(theme.getInteractableForegroundColor());
|
||||
postButton.setBackground(theme.getInteractableBackgroundColor());
|
||||
// settingsButton
|
||||
settingsButton.setForeground(theme.getInteractableForegroundColor());
|
||||
settingsButton.setBackground(theme.getInteractableBackgroundColor());
|
||||
// textPane
|
||||
textPane.setBackground(theme.getBackgroundColor());
|
||||
textPane.setForeground(theme.getUserNameColor());
|
||||
// userList
|
||||
userList.setSelectionForeground(theme.getUserNameColor());
|
||||
userList.setSelectionBackground(theme.getSelectionColor());
|
||||
userList.setForeground(theme.getUserNameColor());
|
||||
userList.setBackground(theme.getCellColor());
|
||||
// contacts header
|
||||
contactsHeader.setBackground(theme.getCellColor());
|
||||
contactsDisplay.setBackground(theme.getCellColor());
|
||||
contactsDisplay.setForeground(theme.getUserNameColor());
|
||||
addContact.setBackground(theme.getInteractableBackgroundColor());
|
||||
addContact.setForeground(theme.getInteractableForegroundColor());
|
||||
// SearchPane
|
||||
searchPane.setBackground(theme.getCellColor());
|
||||
searchField.setBackground(theme.getBackgroundColor());
|
||||
searchField.setForeground(theme.getUserNameColor());
|
||||
cancelButton.setBackground(theme.getInteractableBackgroundColor());
|
||||
cancelButton.setForeground(theme.getInteractableForegroundColor());
|
||||
contactList.setForeground(theme.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();
|
||||
|
||||
// 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);
|
||||
|
||||
// Update UI
|
||||
revalidate();
|
||||
repaint();
|
||||
|
||||
// Request a new id generator if all IDs were used
|
||||
if (!localDB.getIDGenerator().hasNext()) client.requestIdGenerator();
|
||||
|
||||
} catch (Exception e) {
|
||||
JOptionPane.showMessageDialog(this, "Error sending message:\n" + e.toString(), "Message sending error", JOptionPane.ERROR_MESSAGE);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void readCurrentChat() {
|
||||
try {
|
||||
currentChat.read(writeProxy);
|
||||
if (messageList.getRenderer() != null) messageList.synchronizeModel();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
logger.log(Level.WARNING, "Couldn't notify server about message status change", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawChatBox(GridBagConstraints gbc_scrollPane) {
|
||||
contentPane.remove(searchPane);
|
||||
contentPane.add(scrollPane, gbc_scrollPane);
|
||||
contentPane.revalidate();
|
||||
contentPane.repaint();
|
||||
}
|
||||
|
||||
private void drawContactSearch(GridBagConstraints gbc_searchPane) {
|
||||
currentChat = null;
|
||||
userList.removeSelectionInterval(0, userList.getModel().getSize() - 1);
|
||||
messageList.setModel(null);
|
||||
textPane.setText("");
|
||||
contentPane.remove(scrollPane);
|
||||
contentPane.add(searchPane, gbc_searchPane);
|
||||
contentPane.revalidate();
|
||||
contentPane.repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the components responsible server communication and
|
||||
* persistence.<br>
|
||||
* <br>
|
||||
* This will trigger the display of the contact list.
|
||||
*
|
||||
* @param client the client used to send and receive messages
|
||||
* @param localDB the local database used to manage stored messages
|
||||
* and users
|
||||
* @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 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 -> {
|
||||
userListModel.addElement(user);
|
||||
|
||||
// Check if user exists in local DB
|
||||
if (localDB.getChats().stream().noneMatch(c -> c.getRecipient().getID() == user.getID())) localDB.getChats().add(new Chat(user));
|
||||
});
|
||||
SwingUtilities.invokeLater(() -> userList.setModel(userListModel));
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the length of the text inside messageEnterTextArea >=
|
||||
* {@link ChatWindow#MAX_MESSAGE_LENGTH}
|
||||
* and splits the text into the allowed part, if that is the case.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
private void checkMessageTextLength() {
|
||||
String input = messageEnterTextArea.getText();
|
||||
if (input.length() >= MAX_MESSAGE_LENGTH) {
|
||||
messageEnterTextArea.setText(input.substring(0, MAX_MESSAGE_LENGTH - 1));
|
||||
// TODO: current notification is like being hit with a hammer, maybe it should
|
||||
// be replaced with a more subtle notification
|
||||
JOptionPane.showMessageDialog(messageEnterTextArea,
|
||||
"the maximum length for a message has been reached",
|
||||
"maximum message length reached",
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPostButton(String text) { postButton.setEnabled(!text.trim().isBlank()); }
|
||||
}
|
@ -1,145 +0,0 @@
|
||||
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 = 0L;
|
||||
|
||||
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); }
|
||||
}
|
@ -1,255 +0,0 @@
|
||||
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 = 0L;
|
||||
|
||||
/**
|
||||
* 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,261 +0,0 @@
|
||||
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 Model}. 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 Client v0.3-alpha
|
||||
*/
|
||||
public class ComponentList<E> extends JPanel {
|
||||
|
||||
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 = 0L;
|
||||
|
||||
/**
|
||||
* Defines the possible modes of selection that can be performed by the user
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
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}.
|
||||
*
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public ComponentList() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); }
|
||||
|
||||
/**
|
||||
* Removes all child components and then adds all components representing the
|
||||
* elements of the {@link Model}.
|
||||
*
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public void synchronizeModel() {
|
||||
if (model != null) {
|
||||
removeAll();
|
||||
model.forEach(this::addElement);
|
||||
revalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Client v0.1-beta
|
||||
*/
|
||||
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);
|
||||
|
||||
} else {
|
||||
|
||||
// Remove old selection if single selection is enabled
|
||||
if (selectionMode == SelectionMode.SINGLE) clearSelection();
|
||||
|
||||
// Select item
|
||||
if (selectionMode != SelectionMode.NONE) {
|
||||
|
||||
// Assign new selection
|
||||
selection.add(index);
|
||||
|
||||
// Update element
|
||||
if (selectionHandler != null) selectionHandler.selectionChanged(model.get(index), element, true);
|
||||
}
|
||||
}
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the current selection.
|
||||
*
|
||||
* @since Envoy Client v0.1-alpha
|
||||
*/
|
||||
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; }
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
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>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 Client v0.3-alpha
|
||||
*/
|
||||
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 = 0L;
|
||||
|
||||
/**
|
||||
* 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 Client v0.3-alpha
|
||||
*/
|
||||
public boolean add(E e) {
|
||||
if (componentList != null) {
|
||||
componentList.addElement(e);
|
||||
componentList.revalidate();
|
||||
}
|
||||
return elements.add(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all elements from this model and clears the associated
|
||||
* {@link ComponentList}.
|
||||
*
|
||||
* @see java.util.List#clear()
|
||||
* @since Envoy Client 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 Client 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 Client 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 Client v0.3-alpha
|
||||
*/
|
||||
public int size() { return elements.size(); }
|
||||
|
||||
/**
|
||||
* @return {@code true} if this model contains no elements
|
||||
* @see java.util.List#isEmpty()
|
||||
*/
|
||||
public boolean isEmpty() { return elements.isEmpty(); }
|
||||
|
||||
/**
|
||||
* @return an iterator over the elements of this list model
|
||||
* @see java.util.List#iterator()
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new Iterator<>() {
|
||||
|
||||
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 Client v0.3-alpha
|
||||
*/
|
||||
void setComponentList(ComponentList<E> componentList) {
|
||||
this.componentList = componentList;
|
||||
if (componentList != null && componentList.getRenderer() != null) componentList.synchronizeModel();
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
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>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 Client v0.3-alpha
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Renderer<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 Client v0.3-alpha
|
||||
*/
|
||||
JComponent getListCellComponent(ComponentList<? extends E> list, E value);
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
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);
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
/**
|
||||
* This package defines a Swing component that can be used to display lists of
|
||||
* other components to the user.
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Leon Hofmeister
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
package envoy.client.ui.list;
|
@ -1,73 +0,0 @@
|
||||
package envoy.client.ui.list_component;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.client.event.SendEvent;
|
||||
import envoy.client.ui.list.ComponentList;
|
||||
import envoy.client.ui.primary.PrimaryButton;
|
||||
import envoy.data.User;
|
||||
import envoy.event.ContactOperationEvent;
|
||||
import envoy.event.EventBus;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong>
|
||||
* File: <strong>ContactSearchComponent.java</strong>
|
||||
* Created: <strong>21.03.2020</strong>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public class ContactSearchComponent extends JComponent {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* @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().getCurrentTheme().getTextColor());
|
||||
display.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
display.setAlignmentY(Component.CENTER_ALIGNMENT);
|
||||
display.setFont(new Font("Arial", Font.PLAIN, 16));
|
||||
add(display);
|
||||
|
||||
PrimaryButton add = new PrimaryButton("+");
|
||||
add.setFont(new Font("Arial", Font.PLAIN, 19));
|
||||
add.setPreferredSize(new Dimension(45, 45));
|
||||
add.setMinimumSize(new Dimension(45, 45));
|
||||
add.setMaximumSize(new Dimension(45, 45));
|
||||
|
||||
add.setBackground(list.getBackground());
|
||||
add.setForeground(list.getForeground());
|
||||
|
||||
add.addActionListener(evt -> {
|
||||
ContactOperationEvent contactsOperationEvent = new ContactOperationEvent(user, ContactOperationEvent.Operation.ADD);
|
||||
EventBus.getInstance().dispatch(contactsOperationEvent);
|
||||
EventBus.getInstance().dispatch(new SendEvent(contactsOperationEvent));
|
||||
});
|
||||
|
||||
add(add);
|
||||
|
||||
// Define some space to the messages below
|
||||
setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 0, 15, 0), BorderFactory.createEtchedBorder()));
|
||||
|
||||
// Define a maximum height of 50px
|
||||
Dimension size = new Dimension(435, 50);
|
||||
setMaximumSize(size);
|
||||
setMinimumSize(size);
|
||||
setPreferredSize(size);
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
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 = 0L;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
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 = 0L;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
/**
|
||||
* 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;
|
@ -1,65 +0,0 @@
|
||||
package envoy.client.ui.renderer;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.ListCellRenderer;
|
||||
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.data.User;
|
||||
import envoy.data.User.UserStatus;
|
||||
|
||||
/**
|
||||
* Defines how the {@code UserList} is displayed.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>UserListRenderer.java</strong><br>
|
||||
* Created: <strong>12 Oct 2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.1-alpha
|
||||
*/
|
||||
public class UserListRenderer extends JLabel implements ListCellRenderer<User> {
|
||||
|
||||
private static final long serialVersionUID = 5164417379767181198L;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList<? extends User> list, User value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
if (isSelected) {
|
||||
setBackground(list.getSelectionBackground());
|
||||
setForeground(list.getSelectionForeground());
|
||||
} else {
|
||||
setBackground(list.getBackground());
|
||||
setForeground(list.getForeground());
|
||||
}
|
||||
|
||||
// Enable background rendering
|
||||
setOpaque(true);
|
||||
|
||||
final String name = value.getName();
|
||||
final UserStatus status = value.getStatus();
|
||||
|
||||
this.setPreferredSize(new Dimension(100, 35));
|
||||
|
||||
// Getting the UserNameColor of the current theme
|
||||
String textColor = null;
|
||||
textColor = Settings.getInstance().getCurrentTheme().getUserNameColor().toHex();
|
||||
switch (status) {
|
||||
case ONLINE:
|
||||
setText(String
|
||||
.format("<html><p style=\"color:#03fc20\"><b><small>%s</b></small><br><p style=\"color:%s\">%s</html>", status, textColor, name));
|
||||
break;
|
||||
case OFFLINE:
|
||||
setText(String
|
||||
.format("<html><p style=\"color:#fc0303\"><b><small>%s</b></small><br><p style=\"color:%s\">%s</html>", status, textColor, name));
|
||||
break;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
/**
|
||||
* 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;
|
Reference in New Issue
Block a user