Removed most Swing components
This commit is contained in:
		@@ -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