added missing merge content,mnemonics support(ContextMenu)and Nullchecks
This commit is contained in:
parent
a6b6a68a56
commit
ad59fe0958
@ -46,15 +46,18 @@ public class ContextMenu extends JPopupMenu {
|
|||||||
|
|
||||||
private Map<String, ActionListener> items = new HashMap<>();
|
private Map<String, ActionListener> items = new HashMap<>();
|
||||||
private Map<String, Icon> icons = new HashMap<>();
|
private Map<String, Icon> icons = new HashMap<>();
|
||||||
|
private Map<String, Integer> mnemonics = new HashMap<>();
|
||||||
|
|
||||||
private ButtonGroup radioButtonGroup = new ButtonGroup();
|
private ButtonGroup radioButtonGroup = new ButtonGroup();
|
||||||
private boolean built = false;
|
private boolean built = false;
|
||||||
private boolean visible = false;
|
private boolean visible = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param parent the component which will call this
|
||||||
|
* {@link ContextMenu}
|
||||||
* @since Envoy v0.1-beta
|
* @since Envoy v0.1-beta
|
||||||
*/
|
*/
|
||||||
public ContextMenu() {}
|
public ContextMenu(Component parent) { setInvoker(parent); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param label the string that a UI may use to display as a title
|
* @param label the string that a UI may use to display as a title
|
||||||
@ -67,13 +70,18 @@ public class ContextMenu extends JPopupMenu {
|
|||||||
* Only keys in here will have an Icon displayed. More
|
* Only keys in here will have an Icon displayed. More
|
||||||
* precisely, all keys here not included in the first
|
* precisely, all keys here not included in the first
|
||||||
* map will be thrown out.
|
* 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 v0.1-beta
|
* @since Envoy v0.1-beta
|
||||||
*/
|
*/
|
||||||
public ContextMenu(String label, Component parent, Map<String, ActionListener> itemsWithActions, Map<String, Icon> itemIcons) {
|
public ContextMenu(String label, Component parent, Map<String, ActionListener> itemsWithActions, Map<String, Icon> itemIcons,
|
||||||
|
Map<String, Integer> itemMnemonics) {
|
||||||
super(label);
|
super(label);
|
||||||
setInvoker(parent);
|
setInvoker(parent);
|
||||||
this.items = (itemsWithActions != null) ? itemsWithActions : items;
|
this.items = (itemsWithActions != null) ? itemsWithActions : items;
|
||||||
this.icons = (itemIcons != null) ? itemIcons : icons;
|
this.icons = (itemIcons != null) ? itemIcons : icons;
|
||||||
|
this.mnemonics = (itemMnemonics != null) ? itemMnemonics : mnemonics;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,27 +95,20 @@ public class ContextMenu extends JPopupMenu {
|
|||||||
public ContextMenu build() {
|
public ContextMenu build() {
|
||||||
items.forEach((text, action) -> {
|
items.forEach((text, action) -> {
|
||||||
// case radio button wanted
|
// case radio button wanted
|
||||||
|
AbstractButton item;
|
||||||
if (text.startsWith(radioButtonMenuItem)) {
|
if (text.startsWith(radioButtonMenuItem)) {
|
||||||
var item = new JRadioButtonMenuItem(text.substring(radioButtonMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
|
item = new JRadioButtonMenuItem(text.substring(radioButtonMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
|
||||||
item.addActionListener(action);
|
|
||||||
radioButtonGroup.add(item);
|
radioButtonGroup.add(item);
|
||||||
add(item);
|
|
||||||
// case check box wanted
|
// case check box wanted
|
||||||
} else if (text.startsWith(checkboxMenuItem)) {
|
} else if (text.startsWith(checkboxMenuItem))
|
||||||
var item = new JCheckBoxMenuItem(text.substring(checkboxMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
|
item = new JCheckBoxMenuItem(text.substring(checkboxMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
|
||||||
item.addActionListener(action);
|
|
||||||
add(item);
|
|
||||||
// case sub-menu wanted
|
// case sub-menu wanted
|
||||||
} else if (text.startsWith(subMenuItem)) {
|
else if (text.startsWith(subMenuItem)) item = new JMenu(text.substring(subMenuItem.length()));
|
||||||
var 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.addActionListener(action);
|
||||||
|
if (mnemonics.containsKey(text)) item.setMnemonic(mnemonics.get(text));
|
||||||
add(item);
|
add(item);
|
||||||
} else {
|
|
||||||
// normal JMenuItem wanted
|
|
||||||
var item = new JMenuItem(text, icons.containsKey(text) ? icons.get(text) : null);
|
|
||||||
item.addActionListener(action);
|
|
||||||
add(item);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
getInvoker().addMouseListener(getShowingListener());
|
getInvoker().addMouseListener(getShowingListener());
|
||||||
built = true;
|
built = true;
|
||||||
@ -136,10 +137,10 @@ public class ContextMenu extends JPopupMenu {
|
|||||||
private void action(MouseEvent e) {
|
private void action(MouseEvent e) {
|
||||||
if (!built) build();
|
if (!built) build();
|
||||||
if (e.isPopupTrigger()) {
|
if (e.isPopupTrigger()) {
|
||||||
show(e.getComponent(), e.getX(), e.getY());
|
|
||||||
// hides the menu if already visible
|
// hides the menu if already visible
|
||||||
visible = !visible;
|
visible = !visible;
|
||||||
setVisible(visible);
|
if (visible) show(e.getComponent(), e.getX(), e.getY());
|
||||||
|
else setVisible(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,6 +156,7 @@ public class ContextMenu extends JPopupMenu {
|
|||||||
removeAll();
|
removeAll();
|
||||||
items = new HashMap<>();
|
items = new HashMap<>();
|
||||||
icons = new HashMap<>();
|
icons = new HashMap<>();
|
||||||
|
mnemonics = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -181,4 +183,19 @@ public class ContextMenu extends JPopupMenu {
|
|||||||
* @since Envoy v0.1-beta
|
* @since Envoy v0.1-beta
|
||||||
*/
|
*/
|
||||||
public void setIcons(Map<String, Icon> icons) { this.icons = icons; }
|
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 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 v0.1-beta
|
||||||
|
*/
|
||||||
|
public void setMnemonics(Map<String, Integer> mnemonics) { this.mnemonics = mnemonics; }
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import envoy.client.event.MessageCreationEvent;
|
|||||||
import envoy.client.event.ThemeChangeEvent;
|
import envoy.client.event.ThemeChangeEvent;
|
||||||
import envoy.client.net.Client;
|
import envoy.client.net.Client;
|
||||||
import envoy.client.net.WriteProxy;
|
import envoy.client.net.WriteProxy;
|
||||||
|
import envoy.client.ui.ContextMenu;
|
||||||
import envoy.client.ui.Theme;
|
import envoy.client.ui.Theme;
|
||||||
import envoy.client.ui.list.ComponentList;
|
import envoy.client.ui.list.ComponentList;
|
||||||
import envoy.client.ui.list.ComponentListModel;
|
import envoy.client.ui.list.ComponentListModel;
|
||||||
@ -29,6 +30,7 @@ import envoy.client.ui.primary.PrimaryButton;
|
|||||||
import envoy.client.ui.primary.PrimaryScrollPane;
|
import envoy.client.ui.primary.PrimaryScrollPane;
|
||||||
import envoy.client.ui.primary.PrimaryTextArea;
|
import envoy.client.ui.primary.PrimaryTextArea;
|
||||||
import envoy.client.ui.renderer.ContactsSearchRenderer;
|
import envoy.client.ui.renderer.ContactsSearchRenderer;
|
||||||
|
import envoy.client.ui.renderer.MessageListRenderer;
|
||||||
import envoy.client.ui.renderer.UserListRenderer;
|
import envoy.client.ui.renderer.UserListRenderer;
|
||||||
import envoy.client.ui.settings.SettingsScreen;
|
import envoy.client.ui.settings.SettingsScreen;
|
||||||
import envoy.data.Message;
|
import envoy.data.Message;
|
||||||
@ -74,6 +76,7 @@ public class ChatWindow extends JFrame {
|
|||||||
private JTextPane textPane = new JTextPane();
|
private JTextPane textPane = new JTextPane();
|
||||||
private PrimaryButton postButton = new PrimaryButton("Post");
|
private PrimaryButton postButton = new PrimaryButton("Post");
|
||||||
private PrimaryButton settingsButton = new PrimaryButton("Settings");
|
private PrimaryButton settingsButton = new PrimaryButton("Settings");
|
||||||
|
@SuppressWarnings("unused")
|
||||||
private JPopupMenu contextMenu;
|
private JPopupMenu contextMenu;
|
||||||
|
|
||||||
// Contacts Header
|
// Contacts Header
|
||||||
@ -107,6 +110,7 @@ public class ChatWindow extends JFrame {
|
|||||||
public ChatWindow() {
|
public ChatWindow() {
|
||||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
setBounds(100, 100, 600, 800);
|
setBounds(100, 100, 600, 800);
|
||||||
|
setMinimumSize(new Dimension(400, 300));
|
||||||
setTitle("Envoy");
|
setTitle("Envoy");
|
||||||
setLocationRelativeTo(null);
|
setLocationRelativeTo(null);
|
||||||
setIconImage(Toolkit.getDefaultToolkit().createImage(getClass().getClassLoader().getResource("envoy_logo.png")));
|
setIconImage(Toolkit.getDefaultToolkit().createImage(getClass().getClassLoader().getResource("envoy_logo.png")));
|
||||||
@ -121,12 +125,6 @@ public class ChatWindow extends JFrame {
|
|||||||
contentPane.setLayout(gbl_contentPane);
|
contentPane.setLayout(gbl_contentPane);
|
||||||
|
|
||||||
messageList.setBorder(new EmptyBorder(space, space, space, space));
|
messageList.setBorder(new EmptyBorder(space, space, space, space));
|
||||||
messageList.addMouseListener(new MouseAdapter() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseClicked(MouseEvent e) {
|
|
||||||
if (e.isPopupTrigger()) {
|
|
||||||
contextMenu = new JPopupMenu("message options");
|
|
||||||
Map<String, ActionListener> commands = new HashMap<>() {
|
Map<String, ActionListener> commands = new HashMap<>() {
|
||||||
|
|
||||||
private static final long serialVersionUID = -2755235774946990126L;
|
private static final long serialVersionUID = -2755235774946990126L;
|
||||||
@ -134,8 +132,7 @@ public class ChatWindow extends JFrame {
|
|||||||
{
|
{
|
||||||
put("forward selected message",
|
put("forward selected message",
|
||||||
evt -> forwardMessageToMultipleUsers(messageList.getSelected().get(0),
|
evt -> forwardMessageToMultipleUsers(messageList.getSelected().get(0),
|
||||||
ContactsChooserDialog
|
ContactsChooserDialog.showForwardingDialog("Forward selected message to", messageList.getSelected().get(0), client)));
|
||||||
.showForwardingDialog("Forward selected message to", messageList.getSelected().get(0), client)));
|
|
||||||
put("copy", evt -> {
|
put("copy", evt -> {
|
||||||
// TODO should be enhanced to allow also copying of message attachments,
|
// TODO should be enhanced to allow also copying of message attachments,
|
||||||
// especially pictures
|
// especially pictures
|
||||||
@ -148,17 +145,17 @@ public class ChatWindow extends JFrame {
|
|||||||
put("quote", evt -> {});
|
put("quote", evt -> {});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
commands.forEach((name, action) -> { var item = new JMenuItem(name); item.addActionListener(action); contextMenu.add(item); });
|
contextMenu = new ContextMenu(null, messageList, commands, null, null).build();
|
||||||
contextMenu.show(e.getComponent(), e.getX(), e.getY());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
scrollPane.setViewportView(messageList);
|
scrollPane.setViewportView(messageList);
|
||||||
scrollPane.addComponentListener(new ComponentAdapter() {
|
scrollPane.addComponentListener(new ComponentAdapter() {
|
||||||
|
|
||||||
// updates list elements when list is resized
|
// Update list elements when scroll pane (and thus list) is resized
|
||||||
@Override
|
@Override
|
||||||
public void componentResized(ComponentEvent e) { messageList.synchronizeModel(); }
|
public void componentResized(ComponentEvent e) {
|
||||||
|
messageList.setMaximumSize(new Dimension(scrollPane.getWidth(), Integer.MAX_VALUE));
|
||||||
|
messageList.synchronizeModel();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||||
|
|
||||||
@ -600,7 +597,7 @@ public class ChatWindow extends JFrame {
|
|||||||
private void readCurrentChat() {
|
private void readCurrentChat() {
|
||||||
try {
|
try {
|
||||||
currentChat.read(writeProxy);
|
currentChat.read(writeProxy);
|
||||||
messageList.synchronizeModel();
|
if (messageList.getRenderer() != null) messageList.synchronizeModel();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
logger.log(Level.WARNING, "Couldn't notify server about message status change", e);
|
logger.log(Level.WARNING, "Couldn't notify server about message status change", e);
|
||||||
@ -644,6 +641,8 @@ public class ChatWindow extends JFrame {
|
|||||||
this.localDb = localDb;
|
this.localDb = localDb;
|
||||||
this.writeProxy = writeProxy;
|
this.writeProxy = writeProxy;
|
||||||
|
|
||||||
|
messageList.setRenderer(new MessageListRenderer(client.getSender().getId()));
|
||||||
|
|
||||||
// Load users and chats
|
// Load users and chats
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
localDb.getUsers().values().forEach(user -> {
|
localDb.getUsers().values().forEach(user -> {
|
||||||
|
@ -31,6 +31,12 @@ public class ComponentList<E> extends JPanel {
|
|||||||
|
|
||||||
private static final long serialVersionUID = 1759644503942876737L;
|
private static final long serialVersionUID = 1759644503942876737L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the layout of this {@link ComponentList} to be a vertically oriented
|
||||||
|
* BoxLayout.
|
||||||
|
*
|
||||||
|
* @since Envoy v0.1-beta
|
||||||
|
*/
|
||||||
public ComponentList() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); }
|
public ComponentList() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,7 +80,7 @@ public class ComponentList<E> extends JPanel {
|
|||||||
// Synchronize with new model
|
// Synchronize with new model
|
||||||
this.model = model;
|
this.model = model;
|
||||||
if (model != null) this.model.setComponentList(this);
|
if (model != null) this.model.setComponentList(this);
|
||||||
synchronizeModel();
|
if (renderer != null) synchronizeModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,10 +90,12 @@ public class ComponentList<E> extends JPanel {
|
|||||||
* @since Envoy v0.3-alpha
|
* @since Envoy v0.3-alpha
|
||||||
*/
|
*/
|
||||||
public void synchronizeModel() {
|
public void synchronizeModel() {
|
||||||
|
if (model != null && renderer != null) {
|
||||||
removeAll();
|
removeAll();
|
||||||
if (model != null) model.forEach(this::add);
|
model.forEach(this::add);
|
||||||
revalidate();
|
revalidate();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an object to the list by rendering it with the current
|
* Adds an object to the list by rendering it with the current
|
||||||
@ -112,10 +120,12 @@ public class ComponentList<E> extends JPanel {
|
|||||||
clearSelections();
|
clearSelections();
|
||||||
currentSelections.add(index);
|
currentSelections.add(index);
|
||||||
}
|
}
|
||||||
|
if (renderer != null) {
|
||||||
final JComponent component = renderer.getListCellComponent(this, elem, isSelected);
|
final JComponent component = renderer.getListCellComponent(this, elem, isSelected);
|
||||||
component.addMouseListener(getSelectionListener(index));
|
component.addMouseListener(getSelectionListener(index));
|
||||||
add(component, index);
|
add(component, index);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param componentIndex the index of the list component to which the mouse
|
* @param componentIndex the index of the list component to which the mouse
|
||||||
|
@ -115,6 +115,6 @@ public final class ComponentListModel<E> implements Iterable<E>, Serializable {
|
|||||||
*/
|
*/
|
||||||
void setComponentList(ComponentList<E> componentList) {
|
void setComponentList(ComponentList<E> componentList) {
|
||||||
this.componentList = componentList;
|
this.componentList = componentList;
|
||||||
if (componentList != null) componentList.synchronizeModel();
|
if (componentList != null && componentList.getRenderer() != null) componentList.synchronizeModel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user