added missing merge content,mnemonics support(ContextMenu)and Nullchecks
This commit is contained in:
		@@ -44,17 +44,20 @@ public class ContextMenu extends JPopupMenu {
 | 
			
		||||
	 */
 | 
			
		||||
	public static final String	subMenuItem			= "SubMI";
 | 
			
		||||
 | 
			
		||||
	private Map<String, ActionListener>	items	= new HashMap<>();
 | 
			
		||||
	private Map<String, Icon>			icons	= new HashMap<>();
 | 
			
		||||
	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 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
 | 
			
		||||
@@ -67,13 +70,18 @@ public class ContextMenu extends JPopupMenu {
 | 
			
		||||
	 *                         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 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);
 | 
			
		||||
		setInvoker(parent);
 | 
			
		||||
		this.items	= (itemsWithActions != null) ? itemsWithActions : items;
 | 
			
		||||
		this.icons	= (itemIcons != null) ? itemIcons : icons;
 | 
			
		||||
		this.items		= (itemsWithActions != null) ? itemsWithActions : items;
 | 
			
		||||
		this.icons		= (itemIcons != null) ? itemIcons : icons;
 | 
			
		||||
		this.mnemonics	= (itemMnemonics != null) ? itemMnemonics : mnemonics;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
@@ -87,27 +95,20 @@ public class ContextMenu extends JPopupMenu {
 | 
			
		||||
	public ContextMenu build() {
 | 
			
		||||
		items.forEach((text, action) -> {
 | 
			
		||||
			// case radio button wanted
 | 
			
		||||
			AbstractButton item;
 | 
			
		||||
			if (text.startsWith(radioButtonMenuItem)) {
 | 
			
		||||
				var item = new JRadioButtonMenuItem(text.substring(radioButtonMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
 | 
			
		||||
				item.addActionListener(action);
 | 
			
		||||
				item = new JRadioButtonMenuItem(text.substring(radioButtonMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
 | 
			
		||||
				radioButtonGroup.add(item);
 | 
			
		||||
				add(item);
 | 
			
		||||
				// case check box wanted
 | 
			
		||||
			} else if (text.startsWith(checkboxMenuItem)) {
 | 
			
		||||
				var item = new JCheckBoxMenuItem(text.substring(checkboxMenuItem.length()), icons.containsKey(text) ? icons.get(text) : null);
 | 
			
		||||
				item.addActionListener(action);
 | 
			
		||||
				add(item);
 | 
			
		||||
				// case sub-menu wanted
 | 
			
		||||
			} else if (text.startsWith(subMenuItem)) {
 | 
			
		||||
				var item = new JMenu(text.substring(subMenuItem.length()));
 | 
			
		||||
				item.addActionListener(action);
 | 
			
		||||
				add(item);
 | 
			
		||||
			} else {
 | 
			
		||||
				// normal JMenuItem wanted
 | 
			
		||||
				var item = new JMenuItem(text, icons.containsKey(text) ? icons.get(text) : null);
 | 
			
		||||
				item.addActionListener(action);
 | 
			
		||||
				add(item);
 | 
			
		||||
			}
 | 
			
		||||
			} 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);
 | 
			
		||||
			if (mnemonics.containsKey(text)) item.setMnemonic(mnemonics.get(text));
 | 
			
		||||
			add(item);
 | 
			
		||||
		});
 | 
			
		||||
		getInvoker().addMouseListener(getShowingListener());
 | 
			
		||||
		built = true;
 | 
			
		||||
@@ -136,10 +137,10 @@ public class ContextMenu extends JPopupMenu {
 | 
			
		||||
			private void action(MouseEvent e) {
 | 
			
		||||
				if (!built) build();
 | 
			
		||||
				if (e.isPopupTrigger()) {
 | 
			
		||||
					show(e.getComponent(), e.getX(), e.getY());
 | 
			
		||||
					// hides the menu if already visible
 | 
			
		||||
					visible = !visible;
 | 
			
		||||
					setVisible(visible);
 | 
			
		||||
					if (visible) show(e.getComponent(), e.getX(), e.getY());
 | 
			
		||||
					else setVisible(false);
 | 
			
		||||
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@@ -153,8 +154,9 @@ public class ContextMenu extends JPopupMenu {
 | 
			
		||||
	 */
 | 
			
		||||
	public void clear() {
 | 
			
		||||
		removeAll();
 | 
			
		||||
		items	= new HashMap<>();
 | 
			
		||||
		icons	= new HashMap<>();
 | 
			
		||||
		items		= new HashMap<>();
 | 
			
		||||
		icons		= new HashMap<>();
 | 
			
		||||
		mnemonics	= new HashMap<>();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
@@ -181,4 +183,19 @@ public class ContextMenu extends JPopupMenu {
 | 
			
		||||
	 * @since Envoy 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 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.net.Client;
 | 
			
		||||
import envoy.client.net.WriteProxy;
 | 
			
		||||
import envoy.client.ui.ContextMenu;
 | 
			
		||||
import envoy.client.ui.Theme;
 | 
			
		||||
import envoy.client.ui.list.ComponentList;
 | 
			
		||||
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.PrimaryTextArea;
 | 
			
		||||
import envoy.client.ui.renderer.ContactsSearchRenderer;
 | 
			
		||||
import envoy.client.ui.renderer.MessageListRenderer;
 | 
			
		||||
import envoy.client.ui.renderer.UserListRenderer;
 | 
			
		||||
import envoy.client.ui.settings.SettingsScreen;
 | 
			
		||||
import envoy.data.Message;
 | 
			
		||||
@@ -74,6 +76,7 @@ public class ChatWindow extends JFrame {
 | 
			
		||||
	private JTextPane				textPane				= new JTextPane();
 | 
			
		||||
	private PrimaryButton			postButton				= new PrimaryButton("Post");
 | 
			
		||||
	private PrimaryButton			settingsButton			= new PrimaryButton("Settings");
 | 
			
		||||
	@SuppressWarnings("unused")
 | 
			
		||||
	private JPopupMenu				contextMenu;
 | 
			
		||||
 | 
			
		||||
	// Contacts Header
 | 
			
		||||
@@ -107,6 +110,7 @@ public class ChatWindow extends JFrame {
 | 
			
		||||
	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")));
 | 
			
		||||
@@ -121,44 +125,37 @@ public class ChatWindow extends JFrame {
 | 
			
		||||
		contentPane.setLayout(gbl_contentPane);
 | 
			
		||||
 | 
			
		||||
		messageList.setBorder(new EmptyBorder(space, space, space, space));
 | 
			
		||||
		messageList.addMouseListener(new MouseAdapter() {
 | 
			
		||||
		Map<String, ActionListener> commands = new HashMap<>() {
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
			public void mouseClicked(MouseEvent e) {
 | 
			
		||||
				if (e.isPopupTrigger()) {
 | 
			
		||||
					contextMenu = new JPopupMenu("message options");
 | 
			
		||||
					Map<String, ActionListener> commands = new HashMap<>() {
 | 
			
		||||
			private static final long serialVersionUID = -2755235774946990126L;
 | 
			
		||||
 | 
			
		||||
						private static final long serialVersionUID = -2755235774946990126L;
 | 
			
		||||
 | 
			
		||||
						{
 | 
			
		||||
							put("forward selected message",
 | 
			
		||||
									evt -> forwardMessageToMultipleUsers(messageList.getSelected().get(0),
 | 
			
		||||
											ContactsChooserDialog
 | 
			
		||||
												.showForwardingDialog("Forward selected message to", messageList.getSelected().get(0), client)));
 | 
			
		||||
							put("copy", evt -> {
 | 
			
		||||
								// TODO should be enhanced to allow also copying of message attachments,
 | 
			
		||||
								// especially pictures
 | 
			
		||||
								StringSelection copy = new StringSelection(messageList.getSelected().get(0).getText());
 | 
			
		||||
								Toolkit.getDefaultToolkit().getSystemClipboard().setContents(copy, copy);
 | 
			
		||||
							});
 | 
			
		||||
							// TODO insert implementation to edit and delete messages
 | 
			
		||||
							put("delete", evt -> {});
 | 
			
		||||
							put("edit", evt -> {});
 | 
			
		||||
							put("quote", evt -> {});
 | 
			
		||||
						}
 | 
			
		||||
					};
 | 
			
		||||
					commands.forEach((name, action) -> { var item = new JMenuItem(name); item.addActionListener(action); contextMenu.add(item); });
 | 
			
		||||
					contextMenu.show(e.getComponent(), e.getX(), e.getY());
 | 
			
		||||
				}
 | 
			
		||||
			{
 | 
			
		||||
				put("forward selected message",
 | 
			
		||||
						evt -> forwardMessageToMultipleUsers(messageList.getSelected().get(0),
 | 
			
		||||
								ContactsChooserDialog.showForwardingDialog("Forward selected message to", messageList.getSelected().get(0), client)));
 | 
			
		||||
				put("copy", evt -> {
 | 
			
		||||
					// TODO should be enhanced to allow also copying of message attachments,
 | 
			
		||||
					// especially pictures
 | 
			
		||||
					StringSelection copy = new StringSelection(messageList.getSelected().get(0).getText());
 | 
			
		||||
					Toolkit.getDefaultToolkit().getSystemClipboard().setContents(copy, copy);
 | 
			
		||||
				});
 | 
			
		||||
				// TODO insert implementation to edit and delete messages
 | 
			
		||||
				put("delete", evt -> {});
 | 
			
		||||
				put("edit", evt -> {});
 | 
			
		||||
				put("quote", evt -> {});
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		};
 | 
			
		||||
		contextMenu = new ContextMenu(null, messageList, commands, null, null).build();
 | 
			
		||||
 | 
			
		||||
		scrollPane.setViewportView(messageList);
 | 
			
		||||
		scrollPane.addComponentListener(new ComponentAdapter() {
 | 
			
		||||
 | 
			
		||||
			// updates list elements when list is resized
 | 
			
		||||
			// Update list elements when scroll pane (and thus list) is resized
 | 
			
		||||
			@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);
 | 
			
		||||
 | 
			
		||||
@@ -600,7 +597,7 @@ public class ChatWindow extends JFrame {
 | 
			
		||||
	private void readCurrentChat() {
 | 
			
		||||
		try {
 | 
			
		||||
			currentChat.read(writeProxy);
 | 
			
		||||
			messageList.synchronizeModel();
 | 
			
		||||
			if (messageList.getRenderer() != null) messageList.synchronizeModel();
 | 
			
		||||
		} catch (IOException e) {
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
			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.writeProxy	= writeProxy;
 | 
			
		||||
 | 
			
		||||
		messageList.setRenderer(new MessageListRenderer(client.getSender().getId()));
 | 
			
		||||
 | 
			
		||||
		// Load users and chats
 | 
			
		||||
		new Thread(() -> {
 | 
			
		||||
			localDb.getUsers().values().forEach(user -> {
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,12 @@ public class ComponentList<E> extends JPanel {
 | 
			
		||||
 | 
			
		||||
	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)); }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
@@ -74,7 +80,7 @@ public class ComponentList<E> extends JPanel {
 | 
			
		||||
		// Synchronize with new model
 | 
			
		||||
		this.model = model;
 | 
			
		||||
		if (model != null) this.model.setComponentList(this);
 | 
			
		||||
		synchronizeModel();
 | 
			
		||||
		if (renderer != null) synchronizeModel();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
@@ -84,9 +90,11 @@ public class ComponentList<E> extends JPanel {
 | 
			
		||||
	 * @since Envoy v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public void synchronizeModel() {
 | 
			
		||||
		removeAll();
 | 
			
		||||
		if (model != null) model.forEach(this::add);
 | 
			
		||||
		revalidate();
 | 
			
		||||
		if (model != null && renderer != null) {
 | 
			
		||||
			removeAll();
 | 
			
		||||
			model.forEach(this::add);
 | 
			
		||||
			revalidate();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
@@ -112,9 +120,11 @@ public class ComponentList<E> extends JPanel {
 | 
			
		||||
			clearSelections();
 | 
			
		||||
			currentSelections.add(index);
 | 
			
		||||
		}
 | 
			
		||||
		if (renderer != null) {
 | 
			
		||||
		final JComponent component = renderer.getListCellComponent(this, elem, isSelected);
 | 
			
		||||
		component.addMouseListener(getSelectionListener(index));
 | 
			
		||||
		add(component, index);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
 
 | 
			
		||||
@@ -115,6 +115,6 @@ public final class ComponentListModel<E> implements Iterable<E>, Serializable {
 | 
			
		||||
	 */
 | 
			
		||||
	void setComponentList(ComponentList<E> componentList) {
 | 
			
		||||
		this.componentList = componentList;
 | 
			
		||||
		if (componentList != null) componentList.synchronizeModel();
 | 
			
		||||
		if (componentList != null && componentList.getRenderer() != null) componentList.synchronizeModel();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user