com.mattc.argus2.gui.InstallerGUI.java Source code

Java tutorial

Introduction

Here is the source code for com.mattc.argus2.gui.InstallerGUI.java

Source

/*
 * Argus Installer v2 -- A Better School Zip Alternative Copyright (C) 2014 Matthew
 * Crocco
 * 
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this
 * program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.mattc.argus2.gui;

import static com.mattc.argus2.Strings.Names.CONFIG_ARCHIVES;
import static com.mattc.argus2.Strings.Names.LANG_BORDER_ARCHIVES;
import static com.mattc.argus2.Strings.Names.LANG_BORDER_DOWNLOADS;
import static com.mattc.argus2.Strings.Names.LANG_BORDER_STATUS;
import static com.mattc.argus2.Strings.Names.LANG_BUTTON_ADD;
import static com.mattc.argus2.Strings.Names.LANG_BUTTON_CLEAR;
import static com.mattc.argus2.Strings.Names.LANG_BUTTON_EXIT;
import static com.mattc.argus2.Strings.Names.LANG_BUTTON_INSTALL;
import static com.mattc.argus2.Strings.Names.LANG_BUTTON_REMOVE;
import static com.mattc.argus2.Strings.Names.LANG_BUTTON_SELECT;
import static com.mattc.argus2.Strings.Names.LANG_BUTTON_START;
import static com.mattc.argus2.Strings.Names.LANG_COL_ARCHIVES_FORM;
import static com.mattc.argus2.Strings.Names.LANG_COL_ARCHIVES_NAME;
import static com.mattc.argus2.Strings.Names.LANG_COL_ARCHIVES_SIZE;
import static com.mattc.argus2.Strings.Names.LANG_COL_DOWNLOADS_NAME;
import static com.mattc.argus2.Strings.Names.LANG_COL_DOWNLOADS_URLL;
import static com.mattc.argus2.Strings.Names.LANG_FAILURE;
import static com.mattc.argus2.Strings.Names.LANG_MENU_FILE;
import static com.mattc.argus2.Strings.Names.LANG_MENU_ITEM_DOWNLOADS;
import static com.mattc.argus2.Strings.Names.LANG_MENU_ITEM_EXIT;
import static com.mattc.argus2.Strings.Names.LANG_MENU_ITEM_INSTALL_DIR;
import static com.mattc.argus2.Strings.Names.LANG_MENU_ITEM_LANGUAGE;
import static com.mattc.argus2.Strings.Names.LANG_MENU_ITEM_MUTABLE;
import static com.mattc.argus2.Strings.Names.LANG_MENU_ITEM_OPTIMIZE;
import static com.mattc.argus2.Strings.Names.LANG_MENU_ITEM_RESTORE_DEF;
import static com.mattc.argus2.Strings.Names.LANG_MENU_ITEM_THREAD_USE;
import static com.mattc.argus2.Strings.Names.LANG_MENU_SETTINGS;
import static com.mattc.argus2.Strings.Names.LANG_MESSAGE_LANGFAIL;
import static com.mattc.argus2.Strings.Names.LANG_MESSAGE_LANGSUCCESS;
import static com.mattc.argus2.Strings.Names.LANG_NOTIFY_OPTIMIZE;
import static com.mattc.argus2.Strings.Names.LANG_NOTIFY_STATADD;
import static com.mattc.argus2.Strings.Names.LANG_NOTIFY_STATCLEAR;
import static com.mattc.argus2.Strings.Names.LANG_NOTIFY_STATREADY;
import static com.mattc.argus2.Strings.Names.LANG_NOTIFY_STATREMOV;
import static com.mattc.argus2.Strings.Names.LANG_PROG_LABEL;
import static com.mattc.argus2.Strings.Names.LANG_PROMPT_INSTALL;
import static com.mattc.argus2.Strings.Names.LANG_PROMPT_SELECT;
import static com.mattc.argus2.Strings.Names.LANG_PROPMT_LANGUAGE;
import static com.mattc.argus2.Strings.Names.LANG_SUCCESS;
import static com.mattc.argus2.Strings.Names.LANG_TITLE_INSTALL;
import static com.mattc.argus2.Strings.Names.LANG_TITLE_LANGUAGE;
import static com.mattc.argus2.Strings.Names.LANG_TITLE_NUMBER;
import static com.mattc.argus2.Strings.Names.LANG_TITLE_SELECT;
import static com.mattc.argus2.Strings.Names.LANG_TITLE_STATE;
import static com.mattc.argus2.Strings.Names.LANG_TITLE_TEXT;
import static com.mattc.argus2.Strings.Names.LANG_TOOLTIP_THREAD_USE;
import static com.mattc.argus2.util.OS.MemoryUnit.BYTES;
import static com.mattc.argus2.util.OS.MemoryUnit.KILOBYTES;
import static com.mattc.argus2.util.OS.MemoryUnit.MEGABYTES;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Font;
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.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.Locale;
import java.util.Set;
import java.util.TimerTask;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTable;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;
import javax.swing.WindowConstants;
import javax.swing.border.BevelBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.text.DefaultCaret;

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.DateTimeFormatterBuilder;

import com.google.common.base.Function;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import com.mattc.argus2.Argus;
import com.mattc.argus2.Language;
import com.mattc.argus2.Language.LangInfo;
import com.mattc.argus2.Ref;
import com.mattc.argus2.Settings;
import com.mattc.argus2.Settings.Numbers;
import com.mattc.argus2.Settings.State;
import com.mattc.argus2.Settings.Text;
import com.mattc.argus2.concurrent.DecompressProcess;
import com.mattc.argus2.io.ArchiveFile;
import com.mattc.argus2.io.Configs;
import com.mattc.argus2.io.DownloadManager;
import com.mattc.argus2.io.DownloadManager.DownloadInfo;
import com.mattc.argus2.io.ListFile;
import com.mattc.argus2.misc.Disposable;
import com.mattc.argus2.misc.LineArray;
import com.mattc.argus2.util.Console;
import com.mattc.argus2.util.IOUtils;
import com.mattc.argus2.util.OS.MemoryUnit;

public class InstallerGUI extends JFrame implements Disposable {

    private static final long serialVersionUID = -1770859866496285087L;
    private static final DateTimeFormatter dtFormat = new DateTimeFormatterBuilder().appendHourOfHalfday(2)
            .appendLiteral(':').appendMinuteOfHour(2).appendLiteral(':').appendSecondOfMinute(2).appendLiteral(' ')
            .appendHalfdayOfDayText().toFormatter();
    private static InstallerGUI __instance = null;

    // Files that were not default and were copied into the directory that can be
    // deleted safely.
    private static final Set<File> deletableFiles = Sets.newConcurrentHashSet();

    private final LineArray statusLog = new LineArray();
    private PopUpManager popups;
    private WatchService wservice;
    private WatchKey wkey;

    public static final InstallerGUI getInstance() {
        if (__instance == null) {
            synchronized (InstallerGUI.class) {
                Console.debug("Creating instance of Installer GUI...");
                __instance = new InstallerGUI();
            }
        }

        return __instance;
    }

    private InstallerGUI() {
        super(Ref.getFullTitle());

        EventQueue.invokeLater(new Runnable() {
            InstallerGUI self = InstallerGUI.this;

            @Override
            public void run() {
                Thread.currentThread().setName("GUI");
                // Do nothing, we will make our own.
                this.self.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
                this.self.setLayout(new BoxLayout(this.self.getContentPane(), BoxLayout.Y_AXIS));

                this.self.init();
                this.self.initMenuBar();

                this.self.setSize(1200, 600);
                this.self.setLocationRelativeTo(null);

                // Our Own Window Close Action
                this.self.addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosing(WindowEvent evt) {
                        Argus.shutdown();
                    }
                });

                this.self.setVisible(true);
            }
        });
    }

    /**
     * Initialize Components
     */
    private void init() {
        final Container cont = getContentPane();

        final String urlColumn = toLocal(LANG_COL_DOWNLOADS_URLL);

        final JPanel buttonPanel = new JPanel(); // Panel for Start/Exit Buttons
        final JPanel subPanel = new JPanel(); // Panel Containing Main Content
        // Panel Inside Sub-Panel containing Archives/Replacements
        final JPanel innerPanelOne = new JPanel();
        // Panel inside Sub-Panel containing the Status Log
        final JPanel innerPanelTwo = new JPanel();
        subPanel.setLayout(new BoxLayout(subPanel, BoxLayout.X_AXIS));
        innerPanelOne.setLayout(new BoxLayout(innerPanelOne, BoxLayout.Y_AXIS));
        innerPanelTwo.setLayout(new BorderLayout(5, 5));

        // Add Inner Panels to Sub Panels with Spacing
        subPanel.add(Box.createHorizontalStrut(5)); // 5 pixel separator
        subPanel.add(innerPanelOne);
        subPanel.add(Box.createHorizontalStrut(5));
        subPanel.add(innerPanelTwo);
        subPanel.add(Box.createHorizontalStrut(5));

        final JProgressBar progressBar = new JProgressBar(0, 100);
        final JLabel curr = new JLabel(String.format(toLocal(LANG_PROG_LABEL), 0));
        final JTextPane log = new JTextPane();
        final AutoUpdateTablePane archiveScroll = AutoUpdateTablePane.create(new String[] {
                toLocal(LANG_COL_ARCHIVES_NAME), toLocal(LANG_COL_ARCHIVES_SIZE), toLocal(LANG_COL_ARCHIVES_FORM) },
                assembleArchiveData(3), archiveUpdater);// new
        // JScrollPane(archiveTable);
        final AutoUpdateTablePane downloadScroll = AutoUpdateTablePane.create(
                new String[] { toLocal(LANG_COL_DOWNLOADS_NAME), urlColumn }, assembleDownloadData(2), null);
        final JScrollPane scrollLog = new JScrollPane(log);
        final JPanel archives = new BorderedPanel(toLocal(LANG_BORDER_ARCHIVES), new BorderLayout(10, 15));
        final JPanel downloads = new BorderedPanel(toLocal(LANG_BORDER_DOWNLOADS), new BorderLayout(10, 15));
        final JPanel status = new BorderedPanel(toLocal(LANG_BORDER_STATUS), new BorderLayout(10, 20));
        final JPanel progress = new BorderedPanel(BorderedPanel.BorderType.ETCHED);

        downloadScroll.getTable().getColumn(urlColumn).setMinWidth(500);
        downloadScroll.getTable().getColumn(urlColumn).setCellRenderer(new TooltipRenderer());
        downloadScroll.getTable().getTableHeader().setResizingAllowed(false);

        // Construct Progress Bar Panel with Status Label and Progress Bar
        progress.setLayout(new BoxLayout(progress, BoxLayout.Y_AXIS));
        progress.add(Box.createVerticalStrut(20));
        progress.add(curr);
        progress.add(progressBar);
        progress.add(Box.createVerticalStrut(20));
        progressBar.setBackground(Color.DARK_GRAY);
        progressBar.setForeground(Color.GREEN);
        // --

        // Set up Status Log Panel
        log.setForeground(Color.WHITE);
        log.setBackground(Color.BLACK);
        log.setCaretColor(log.getBackground());
        log.setEditable(false);
        log.setFont(new Font(Font.DIALOG, Font.BOLD, 12));
        final DefaultCaret caret = (DefaultCaret) log.getCaret();
        caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
        status.add(scrollLog);
        scrollLog.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
        // --

        innerPanelOne.add(archives);
        archives.add(archiveScroll);
        innerPanelOne.add(downloads);
        downloads.add(downloadScroll);
        innerPanelTwo.add(status, BorderLayout.CENTER);
        // --

        // Populate Button Panel
        final JButton startButton = new SpacedButton(toLocal(LANG_BUTTON_START), 32);
        final JButton exitButton = new SpacedButton(toLocal(LANG_BUTTON_EXIT), 32);
        final JButton clearButton = new SpacedButton(toLocal(LANG_BUTTON_CLEAR), 5);

        startButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                final JFileChooser chooser = new DirectoryChooser(
                        new File(Settings.Text.DEFAULT_INSTALL_DESTINATION));

                int val = chooser.showDialog(InstallerGUI.this, toLocal(LANG_BUTTON_INSTALL));

                if (val == JFileChooser.APPROVE_OPTION) {
                    final String path = chooser.getSelectedFile().getAbsolutePath();
                    final String question = String.format(toLocal(LANG_PROMPT_INSTALL), path);
                    val = JOptionPane.showConfirmDialog(InstallerGUI.this, question, toLocal(LANG_TITLE_INSTALL),
                            JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
                    if (val == JOptionPane.YES_OPTION) {
                        Argus.begin(chooser.getSelectedFile());
                    }
                }
            }
        });

        exitButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Argus.shutdown();
            }
        });

        clearButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                InstallerGUI.this.statusLog.clearAll();
                writeLine(toLocal(LANG_NOTIFY_STATCLEAR));
            }
        });

        buttonPanel.add(startButton);
        buttonPanel.add(Box.createHorizontalStrut(60));
        buttonPanel.add(exitButton);
        // --

        innerPanelTwo.add(clearButton, BorderLayout.SOUTH);
        addArchiveButtons(archives, archiveScroll.getTable());

        // Populate Content Pane
        cont.add(Box.createVerticalStrut(5));
        cont.add(subPanel);
        cont.add(Box.createVerticalStrut(5));
        cont.add(progress);
        cont.add(buttonPanel);
        cont.add(Box.createVerticalStrut(5));
        // --

        // Update Log ONLY When Necessary!
        this.statusLog.addListener(new LineArray.LineArrayListener() {
            @Override
            public void lineAdded(String line) {
                if (!InstallerGUI.this.statusLog.isEqualTo(log.getText())) {
                    log.setText(InstallerGUI.this.statusLog.getText());
                }
            }
        });

        /*
         * Attempt to Register WatchKey for Archives directory.
         * 
         * Listens for Delete Actions and updates Deletable Files and the Archive
         * List as necessary.
         */
        try {
            this.wservice = FileSystems.getDefault().newWatchService();
            this.wkey = Paths.get(Ref.getArchivesDir().toURI()).register(this.wservice, ENTRY_DELETE);
            Argus.timer().scheduleAtFixedRate(new TimerTask() {

                @Override
                public void run() {
                    for (final WatchEvent<?> evt : InstallerGUI.this.wkey.pollEvents()) {
                        if (evt.kind() == OVERFLOW) {
                            continue;
                        } else if (evt.kind() == ENTRY_DELETE) {
                            @SuppressWarnings("unchecked")
                            final WatchEvent<Path> wPath = (WatchEvent<Path>) evt;
                            final File f = wPath.context().toFile();

                            // Handle Deletable Files
                            for (final File dFile : deletableFiles) {
                                if (f.getName().equals(dFile.getName())) {
                                    deletableFiles.remove(dFile);
                                    break;
                                }
                            }

                            // Even if not deletable, we must remove it from the
                            // list!
                            final ListFile archiveListFile = Configs.getFile(CONFIG_ARCHIVES);
                            if (archiveListFile.getItem(f.getName()) != null) {
                                archiveListFile.removeItem(f.getName());
                                Argus.postEvent(archiveScroll.getTable());
                            }
                        }
                    }
                }
            }, 200, 200);

            Console.info("Dynamic Archive Directory Watch Enabled!");
        } catch (final IOException e) {
            Console.exception(e, "No Watch Service on the Archives Directory!");
        }

        initPopups();

        // Test Line
        writeLine(toLocal(LANG_NOTIFY_STATREADY));
    }

    private void addArchiveButtons(JPanel panel, final JTable list) {
        final JPanel bPanel = new JPanel(new FlowLayout());
        final JButton add = new SpacedButton(toLocal(LANG_BUTTON_ADD), 10);
        final JButton remove = new SpacedButton(toLocal(LANG_BUTTON_REMOVE), 7);

        bPanel.add(add);
        bPanel.add(Box.createHorizontalStrut(30));
        bPanel.add(remove);

        remove.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (list.getSelectedRow() != -1) {
                    final ListFile archiveList = Configs.getFile(CONFIG_ARCHIVES);

                    for (final int i : list.getSelectedRows()) {
                        final String name = (String) list.getValueAt(i, 0);
                        final File f = new File(Ref.getArchivesDir(), name);
                        archiveList.removeItem(name);
                        Console.info("Removed " + name + " from list...");
                        writeLine(String.format(toLocal(LANG_NOTIFY_STATREMOV), name));

                        for (final File dFile : deletableFiles) {
                            if (!f.getAbsolutePath().equals(dFile.getAbsolutePath())) {
                                continue;
                            }

                            deletableFiles.remove(dFile);
                            if (!f.delete()) {
                                f.deleteOnExit();
                            }
                        }
                    }

                    Argus.postEvent(list);
                    // populateList(list);
                }
            }
        });

        add.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                final JFileChooser chooser = new ArchiveChooser();
                final int val = chooser.showOpenDialog(InstallerGUI.this);

                try {
                    if ((val == JFileChooser.APPROVE_OPTION) && (chooser.getSelectedFile() != null)) {

                        final ListFile archiveList = Configs.getFile(CONFIG_ARCHIVES);
                        for (final File selected : chooser.getSelectedFiles()) {
                            File f = selected;

                            if (archiveList.getItem(f.getName()) != null) {
                                continue;
                            }

                            if (!new File(Ref.getArchivesDir(), f.getName()).exists()) {
                                f = IOUtils.copyTo(selected, Ref.getArchivesDir());
                                deletableFiles.add(f);
                            }

                            archiveList.addItem(f.getName());
                            Console.info("Added " + selected.getAbsolutePath() + " as " + f.getName() + "...");
                            writeLine(String.format(toLocal(LANG_NOTIFY_STATADD), f.getName()));
                        }

                        Argus.postEvent(list);
                    } else {
                        Console.info("No File Selected...");
                    }
                } catch (final IOException ex) {
                    Console.exception(ex);
                }
            }
        });

        panel.add(bPanel, BorderLayout.SOUTH);
    }

    private void initMenuBar() {
        final JMenuBar bar = new JMenuBar();
        final JMenu fileMenu = new JMenu(toLocal(LANG_MENU_FILE));
        final JMenu setMenu = new JMenu(toLocal(LANG_MENU_SETTINGS));
        final JMenuItem exitItem = new JMenuItem(toLocal(LANG_MENU_ITEM_EXIT), KeyEvent.VK_E);
        final JCheckBoxMenuItem mutableItem = new JCheckBoxMenuItem(toLocal(LANG_MENU_ITEM_MUTABLE),
                Settings.State.MUTABLE);
        final JCheckBoxMenuItem downloadItem = new JCheckBoxMenuItem(toLocal(LANG_MENU_ITEM_DOWNLOADS),
                Settings.State.DOWNLOADS);
        final JMenuItem langItem = new JMenuItem(toLocal(LANG_MENU_ITEM_LANGUAGE));
        final BorderedSpinnerMenuItem threadsItem = new BorderedSpinnerMenuItem(toLocal(LANG_MENU_ITEM_THREAD_USE),
                1, 8, Settings.Numbers.MAX_THREAD_COUNT);
        final JMenuItem optiItem = new JMenuItem(toLocal(LANG_MENU_ITEM_OPTIMIZE), KeyEvent.VK_O);
        final JMenuItem instItem = new JMenuItem(toLocal(LANG_MENU_ITEM_INSTALL_DIR), KeyEvent.VK_I);
        final JMenuItem defItem = new JMenuItem(toLocal(LANG_MENU_ITEM_RESTORE_DEF), KeyEvent.VK_D);

        threadsItem.setToolTipText(toLocal(LANG_TOOLTIP_THREAD_USE));
        exitItem.addActionListener(GuiUtils.EXIT_ARGUS_ACTION);
        exitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, ActionEvent.CTRL_MASK));

        mutableItem.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                final JCheckBoxMenuItem comp = (JCheckBoxMenuItem) e.getSource();

                Settings.State.MUTABLE = comp.isSelected();
            }
        });
        mutableItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK));
        mutableItem.setMnemonic(KeyEvent.VK_1);

        downloadItem.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                final JCheckBoxMenuItem comp = (JCheckBoxMenuItem) e.getSource();

                Settings.State.DOWNLOADS = comp.isSelected();
            }
        });
        downloadItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_2, ActionEvent.ALT_MASK));
        downloadItem.setMnemonic(KeyEvent.VK_2);

        threadsItem.addChangeListener(new ChangeListener() {

            @Override
            public void stateChanged(ChangeEvent e) {
                final JSpinner slider = (JSpinner) e.getSource();

                Numbers.MAX_THREAD_COUNT = (Integer) slider.getValue();
                Argus.updateExecutors();
            }
        });
        threadsItem.getComponent().setBackground(getBackground());

        optiItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                final int proc = Runtime.getRuntime().availableProcessors();

                if (proc != Numbers.MAX_THREAD_COUNT) {
                    Numbers.MAX_THREAD_COUNT = proc;
                    threadsItem.setValue(proc);
                    // The Spinner ChangeListener will handle Executor Updates

                    InstallerGUI.this.writeLine(toLocal(LANG_NOTIFY_OPTIMIZE));
                }
            }
        });
        optiItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK));

        defItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Settings.toDefaults();

                // Update Appropriate Components
                mutableItem.setSelected(State.MUTABLE);
                downloadItem.setSelected(State.DOWNLOADS);
                threadsItem.setValue(Numbers.MAX_THREAD_COUNT);
            }
        });

        instItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                final JFileChooser chooser = new DirectoryChooser();

                int val = chooser.showDialog(InstallerGUI.this, toLocal(LANG_BUTTON_SELECT));

                if (val == JFileChooser.APPROVE_OPTION) {
                    final String path = chooser.getSelectedFile().getAbsolutePath();
                    final String question = String.format(toLocal(LANG_PROMPT_SELECT), path,
                            Text.DEFAULT_INSTALL_DESTINATION);
                    val = JOptionPane.showConfirmDialog(InstallerGUI.this, question, toLocal(LANG_TITLE_SELECT),
                            JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
                    if (val == JOptionPane.YES_OPTION) {
                        Settings.Text.DEFAULT_INSTALL_DESTINATION = chooser.getSelectedFile().getPath();
                    }
                }
            }
        });
        instItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_3, ActionEvent.ALT_MASK));

        langItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                final Font font = new Font(Font.MONOSPACED, Font.ITALIC, 20);

                final JOptionPane pane = new JOptionPane(toLocal(LANG_PROPMT_LANGUAGE),
                        JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
                pane.setWantsInput(true);
                pane.setSelectionValues(Language.getAllUniqueLocales());
                pane.setInitialSelectionValue(new LangInfo(new Locale(Settings.Text.LANGUAGE.getLanguage())));
                pane.setFont(font);

                final JDialog dialog = pane.createDialog(Argus.gui(), toLocal(LANG_TITLE_LANGUAGE));
                pane.selectInitialValue();
                dialog.setVisible(true);
                dialog.dispose();

                final Object tmp = pane.getInputValue();
                if ((tmp == null) || (tmp == JOptionPane.UNINITIALIZED_VALUE))
                    return;

                final LangInfo info = (LangInfo) tmp;
                final Locale loc = info.locale;
                if (loc != Settings.Text.LANGUAGE) {
                    if (!Settings.Text.setLanguage(loc)) {
                        JOptionPane.showMessageDialog(Argus.gui(),
                                String.format(toLocal(LANG_MESSAGE_LANGFAIL), loc.getDisplayLanguage()),
                                toLocal(LANG_FAILURE), JOptionPane.ERROR_MESSAGE);
                    } else {
                        JOptionPane.showMessageDialog(Argus.gui(),
                                String.format(toLocal(LANG_MESSAGE_LANGSUCCESS), loc.getDisplayLanguage()),
                                toLocal(LANG_SUCCESS), JOptionPane.INFORMATION_MESSAGE);
                    }
                }

            }
        });

        fileMenu.add(exitItem);
        setMenu.add(new TitledSeparator(toLocal(LANG_TITLE_STATE), Color.GRAY));
        setMenu.add(mutableItem);
        setMenu.add(downloadItem);
        setMenu.add(new TitledSeparator(toLocal(LANG_TITLE_NUMBER), Color.GRAY));
        setMenu.add(threadsItem);
        setMenu.add(optiItem);
        setMenu.add(new TitledSeparator(toLocal(LANG_TITLE_TEXT), Color.GRAY));
        setMenu.add(instItem);
        setMenu.add(langItem);
        setMenu.add(new Separator(Color.GRAY));
        setMenu.add(defItem);
        setMenu.add(new Separator(Color.GRAY));

        bar.add(fileMenu);
        bar.add(setMenu);
        setJMenuBar(bar);
    }

    private void initPopups() {
        this.popups = new PopUpManager(this);

        this.popups.allowListenersFor(JTable.class);
        this.popups.addListenersFor(this);
    }

    private String toLocal(String langKey) {
        return Language.getLocalized(langKey);
    }

    // Internal Use, Simply uses CODE as the Identifier
    public void writeLine(String msg) {
        final DateTime date = new DateTime();

        final String time = date.toString(dtFormat);
        final String text = String.format("%s - [%-5s]: %s%n", time, "CODE", msg);

        this.statusLog.addLine(text);
    }

    /**
     * Add's a line to the Status Log.
     * 
     * @param msg
     *            - Actual Message
     * @param archiveType
     *            - Archive Type Identifier like ZIP or TAR or BZIP
     */
    public void writeLine(String msg, DecompressProcess process) {
        final DateTime date = new DateTime();

        final String time = date.toString(dtFormat);
        final String text = String.format("%s - [%-5s]: %s%n", time,
                Files.getFileExtension(process.getArchiveName()).toUpperCase(), msg);

        this.statusLog.addLine(text);
    }

    @Override
    public void dispose() {
        Console.debug("Disposing of Installer GUI...");
        this.wkey.cancel();
        IOUtils.closeSilently(this.wservice);
        super.dispose();
        __instance = null;
    }

    public PopUpManager getPopups() {
        return this.popups;
    }

    public void addPopups(JPopupMenu menu, Component comp) {
        this.popups.register(menu, comp);
    }

    public void addPopupsForAll(JPopupMenu menu, Class<? extends Component> componentClass) {
        this.popups.register(menu, componentClass);
    }

    private static final Object[][] assembleDownloadData(int columnCount) {
        final DownloadInfo[] downloads = DownloadManager.getDownloadList();
        final Object[][] data = new Object[downloads.length][columnCount];

        for (int i = 0; i < downloads.length; i++) {
            final DownloadInfo info = downloads[i];
            data[i][0] = info.name;
            data[i][1] = info.url;
        }

        return data;
    }

    private static final Object[][] assembleArchiveData(int columnCount) {
        final ArchiveFile[] files = ArchiveFile.getCurrentArchives();
        final Object[][] data = new Object[files.length][columnCount];

        for (int i = 0; i < files.length; i++) {
            final ArchiveFile file = files[i];
            final MemoryUnit tMem = file.length() < 1_000L ? BYTES
                    : file.length() < 1_000_000 ? KILOBYTES : MEGABYTES;
            data[i][0] = file.getName();
            data[i][1] = String.format("%,5d %-2s", tMem.convert(file.length(), BYTES), tMem.getTag());
            data[i][2] = file.getFormatType();
        }

        return data;
    }

    private static final Function<DefaultTableModel, Void> archiveUpdater = new Function<DefaultTableModel, Void>() {
        @Override
        public Void apply(DefaultTableModel input) {
            final String[] columns = new String[input.getColumnCount()];
            final ArchiveFile[] files = ArchiveFile.getCurrentArchives();
            final Object[][] data = new Object[files.length][columns.length];

            for (int i = 0; i < columns.length; i++) {
                columns[i] = input.getColumnName(i);
            }

            for (int i = 0; i < files.length; i++) {
                final ArchiveFile file = files[i];
                final MemoryUnit tMem = file.length() < 1_000L ? BYTES
                        : file.length() < 1_000_000 ? KILOBYTES : MEGABYTES;
                data[i][0] = file.getName();
                data[i][1] = String.format("%,5d %-2s", tMem.convert(file.length(), BYTES), tMem.getTag());
                data[i][2] = file.getFormatType();
            }

            input.setDataVector(data, columns);

            return null;
        }
    };

    // TODO This probably wont work for Localization with EXCEPTIONALLY long names
    private static class SpacedButton extends JButton {

        private static final long serialVersionUID = -3902145219557616202L;

        public SpacedButton(String name, int offset) {
            super(buildName(name, (name.length() % 2) == 0 ? offset : offset - 1));
        }

        private static String buildName(String name, int offset) {
            final StringBuilder sb = new StringBuilder();
            int mut = -1;
            for (int i = offset; i < (offset + 1); i += mut) {
                sb.append(" ");

                if (i == 0) {
                    mut = 1;
                    sb.append(name);
                }
            }

            return sb.toString();
        }

    }
}