@Override
        public void actionPerformed(ActionEvent e) {
            final JDialog dialog = new JDialog(frame, "Configure", true);
            final SpringLayout layout = new SpringLayout();

            dialog.setPreferredSize(new Dimension(380, 550));

            Container contentpane = dialog.getContentPane();
            contentpane.setLayout(layout);

            final JLabel chatFontLabel = new JLabel("Chat text");

            final JTextField chatFontField = new JTextField(chatFont + " " + chatFontSize);
            chatFontField.setFont(Font.decode(chatFont+"-"+chatFontStyle+"-"+chatFontSize));
            chatFontField.setEditable(false);
            chatFontField.setPreferredSize(new Dimension(200, 25));
            final JButton chatFontButton = new JButton("Choose");

            final JLabel userListFontLabel = new JLabel("Nick list text");
            final JTextField userListFontField = new JTextField(userListFont + " " + userListFontSize);
            userListFontField.setFont(Font.decode(userListFont+"-"+userListFontStyle+"-"+userListFontSize));
            userListFontField.setEditable(false);
            userListFontField.setPreferredSize(new Dimension(200,25));
            final JButton userListFontButton = new JButton("Choose");



            JLabel colorsLabel = new JLabel("CTCP Text Colors");
            JLabel nickColorsLabel = new JLabel("Nick Colors");

            layout.putConstraint(SpringLayout.WEST, chatFontLabel, 10, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, chatFontLabel, 15, SpringLayout.NORTH, dialog);
            layout.putConstraint(SpringLayout.WEST, chatFontField, 33, SpringLayout.EAST, chatFontLabel);
            layout.putConstraint(SpringLayout.NORTH, chatFontField, 10, SpringLayout.NORTH, dialog);
            layout.putConstraint(SpringLayout.NORTH, chatFontButton, 11, SpringLayout.NORTH, dialog);
            layout.putConstraint(SpringLayout.WEST, chatFontButton, 5, SpringLayout.EAST, chatFontField);

            layout.putConstraint(SpringLayout.WEST, userListFontLabel, 10, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, userListFontLabel, 20, SpringLayout.SOUTH, chatFontLabel);
            layout.putConstraint(SpringLayout.WEST, userListFontField, 0, SpringLayout.WEST, chatFontField);
            layout.putConstraint(SpringLayout.NORTH, userListFontField, 15, SpringLayout.SOUTH, chatFontLabel);
            layout.putConstraint(SpringLayout.NORTH, userListFontButton, 16, SpringLayout.SOUTH, chatFontLabel);
            layout.putConstraint(SpringLayout.WEST, userListFontButton, 5, SpringLayout.EAST, userListFontField);



            layout.putConstraint(SpringLayout.WEST, colorsLabel, 10, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, colorsLabel, 15, SpringLayout.SOUTH, userListFontLabel);

            final Button[] ct = new Button[CTCPMap.size()];
            for (int i = 0; i < CTCPMap.size(); i++) {
                Color c = CTCPMap.get(i);
                Button colorsButton = new Button();
                colorsButton.setBackground(c);
                colorsButton.addActionListener(ColorChooseAction.getInstance());
                colorsButton.setPreferredSize(new Dimension(16, 16));
                dialog.add(colorsButton);
                ct[i] = colorsButton;

                layout.putConstraint(SpringLayout.WEST, colorsButton, 30 + 20 * i, SpringLayout.WEST, dialog);
                layout.putConstraint(SpringLayout.NORTH, colorsButton, 20, SpringLayout.NORTH, colorsLabel);
            }


            layout.putConstraint(SpringLayout.WEST, nickColorsLabel, 10, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, nickColorsLabel, 30, SpringLayout.SOUTH, colorsLabel);

            final Button[] cb = new Button[chatColorMap.size()];

            for (int i = 0; i < chatColorMap.size(); i++) {
                Color c = chatColorMap.get(i);
                Button colorsButton = new Button();
                colorsButton.setBackground(c);
                colorsButton.addActionListener(ColorChooseAction.getInstance());
                colorsButton.setPreferredSize(new Dimension(16, 16));
                dialog.add(colorsButton);
                cb[i] = colorsButton;

                layout.putConstraint(SpringLayout.WEST, colorsButton, 30 + 20 * i, SpringLayout.WEST, dialog);
                layout.putConstraint(SpringLayout.NORTH, colorsButton, 20, SpringLayout.NORTH, nickColorsLabel);
            }


            dialog.add(chatFontLabel);
            dialog.add(chatFontField);
            dialog.add(chatFontButton);
            dialog.add(userListFontLabel);
            dialog.add(userListFontField);
            dialog.add(userListFontButton);
            dialog.add(colorsLabel);
            dialog.add(nickColorsLabel);
            dialog.revalidate();

            /** Chat Font settings */
            chatFontButton.addActionListener(new FontMenuAction(chatFontField, dialog, FontMenuAction.CHATFONT));
            userListFontButton.addActionListener(new FontMenuAction(userListFontField, dialog, FontMenuAction.NICKLISTFONT));
            //End of chat Font settings


            final JCheckBox coloredNicksOption = new JCheckBox("Use colored nicks in chat");
            if (chatNameColors) coloredNicksOption.setSelected(true);
            else coloredNicksOption.setSelected(false);

            final JCheckBox hideMessagesOption = new JCheckBox("Hide join/part/nick messages");
            if (hideJoinPartQuitNotifications) hideMessagesOption.setSelected(true);
            else hideMessagesOption.setSelected(false);

            final JCheckBox showTimestampsOption = new JCheckBox("Show Timestamps");
            if (showTimestamp) showTimestampsOption.setSelected(true);
            else showTimestampsOption.setSelected(false);

            final JCheckBox disableChatNotificationsOption = new JCheckBox("Disable chat notifications");
            if (disableTabNotificationsGlobally) disableChatNotificationsOption.setSelected(true);
            else disableChatNotificationsOption.setSelected(false);

            final JCheckBox focusNewTabOption = new JCheckBox("Focus new tabs");
            if (focusNewTab) focusNewTabOption.setSelected(true);
            else focusNewTabOption.setSelected(false);

            final JCheckBox sortTabsAlphabeticallyOption = new JCheckBox("Sort tabs alphabetically");
            if (sortTabsAlphabetically) sortTabsAlphabeticallyOption.setSelected(true);
            else sortTabsAlphabeticallyOption.setSelected(false);

            JLabel tabPlacementOptionLabel = new JLabel("Tab placement");
            Object[] things = {"Top", "Bottom"};
            final JComboBox tabPlacementOption = new JComboBox(things);
            if (tabPlacement == DnDTabbedPane.BOTTOM) tabPlacementOption.setSelectedItem("Bottom");
            else tabPlacementOption.setSelectedItem("Top");


            layout.putConstraint(SpringLayout.NORTH, coloredNicksOption, 35, SpringLayout.SOUTH, nickColorsLabel);
            layout.putConstraint(SpringLayout.WEST, coloredNicksOption, 10, SpringLayout.WEST, dialog);

            layout.putConstraint(SpringLayout.NORTH, hideMessagesOption, 5, SpringLayout.SOUTH, coloredNicksOption);
            layout.putConstraint(SpringLayout.WEST, hideMessagesOption, 10, SpringLayout.WEST, dialog);

            layout.putConstraint(SpringLayout.NORTH, showTimestampsOption, 5, SpringLayout.SOUTH, hideMessagesOption);
            layout.putConstraint(SpringLayout.WEST, showTimestampsOption, 10, SpringLayout.WEST, dialog);

            layout.putConstraint(SpringLayout.NORTH, disableChatNotificationsOption, 5, SpringLayout.SOUTH, showTimestampsOption);
            layout.putConstraint(SpringLayout.WEST, disableChatNotificationsOption, 10, SpringLayout.WEST, dialog);

            layout.putConstraint(SpringLayout.NORTH, focusNewTabOption, 5, SpringLayout.SOUTH, disableChatNotificationsOption);
            layout.putConstraint(SpringLayout.WEST, focusNewTabOption, 10, SpringLayout.WEST, dialog);

            layout.putConstraint(SpringLayout.NORTH, sortTabsAlphabeticallyOption, 5, SpringLayout.SOUTH, focusNewTabOption);
            layout.putConstraint(SpringLayout.WEST, sortTabsAlphabeticallyOption, 10, SpringLayout.WEST, dialog);

            layout.putConstraint(SpringLayout.NORTH, tabPlacementOptionLabel, 13, SpringLayout.SOUTH, sortTabsAlphabeticallyOption);
            layout.putConstraint(SpringLayout.WEST, tabPlacementOptionLabel, 10, SpringLayout.WEST, dialog);

            layout.putConstraint(SpringLayout.NORTH, tabPlacementOption, 8, SpringLayout.SOUTH, sortTabsAlphabeticallyOption);
            layout.putConstraint(SpringLayout.WEST, tabPlacementOption, 10, SpringLayout.EAST, tabPlacementOptionLabel);

            dialog.add(coloredNicksOption);
            dialog.add(hideMessagesOption);
            dialog.add(showTimestampsOption);
            dialog.add(disableChatNotificationsOption);
            dialog.add(focusNewTabOption);
            dialog.add(sortTabsAlphabeticallyOption);
            dialog.add(tabPlacementOptionLabel);
            dialog.add(tabPlacementOption);



            JLabel quitMessageLabel = new JLabel("Quit");
            final JTextField quitMessageText = new JTextField();
            quitMessageText.setText(quitMessage);
            quitMessageText.setPreferredSize(new Dimension(253, 25));
            JLabel leaveMessageLabel = new JLabel("Leave channel");
            final JTextField leaveMessageText = new JTextField(leaveMessage);
            leaveMessageText.setPreferredSize(new Dimension(253, 25));
            JLabel awayMessageLabel = new JLabel("Away message");
            final JTextField awayMessageText = new JTextField(awayMessage);
            awayMessageText.setPreferredSize(new Dimension(253, 25));


            layout.putConstraint(SpringLayout.NORTH, quitMessageLabel, 18, SpringLayout.SOUTH, tabPlacementOptionLabel);
            layout.putConstraint(SpringLayout.WEST, quitMessageLabel, 10, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, quitMessageText, 13, SpringLayout.SOUTH, tabPlacementOptionLabel);
            //layout.putConstraint(SpringLayout.WEST, quitMessageText, 5, SpringLayout.EAST, quitMessageLabel);

            layout.putConstraint(SpringLayout.NORTH, leaveMessageLabel, 18, SpringLayout.SOUTH, quitMessageLabel);
            layout.putConstraint(SpringLayout.WEST, leaveMessageLabel, 10, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, leaveMessageText, 13, SpringLayout.SOUTH, quitMessageLabel);
            //layout.putConstraint(SpringLayout.WEST, leaveMessageText, 0, SpringLayout.EAST, leaveMessageLabel);

            layout.putConstraint(SpringLayout.NORTH, awayMessageLabel, 18, SpringLayout.SOUTH, leaveMessageLabel);
            layout.putConstraint(SpringLayout.WEST, awayMessageLabel, 10, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, awayMessageText, 13, SpringLayout.SOUTH, leaveMessageLabel);
            layout.putConstraint(SpringLayout.WEST, awayMessageText, 5, SpringLayout.EAST, awayMessageLabel);

            layout.putConstraint(SpringLayout.WEST, leaveMessageText, 0, SpringLayout.WEST, awayMessageText);
            layout.putConstraint(SpringLayout.WEST, quitMessageText, 0, SpringLayout.WEST, awayMessageText);



            dialog.add(quitMessageLabel);
            dialog.add(quitMessageText);
            dialog.add(leaveMessageLabel);
            dialog.add(leaveMessageText);
            dialog.add(awayMessageLabel);
            dialog.add(awayMessageText);


            JButton cancelButton = new JButton("Cancel");
            cancelButton.setPreferredSize(new Dimension(70,30));
            JButton OKButton = new JButton("OK");
            OKButton.setPreferredSize(new Dimension(70, 30));

            layout.putConstraint(SpringLayout.EAST, OKButton, -25, SpringLayout.EAST, dialog);
            layout.putConstraint(SpringLayout.SOUTH, OKButton, -35, SpringLayout.SOUTH, dialog);
            layout.putConstraint(SpringLayout.EAST, cancelButton, -5, SpringLayout.WEST, OKButton);
            layout.putConstraint(SpringLayout.SOUTH, cancelButton, 0, SpringLayout.SOUTH, OKButton);

            dialog.add(OKButton);
            OKButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent actionEvent) {
                    try {
                        prop.load(new FileInputStream("config.properties"));


                        for (int i = 0; i < ct.length; i++) {
                            String rgb = Integer.toHexString(ct[i].getBackground().getRGB()).substring(1);
                            prop.setProperty("CTCP"+String.valueOf(i), "#"+rgb);
                        }

                        for (int i = 0; i < cb.length; i++) {
                            String rgb = Integer.toHexString(cb[i].getBackground().getRGB()).substring(1);
                            prop.setProperty("chatColor"+String.valueOf(i), "#"+rgb);
                        }

                        prop.setProperty("awayMessage", awayMessageText.getText());
                        prop.setProperty("quitMessage", quitMessageText.getText());
                        prop.setProperty("leaveMessage", leaveMessageText.getText());

                        if (tabPlacementOption.getSelectedItem().equals("Bottom")) prop.setProperty("tabPlacement", String.valueOf(DnDTabbedPane.BOTTOM));
                        else prop.setProperty("tabPlacement", String.valueOf(DnDTabbedPane.TOP));

                        prop.setProperty("chatNameColors", String.valueOf(coloredNicksOption.isSelected()));
                        prop.setProperty("hideJoinPartQuitNotifications", String.valueOf(hideMessagesOption.isSelected()));
                        prop.setProperty("showTimestamp", String.valueOf(showTimestampsOption.isSelected()));
                        prop.setProperty("disableTabNotificationsGlobally", String.valueOf(disableChatNotificationsOption.isSelected()));
                        prop.setProperty("focusNewTab", String.valueOf(focusNewTabOption.isSelected()));
                        prop.setProperty("sortTabsAlphabetically", String.valueOf(sortTabsAlphabeticallyOption.isSelected()));

                        prop.store(new FileOutputStream("config.properties"), null);

                        loadProperties();
                        setStyles();

                        dialog.dispose();

                    } catch (FileNotFoundException e1) {
                        e1.printStackTrace();
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }

                }
            });



            dialog.add(cancelButton);
            cancelButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent actionEvent) {
                    dialog.dispose();
                }
            });

            OKButton.getRootPane().setDefaultButton(OKButton);
            OKButton.requestFocus();
            dialog.pack();
            dialog.setResizable(false);
            dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            dialog.setLocationRelativeTo(frame);
            dialog.setVisible(true);
        }
    }




    /** Action for disconnecting from a server */
    static class DisconnectAction extends AbstractAction {
        private static DisconnectAction ref = null;

        private DisconnectAction(String text, ImageIcon icon, String desc, Integer mnemonic) {
            super(text, icon);
            putValue(SHORT_DESCRIPTION, desc);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        public static DisconnectAction getInstance() {
            if (ref == null) ref = new DisconnectAction("Disconnect", disconnectIcon, null, KeyEvent.VK_D);
            return ref;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            int index = tabbedPane.getSelectedIndex();
            if (index == -1) return;
            ChannelPanel channel = (ChannelPanel) tabbedPane.getSelectedComponent();
            if (!channel.connection.isConnected) return;
            channel.model.removeAll();
            channel.connection.disconnect();
            for (int i = 0; i < tabbedPane.getTabCount(); i++) {
                ChannelPanel otherChannel = (ChannelPanel) tabbedPane.getComponentAt(i);

                if (otherChannel.name.equals(channel.server)) {
                    try {
                        String[] msg = {null, "Disconnected from " + otherChannel.server + " (port " + otherChannel.connection.port + ")"};
                        otherChannel.insertString(msg, serverStyle, false);
                    } catch (BadLocationException | IOException ex) {
                        Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                if (otherChannel == channel) tabbedPane.setIconAt(i, notConnectedIcon);
                if (otherChannel.connection == channel.connection) {
                    tabbedPane.setIconAt(i, notConnectedIcon);
                    otherChannel.checkForActiveTab();
                    otherChannel.model.removeAll();
                }

            }
            channel.updateTabInfo();
            channel.checkForActiveTab();
        }

    }




    /** Action for finding text in the chatPane i.e. ctrl+F */
    static class FindTextAction extends AbstractAction {
        static boolean isOpened;
        private static FindTextAction ref = null;

        private FindTextAction(String text, ImageIcon icon, String desc, Integer mnemonic) {
            super(text, icon);
            putValue(SHORT_DESCRIPTION, desc);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        public static FindTextAction getInstance() {
            if (ref == null) ref = new FindTextAction("Find", findTextIcon, null, KeyEvent.VK_F);
            return ref;
        }

        private void removeHighlights() {
            for (int i = 0; i < tabbedPane.getTabCount(); i++) {
                ChannelPanel channel = (ChannelPanel) tabbedPane.getComponentAt(i);
                DefaultHighlighter highlighter = (DefaultHighlighter) channel.chatPane.getHighlighter();
                highlighter.removeAllHighlights();
                channel.lastSearchIndex = -1;
            }
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (isOpened) return;
            final JDialog dialog = new JDialog(frame, true);
            dialog.setTitle("Find");
            SpringLayout layout = new SpringLayout();
            JPanel panel = new JPanel(layout);

            dialog.add(panel);
            dialog.setPreferredSize(new Dimension(240, 120));
            dialog.setResizable(false);

            Container contentPane = dialog.getContentPane();
            contentPane.setLayout(layout);

            final JTextField field = new JTextField();

            final JCheckBox matchCase = new JCheckBox("Match Case");
            final JCheckBox highlightAll = new JCheckBox("Highlight All");
            final JButton findButton = new JButton("Find");
            final JButton closeButton = new JButton("Close");
            findButton.setPreferredSize(new Dimension(100, 25));
            closeButton.setPreferredSize(new Dimension(100, 25));
            contentPane.add(field);
            contentPane.add(matchCase);
            contentPane.add(highlightAll);
            contentPane.add(findButton);
            contentPane.add(closeButton);
            layout.putConstraint(SpringLayout.NORTH, field, 5, SpringLayout.NORTH, dialog);
            layout.putConstraint(SpringLayout.WEST, field, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.EAST, field, -15, SpringLayout.EAST, dialog);
            layout.putConstraint(SpringLayout.WEST, matchCase, 0, SpringLayout.WEST, field);
            layout.putConstraint(SpringLayout.NORTH, matchCase, 5, SpringLayout.SOUTH, field);
            layout.putConstraint(SpringLayout.NORTH, highlightAll, 5, SpringLayout.SOUTH, field);
            layout.putConstraint(SpringLayout.EAST, highlightAll, -5, SpringLayout.EAST, contentPane);
            layout.putConstraint(SpringLayout.SOUTH, findButton, -5, SpringLayout.SOUTH, contentPane);
            layout.putConstraint(SpringLayout.WEST, findButton, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.SOUTH, closeButton, -5, SpringLayout.SOUTH, contentPane);
            layout.putConstraint(SpringLayout.EAST, closeButton, -15, SpringLayout.EAST, dialog);
            closeButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    removeHighlights();
                    dialog.dispose();
                }
            });
            closeButton.addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent e) {
                    if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                        closeButton.getActionListeners()[0].actionPerformed(null);
                    }
                    if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                        closeButton.getActionListeners()[0].actionPerformed(null);
                    }
                }
            });

            dialog.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosing(WindowEvent e) {
                    closeButton.getActionListeners()[0].actionPerformed(null);
                }
            });
            field.getDocument().addDocumentListener(new DocumentListener() {
                @Override
                public void insertUpdate(DocumentEvent e) {
                    removeHighlights();
                    findButton.getActionListeners()[0].actionPerformed(null);
                }

                @Override
                public void removeUpdate(DocumentEvent e) {
                    removeHighlights();
                    findButton.getActionListeners()[0].actionPerformed(null);
                }

                @Override
                public void changedUpdate(DocumentEvent e) {
                    removeHighlights();
                    findButton.getActionListeners()[0].actionPerformed(null);
                }
            });
            field.addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent e) {
                    if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                        closeButton.getActionListeners()[0].actionPerformed(null);
                    }
                    if (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_F) {
                        closeButton.getActionListeners()[0].actionPerformed(null);
                    }
                    if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                        findButton.getActionListeners()[0].actionPerformed(null);
                    }
                }
            });
            findButton.addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent e) {
                    if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                        closeButton.getActionListeners()[0].actionPerformed(null);
                    }
                    if (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_F) {
                        closeButton.getActionListeners()[0].actionPerformed(null);
                    }
                    if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                        findButton.getActionListeners()[0].actionPerformed(null);
                    }
                }
            });

            findButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    ChannelPanel channel = (ChannelPanel) tabbedPane.getSelectedComponent();
                    StyledDocument doc = channel.chatPane.getStyledDocument();
                    DefaultHighlighter.DefaultHighlightPainter p = new DefaultHighlighter.DefaultHighlightPainter(highlightColor);
                    DefaultHighlighter highlighter = (DefaultHighlighter) channel.chatPane.getHighlighter();
                    highlighter.setDrawsLayeredHighlights(false);
                    String searchString = field.getText();


                    if (highlightAll.isSelected()) {
                        highlightAll(searchString);
                        return;
                    }


                    String s = "";
                    try {
                        s = doc.getText(0, doc.getLength());
                    } catch (BadLocationException ex) {
                        Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
                    }


                    int location;
                    if (matchCase.isSelected()) {
                        if (channel.lastSearchIndex == -1) location = s.indexOf(searchString);
                        else location = s.indexOf(searchString, channel.lastSearchIndex + searchString.length());
                    } else {
                        if (channel.lastSearchIndex == -1) location = StringUtils.indexOfIgnoreCase(s, searchString);
                        else
                            location = StringUtils.indexOfIgnoreCase(s, searchString, channel.lastSearchIndex + searchString.length());
                    }

                    if (location != -1) {
                        try {
                            removeHighlights();
                            highlighter.addHighlight(location, location + searchString.length(), p);
                            channel.lastSearchIndex = location;
                            DefaultCaret caret = (DefaultCaret) channel.chatPane.getCaret();
                            caret.setDot(location);
                        } catch (BadLocationException ex) {
                            Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                }

                private void highlightAll(String searchString) {
                    ChannelPanel channel = (ChannelPanel) tabbedPane.getSelectedComponent();
                    StyledDocument doc = channel.chatPane.getStyledDocument();
                    DefaultHighlighter.DefaultHighlightPainter p = new DefaultHighlighter.DefaultHighlightPainter(highlightColor);
                    DefaultHighlighter highlighter = (DefaultHighlighter) channel.chatPane.getHighlighter();
                    String s = "";
                    try {
                        s = doc.getText(0, doc.getLength());
                    } catch (BadLocationException ex) {
                        Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
                    }

                    removeHighlights();

                    int location = 0;

                    while (location != -1) {
                        if (matchCase.isSelected()) {
                            if (channel.lastSearchIndex == -1) location = s.indexOf(searchString);
                            else location = s.indexOf(searchString, channel.lastSearchIndex + searchString.length());
                        } else {
                            if (channel.lastSearchIndex == -1)
                                location = StringUtils.indexOfIgnoreCase(s, searchString);
                            else
                                location = StringUtils.indexOfIgnoreCase(s, searchString, channel.lastSearchIndex + searchString.length());
                        }
                        channel.lastSearchIndex = location;
                        if (location == -1) break;
                        try {
                            highlighter.addHighlight(location, location + searchString.length(), p);
                        } catch (BadLocationException ex) {
                            Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                }
            });
            findButton.addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent e) {
                    findButton.getActionListeners()[0].actionPerformed(null);
                }
            });
            highlightAll.addItemListener(new ItemListener() {
                @Override
                public void itemStateChanged(ItemEvent e) {
                    matchCase.getItemListeners()[0].itemStateChanged(null);
                }
            });
            matchCase.addItemListener(new ItemListener() {
                @Override
                public void itemStateChanged(ItemEvent e) {
                    if (field.getText().isEmpty()) return;
                    for (int i = 0; i < tabbedPane.getTabCount(); i++) {
                        ChannelPanel channel = (ChannelPanel) tabbedPane.getComponentAt(i);
                        DefaultHighlighter highlighter = (DefaultHighlighter) channel.chatPane.getHighlighter();
                        if (!channel.isShowing()) highlighter.removeAllHighlights();
                        channel.lastSearchIndex = -1;
                    }
                    findButton.getActionListeners()[0].actionPerformed(null);
                }
            });
            dialog.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosed(WindowEvent e) {
                    removeHighlights();
                    isOpened = false;
                }
            });
            dialog.pack();
            isOpened = true;
            dialog.setModalityType(Dialog.ModalityType.MODELESS);
            dialog.setLocationRelativeTo(frame);
            dialog.setVisible(!dialog.isShowing());
        }

    }



    /** Constructs a menu for changing fonts */
    static class FontMenuAction extends AbstractAction {
        final static int CHATFONT = 0;
        final static int NICKLISTFONT = 1;
        final JTextField fontField;
        final JDialog dialog;
        final int choice;

        private FontMenuAction(JTextField f, JDialog d, int j) {
            fontField = f;
            dialog = d;
            choice = j; //CHATFONT or NICKLISTFONT
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            SpringLayout fontLayout = new SpringLayout();
            final JDialog fontDialog = new JDialog(frame, "Select Font", true);
            fontDialog.getContentPane().setLayout(fontLayout);
            fontDialog.setPreferredSize(new Dimension(395, 350));
            fontDialog.setResizable(false);

            final JLabel chatFontLabel = new JLabel("Family");
            JLabel chatFontStyleLabel = new JLabel("Style");
            JLabel chatFontSizeLabel = new JLabel("Size");
            JLabel chatFontPreview = new JLabel("Preview");
            final JButton OKButton = new JButton("OK");
            OKButton.setPreferredSize(new Dimension(70, 30));
            JButton cancelButton = new JButton("Cancel");
            cancelButton.setPreferredSize(new Dimension(70, 30));


            String[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
            final JList<String> ffList = new JList<>(fonts);
            ffList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            JScrollPane ffsp = new JScrollPane(ffList);
            ffsp.setPreferredSize(new Dimension(200, 200));
            if (choice == CHATFONT) ffList.setSelectedValue(chatFont, true);
            else ffList.setSelectedValue(userListFont, true);


            String fontStyles[] = {"Plain", "Bold", "Italic", "Bold Italic"};
            final JList<String> fsList = new JList<>(fontStyles);
            fsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

            JScrollPane fssp = new JScrollPane(fsList);
            fssp.setPreferredSize(new Dimension(100, 200));
            if (choice == CHATFONT) fsList.setSelectedValue(chatFontStyle, true);
            else fsList.setSelectedValue(userListFontStyle, true);

            Integer[] fontSizes = {6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 30, 32};
            final JList<Integer> sizeList = new JList<>(fontSizes);
            sizeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            JScrollPane sizesp = new JScrollPane(sizeList);
            sizesp.setPreferredSize(new Dimension(50, 200));

            if (choice == CHATFONT) sizeList.setSelectedValue(chatFontSize, true);
            else sizeList.setSelectedValue(userListFontSize, true);

            final JTextField previewArea = new JTextField();
            previewArea.setPreferredSize(new Dimension(370, 70));
            previewArea.setAlignmentY(JTextField.CENTER_ALIGNMENT);
            previewArea.setHorizontalAlignment(JTextField.CENTER);
            final String fontName = ffList.getSelectedValue();
            final int size = sizeList.getSelectedValue();
            final String style = fsList.getSelectedValue();

            Font font;
            switch (style) {
                case "Bold":
                    font = new Font(fontName, Font.BOLD, size);
                    break;
                case "Italic":
                    font = new Font(fontName, Font.ITALIC, size);
                    break;
                case "Bold Italic":
                    font = new Font(fontName, Font.ITALIC | Font.BOLD, size);
                    break;
                default:
                    font = new Font(fontName, Font.PLAIN, size);
                    break;
            }

            previewArea.setFont(font);
            previewArea.setText("The quick brown fox jumps over the lazy dog");

            fontLayout.putConstraint(SpringLayout.NORTH, chatFontLabel, 0, SpringLayout.NORTH, fontDialog);
            fontLayout.putConstraint(SpringLayout.WEST, chatFontLabel, 10, SpringLayout.WEST, fontDialog);
            fontLayout.putConstraint(SpringLayout.NORTH, ffsp, 2, SpringLayout.SOUTH, chatFontLabel);
            fontLayout.putConstraint(SpringLayout.WEST, ffsp, 0, SpringLayout.WEST, chatFontLabel);

            fontLayout.putConstraint(SpringLayout.NORTH, chatFontStyleLabel, 0, SpringLayout.NORTH, fontDialog);
            fontLayout.putConstraint(SpringLayout.WEST, chatFontStyleLabel, 10, SpringLayout.EAST, ffsp);
            fontLayout.putConstraint(SpringLayout.NORTH, fssp, 2, SpringLayout.SOUTH, chatFontStyleLabel);
            fontLayout.putConstraint(SpringLayout.WEST, fssp, 10, SpringLayout.EAST, ffsp);

            fontLayout.putConstraint(SpringLayout.NORTH, chatFontSizeLabel, 0, SpringLayout.NORTH, fontDialog);
            fontLayout.putConstraint(SpringLayout.WEST, chatFontSizeLabel, 10, SpringLayout.EAST, fssp);
            fontLayout.putConstraint(SpringLayout.NORTH, sizesp, 2, SpringLayout.SOUTH, chatFontSizeLabel);
            fontLayout.putConstraint(SpringLayout.WEST, sizesp, 10, SpringLayout.EAST, fssp);

            fontLayout.putConstraint(SpringLayout.NORTH, chatFontPreview, 5, SpringLayout.SOUTH, ffsp);
            fontLayout.putConstraint(SpringLayout.WEST, chatFontPreview, 10, SpringLayout.WEST, fontDialog);
            fontLayout.putConstraint(SpringLayout.NORTH, previewArea, 2, SpringLayout.SOUTH, chatFontPreview);
            fontLayout.putConstraint(SpringLayout.WEST, previewArea, 0, SpringLayout.WEST, ffsp);

            fontLayout.putConstraint(SpringLayout.EAST, OKButton, -15, SpringLayout.EAST, fontDialog);
            fontLayout.putConstraint(SpringLayout.SOUTH, OKButton, -5, SpringLayout.SOUTH, fontDialog);
            fontLayout.putConstraint(SpringLayout.SOUTH, cancelButton, 0, SpringLayout.SOUTH, OKButton);
            fontLayout.putConstraint(SpringLayout.EAST, cancelButton, -5, SpringLayout.WEST, OKButton);

            fontDialog.getRootPane().setDefaultButton(OKButton);
            OKButton.requestFocus();

            fontDialog.add(OKButton);
            fontDialog.add(cancelButton);
            fontDialog.add(chatFontLabel);
            fontDialog.add(chatFontStyleLabel);
            fontDialog.add(chatFontSizeLabel);
            fontDialog.add(chatFontPreview);
            fontDialog.add(previewArea);
            fontDialog.add(ffsp);
            fontDialog.add(fssp);
            fontDialog.add(sizesp);

            cancelButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    fontDialog.dispose();
                }
            });
            cancelButton.addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent e) {
                    if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                        fontDialog.dispose();
                    }
                }
            });
            OKButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (choice == CHATFONT) {
                        chatFont = ffList.getSelectedValue();
                        chatFontSize = sizeList.getSelectedValue();
                        chatFontStyle = fsList.getSelectedValue();

                        try {
                            prop.load(new FileInputStream("config.properties"));
                            prop.setProperty("chatFont", chatFont);
                            prop.setProperty("chatFontSize", String.valueOf(chatFontSize));
                            prop.setProperty("chatFontStyle", chatFontStyle);
                            prop.store(new FileOutputStream("config.properties"), null);
                        } catch (IOException e1) {
                            e1.printStackTrace();
                        }

                        setStyles();
                    }
                    else //choice == NICKLISTFONT
                    {
                        userListFont = ffList.getSelectedValue();
                        userListFontSize = sizeList.getSelectedValue();
                        userListFontStyle = fsList.getSelectedValue();

                        try {
                            prop.load(new FileInputStream("config.properties"));
                            prop.setProperty("userListFont", userListFont);
                            prop.setProperty("userListFontSize", String.valueOf(userListFontSize));
                            prop.setProperty("userListFontStyle", userListFontStyle);
                            prop.store(new FileOutputStream("config.properties"), null);
                        } catch (IOException e1) {
                            e1.printStackTrace();
                        }
                        Font font;
                        switch (style) {
                            case "Bold":
                                font = new Font(fontName, Font.BOLD, size);
                                break;
                            case "Italic":
                                font = new Font(fontName, Font.ITALIC, size);
                                break;
                            case "Bold Italic":
                                font = new Font(fontName, Font.ITALIC | Font.BOLD, size);
                                break;
                            default:
                                font = new Font(fontName, Font.PLAIN, size);
                                break;
                        }
                        fontField.setFont(font);
                        fontField.setText(font.getFamily()+" "+font.getSize());

                        setStyles();
                    }
                    fontDialog.dispose();
                }
            });
            OKButton.addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent e) {
                    if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                        fontDialog.dispose();
                    }
                    if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                        OKButton.getActionListeners()[0].actionPerformed(null);
                    }
                }
            });
            ffList.addListSelectionListener(new ListSelectionListener() {
                @Override
                public void valueChanged(ListSelectionEvent e) {
                    final String fontName = ffList.getSelectedValue();
                    final int size = sizeList.getSelectedValue();
                    final String style = fsList.getSelectedValue();

                    Font font;
                    switch (style) {
                        case "Bold":
                            font = new Font(fontName, Font.BOLD, size);
                            break;
                        case "Italic":
                            font = new Font(fontName, Font.ITALIC, size);
                            break;
                        case "Bold Italic":
                            font = new Font(fontName, Font.ITALIC | Font.BOLD, size);
                            break;
                        default:
                            font = new Font(fontName, Font.PLAIN, size);
                            break;
                    }
                    previewArea.setFont(font);
                    String text = previewArea.getText();
                    try {
                        previewArea.getDocument().remove(0, previewArea.getDocument().getLength());
                        previewArea.setText(text);

                    } catch (BadLocationException ex) {
                        Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            });
            fsList.addListSelectionListener(ffList.getListSelectionListeners()[0]);
            sizeList.addListSelectionListener(ffList.getListSelectionListeners()[0]);

            fontDialog.pack();
            fontDialog.setResizable(false);
            fontDialog.setLocationRelativeTo(dialog);
            fontDialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            fontDialog.setVisible(true);
        }
    }




    /** Action for away status in all channels and connections */
    static class GlobalAwayAction extends AbstractAction {
        private static GlobalAwayAction ref = null;

        private GlobalAwayAction(String text, ImageIcon icon, String desc, Integer mnemonic) {
            super(text, icon);
            putValue(SHORT_DESCRIPTION, desc);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        public static GlobalAwayAction getInstance() {
            if (ref == null) ref = new GlobalAwayAction("Global Away", globalAwayIcon, null, KeyEvent.VK_A);
            return ref;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            for (int i = 0; i < tabbedPane.getTabCount(); i++) {
                ChannelPanel channel = (ChannelPanel) tabbedPane.getComponentAt(i);
                String name = channel.name;
                String server = channel.server;
                String message;
                if (name.equals(server)) {
                    if (ChannelPanel.awayStatus) {
                        message = "AWAY";
                        ChannelPanel.awayStatus = false;
                    } else {
                        message = "AWAY " + awayMessage;
                        ChannelPanel.awayStatus = true;
                    }
                    try {
                        channel.connection.send(message);
                    } catch (IOException | BadLocationException ex) {
                        Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }

        }
    }




    /** Builds menu for settings nick fields */
    static class IdentityAction extends AbstractAction {
        private static IdentityAction ref = null;

        private IdentityAction(String text, ImageIcon icon, String desc, Integer mnemonic) {
            super(text, icon);
            putValue(SHORT_DESCRIPTION, desc);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        public static IdentityAction getInstance() {
            if (ref == null) ref = new IdentityAction("Identities", identityIcon, null, KeyEvent.VK_I);
            return ref;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            final JDialog dialog = new JDialog(frame, "Identity", true);
            SpringLayout layout = new SpringLayout();
            dialog.getContentPane().setLayout(layout);
            dialog.setPreferredSize(new Dimension(200, 310));

            JLabel realNameLabel = new JLabel(" Real Name");
            realNameLabel.setPreferredSize(new Dimension(175, 25));
            JLabel firstChoiceLabel = new JLabel(" Nick");
            firstChoiceLabel.setPreferredSize(new Dimension(175, 25));
            JLabel secondChoiceLabel = new JLabel("Second choice");
            secondChoiceLabel.setPreferredSize(new Dimension(175, 25));
            JLabel thirdChoiceLabel = new JLabel(" Third Choice");
            thirdChoiceLabel.setPreferredSize(new Dimension(175, 25));

            final JTextField realNameField = new JTextField();
            realNameField.setText(Connection.real);
            realNameField.setPreferredSize(new Dimension(175, 25));
            final JTextField nameField = new JTextField();
            nameField.setText(Connection.nicks[0]);
            nameField.setPreferredSize(new Dimension(175, 25));
            final JTextField secondNameField = new JTextField();
            secondNameField.setText(Connection.nicks[1]);
            secondNameField.setPreferredSize(new Dimension(175, 25));
            final JTextField thirdNameField = new JTextField();
            thirdNameField.setText(Connection.nicks[2]);
            thirdNameField.setPreferredSize(new Dimension(175, 25));

            final JButton saveButton = new JButton("Save");
            saveButton.setPreferredSize(new Dimension(70, 30));

            layout.putConstraint(SpringLayout.WEST, realNameLabel, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, realNameLabel, 5, SpringLayout.NORTH, dialog);

            layout.putConstraint(SpringLayout.WEST, realNameField, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, realNameField, 0, SpringLayout.SOUTH, realNameLabel);

            layout.putConstraint(SpringLayout.WEST, firstChoiceLabel, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, firstChoiceLabel, 5, SpringLayout.SOUTH, realNameField);

            layout.putConstraint(SpringLayout.WEST, nameField, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, nameField, 0, SpringLayout.SOUTH, firstChoiceLabel);

            layout.putConstraint(SpringLayout.WEST, secondChoiceLabel, 5, SpringLayout.WEST, nameField);
            layout.putConstraint(SpringLayout.NORTH, secondChoiceLabel, 5, SpringLayout.SOUTH, nameField);

            layout.putConstraint(SpringLayout.WEST, secondNameField, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, secondNameField, 0, SpringLayout.SOUTH, secondChoiceLabel);

            layout.putConstraint(SpringLayout.WEST, thirdChoiceLabel, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, thirdChoiceLabel, 5, SpringLayout.SOUTH, secondNameField);

            layout.putConstraint(SpringLayout.WEST, thirdNameField, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, thirdNameField, 0, SpringLayout.SOUTH, thirdChoiceLabel);

            layout.putConstraint(SpringLayout.EAST, saveButton, -35, SpringLayout.EAST, dialog);
            layout.putConstraint(SpringLayout.SOUTH, saveButton, -45, SpringLayout.SOUTH, dialog);

            dialog.add(realNameLabel);
            dialog.add(realNameField);
            dialog.add(firstChoiceLabel);
            dialog.add(nameField);
            dialog.add(secondChoiceLabel);
            dialog.add(secondNameField);
            dialog.add(thirdChoiceLabel);
            dialog.add(thirdNameField);
            dialog.add(saveButton);


            saveButton.addKeyListener(new KeyAdapter() {
                public void keyPressed(KeyEvent evt) {
                    if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
                        saveButton.getActionListeners()[0].actionPerformed(null);
                    }
                }
            });
            saveButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    try {
                        Connection.real = realNameField.getText().trim();
                        Connection.nicks[0] = nameField.getText().trim();
                        Connection.nicks[1] = secondNameField.getText().trim();
                        Connection.nicks[2] = thirdNameField.getText().trim();

                        prop.load(new FileInputStream("config.properties"));
                        prop.setProperty("Real", Connection.real);
                        prop.setProperty("Nick", Connection.nicks[0]);
                        prop.setProperty("Second", Connection.nicks[1]);
                        prop.setProperty("Third", Connection.nicks[2]);
                        prop.store(new FileOutputStream("config.properties"), null);

                    } catch (IOException io) {
                        Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, io);
                    }
                    dialog.dispose();
                }

            });

            dialog.pack();
            dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            dialog.setLocationRelativeTo(frame);
            dialog.setVisible(true);
        }
    }




    /** A dialog for joining a channel */
    static class JoinChannelAction extends AbstractAction {
        static JoinChannelAction ref = null;

        private JoinChannelAction(String text, ImageIcon icon, String desc, Integer mnemonic) {
            super(text, icon);
            putValue(SHORT_DESCRIPTION, desc);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        public static JoinChannelAction getInstance() {
            if (ref == null) ref = new JoinChannelAction("Join Channel", joinChannelIcon, null, KeyEvent.VK_J);
            return ref;
        }

        @Override
        public void actionPerformed(ActionEvent e) {

            SortedSet<String> set = new TreeSet<>();
            for (int i = 0; i < tabbedPane.getTabCount(); i++) {
                ChannelPanel channel = (ChannelPanel) tabbedPane.getComponentAt(i);
                String srv = channel.connection.title;
                set.add(srv);
            }
            Object[] things = set.toArray();


            final JDialog dialog = new JDialog(frame, "Join Channel", true);
            dialog.setSize(new Dimension(300, 190));
            SpringLayout layout = new SpringLayout();
            JPanel panel = new JPanel(layout);
            dialog.setResizable(false);
            dialog.add(panel);
            final JComboBox<Object> combobox = new JComboBox<Object>(things);
            JLabel serverLabel = new JLabel("Connection");
            JLabel chanLabel = new JLabel("Channel");

            JLabel pwLabel = new JLabel("Password");
            final JTextField channelField = new JTextField();
            channelField.setPreferredSize(new Dimension(180, 21));
            JTextField pwField = new JTextField();
            pwField.setPreferredSize(new Dimension(180, 21));
            combobox.setPreferredSize(new Dimension(180, 21));
            JButton cancel = new JButton("Cancel");
            final JButton ok = new JButton("Join");

            cancel.setPreferredSize(new Dimension(70, 30));
            ok.setPreferredSize(new Dimension(70, 30));

            panel.add(serverLabel);
            panel.add(combobox);
            panel.add(chanLabel);
            panel.add(channelField);
            panel.add(pwLabel);
            panel.add(pwField);
            panel.add(cancel);
            panel.add(ok);


            layout.putConstraint(SpringLayout.NORTH, serverLabel, 15, SpringLayout.NORTH, dialog);
            layout.putConstraint(SpringLayout.WEST, serverLabel, 15, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, combobox, 13, SpringLayout.NORTH, dialog);
            layout.putConstraint(SpringLayout.WEST, combobox, 10, SpringLayout.EAST, serverLabel);
            layout.putConstraint(SpringLayout.NORTH, chanLabel, 16, SpringLayout.SOUTH, combobox);
            layout.putConstraint(SpringLayout.WEST, chanLabel, 0, SpringLayout.WEST, serverLabel);
            layout.putConstraint(SpringLayout.NORTH, channelField, 15, SpringLayout.SOUTH, combobox);
            layout.putConstraint(SpringLayout.WEST, channelField, 0, SpringLayout.WEST, combobox);
            layout.putConstraint(SpringLayout.NORTH, pwLabel, 18, SpringLayout.SOUTH, chanLabel);
            layout.putConstraint(SpringLayout.WEST, pwLabel, 0, SpringLayout.WEST, serverLabel);
            layout.putConstraint(SpringLayout.NORTH, pwField, 13, SpringLayout.SOUTH, channelField);
            layout.putConstraint(SpringLayout.WEST, pwField, 0, SpringLayout.WEST, combobox);
            layout.putConstraint(SpringLayout.WEST, cancel, 115, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.SOUTH, cancel, 120, SpringLayout.SOUTH, dialog);
            layout.putConstraint(SpringLayout.WEST, ok, 10, SpringLayout.EAST, cancel);
            layout.putConstraint(SpringLayout.SOUTH, ok, 0, SpringLayout.SOUTH, cancel);

            cancel.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    dialog.dispose();
                }
            });
            cancel.addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent evt) {
                    if (evt.getKeyCode() == KeyEvent.VK_ENTER) dialog.dispose();
                }
            });


            ok.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    String choice = (String) combobox.getSelectedItem();
                    String chan = channelField.getText();


                    if (!chan.startsWith("#")) chan = "#" + chan;
                    if (choice == null) {
                        JOptionPane.showConfirmDialog(dialog, "Please choose a connection.", "No connection chosen", JOptionPane.WARNING_MESSAGE);
                        return;
                    }
                    ChannelPanel channel = null;
                    for (int i = 0; i < tabbedPane.getTabCount(); i++) {
                        channel = ((ChannelPanel) tabbedPane.getComponentAt(i));
                        if (channel.connection.title.equals(choice)) {
                            break;
                        }
                    }
                    try {
                        if (channel != null) {
                            channel.connection.send("JOIN " + chan);
                        }
                        dialog.dispose();
                    } catch (IOException | BadLocationException ex) {
                        Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
                    }

                }
            });
            ok.addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent evt) {
                    if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
                        ok.getActionListeners()[0].actionPerformed(null);
                    }
                }
            });
            dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            dialog.setLocationRelativeTo(frame);
            dialog.setVisible(true);
        }
    }





    /** Shifts selected tab left on the tabbedPane and maintains the selected window */
    static class MoveTabLeftAction extends AbstractAction {
        private static MoveTabLeftAction ref = null;

        private MoveTabLeftAction(String text, ImageIcon icon, String desc, Integer mnemonic) {
            super(text, icon);
            putValue(SHORT_DESCRIPTION, desc);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        public static MoveTabLeftAction getInstance() {
            if (ref == null) ref = new MoveTabLeftAction("Move Tab Left", moveTabLeftIcon, null, KeyEvent.VK_L);
            return ref;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            int index = tabbedPane.getSelectedIndex();
            if (index <= 0) return;
            ChannelPanel moved = (ChannelPanel) tabbedPane.getComponentAt(index);
            Color c = tabbedPane.getForegroundAt(index);
            String label = tabbedPane.getTitleAt(index);
            tabbedPane.add(moved, index - 1);
            tabbedPane.setTitleAt(index - 1, label);
            tabbedPane.setSelectedComponent(moved);
            tabbedPane.setForegroundAt(index - 1, c);
        }
    }





    /** Shifts selected tab right on the tabbedPane and maintains the selected window */
    static class MoveTabRightAction extends AbstractAction {
        private static MoveTabRightAction ref = null;

        private MoveTabRightAction(String text, ImageIcon icon, String desc, Integer mnemonic) {
            super(text, icon);
            putValue(SHORT_DESCRIPTION, desc);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        public static MoveTabRightAction getInstance() {
            if (ref == null) ref = new MoveTabRightAction("Move Tab Right", moveTabRightIcon, null, KeyEvent.VK_R);
            return ref;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            int index = tabbedPane.getSelectedIndex() + 1;
            if (index >= tabbedPane.getTabCount()) return;
            ChannelPanel moved = (ChannelPanel) tabbedPane.getComponentAt(index);
            Color c = tabbedPane.getForegroundAt(index);
            String label = tabbedPane.getTitleAt(index);
            tabbedPane.add(moved, index - 1);
            tabbedPane.setTitleAt(index - 1, label);
            tabbedPane.setSelectedIndex(index);
            tabbedPane.setForegroundAt(index - 1, c);
        }
    }





    /** Action for changing the currently selected tab */
    static class NextTabAction extends AbstractAction {
        private static NextTabAction ref = null;

        private NextTabAction(String text, ImageIcon icon, String desc, Integer mnemonic) {
            super(text, icon);
            putValue(SHORT_DESCRIPTION, desc);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        public static NextTabAction getInstance() {
            if (ref == null) ref = new NextTabAction("Next Tab", nextTabIcon, null, KeyEvent.VK_N);
            return ref;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            int indexOfChannel = tabbedPane.getSelectedIndex();
            if (indexOfChannel < tabbedPane.getTabCount() - 1) tabbedPane.setSelectedIndex(indexOfChannel + 1);
        }
    }





    /** Action for changing the currently selected tab */
    static class PreviousTabAction extends AbstractAction {
        private static PreviousTabAction ref = null;

        private PreviousTabAction(String text, ImageIcon icon, String desc, Integer mnemonic) {
            super(text, icon);
            putValue(SHORT_DESCRIPTION, desc);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        public static PreviousTabAction getInstance() {
            if (ref == null) ref = new PreviousTabAction("PreviousTab", prevTabIcon, null, KeyEvent.VK_P);
            return ref;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            int indexOfChannel = tabbedPane.getSelectedIndex();
            if (indexOfChannel > 0) tabbedPane.setSelectedIndex(indexOfChannel - 1);
        }
    }




    /** A small dialog allowing quick connection to a server */
    static class QuickConnectAction extends AbstractAction {
        private static QuickConnectAction ref = null;

        private QuickConnectAction(String text, ImageIcon icon, String desc, Integer mnemonic) {
            super(text, icon);
            putValue(SHORT_DESCRIPTION, desc);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        public static QuickConnectAction getInstance() {
            if (ref == null) ref = new QuickConnectAction("Quick Connect", quickConnectIcon, null, KeyEvent.VK_C);
            return ref;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            SpringLayout layout = new SpringLayout();

            final JDialog dialog = new JDialog(frame, "Quick Connect", true);
            dialog.getContentPane().setLayout(layout);
            dialog.setPreferredSize(new Dimension(200, 340));
            final JButton connectButton = new JButton("Connect");
            JButton cancelButton = new JButton("Cancel");
            connectButton.setPreferredSize(new Dimension(80, 30));
            cancelButton.setPreferredSize(new Dimension(70, 30));

            JLabel networkNameLabel = new JLabel("Network Name");
            JLabel serverLabel = new JLabel("Server");
            JLabel portLabel = new JLabel("Port");
            JLabel nickLabel = new JLabel("Nick");
            JLabel passLabel = new JLabel("Password");

            final JTextField networkNameField = new JTextField();
            networkNameField.setPreferredSize(new Dimension(175, 25));
            final JTextField serverField = new JTextField();
            serverField.setPreferredSize(new Dimension(175, 25));
            final JTextField portField = new JTextField();
            portField.setPreferredSize(new Dimension(175, 25));
            final JTextField nickField = new JTextField();
            nickField.setPreferredSize(new Dimension(175, 25));
            final JTextField passField = new JTextField();
            passField.setPreferredSize(new Dimension(175, 25));

            layout.putConstraint(SpringLayout.WEST, networkNameLabel, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, networkNameLabel, 5, SpringLayout.NORTH, dialog);

            layout.putConstraint(SpringLayout.WEST, networkNameField, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, networkNameField, 5, SpringLayout.SOUTH, networkNameLabel);

            layout.putConstraint(SpringLayout.WEST, serverLabel, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, serverLabel, 5, SpringLayout.SOUTH, networkNameField);

            layout.putConstraint(SpringLayout.WEST, serverField, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, serverField, 5, SpringLayout.SOUTH, serverLabel);

            layout.putConstraint(SpringLayout.WEST, portLabel, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, portLabel, 5, SpringLayout.SOUTH, serverField);

            layout.putConstraint(SpringLayout.WEST, portField, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, portField, 5, SpringLayout.SOUTH, portLabel);

            layout.putConstraint(SpringLayout.WEST, nickLabel, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, nickLabel, 5, SpringLayout.SOUTH, portField);

            layout.putConstraint(SpringLayout.WEST, nickField, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, nickField, 5, SpringLayout.SOUTH, nickLabel);

            layout.putConstraint(SpringLayout.WEST, passLabel, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, passLabel, 5, SpringLayout.SOUTH, nickField);

            layout.putConstraint(SpringLayout.WEST, passField, 5, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.NORTH, passField, 5, SpringLayout.SOUTH, passLabel);

            layout.putConstraint(SpringLayout.WEST, cancelButton, 15, SpringLayout.WEST, dialog);
            layout.putConstraint(SpringLayout.SOUTH, cancelButton, -55, SpringLayout.SOUTH, dialog);

            layout.putConstraint(SpringLayout.WEST, connectButton, 10, SpringLayout.EAST, cancelButton);
            layout.putConstraint(SpringLayout.SOUTH, connectButton, -55, SpringLayout.SOUTH, dialog);

            dialog.add(networkNameLabel);
            dialog.add(networkNameField);
            dialog.add(serverLabel);
            dialog.add(serverField);
            dialog.add(portLabel);
            dialog.add(portField);
            dialog.add(nickLabel);
            dialog.add(nickField);
            dialog.add(passLabel);
            dialog.add(passField);
            dialog.add(connectButton);
            dialog.add(cancelButton);


            connectButton.addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent evt) {
                    if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
                        connectButton.getActionListeners()[0].actionPerformed(null);
                    }
                    if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
                        dialog.dispose();
                    }
                }
            });

            connectButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    String networkName = networkNameField.getText().trim();
                    String server = serverField.getText().trim();
                    String port = portField.getText().trim();
                    String nick = nickField.getText().trim();
                    String pass = passField.getText().trim();

                    if (networkName.isEmpty() || server.isEmpty() || port.isEmpty() || nick.isEmpty()) {
                        JOptionPane.showMessageDialog(dialog, "Network name, server, port and nick are required");
                    } else {
                        dialog.dispose();
                        Connection.nicks[0] = nick;

                        for (int i = 0; i < tabbedPane.getTabCount(); i++) {
                            ChannelPanel channel = (ChannelPanel) tabbedPane.getComponentAt(i);
                            if (networkName.equals(channel.title)) {
                                tabbedPane.setSelectedComponent(channel);
                                String[] msg = {null, "You are already connected to " + networkName + "."};
                                try {
                                    channel.insertString(msg, errorStyle, false);
                                } catch (BadLocationException | IOException ex) {
                                    Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
                                }
                                return;
                            }
                        }

                        Connection c = new Connection(networkName, server, Integer.valueOf(port), false);
                        if (!pass.isEmpty()) c.password = pass;
                    }
                }

            });
            cancelButton.addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent e) {
                    if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                        dialog.dispose();
                    }
                }
            });
            cancelButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    dialog.dispose();
                }
            });

            cancelButton.getRootPane().setDefaultButton(cancelButton);
            cancelButton.requestFocus();
            dialog.pack();
            dialog.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
            dialog.setLocationRelativeTo(frame);
            dialog.setVisible(true);
        }
    }




    /** Menu item for closing the client */
    static class QuitProgramAction extends AbstractAction {
        private static QuitProgramAction ref = null;

        private QuitProgramAction(String text, ImageIcon icon, String desc, Integer mnemonic) {
            super(text, icon);
            putValue(SHORT_DESCRIPTION, desc);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        public static QuitProgramAction getInstance() {
            if (ref == null) ref = new QuitProgramAction("Quit", quitProgramIcon, null, KeyEvent.VK_Q);
            return ref;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            System.exit(0);
        }
    }




    /** Closes and reopens server connections */
    static class ReconnectAction extends AbstractAction {
        private static ReconnectAction ref = null;

        private ReconnectAction(String text, ImageIcon icon, String desc, Integer mnemonic) {
            super(text, icon);
            putValue(SHORT_DESCRIPTION, desc);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        public static ReconnectAction getInstance() {
            if (ref == null) ref = new ReconnectAction("Reconnect", reconnectIcon, null, KeyEvent.VK_R);
            return ref;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            ChannelPanel selected = (ChannelPanel) tabbedPane.getSelectedComponent();
            if (selected == null) return;
            if (selected.connection.isConnected) {
                selected.connection.disconnect();
            }

            //sleep ffor one second to avoid Error: reconnecting too fast from server
            try {
                selected.connection.thread.sleep(1000);
            } catch (InterruptedException e1) {
                System.out.println("reconnect sleep interrupted");
            }

            for (int j = 0; j < tabbedPane.getTabCount(); j++) {
                ChannelPanel otherChannel = (ChannelPanel) tabbedPane.getComponentAt(j);
                if (selected.connection == otherChannel.connection) {
                    selected.connection.thread = new Thread(selected.connection);
                    selected.connection.thread.start();
                    break;
                }
            }
            selected.connection.autoconnect = false;

            //because we can't rejoin channels until the connection is established
            //ths should check for the connection and not just blindly wait

            int tries = 0;
            while (tries != 3) {
                if (!selected.connection.isConnected) {
                    //try to reconnect every 1.5 seconds up to 4 times
                    try {
                        selected.connection.thread.sleep(1500);
                        tries++;
                    } catch (InterruptedException e1) {
                        System.out.println("interrupted sleep");
                    }
                }
                else break;
            }

            if (selected.connection.isConnected) {
                for (int i = 0; i < tabbedPane.getTabCount(); i++) {
                    ChannelPanel channel = (ChannelPanel) tabbedPane.getComponentAt(i);
                    if (channel.connection == selected.connection) {
                        if (channel.title.startsWith("#")) {
                            try {
                                selected.connection.send("JOIN " + channel.title);
                            } catch (IOException ex) {
                                System.out.println("io");
                            } catch (BadLocationException ex) {
                                Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        }
                    }
                }
            }
        }
    }





    /** Displays the list of saved connections and channels to auto-connect to*/
    static class ServerListAction extends AbstractAction {
        private static ServerListAction ref = null;

        private ServerListAction(String text, ImageIcon icon, String desc, Integer mnemonic) {
            super(text, icon);
            putValue(SHORT_DESCRIPTION, desc);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        public static ServerListAction getInstance() {
            if (ref == null) ref = new ServerListAction("Server List", serverListIcon, null, KeyEvent.VK_L);
            return ref;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            final JDialog dialog = new JDialog(frame, "Server List", true);
            Container contentpane = dialog.getContentPane();
            SpringLayout layout = new SpringLayout();
            contentpane.setLayout(layout);
            contentpane.setPreferredSize(new Dimension(550, 200));
            final BeanTableModel<SavedConnection> model = new BeanTableModel<>(SavedConnection.class);
            model.sortColumnNames();
            for (SavedConnection savedConnection : savedConnections) model.addRow(savedConnection);


            final JTable table = new JTable(model) {
                @Override
                public boolean isCellEditable(int row, int column) {
                    return false;
                }
            };

            table.setShowGrid(false);
            table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            table.getColumnModel().getColumn(0).setPreferredWidth(150);
            table.getColumnModel().getColumn(1).setPreferredWidth(120);
            table.getColumnModel().getColumn(2).setPreferredWidth(272);

            JScrollPane scrollPane = new JScrollPane(table);
            scrollPane.setPreferredSize(new Dimension(540, 150));
            scrollPane.setViewportView(table);
            scrollPane.getViewport().setBackground(Color.white);

            final JButton add = new JButton("Add");
            JButton edit = new JButton("Edit");
            JButton remove = new JButton("Remove");
            JButton connect = new JButton("Connect");
            add.setPreferredSize(new Dimension(70, 30));
            edit.setPreferredSize(new Dimension(70, 30));
            remove.setPreferredSize(new Dimension(80, 30));
            connect.setPreferredSize(new Dimension(80, 30));

            scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
            scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
            dialog.setResizable(false);
            dialog.setTitle("Server List");
            contentpane.add(scrollPane);
            contentpane.add(add);
            contentpane.add(edit);
            contentpane.add(remove);
            contentpane.add(connect);
            layout.putConstraint(SpringLayout.WEST, scrollPane, 5, SpringLayout.WEST, contentpane);
            layout.putConstraint(SpringLayout.SOUTH, add, -10, SpringLayout.SOUTH, contentpane);
            layout.putConstraint(SpringLayout.WEST, add, 10, SpringLayout.WEST, contentpane);
            layout.putConstraint(SpringLayout.SOUTH, edit, -10, SpringLayout.SOUTH, contentpane);
            layout.putConstraint(SpringLayout.WEST, edit, 10, SpringLayout.EAST, add);
            layout.putConstraint(SpringLayout.SOUTH, remove, -10, SpringLayout.SOUTH, contentpane);
            layout.putConstraint(SpringLayout.WEST, remove, 10, SpringLayout.EAST, edit);
            layout.putConstraint(SpringLayout.SOUTH, connect, -10, SpringLayout.SOUTH, contentpane);
            layout.putConstraint(SpringLayout.EAST, connect, -15, SpringLayout.EAST, contentpane);

            add.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    JTextField name = new JTextField();
                    JTextField server = new JTextField();
                    JTextField port = new JTextField();
                    JTextField password = new JTextField();
                    JTextField channels = new JTextField();
                    JCheckBox autoconnect = new JCheckBox();

                    Object[] fields = {
                            "Name", name,
                            "Server", server,
                            "Port", port,
                            "Password", password,
                            "Auto Join Channels (#c1 #c2)", channels,
                            "Connect on startup", autoconnect,
                    };
                    int x = JOptionPane.showConfirmDialog(dialog, fields, "Add Server", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
                    if (x == JOptionPane.CLOSED_OPTION || x == JOptionPane.CANCEL_OPTION) return;

                    String[] s = channels.getText().split(" ");
                    ArrayList<String> channelList = new ArrayList<>(Arrays.asList(s));
                    if (!StringUtils.isNumeric(port.getText())) {
                        JOptionPane.showMessageDialog(dialog, "Port must be an integer.");
                        return;
                    }
                    SavedConnection connection = new SavedConnection(name.getText(), server.getText(), password.getText(), channelList, autoconnect.isSelected(), Integer.valueOf(port.getText().trim()));
                    model.addRow(connection);
                    savedConnections.add(connection);
                    serializeSavedConnections();
                }
            });

            add.addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent evt) {
                    if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
                        add.getActionListeners()[0].actionPerformed(null);
                    }
                }
            });
            edit.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    int rowIndex = table.getSelectedRow();
                    if (rowIndex == -1) return;
                    int modelIndex = table.convertRowIndexToModel(rowIndex);
                    SavedConnection conn = model.modelData.get(modelIndex);

                    JTextField ename = new JTextField(conn.retrieveName());
                    JTextField eserver = new JTextField(conn.retrieveServer());
                    JTextField eport = new JTextField(Integer.toString(conn.retrievePort()));
                    JTextField epassword = new JTextField(conn.retrievePassword());
                    String c = conn.retrieveChannels().toString();
                    JTextField echannels = new JTextField(c.substring(1, c.length() - 1));
                    JCheckBox eautoconnect = new JCheckBox();
                    if (conn.retrieveAutoConnect()) eautoconnect.setSelected(true);
                    else eautoconnect.setSelected(false);

                    Object[] efields = {
                            "Name", ename,
                            "Server", eserver,
                            "Port", eport,
                            "Password", epassword,
                            "Auto Join Channels (#c1 #c2)", echannels,
                            "Connect on startup", eautoconnect,
                    };
                    int ex = JOptionPane.showConfirmDialog(dialog, efields, "Edit Server", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
                    if (ex == JOptionPane.CLOSED_OPTION || ex == JOptionPane.CANCEL_OPTION) return;

                    String[] es = echannels.getText().replace(",", "").split(" ");
                    ArrayList<String> echannelList = new ArrayList<>(Arrays.asList(es));
                    if (!StringUtils.isNumeric(eport.getText())) {
                        JOptionPane.showMessageDialog(dialog, "Port must be an integer.");
                        return;
                    }
                    model.removeRowRange(modelIndex, modelIndex);
                    savedConnections.remove(conn);
                    SavedConnection connection = new SavedConnection(ename.getText(), eserver.getText(), epassword.getText(), echannelList, eautoconnect.isSelected(), Integer.valueOf(eport.getText().trim()));
                    model.addRow(connection);
                    savedConnections.add(connection);
                    serializeSavedConnections();
                }
            });

            remove.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    int rowIndex = table.getSelectedRow();
                    if (rowIndex == -1) return;
                    int modelIndex = table.convertRowIndexToModel(rowIndex);
                    SavedConnection conn = model.modelData.get(modelIndex);
                    savedConnections.remove(conn);
                    serializeSavedConnections();
                    model.removeRowRange(modelIndex, modelIndex);
                }
            });

            connect.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    int rowIndex = table.getSelectedRow();
                    if (rowIndex == -1) return;
                    SavedConnection conn = model.modelData.get(table.convertRowIndexToModel(rowIndex));

                    String network = conn.getNetwork(), server = conn.getServer();
                    int port = conn.retrievePort();
                    for (int i = 0; i < tabbedPane.getTabCount(); i++) {
                        ChannelPanel channel = (ChannelPanel) tabbedPane.getComponentAt(i);
                        if (network.equals(channel.title)) {
                            tabbedPane.setSelectedComponent(channel);
                            String[] msg = {null, "You are already connected to " + network + "."};
                            try {
                                channel.insertString(msg, errorStyle, false);
                            } catch (BadLocationException | IOException ex) {
                                Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
                            }
                            dialog.dispose();
                            return;
                        }
                    }
                    new Connection(network, server, port);
                    dialog.dispose();
                }
            });
            dialog.pack();
            dialog.setLocationRelativeTo(frame);
            dialog.setVisible(true);
        }
    }



    /** Toggles visibility of the user list of a given channel */
    static class ShowNickListAction extends AbstractAction {
        private static ShowNickListAction ref = null;

        private ShowNickListAction(String text, ImageIcon icon, String desc, Integer mnemonic) {
            super(text, icon);
            putValue(SHORT_DESCRIPTION, desc);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        public static ShowNickListAction getInstance() {
            if (ref == null) ref = new ShowNickListAction("Show/Hide Nicklist", showNicklistIcon, null, KeyEvent.VK_N);
            return ref;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            for (int i = 0; i < tabbedPane.getTabCount(); i++) {
                ChannelPanel channel = (ChannelPanel) tabbedPane.getComponentAt(i);
                if (channel.title.equals(channel.name)) {
                    if (channel.getRightComponent() != null) {
                        channel.setRightComponent(null);
                        channel.setDividerSize(0);
                        channel.setDividerLocation(0);
                    } else {
                        channel.setRightComponent(channel.userListScrollPane);
                        channel.setDividerSize(5);
                        channel.setDividerLocation(GUI.frame.getWidth() - 160);
                    }
                }
            }
        }
    }
    static class ColorHashMap<K,V> extends HashMap<K,V> {
        protected V defaultValue;
        public ColorHashMap(V defaultValue) {
            this.defaultValue = defaultValue;
        }
        @Override
        public V get(Object k) {
            return containsKey(k) ? super.get(k) : defaultValue;
        }