package chatty.gui;
import chatty.Chatty;
import chatty.gui.components.LinkLabel;
import chatty.gui.components.LinkLabelListener;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.event.MenuListener;
/**
* The main menu of the application (actually several menus in a MenuBar).
*
* @author tduva
*/
public class MainMenu extends JMenuBar {
private final JMenu main = new JMenu("Main");
protected final JMenu view = new JMenu("View");
private final JMenu channels = new JMenu("Channels");
private final JMenu srl = new JMenu("SRL");
protected final JMenu srlStreams = new JMenu("Races with..");
private final JMenu extra = new JMenu("Extra");
private final JMenu help = new JMenu("Help");
private final JMenuItem highlights;
private final JMenuItem ignored;
private final ItemListener itemListener;
private final ActionListener actionListener;
private final LinkLabelListener linkLabelListener;
// Set here because it is used more than once
private final String IGNORED_LABEL = "Ignored";
private final String HIGHLIGHTS_LABEL = "Highlights";
private final Notification notification = new Notification();
/**
* Stores all the menu items associated with a key
*/
private final HashMap<String,JMenuItem> menuItems = new HashMap<>();
public MainMenu(ActionListener actionListener, ItemListener itemListener,
LinkLabelListener linkLabelListener) {
this.itemListener = itemListener;
this.actionListener = actionListener;
this.linkLabelListener = linkLabelListener;
//this.setBackground(Color.black);
//this.setForeground(Color.white);
main.addActionListener(actionListener);
view.addActionListener(actionListener);
channels.addActionListener(actionListener);
srl.addActionListener(actionListener);
extra.addActionListener(actionListener);
help.addActionListener(actionListener);
view.addMenuListener((MenuListener)itemListener);
main.setMnemonic(KeyEvent.VK_M);
view.setMnemonic(KeyEvent.VK_V);
channels.setMnemonic(KeyEvent.VK_C);
srl.setMnemonic(KeyEvent.VK_S);
extra.setMnemonic(KeyEvent.VK_E);
help.setMnemonic(KeyEvent.VK_H);
// Main
addItem(main,"connect","Connect");
addItem(main,"disconnect","Disconnect").setEnabled(false);
main.addSeparator();
setIcon(addItem(main,"settings","Settings", KeyEvent.VK_S), "preferences-system.png");
addItem(main,"configureLogin","Login..");
main.addSeparator();
addItem(main,"saveSettings","Save..");
main.addSeparator();
addItem(main,"application.exit","Exit");
// View
addCheckboxItem(view,"ontop","Always on top");
JMenu viewOptions = new JMenu("Options");
JMenu titleOptions = new JMenu("Titlebar");
addCheckboxItem(titleOptions, "titleShowUptime", "Stream Uptime");
addCheckboxItem(titleOptions, "titleLongerUptime", "More Detailed Uptime");
addCheckboxItem(titleOptions, "titleShowChannelState", "Channel State");
addCheckboxItem(titleOptions, "titleShowViewerCount", "Viewer/Chatter Count");
titleOptions.addSeparator();
addCheckboxItem(titleOptions, "simpleTitle", "Simple Title");
viewOptions.add(titleOptions);
addCheckboxItem(viewOptions,"showJoinsParts","Show joins/parts");
addCheckboxItem(viewOptions, "showModMessages", "Show mod/unmod");
addCheckboxItem(viewOptions, "attachedWindows", "Attached dialogs");
addCheckboxItem(viewOptions, "mainResizable", "Window resizable");
view.add(viewOptions);
view.addSeparator();
addItem(view,"dialog.channelInfo","Channel Info");
addItem(view,"dialog.channelAdmin","Channel Admin");
view.addSeparator();
highlights = addItem(view,"dialog.highlightedMessages",HIGHLIGHTS_LABEL);
ignored = addItem(view,"dialog.ignoredMessages",IGNORED_LABEL);
view.addSeparator();
addItem(view,"dialog.search","Find text..");
// Channels
addItem(channels, "favoritesDialog", "Favorites/History", KeyEvent.VK_F);
addItem(channels, "dialog.streams", "Live Channels");
addItem(channels, "dialog.addressbook", "Addressbook");
channels.addSeparator();
addItem(channels, "dialog.joinChannel", "Join Channel");
// SRL
addItem(srl, "srlRaces", "Race List");
srl.addSeparator();
srl.add(srlStreams);
srlStreams.addMenuListener((MenuListener)itemListener);
// Extra
addItem(extra,"livestreamer","Livestreamer", KeyEvent.VK_L);
addItem(extra,"dialog.toggleEmotes","Emoticons");
extra.addSeparator();
addItem(extra,"dialog.followers","Followers");
addItem(extra,"dialog.subscribers","Subscribers");
extra.addSeparator();
addItem(extra,"dialog.moderationLog", "Moderation Log");
addItem(extra,"dialog.autoModDialog", "AutoMod");
addItem(extra,"dialog.chatRules", "Chat Rules");
extra.addSeparator();
JMenu streamChat = new JMenu("Stream Chat");
addItem(streamChat,"dialog.streamchat", "Open");
addCheckboxItem(streamChat, "streamChatResizable", "Resizable");
extra.add(streamChat);
JMenu streamHighlights = new JMenu("Stream Highlights");
addItem(streamHighlights, "addStreamHighlight", "Add Stream Highlight");
addItem(streamHighlights, "openStreamHighlights", "Open Stream Highlights");
extra.add(streamHighlights);
extra.addSeparator();
JMenu debugOptions = new JMenu("Options");
addCheckboxItem(debugOptions,"globalHotkeysEnabled","Global Hotkeys");
extra.add(debugOptions);
if (Chatty.DEBUG) {
addItem(extra,"unhandledException", "Unhandled Exception");
}
addItem(extra,"debug","Debug window");
// Maybe add own submenu later when more functions are available
// extra.addSeparator();
// JMenu speedruncom = new JMenu("Speedrun.com");
// addItem(speedruncom, "srcOpen", "Open Game Website");
// extra.add(speedruncom);
// Help
addItem(help,"website","Website");
JMenuItem helpItem = addItem(help,"about","About/Help", KeyEvent.VK_H);
helpItem.setAccelerator(KeyStroke.getKeyStroke("F1"));
setIcon(helpItem, "help-browser.png");
help.addSeparator();
addItem(help,"news","Announcements");
add(main);
add(view);
add(channels);
add(srl);
add(extra);
add(help);
}
/**
* Adds a MenuItem to a menu.
*
* @param menu The Menu to which the item is added
* @param key The key this item is associated with
* @param label The text of the item
* @return The created MenuItem
*/
public final JMenuItem addItem(JMenu menu, String key, String label, int mnemonic) {
JMenuItem item = new JMenuItem(label);
if (mnemonic != -1) {
item.setMnemonic(mnemonic);
}
menuItems.put(key,item);
item.setActionCommand(key);
menu.add(item);
item.addActionListener(actionListener);
return item;
}
public final JMenuItem addItem(JMenu menu, String key, String label) {
return addItem(menu, key, label, -1);
}
public final void setAction(String key, Action action) {
menuItems.get(key).setAction(action);
}
/**
* Adds a CheckboxMenuItem to a menu.
*
* @param menu The Menu to which the item is added
* @param key The key this item is associated with (the setting)
* @param label The text of the item
* @return The created MenuItem
*/
public final JMenuItem addCheckboxItem(JMenu menu, String key, String label) {
JCheckBoxMenuItem item = new JCheckBoxMenuItem(label);
menuItems.put(key, item);
item.setActionCommand(key);
menu.add(item);
item.addItemListener(itemListener);
return item;
}
/**
* Gets the MenuItem for the given setting name.
*
* @param key
* @return
*/
public JMenuItem getMenuItem(String key) {
return menuItems.get(key);
}
/**
* Gets the setting name for the given menu item.
*
* @param item
* @return
*/
public String getSettingByMenuItem(Object item) {
Iterator<Entry<String,JMenuItem>> it = menuItems.entrySet().iterator();
while (it.hasNext()) {
Entry<String,JMenuItem> entry = it.next();
if (entry.getValue() == item) {
return entry.getKey();
}
}
return null;
}
/**
* Sets the state (selected/unselected) of the CheckboxMenuItem associated
* with the given setting.
*
* @param setting
* @param state
*/
public void setItemState(String setting, boolean state) {
JMenuItem item = getMenuItem(setting);
if (item != null && item instanceof JCheckBoxMenuItem) {
((JCheckBoxMenuItem)item).setState(state);
}
}
private void setIcon(JMenuItem item, String name) {
item.setIcon(new ImageIcon(MainMenu.class.getResource(name)));
}
/**
* Updates the labels for the highlights/ignored messages menu entries.
*
* @param highlightsCountNew
* @param highlightsCount
* @param ignoredCountNew
* @param ignoredCount
*/
public void updateCount(int highlightsCountNew, int highlightsCount,
int ignoredCountNew, int ignoredCount) {
highlights.setText(buildCount(HIGHLIGHTS_LABEL, highlightsCountNew,
highlightsCount));
ignored.setText(buildCount(IGNORED_LABEL, ignoredCountNew,
ignoredCount));
}
/**
* Create count String for highlights/ignored mesages.
*
* @param label
* @param countNew
* @param count
* @return
*/
private String buildCount(String label, int countNew, int count) {
if (count == 0) {
return label;
}
return label+" ("+countNew+"/"+count+")";
}
/**
* Update the entries for the "Races with.." submenu.
*
* @param active The currently active stream
* @param popout Current streams in popout dialogs
*/
public void updateSrlStreams(String active, List<String> popout) {
srlStreams.removeAll();
if (active == null || active.isEmpty()) {
addItem(srlStreams, "", "No channel joined");
} else {
addItem(srlStreams, "srlRaceActive", active);
}
if (!popout.isEmpty()) {
srlStreams.addSeparator();
for (String chan : popout) {
addItem(srlStreams, "srlRace4"+chan, chan);
}
}
}
public void setUpdateNotification(boolean enabled) {
notification.setUpdateNotification(enabled);
}
public void setAnnouncementNotification(boolean enabled) {
notification.setAnnouncementNotification(enabled);
}
private class Notification {
private static final String MESSAGE_BASE = "<html>"
+ "<body style='text-align: right;padding-right:5px;'>";
/**
* Stores whether the notification label has been added to the layout
* yet, so it's guaranteed to be only added once.
*/
private boolean addedLabelToLayout;
/**
* Store whether the notification is currently set to the smaller
* version, so it doesn't constantly change unless necessary.
*/
private boolean updateMessageSmaller;
private String message;
private String shortMessage;
private Dimension preferredSize = new Dimension();
private LinkLabel notification;
private boolean updateNotificationEnabled;
private boolean announcementNotificationEnabled;
// private Timer flashTimer;
// private int flashCount;
public void setUpdateNotification(boolean enabled) {
if (updateNotificationEnabled != enabled) {
updateNotificationEnabled = enabled;
setNotification();
}
}
public void setAnnouncementNotification(boolean enabled) {
if (announcementNotificationEnabled != enabled) {
announcementNotificationEnabled = enabled;
setNotification();
}
}
private void makeText() {
message = MESSAGE_BASE;
shortMessage = MESSAGE_BASE;
if (announcementNotificationEnabled) {
message += "[announcement:show Announcement]";
shortMessage += "[announcement:show News]";
}
if (updateNotificationEnabled) {
if (announcementNotificationEnabled) {
message += " | ";
shortMessage += " | ";
}
message += "[update:show Update available!]";
shortMessage += "[update:show Update!]";
}
}
private void setNotification() {
makeText();
if (!addedLabelToLayout) {
addNotificationToLayout();
addedLabelToLayout = true;
}
// Save preferred size of regular version to compare to in listener
notification.setText(message);
preferredSize = notification.getPreferredSize();
// This needs to be improved/tested more first
// if (announcementNotificationEnabled || updateNotificationEnabled) {
// flashCount = 9;
//
// if (flashTimer != null) {
// flashTimer.stop();
// flashTimer = null;
// }
// flashTimer = new Timer(500, new ActionListener() {
//
// @Override
// public void actionPerformed(ActionEvent e) {
// System.out.println(flashCount);
// if (flashCount % 2 == 0) {
// notification.addRule("a { color: blue; }");
// } else {
// notification.addRule("a { color: red; }");
//
// }
// if (flashCount == 0) {
// flashTimer.stop();
// System.out.println("Stop");
// } else {
// flashCount--;
// }
// }
// });
// flashTimer.setRepeats(true);
// flashTimer.start();
// }
}
private void addNotificationToLayout() {
notification = new LinkLabel("", linkLabelListener);
/**
* Add listener to change notification text to a shorter version
* when less space is available ("Update available!" -> "Update!").
*/
notification.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
Dimension actualSize = e.getComponent().getSize();
if (actualSize.width < preferredSize.width + 10) {
if (!updateMessageSmaller) {
notification.setText(shortMessage);
updateMessageSmaller = true;
//System.out.println("made smaller");
}
} else {
if (updateMessageSmaller) {
notification.setText(message);
updateMessageSmaller = false;
//System.out.println("made bigger again");
}
}
}
});
add(notification);
}
}
}