diff --git a/src/main/java/envoy/client/ui/ContextMenu.java b/src/main/java/envoy/client/ui/ContextMenu.java index 1ba5d14..ff19ac8 100644 --- a/src/main/java/envoy/client/ui/ContextMenu.java +++ b/src/main/java/envoy/client/ui/ContextMenu.java @@ -50,7 +50,6 @@ public class ContextMenu extends JPopupMenu { private ButtonGroup radioButtonGroup = new ButtonGroup(); private boolean built = false; - private boolean visible = false; /** * @param parent the component which will call this @@ -110,8 +109,10 @@ public class ContextMenu extends JPopupMenu { if (mnemonics.containsKey(text)) item.setMnemonic(mnemonics.get(text)); add(item); }); - getInvoker().addMouseListener(getShowingListener()); - built = true; + if (getInvoker() != null) { + getInvoker().addMouseListener(getShowingListener()); + built = true; + } return this; } @@ -138,10 +139,8 @@ public class ContextMenu extends JPopupMenu { if (!built) build(); if (e.isPopupTrigger()) { // hides the menu if already visible - visible = !visible; - if (visible) show(e.getComponent(), e.getX(), e.getY()); + if (!isVisible()) show(e.getComponent(), e.getX(), e.getY()); else setVisible(false); - } } }; diff --git a/src/main/java/envoy/client/ui/MessageComponent.java b/src/main/java/envoy/client/ui/MessageComponent.java new file mode 100644 index 0000000..045e725 --- /dev/null +++ b/src/main/java/envoy/client/ui/MessageComponent.java @@ -0,0 +1,120 @@ +package envoy.client.ui; + +import java.awt.*; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.EnumMap; + +import javax.swing.*; + +import envoy.client.data.Settings; +import envoy.client.ui.list.ComponentList; +import envoy.data.Message; +import envoy.data.Message.MessageStatus; + +/** + * Project: envoy-client + * File: MessageComponent.java + * Created: 21.03.2020 + * + * @author Kai S. K. Engelbart + * @since Envoy v0.1-beta + */ +public class MessageComponent extends JPanel { + + private static final long serialVersionUID = 103920706139926996L; + + private static EnumMap 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(); + } + } + + public MessageComponent(ComponentList list, Message message, boolean isSelected, long senderId) { + this(list.getMaximumSize().width, message, isSelected, senderId); + } + + public MessageComponent(int width, Message message, boolean isSelected, long senderId) { + final var theme = Settings.getInstance().getThemes().get(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(isSelected ? theme.getSelectionColor() : 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.getMessageTextColor()); + 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); + } +} diff --git a/src/main/java/envoy/client/ui/container/ChatWindow.java b/src/main/java/envoy/client/ui/container/ChatWindow.java index 7d883c2..808a4f7 100644 --- a/src/main/java/envoy/client/ui/container/ChatWindow.java +++ b/src/main/java/envoy/client/ui/container/ChatWindow.java @@ -125,16 +125,14 @@ public class ChatWindow extends JFrame { gbl_contentPane.rowWeights = new double[] { 0.03, 0.001, 1.0, 0.005 }; contentPane.setLayout(gbl_contentPane); - messageList.setBorder(new EmptyBorder(space, space, space, space)); - messageList.setSelectionMode(SelectionMode.SINGLE); + // ContextMenu Map commands = new HashMap<>() { private static final long serialVersionUID = -2755235774946990126L; { put("forward selected message", - evt -> forwardMessageToMultipleUsers(messageList - .getSingleSelectedElement(), + evt -> forwardMessageToMultipleUsers(messageList.getSingleSelectedElement(), ContactsChooserDialog .showForwardingDialog("Forward selected message to", messageList.getSingleSelectedElement(), client))); put("copy", evt -> { @@ -151,6 +149,11 @@ public class ChatWindow extends JFrame { }; contextMenu = new ContextMenu(null, messageList, commands, null, null).build(); + messageList.setBorder(new EmptyBorder(space, space, space, space)); + messageList.setSelectionMode(SelectionMode.SINGLE); + messageList.setSelectionListener((list, comp) -> + contextMenu.show(comp, 0, 0)); + scrollPane.setViewportView(messageList); scrollPane.addComponentListener(new ComponentAdapter() { diff --git a/src/main/java/envoy/client/ui/container/ContactsChooserDialog.java b/src/main/java/envoy/client/ui/container/ContactsChooserDialog.java index 467d43f..3add91a 100644 --- a/src/main/java/envoy/client/ui/container/ContactsChooserDialog.java +++ b/src/main/java/envoy/client/ui/container/ContactsChooserDialog.java @@ -76,7 +76,7 @@ public class ContactsChooserDialog extends JDialog { dialog.setVisible(true); dialog.repaint(); dialog.revalidate(); - return results.size() > 0 ? results : null; + return results; } /** diff --git a/src/main/java/envoy/client/ui/list/ComponentList.java b/src/main/java/envoy/client/ui/list/ComponentList.java index 199946d..a4b4953 100644 --- a/src/main/java/envoy/client/ui/list/ComponentList.java +++ b/src/main/java/envoy/client/ui/list/ComponentList.java @@ -5,6 +5,7 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.HashSet; import java.util.Set; +import java.util.function.BiConsumer; import javax.swing.*; @@ -23,10 +24,11 @@ import javax.swing.*; */ public class ComponentList extends JPanel { - private ComponentListModel model; - private ComponentListCellRenderer renderer; - private SelectionMode selectionMode = SelectionMode.NONE; - private Set selection = new HashSet<>(); + private ComponentListModel model; + private ComponentListCellRenderer renderer; + private SelectionMode selectionMode = SelectionMode.NONE; + private Set selection = new HashSet<>(); + private BiConsumer, JComponent> selectionListener; private static final long serialVersionUID = 1759644503942876737L; @@ -142,6 +144,9 @@ public class ComponentList extends JPanel { // Update element updateElement(index, true); + + // Trigger selection listener + if (selectionListener != null) selectionListener.accept(this, (JComponent) getComponents()[index]); } } @@ -273,4 +278,14 @@ public class ComponentList extends JPanel { * @since Envoy v0.1-beta */ public void setSelectionMode(SelectionMode selectionMode) { this.selectionMode = selectionMode; } + + /** + * @return the selectionListener + */ + public BiConsumer, JComponent> getSelectionListener() { return selectionListener; } + + /** + * @param selectionListener the selectionListener to set + */ + public void setSelectionListener(BiConsumer, JComponent> selectionListener) { this.selectionListener = selectionListener; } } diff --git a/src/main/java/envoy/client/ui/renderer/MessageListRenderer.java b/src/main/java/envoy/client/ui/renderer/MessageListRenderer.java index 27a5a0d..a3c20bd 100644 --- a/src/main/java/envoy/client/ui/renderer/MessageListRenderer.java +++ b/src/main/java/envoy/client/ui/renderer/MessageListRenderer.java @@ -1,19 +1,11 @@ package envoy.client.ui.renderer; -import java.awt.*; -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.EnumMap; +import javax.swing.JPanel; -import javax.swing.*; - -import envoy.client.data.Settings; -import envoy.client.ui.Color; -import envoy.client.ui.IconUtil; +import envoy.client.ui.MessageComponent; import envoy.client.ui.list.ComponentList; import envoy.client.ui.list.ComponentListCellRenderer; import envoy.data.Message; -import envoy.data.Message.MessageStatus; /** * Defines how a message is displayed.
@@ -29,18 +21,6 @@ import envoy.data.Message.MessageStatus; */ public class MessageListRenderer implements ComponentListCellRenderer { - private static EnumMap 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(); - } - } - private final long senderId; /** @@ -57,84 +37,6 @@ public class MessageListRenderer implements ComponentListCellRenderer { @Override public JPanel getListCellComponent(ComponentList list, Message message, boolean isSelected) { - final var theme = Settings.getInstance().getThemes().get(Settings.getInstance().getCurrentTheme()); - - // Panel - final var panel = new JPanel(); - final int padding = (int) (list.getWidth() * 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 }; - - panel.setLayout(gbl_panel); - panel.setBackground(isSelected ? theme.getSelectionColor() : 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; - panel.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.getMessageTextColor()); - messageTextArea.setAlignmentX(0.5f); - messageTextArea.setBackground(theme.getCellColor()); - messageTextArea.setEditable(false); - var font = new Font("Arial", Font.PLAIN, 14); - messageTextArea.setFont(font); - messageTextArea.setSize(list.getMaximumSize().width - padding - 16, 10); - - var gbc_messageTextArea = new GridBagConstraints(); - gbc_messageTextArea.fill = GridBagConstraints.HORIZONTAL; - gbc_messageTextArea.gridx = 0; - gbc_messageTextArea.gridy = 1; - panel.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; - panel.add(statusLabel, gbc_statusLabel); - - // Forwarding - if (message.isForwarded()) { - var forwardLabel = new JLabel("Forwarded", forwardIcon, SwingConstants.CENTER); - forwardLabel.setBackground(panel.getBackground()); - forwardLabel.setForeground(Color.lightGray); - - var gbc_forwardLabel = new GridBagConstraints(); - gbc_forwardLabel.fill = GridBagConstraints.BOTH; - gbc_forwardLabel.gridx = 1; - gbc_forwardLabel.gridy = 0; - panel.add(forwardLabel, gbc_forwardLabel); - } - - // Define an etched border and some space to the messages below - var ours = senderId == message.getSenderId(); - panel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, ours ? padding : 0, 10, ours ? 0 : padding), - BorderFactory.createEtchedBorder())); - - var size = new Dimension(list.getMaximumSize().width - 50, panel.getPreferredSize().height); - - panel.setPreferredSize(size); - panel.setMinimumSize(size); - panel.setMaximumSize(size); - - return panel; + return new MessageComponent(list, message, isSelected, senderId); } }