Removed most Swing components
This commit is contained in:
parent
b5fd690f43
commit
c12eac1cbc
@ -1,64 +0,0 @@
|
|||||||
package envoy.client;
|
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import envoy.client.ui.container.ChatWindow;
|
|
||||||
import envoy.data.Config;
|
|
||||||
import envoy.util.EnvoyLog;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the Envoy client and prompts the user to enter their name.<br>
|
|
||||||
* <br>
|
|
||||||
* Project: <strong>envoy-client</strong><br>
|
|
||||||
* File: <strong>Startup.java</strong><br>
|
|
||||||
* Created: <strong>12 Oct 2019</strong><br>
|
|
||||||
*
|
|
||||||
* @author Leon Hofmeister
|
|
||||||
* @author Maximilian Käfer
|
|
||||||
* @author Kai S. K. Engelbart
|
|
||||||
* @since Envoy Client v0.1-alpha
|
|
||||||
*/
|
|
||||||
public class Startup {
|
|
||||||
|
|
||||||
private static final Logger logger = EnvoyLog.getLogger(Startup.class);
|
|
||||||
|
|
||||||
// TODO: Update Javadoc
|
|
||||||
/**
|
|
||||||
* Loads the application by first loading the configuration, then acquiring a
|
|
||||||
* user name and connecting to the server. If the server cannot be reached,
|
|
||||||
* offline mode is entered if possible. After that, a {@link ChatWindow}
|
|
||||||
* instance is initialized and then displayed to the user. Upon application
|
|
||||||
* exit, settings and the local database are saved.
|
|
||||||
*
|
|
||||||
* @param args the command line arguments may contain configuration parameters
|
|
||||||
* and are parsed by the {@link Config} class
|
|
||||||
* @since Envoy Client v0.1-alpha
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
|
||||||
// Display ChatWindow and StatusTrayIcon
|
|
||||||
// EventQueue.invokeLater(() -> {
|
|
||||||
// try {
|
|
||||||
// chatWindow.initContent(client, localDB, writeProxy);
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// try {
|
|
||||||
// new StatusTrayIcon(chatWindow).show();
|
|
||||||
//
|
|
||||||
// // If the tray icon is supported and corresponding settings is set, hide the
|
|
||||||
// // chat window on close
|
|
||||||
// Settings.getInstance()
|
|
||||||
// .getItems()
|
|
||||||
// .get("onCloseMode")
|
|
||||||
// .setChangeHandler((onCloseMode) -> chatWindow
|
|
||||||
// .setDefaultCloseOperation((Boolean) onCloseMode ? JFrame.HIDE_ON_CLOSE :
|
|
||||||
// JFrame.EXIT_ON_CLOSE));
|
|
||||||
// } catch (EnvoyException e) {
|
|
||||||
// logger.warning("The StatusTrayIcon is not supported on this platform!");
|
|
||||||
// }
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// e.printStackTrace();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,687 +0,0 @@
|
|||||||
package envoy.client.ui.container;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.datatransfer.StringSelection;
|
|
||||||
import java.awt.event.*;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.border.EmptyBorder;
|
|
||||||
import javax.swing.event.DocumentEvent;
|
|
||||||
import javax.swing.event.DocumentListener;
|
|
||||||
|
|
||||||
import envoy.client.data.Chat;
|
|
||||||
import envoy.client.data.LocalDB;
|
|
||||||
import envoy.client.data.Settings;
|
|
||||||
import envoy.client.event.MessageCreationEvent;
|
|
||||||
import envoy.client.event.ThemeChangeEvent;
|
|
||||||
import envoy.client.net.Client;
|
|
||||||
import envoy.client.net.WriteProxy;
|
|
||||||
import envoy.client.ui.Theme;
|
|
||||||
import envoy.client.ui.list.ComponentList;
|
|
||||||
import envoy.client.ui.list.ComponentList.SelectionMode;
|
|
||||||
import envoy.client.ui.list.Model;
|
|
||||||
import envoy.client.ui.list_component.ContactSearchComponent;
|
|
||||||
import envoy.client.ui.list_component.MessageComponent;
|
|
||||||
import envoy.client.ui.primary.PrimaryButton;
|
|
||||||
import envoy.client.ui.primary.PrimaryScrollPane;
|
|
||||||
import envoy.client.ui.primary.PrimaryTextArea;
|
|
||||||
import envoy.client.ui.renderer.UserListRenderer;
|
|
||||||
import envoy.client.ui.settings.SettingsScreen;
|
|
||||||
import envoy.data.Message;
|
|
||||||
import envoy.data.Message.MessageStatus;
|
|
||||||
import envoy.data.MessageBuilder;
|
|
||||||
import envoy.data.User;
|
|
||||||
import envoy.event.*;
|
|
||||||
import envoy.util.EnvoyLog;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Project: <strong>envoy-client</strong><br>
|
|
||||||
* File: <strong>ChatWindow.java</strong><br>
|
|
||||||
* Created: <strong>28 Sep 2019</strong><br>
|
|
||||||
*
|
|
||||||
* @author Kai S. K. Engelbart
|
|
||||||
* @author Maximilian Käfer
|
|
||||||
* @author Leon Hofmeister
|
|
||||||
* @since Envoy Client v0.1-alpha
|
|
||||||
*/
|
|
||||||
public class ChatWindow extends JFrame {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This integer defines the maximum amount of chars allowed per message.
|
|
||||||
*
|
|
||||||
* @since Envoy 0.1-beta
|
|
||||||
*/
|
|
||||||
public static final int MAX_MESSAGE_LENGTH = 200;
|
|
||||||
|
|
||||||
// User specific objects
|
|
||||||
private Client client;
|
|
||||||
private WriteProxy writeProxy;
|
|
||||||
private LocalDB localDB;
|
|
||||||
private Chat currentChat;
|
|
||||||
|
|
||||||
// GUI components
|
|
||||||
private JPanel contentPane = new JPanel();
|
|
||||||
private PrimaryTextArea messageEnterTextArea = new PrimaryTextArea(space);
|
|
||||||
private JList<User> userList = new JList<>();
|
|
||||||
private DefaultListModel<User> userListModel = new DefaultListModel<>();
|
|
||||||
private ComponentList<Message> messageList = new ComponentList<>();
|
|
||||||
private PrimaryScrollPane scrollPane = new PrimaryScrollPane();
|
|
||||||
private JTextPane textPane = new JTextPane();
|
|
||||||
private PrimaryButton postButton = new PrimaryButton("Post");
|
|
||||||
private PrimaryButton settingsButton = new PrimaryButton("Settings");
|
|
||||||
private JPopupMenu contextMenu;
|
|
||||||
|
|
||||||
// Contacts Header
|
|
||||||
private JPanel contactsHeader = new JPanel();
|
|
||||||
private JTextPane contactsDisplay = new JTextPane();
|
|
||||||
private PrimaryButton addContact = new PrimaryButton("+");
|
|
||||||
|
|
||||||
// Search Contacts
|
|
||||||
private final JPanel searchPane = new JPanel();
|
|
||||||
private final PrimaryButton cancelButton = new PrimaryButton("x");
|
|
||||||
private final PrimaryTextArea searchField = new PrimaryTextArea(space);
|
|
||||||
private final PrimaryScrollPane scrollForPossibleContacts = new PrimaryScrollPane();
|
|
||||||
private final Model<User> contactsModel = new Model<>();
|
|
||||||
private final ComponentList<User> contactList = new ComponentList<User>().setRenderer(ContactSearchComponent::new);
|
|
||||||
|
|
||||||
private static final Logger logger = EnvoyLog.getLogger(ChatWindow.class);
|
|
||||||
|
|
||||||
// GUI component spacing
|
|
||||||
private final static int space = 4;
|
|
||||||
private static final Insets insets = new Insets(space, space, space, space);
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 0L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a {@link JFrame} with UI elements used to send and read messages
|
|
||||||
* to different users.
|
|
||||||
*
|
|
||||||
* @since Envoy Client v0.1-alpha
|
|
||||||
*/
|
|
||||||
public ChatWindow() {
|
|
||||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
|
||||||
setBounds(100, 100, 600, 800);
|
|
||||||
setMinimumSize(new Dimension(400, 300));
|
|
||||||
setTitle("Envoy");
|
|
||||||
setLocationRelativeTo(null);
|
|
||||||
setIconImage(Toolkit.getDefaultToolkit().createImage(getClass().getClassLoader().getResource("envoy_logo.png")));
|
|
||||||
|
|
||||||
contentPane.setBorder(new EmptyBorder(space, space, space, space));
|
|
||||||
setContentPane(contentPane);
|
|
||||||
GridBagLayout gbl_contentPane = new GridBagLayout();
|
|
||||||
gbl_contentPane.columnWidths = new int[] { 1, 1, 1 };
|
|
||||||
gbl_contentPane.rowHeights = new int[] { 1, 1, 1, 1 };
|
|
||||||
gbl_contentPane.columnWeights = new double[] { 0.03, 1.0, 0.1 };
|
|
||||||
gbl_contentPane.rowWeights = new double[] { 0.03, 0.001, 1.0, 0.001 };
|
|
||||||
contentPane.setLayout(gbl_contentPane);
|
|
||||||
|
|
||||||
messageList.setBorder(new EmptyBorder(space, space, space, space));
|
|
||||||
messageList.setSelectionMode(SelectionMode.SINGLE);
|
|
||||||
messageList.setSelectionHandler((message, comp, isSelected) -> {
|
|
||||||
final var theme = Settings.getInstance().getCurrentTheme();
|
|
||||||
comp.setBackground(isSelected ? theme.getSelectionColor() : theme.getCellColor());
|
|
||||||
|
|
||||||
// ContextMenu
|
|
||||||
Map<String, ActionListener> commands = Map.of("forward selected message", evt -> {
|
|
||||||
final Message selectedMessage = messageList.getSingleSelectedElement();
|
|
||||||
List<User> chosenContacts = ContactsChooserDialog
|
|
||||||
.showForwardingDialog("Forward selected message to", null, selectedMessage, localDB.getUsers().values());
|
|
||||||
if (chosenContacts != null && chosenContacts.size() > 0) forwardMessage(selectedMessage, chosenContacts.toArray(new User[0]));
|
|
||||||
}, "copy", evt -> {
|
|
||||||
// TODO should be enhanced to allow also copying of message attachments,
|
|
||||||
// especially pictures
|
|
||||||
StringSelection copy = new StringSelection(messageList.getSingleSelectedElement().getText());
|
|
||||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(copy, copy);
|
|
||||||
// TODO insert implementation to edit and delete messages
|
|
||||||
}, "delete", evt -> {}, "edit", evt -> {}, "quote", evt -> {});
|
|
||||||
|
|
||||||
if (isSelected) {
|
|
||||||
contextMenu = new ContextMenu(null, comp, commands, null, null).build();
|
|
||||||
contextMenu.show(comp, 0, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
scrollPane.setViewportView(messageList);
|
|
||||||
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
|
||||||
scrollPane.addComponentListener(new ComponentAdapter() {
|
|
||||||
|
|
||||||
// Update list elements when scroll pane (and thus list) is resized
|
|
||||||
@Override
|
|
||||||
public void componentResized(ComponentEvent e) {
|
|
||||||
messageList.setMaximumSize(new Dimension(scrollPane.getWidth(), Integer.MAX_VALUE));
|
|
||||||
messageList.synchronizeModel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
GridBagConstraints gbc_scrollPane = new GridBagConstraints();
|
|
||||||
gbc_scrollPane.fill = GridBagConstraints.BOTH;
|
|
||||||
gbc_scrollPane.gridwidth = 2;
|
|
||||||
gbc_scrollPane.gridheight = 2;
|
|
||||||
gbc_scrollPane.gridx = 1;
|
|
||||||
gbc_scrollPane.gridy = 1;
|
|
||||||
|
|
||||||
gbc_scrollPane.insets = insets;
|
|
||||||
|
|
||||||
drawChatBox(gbc_scrollPane);
|
|
||||||
|
|
||||||
// MessageEnterTextArea
|
|
||||||
messageEnterTextArea.addInputMethodListener(new InputMethodListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void inputMethodTextChanged(InputMethodEvent event) {
|
|
||||||
checkMessageTextLength();
|
|
||||||
checkPostButton(messageEnterTextArea.getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void caretPositionChanged(InputMethodEvent event) {}
|
|
||||||
});
|
|
||||||
|
|
||||||
messageEnterTextArea.addKeyListener(new KeyAdapter() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void keyReleased(KeyEvent e) {
|
|
||||||
if (e.getKeyCode() == KeyEvent.VK_ENTER
|
|
||||||
&& (Settings.getInstance().isEnterToSend() && e.getModifiersEx() == 0 || e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK)
|
|
||||||
&& postButton.isEnabled())
|
|
||||||
postMessage();
|
|
||||||
// Checking if text is too long
|
|
||||||
checkMessageTextLength();
|
|
||||||
checkPostButton(messageEnterTextArea.getText());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
GridBagConstraints gbc_messageEnterTextArea = new GridBagConstraints();
|
|
||||||
gbc_messageEnterTextArea.fill = GridBagConstraints.BOTH;
|
|
||||||
gbc_messageEnterTextArea.gridx = 1;
|
|
||||||
gbc_messageEnterTextArea.gridy = 3;
|
|
||||||
gbc_messageEnterTextArea.insets = insets;
|
|
||||||
contentPane.add(messageEnterTextArea, gbc_messageEnterTextArea);
|
|
||||||
|
|
||||||
// Post Button
|
|
||||||
GridBagConstraints gbc_postButton = new GridBagConstraints();
|
|
||||||
gbc_postButton.fill = GridBagConstraints.BOTH;
|
|
||||||
gbc_postButton.gridx = 2;
|
|
||||||
gbc_postButton.gridy = 3;
|
|
||||||
gbc_postButton.insets = insets;
|
|
||||||
postButton.addActionListener((evt) -> { postMessage(); });
|
|
||||||
postButton.setEnabled(false);
|
|
||||||
contentPane.add(postButton, gbc_postButton);
|
|
||||||
|
|
||||||
// Settings Button
|
|
||||||
GridBagConstraints gbc_moveSelectionSettingsButton = new GridBagConstraints();
|
|
||||||
|
|
||||||
gbc_moveSelectionSettingsButton.fill = GridBagConstraints.BOTH;
|
|
||||||
gbc_moveSelectionSettingsButton.gridx = 2;
|
|
||||||
gbc_moveSelectionSettingsButton.gridy = 0;
|
|
||||||
|
|
||||||
gbc_moveSelectionSettingsButton.insets = insets;
|
|
||||||
|
|
||||||
settingsButton.addActionListener(evt -> new SettingsScreen().setVisible(true));
|
|
||||||
contentPane.add(settingsButton, gbc_moveSelectionSettingsButton);
|
|
||||||
|
|
||||||
// Partner name display
|
|
||||||
textPane.setFont(new Font("Arial", Font.PLAIN, 20));
|
|
||||||
textPane.setEditable(false);
|
|
||||||
|
|
||||||
GridBagConstraints gbc_partnerName = new GridBagConstraints();
|
|
||||||
gbc_partnerName.fill = GridBagConstraints.HORIZONTAL;
|
|
||||||
gbc_partnerName.gridx = 1;
|
|
||||||
gbc_partnerName.gridy = 0;
|
|
||||||
|
|
||||||
gbc_partnerName.insets = insets;
|
|
||||||
contentPane.add(textPane, gbc_partnerName);
|
|
||||||
|
|
||||||
userList.setCellRenderer(new UserListRenderer());
|
|
||||||
userList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
|
||||||
userList.addListSelectionListener((listSelectionEvent) -> {
|
|
||||||
if (client != null && localDB != null && !listSelectionEvent.getValueIsAdjusting()) {
|
|
||||||
final JList<User> selectedUserList = (JList<User>) listSelectionEvent.getSource();
|
|
||||||
final User user = selectedUserList.getSelectedValue();
|
|
||||||
|
|
||||||
for (int i = 0; i < contentPane.getComponents().length; i++)
|
|
||||||
if (contentPane.getComponent(i).equals(searchPane)) drawChatBox(gbc_scrollPane);
|
|
||||||
if (user != null) {
|
|
||||||
// Select current chat
|
|
||||||
currentChat = localDB.getChats().stream().filter(chat -> chat.getRecipient().getID() == user.getID()).findFirst().get();
|
|
||||||
|
|
||||||
// Read current chat
|
|
||||||
readCurrentChat();
|
|
||||||
|
|
||||||
// Set chat title
|
|
||||||
textPane.setText(currentChat.getRecipient().getName());
|
|
||||||
|
|
||||||
// Update model and scroll down
|
|
||||||
// messageList.setModel(currentChat.getModel());
|
|
||||||
scrollPane.setChatOpened(true);
|
|
||||||
|
|
||||||
messageList.synchronizeModel();
|
|
||||||
revalidate();
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
userList.setFont(new Font("Arial", Font.PLAIN, 17));
|
|
||||||
userList.setBorder(new EmptyBorder(space, space, space, space));
|
|
||||||
|
|
||||||
GridBagConstraints gbc_userList = new GridBagConstraints();
|
|
||||||
gbc_userList.fill = GridBagConstraints.VERTICAL;
|
|
||||||
gbc_userList.gridx = 0;
|
|
||||||
gbc_userList.gridy = 2;
|
|
||||||
gbc_userList.gridheight = 2;
|
|
||||||
gbc_userList.anchor = GridBagConstraints.PAGE_START;
|
|
||||||
gbc_userList.insets = insets;
|
|
||||||
|
|
||||||
contentPane.add(userList, gbc_userList);
|
|
||||||
contentPane.revalidate();
|
|
||||||
|
|
||||||
// Contacts Search
|
|
||||||
GridBagConstraints gbc_searchPane = new GridBagConstraints();
|
|
||||||
gbc_searchPane.fill = GridBagConstraints.BOTH;
|
|
||||||
gbc_searchPane.gridwidth = 2;
|
|
||||||
gbc_searchPane.gridheight = 2;
|
|
||||||
gbc_searchPane.gridx = 1;
|
|
||||||
gbc_searchPane.gridy = 1;
|
|
||||||
|
|
||||||
gbc_searchPane.insets = insets;
|
|
||||||
|
|
||||||
GridBagLayout gbl_contactsSearch = new GridBagLayout();
|
|
||||||
gbl_contactsSearch.columnWidths = new int[] { 1, 1 };
|
|
||||||
gbl_contactsSearch.rowHeights = new int[] { 1, 1 };
|
|
||||||
gbl_contactsSearch.columnWeights = new double[] { 1, 0.1 };
|
|
||||||
gbl_contactsSearch.rowWeights = new double[] { 0.001, 1 };
|
|
||||||
searchPane.setLayout(gbl_contactsSearch);
|
|
||||||
|
|
||||||
GridBagConstraints gbc_searchField = new GridBagConstraints();
|
|
||||||
gbc_searchField.fill = GridBagConstraints.BOTH;
|
|
||||||
gbc_searchField.gridx = 0;
|
|
||||||
gbc_searchField.gridy = 0;
|
|
||||||
gbc_searchField.insets = new Insets(7, 4, 4, 4);
|
|
||||||
|
|
||||||
searchPane.add(searchField, gbc_searchField);
|
|
||||||
|
|
||||||
// Sends event to server, if input has changed
|
|
||||||
searchField.getDocument().addDocumentListener(new DocumentListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeUpdate(DocumentEvent evt) {
|
|
||||||
if (client.isOnline()) if (searchField.getText().isEmpty()) {
|
|
||||||
contactsModel.clear();
|
|
||||||
revalidate();
|
|
||||||
repaint();
|
|
||||||
} else try {
|
|
||||||
client.sendEvent(new ContactSearchRequest(searchField.getText()));
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void insertUpdate(DocumentEvent evt) {
|
|
||||||
if (client.isOnline()) try {
|
|
||||||
client.sendEvent(new ContactSearchRequest(searchField.getText()));
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void changedUpdate(DocumentEvent evt) {}
|
|
||||||
});
|
|
||||||
|
|
||||||
GridBagConstraints gbc_cancelButton = new GridBagConstraints();
|
|
||||||
gbc_cancelButton.fill = GridBagConstraints.BOTH;
|
|
||||||
gbc_cancelButton.gridx = 1;
|
|
||||||
gbc_cancelButton.gridy = 0;
|
|
||||||
gbc_cancelButton.insets = new Insets(7, 4, 4, 4);
|
|
||||||
|
|
||||||
cancelButton.addActionListener((evt) -> { drawChatBox(gbc_scrollPane); });
|
|
||||||
|
|
||||||
searchPane.add(cancelButton, gbc_cancelButton);
|
|
||||||
|
|
||||||
contactList.setModel(contactsModel);
|
|
||||||
scrollForPossibleContacts.setBorder(new EmptyBorder(space, space, space, space));
|
|
||||||
scrollForPossibleContacts.setViewportView(contactList);
|
|
||||||
|
|
||||||
GridBagConstraints gbc_possibleContacts = new GridBagConstraints();
|
|
||||||
gbc_possibleContacts.fill = GridBagConstraints.BOTH;
|
|
||||||
gbc_possibleContacts.gridwidth = 2;
|
|
||||||
gbc_possibleContacts.gridx = 0;
|
|
||||||
gbc_possibleContacts.gridy = 1;
|
|
||||||
|
|
||||||
gbc_possibleContacts.insets = insets;
|
|
||||||
|
|
||||||
searchPane.add(scrollForPossibleContacts, gbc_possibleContacts);
|
|
||||||
|
|
||||||
// Contacts Header
|
|
||||||
GridBagConstraints gbc_contactsHeader = new GridBagConstraints();
|
|
||||||
gbc_contactsHeader.fill = GridBagConstraints.BOTH;
|
|
||||||
gbc_contactsHeader.gridx = 0;
|
|
||||||
gbc_contactsHeader.gridy = 1;
|
|
||||||
gbc_contactsHeader.insets = insets;
|
|
||||||
|
|
||||||
GridBagLayout gbl_contactHeader = new GridBagLayout();
|
|
||||||
gbl_contactHeader.columnWidths = new int[] { 1, 1 };
|
|
||||||
gbl_contactHeader.rowHeights = new int[] { 1 };
|
|
||||||
gbl_contactHeader.columnWeights = new double[] { 1, 1 };
|
|
||||||
gbl_contactHeader.rowWeights = new double[] { 1 };
|
|
||||||
contactsHeader.setLayout(gbl_contactHeader);
|
|
||||||
|
|
||||||
contactsDisplay.setEditable(false);
|
|
||||||
contactsDisplay.setFont(new Font("Arial", Font.PLAIN, 12));
|
|
||||||
contactsDisplay.setText("Contacts");
|
|
||||||
|
|
||||||
GridBagConstraints gbc_contactsDisplay = new GridBagConstraints();
|
|
||||||
gbc_contactsDisplay.fill = GridBagConstraints.BOTH;
|
|
||||||
gbc_contactsDisplay.gridx = 0;
|
|
||||||
gbc_contactsDisplay.gridy = 0;
|
|
||||||
|
|
||||||
contactsHeader.add(contactsDisplay, gbc_contactsDisplay);
|
|
||||||
|
|
||||||
addContact.setFont(new Font("Arial", Font.PLAIN, 15));
|
|
||||||
|
|
||||||
GridBagConstraints gbc_addContact = new GridBagConstraints();
|
|
||||||
gbc_addContact.fill = GridBagConstraints.BOTH;
|
|
||||||
gbc_addContact.gridx = 1;
|
|
||||||
gbc_addContact.gridy = 0;
|
|
||||||
gbc_addContact.insets = insets;
|
|
||||||
|
|
||||||
addContact.addActionListener(evt -> drawContactSearch(gbc_searchPane));
|
|
||||||
|
|
||||||
contactsHeader.add(addContact, gbc_addContact);
|
|
||||||
|
|
||||||
applyTheme(Settings.getInstance().getCurrentTheme());
|
|
||||||
|
|
||||||
contentPane.add(contactsHeader, gbc_contactsHeader);
|
|
||||||
contentPane.revalidate();
|
|
||||||
|
|
||||||
// Listen to theme changes
|
|
||||||
EventBus.getInstance().register(ThemeChangeEvent.class, evt -> applyTheme(evt.get()));
|
|
||||||
|
|
||||||
// Listen to user status changes
|
|
||||||
EventBus.getInstance().register(UserStatusChangeEvent.class, evt -> { userList.revalidate(); userList.repaint(); });
|
|
||||||
|
|
||||||
// Listen to received messages
|
|
||||||
EventBus.getInstance().register(MessageCreationEvent.class, evt -> {
|
|
||||||
Message message = evt.get();
|
|
||||||
Chat chat = localDB.getChats().stream().filter(c -> c.getRecipient().getID() == message.getSenderID()).findFirst().get();
|
|
||||||
// chat.appendMessage(message);
|
|
||||||
|
|
||||||
// Read message and update UI if in current chat
|
|
||||||
if (chat == currentChat) readCurrentChat();
|
|
||||||
|
|
||||||
revalidate();
|
|
||||||
repaint();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Listen to message status changes
|
|
||||||
EventBus.getInstance().register(MessageStatusChangeEvent.class, evt -> {
|
|
||||||
final long id = evt.getID();
|
|
||||||
final MessageStatus status = evt.get();
|
|
||||||
|
|
||||||
for (Chat c : localDB.getChats())
|
|
||||||
// for (Message m : c.getModel())
|
|
||||||
// if (m.getID() == id) {
|
|
||||||
//
|
|
||||||
// // Update message status
|
|
||||||
// m.setStatus(status);
|
|
||||||
//
|
|
||||||
// // Update model and scroll down if current chat
|
|
||||||
// if (c == currentChat) {
|
|
||||||
// messageList.setModel(currentChat.getModel());
|
|
||||||
// scrollPane.setChatOpened(true);
|
|
||||||
// } else messageList.synchronizeModel();
|
|
||||||
// }
|
|
||||||
|
|
||||||
revalidate();
|
|
||||||
repaint();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Listen to contact search results
|
|
||||||
EventBus.getInstance()
|
|
||||||
.register(ContactSearchResult.class,
|
|
||||||
evt -> {
|
|
||||||
contactsModel.clear();
|
|
||||||
final java.util.List<User> contacts = evt.get();
|
|
||||||
contacts.forEach(contactsModel::add);
|
|
||||||
revalidate();
|
|
||||||
repaint();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add new contacts to the contact list
|
|
||||||
EventBus.getInstance().register(ContactOperationEvent.class, evt -> {
|
|
||||||
User contact = evt.get();
|
|
||||||
|
|
||||||
// Clearing the search field and the searchResultList
|
|
||||||
searchField.setText("");
|
|
||||||
contactsModel.clear();
|
|
||||||
|
|
||||||
// Update LocalDB
|
|
||||||
userListModel.addElement(contact);
|
|
||||||
localDB.getUsers().put(contact.getName(), contact);
|
|
||||||
localDB.getChats().add(new Chat(contact));
|
|
||||||
|
|
||||||
revalidate();
|
|
||||||
repaint();
|
|
||||||
});
|
|
||||||
|
|
||||||
revalidate();
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to immediately reload the {@link ChatWindow} when settings were changed.
|
|
||||||
*
|
|
||||||
* @param theme the theme to change colors into
|
|
||||||
* @since Envoy Client v0.2-alpha
|
|
||||||
*/
|
|
||||||
private void applyTheme(Theme theme) {
|
|
||||||
// contentPane
|
|
||||||
contentPane.setBackground(theme.getBackgroundColor());
|
|
||||||
contentPane.setForeground(theme.getUserNameColor());
|
|
||||||
// messageList
|
|
||||||
messageList.setForeground(theme.getTextColor());
|
|
||||||
messageList.setBackground(theme.getCellColor());
|
|
||||||
messageList.synchronizeModel();
|
|
||||||
// scrollPane
|
|
||||||
scrollPane.applyTheme(theme);
|
|
||||||
scrollPane.autoscroll();
|
|
||||||
// messageEnterTextArea
|
|
||||||
messageEnterTextArea.setCaretColor(theme.getTypingMessageColor());
|
|
||||||
messageEnterTextArea.setForeground(theme.getTypingMessageColor());
|
|
||||||
messageEnterTextArea.setBackground(theme.getCellColor());
|
|
||||||
// postButton
|
|
||||||
postButton.setForeground(theme.getInteractableForegroundColor());
|
|
||||||
postButton.setBackground(theme.getInteractableBackgroundColor());
|
|
||||||
// settingsButton
|
|
||||||
settingsButton.setForeground(theme.getInteractableForegroundColor());
|
|
||||||
settingsButton.setBackground(theme.getInteractableBackgroundColor());
|
|
||||||
// textPane
|
|
||||||
textPane.setBackground(theme.getBackgroundColor());
|
|
||||||
textPane.setForeground(theme.getUserNameColor());
|
|
||||||
// userList
|
|
||||||
userList.setSelectionForeground(theme.getUserNameColor());
|
|
||||||
userList.setSelectionBackground(theme.getSelectionColor());
|
|
||||||
userList.setForeground(theme.getUserNameColor());
|
|
||||||
userList.setBackground(theme.getCellColor());
|
|
||||||
// contacts header
|
|
||||||
contactsHeader.setBackground(theme.getCellColor());
|
|
||||||
contactsDisplay.setBackground(theme.getCellColor());
|
|
||||||
contactsDisplay.setForeground(theme.getUserNameColor());
|
|
||||||
addContact.setBackground(theme.getInteractableBackgroundColor());
|
|
||||||
addContact.setForeground(theme.getInteractableForegroundColor());
|
|
||||||
// SearchPane
|
|
||||||
searchPane.setBackground(theme.getCellColor());
|
|
||||||
searchField.setBackground(theme.getBackgroundColor());
|
|
||||||
searchField.setForeground(theme.getUserNameColor());
|
|
||||||
cancelButton.setBackground(theme.getInteractableBackgroundColor());
|
|
||||||
cancelButton.setForeground(theme.getInteractableForegroundColor());
|
|
||||||
contactList.setForeground(theme.getTextColor());
|
|
||||||
contactList.setBackground(theme.getCellColor());
|
|
||||||
scrollForPossibleContacts.applyTheme(theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a new message to the server based on the text entered in the textArea.
|
|
||||||
*
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
private void postMessage() {
|
|
||||||
if (userList.isSelectionEmpty()) {
|
|
||||||
JOptionPane.showMessageDialog(this, "Please select a recipient!", "Cannot send message", JOptionPane.INFORMATION_MESSAGE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String text = messageEnterTextArea.getText().trim();
|
|
||||||
if (!text.isEmpty()) checkMessageTextLength();
|
|
||||||
|
|
||||||
// Create message
|
|
||||||
final Message message = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
|
|
||||||
.setText(text)
|
|
||||||
.build();
|
|
||||||
sendMessage(message);
|
|
||||||
// Clear text field
|
|
||||||
messageEnterTextArea.setText("");
|
|
||||||
postButton.setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Forwards a message.
|
|
||||||
*
|
|
||||||
* @param message the message to forward
|
|
||||||
* @param recipient the new recipient of the message
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
private void forwardMessage(Message message, User... recipients) {
|
|
||||||
Arrays.stream(recipients).forEach(recipient -> {
|
|
||||||
if (message != null && recipients != null) sendMessage(new MessageBuilder(message, recipient.getID(), localDB.getIDGenerator()).build());
|
|
||||||
else throw new NullPointerException("No recipient or no message selected");
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private void forwardMessages(Collection<Message> messages, User... recipients) {
|
|
||||||
messages.forEach(message -> { forwardMessage(message, recipients); });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a {@link Message} to the server.
|
|
||||||
*
|
|
||||||
* @param message the message to send
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
private void sendMessage(final Message message) {
|
|
||||||
try {
|
|
||||||
// Send message
|
|
||||||
writeProxy.writeMessage(message);
|
|
||||||
|
|
||||||
// Add message to PersistentLocalDB and update UI
|
|
||||||
// currentChat.appendMessage(message);
|
|
||||||
|
|
||||||
// Update UI
|
|
||||||
revalidate();
|
|
||||||
repaint();
|
|
||||||
|
|
||||||
// Request a new id generator if all IDs were used
|
|
||||||
if (!localDB.getIDGenerator().hasNext()) client.requestIdGenerator();
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
JOptionPane.showMessageDialog(this, "Error sending message:\n" + e.toString(), "Message sending error", JOptionPane.ERROR_MESSAGE);
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readCurrentChat() {
|
|
||||||
try {
|
|
||||||
currentChat.read(writeProxy);
|
|
||||||
if (messageList.getRenderer() != null) messageList.synchronizeModel();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
logger.log(Level.WARNING, "Couldn't notify server about message status change", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void drawChatBox(GridBagConstraints gbc_scrollPane) {
|
|
||||||
contentPane.remove(searchPane);
|
|
||||||
contentPane.add(scrollPane, gbc_scrollPane);
|
|
||||||
contentPane.revalidate();
|
|
||||||
contentPane.repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void drawContactSearch(GridBagConstraints gbc_searchPane) {
|
|
||||||
currentChat = null;
|
|
||||||
userList.removeSelectionInterval(0, userList.getModel().getSize() - 1);
|
|
||||||
messageList.setModel(null);
|
|
||||||
textPane.setText("");
|
|
||||||
contentPane.remove(scrollPane);
|
|
||||||
contentPane.add(searchPane, gbc_searchPane);
|
|
||||||
contentPane.revalidate();
|
|
||||||
contentPane.repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the components responsible server communication and
|
|
||||||
* persistence.<br>
|
|
||||||
* <br>
|
|
||||||
* This will trigger the display of the contact list.
|
|
||||||
*
|
|
||||||
* @param client the client used to send and receive messages
|
|
||||||
* @param localDB the local database used to manage stored messages
|
|
||||||
* and users
|
|
||||||
* @param writeProxy the write proxy used to send messages and status change
|
|
||||||
* events to the server or cache them inside the local
|
|
||||||
* database
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
public void initContent(Client client, LocalDB localDB, WriteProxy writeProxy) {
|
|
||||||
this.client = client;
|
|
||||||
this.localDB = localDB;
|
|
||||||
this.writeProxy = writeProxy;
|
|
||||||
|
|
||||||
messageList.setRenderer((list, message) -> new MessageComponent(list, message, client.getSender().getID()));
|
|
||||||
|
|
||||||
// Load users and chats
|
|
||||||
new Thread(() -> {
|
|
||||||
localDB.getUsers().values().forEach(user -> {
|
|
||||||
userListModel.addElement(user);
|
|
||||||
|
|
||||||
// Check if user exists in local DB
|
|
||||||
if (localDB.getChats().stream().noneMatch(c -> c.getRecipient().getID() == user.getID())) localDB.getChats().add(new Chat(user));
|
|
||||||
});
|
|
||||||
SwingUtilities.invokeLater(() -> userList.setModel(userListModel));
|
|
||||||
|
|
||||||
revalidate();
|
|
||||||
repaint();
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the length of the text inside messageEnterTextArea >=
|
|
||||||
* {@link ChatWindow#MAX_MESSAGE_LENGTH}
|
|
||||||
* and splits the text into the allowed part, if that is the case.
|
|
||||||
*
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
private void checkMessageTextLength() {
|
|
||||||
String input = messageEnterTextArea.getText();
|
|
||||||
if (input.length() >= MAX_MESSAGE_LENGTH) {
|
|
||||||
messageEnterTextArea.setText(input.substring(0, MAX_MESSAGE_LENGTH - 1));
|
|
||||||
// TODO: current notification is like being hit with a hammer, maybe it should
|
|
||||||
// be replaced with a more subtle notification
|
|
||||||
JOptionPane.showMessageDialog(messageEnterTextArea,
|
|
||||||
"the maximum length for a message has been reached",
|
|
||||||
"maximum message length reached",
|
|
||||||
JOptionPane.WARNING_MESSAGE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkPostButton(String text) { postButton.setEnabled(!text.trim().isBlank()); }
|
|
||||||
}
|
|
@ -1,145 +0,0 @@
|
|||||||
package envoy.client.ui.container;
|
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JDialog;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.border.EmptyBorder;
|
|
||||||
|
|
||||||
import envoy.client.data.Settings;
|
|
||||||
import envoy.client.ui.Theme;
|
|
||||||
import envoy.client.ui.list.ComponentList;
|
|
||||||
import envoy.client.ui.list.ComponentList.SelectionMode;
|
|
||||||
import envoy.client.ui.list.Model;
|
|
||||||
import envoy.client.ui.list_component.UserComponent;
|
|
||||||
import envoy.data.Message;
|
|
||||||
import envoy.data.User;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class defines a dialog to choose contacts from.<br>
|
|
||||||
* <br>
|
|
||||||
* Project: <strong>envoy-client</strong><br>
|
|
||||||
* File: <strong>ContactsChooserDialog.java</strong><br>
|
|
||||||
* Created: <strong>15 Mar 2020</strong><br>
|
|
||||||
*
|
|
||||||
* @author Leon Hofmeister
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public class ContactsChooserDialog extends JDialog {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 0L;
|
|
||||||
|
|
||||||
private ComponentList<User> contactList = new ComponentList<User>().setModel(new Model<User>())
|
|
||||||
.setRenderer((list, user) -> new UserComponent(user));
|
|
||||||
private JButton okButton = new JButton("Ok");
|
|
||||||
private JButton cancelButton = new JButton("Cancel");
|
|
||||||
|
|
||||||
private final Theme theme = Settings.getInstance().getCurrentTheme();
|
|
||||||
|
|
||||||
private final JPanel contentPanel = new JPanel();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows a modal contacts-chooser dialog and blocks until the
|
|
||||||
* dialog is hidden. If the user presses the "OK" button, then
|
|
||||||
* this method hides/disposes the dialog and returns the selected element (has
|
|
||||||
* yet
|
|
||||||
* to be casted back to its original type due to the limitations of Generics in
|
|
||||||
* Java).
|
|
||||||
* If the user presses the "Cancel" button or closes the dialog without
|
|
||||||
* pressing "OK", then this method disposes the dialog and returns an empty
|
|
||||||
* <code>ArrayList</code>.
|
|
||||||
*
|
|
||||||
* @param title the title of the dialog
|
|
||||||
* @param parent this @{@link Component} will be parsed to
|
|
||||||
* {@link java.awt.Window#setLocationRelativeTo(Component)} in
|
|
||||||
* order to change the location of the dialog
|
|
||||||
* @param message the {@link Message} to display on top of the Dialog
|
|
||||||
* @param users the users that should be displayed
|
|
||||||
* @return the selected Element (yet has to be casted to the wanted type due to
|
|
||||||
* the Generics limitations in Java)
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public static List<User> showForwardingDialog(String title, Component parent, Message message, Collection<User> users) {
|
|
||||||
ContactsChooserDialog dialog = new ContactsChooserDialog(parent);
|
|
||||||
dialog.setTitle(title);
|
|
||||||
dialog.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
|
||||||
dialog.addCancelButtonActionListener(e -> dialog.dispose());
|
|
||||||
|
|
||||||
List<User> results = new ArrayList<>();
|
|
||||||
dialog.addOkButtonActionListener(e -> { results.addAll(dialog.getContactList().getSelectedElements()); dialog.dispose(); });
|
|
||||||
Model<User> contactListModel = dialog.getContactList().getModel();
|
|
||||||
users.forEach(contactListModel::add);
|
|
||||||
|
|
||||||
dialog.setModalityType(ModalityType.APPLICATION_MODAL);
|
|
||||||
dialog.setVisible(true);
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param parent this @{@link Component} will be parsed to
|
|
||||||
* {@link java.awt.Window#setLocationRelativeTo(Component)}
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
private ContactsChooserDialog(Component parent) {
|
|
||||||
contactList.setSelectionMode(SelectionMode.MULTIPLE);
|
|
||||||
contactList.setSelectionHandler((user, comp, isSelected) -> {
|
|
||||||
final var theme = Settings.getInstance().getCurrentTheme();
|
|
||||||
comp.setBackground(isSelected ? theme.getSelectionColor() : theme.getCellColor());
|
|
||||||
});
|
|
||||||
setLocationRelativeTo(parent);
|
|
||||||
getContentPane().setLayout(new BorderLayout());
|
|
||||||
setBackground(theme.getBackgroundColor());
|
|
||||||
setForeground(theme.getTextColor());
|
|
||||||
setSize(400, 400);
|
|
||||||
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
|
|
||||||
getContentPane().add(contentPanel, BorderLayout.CENTER);
|
|
||||||
contentPanel.setLayout(new BorderLayout(0, 0));
|
|
||||||
contentPanel.add(contactList, BorderLayout.CENTER);
|
|
||||||
{
|
|
||||||
JPanel buttonPane = new JPanel();
|
|
||||||
getContentPane().add(buttonPane, BorderLayout.SOUTH);
|
|
||||||
{
|
|
||||||
okButton = new JButton("OK");
|
|
||||||
okButton.setMnemonic(KeyEvent.VK_ENTER);
|
|
||||||
okButton.setActionCommand("OK");
|
|
||||||
buttonPane.setLayout(new BorderLayout(0, 0));
|
|
||||||
buttonPane.add(okButton, BorderLayout.EAST);
|
|
||||||
getRootPane().setDefaultButton(okButton);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
cancelButton = new JButton("Cancel");
|
|
||||||
cancelButton.setActionCommand("Cancel");
|
|
||||||
buttonPane.add(cancelButton, BorderLayout.WEST);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
applyTheme(Settings.getInstance().getCurrentTheme());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applyTheme(Theme theme) {
|
|
||||||
contentPanel.setBackground(theme.getBackgroundColor());
|
|
||||||
contentPanel.setForeground(theme.getTextColor());
|
|
||||||
contactList.setBackground(theme.getCellColor());
|
|
||||||
okButton.setBackground(theme.getInteractableBackgroundColor());
|
|
||||||
okButton.setForeground(theme.getTextColor());
|
|
||||||
cancelButton.setBackground(theme.getInteractableBackgroundColor());
|
|
||||||
cancelButton.setForeground(theme.getTextColor());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the underlying {@link ComponentList}
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
private ComponentList<User> getContactList() { return contactList; }
|
|
||||||
|
|
||||||
private void addOkButtonActionListener(ActionListener l) { okButton.addActionListener(l); }
|
|
||||||
|
|
||||||
private void addCancelButtonActionListener(ActionListener l) { cancelButton.addActionListener(l); }
|
|
||||||
}
|
|
@ -1,255 +0,0 @@
|
|||||||
package envoy.client.ui.container;
|
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
import envoy.client.data.Settings;
|
|
||||||
import envoy.client.ui.Theme;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class defines a menu that will be automatically called if
|
|
||||||
* {@link MouseEvent#isPopupTrigger()} returns true for the parent component.
|
|
||||||
* The user has the possibility to directly add actions to be performed when
|
|
||||||
* clicking on the element with the selected String. Additionally, for each
|
|
||||||
* element an {@link Icon} can be added, but it must not be.
|
|
||||||
* If the key(text) of an element starts with one of the predefined values, a
|
|
||||||
* special component will be called: either a {@link JRadioButtonMenuItem}, a
|
|
||||||
* {@link JCheckBoxMenuItem} or a {@link JMenu} will be created.<br>
|
|
||||||
* <br>
|
|
||||||
* Project: <strong>envoy-client</strong><br>
|
|
||||||
* File: <strong>ContextMenu.java</strong><br>
|
|
||||||
* Created: <strong>17 Mar 2020</strong><br>
|
|
||||||
*
|
|
||||||
* @author Leon Hofmeister
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public class ContextMenu extends JPopupMenu {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 0L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If a key starts with this String, a {@link JCheckBoxMenuItem} will be created
|
|
||||||
*/
|
|
||||||
public static final String checkboxMenuItem = "ChBoMI";
|
|
||||||
/**
|
|
||||||
* If a key starts with this String, a {@link JRadioButtonMenuItem} will be
|
|
||||||
* created
|
|
||||||
*/
|
|
||||||
public static final String radioButtonMenuItem = "RaBuMI";
|
|
||||||
/**
|
|
||||||
* If a key starts with this String, a {@link JMenu} will be created
|
|
||||||
*/
|
|
||||||
public static final String subMenuItem = "SubMI";
|
|
||||||
|
|
||||||
private Map<String, ActionListener> items = new HashMap<>();
|
|
||||||
private Map<String, Icon> icons = new HashMap<>();
|
|
||||||
private Map<String, Integer> mnemonics = new HashMap<>();
|
|
||||||
|
|
||||||
private ButtonGroup radioButtonGroup = new ButtonGroup();
|
|
||||||
private boolean built = false;
|
|
||||||
private boolean visible = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param parent the component which will call this
|
|
||||||
* {@link ContextMenu}
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public ContextMenu(Component parent) {
|
|
||||||
setInvoker(parent);
|
|
||||||
setOpaque(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param label the string that a UI may use to display as a title
|
|
||||||
* for the pop-up menu
|
|
||||||
* @param parent the component which will call this
|
|
||||||
* {@link ContextMenu}
|
|
||||||
* @param itemsWithActions a map of all strings to be displayed with according
|
|
||||||
* actions
|
|
||||||
* @param itemIcons the icons to be displayed before a name, if wanted.
|
|
||||||
* Only keys in here will have an Icon displayed. More
|
|
||||||
* precisely, all keys here not included in the first
|
|
||||||
* map will be thrown out.
|
|
||||||
* @param itemMnemonics the keyboard shortcuts that need to be pressed to
|
|
||||||
* automatically execute the {@link JMenuItem} with the
|
|
||||||
* given text
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public ContextMenu(String label, Component parent, Map<String, ActionListener> itemsWithActions, Map<String, Icon> itemIcons,
|
|
||||||
Map<String, Integer> itemMnemonics) {
|
|
||||||
this(label);
|
|
||||||
setInvoker(parent);
|
|
||||||
this.items = (itemsWithActions != null) ? itemsWithActions : items;
|
|
||||||
this.icons = (itemIcons != null) ? itemIcons : icons;
|
|
||||||
this.mnemonics = (itemMnemonics != null) ? itemMnemonics : mnemonics;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param label the string that a UI may use to display as a title for the
|
|
||||||
* pop-up menu.
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public ContextMenu(String label) {
|
|
||||||
super(label);
|
|
||||||
setOpaque(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepares the PopupMenu to be displayed. Should only be used once all map
|
|
||||||
* values have been set.
|
|
||||||
*
|
|
||||||
* @return this instance of {@link ContextMenu} to allow chaining behind the
|
|
||||||
* constructor
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public ContextMenu build() {
|
|
||||||
items.forEach((text, action) -> {
|
|
||||||
// case radio button wanted
|
|
||||||
AbstractButton item;
|
|
||||||
if (text.startsWith(radioButtonMenuItem)) {
|
|
||||||
item = new JRadioButtonMenuItem(text.substring(radioButtonMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
|
|
||||||
radioButtonGroup.add(item);
|
|
||||||
// case check box wanted
|
|
||||||
} else if (text.startsWith(checkboxMenuItem))
|
|
||||||
item = new JCheckBoxMenuItem(text.substring(checkboxMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
|
|
||||||
// case sub-menu wanted
|
|
||||||
else if (text.startsWith(subMenuItem)) item = new JMenu(text.substring(subMenuItem.length()));
|
|
||||||
else // normal JMenuItem wanted
|
|
||||||
item = new JMenuItem(text, icons.containsKey(text) ? icons.get(text) : null);
|
|
||||||
item.addActionListener(action);
|
|
||||||
item.setOpaque(true);
|
|
||||||
if (mnemonics.containsKey(text)) item.setMnemonic(mnemonics.get(text));
|
|
||||||
add(item);
|
|
||||||
});
|
|
||||||
getInvoker().addMouseListener(getShowingListener());
|
|
||||||
applyTheme(Settings.getInstance().getCurrentTheme());
|
|
||||||
built = true;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MouseAdapter getShowingListener() {
|
|
||||||
return new MouseAdapter() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseClicked(MouseEvent e) { action(e); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mousePressed(MouseEvent e) { action(e); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseReleased(MouseEvent e) { action(e); }
|
|
||||||
|
|
||||||
private void action(MouseEvent e) {
|
|
||||||
if (!built) build();
|
|
||||||
if (e.isPopupTrigger()) {
|
|
||||||
// hides the menu if already visible
|
|
||||||
visible = !visible;
|
|
||||||
if (visible) show(e.getComponent(), e.getX(), e.getY());
|
|
||||||
else setVisible(false);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all subcomponents of this menu.
|
|
||||||
*
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public void clear() {
|
|
||||||
removeAll();
|
|
||||||
items = new HashMap<>();
|
|
||||||
icons = new HashMap<>();
|
|
||||||
mnemonics = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the items
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public Map<String, ActionListener> getItems() { return items; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param items the items with the displayed text and the according action to
|
|
||||||
* take once called
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public void setItems(Map<String, ActionListener> items) { this.items = items; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the icons
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public Map<String, Icon> getIcons() { return icons; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param icons the icons to set
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public void setIcons(Map<String, Icon> icons) { this.icons = icons; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the mnemonics (the keyboard shortcuts that automatically execute the
|
|
||||||
* command for a {@link JMenuItem} with corresponding text)
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public Map<String, Integer> getMnemonics() { return mnemonics; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mnemonics the keyboard shortcuts that need to be pressed to
|
|
||||||
* automatically execute the {@link JMenuItem} with the given
|
|
||||||
* text
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public void setMnemonics(Map<String, Integer> mnemonics) { this.mnemonics = mnemonics; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}<br>
|
|
||||||
* Additionally sets the foreground of all subcomponents of this
|
|
||||||
* {@link ContextMenu}.
|
|
||||||
*
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setForeground(Color color) {
|
|
||||||
super.setForeground(color);
|
|
||||||
for (MenuElement element : getSubElements())
|
|
||||||
((Component) element).setForeground(color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}<br>
|
|
||||||
* Additionally sets the background of all subcomponents of this
|
|
||||||
* {@link ContextMenu}.
|
|
||||||
*
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setBackground(Color color) {
|
|
||||||
super.setBackground(color);
|
|
||||||
for (MenuElement element : getSubElements())
|
|
||||||
((Component) element).setBackground(color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the fore- and background of all elements contained in this
|
|
||||||
* {@link ContextMenu}
|
|
||||||
* This method is to be only used by Envoy as {@link Theme} is an
|
|
||||||
* Envoy-exclusive object.
|
|
||||||
*
|
|
||||||
* @param theme the theme to use
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
protected void applyTheme(Theme theme) {
|
|
||||||
setBackground(theme.getCellColor());
|
|
||||||
setForeground(theme.getTextColor());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,261 +0,0 @@
|
|||||||
package envoy.client.ui.list;
|
|
||||||
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.awt.event.MouseListener;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides a vertical list layout of components provided in a
|
|
||||||
* {@link Model}. Similar to {@link javax.swing.JList} but capable
|
|
||||||
* of rendering {@link JPanel}s.<br>
|
|
||||||
* <br>
|
|
||||||
* Project: <strong>envoy-client</strong><br>
|
|
||||||
* File: <strong>ComponentList.java</strong><br>
|
|
||||||
* Created: <strong>25.01.2020</strong><br>
|
|
||||||
*
|
|
||||||
* @param <E> the type of object displayed in this list
|
|
||||||
* @author Kai S. K. Engelbart
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
public class ComponentList<E> extends JPanel {
|
|
||||||
|
|
||||||
private Model<E> model;
|
|
||||||
private Renderer<E> renderer;
|
|
||||||
private SelectionHandler<E> selectionHandler;
|
|
||||||
private SelectionMode selectionMode = SelectionMode.NONE;
|
|
||||||
private Set<Integer> selection = new HashSet<>();
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 0L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines the possible modes of selection that can be performed by the user
|
|
||||||
*
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public static enum SelectionMode {
|
|
||||||
/**
|
|
||||||
* Selection is completely ignored.
|
|
||||||
*/
|
|
||||||
NONE,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only a single element can be selected.
|
|
||||||
*/
|
|
||||||
SINGLE,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Multiple elements can be selected regardless of their position.
|
|
||||||
*/
|
|
||||||
MULTIPLE
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance of {@link ComponentList}.
|
|
||||||
*
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
public ComponentList() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all child components and then adds all components representing the
|
|
||||||
* elements of the {@link Model}.
|
|
||||||
*
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
public void synchronizeModel() {
|
|
||||||
if (model != null) {
|
|
||||||
removeAll();
|
|
||||||
model.forEach(this::addElement);
|
|
||||||
revalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Selects a list element by index. If the element is already selected, it is
|
|
||||||
* removed from the selection.
|
|
||||||
*
|
|
||||||
* @param index the index of the selected component
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public void selectElement(int index) {
|
|
||||||
final JComponent element = getComponent(index);
|
|
||||||
if (selection.contains(index)) {
|
|
||||||
|
|
||||||
// Deselect if clicked again
|
|
||||||
if (selectionHandler != null) selectionHandler.selectionChanged(model.get(index), element, true);
|
|
||||||
selection.remove(index);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Remove old selection if single selection is enabled
|
|
||||||
if (selectionMode == SelectionMode.SINGLE) clearSelection();
|
|
||||||
|
|
||||||
// Select item
|
|
||||||
if (selectionMode != SelectionMode.NONE) {
|
|
||||||
|
|
||||||
// Assign new selection
|
|
||||||
selection.add(index);
|
|
||||||
|
|
||||||
// Update element
|
|
||||||
if (selectionHandler != null) selectionHandler.selectionChanged(model.get(index), element, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
revalidate();
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the current selection.
|
|
||||||
*
|
|
||||||
* @since Envoy Client v0.1-alpha
|
|
||||||
*/
|
|
||||||
public void clearSelection() {
|
|
||||||
if (selectionHandler != null) selection.forEach(i -> selectionHandler.selectionChanged(model.get(i), getComponent(i), false));
|
|
||||||
selection.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an object to the list by rendering it with the current
|
|
||||||
* {@link Renderer}.
|
|
||||||
*
|
|
||||||
* @param elem the element to add
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
void addElement(E elem) {
|
|
||||||
if (renderer != null) {
|
|
||||||
final JComponent component = renderer.getListCellComponent(this, elem);
|
|
||||||
component.addMouseListener(getSelectionListener(getComponentCount()));
|
|
||||||
add(component, getComponentCount());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param componentIndex the index of the list component to which the mouse
|
|
||||||
* listener will be added
|
|
||||||
* @return a mouse listener calling the
|
|
||||||
* {@link ComponentList#selectElement(int)} method with the
|
|
||||||
* component's index when a left click is performed by the user
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
private MouseListener getSelectionListener(int componentIndex) {
|
|
||||||
return new MouseAdapter() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseClicked(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) selectElement(componentIndex); }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JComponent getComponent(int n) { return (JComponent) super.getComponent(n); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return a set of all selected indices
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public Set<Integer> getSelection() { return selection; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return a set of all selected elements
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public Set<E> getSelectedElements() {
|
|
||||||
var selectedElements = new HashSet<E>();
|
|
||||||
selection.forEach(i -> selectedElements.add(model.get(i)));
|
|
||||||
return selectedElements;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the index of an arbitrary selected element
|
|
||||||
* @throws java.util.NoSuchElementException if no selection is present
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public int getSingleSelection() { return selection.stream().findAny().get(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return an arbitrary selected element
|
|
||||||
* @throws java.util.NoSuchElementException if no selection is present
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public E getSingleSelectedElement() { return model.get(getSingleSelection()); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the model
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public Model<E> getModel() { return model; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the list model providing the list elements to render. The rendered
|
|
||||||
* components will be synchronized with the contents of the new model or removed
|
|
||||||
* if the new model is {@code null}.
|
|
||||||
*
|
|
||||||
* @param model the list model to set
|
|
||||||
* @return this component list
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
public ComponentList<E> setModel(Model<E> model) {
|
|
||||||
|
|
||||||
// Remove old model
|
|
||||||
if (this.model != null) this.model.setComponentList(null);
|
|
||||||
|
|
||||||
// Synchronize with new model
|
|
||||||
this.model = model;
|
|
||||||
if (model != null) model.setComponentList(this);
|
|
||||||
synchronizeModel();
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the renderer
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public Renderer<E> getRenderer() { return renderer; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param renderer the renderer to set
|
|
||||||
* @return this component list
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public ComponentList<E> setRenderer(Renderer<E> renderer) {
|
|
||||||
this.renderer = renderer;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the selection mode
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public SelectionMode getSelectionMode() { return selectionMode; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a new selection mode. The current selection will be cleared during this
|
|
||||||
* action.
|
|
||||||
*
|
|
||||||
* @param selectionMode the selection mode to set
|
|
||||||
* @return this component list
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public ComponentList<E> setSelectionMode(SelectionMode selectionMode) {
|
|
||||||
this.selectionMode = selectionMode;
|
|
||||||
clearSelection();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the selection handler
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public SelectionHandler<E> getSelectionHandler() { return selectionHandler; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param selectionHandler the selection handler to set
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public void setSelectionHandler(SelectionHandler<E> selectionHandler) { this.selectionHandler = selectionHandler; }
|
|
||||||
}
|
|
@ -1,120 +0,0 @@
|
|||||||
package envoy.client.ui.list;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores objects that will be displayed in a {@link ComponentList}.<br>
|
|
||||||
* <br>
|
|
||||||
* Project: <strong>envoy-client</strong><br>
|
|
||||||
* File: <strong>Model.java</strong><br>
|
|
||||||
* Created: <strong>25.01.2020</strong><br>
|
|
||||||
*
|
|
||||||
* @param <E> the type of object displayed in this list
|
|
||||||
* @author Kai S. K. Engelbart
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
public final class Model<E> implements Iterable<E>, Serializable {
|
|
||||||
|
|
||||||
private List<E> elements = new ArrayList<>();
|
|
||||||
transient private ComponentList<E> componentList;
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 0L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an element to this model and notifies the associated
|
|
||||||
* {@link ComponentList} to add the corresponding component.
|
|
||||||
*
|
|
||||||
* @param e the element to add
|
|
||||||
* @return {@code true}
|
|
||||||
* @see java.util.List#add(java.lang.Object)
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
public boolean add(E e) {
|
|
||||||
if (componentList != null) {
|
|
||||||
componentList.addElement(e);
|
|
||||||
componentList.revalidate();
|
|
||||||
}
|
|
||||||
return elements.add(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all elements from this model and clears the associated
|
|
||||||
* {@link ComponentList}.
|
|
||||||
*
|
|
||||||
* @see java.util.List#clear()
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
public void clear() {
|
|
||||||
elements.clear();
|
|
||||||
if (componentList != null) componentList.removeAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param index the index to retrieve the element from
|
|
||||||
* @return the element located at the index
|
|
||||||
* @see java.util.List#get(int)
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
public E get(int index) { return elements.get(index); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the element at a specific index from this model and the corresponding
|
|
||||||
* component from the {@link ComponentList}.
|
|
||||||
*
|
|
||||||
* @param index the index of the element to remove
|
|
||||||
* @return the removed element
|
|
||||||
* @see java.util.List#remove(int)
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
public E remove(int index) {
|
|
||||||
if (componentList != null) componentList.remove(index);
|
|
||||||
return elements.remove(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the amount of elements in this list model
|
|
||||||
* @see java.util.List#size()
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
public int size() { return elements.size(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {@code true} if this model contains no elements
|
|
||||||
* @see java.util.List#isEmpty()
|
|
||||||
*/
|
|
||||||
public boolean isEmpty() { return elements.isEmpty(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return an iterator over the elements of this list model
|
|
||||||
* @see java.util.List#iterator()
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Iterator<E> iterator() {
|
|
||||||
return new Iterator<>() {
|
|
||||||
|
|
||||||
Iterator<E> iter = elements.iterator();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() { return iter.hasNext(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public E next() { return iter.next(); }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the component list displaying the elements of this model and triggers a
|
|
||||||
* synchronization.
|
|
||||||
*
|
|
||||||
* @param componentList the component list to set
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
void setComponentList(ComponentList<E> componentList) {
|
|
||||||
this.componentList = componentList;
|
|
||||||
if (componentList != null && componentList.getRenderer() != null) componentList.synchronizeModel();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package envoy.client.ui.list;
|
|
||||||
|
|
||||||
import javax.swing.JComponent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows a {@link ComponentList} convert its elements into Swing components
|
|
||||||
* that can be rendered.<br>
|
|
||||||
* <br>
|
|
||||||
* Project: <strong>envoy-client</strong><br>
|
|
||||||
* File: <strong>Renderer.java</strong><br>
|
|
||||||
* Created: <strong>25.01.2020</strong><br>
|
|
||||||
*
|
|
||||||
* @param <E> the type of object displayed in this list
|
|
||||||
* @author Kai S. K. Engelbart
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface Renderer<E> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides a Swing component representing a list element.
|
|
||||||
*
|
|
||||||
* @param list the list in which the component will be displayed
|
|
||||||
* @param value the list element that will be converted
|
|
||||||
* @param isSelected {@code true} if the user has selected the list cell in
|
|
||||||
* which the list element is rendered
|
|
||||||
* @return the component representing the list element
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
JComponent getListCellComponent(ComponentList<? extends E> list, E value);
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
package envoy.client.ui.list;
|
|
||||||
|
|
||||||
import javax.swing.JComponent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the selection of elements in a {@link ComponentList}.<br>
|
|
||||||
* <br>
|
|
||||||
* Project: <strong>envoy-client</strong>
|
|
||||||
* File: <strong>SelectionHandler.java</strong>
|
|
||||||
* Created: <strong>21.03.2020</strong>
|
|
||||||
*
|
|
||||||
* @author Kai S. K. Engelbart
|
|
||||||
* @param <E> the type of the underlying {@link ComponentList}
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface SelectionHandler<E> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notifies the handler about a selection.
|
|
||||||
*
|
|
||||||
* @param element the selected element
|
|
||||||
* @param component the selected component
|
|
||||||
* @param isSelected contains the selection state
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
void selectionChanged(E element, JComponent component, boolean isSelected);
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
/**
|
|
||||||
* This package defines a Swing component that can be used to display lists of
|
|
||||||
* other components to the user.
|
|
||||||
*
|
|
||||||
* @author Kai S. K. Engelbart
|
|
||||||
* @author Leon Hofmeister
|
|
||||||
* @author Maximilian Käfer
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
package envoy.client.ui.list;
|
|
@ -1,73 +0,0 @@
|
|||||||
package envoy.client.ui.list_component;
|
|
||||||
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.awt.Font;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
import envoy.client.data.Settings;
|
|
||||||
import envoy.client.event.SendEvent;
|
|
||||||
import envoy.client.ui.list.ComponentList;
|
|
||||||
import envoy.client.ui.primary.PrimaryButton;
|
|
||||||
import envoy.data.User;
|
|
||||||
import envoy.event.ContactOperationEvent;
|
|
||||||
import envoy.event.EventBus;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Project: <strong>envoy-client</strong>
|
|
||||||
* File: <strong>ContactSearchComponent.java</strong>
|
|
||||||
* Created: <strong>21.03.2020</strong>
|
|
||||||
*
|
|
||||||
* @author Kai S. K. Engelbart
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public class ContactSearchComponent extends JComponent {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 0L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param list the {@link ComponentList} that is used to display search results
|
|
||||||
* @param user the {@link User} that appears as a search result
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public ContactSearchComponent(ComponentList<? extends User> list, User user) {
|
|
||||||
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
|
|
||||||
|
|
||||||
setBackground(list.getBackground());
|
|
||||||
setForeground(list.getForeground());
|
|
||||||
|
|
||||||
JLabel display = new JLabel(user.getName());
|
|
||||||
display.setForeground(Settings.getInstance().getCurrentTheme().getTextColor());
|
|
||||||
display.setAlignmentX(Component.LEFT_ALIGNMENT);
|
|
||||||
display.setAlignmentY(Component.CENTER_ALIGNMENT);
|
|
||||||
display.setFont(new Font("Arial", Font.PLAIN, 16));
|
|
||||||
add(display);
|
|
||||||
|
|
||||||
PrimaryButton add = new PrimaryButton("+");
|
|
||||||
add.setFont(new Font("Arial", Font.PLAIN, 19));
|
|
||||||
add.setPreferredSize(new Dimension(45, 45));
|
|
||||||
add.setMinimumSize(new Dimension(45, 45));
|
|
||||||
add.setMaximumSize(new Dimension(45, 45));
|
|
||||||
|
|
||||||
add.setBackground(list.getBackground());
|
|
||||||
add.setForeground(list.getForeground());
|
|
||||||
|
|
||||||
add.addActionListener(evt -> {
|
|
||||||
ContactOperationEvent contactsOperationEvent = new ContactOperationEvent(user, ContactOperationEvent.Operation.ADD);
|
|
||||||
EventBus.getInstance().dispatch(contactsOperationEvent);
|
|
||||||
EventBus.getInstance().dispatch(new SendEvent(contactsOperationEvent));
|
|
||||||
});
|
|
||||||
|
|
||||||
add(add);
|
|
||||||
|
|
||||||
// Define some space to the messages below
|
|
||||||
setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 0, 15, 0), BorderFactory.createEtchedBorder()));
|
|
||||||
|
|
||||||
// Define a maximum height of 50px
|
|
||||||
Dimension size = new Dimension(435, 50);
|
|
||||||
setMaximumSize(size);
|
|
||||||
setMinimumSize(size);
|
|
||||||
setPreferredSize(size);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,127 +0,0 @@
|
|||||||
package envoy.client.ui.list_component;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.EnumMap;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
import envoy.client.data.Chat;
|
|
||||||
import envoy.client.data.Settings;
|
|
||||||
import envoy.client.ui.Color;
|
|
||||||
import envoy.client.ui.IconUtil;
|
|
||||||
import envoy.client.ui.list.ComponentList;
|
|
||||||
import envoy.data.Message;
|
|
||||||
import envoy.data.Message.MessageStatus;
|
|
||||||
import envoy.data.User;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Project: <strong>envoy-client</strong>
|
|
||||||
* File: <strong>MessageComponent.java</strong>
|
|
||||||
* Created: <strong>21.03.2020</strong>
|
|
||||||
*
|
|
||||||
* @author Kai S. K. Engelbart
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public class MessageComponent extends JPanel {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 0L;
|
|
||||||
|
|
||||||
private static EnumMap<MessageStatus, ImageIcon> statusIcons;
|
|
||||||
private static ImageIcon forwardIcon;
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
statusIcons = IconUtil.loadByEnum(MessageStatus.class, 16);
|
|
||||||
forwardIcon = IconUtil.load("/icons/forward.png", 16);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param list the {@link ComponentList} that displays this {@link Chat}
|
|
||||||
* @param message the {@link Message} to display
|
|
||||||
* @param senderId the id of the {@link User} who sends messages from this
|
|
||||||
* account
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public MessageComponent(ComponentList<? extends Message> list, Message message, long senderId) {
|
|
||||||
var width = list.getMaximumSize().width;
|
|
||||||
final var theme = Settings.getInstance().getCurrentTheme();
|
|
||||||
final int padding = (int) (width * 0.35);
|
|
||||||
|
|
||||||
GridBagLayout gbl_panel = new GridBagLayout();
|
|
||||||
gbl_panel.columnWidths = new int[] { 1, 1 };
|
|
||||||
gbl_panel.rowHeights = new int[] { 1, 1 };
|
|
||||||
gbl_panel.columnWeights = new double[] { 1, 1 };
|
|
||||||
gbl_panel.rowWeights = new double[] { 1, 1 };
|
|
||||||
|
|
||||||
setLayout(gbl_panel);
|
|
||||||
setBackground(theme.getCellColor());
|
|
||||||
|
|
||||||
// Date Label - The Label that displays the creation date of a message
|
|
||||||
var dateLabel = new JLabel(new SimpleDateFormat("dd.MM.yyyy HH:mm").format(message.getCreationDate()));
|
|
||||||
dateLabel.setForeground(theme.getDateColor());
|
|
||||||
dateLabel.setAlignmentX(1f);
|
|
||||||
dateLabel.setFont(new Font("Arial", Font.PLAIN, 12));
|
|
||||||
dateLabel.setPreferredSize(dateLabel.getPreferredSize());
|
|
||||||
|
|
||||||
var gbc_dateLabel = new GridBagConstraints();
|
|
||||||
gbc_dateLabel.fill = GridBagConstraints.BOTH;
|
|
||||||
gbc_dateLabel.gridx = 0;
|
|
||||||
gbc_dateLabel.gridy = 0;
|
|
||||||
add(dateLabel, gbc_dateLabel);
|
|
||||||
|
|
||||||
// Message area - The JTextArea that displays the text content of a message.
|
|
||||||
var messageTextArea = new JTextArea(message.getText());
|
|
||||||
messageTextArea.setLineWrap(true);
|
|
||||||
messageTextArea.setWrapStyleWord(true);
|
|
||||||
messageTextArea.setForeground(theme.getTextColor());
|
|
||||||
messageTextArea.setAlignmentX(0.5f);
|
|
||||||
messageTextArea.setBackground(theme.getCellColor());
|
|
||||||
messageTextArea.setEditable(false);
|
|
||||||
var font = new Font("Arial", Font.PLAIN, 14);
|
|
||||||
messageTextArea.setFont(font);
|
|
||||||
messageTextArea.setSize(width - padding - 16, 10);
|
|
||||||
|
|
||||||
var gbc_messageTextArea = new GridBagConstraints();
|
|
||||||
gbc_messageTextArea.fill = GridBagConstraints.HORIZONTAL;
|
|
||||||
gbc_messageTextArea.gridx = 0;
|
|
||||||
gbc_messageTextArea.gridy = 1;
|
|
||||||
add(messageTextArea, gbc_messageTextArea);
|
|
||||||
|
|
||||||
// Status Label - displays the status of the message
|
|
||||||
var statusLabel = new JLabel(statusIcons.get(message.getStatus()));
|
|
||||||
|
|
||||||
var gbc_statusLabel = new GridBagConstraints();
|
|
||||||
gbc_statusLabel.gridx = 1;
|
|
||||||
gbc_statusLabel.gridy = 1;
|
|
||||||
add(statusLabel, gbc_statusLabel);
|
|
||||||
|
|
||||||
// Forwarding
|
|
||||||
if (message.isForwarded()) {
|
|
||||||
var forwardLabel = new JLabel("Forwarded", forwardIcon, SwingConstants.CENTER);
|
|
||||||
forwardLabel.setBackground(getBackground());
|
|
||||||
forwardLabel.setForeground(Color.lightGray);
|
|
||||||
|
|
||||||
var gbc_forwardLabel = new GridBagConstraints();
|
|
||||||
gbc_forwardLabel.fill = GridBagConstraints.BOTH;
|
|
||||||
gbc_forwardLabel.gridx = 1;
|
|
||||||
gbc_forwardLabel.gridy = 0;
|
|
||||||
add(forwardLabel, gbc_forwardLabel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define an etched border and some space to the messages below
|
|
||||||
var ours = senderId == message.getSenderID();
|
|
||||||
setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, ours ? padding : 10, 10, ours ? 0 : padding),
|
|
||||||
BorderFactory.createEtchedBorder()));
|
|
||||||
|
|
||||||
var size = new Dimension(width - 50, getPreferredSize().height);
|
|
||||||
|
|
||||||
setPreferredSize(size);
|
|
||||||
setMinimumSize(size);
|
|
||||||
setMaximumSize(size);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
package envoy.client.ui.list_component;
|
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
|
|
||||||
import envoy.client.data.Settings;
|
|
||||||
import envoy.client.ui.Color;
|
|
||||||
import envoy.client.ui.Theme;
|
|
||||||
import envoy.data.User;
|
|
||||||
import envoy.data.User.UserStatus;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays a {@link User}.<br>
|
|
||||||
* <br>
|
|
||||||
* Project: <strong>envoy-client</strong>
|
|
||||||
* File: <strong>UserComponent.java</strong>
|
|
||||||
* Created: <strong>21.03.2020</strong>
|
|
||||||
*
|
|
||||||
* @author Kai S. K. Engelbart
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public class UserComponent extends JPanel {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 0L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param user the {@link User} whose information is displayed
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public UserComponent(User user) {
|
|
||||||
final Theme theme = Settings.getInstance().getCurrentTheme();
|
|
||||||
|
|
||||||
setLayout(new BorderLayout());
|
|
||||||
|
|
||||||
// Panel background
|
|
||||||
setBackground(theme.getCellColor());
|
|
||||||
setOpaque(true);
|
|
||||||
setPreferredSize(new Dimension(100, 35));
|
|
||||||
|
|
||||||
// TODO add profile picture support in BorderLayout.West
|
|
||||||
|
|
||||||
JLabel username = new JLabel(user.getName());
|
|
||||||
username.setForeground(theme.getUserNameColor());
|
|
||||||
add(username, BorderLayout.CENTER);
|
|
||||||
|
|
||||||
final UserStatus status = user.getStatus();
|
|
||||||
JLabel statusLabel = new JLabel(status.toString());
|
|
||||||
Color foreground;
|
|
||||||
switch (status) {
|
|
||||||
case AWAY:
|
|
||||||
foreground = Color.yellow;
|
|
||||||
break;
|
|
||||||
case BUSY:
|
|
||||||
foreground = Color.blue;
|
|
||||||
break;
|
|
||||||
case ONLINE:
|
|
||||||
foreground = Color.green;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
foreground = Color.lightGray;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
statusLabel.setForeground(foreground);
|
|
||||||
add(statusLabel, BorderLayout.NORTH);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
/**
|
|
||||||
* This package contains swing components that can be displayed by
|
|
||||||
* {@link envoy.client.ui.list.ComponentList}.<br>
|
|
||||||
* <br>
|
|
||||||
* Project: <strong>envoy-client</strong><br>
|
|
||||||
* File: <strong>package-info.java</strong><br>
|
|
||||||
* Created: <strong>21 Mar 2020</strong><br>
|
|
||||||
*
|
|
||||||
* @author Leon Hofmeister
|
|
||||||
* @author Kai S. K. Engelbart
|
|
||||||
* @author Maximilian Käfer
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
package envoy.client.ui.list_component;
|
|
@ -1,65 +0,0 @@
|
|||||||
package envoy.client.ui.renderer;
|
|
||||||
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JList;
|
|
||||||
import javax.swing.ListCellRenderer;
|
|
||||||
|
|
||||||
import envoy.client.data.Settings;
|
|
||||||
import envoy.data.User;
|
|
||||||
import envoy.data.User.UserStatus;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines how the {@code UserList} is displayed.<br>
|
|
||||||
* <br>
|
|
||||||
* Project: <strong>envoy-client</strong><br>
|
|
||||||
* File: <strong>UserListRenderer.java</strong><br>
|
|
||||||
* Created: <strong>12 Oct 2019</strong><br>
|
|
||||||
*
|
|
||||||
* @author Kai S. K. Engelbart
|
|
||||||
* @author Maximilian Käfer
|
|
||||||
* @since Envoy Client v0.1-alpha
|
|
||||||
*/
|
|
||||||
public class UserListRenderer extends JLabel implements ListCellRenderer<User> {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 5164417379767181198L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Component getListCellRendererComponent(JList<? extends User> list, User value, int index, boolean isSelected, boolean cellHasFocus) {
|
|
||||||
if (isSelected) {
|
|
||||||
setBackground(list.getSelectionBackground());
|
|
||||||
setForeground(list.getSelectionForeground());
|
|
||||||
} else {
|
|
||||||
setBackground(list.getBackground());
|
|
||||||
setForeground(list.getForeground());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable background rendering
|
|
||||||
setOpaque(true);
|
|
||||||
|
|
||||||
final String name = value.getName();
|
|
||||||
final UserStatus status = value.getStatus();
|
|
||||||
|
|
||||||
this.setPreferredSize(new Dimension(100, 35));
|
|
||||||
|
|
||||||
// Getting the UserNameColor of the current theme
|
|
||||||
String textColor = null;
|
|
||||||
textColor = Settings.getInstance().getCurrentTheme().getUserNameColor().toHex();
|
|
||||||
switch (status) {
|
|
||||||
case ONLINE:
|
|
||||||
setText(String
|
|
||||||
.format("<html><p style=\"color:#03fc20\"><b><small>%s</b></small><br><p style=\"color:%s\">%s</html>", status, textColor, name));
|
|
||||||
break;
|
|
||||||
case OFFLINE:
|
|
||||||
setText(String
|
|
||||||
.format("<html><p style=\"color:#fc0303\"><b><small>%s</b></small><br><p style=\"color:%s\">%s</html>", status, textColor, name));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
/**
|
|
||||||
* This package contains all Envoy-specific renderers for lists that store an
|
|
||||||
* arbitrary number of JComponents.<br>
|
|
||||||
* <br>
|
|
||||||
* Project: <strong>envoy-client</strong><br>
|
|
||||||
* File: <strong>package-info.java</strong><br>
|
|
||||||
* Created: <strong>14 Mar 2020</strong><br>
|
|
||||||
*
|
|
||||||
* @author Leon Hofmeister
|
|
||||||
* @author Kai S. K. Engelbart
|
|
||||||
* @author Maximilian Käfer
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
package envoy.client.ui.renderer;
|
|
Reference in New Issue
Block a user