package chatty.gui.components.settings;
import chatty.gui.MainGui;
import chatty.gui.components.LinkLabel;
import chatty.gui.components.LinkLabelListener;
import chatty.util.Sound;
import chatty.util.settings.Setting;
import chatty.util.settings.Settings;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
/**
* Main settings dialog class that provides ways to add different kinds of
* settings which are then automatically loaded and saved.
*
* @author tduva
*/
public class SettingsDialog extends JDialog implements ActionListener {
private final static Logger LOGGER = Logger.getLogger(SettingsDialog.class.getName());
private final JButton ok = new JButton("Save");
private final JButton cancel = new JButton("Cancel");
private final Set<String> restartRequiredDef = new HashSet<>(Arrays.asList(
"ffz", "nod3d", "noddraw",
"userlistWidth", "userlistMinWidth", "userlistEnabled",
"capitalizedNames", "correctlyCapitalizedNames", "ircv3CapitalizedNames",
"tabOrder", "tabsMwheelScrolling", "tabsMwheelScrollingAnywhere", "inputFont",
"bttvEmotes", "botNamesBTTV", "botNamesFFZ", "ffzEvent",
"logPath", "logTimestamp", "logSplit", "logSubdirectories",
"tabsPlacement", "tabsLayout", "logLockFiles"
));
private final Set<String> reconnectRequiredDef = new HashSet<>(Arrays.asList(
"membershipEnabled"
));
private boolean restartRequired = false;
private boolean reconnectRequired = false;
private static final String RESTART_REQUIRED_INFO = "<html><body style='width: 280px'>One or more settings "
+ "you have changed require a restart of Chatty to take any or full effect.";
private static final String RECONNECT_REQUIRED_INFO = "<html><body style='width: 280px'>One or more settings "
+ "you have changed require you to reconnect to have any effect.";
private final HashMap<String,StringSetting> stringSettings = new HashMap<>();
private final HashMap<String,LongSetting> longSettings = new HashMap<>();
private final HashMap<String,BooleanSetting> booleanSettings = new HashMap<>();
private final HashMap<String,ListSetting> listSettings = new HashMap<>();
private final HashMap<String,MapSetting> mapSettings = new HashMap<>();
private final Settings settings;
private final MainGui owner;
private final NotificationSettings notificationSettings;
private final UsercolorSettings usercolorSettings;
private final ImageSettings imageSettings;
private final HotkeySettings hotkeySettings;
private final NameSettings nameSettings;
private static final String PANEL_MAIN = "Main";
private static final String PANEL_MESSAGES = "Messages";
private static final String PANEL_EMOTES = "Emoticons";
private static final String PANEL_USERICONS = "Usericons";
private static final String PANEL_COLORS = "Colors";
private static final String PANEL_HIGHLIGHT = "Highlight";
private static final String PANEL_IGNORE = "Ignore";
private static final String PANEL_HISTORY = "History";
private static final String PANEL_SOUND = "Sounds";
private static final String PANEL_NOTIFICATIONS = "Notifications";
private static final String PANEL_USERCOLORS = "Usercolors";
private static final String PANEL_LOG = "Log to file";
private static final String PANEL_WINDOW = "Window";
private static final String PANEL_TABS = "Tabs";
private static final String PANEL_COMMANDS = "Commands";
private static final String PANEL_OTHER = "Other";
private static final String PANEL_ADVANCED = "Advanced";
private static final String PANEL_HOTKEYS = "Hotkeys";
private static final String PANEL_COMPLETION = "Completion";
private static final String PANEL_CHAT = "Chat";
private static final String PANEL_NAMES = "Names";
private static final String PANEL_MODERATION = "Moderation";
private String currentlyShown;
private final CardLayout cardManager;
private final JPanel cards;
private final JList<String> selection;
private final LinkLabelListener settingsHelpLinkLabelListener;
private static final String[] MENU = {
PANEL_MAIN,
PANEL_MESSAGES,
PANEL_MODERATION,
PANEL_CHAT,
PANEL_EMOTES,
PANEL_USERICONS,
PANEL_COLORS,
PANEL_USERCOLORS,
PANEL_NAMES,
PANEL_HIGHLIGHT,
PANEL_IGNORE,
PANEL_HISTORY,
PANEL_SOUND,
PANEL_NOTIFICATIONS,
PANEL_LOG,
PANEL_WINDOW,
PANEL_TABS,
PANEL_COMMANDS,
PANEL_OTHER,
PANEL_ADVANCED,
PANEL_HOTKEYS,
PANEL_COMPLETION
};
public SettingsDialog(final MainGui owner, final Settings settings) {
super(owner,"Settings",true);
setResizable(false);
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
cancel();
}
});
settingsHelpLinkLabelListener = new LinkLabelListener() {
@Override
public void linkClicked(String type, String ref) {
owner.openHelp("help-settings.html", ref);
}
};
// Save references
this.owner = owner;
this.settings = settings;
/*
* Layout
*/
setLayout(new GridBagLayout());
GridBagConstraints gbc;
/*
* Add to Tabs
*/
//JTabbedPane tabs = new JTabbedPane();
selection = new JList<>(MENU);
selection.setSelectedIndex(0);
selection.setSize(200, 200);
Font defaultFont = selection.getFont();
selection.setFont(new Font(defaultFont.getFontName(), Font.BOLD, 12));
selection.setFixedCellHeight(20);
selection.setFixedCellWidth(100);
selection.setBorder(BorderFactory.createEtchedBorder());
// selection.setBackground(getBackground());
// selection.setForeground(getForeground());
gbc = makeGbc(0,0,1,1);
gbc.insets = new Insets(10,10,10,3);
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 0;
gbc.weighty = 1;
add(selection, gbc);
cardManager = new CardLayout();
cards = new JPanel(cardManager);
cards.add(new MainSettings(this), PANEL_MAIN);
cards.add(new MessageSettings(this), PANEL_MESSAGES);
cards.add(new ModerationSettings(this), PANEL_MODERATION);
cards.add(new EmoteSettings(this), PANEL_EMOTES);
imageSettings = new ImageSettings(this);
cards.add(imageSettings, PANEL_USERICONS);
cards.add(new ColorSettings(this), PANEL_COLORS);
cards.add(new HighlightSettings(this), PANEL_HIGHLIGHT);
cards.add(new IgnoreSettings(this), PANEL_IGNORE);
cards.add(new HistorySettings(this), PANEL_HISTORY);
cards.add(new SoundSettings(this), PANEL_SOUND);
notificationSettings = new NotificationSettings(this);
cards.add(notificationSettings, PANEL_NOTIFICATIONS);
usercolorSettings = new UsercolorSettings(this);
cards.add(usercolorSettings, PANEL_USERCOLORS);
cards.add(new LogSettings(this), PANEL_LOG);
cards.add(new WindowSettings(this), PANEL_WINDOW);
cards.add(new TabSettings(this), PANEL_TABS);
cards.add(new CommandSettings(this), PANEL_COMMANDS);
cards.add(new OtherSettings(this), PANEL_OTHER);
cards.add(new AdvancedSettings(this), PANEL_ADVANCED);
hotkeySettings = new HotkeySettings(this);
cards.add(hotkeySettings, PANEL_HOTKEYS);
cards.add(new CompletionSettings(this), PANEL_COMPLETION);
cards.add(new ChatSettings(this), PANEL_CHAT);
nameSettings = new NameSettings(this);
cards.add(nameSettings, PANEL_NAMES);
currentlyShown = PANEL_MAIN;
selection.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
showPanel(selection.getSelectedValue());
}
});
// Cards
gbc = makeGbc(1,0,2,1);
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1;
gbc.weighty = 1;
add(cards, gbc);
// Help Link
gbc = makeGbc(0,2,1,1);
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.insets = new Insets(0,10,0,0);
add(new LinkLabel("[maeh:muh Help]", new LinkLabelListener() {
@Override
public void linkClicked(String type, String ref) {
owner.openHelp("help-settings.html", currentlyShown);
}
}), gbc);
// Buttons
ok.setMnemonic(KeyEvent.VK_S);
gbc = makeGbc(1,2,1,1);
gbc.weightx = 0.5;
gbc.anchor = GridBagConstraints.EAST;
gbc.insets = new Insets(4,3,8,8);
add(ok,gbc);
cancel.setMnemonic(KeyEvent.VK_C);
gbc = makeGbc(2,2,1,1);
gbc.weightx = 0;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(4,3,8,8);
add(cancel,gbc);
// Listeners
ok.addActionListener(this);
cancel.addActionListener(this);
pack();
}
/**
* Opens the settings dialog
*/
public void showSettings() {
showSettings(null, null);
}
public void showSettings(String action, String parameter) {
loadSettings();
notificationSettings.setUserReadPermission(settings.getBoolean("token_user"));
setLocationRelativeTo(owner);
if (action != null) {
editDirectly(action, parameter);
}
stuffBasedOnPanel();
selection.requestFocusInWindow();
setVisible(true);
}
private void stuffBasedOnPanel() {
if (currentlyShown.equals(PANEL_HOTKEYS)) {
owner.hotkeyManager.setEnabled(false);
}
}
private void editDirectly(final String action, final String parameter) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (action.equals("editUsercolorItem")) {
showPanel(PANEL_USERCOLORS);
usercolorSettings.editItem(parameter);
} else if (action.equals("editCustomNameItem")) {
showPanel(PANEL_NAMES);
nameSettings.editCustomName(parameter);
} else if (action.equals("addUsericonOfBadgeType")) {
showPanel(PANEL_USERICONS);
imageSettings.addUsericonOfBadgeType(parameter);
}
}
});
}
private void showPanel(String showCard) {
cardManager.show(cards, showCard);
currentlyShown = showCard;
selection.setSelectedValue(showCard, true);
stuffBasedOnPanel();
}
/**
* Loads all settings from the settings object
*/
private void loadSettings() {
loadStringSettings();
loadNumericSettings();
loadBooleanSettings();
loadListSettings();
loadMapSettings();
usercolorSettings.setData(owner.getUsercolorData());
imageSettings.setData(owner.getUsericonData());
imageSettings.setTwitchBadgeTypes(owner.getTwitchBadgeTypes());
hotkeySettings.setData(owner.hotkeyManager.getActionsMap(),
owner.hotkeyManager.getData(), owner.hotkeyManager.globalHotkeysAvailable());
}
/**
* Loads all settings of type String
*/
private void loadStringSettings() {
for (String settingName : stringSettings.keySet()) {
StringSetting setting = stringSettings.get(settingName);
String value = settings.getString(settingName);
setting.setSettingValue(value);
}
}
/**
* Loads all settings of type Integer
*/
private void loadNumericSettings() {
for (String settingName : longSettings.keySet()) {
LongSetting setting = longSettings.get(settingName);
Long value = settings.getLong(settingName);
setting.setSettingValue(value);
}
}
/**
* Loads all settings of type Boolean
*/
private void loadBooleanSettings() {
for (String settingName : booleanSettings.keySet()) {
BooleanSetting setting = booleanSettings.get(settingName);
Boolean value = settings.getBoolean(settingName);
setting.setSettingValue(value);
}
}
private void loadListSettings() {
for (String settingName : listSettings.keySet()) {
ListSetting setting = listSettings.get(settingName);
List data = settings.getList(settingName);
setting.setSettingValue(data);
}
}
private void loadMapSettings() {
for (String settingName : mapSettings.keySet()) {
MapSetting setting = mapSettings.get(settingName);
Map data = settings.getMap(settingName);
setting.setSettingValue(data);
}
}
/**
* Saves settings into the settings object
*/
private void saveSettings() {
restartRequired = false;
reconnectRequired = false;
saveStringSettings();
saveBooleanSettings();
saveIntegerSettings();
saveListSettings();
saveMapSettings();
owner.setUsercolorData(usercolorSettings.getData());
owner.setUsericonData(imageSettings.getData());
owner.hotkeyManager.setData(hotkeySettings.getData());
if (restartRequired) {
JOptionPane.showMessageDialog(this, RESTART_REQUIRED_INFO, "Info", JOptionPane.INFORMATION_MESSAGE);
}
if (reconnectRequired) {
String[] options = new String[]{"Reconnect now", "Reconnect manually"};
int result = JOptionPane.showOptionDialog(this,
RECONNECT_REQUIRED_INFO,
"Reconect?",
JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE,
null, options, null);
if (result == 0) {
owner.reconnect();
}
}
}
/**
* Saves all settings of type String
*/
private void saveStringSettings() {
for (String settingName : stringSettings.keySet()) {
StringSetting setting = stringSettings.get(settingName);
String value = setting.getSettingValue();
if (settings.setString(settingName,value) == Setting.CHANGED) {
changed(settingName);
}
}
}
/**
* Saves all settings of type Boolean
*/
private void saveBooleanSettings() {
for (String settingName : booleanSettings.keySet()) {
BooleanSetting setting = booleanSettings.get(settingName);
if (settings.setBoolean(settingName, setting.getSettingValue()) == Setting.CHANGED) {
changed(settingName);
}
}
}
/**
* Saves all settings of type Integer.
*
* Parses the String of the JTextFields into an Integer and only saves if
* it succeeds
*/
private void saveIntegerSettings() {
for (String settingName : longSettings.keySet()) {
LongSetting setting = longSettings.get(settingName);
Long value = setting.getSettingValue();
if (value != null) {
if (settings.setLong(settingName, setting.getSettingValue()) == Setting.CHANGED) {
changed(settingName);
}
} else {
LOGGER.warning("Invalid number format for setting "+settingName);
}
}
}
private void changed(String settingName) {
if (restartRequiredDef.contains(settingName)) {
restartRequired = true;
reconnectRequired = false;
}
if (reconnectRequiredDef.contains(settingName) && !restartRequired) {
reconnectRequired = true;
}
}
private void saveListSettings() {
for (String settingName : listSettings.keySet()) {
ListSetting setting = listSettings.get(settingName);
settings.putList(settingName, setting.getSettingValue());
// settingsgetList2t(settingName).clear();
// settinggetList2st(settingName).addAll(setting.getSettingValue());
settings.setSettingChanged(settingName);
}
}
private void saveMapSettings() {
for (String settingName : mapSettings.keySet()) {
MapSetting setting = mapSettings.get(settingName);
boolean changed = settings.putMap(settingName, setting.getSettingValue());
if (changed) {
settings.setSettingChanged(settingName);
}
}
}
protected GridBagConstraints makeGbc(int x, int y, int w, int h) {
return makeGbc(x, y, w, h, GridBagConstraints.CENTER);
}
protected GridBagConstraints makeGbc(int x, int y, int w, int h, int anchor) {
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = x;
gbc.gridy = y;
gbc.gridwidth = w;
gbc.gridheight = h;
gbc.insets = new Insets(4,5,4,5);
gbc.anchor = anchor;
return gbc;
}
protected GridBagConstraints makeGbcCloser(int x, int y, int w, int h, int anchor) {
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = x;
gbc.gridy = y;
gbc.gridwidth = w;
gbc.gridheight = h;
gbc.insets = new Insets(1,5,1,5);
gbc.anchor = anchor;
return gbc;
}
protected GridBagConstraints makeGbcSub(int x, int y, int w, int h, int anchor) {
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = x;
gbc.gridy = y;
gbc.gridwidth = w;
gbc.gridheight = h;
gbc.insets = new Insets(1,18,1,5);
gbc.anchor = anchor;
return gbc;
}
protected void addBooleanSetting(String name, BooleanSetting setting) {
booleanSettings.put(name, setting);
}
protected JCheckBox addSimpleBooleanSetting(String name, String description, String tooltipText) {
SimpleBooleanSetting result = new SimpleBooleanSetting(description, tooltipText);
booleanSettings.put(name,result);
return result;
}
protected void setBooleanSetting(String name, Boolean value) {
if (booleanSettings.containsKey(name)) {
booleanSettings.get(name).setSettingValue(value);
}
}
protected Boolean getBooleanSetting(String name) {
if (booleanSettings.containsKey(name)) {
return booleanSettings.get(name).getSettingValue();
}
return null;
}
protected ComboStringSetting addComboStringSetting(String name, int size, boolean editable, String[] choices) {
ComboStringSetting result = new ComboStringSetting(choices);
result.setEditable(editable);
stringSettings.put(name, result);
return result;
}
protected ComboStringSetting addComboStringSetting(String name, int size, boolean editable, Map<String, String> choices) {
ComboStringSetting result = new ComboStringSetting(choices);
result.setEditable(editable);
stringSettings.put(name, result);
return result;
}
protected StringSetting addStringSetting(String settingName, StringSetting setting) {
stringSettings.put(settingName, setting);
return setting;
}
protected JTextField addSimpleStringSetting(String settingName, int size, boolean editable) {
SimpleStringSetting s = new SimpleStringSetting(size, editable);
addStringSetting(settingName, s);
return s;
}
protected EditorStringSetting addEditorStringSetting(String settingName, int size,
boolean editable, final String title, final boolean linebreaks,
String info) {
return addEditorStringSetting(settingName, size, editable, title, linebreaks, info, null);
}
protected EditorStringSetting addEditorStringSetting(String settingName, int size,
boolean editable, final String title, final boolean linebreaks,
String info, Editor.Tester tester) {
EditorStringSetting s = new EditorStringSetting(this, title, size, true, linebreaks, info, tester);
addStringSetting(settingName, s);
return s;
}
/**
* Changes the String setting with the given name to the given value. Does
* nothing if a setting with this name doesn't exist.
*
* @param name The name of the setting
* @param value The new value
*/
protected void setStringSetting(String name, String value) {
if (stringSettings.containsKey(name)) {
stringSettings.get(name).setSettingValue(value);
}
}
/**
* Retrieves the value of the String setting with the given name.
*
* @param name The name of the setting
* @return The value of the setting or null if it doesn't exist
*/
protected String getStringSetting(String name) {
if (stringSettings.containsKey(name)) {
return stringSettings.get(name).getSettingValue();
}
return null;
}
/**
* Adds an Integer setting.
*
* @param name The name of the setting
* @param size The size of the editbox
* @param editable Whether the value can be changed by the user
* @return The JTextField used for this setting
*/
protected JTextField addSimpleLongSetting(String name, int size, boolean editable) {
SimpleLongSetting result = new SimpleLongSetting(size, editable);
addLongSetting(name, result);
return result;
}
protected void addLongSetting(String settingName, LongSetting setting) {
longSettings.put(settingName, setting);
}
/**
* Changes the value of an Integer setting to the given value. Does nothing
* if the setting doesn't exist.
*
* @param name The name of the setting
* @param value The new value
*/
protected void setLongSetting(String name, Long value) {
if (longSettings.containsKey(name)) {
longSettings.get(name).setSettingValue(value);
}
}
/**
* Retrieves the Integer value for the given Integer setting. Returns null
* if value couldn't be parsed as an Integer or if the setting doesn't
* exist.
*
* @param name
* @return
*/
protected Long getLongSetting(String name) {
if (longSettings.containsKey(name)) {
return longSettings.get(name).getSettingValue();
}
return null;
}
/**
* Adds a List setting.
*
* @param name
* @param width
* @param height
* @return
*/
protected ListSelector addListSetting(String name, int width, int height,
boolean manualSorting, boolean alphabeticSorting) {
ListSelector result = new ListSelector(this, manualSorting, alphabeticSorting);
result.setPreferredSize(new Dimension(width, height));
listSettings.put(name, result);
return result;
}
protected SimpleTableEditor addStringMapSetting(String name, int width, int height) {
SimpleTableEditor<String> table = new SimpleTableEditor<String>(this) {
@Override
protected String valueFromString(String input) {
return input;
}
};
table.setPreferredSize(new Dimension(width, height));
mapSettings.put(name, table);
return table;
}
protected SimpleTableEditor addLongMapSetting(String name, int width, int height) {
SimpleTableEditor<Long> table = new SimpleTableEditor<Long>(this) {
@Override
protected Long valueFromString(String input) {
return Long.valueOf(input);
}
};
table.setValueFilter("[^0-9]");
table.setPreferredSize(new Dimension(width, height));
mapSettings.put(name, table);
return table;
}
protected void clearHistory() {
owner.clearHistory();
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == ok) {
save();
}
else if (e.getSource() == cancel) {
cancel();
}
}
private void save() {
saveSettings();
close();
}
private void cancel() {
Sound.setDeviceName(settings.getString("soundDevice"));
close();
}
private void close() {
owner.hotkeyManager.setEnabled(true);
setVisible(false);
}
protected LinkLabelListener getLinkLabelListener() {
return owner.getLinkLabelListener();
}
protected LinkLabelListener getSettingsHelpLinkLabelListener() {
return settingsHelpLinkLabelListener;
}
}