/*
 * FindBugs - Find bugs in Java programs
 * Copyright (C) 2003-2005, University of Maryland
 * Copyright (C) 2004 Dave Brosius <dbrosius@users.sourceforge.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA     02111-1307     USA
 */

/*
 * FindBugsFrame.java
 *
 * Created on March 30, 2003, 12:05 PM
 */

package edu.umd.cs.findbugs.gui;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.HeadlessException;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.net.URL;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;

import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.ListModel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import javax.swing.text.Position;
import javax.swing.text.View;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import edu.umd.cs.findbugs.cloud.SignInCancelledException;
import org.dom4j.DocumentException;

import edu.umd.cs.findbugs.BugAnnotation;
import edu.umd.cs.findbugs.BugAnnotationWithSourceLines;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugPattern;
import edu.umd.cs.findbugs.Detector;
import edu.umd.cs.findbugs.DetectorFactoryCollection;
import edu.umd.cs.findbugs.FindBugs;
import edu.umd.cs.findbugs.FindBugsCommandLine;
import edu.umd.cs.findbugs.I18N;
import edu.umd.cs.findbugs.L10N;
import edu.umd.cs.findbugs.Project;
import edu.umd.cs.findbugs.ShowHelp;
import edu.umd.cs.findbugs.SourceLineAnnotation;
import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.annotations.SuppressWarnings;
import edu.umd.cs.findbugs.ba.SourceFile;
import edu.umd.cs.findbugs.ba.SourceFinder;
import edu.umd.cs.findbugs.config.AnalysisFeatureSetting;
import edu.umd.cs.findbugs.config.ProjectFilterSettings;
import edu.umd.cs.findbugs.config.UserPreferences;
import edu.umd.cs.findbugs.config.CommandLine.HelpRequestedException;
import edu.umd.cs.findbugs.log.ConsoleLogger;
import edu.umd.cs.findbugs.log.LogSync;
import edu.umd.cs.findbugs.log.Logger;

/**
 * The main GUI frame for FindBugs.
 *
 * @author David Hovemeyer
 */
public final class  FindBugsFrame extends javax.swing.JFrame implements LogSync {
    /**
     *
     */
    private static final int fontSize = 12;
    /**
     *
     */
    private static final Font SOURCE_FONT = new java.awt.Font("Monospaced", 0, fontSize);
    private static final Font JTREE_FONT = new java.awt.Font("SansSerif", 0, fontSize);

    /**
     *
     */
    private static final Font LABEL_FONT = new java.awt.Font("Dialog", 1, 2*fontSize);

    /**
     *
     */
    private static final Font BUTTON_FONT = new java.awt.Font("Dialog", 0, fontSize);

    private static final long serialVersionUID = 1L;

    /* ----------------------------------------------------------------------
     * Helper classes
     * ---------------------------------------------------------------------- */
    static final Color HIGH_PRIORITY_COLOR = new Color(0xff0000);
    static final Color NORMAL_PRIORITY_COLOR = new Color(0x9f0000);
    static final Color LOW_PRIORITY_COLOR = Color.BLACK;
    static final Color EXP_PRIORITY_COLOR = Color.BLACK;

    /**
     * Tree node type for BugInstances.
     * We use this instead of plain DefaultMutableTreeNodes in order to
     * get more control over the exact text that is shown in the tree.
     */
    private class BugTreeNode extends DefaultMutableTreeNode {
        private static final long serialVersionUID = 1L;
        private int count;

        public BugTreeNode(BugInstance bugInstance) {
            super(bugInstance);
            count = -1;
        }

        public void setCount(int count) {
            this.count = count;
        }

        @Override
        public String toString() {
            try {
                BugInstance bugInstance = (BugInstance) getUserObject();
                StringBuilder result = new StringBuilder();

                if (count >= 0) {
                    result.append(count);
                    result.append(": ");
                }

                if (bugInstance.isExperimental())
                    result.append(L10N.getLocalString("msg.exp_txt", "EXP: "));

                result.append(fullDescriptionsItem.isSelected() ? bugInstance.getMessage() : bugInstance.toString());

                return result.toString();
            } catch (Exception e) {
                return MessageFormat.format(L10N.getLocalString("msg.errorformatting_txt", "Error formatting message for bug: "), new Object[]{e.toString()});
            }
        }
    }

    /**
     * Compare BugInstance class names.
     * This is useful for grouping bug instances by class.
     * Note that all instances with the same class name will compare
     * as equal.
     */
    private static class BugInstanceClassComparator implements Comparator<BugInstance> {
        public int compare(BugInstance lhs, BugInstance rhs) {
            return lhs.getPrimaryClass().compareTo(rhs.getPrimaryClass());
        }
    }

    /**
     * The instance of BugInstanceClassComparator.
     */
    private static final Comparator<BugInstance> bugInstanceClassComparator = new BugInstanceClassComparator();

    /**
     * Compare BugInstance package names.
     * This is useful for grouping bug instances by package.
     * Note that all instances with the same package name will compare
     * as equal.
     */
    private static class BugInstancePackageComparator implements Comparator<BugInstance>, Serializable {
        private static final long serialVersionUID = 1L;
        public int compare(BugInstance lhs, BugInstance rhs) {
            return lhs.getPrimaryClass().getPackageName().compareTo(rhs.getPrimaryClass().getPackageName());
        }
    }

    /**
     * The instance of BugInstancePackageComparator.
     */
    private static final Comparator<BugInstance> bugInstancePackageComparator = new BugInstancePackageComparator();

    /**
     * Compare BugInstance bug types.
     * This is useful for grouping bug instances by bug type.
     * Note that all instances with the same bug type will compare
     * as equal.
     */
    private static class BugInstanceTypeComparator implements Comparator<BugInstance>, Serializable {
        private static final long serialVersionUID = 1L;
        public int compare(BugInstance lhs, BugInstance rhs) {
            String lhsString = lhs.toString();
            String rhsString = rhs.toString();
            return lhsString.substring(0, lhsString.indexOf(':')).compareTo(rhsString.substring(0, rhsString.indexOf(':')));
        }
    }

    /**
     * The instance of BugInstanceTypeComparator.
     */
    private static final Comparator<BugInstance> bugInstanceTypeComparator = new BugInstanceTypeComparator();

    /**
     * Compare BugInstance bug categories.
     * This is useful for grouping bug instances by bug category.
     * Note that all instances with the same bug category will compare
     * as equal.
     */
    private static class BugInstanceCategoryComparator implements Comparator<BugInstance>, Serializable {
        private static final long serialVersionUID = 1L;

        public int compare(BugInstance lhs, BugInstance rhs) {
            return getCategory(lhs).compareTo(getCategory(rhs));
        }

        private String getCategory(BugInstance warning) {
            BugPattern bugPattern = warning.getBugPattern();
            if (bugPattern == null) {
                if (FindBugs.DEBUG)
                    System.out.println("Unknown bug pattern for bug type: " + warning.getType());
                return "";
            } else {
                return bugPattern.getCategory();
            }
        }
    }

    /**
     * The instance of BugInstanceCategoryComparator.
     */
    private static final Comparator<BugInstance> bugInstanceCategoryComparator = new BugInstanceCategoryComparator();

        /**
     * Two-level comparison of bug instances by class name and
     * BugInstance natural ordering.
     */
    private static class BugInstanceByClassComparator implements Comparator<BugInstance>, Serializable {
        private static final long serialVersionUID = 1L;
        public int compare(BugInstance a, BugInstance b) {
            int cmp = bugInstanceClassComparator.compare(a, b);
            if (cmp != 0)
                return cmp;
            return a.compareTo(b);
        }
    }

    /**
     * The instance of BugInstanceByClassComparator.
     */
    private static final Comparator<BugInstance> bugInstanceByClassComparator = new FindBugsFrame.BugInstanceByClassComparator();

    /**
     * Two-level comparison of bug instances by package and
     * BugInstance natural ordering.
     */
    private static class BugInstanceByPackageComparator implements Comparator<BugInstance> {
        public int compare(BugInstance a, BugInstance b) {
            int cmp = bugInstancePackageComparator.compare(a, b);
            if (cmp != 0)
                return cmp;
            return a.compareTo(b);
        }
    }

    /**
     * The instance of BugInstanceByPackageComparator.
     */
    private static final Comparator<BugInstance> bugInstanceByPackageComparator = new FindBugsFrame.BugInstanceByPackageComparator();

    /**
     * Two-level comparison of bug instances by bug type and
     * BugInstance natural ordering.
     */
    private static class BugInstanceByTypeComparator implements Comparator<BugInstance>, Serializable {
        private static final long serialVersionUID = 1L;
        public int compare(BugInstance a, BugInstance b) {
            int cmp = bugInstanceTypeComparator.compare(a, b);
            if (cmp != 0)
                return cmp;
            return a.compareTo(b);
        }
    }

        /**
     * The instance of BugTypeByTypeComparator.
     */
    private static final Comparator<BugInstance> bugInstanceByTypeComparator = new FindBugsFrame.BugInstanceByTypeComparator();

    /**
     * Two-level comparison of bug instances by bug category and
     * BugInstance natural ordering.
     */
    private static class BugInstanceByCategoryComparator implements Comparator<BugInstance>, Serializable {
        private static final long serialVersionUID = 1L;
        public int compare(BugInstance a, BugInstance b) {
            int cmp = bugInstanceCategoryComparator.compare(a, b);
            if (cmp != 0)
                return cmp;
            return a.compareTo(b);
        }
    }

         /**
     * The instance of BugTypeByCategoryComparator.
     */
    private static final Comparator<BugInstance> bugInstanceByCategoryComparator = new FindBugsFrame.BugInstanceByCategoryComparator();

        /**
     * Swing FileFilter class for file selection dialogs for FindBugs project files.
     */
    private static class ProjectFileFilter extends FileFilter {
        @Override
        public boolean accept(File file) {
            return file.isDirectory() || file.getName().endsWith(".fb");
        }

        @Override
        public String getDescription() {
            return L10N.getLocalString("dlg.findbugsprojects_lbl", "FindBugs projects (*.fb)");
        }
    }

        /**
     * The instance of ProjectFileFilter.
     */
    private static final FileFilter projectFileFilter = new ProjectFileFilter();

    /**
     * Swing FileFilter for choosing an auxiliary classpath entry.
     * Both Jar files and directories can be chosen.
     */
    private static class AuxClasspathEntryFileFilter extends FileFilter {
        @Override
        public boolean accept(File file) {
            return file.isDirectory() || file.getName().endsWith(".jar");
        }

        @Override
        public String getDescription() {
            return L10N.getLocalString("dlg.jarsanddirectories_lbl", "Jar files and directories");
        }
    }

    /**
     * The instance of AuxClasspathEntryFileFilter.
     */
    private static final FileFilter auxClasspathEntryFileFilter = new AuxClasspathEntryFileFilter();

    /**
     * Swing FileFilter for choosing XML saved bug files.
     */
    private static class XMLFileFilter extends FileFilter {
        @Override
        public boolean accept(File file) {
            return file.isDirectory() || file.getName().endsWith(".xml");
        }

        @Override
        public String getDescription() {
            return L10N.getLocalString("dlg.xmlsavedbugs_lbl", "XML saved bug files");
        }
    }

    /**
     * The instance of XMLFileFilter.
     */
    private static final FileFilter xmlFileFilter = new XMLFileFilter();

    /**
     * Set of archive file extensions.
     */
    private static final HashSet<String> archiveExtensionSet = new HashSet<String>();

    static {
        archiveExtensionSet.add(".jar");
        archiveExtensionSet.add(".zip");
        archiveExtensionSet.add(".ear");
        archiveExtensionSet.add(".war");
        archiveExtensionSet.add(".sar");
    }

    /**
     * File filter for choosing archives and directories.
     */
    private static class ArchiveAndDirectoryFilter extends FileFilter {
        @Override
        public boolean accept(File file) {
            if (file.isDirectory())
                return true;

            String fileName = file.getName();
            int dot = fileName.lastIndexOf('.');
            if (dot < 0)
                return false;
            String extension = fileName.substring(dot);
            return archiveExtensionSet.contains(extension);
        }

        @Override
        public String getDescription() {
            return L10N.getLocalString("dlg.javaarchives_lbl", "Java archives (*.jar,*.zip,*.ear,*.war,*.sar)");
        }
    }

    /**
     * The instance of ArchiveAndDirectoryFilter.
     */
    private static final FileFilter archiveAndDirectoryFilter = new ArchiveAndDirectoryFilter();

    /* ----------------------------------------------------------------------
     * Constants
     * ---------------------------------------------------------------------- */

    static final String GROUP_BY_CLASS = "By class";
    static final String GROUP_BY_PACKAGE = "By package";
    static final String GROUP_BY_BUG_TYPE = "By bug type";
        static final String GROUP_BY_BUG_CATEGORY="By bug category";
    private static final String[] GROUP_BY_ORDER_LIST = {
        GROUP_BY_CLASS, GROUP_BY_PACKAGE, GROUP_BY_BUG_TYPE, GROUP_BY_BUG_CATEGORY
    };

    /**
     * A fudge value required in our hack to get the REAL maximum
     * divider location for a JSplitPane.  Experience suggests that
     * the value "1" would work here, but setting it a little higher
     * makes the code a bit more robust.
     */
    private static final int DIVIDER_FUDGE = 3;

    private static final boolean BUG_COUNT = SystemProperties.getBoolean("findbugs.gui.bugCount");

    /* ----------------------------------------------------------------------
     * Member fields
     * ---------------------------------------------------------------------- */
    Component selectedComponent = null;

    /* ----------------------------------------------------------------------
     * Constructor
     * ---------------------------------------------------------------------- */

    /**
     * Creates new form FindBugsFrame.
     */
    public FindBugsFrame() {

        UserPreferences prefs = UserPreferences.getUserPreferences();
        prefs.read();

        String dirProp = SystemProperties.getProperty("user.dir");

        if (dirProp != null) {
            currentDirectory = new File(dirProp);
        }

        initComponents();
        postInitComponents();
    }

    /* ----------------------------------------------------------------------
     * Component initialization and event handlers
     * ---------------------------------------------------------------------- */

    /**
     * This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    private void initComponents() {//GEN-BEGIN:initComponents
        java.awt.GridBagConstraints gridBagConstraints;

        priorityButtonGroup = new javax.swing.ButtonGroup();
        effortButtonGroup = new javax.swing.ButtonGroup();
        //consoleSplitter = new javax.swing.JSplitPane();
        viewPanel = new javax.swing.JPanel();
        emptyPanel = new javax.swing.JPanel();
        reportPanel = new javax.swing.JPanel();
        editProjectPanel = new javax.swing.JPanel();
        jarFileLabel = new javax.swing.JLabel();
        jarNameTextField = new javax.swing.JTextField();
        addJarButton = new javax.swing.JButton();
        jarFileListLabel = new javax.swing.JLabel();
        sourceDirLabel = new javax.swing.JLabel();
        srcDirTextField = new javax.swing.JTextField();
        addSourceDirButton = new javax.swing.JButton();
        sourceDirListLabel = new javax.swing.JLabel();
        removeJarButton = new javax.swing.JButton();
        removeSrcDirButton = new javax.swing.JButton();
        jSeparator1 = new javax.swing.JSeparator();
        browseJarButton = new javax.swing.JButton();
        browseSrcDirButton = new javax.swing.JButton();
        editProjectLabel = new javax.swing.JLabel();
        jSeparator2 = new javax.swing.JSeparator();
        findBugsButton = new javax.swing.JButton();
        jSeparator4 = new javax.swing.JSeparator();
        jarFileListScrollPane = new javax.swing.JScrollPane();
        jarFileList = new javax.swing.JList();
        sourceDirListScrollPane = new javax.swing.JScrollPane();
        sourceDirList = new javax.swing.JList();
        classpathEntryLabel = new javax.swing.JLabel();
        classpathEntryListLabel = new javax.swing.JLabel();
        classpathEntryTextField = new javax.swing.JTextField();
        browseClasspathEntryButton = new javax.swing.JButton();
        addClasspathEntryButton = new javax.swing.JButton();
        removeClasspathEntryButton = new javax.swing.JButton();
        classpathEntryListScrollPane = new javax.swing.JScrollPane();
        classpathEntryList = new javax.swing.JList();
        jSeparator5 = new javax.swing.JSeparator();
        sourceUpButton = new javax.swing.JButton();
        sourceDownButton = new javax.swing.JButton();
        classpathUpButton = new javax.swing.JButton();
        classpathDownButton = new javax.swing.JButton();
        bugTreePanel = new javax.swing.JPanel();
        bugTreeBugDetailsSplitter = new javax.swing.JSplitPane();
        groupByTabbedPane = new javax.swing.JTabbedPane();
        byClassScrollPane = new javax.swing.JScrollPane();
        byClassBugTree = new javax.swing.JTree();
        byClassBugTree.setFont(JTREE_FONT);
        byPackageScrollPane = new javax.swing.JScrollPane();
        byPackageBugTree = new javax.swing.JTree();
        byPackageBugTree.setFont(JTREE_FONT);
        byBugTypeScrollPane = new javax.swing.JScrollPane();
        byBugTypeBugTree = new javax.swing.JTree();
        byBugTypeBugTree.setFont(JTREE_FONT);
        byBugCategoryScrollPane = new javax.swing.JScrollPane();
        byBugCategoryBugTree = new javax.swing.JTree();
        byBugCategoryBugTree.setFont(JTREE_FONT);
        bySummary = new javax.swing.JScrollPane();
        bugSummaryEditorPane = new javax.swing.JEditorPane();
        bugDetailsTabbedPane = new javax.swing.JTabbedPane();
        bugDescriptionScrollPane = new javax.swing.JScrollPane();
        bugDescriptionEditorPane = new javax.swing.JEditorPane();
        sourceTextAreaScrollPane = new javax.swing.JScrollPane();
        sourceTextArea = new javax.swing.JTextArea();
        annotationTextAreaScrollPane = new javax.swing.JScrollPane();
        annotationTextArea = new javax.swing.JTextArea();
        urlLabel = new javax.swing.JLabel();
        jPanel1 = new javax.swing.JPanel();
        logoLabel = new javax.swing.JLabel();
        growBoxSpacer = new javax.swing.JLabel();
        theMenuBar = new javax.swing.JMenuBar();
        fileMenu = new javax.swing.JMenu();
        newProjectItem = new javax.swing.JMenuItem();
        openProjectItem = new javax.swing.JMenuItem();
        recentProjectsMenu = new javax.swing.JMenu();
        jSeparator9 = new javax.swing.JSeparator();
        closeProjectItem = new javax.swing.JMenuItem();
        saveProjectItem = new javax.swing.JMenuItem();
        saveProjectAsItem = new javax.swing.JMenuItem();
        reloadProjectItem = new javax.swing.JMenuItem();
        jSeparator3 = new javax.swing.JSeparator();
        loadBugsItem = new javax.swing.JMenuItem();
        saveBugsItem = new javax.swing.JMenuItem();
        jSeparator6 = new javax.swing.JSeparator();
        exitItem = new javax.swing.JMenuItem();
        editMenu = new javax.swing.JMenu();
        cutItem = new javax.swing.JMenuItem();
        copyItem = new javax.swing.JMenuItem();
        pasteItem = new javax.swing.JMenuItem();
        jSeparator10 = new javax.swing.JSeparator();
        selectAllItem = new javax.swing.JMenuItem();
        viewMenu = new javax.swing.JMenu();
        viewBugDetailsItem = new javax.swing.JCheckBoxMenuItem();
        fullDescriptionsItem = new javax.swing.JCheckBoxMenuItem();
        jSeparator7 = new javax.swing.JSeparator();
        filterWarningsMenu = new javax.swing.JMenu();
        expPriorityButton = new javax.swing.JRadioButtonMenuItem();
        lowPriorityButton = new javax.swing.JRadioButtonMenuItem();
        mediumPriorityButton = new javax.swing.JRadioButtonMenuItem();
        highPriorityButton = new javax.swing.JRadioButtonMenuItem();
        jSeparator11 = new javax.swing.JSeparator();
        jSeparator8 = new javax.swing.JSeparator();
        viewProjectItem = new javax.swing.JRadioButtonMenuItem();
        viewBugsItem = new javax.swing.JRadioButtonMenuItem();
        settingsMenu = new javax.swing.JMenu();
        configureDetectorsItem = new javax.swing.JMenuItem();
        effortMenu = new javax.swing.JMenu();
        minEffortItem = new javax.swing.JCheckBoxMenuItem();
        normalEffortItem = new javax.swing.JCheckBoxMenuItem();
        maxEffortItem = new javax.swing.JCheckBoxMenuItem();
        helpMenu = new javax.swing.JMenu();
        aboutItem = new javax.swing.JMenuItem();

        getContentPane().setLayout(new java.awt.GridBagLayout());

        addWindowListener(new java.awt.event.WindowAdapter() {
            @Override
            public void windowClosing(java.awt.event.WindowEvent evt) {
                exitForm(evt);
            }
            @Override
            public void windowOpened(java.awt.event.WindowEvent evt) {
                formWindowOpened(evt);
            }
        });

        viewPanel.setLayout(new java.awt.CardLayout());

        viewPanel.add(emptyPanel, "EmptyPanel");

        viewPanel.add(reportPanel, "ReportPanel");

        editProjectPanel.setLayout(new java.awt.GridBagLayout());

        jarFileLabel.setFont(BUTTON_FONT);
        jarFileLabel.setText("Archive or directory:");
        jarFileLabel.setText(L10N.getLocalString("dlg.jarfile_lbl", "Archive or Directory:"));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
        editProjectPanel.add(jarFileLabel, gridBagConstraints);

        jarNameTextField.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jarNameTextFieldActionPerformed(evt);
            }
        });
        jarNameTextField.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                focusGainedHandler(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
        editProjectPanel.add(jarNameTextField, gridBagConstraints);

        addJarButton.setFont(BUTTON_FONT);
        addJarButton.setText("Add");
        addJarButton.setMaximumSize(new java.awt.Dimension(90, 25));
        addJarButton.setMinimumSize(new java.awt.Dimension(90, 25));
        addJarButton.setPreferredSize(new java.awt.Dimension(90, 25));
        addJarButton.setText(L10N.getLocalString("dlg.add_btn", "Add"));
        addJarButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addJarButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
        editProjectPanel.add(addJarButton, gridBagConstraints);

        jarFileListLabel.setFont(BUTTON_FONT);
        jarFileListLabel.setText("Archives/directories:");
        jarFileListLabel.setText(L10N.getLocalString("dlg.jarlist_lbl", "Archives/Directories:"));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
        editProjectPanel.add(jarFileListLabel, gridBagConstraints);

        sourceDirLabel.setFont(BUTTON_FONT);
        sourceDirLabel.setText("Source directory:");
        sourceDirLabel.setText(L10N.getLocalString("dlg.srcfile_lbl", "Source directory:"));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 8;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
        editProjectPanel.add(sourceDirLabel, gridBagConstraints);

        srcDirTextField.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                srcDirTextFieldActionPerformed(evt);
            }
        });
        srcDirTextField.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                focusGainedHandler(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 8;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
        editProjectPanel.add(srcDirTextField, gridBagConstraints);

        addSourceDirButton.setFont(BUTTON_FONT);
        addSourceDirButton.setText("Add");
        addSourceDirButton.setMaximumSize(new java.awt.Dimension(90, 25));
        addSourceDirButton.setMinimumSize(new java.awt.Dimension(90, 25));
        addSourceDirButton.setPreferredSize(new java.awt.Dimension(90, 25));
        addSourceDirButton.setText(L10N.getLocalString("dlg.add_btn", "Add"));
        addSourceDirButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addSourceDirButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 8;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
        editProjectPanel.add(addSourceDirButton, gridBagConstraints);

        sourceDirListLabel.setFont(BUTTON_FONT);
        sourceDirListLabel.setText("Source directories:");
        sourceDirListLabel.setText(L10N.getLocalString("dlg.srclist_lbl", "Source directories:"));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 9;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
        editProjectPanel.add(sourceDirListLabel, gridBagConstraints);

        removeJarButton.setFont(BUTTON_FONT);
        removeJarButton.setText("Remove");
        removeJarButton.setMaximumSize(new java.awt.Dimension(90, 25));
        removeJarButton.setMinimumSize(new java.awt.Dimension(90, 25));
        removeJarButton.setPreferredSize(new java.awt.Dimension(90, 25));
        removeJarButton.setText(L10N.getLocalString("dlg.remove_btn", "Remove"));
        removeJarButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                removeJarButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
        editProjectPanel.add(removeJarButton, gridBagConstraints);

        removeSrcDirButton.setFont(BUTTON_FONT);
        removeSrcDirButton.setText("Remove");
        removeSrcDirButton.setMaximumSize(new java.awt.Dimension(90, 25));
        removeSrcDirButton.setMinimumSize(new java.awt.Dimension(90, 25));
        removeSrcDirButton.setPreferredSize(new java.awt.Dimension(90, 25));
        removeSrcDirButton.setText(L10N.getLocalString("dlg.remove_btn", "Remove"));
        removeSrcDirButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                removeSrcDirButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 9;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
        editProjectPanel.add(removeSrcDirButton, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = 4;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
        editProjectPanel.add(jSeparator1, gridBagConstraints);

        browseJarButton.setFont(BUTTON_FONT);
        browseJarButton.setText("Browse");
        browseJarButton.setMaximumSize(new java.awt.Dimension(90, 25));
        browseJarButton.setMinimumSize(new java.awt.Dimension(90, 25));
        browseJarButton.setPreferredSize(new java.awt.Dimension(90, 25));
        browseJarButton.setText(L10N.getLocalString("dlg.browse_btn", "Browse..."));
        browseJarButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                browseJarButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
        editProjectPanel.add(browseJarButton, gridBagConstraints);

        browseSrcDirButton.setFont(BUTTON_FONT);
        browseSrcDirButton.setText("Browse");
        browseSrcDirButton.setMaximumSize(new java.awt.Dimension(90, 25));
        browseSrcDirButton.setMinimumSize(new java.awt.Dimension(90, 25));
        browseSrcDirButton.setPreferredSize(new java.awt.Dimension(90, 25));
        browseSrcDirButton.setText(L10N.getLocalString("dlg.browse_btn", "Browse..."));
        browseSrcDirButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                browseSrcDirButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 8;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
        editProjectPanel.add(browseSrcDirButton, gridBagConstraints);

        editProjectLabel.setBackground(new java.awt.Color(0, 0, 204));
        editProjectLabel.setFont(LABEL_FONT);
        editProjectLabel.setForeground(new java.awt.Color(255, 255, 255));
        editProjectLabel.setText("Project");
        editProjectLabel.setOpaque(true);
        editProjectLabel.setText(L10N.getLocalString("dlg.project_lbl", "Project"));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.gridwidth = 4;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        editProjectPanel.add(editProjectLabel, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 7;
        gridBagConstraints.gridwidth = 4;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
        editProjectPanel.add(jSeparator2, gridBagConstraints);

        findBugsButton.setMnemonic('B');
        findBugsButton.setText("Find Bugs!");
        findBugsButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                findBugsButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 21;
        gridBagConstraints.gridwidth = 4;
        gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
        editProjectPanel.add(findBugsButton, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 14;
        gridBagConstraints.gridwidth = 4;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
        editProjectPanel.add(jSeparator4, gridBagConstraints);

        jarFileListScrollPane.setPreferredSize(new java.awt.Dimension(259, 1));
        jarFileList.setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.LOWERED));
        jarFileList.setFont(BUTTON_FONT);
        disableEditKeyBindings(jarFileList);

        jarFileList.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                focusGainedHandler(evt);
            }
        });

        jarFileListScrollPane.setViewportView(jarFileList);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weighty = 0.4;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
        editProjectPanel.add(jarFileListScrollPane, gridBagConstraints);

        sourceDirListScrollPane.setPreferredSize(new java.awt.Dimension(259, 1));
        sourceDirList.setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.LOWERED));
        sourceDirList.setFont(BUTTON_FONT);
        disableEditKeyBindings(sourceDirList);
        sourceDirList.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                focusGainedHandler(evt);
            }
        });

        sourceDirListScrollPane.setViewportView(sourceDirList);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 9;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.gridheight = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weighty = 0.1;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
        editProjectPanel.add(sourceDirListScrollPane, gridBagConstraints);

        classpathEntryLabel.setFont(BUTTON_FONT);
        classpathEntryLabel.setText("Classpath entry:");
        classpathEntryLabel.setText(L10N.getLocalString("dlg.classpathfile_lbl", "Classpath entry:"));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 15;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 3);
        editProjectPanel.add(classpathEntryLabel, gridBagConstraints);

        classpathEntryListLabel.setFont(BUTTON_FONT);
        classpathEntryListLabel.setText("Classpath entries:");
        classpathEntryListLabel.setText(L10N.getLocalString("dlg.classpathlist_lbl", "Classpath entries:"));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 16;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 3);
        editProjectPanel.add(classpathEntryListLabel, gridBagConstraints);

        classpathEntryTextField.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                focusGainedHandler(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 15;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
        editProjectPanel.add(classpathEntryTextField, gridBagConstraints);

        browseClasspathEntryButton.setFont(BUTTON_FONT);
        browseClasspathEntryButton.setText("Browse");
        browseClasspathEntryButton.setMaximumSize(new java.awt.Dimension(90, 25));
        browseClasspathEntryButton.setMinimumSize(new java.awt.Dimension(90, 25));
        browseClasspathEntryButton.setPreferredSize(new java.awt.Dimension(90, 25));
        browseClasspathEntryButton.setText(L10N.getLocalString("dlg.browse_btn", "Browse..."));
        browseClasspathEntryButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                browseClasspathEntryButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 15;
        editProjectPanel.add(browseClasspathEntryButton, gridBagConstraints);

        addClasspathEntryButton.setFont(BUTTON_FONT);
        addClasspathEntryButton.setText("Add");
        addClasspathEntryButton.setMaximumSize(new java.awt.Dimension(90, 25));
        addClasspathEntryButton.setMinimumSize(new java.awt.Dimension(90, 25));
        addClasspathEntryButton.setPreferredSize(new java.awt.Dimension(90, 25));
        addClasspathEntryButton.setText(L10N.getLocalString("dlg.add_btn", "Add"));
        addClasspathEntryButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addClasspathEntryButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 15;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
        editProjectPanel.add(addClasspathEntryButton, gridBagConstraints);

        removeClasspathEntryButton.setFont(BUTTON_FONT);
        removeClasspathEntryButton.setText("Remove");
        removeClasspathEntryButton.setMaximumSize(new java.awt.Dimension(90, 25));
        removeClasspathEntryButton.setMinimumSize(new java.awt.Dimension(90, 25));
        removeClasspathEntryButton.setPreferredSize(new java.awt.Dimension(90, 25));
        removeClasspathEntryButton.setText(L10N.getLocalString("dlg.remove_btn", "Remove"));
        removeClasspathEntryButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                removeClasspathEntryButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 16;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
        editProjectPanel.add(removeClasspathEntryButton, gridBagConstraints);

        classpathEntryListScrollPane.setPreferredSize(new java.awt.Dimension(259, 1));
        classpathEntryList.setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.LOWERED));
        classpathEntryList.setFont(BUTTON_FONT);
        disableEditKeyBindings(classpathEntryList);
        classpathEntryList.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                focusGainedHandler(evt);
            }
        });

        classpathEntryListScrollPane.setViewportView(classpathEntryList);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 16;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.gridheight = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weighty = 0.1;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
        editProjectPanel.add(classpathEntryListScrollPane, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 20;
        gridBagConstraints.gridwidth = 4;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.insets = new java.awt.Insets(3, 0, 3, 0);
        editProjectPanel.add(jSeparator5, gridBagConstraints);

        sourceUpButton.setFont(BUTTON_FONT);
        sourceUpButton.setText("Up");
        sourceUpButton.setMaximumSize(new java.awt.Dimension(90, 25));
        sourceUpButton.setMinimumSize(new java.awt.Dimension(90, 25));
        sourceUpButton.setPreferredSize(new java.awt.Dimension(90, 25));
        sourceUpButton.setText(L10N.getLocalString("dlg.up_btn", "Up"));
        sourceUpButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                sourceUpButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 10;
        gridBagConstraints.weighty = 0.2;
        editProjectPanel.add(sourceUpButton, gridBagConstraints);

        sourceDownButton.setFont(BUTTON_FONT);
        sourceDownButton.setText("Down");
        sourceDownButton.setMaximumSize(new java.awt.Dimension(90, 25));
        sourceDownButton.setMinimumSize(new java.awt.Dimension(90, 25));
        sourceDownButton.setPreferredSize(new java.awt.Dimension(90, 25));
        sourceDownButton.setText(L10N.getLocalString("dlg.down_btn", "Down"));
        sourceDownButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                sourceDownButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridy = 11;
        editProjectPanel.add(sourceDownButton, gridBagConstraints);

        classpathUpButton.setFont(BUTTON_FONT);
        classpathUpButton.setText("Up");
        classpathUpButton.setMaximumSize(new java.awt.Dimension(90, 25));
        classpathUpButton.setMinimumSize(new java.awt.Dimension(90, 25));
        classpathUpButton.setPreferredSize(new java.awt.Dimension(90, 25));
        classpathUpButton.setText(L10N.getLocalString("dlg.up_btn", "Up"));
        classpathUpButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                classpathUpButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 17;
        gridBagConstraints.weighty = 0.2;
        editProjectPanel.add(classpathUpButton, gridBagConstraints);

        classpathDownButton.setFont(BUTTON_FONT);
        classpathDownButton.setText("Down");
        classpathDownButton.setMaximumSize(new java.awt.Dimension(90, 25));
        classpathDownButton.setMinimumSize(new java.awt.Dimension(90, 25));
        classpathDownButton.setPreferredSize(new java.awt.Dimension(90, 25));
        classpathDownButton.setText(L10N.getLocalString("dlg.down_btn", "Down"));
        classpathDownButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                classpathDownButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 18;
        editProjectPanel.add(classpathDownButton, gridBagConstraints);

        viewPanel.add(editProjectPanel, "EditProjectPanel");

        bugTreePanel.setLayout(new java.awt.GridBagLayout());

        bugTreeBugDetailsSplitter.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
        bugTreeBugDetailsSplitter.setResizeWeight(1.0);
        bugTreeBugDetailsSplitter.setOneTouchExpandable(true);
        bugTreeBugDetailsSplitter.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
            public void propertyChange(java.beans.PropertyChangeEvent evt) {
                bugTreeBugDetailsSplitterPropertyChange(evt);
            }
        });

        byClassBugTree.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                focusGainedHandler(evt);
            }
        });

        byClassScrollPane.setViewportView(byClassBugTree);

        groupByTabbedPane.addTab("By Class", byClassScrollPane);

        byPackageBugTree.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                focusGainedHandler(evt);
            }
        });

        byPackageScrollPane.setViewportView(byPackageBugTree);

        groupByTabbedPane.addTab("By Package", byPackageScrollPane);

        byBugTypeBugTree.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                focusGainedHandler(evt);
            }
        });

        byBugTypeScrollPane.setViewportView(byBugTypeBugTree);

        groupByTabbedPane.addTab("By Bug Type", byBugTypeScrollPane);

        byBugCategoryBugTree.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                focusGainedHandler(evt);
            }
        });

        byBugCategoryScrollPane.setViewportView(byBugCategoryBugTree);

        groupByTabbedPane.addTab("By Category Type", byBugCategoryScrollPane);

        bugSummaryEditorPane.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                focusGainedHandler(evt);
            }
        });

        bySummary.setViewportView(bugSummaryEditorPane);

        groupByTabbedPane.addTab("Summary", bySummary);

        bugTreeBugDetailsSplitter.setTopComponent(groupByTabbedPane);

        bugDescriptionEditorPane.setEditable(false);
        bugDescriptionEditorPane.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                focusGainedHandler(evt);
            }
        });

        bugDescriptionScrollPane.setViewportView(bugDescriptionEditorPane);

        bugDetailsTabbedPane.addTab("Details", bugDescriptionScrollPane);

        sourceTextAreaScrollPane.setMinimumSize(new java.awt.Dimension(22, 180));
        sourceTextAreaScrollPane.setPreferredSize(new java.awt.Dimension(0, 100));
        sourceTextArea.setEditable(false);
        sourceTextArea.setFont(SOURCE_FONT);
        sourceTextArea.setEnabled(false);
        sourceTextArea.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                focusGainedHandler(evt);
            }
        });


        sourceTextAreaScrollPane.setViewportView(sourceTextArea);
        sourceLineNumberer = new LineNumberer(sourceTextArea);
        sourceLineNumberer.setBackground(Color.WHITE);
        sourceTextAreaScrollPane.setRowHeaderView(sourceLineNumberer);

        bugDetailsTabbedPane.addTab("Source code", sourceTextAreaScrollPane);

        annotationTextArea.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                focusGainedHandler(evt);
            }
        });

        annotationTextAreaScrollPane.setViewportView(annotationTextArea);

        bugDetailsTabbedPane.addTab("Annotations", annotationTextAreaScrollPane);

        bugTreeBugDetailsSplitter.setBottomComponent(bugDetailsTabbedPane);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        bugTreePanel.add(bugTreeBugDetailsSplitter, gridBagConstraints);

        viewPanel.add(bugTreePanel, "BugTree");

        {
            equalizeControls( new JComponent[]
                {
                    addJarButton,
                    addSourceDirButton,
                    addClasspathEntryButton,
                    removeJarButton,
                    removeSrcDirButton,
                    removeClasspathEntryButton,
                    browseJarButton,
                    browseSrcDirButton,
                    browseClasspathEntryButton,
                    sourceUpButton,
                    sourceDownButton,
                    classpathUpButton,
                    classpathDownButton
                });

                groupByTabbedPane.setTitleAt(0, L10N.getLocalString( "dlg.byclass_tab", "By Class"));
                groupByTabbedPane.setTitleAt(1, L10N.getLocalString( "dlg.bypackage_tab", "By Package"));
                groupByTabbedPane.setTitleAt(2, L10N.getLocalString( "dlg.bybugtype_tab", "By Bug Type"));
                groupByTabbedPane.setTitleAt(3, L10N.getLocalString( "dlg.bybugcategory_tab", "By Bug Category"));
                groupByTabbedPane.setTitleAt(4, L10N.getLocalString( "dlg.summary_tab", "Summary"));
                bugDetailsTabbedPane.setTitleAt(0, L10N.getLocalString( "dlg.details_tab", "Details"));
                bugDetailsTabbedPane.setTitleAt(1, L10N.getLocalString( "dlg.sourcecode_tab", "Source Code"));
                bugDetailsTabbedPane.setTitleAt(2, L10N.getLocalString( "dlg.annotations_tab", "Annotations"));
            }

            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 0;
            gridBagConstraints.gridy = 0;
            gridBagConstraints.gridwidth = 2;
            gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
            gridBagConstraints.weightx = 1.0;
            gridBagConstraints.weighty = 1.0;
            getContentPane().add(viewPanel, gridBagConstraints);

            urlLabel.setFont(BUTTON_FONT);
            urlLabel.setText("FindBugs - http://findbugs.sourceforge.net/");
            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 0;
            gridBagConstraints.gridy = 1;
            gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
            gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 0);
            getContentPane().add(urlLabel, gridBagConstraints);

            jPanel1.setLayout(new javax.swing.BoxLayout(jPanel1, javax.swing.BoxLayout.X_AXIS));

            jPanel1.add(logoLabel);

            growBoxSpacer.setMaximumSize(new java.awt.Dimension(16, 16));
            jPanel1.add(growBoxSpacer);

            gridBagConstraints = new java.awt.GridBagConstraints();
            gridBagConstraints.gridx = 1;
            gridBagConstraints.gridy = 1;
            gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHEAST;
            gridBagConstraints.insets = new java.awt.Insets(2, 0, 2, 2);
            getContentPane().add(jPanel1, gridBagConstraints);

            theMenuBar.setFont(BUTTON_FONT);
            fileMenu.setText("File");
            fileMenu.setFont(BUTTON_FONT);
            localiseButton(fileMenu, "menu.file_menu", "&File", true);
            fileMenu.addMenuListener(new javax.swing.event.MenuListener() {
                public void menuCanceled(javax.swing.event.MenuEvent evt) {
                }
                public void menuDeselected(javax.swing.event.MenuEvent evt) {
                }
                public void menuSelected(javax.swing.event.MenuEvent evt) {
                    fileMenuMenuSelected(evt);
                }
            });

            newProjectItem.setFont(BUTTON_FONT);
            newProjectItem.setText("New Project");
            localiseButton(newProjectItem, "menu.new_item", "&New Project", true);
            newProjectItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    newProjectItemActionPerformed(evt);
                }
            });

            fileMenu.add(newProjectItem);

            openProjectItem.setFont(BUTTON_FONT);
            openProjectItem.setText("Open Project...");
            localiseButton(openProjectItem, "menu.open_item", "&Open Project...", true);
            openProjectItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    openProjectItemActionPerformed(evt);
                }
            });

            fileMenu.add(openProjectItem);

            recentProjectsMenu.setText("Recent Projects");
            recentProjectsMenu.setFont(BUTTON_FONT);
            localiseButton(recentProjectsMenu, "menu.recent_menu", "R&ecent Projects", true);
            rebuildRecentProjectsMenu();
            fileMenu.add(recentProjectsMenu);

            fileMenu.add(jSeparator9);

            closeProjectItem.setFont(BUTTON_FONT);
            closeProjectItem.setText("Close Project");
            localiseButton(closeProjectItem, "menu.close_item", "&Close Project", true);
            closeProjectItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    closeProjectItemActionPerformed(evt);
                }
            });

            fileMenu.add(closeProjectItem);

            saveProjectItem.setFont(BUTTON_FONT);
            saveProjectItem.setText("Save Project");
            localiseButton(saveProjectItem, "menu.save_item", "&Save Project", true);
            saveProjectItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    saveProjectItemActionPerformed(evt);
                }
            });

            fileMenu.add(saveProjectItem);

            saveProjectAsItem.setFont(BUTTON_FONT);
            saveProjectAsItem.setText("Save Project As...");
            localiseButton(saveProjectAsItem, "menu.saveas_item", "Save Project &As...", true);
            saveProjectAsItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    saveProjectAsItemActionPerformed(evt);
                }
            });

            fileMenu.add(saveProjectAsItem);

            reloadProjectItem.setFont(BUTTON_FONT);
            reloadProjectItem.setText("Reload Project");
            localiseButton(reloadProjectItem, "menu.reload_item", "&Reload Project", true);
            reloadProjectItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    reloadProjectItemActionPerformed(evt);
                }
            });

            fileMenu.add(reloadProjectItem);

            fileMenu.add(jSeparator3);

            loadBugsItem.setFont(BUTTON_FONT);
            loadBugsItem.setText("Load Bugs...");
            localiseButton(loadBugsItem, "menu.loadbugs_item", "&Load Bugs...", true);
            loadBugsItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    loadBugsItemActionPerformed(evt);
                }
            });

            fileMenu.add(loadBugsItem);

            saveBugsItem.setFont(BUTTON_FONT);
            saveBugsItem.setText("Save Bugs");
            localiseButton(saveBugsItem, "menu.savebugs_item", "Save &Bugs...", true);
            saveBugsItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    saveBugsItemActionPerformed(evt);
                }
            });

            fileMenu.add(saveBugsItem);

            fileMenu.add(jSeparator6);

            exitItem.setFont(BUTTON_FONT);
            exitItem.setText("Exit");
            localiseButton(exitItem, "menu.exit_item", "E&xit", true);
            exitItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    exitItemActionPerformed(evt);
                }
            });

            fileMenu.add(exitItem);

            theMenuBar.add(fileMenu);

            editMenu.setText("Edit");
            editMenu.setFont(BUTTON_FONT);
            editMenu.setEnabled(false);
            localiseButton(editMenu, "menu.edit_menu", "&Edit", true);
            cutItem.setFont(BUTTON_FONT);
            cutItem.setText("Cut");
            localiseButton(cutItem, "menu.cut_item", "Cut", true);
            cutItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    cutActionPerformed(evt);
                }
            });

            editMenu.add(cutItem);

            copyItem.setFont(BUTTON_FONT);
            copyItem.setText("Copy");
            localiseButton(copyItem, "menu.copy_item", "Copy", true);
            copyItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    copyActionPerformed(evt);
                }
            });

            editMenu.add(copyItem);

            pasteItem.setFont(BUTTON_FONT);
            pasteItem.setText("Paste");
            localiseButton(pasteItem, "menu.paste_item", "Paste", true);
            pasteItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    pasteActionPerformed(evt);
                }
            });

            editMenu.add(pasteItem);

            editMenu.add(jSeparator10);

            selectAllItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_A, java.awt.event.InputEvent.CTRL_MASK));
            selectAllItem.setFont(BUTTON_FONT);
            selectAllItem.setText("Select All");
            localiseButton(selectAllItem, "menu.selectall_item", "Select &All", true);
            selectAllItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    selectAllActionPerformed(evt);
                }
            });

            editMenu.add(selectAllItem);

            theMenuBar.add(editMenu);

            viewMenu.setText("View");
            viewMenu.setFont(BUTTON_FONT);
            localiseButton(viewMenu, "menu.view_menu", "&View", true);

            viewMenu.addMenuListener(new javax.swing.event.MenuListener() {
                public void menuCanceled(javax.swing.event.MenuEvent evt) {
                }
                public void menuDeselected(javax.swing.event.MenuEvent evt) {
                }
                public void menuSelected(javax.swing.event.MenuEvent evt) {
                    viewMenuMenuSelected(evt);
                }
            });

            viewBugDetailsItem.setFont(BUTTON_FONT);
            viewBugDetailsItem.setSelected(true);
            viewBugDetailsItem.setText("Bug Details");
            localiseButton(viewBugDetailsItem, "menu.bugdetails_item", "Bug &Details", true);
            viewBugDetailsItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    viewBugDetailsItemActionPerformed(evt);
                }
            });

            viewMenu.add(viewBugDetailsItem);

            fullDescriptionsItem.setFont(BUTTON_FONT);
            fullDescriptionsItem.setSelected(true);
            fullDescriptionsItem.setText("Full Descriptions");
            localiseButton(fullDescriptionsItem, "menu.fulldescriptions_item", "&Full Descriptions", true);
            fullDescriptionsItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    fullDescriptionsItemActionPerformed(evt);
                }
            });

            viewMenu.add(fullDescriptionsItem);

            viewMenu.add(jSeparator7);

            filterWarningsMenu.setText("Filter Warnings");
            filterWarningsMenu.setFont(BUTTON_FONT);
            localiseButton(filterWarningsMenu, "menu.filterwarnings_menu", "Filter &Warnings", true);
            expPriorityButton.setFont(BUTTON_FONT);
            expPriorityButton.setText("Experimental Priority");
            priorityButtonGroup.add(expPriorityButton);
            localiseButton(expPriorityButton, "menu.exppriority_item", "&Experimental Priority", true);
            expPriorityButton.setSelected(getPriorityThreshold() == Detector.EXP_PRIORITY);
            expPriorityButton.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    expPriorityButtonActionPerformed(evt);
                }
            });

            filterWarningsMenu.add(expPriorityButton);

            lowPriorityButton.setFont(BUTTON_FONT);
            lowPriorityButton.setText("Low Priority");
            priorityButtonGroup.add(lowPriorityButton);
            localiseButton(lowPriorityButton, "menu.lowpriority_item", "&Low Priority", true);
            lowPriorityButton.setSelected(getPriorityThreshold() == Detector.LOW_PRIORITY);
            lowPriorityButton.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    lowPriorityButtonActionPerformed(evt);
                }
            });

            filterWarningsMenu.add(lowPriorityButton);

            mediumPriorityButton.setFont(BUTTON_FONT);
            mediumPriorityButton.setText("Medium Priority");
            priorityButtonGroup.add(mediumPriorityButton);
            localiseButton(mediumPriorityButton, "menu.mediumpriority_item", "&Medium Priority", true);
            mediumPriorityButton.setSelected(getPriorityThreshold() == Detector.NORMAL_PRIORITY);
            mediumPriorityButton.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    mediumPriorityButtonActionPerformed(evt);
                }
            });

            filterWarningsMenu.add(mediumPriorityButton);

            highPriorityButton.setFont(BUTTON_FONT);
            highPriorityButton.setText("High Priority");
            priorityButtonGroup.add(highPriorityButton);
            localiseButton(highPriorityButton, "menu.highpriority_item", "&High Priority", true);
            highPriorityButton.setSelected(getPriorityThreshold() == Detector.HIGH_PRIORITY);
            highPriorityButton.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    highPriorityButtonActionPerformed(evt);
                }
            });

            filterWarningsMenu.add(highPriorityButton);

            filterWarningsMenu.add(jSeparator11);

            viewMenu.add(filterWarningsMenu);

            ButtonGroup bg = new ButtonGroup();
            bg.add(expPriorityButton);
            bg.add(lowPriorityButton);
            bg.add(mediumPriorityButton);
            bg.add(highPriorityButton);

            viewMenu.add(jSeparator8);

            viewProjectItem.setFont(BUTTON_FONT);
            viewProjectItem.setText("View Project Details");
            viewProjectItem.setEnabled(false);
            localiseButton(viewProjectItem, "menu.viewprojectdetails_item", "View Project Details", true);
            viewProjectItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    viewProjectItemActionPerformed(evt);
                }
            });

            viewMenu.add(viewProjectItem);

            viewBugsItem.setFont(BUTTON_FONT);
            viewBugsItem.setText("View Bugs");
            viewBugsItem.setEnabled(false);
            localiseButton(viewBugsItem, "menu.viewbugs_item", "View Bugs", true);
            viewBugsItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    viewBugsItemActionPerformed(evt);
                }
            });

            viewMenu.add(viewBugsItem);

            theMenuBar.add(viewMenu);

            settingsMenu.setText("Settings");
            settingsMenu.setFont(BUTTON_FONT);
            localiseButton(settingsMenu, "menu.settings_menu", "&Settings", true);
            settingsMenu.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    settingsMenuActionPerformed(evt);
                }
            });

            configureDetectorsItem.setFont(BUTTON_FONT);
            configureDetectorsItem.setText("Configure Detectors...");
            localiseButton(configureDetectorsItem, "menu.configure_item", "&Configure Detectors...", true);
            configureDetectorsItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    configureDetectorsItemActionPerformed(evt);
                }
            });

            settingsMenu.add(configureDetectorsItem);

            effortMenu.setText("Effort");
            effortMenu.setFont(BUTTON_FONT);
            localiseButton(effortMenu, "menu.effort_menu", "Effort", true);
            minEffortItem.setFont(BUTTON_FONT);
            minEffortItem.setText("Minimum");
            effortButtonGroup.add(minEffortItem);
            localiseButton(minEffortItem, "menu.mineffort_item", "&Minimum", true);
            minEffortItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    minEffortItemActionPerformed(evt);
                }
            });

            effortMenu.add(minEffortItem);

            normalEffortItem.setFont(BUTTON_FONT);
            normalEffortItem.setSelected(true);
            normalEffortItem.setText("Normal");
            effortButtonGroup.add(normalEffortItem);
            localiseButton(normalEffortItem, "menu.normaleffort_item", "&Normal", true);
            normalEffortItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    normalEffortItemActionPerformed(evt);
                }
            });

            effortMenu.add(normalEffortItem);

            maxEffortItem.setFont(BUTTON_FONT);
            maxEffortItem.setText("Maximum");
            effortButtonGroup.add(maxEffortItem);
            localiseButton(maxEffortItem, "menu.maxeffort_item", "&Maximum", true);
            maxEffortItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    maxEffortItemActionPerformed(evt);
                }
            });

            effortMenu.add(maxEffortItem);

            settingsMenu.add(effortMenu);

            theMenuBar.add(settingsMenu);

            helpMenu.setText("Help");
            helpMenu.setFont(BUTTON_FONT);
            localiseButton(helpMenu, "menu.help_menu", "&Help", true);
            aboutItem.setFont(BUTTON_FONT);
            aboutItem.setText("About...");
            localiseButton(aboutItem, "menu.about_item", "&About", true);
            aboutItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    aboutItemActionPerformed(evt);
                }
            });

            helpMenu.add(aboutItem);

            theMenuBar.add(helpMenu);

            setJMenuBar(theMenuBar);

            pack();
        }//GEN-END:initComponents

    private void maxEffortItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_maxEffortItemActionPerformed
        settingList = FindBugs.MAX_EFFORT;
    }//GEN-LAST:event_maxEffortItemActionPerformed

    private void normalEffortItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_normalEffortItemActionPerformed
        settingList = FindBugs.DEFAULT_EFFORT;
    }//GEN-LAST:event_normalEffortItemActionPerformed

    private void minEffortItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_minEffortItemActionPerformed
        settingList = FindBugs.MIN_EFFORT;
    }//GEN-LAST:event_minEffortItemActionPerformed

    private void settingsMenuActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_settingsMenuActionPerformed
        // TODO add your handling code here:
    }//GEN-LAST:event_settingsMenuActionPerformed

    private void formWindowOpened(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowOpened
        if (SystemProperties.getBoolean("findbugs.noSummary")) {
            groupByTabbedPane.remove(bySummary);
        }
    }//GEN-LAST:event_formWindowOpened

    private void selectAllActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectAllActionPerformed
        if (selectedComponent == null)
            return;

        if (selectedComponent instanceof JTextComponent)
            ((JTextComponent)selectedComponent).selectAll();
        else if (selectedComponent instanceof JList) {
            JList list = (JList)selectedComponent;
            list.setSelectionInterval(0, list.getModel().getSize()-1);
        }
    }//GEN-LAST:event_selectAllActionPerformed

    private void disableEditKeyBindings(JList list) {
        list.getInputMap().put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_X, java.awt.event.InputEvent.CTRL_MASK), "none");
        list.getInputMap().put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_C, java.awt.event.InputEvent.CTRL_MASK), "none");
        list.getInputMap().put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_V, java.awt.event.InputEvent.CTRL_MASK), "none");
    }

    private String buildSelectPath(JList list) {
        StringBuilder path = new StringBuilder();
        int[] indices = list.getSelectedIndices();
        String separatorStr = SystemProperties.getProperty("path.separator");
        String sep = "";
        ListModel m = list.getModel();
        for (int indice : indices) {
            path.append(sep);
            sep = separatorStr;
            path.append(m.getElementAt(indice));
        }
        return path.toString();
    }

    private void pasteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pasteActionPerformed
        if (selectedComponent == null)
            return;

        if (selectedComponent instanceof JTextComponent)
            ((JTextComponent)selectedComponent).paste();
        else if (selectedComponent instanceof JList) {
            Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
            Transferable transfer = cb.getContents(this);
            if (transfer.isDataFlavorSupported(DataFlavor.stringFlavor)) {
                try {
                    String path = (String)transfer.getTransferData(DataFlavor.stringFlavor);

                    if (selectedComponent == jarFileList) {
                        jarNameTextField.setText(path);
                        addJarButtonActionPerformed(evt);
                    }
                    else if (selectedComponent == sourceDirList) {
                        srcDirTextField.setText(path);
                        this.addSourceDirButtonActionPerformed(evt);
                    }
                    else if (selectedComponent == classpathEntryList) {
                        classpathEntryTextField.setText(path);
                        addClasspathEntryButtonActionPerformed(evt);
                    }
                } catch (Exception e) {
                }
            }
        }
    }//GEN-LAST:event_pasteActionPerformed

    private void copyActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_copyActionPerformed
        if (selectedComponent == null)
            return;

        if (selectedComponent instanceof JTextComponent)
            ((JTextComponent)selectedComponent).copy();
        else if (selectedComponent instanceof JTree) {
            TreePath path = ((JTree)selectedComponent).getSelectionPath();
            StringSelection data = new StringSelection(path.getLastPathComponent().toString());
            Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
            cb.setContents(data, data);
       }
        else if (selectedComponent instanceof JList) {
            StringSelection path = new StringSelection(buildSelectPath((JList)selectedComponent));
            Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
            cb.setContents(path, path);
        }
    }//GEN-LAST:event_copyActionPerformed

    private void cutActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cutActionPerformed
        if (selectedComponent == null)
            return;

        if (selectedComponent instanceof JTextComponent)
            ((JTextComponent)selectedComponent).cut();
        else if (selectedComponent instanceof JList) {
            StringSelection path = new StringSelection(buildSelectPath((JList)selectedComponent));
            Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
            cb.setContents(path, path);
            if (selectedComponent == jarFileList)
                removeJarButtonActionPerformed(evt);
            else if (selectedComponent == sourceDirList)
                removeSrcDirButtonActionPerformed(evt);
            else if (selectedComponent == classpathEntryList)
                removeClasspathEntryButtonActionPerformed(evt);
        }
    }//GEN-LAST:event_cutActionPerformed

    private void focusGainedHandler(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_focusGainedHandler
        Component old = evt.getOppositeComponent();
        if (old instanceof JList)
            ((JList) old).clearSelection();
        selectedComponent = evt.getComponent();
        ableEditMenu();
    }//GEN-LAST:event_focusGainedHandler

    private void classpathUpButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_classpathUpButtonActionPerformed
        if (moveEntriesUp(classpathEntryList))
            resyncAuxClasspathEntries();
    }//GEN-LAST:event_classpathUpButtonActionPerformed

    private void sourceDownButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sourceDownButtonActionPerformed
        if (moveEntriesDown(sourceDirList))
            resyncSourceEntries();
    }//GEN-LAST:event_sourceDownButtonActionPerformed

    private void sourceUpButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sourceUpButtonActionPerformed
        if (moveEntriesUp(sourceDirList))
            resyncSourceEntries();
    }//GEN-LAST:event_sourceUpButtonActionPerformed

    private void classpathDownButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_classpathDownButtonActionPerformed
        if (moveEntriesDown(classpathEntryList))
            resyncAuxClasspathEntries();
    }//GEN-LAST:event_classpathDownButtonActionPerformed

    private void viewBugsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewBugsItemActionPerformed
        setView("BugTree");
    }//GEN-LAST:event_viewBugsItemActionPerformed

    private void viewProjectItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewProjectItemActionPerformed
        setView("EditProjectPanel");
    }//GEN-LAST:event_viewProjectItemActionPerformed

    private void highPriorityButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_highPriorityButtonActionPerformed
        setPriorityThreshold(Detector.HIGH_PRIORITY);
    }//GEN-LAST:event_highPriorityButtonActionPerformed

    private void mediumPriorityButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mediumPriorityButtonActionPerformed
        setPriorityThreshold(Detector.NORMAL_PRIORITY);
    }//GEN-LAST:event_mediumPriorityButtonActionPerformed

    private void lowPriorityButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_lowPriorityButtonActionPerformed
        setPriorityThreshold(Detector.LOW_PRIORITY);
    }//GEN-LAST:event_lowPriorityButtonActionPerformed

    private void expPriorityButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_expPriorityButtonActionPerformed
        setPriorityThreshold(Detector.EXP_PRIORITY);
    }//GEN-LAST:event_expPriorityButtonActionPerformed

    private void saveBugsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveBugsItemActionPerformed

        try {
            if (currentAnalysisRun == null) {
                logger.logMessage(Logger.ERROR, "No bugs are loaded!");
                return;
            }

            JFileChooser chooser = createFileChooser();
            chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            chooser.setFileFilter(xmlFileFilter);

            int result = chooseFile(chooser, L10N.getLocalString("dlg.savebugs_ttl", "Save Bugs"));

            if (result != JFileChooser.CANCEL_OPTION) {
                // Make sure current annotation text is up to date with its
                // corresponding bug instance
                if (currentBugInstance != null)
                    synchBugAnnotation(currentBugInstance);

                // Save bugs to file
                File selectedFile = chooser.getSelectedFile();
                currentAnalysisRun.saveBugsToFile(selectedFile);
            }
        } catch (Exception e) {
            if (FindBugs.DEBUG) {
                e.printStackTrace();
            }
            logger.logMessage(Logger.ERROR, "Could not save bugs: " + e.toString());
        }
    }//GEN-LAST:event_saveBugsItemActionPerformed

    private void loadBugsFromFile(File file) throws IOException, DocumentException {
        File selectedFile = file;

        Project project = new Project();
        AnalysisRun analysisRun = new AnalysisRun(project, this);

        analysisRun.loadBugsFromFile(selectedFile);

        project.setProjectFileName(file.getName()); // otherwise frame will show "<<unnamed project>>"
        setProject(project);
        synchAnalysisRun(analysisRun);
    }

    private void loadBugsFromURL(String urlspec) throws IOException, DocumentException {
        URL url = new URL(urlspec);
        InputStream in = url.openStream();

        Project project = new Project();
        AnalysisRun analysisRun = new AnalysisRun(project, this);

        analysisRun.loadBugsFromInputStream(in);

        setProject(project);
        synchAnalysisRun(analysisRun);
    }

    private void loadBugsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadBugsItemActionPerformed
        // FIXME: offer to save current project and bugs

        try {

            JFileChooser chooser = createFileChooser();
            chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            chooser.setFileFilter(xmlFileFilter);

            int result = chooseFile(chooser, L10N.getLocalString("dlg.loadbugs_ttl", "Load Bugs..."));

            if (result != JFileChooser.CANCEL_OPTION) {
                loadBugsFromFile(chooser.getSelectedFile());
            }
        } catch (Exception e) {
            if (FindBugs.DEBUG) {
                e.printStackTrace();
            }
            logger.logMessage(Logger.ERROR, "Could not load bugs: " + e.toString());
        }

    }//GEN-LAST:event_loadBugsItemActionPerformed

    private void configureDetectorsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_configureDetectorsItemActionPerformed
        ConfigureDetectorsDialog dialog = new ConfigureDetectorsDialog(this, true);
        dialog.setSize(700, 520);
        dialog.setLocationRelativeTo(null); // center the dialog
        dialog.setVisible(true);
    }//GEN-LAST:event_configureDetectorsItemActionPerformed

    private void reloadProjectItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_reloadProjectItemActionPerformed
        Project current = getCurrentProject();

        if (current == null)
            return;

        try {
            String filename = current.getProjectFileName();
            Project project = new Project();
            project.read(filename);
            setProject(null);
            setProject(project);
            findBugsButtonActionPerformed(evt);
        } catch (IOException e) {
            logger.logMessage(Logger.ERROR, "Could not reload project: " + e.getMessage());
        }

    }//GEN-LAST:event_reloadProjectItemActionPerformed

    private void saveProjectAsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveProjectAsItemActionPerformed
        saveProject(getCurrentProject(), L10N.getLocalString("dlg.saveprojectas_ttl", "Save Project As..."), true);
    }//GEN-LAST:event_saveProjectAsItemActionPerformed

    private void viewMenuMenuSelected(javax.swing.event.MenuEvent evt) {//GEN-FIRST:event_viewMenuMenuSelected
        // View bug details and full descriptions items,
        // are only enabled if there is a project open.
        boolean hasProject = getCurrentProject() != null;
        viewBugDetailsItem.setEnabled(hasProject);
        fullDescriptionsItem.setEnabled(hasProject);
    }//GEN-LAST:event_viewMenuMenuSelected

    private void fileMenuMenuSelected(javax.swing.event.MenuEvent evt) {//GEN-FIRST:event_fileMenuMenuSelected
        // Save and close project items are only enabled if there is a project open.
        boolean hasProject = getCurrentProject() != null;
        saveProjectItem.setEnabled(hasProject);
        saveProjectAsItem.setEnabled(hasProject);
        reloadProjectItem.setEnabled(hasProject && !getCurrentProject().getProjectFileName().equals(Project.UNNAMED_PROJECT));
        closeProjectItem.setEnabled(hasProject);

        // Save bugs is only enabled if there is a current analysis run
        boolean hasAnalysisRun = currentAnalysisRun != null;
        saveBugsItem.setEnabled(hasAnalysisRun);

    }//GEN-LAST:event_fileMenuMenuSelected

    private void closeProjectItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeProjectItemActionPerformed
        if (closeProjectHook(getCurrentProject(), L10N.getLocalString("dlg.closeproject_lbl", "Close Project"))) {
            setProject(null);
        }
    }//GEN-LAST:event_closeProjectItemActionPerformed

    private void removeClasspathEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeClasspathEntryButtonActionPerformed
        Project project = getCurrentProject();
        DefaultListModel listModel = (DefaultListModel) classpathEntryList.getModel();

        int[] selIndices = classpathEntryList.getSelectedIndices();
        for (int i = selIndices.length - 1; i >= 0; i--) {
            int sel = selIndices[i];
            project.removeAuxClasspathEntry(sel);
            listModel.remove(sel);
        }
    }//GEN-LAST:event_removeClasspathEntryButtonActionPerformed

    private void addClasspathEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addClasspathEntryButtonActionPerformed
        addClasspathEntryToList();
    }//GEN-LAST:event_addClasspathEntryButtonActionPerformed

    private void browseClasspathEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseClasspathEntryButtonActionPerformed
        JFileChooser chooser = createFileChooser();
        chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
        chooser.setFileFilter(auxClasspathEntryFileFilter);
        chooser.setMultiSelectionEnabled(true);

        int result = chooseFile(chooser, "Add Entry");

        if (result != JFileChooser.CANCEL_OPTION) {
            File[] selectedFileList = chooser.getSelectedFiles();
            for (int i = 0; i < selectedFileList.length; ++i) {
                selectedFileList[i] = verifyFileSelection(selectedFileList[i]);
                String entry = selectedFileList[i].getPath();
                addClasspathEntryToProject(entry);
            }
        }
    }//GEN-LAST:event_browseClasspathEntryButtonActionPerformed

    private void fullDescriptionsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fullDescriptionsItemActionPerformed
        for (JTree bugTree : bugTreeList) {
            // Redisplay the displayed bug instance nodes
            DefaultTreeModel bugTreeModel = (DefaultTreeModel) bugTree.getModel();
            int numRows = bugTree.getRowCount();

            for (int i = 0; i < numRows; ++i) {
                //System.out.println("Getting path for row " + i);
                TreePath path = bugTree.getPathForRow(i);
                if (path == null)
                    continue;
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
                if (node instanceof BugTreeNode)
                    bugTreeModel.valueForPathChanged(path, node.getUserObject());
            }
        }
    }//GEN-LAST:event_fullDescriptionsItemActionPerformed

    private void viewBugDetailsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewBugDetailsItemActionPerformed
        String view = getView();
        if (view.equals("BugTree")) {
            checkBugDetailsVisibility();
        }

    }//GEN-LAST:event_viewBugDetailsItemActionPerformed

    private void bugTreeBugDetailsSplitterPropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_bugTreeBugDetailsSplitterPropertyChange
        // Here we want to
        //     (1) Keep the View:Bug details checkbox item up to date, and
        //     (2) keep the details window synchronized with the current bug instance
        String propertyName = evt.getPropertyName();
        if (propertyName.equals(JSplitPane.DIVIDER_LOCATION_PROPERTY)) {
            boolean isMaximized = isSplitterMaximized(bugTreeBugDetailsSplitter, evt);
            viewBugDetailsItem.setSelected(!isMaximized);
            if (!isMaximized) {
                // Details window is shown, so make sure it is populated
                // with bug detail information
                synchBugInstance();
            }
        }
    }//GEN-LAST:event_bugTreeBugDetailsSplitterPropertyChange

    private void openProjectItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openProjectItemActionPerformed

        if (!closeProjectHook(getCurrentProject(), L10N.getLocalString("msg.openproject_txt", "Open Project")))
            return;

        JFileChooser chooser = createFileChooser();
        chooser.setFileFilter(projectFileFilter);
        int result = chooseFileToOpen(chooser);

        if (result == JFileChooser.CANCEL_OPTION)
            return;
        try {
            File file = chooser.getSelectedFile();
            Project project = new Project();
            project.read(file.getPath());
            setProject(project);
            UserPreferences.getUserPreferences().useProject(file.getPath());
            rebuildRecentProjectsMenu();

        } catch (IOException e) {
            logger.logMessage(Logger.ERROR, MessageFormat.format( L10N.getLocalString("msg.couldnotopenproject_txt", "Could not open project: {0}"), new Object[]{e.getMessage()}));
        }
    }//GEN-LAST:event_openProjectItemActionPerformed

    private void saveProjectItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveProjectItemActionPerformed
        saveProject(getCurrentProject(), L10N.getLocalString("msg.saveproject_txt", "Save Project"));
    }//GEN-LAST:event_saveProjectItemActionPerformed

    private void aboutItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_aboutItemActionPerformed
        about();
    }//GEN-LAST:event_aboutItemActionPerformed

    private void findBugsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_findBugsButtonActionPerformed
        Project project = getCurrentProject();

        if (project.getFileCount() == 0) {
            logger.logMessage(Logger.ERROR, MessageFormat.format(L10N.getLocalString("msg.projectnojars_txt", "Project {0} has no Jar files selected"), new Object[]{project}));
            return;
        }

        bugDescriptionEditorPane.setText("");
        currentBugDetailsKey = null;
        sourceTextArea.setText("");
        AnalysisRun analysisRun = new AnalysisRun(project, this);

        logger.logMessage(Logger.INFO, MessageFormat.format(L10N.getLocalString("msg.beginninganalysis_txt", "Beginning analysis of {0}"), new Object[]{project}));

        // Run the analysis!
        RunAnalysisDialog dialog = new RunAnalysisDialog(this, analysisRun, analysisPriority);
        dialog.setSize(400, 300);
        dialog.setLocationRelativeTo(null); // center the dialog
        dialog.setVisible(true);

        if (dialog.isCompleted()) {
            logger.logMessage(Logger.INFO, MessageFormat.format(L10N.getLocalString("msg.analysiscompleted_txt", "Analysis {0} completed"), new Object[]{project}));

            // Report any errors that might have occurred during analysis
            analysisRun.reportAnalysisErrors();

            // Now we have an analysis run to look at
            synchAnalysisRun(analysisRun);
        } else {
            if (dialog.exceptionOccurred()) {
                // The analysis was killed by an unexpected exception
                Exception e = dialog.getException();
                AnalysisErrorDialog err = new AnalysisErrorDialog(this, true, null);
                err.addLine(MessageFormat.format(L10N.getLocalString("msg.fatalanalysisexception_txt", "Fatal analysis exception: {0}"),  new Object[]{e.toString()}));
                StackTraceElement[] callList = e.getStackTrace();
                for (StackTraceElement aCallList : callList)
                    err.addLine("\t" + aCallList);
                err.finish();
                err.setSize(650, 500);
                err.setLocationRelativeTo(null); // center the dialog
                err.setVisible(true);
            } else {
                // Cancelled by user
                logger.logMessage(Logger.INFO, MessageFormat.format(L10N.getLocalString("msg.analysiscancelled_txt", "Analysis of {0} cancelled by user"), new Object[]{project}));
            }
        }
    }//GEN-LAST:event_findBugsButtonActionPerformed

    private void browseSrcDirButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseSrcDirButtonActionPerformed
        JFileChooser chooser = createFileChooser();
        chooser.setFileFilter(archiveAndDirectoryFilter);
        chooser.setMultiSelectionEnabled(true);
        chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);

        int rc = chooseFile(chooser, L10N.getLocalString("msg_addsource_lbl", "Add source directory or archive"));
        if (rc == JFileChooser.APPROVE_OPTION) {
            File[] selectedFileList = chooser.getSelectedFiles();
            for (int i = 0; i < selectedFileList.length; ++i) {
                selectedFileList[i] = verifyFileSelection(selectedFileList[i]);
                String entry = selectedFileList[i].getPath();
                addSrcToProject(entry);
            }
        }
    }//GEN-LAST:event_browseSrcDirButtonActionPerformed

    private void srcDirTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_srcDirTextFieldActionPerformed
        addSourceDirToList();
    }//GEN-LAST:event_srcDirTextFieldActionPerformed

    private void jarNameTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jarNameTextFieldActionPerformed
        addJarToList();
    }//GEN-LAST:event_jarNameTextFieldActionPerformed

    private void browseJarButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseJarButtonActionPerformed
        JFileChooser chooser = createFileChooser();
        chooser.setFileFilter(archiveAndDirectoryFilter);
        chooser.setMultiSelectionEnabled(true);
        chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);

        int rc = chooseFile(chooser, L10N.getLocalString("msg.addarchiveordirectory_txt", "Add archive or directory"));
        if (rc == JFileChooser.APPROVE_OPTION) {
            File[] selectedFileList = chooser.getSelectedFiles();
            for (int i = 0; i < selectedFileList.length; ++i) {
                selectedFileList[i] = verifyFileSelection(selectedFileList[i]);
                String entry = selectedFileList[i].getPath();
                addJarToProject(entry);
            }
        }
    }//GEN-LAST:event_browseJarButtonActionPerformed

    private void newProjectItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newProjectItemActionPerformed

        if (!closeProjectHook(getCurrentProject(), L10N.getLocalString("msg.newproject_txt", "New Project")))
            return;

        Project project = new Project();
        setProject(project);
    }//GEN-LAST:event_newProjectItemActionPerformed

    private void exitItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exitItemActionPerformed
        exitFindBugs();
    }//GEN-LAST:event_exitItemActionPerformed

    private void removeSrcDirButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeSrcDirButtonActionPerformed
        Project project = getCurrentProject();
        DefaultListModel listModel = (DefaultListModel) sourceDirList.getModel();

        int[] selIndices = sourceDirList.getSelectedIndices();
        for (int i = selIndices.length - 1; i >= 0; i--) {
            int sel = selIndices[i];
            project.removeSourceDir(sel);
            listModel.remove(sel);
        }
    }//GEN-LAST:event_removeSrcDirButtonActionPerformed

    private void removeJarButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeJarButtonActionPerformed
        Project project = getCurrentProject();
        DefaultListModel listModel = (DefaultListModel) jarFileList.getModel();

        int[] selIndices = jarFileList.getSelectedIndices();
        for (int i = selIndices.length - 1; i >= 0; i--) {
            int sel = selIndices[i];
            project.removeFile(sel);
            listModel.remove(sel);
        }
    }//GEN-LAST:event_removeJarButtonActionPerformed

    private void addSourceDirButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addSourceDirButtonActionPerformed
        addSourceDirToList();
    }//GEN-LAST:event_addSourceDirButtonActionPerformed

    private void addJarButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addJarButtonActionPerformed
        addJarToList();
    }//GEN-LAST:event_addJarButtonActionPerformed

    /**
     * Exit the Application
     */
    private void exitForm(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_exitForm
        exitFindBugs();
    }//GEN-LAST:event_exitForm

    /**
     * This makes the set of controls passed in all the same size, equal to
     * the minimum needed of the largest control.
     */
    private void equalizeControls(JComponent[] components) {
        Dimension d;

        int minX = 0, minY = 0;
        for (JComponent comp : components) {
            comp.setMaximumSize(null);
            comp.setMinimumSize(null);
            comp.setPreferredSize(null);
            d = comp.getPreferredSize();
            if (d.width > minX)
                minX = d.width;
            if (d.height > minY)
                minY = d.height;
        }

        d = new Dimension(minX, minY);
        for (JComponent comp : components) {
            comp.setMinimumSize(d);
            comp.setMaximumSize(d);
            comp.setPreferredSize(d);
        }
    }

    /**
     * This is called whenever the selection is changed in the bug tree.
     *
     * @param e the TreeSelectionEvent
     */
    private void bugTreeSelectionChanged(TreeSelectionEvent e) {

        BugInstance selected = getCurrentBugInstance();
        if (selected != null) {
            synchBugInstance();
        }
    }

    private void openRecentProjectItemActionPerformed(java.awt.event.ActionEvent evt) {
        if (!closeProjectHook(getCurrentProject(), L10N.getLocalString("msg.openproject_txt", "Open Project")))
            return;

        JMenuItem recentProjectItem = (JMenuItem) evt.getSource();
        File file = new File(recentProjectItem.getText());
        try {
            System.setProperty("user.dir", file.getParent());
            Project project = new Project();
            project.read(file.getPath());
            setProject(project);
            UserPreferences.getUserPreferences().useProject(file.getPath());
        } catch (IOException e) {
            UserPreferences.getUserPreferences().removeProject(file.getPath());
            logger.logMessage(Logger.ERROR, MessageFormat.format(L10N.getLocalString("msg.couldnotopenproject_txt", "Could not open project: {0}"), new Object[]{e.getMessage()}));
        } finally {
            rebuildRecentProjectsMenu();
        }
    }

    private boolean moveEntriesUp(JList entryList) {
        int[] selIndices = entryList.getSelectedIndices();
        if (selIndices.length == 0)
            return false;

        boolean changed = false;
        int lastInsertPos = -1;
        DefaultListModel model = (DefaultListModel) entryList.getModel();
        for (int i = 0; i < selIndices.length; i++) {
            int sel = selIndices[i];
            if ((sel - 1) > lastInsertPos) {
                model.add(sel - 1, model.remove(sel));
                selIndices[i] = sel - 1;
                changed = true;
            }
            lastInsertPos = selIndices[i];
        }

        entryList.setSelectedIndices(selIndices);
        return changed;
    }

    private boolean moveEntriesDown(JList entryList) {
        int[] selIndices = entryList.getSelectedIndices();
        if (selIndices.length == 0)
            return false;

        boolean changed = false;
        DefaultListModel model = (DefaultListModel) entryList.getModel();
        int lastInsertPos = model.getSize();
        for (int i = selIndices.length - 1; i >= 0; i--) {
            int sel = selIndices[i];
            if ((sel + 1) < lastInsertPos) {
                model.add(sel + 1, model.remove(sel));
                selIndices[i] = sel + 1;
                changed = true;
            }
            lastInsertPos = selIndices[i];
        }

        entryList.setSelectedIndices(selIndices);
        return changed;
    }

    private void resyncAuxClasspathEntries() {
        Project project = getCurrentProject();
        int numEntries = project.getNumAuxClasspathEntries();
        while (numEntries-- > 0)
            project.removeAuxClasspathEntry(0);

        DefaultListModel model = (DefaultListModel) classpathEntryList.getModel();
        for (int i = 0; i < model.size(); i++)
            project.addAuxClasspathEntry((String) model.get(i));
    }

    private void resyncSourceEntries() {
        Project project = getCurrentProject();
        int numEntries = project.getNumSourceDirs();
        while (numEntries-- > 0)
            project.removeSourceDir(0);

        DefaultListModel model = (DefaultListModel) sourceDirList.getModel();
        for (int i = 0; i < model.size(); i++)
            project.addSourceDir((String) model.get(i));
    }

    /**
     * Localise the given AbstractButton, setting the text and optionally mnemonic
     * Note that AbstractButton includes menus and menu items.
     * @param button        The button to localise
     * @param key           The key to look up in resource bundle
     * @param defaultString default String to use if key not found
     * @param setMnemonic    whether or not to set the mnemonic. According to Sun's
     *                      guidelines, default/cancel buttons should not have mnemonics
     *                      but instead should use Return/Escape
     */
    private void localiseButton(AbstractButton button, String key, String defaultString,
                                boolean setMnemonic) {
        AnnotatedString.localiseButton(button, key, defaultString, setMnemonic);
    }

    /* ----------------------------------------------------------------------
     * Component initialization support
     * ---------------------------------------------------------------------- */

    /**
     * This is called from the constructor to perform post-initialization
     * of the components in the form.
     */
    private void postInitComponents() {
        logger = new ConsoleLogger(this);

        // Add menu items for bug categories to View->Filter Settings menu.
        // These are automatically localized assuming that a
        // BugCategoryDescriptions_<locale>.properties file exists
        // in edu.umd.cs.findbugs.
        Collection<String> bugCategoryCollection = edu.umd.cs.findbugs.I18N.instance().getBugCategories();
        this.bugCategoryCheckBoxList = new JCheckBoxMenuItem[bugCategoryCollection.size()];
        this.bugCategoryList = new String[bugCategoryCollection.size()];
        int count = 0;
        for (String bugCategory : bugCategoryCollection) {
            String bugCategoryDescription = I18N.instance().getBugCategoryDescription(bugCategory);

            final JCheckBoxMenuItem item = new JCheckBoxMenuItem(bugCategoryDescription, true);
            item.setFont(BUTTON_FONT);
            item.setSelected(getFilterSettings().containsCategory(bugCategory));
            item.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent evt) {
                    toggleBugCategory(item);
                }
            });

            filterWarningsMenu.add(item);

            this.bugCategoryCheckBoxList[count] = item;
            this.bugCategoryList[count] = bugCategory;

            ++count;
        }

        viewPanelLayout = (CardLayout) viewPanel.getLayout();

        // List of bug group tabs.
        // This must be in the same order as GROUP_BY_ORDER_LIST!
        bugTreeList = new JTree[]{byClassBugTree, byPackageBugTree, byBugTypeBugTree, byBugCategoryBugTree};

        // Configure bug trees
        for (JTree bugTree : bugTreeList) {
            bugTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
            bugTree.setCellRenderer(BugCellRenderer.instance());
            bugTree.setRootVisible(false);
            bugTree.setShowsRootHandles(true);
            bugTree.addTreeSelectionListener(new TreeSelectionListener() {
                public void valueChanged(TreeSelectionEvent e) {
                    bugTreeSelectionChanged(e);
                }
            });
        }

        jarFileList.setModel(new DefaultListModel());
        sourceDirList.setModel(new DefaultListModel());
        classpathEntryList.setModel(new DefaultListModel());

        // We use a special highlight painter to ensure that the highlights cover
        // complete source lines, even though the source text doesn't
        // fill the lines completely.
        final Highlighter.HighlightPainter painter =
                new DefaultHighlighter.DefaultHighlightPainter(sourceTextArea.getSelectionColor()) {
            @Override
            public Shape paintLayer(Graphics g, int offs0, int offs1,
                    Shape bounds, JTextComponent c, View view) {
                try {
                    Shape extent = view.modelToView(offs0, Position.Bias.Forward, offs1, Position.Bias.Backward, bounds);
                    Rectangle rect = extent.getBounds();
                    rect.x = 0;
                    rect.width = bounds.getBounds().width;
                    g.setColor(getColor());
                    g.fillRect(rect.x, rect.y, rect.width, rect.height);
                    return rect;
                } catch (BadLocationException e) {
                    return null;
                }
            }
        };
        Highlighter sourceHighlighter = new DefaultHighlighter() {
            @Override
            public Object addHighlight(int p0, int p1, Highlighter.HighlightPainter p)
            throws BadLocationException {
                return super.addHighlight(p0, p1, painter);
            }
        };
        sourceTextArea.setHighlighter(sourceHighlighter);

        updateTitle(getCurrentProject());

        // Load the icon for the UMD logo
        ClassLoader classLoader = this.getClass().getClassLoader();
        ImageIcon logoIcon = new ImageIcon(classLoader.getResource("edu/umd/cs/findbugs/gui/logo_umd.png"));
        logoLabel.setIcon(logoIcon);

        // Set common Menu Accelerators
        final int MENU_MASK = getMenuMask();
        newProjectItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, MENU_MASK));
        openProjectItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, MENU_MASK));
        saveProjectItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, MENU_MASK));
        closeProjectItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, MENU_MASK));
        reloadProjectItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, MENU_MASK));

        cutItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, MENU_MASK));
        copyItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, MENU_MASK));
        pasteItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, MENU_MASK));
        selectAllItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, MENU_MASK));

        if (MAC_OS_X) {
            // Some more accelerators that use modifiers. Other platforms
            // tend not to use modifiers for menu accelerators
            saveProjectAsItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, MENU_MASK | Event.SHIFT_MASK));
            loadBugsItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, MENU_MASK | Event.ALT_MASK));
            saveBugsItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, MENU_MASK | Event.ALT_MASK));

            // Leave room for the growBox on Mac
            growBoxSpacer.setMinimumSize(new java.awt.Dimension(16,16));

            // Remove Unnecessary/Redundant menu items.
            fileMenu.remove(exitItem);
            fileMenu.remove(jSeparator6);
            theMenuBar.remove(helpMenu);

            // Set up listeners for Quit and About menu items using
            // Apple's EAWT API.
            // We use reflection here, so there is no posible chance that the
            // class loader will try to load OSXAdapter on a non Mac system
            try {
                Class<?> osxAdapter = Class.forName("edu.umd.cs.findbugs.gui.OSXAdapter");
                Class<?>[] defArgs = {FindBugsFrame.class};
                Method registerMethod = osxAdapter.getDeclaredMethod("registerMacOSXApplication", defArgs);
                if (registerMethod != null) {
                    Object[] args = {this};
                    registerMethod.invoke(osxAdapter, args);
                }
            } catch (NoClassDefFoundError e) {
                // This will be thrown first if the OSXAdapter is loaded on a system without the EAWT
                // because OSXAdapter extends ApplicationAdapter in its def
                System.err.println("This version of Mac OS X does not support the Apple EAWT. Application Menu handling has been disabled (" + e + ")");
            } catch (ClassNotFoundException e) {
                // This shouldn't be reached; if there's a problem with the OSXAdapter we should get the
                // above NoClassDefFoundError first.
                System.err.println("This version of Mac OS X does not support the Apple EAWT. Application Menu handling has been disabled (" + e + ")");
            } catch (Exception e) {
                System.err.println("Exception while loading the OSXAdapter: " + e);
                if (FindBugs.DEBUG) {
                    e.printStackTrace();
                }
            }
        }

    }

    private void rebuildRecentProjectsMenu() {
        UserPreferences prefs = UserPreferences.getUserPreferences();
        final List<String> recentProjects = prefs.getRecentProjects();
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                recentProjectsMenu.removeAll();
                java.awt.Font ft = BUTTON_FONT;
                if (recentProjects.size() == 0) {
                    JMenuItem emptyItem = new JMenuItem(L10N.getLocalString("menu.empty_item", "Empty"));
                    emptyItem.setFont(ft);
                    emptyItem.setEnabled(false);
                    recentProjectsMenu.add(emptyItem);
                } else {
                    for (String recentProject : recentProjects) {
                        JMenuItem projectItem = new JMenuItem(recentProject);
                        projectItem.setFont(ft);
                        projectItem.addActionListener(new ActionListener() {
                            public void actionPerformed(ActionEvent evt) {
                                openRecentProjectItemActionPerformed(evt);
                            }
                        });
                        recentProjectsMenu.add(projectItem);
                    }
                }
            }
        });
    }

    /* ----------------------------------------------------------------------
     * Helpers for accessing and modifying UI components
     * ---------------------------------------------------------------------- */

    /**
     * Based on the current tree selection path, get a user object
     * whose class is the same as the given class.
     *
     * @param tree the tree
     * @param c    the class
     * @return an instance of the given kind of object which is in the
     *         current selection, or null if there is no matching object
     */
    private static <E> E getTreeSelectionOf(JTree tree, Class<E> c) {
        TreePath selPath = tree.getSelectionPath();

        // There may not be anything selected at the moment
        if (selPath == null)
            return null;

        // Work backwards from end until we get to the kind of
        // object we're looking for.
        Object[] nodeList = selPath.getPath();
        for (int i = nodeList.length - 1; i >= 0; --i) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) nodeList[i];
            Object nodeInfo = node.getUserObject();
            if (nodeInfo != null && nodeInfo.getClass() == c)
                return c.cast(nodeInfo);
        }
        return null;
    }

    /**
     * Get the current project.
     */
    private Project getCurrentProject() {
        return currentProject;
    }

    /**
     * Get the current analysis run.
     */
    private AnalysisRun getCurrentAnalysisRun() {
        return currentAnalysisRun;
    }

    /**
     * Get the bug instance currently selected in the bug tree.
     */
    private BugInstance getCurrentBugInstance() {
        JTree bugTree = getCurrentBugTree();
        if (bugTree != null) {
            return getTreeSelectionOf(bugTree, BugInstance.class);
        }
        return null;
    }

    /**
     * Return whether or not the given splitter is "maximized", meaning that
     * the top window of the split has been given all of the space.
     * Note that this window assumes that the split is vertical (meaning
     * that we have top and bottom components).
     *
     * @param splitter the JSplitPane
     * @param evt      the event that is changing the splitter value
     */
    private boolean isSplitterMaximized(JSplitPane splitter, java.beans.PropertyChangeEvent evt) {
        Integer location = (Integer) evt.getNewValue();

        int height = splitter.getHeight();
        int hopefullyMaxDivider = height - (splitter.getDividerSize() + DIVIDER_FUDGE);
        //System.out.println("Splitter: "+(splitter==consoleSplitter?"consoleSplitter":"bugTreeBugDetailsSplitter")+
        //    ": height="+height+",location="+location+
        //    ",hopefullyMax="+hopefullyMaxDivider);
        boolean isMaximized = location.intValue() >= hopefullyMaxDivider;
        return isMaximized;
    }

    private void checkBugDetailsVisibility() {
        if (viewBugDetailsItem.isSelected()) {
            bugTreeBugDetailsSplitter.resetToPreferredSizes();
        } else {
            bugTreeBugDetailsSplitter.setDividerLocation(1.0);
        }
        //System.out.("New bug detail splitter location " + bugTreeBugDetailsSplitter.getDividerLocation());
    }

    private JTree getCurrentBugTree() {
        JScrollPane selected = (JScrollPane) groupByTabbedPane.getSelectedComponent();
        Object view = selected.getViewport().getView();
        if (view instanceof JTree) {
            return (JTree) view;
        }
        return null;
    }

    /* ----------------------------------------------------------------------
     * Synchronization of data model and UI
     * ---------------------------------------------------------------------- */

    /**
     * Set the priority threshold for display of bugs in the bug tree.
     *
     * @param threshold the threshold
     */
    private void setPriorityThreshold(int threshold) {
        if (threshold != getFilterSettings().getMinPriorityAsInt()) {
            getFilterSettings().setMinPriority(ProjectFilterSettings.getIntPriorityAsString(threshold));
            if (currentAnalysisRun != null)
                synchAnalysisRun(currentAnalysisRun);
        }
    }

    private void ableEditMenu() {
        String view = getView();
        if ((view != null) && view.equals("EditProjectPanel")) {
            if (selectedComponent != null) {
                boolean hasSelection = false;
                if (selectedComponent instanceof JList) {
                    JList list = (JList)selectedComponent;
                    hasSelection = list.getSelectedIndices().length > 0;
                } else if (selectedComponent instanceof JTextField) {
                    JTextField tf = (JTextField)selectedComponent;
                    hasSelection = ((tf.getSelectedText() != null) &&
                            (tf.getSelectedText().length() > 0));
                }

                cutItem.setEnabled(hasSelection);
                copyItem.setEnabled(hasSelection);
                selectAllItem.setEnabled(true);
            }
            //            } else if (view.equals("BugTree")) {
            //            } else if (view.equals("ReportPanel")) {

        } else {
            cutItem.setEnabled(false);
            copyItem.setEnabled(true);
            pasteItem.setEnabled(false);
            selectAllItem.setEnabled(false);
        }
    }

    private void setProject(Project project) {
        currentProject = project;
        if (project != null) {
            synchProject(project);
            setView("EditProjectPanel");
            editMenu.setEnabled(true);
            viewProjectItem.setEnabled(true);
            viewProjectItem.setSelected(true);
            viewBugsItem.setEnabled(false);
            viewBugsItem.setSelected(false);
        } else {
            editMenu.setEnabled(false);
            viewProjectItem.setEnabled(false);
            viewProjectItem.setSelected(false);
            viewBugsItem.setEnabled(false);
            viewBugsItem.setSelected(false);
            setView("EmptyPanel");
        }
        updateTitle(project);
        ableEditMenu();
    }

    private void updateTitle(Project project) {
        if (project == null)
            this.setTitle(L10N.getLocalString("dlg.noproject_lbl", "FindBugs - no project"));
        else
            this.setTitle("FindBugs - " + project.toString());
    }

    /**
     * Save given project.
     * If the project already has a valid filename, use that filename.
     * Otherwise, prompt for one.
     *
     * @param project     the Project to save
     * @param dialogTitle the title for the save dialog (if needed)
     */
    private boolean saveProject(Project project, String dialogTitle) {
        return saveProject(project, dialogTitle, false);
    }

    /**
     * Offer to save the current Project to a file.
     *
     * @param project        the Project to save
     * @param dialogTitle    the title for the save dialog (if needed)
     * @param chooseFilename if true, force a dialog to prompt the user
     *                       for a filename
     * @return true if the project is saved successfully, false if the user
     *         cancels or an error occurs
     */
    private boolean saveProject(Project project, String dialogTitle, boolean chooseFilename) {
        boolean useRelativePaths;
        try {
            if (project == null)
                return true;

            File file;
            String fileName = project.getProjectFileName();

            if (!fileName.startsWith("<") && !chooseFilename) {
                file = new File(fileName);
                useRelativePaths = project.getOption( Project.RELATIVE_PATHS );
            } else {
                JRadioButton relativePaths = new JRadioButton(L10N.getLocalString("msg.userelativepaths_txt", "Use Relative Paths"));
                relativePaths.setSelected(project.getOption(Project.RELATIVE_PATHS));
                JFileChooser chooser = createFileChooser(relativePaths);
                chooser.setFileFilter(projectFileFilter);
                int result = chooseFile(chooser, dialogTitle);
                if (result == JFileChooser.CANCEL_OPTION)
                    return false;
                file = chooser.getSelectedFile();
                fileName = Project.transformFilename(file.getPath());
                file = new File(fileName);
                useRelativePaths = relativePaths.isSelected();
            }

            project.write(file.getPath(), useRelativePaths, file.getParent());
            logger.logMessage(Logger.INFO, "Project saved");
            project.setProjectFileName(file.getPath());

            UserPreferences prefs = UserPreferences.getUserPreferences();
            prefs.useProject(file.getPath());
            prefs.read();
            rebuildRecentProjectsMenu();

            updateTitle(project);

            return true;
        } catch (IOException e) {
            logger.logMessage(Logger.ERROR, "Could not save project: " + e.toString());
            JOptionPane.showMessageDialog(this, "Error saving project: " + e.toString(),
                    "Error", JOptionPane.ERROR_MESSAGE);
            return false;
        }
    }

    /**
     * Hook to call before closing a project.
     *
     * @param project         the project being closed
     * @param savePromptTitle title to use for the "Save project?" dialog
     * @return true if user has confirmed that the project should be closed,
     *         false if the close is cancelled
     */
    private boolean closeProjectHook(Project project, String savePromptTitle) {
        if (project == null || !project.isModified())
            return true;

        // Confirm that the project should be closed.
        int option = JOptionPane.showConfirmDialog(this, L10N.getLocalString("msg.saveprojectquery_txt", "Save Project?"), savePromptTitle,
                JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);

        if (option == JOptionPane.CANCEL_OPTION)
            return false;
        else if (option == JOptionPane.YES_OPTION) {
            boolean result = saveProject(project, "Save Project");
            if (result)
                JOptionPane.showMessageDialog(this, "Project was successfully saved.");
            return result;
        } else
            return true;
    }

    /**
     * Synchronize the edit project dialog with given project.
     *
     * @param project the selected project
     */
    private void synchProject(Project project) {
        // Clear text fields
        jarNameTextField.setText("");
        srcDirTextField.setText("");
        classpathEntryTextField.setText("");

        // Populate jar file, source directory, and aux classpath entry lists

        DefaultListModel jarListModel = (DefaultListModel) jarFileList.getModel();
        jarListModel.clear();
        for (int i = 0; i < project.getFileCount(); ++i) {
            jarListModel.addElement(project.getFile(i));
        }

        DefaultListModel srcDirListModel = (DefaultListModel) sourceDirList.getModel();
        srcDirListModel.clear();
        for (int i = 0; i < project.getNumSourceDirs(); ++i) {
            srcDirListModel.addElement(project.getSourceDir(i));
        }

        DefaultListModel classpathEntryListModel = (DefaultListModel) classpathEntryList.getModel();
        classpathEntryListModel.clear();
        for (int i = 0; i < project.getNumAuxClasspathEntries(); ++i) {
            classpathEntryListModel.addElement(project.getAuxClasspathEntry(i));
        }
    }

    /**
     * Synchronize the bug trees with the given analysisRun object.
     *
     * @param analysisRun the selected analysis run
     */
    private void synchAnalysisRun(AnalysisRun analysisRun) {
        // Create and populate tree models
        for (int i = 0; i < GROUP_BY_ORDER_LIST.length; ++i) {
            DefaultMutableTreeNode bugRootNode = new DefaultMutableTreeNode();
            DefaultTreeModel bugTreeModel = new DefaultTreeModel(bugRootNode);

            String groupByOrder = GROUP_BY_ORDER_LIST[i];
            analysisRun.setTreeModel(groupByOrder, bugTreeModel);
            populateAnalysisRunTreeModel(analysisRun, groupByOrder);
            if (i < bugTreeList.length)
                bugTreeList[i].setModel(bugTreeModel);
        }

        currentAnalysisRun = analysisRun;

        //set the summary output
        setSummary(analysisRun.getSummary());
        setView("BugTree");
    }

    private void setSummary(String summaryXML) {
        bugSummaryEditorPane.setContentType("text/html");
        /*
        bugSummaryEditorPane.setText(summaryXML);
        //      : unfortunately, using setText() on the editor pane
        // results in the contents being scrolled to the bottom of the pane.
        // An immediate inline call to set the scroll position does nothing.
        // So, use invokeLater(), even though this results in flashing.
        // [What we really need is a way to set the text WITHOUT changing
        // the caret position.    Need to investigate.]
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                bySummary.getViewport().setViewPosition(new Point(0, 0));
            }
        });
        */
        StringReader reader = null;
        try {
            if (summaryXML != null) {
                reader = new StringReader(summaryXML); // no need for BufferedReader
                bugSummaryEditorPane.read(reader, "html summary");
            } else {
                bugSummaryEditorPane.setText("");
            }
        } catch (IOException e) {
            bugSummaryEditorPane.setText("Could not set summary: " + e.getMessage());
            logger.logMessage(Logger.WARNING, e.getMessage());
        } finally {
            if (reader != null)
                reader.close(); // polite, but doesn't do much in StringReader
        }

    }

    /**
     * Populate an analysis run's tree model for given sort order.
     */
    private void populateAnalysisRunTreeModel(AnalysisRun analysisRun, final String groupBy) {
        //System.out.println("Populating bug tree for order " + groupBy);

        // Set busy cursor - this is potentially a time-consuming operation
        Cursor orig = this.getCursor();
        this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

        final DefaultTreeModel bugTreeModel = analysisRun.getTreeModel(groupBy);
        final DefaultMutableTreeNode bugRootNode = (DefaultMutableTreeNode) bugTreeModel.getRoot();

        // Delete all children from root node
        bugRootNode.removeAllChildren();

        // Sort the instances (considering only those that meet the
        // priority threshold)
        TreeSet<BugInstance> sortedCollection = new TreeSet<BugInstance>(getBugInstanceComparator(groupBy));
        for (BugInstance bugInstance : analysisRun.getBugInstances()) {
            if (getFilterSettings().displayWarning(bugInstance))
                sortedCollection.add(bugInstance);
        }

        // The grouper callback is what actually adds the group and bug
        // nodes to the tree.
        Grouper.Callback<BugInstance> callback = new Grouper.Callback<BugInstance>() {
            private BugInstanceGroup currentGroup;
            private DefaultMutableTreeNode currentGroupNode;

            public void startGroup(BugInstance member) {
                String groupName;
                if (groupBy == GROUP_BY_CLASS)
                    groupName = member.getPrimaryClass().getClassName();
                else if (groupBy == GROUP_BY_PACKAGE) {
                    groupName = member.getPrimaryClass().getPackageName();
                    if (groupName.equals(""))
                        groupName = "Unnamed package";
                } else if (groupBy == GROUP_BY_BUG_TYPE) {
                    String desc = member.toString();
                    String shortBugType = desc.substring(0, desc.indexOf(':'));
                    String bugTypeDescription = I18N.instance().getBugTypeDescription(shortBugType);
                    groupName = shortBugType + ": " + bugTypeDescription;
                } else if (groupBy == GROUP_BY_BUG_CATEGORY) {
                    BugPattern pattern = member.getBugPattern();
                    if (pattern == null) {
                        if (FindBugs.DEBUG)
                            System.out.println("Unknown bug pattern " + member.getType());
                        groupName = "Unknown category";
                    } else {
                        groupName = I18N.instance().getBugCategoryDescription(pattern.getCategory());
                    }
                } else
                    throw new IllegalStateException("Unknown sort order: " + groupBy);
                currentGroup = new BugInstanceGroup(groupBy, groupName);
                currentGroupNode = new DefaultMutableTreeNode(currentGroup);
                bugTreeModel.insertNodeInto(currentGroupNode, bugRootNode, bugRootNode.getChildCount());

                insertIntoGroup(member);
            }

            public void addToGroup(BugInstance member) {
                insertIntoGroup(member);
            }

            private void insertIntoGroup(BugInstance member) {
                int count = currentGroup.getMemberCount();
                currentGroup.incrementMemberCount();
                BugTreeNode bugNode = new BugTreeNode(member);
                if (BUG_COUNT)
                    bugNode.setCount(count);
                bugTreeModel.insertNodeInto(bugNode, currentGroupNode, currentGroupNode.getChildCount());

                // Insert annotations
                Iterator<BugAnnotation> j = member.annotationIterator();
                while (j.hasNext()) {
                    BugAnnotation annotation = j.next();
                    DefaultMutableTreeNode annotationNode = new DefaultMutableTreeNode(annotation);
                    bugTreeModel.insertNodeInto(annotationNode, bugNode, bugNode.getChildCount());
                }

            }
        };

        // Create the grouper, and execute it to populate the bug tree
        Grouper<BugInstance> grouper = new Grouper<BugInstance>(callback);
        Comparator<BugInstance> groupComparator = getGroupComparator(groupBy);
        grouper.group(sortedCollection, groupComparator);

        // Let the tree know it needs to update itself
        bugTreeModel.nodeStructureChanged(bugRootNode);

        // Now we're done
        this.setCursor(orig);
    }

    /**
     * Get a BugInstance Comparator for given sort order.
     */
    private Comparator<BugInstance> getBugInstanceComparator(String sortOrder) {
        if (sortOrder.equals(GROUP_BY_CLASS))
            return bugInstanceByClassComparator;
        else if (sortOrder.equals(GROUP_BY_PACKAGE))
            return bugInstanceByPackageComparator;
        else if (sortOrder.equals(GROUP_BY_BUG_TYPE))
            return bugInstanceByTypeComparator;
                else if (sortOrder.equals(GROUP_BY_BUG_CATEGORY))
                        return bugInstanceByCategoryComparator;
        else
            throw new IllegalArgumentException("Bad sort order: " + sortOrder);
    }

    /**
     * Get a Grouper for a given sort order.
     */
    private Comparator<BugInstance> getGroupComparator(String groupBy) {
        if (groupBy.equals(GROUP_BY_CLASS)) {
            return bugInstanceClassComparator;
        } else if (groupBy.equals(GROUP_BY_PACKAGE)) {
            return bugInstancePackageComparator;
        } else if (groupBy.equals(GROUP_BY_BUG_TYPE)) {
            return bugInstanceTypeComparator;
        } else if (groupBy.equals(GROUP_BY_BUG_CATEGORY)) {
            return bugInstanceCategoryComparator;
        } else
            throw new IllegalArgumentException("Bad sort order: " + groupBy);
    }

    /**
     * Set the view panel to display the named view.
     */
    private void setView(String viewName) {
        //System.out.println("Showing view " + viewName);
        viewPanelLayout.show(viewPanel, viewName);
        boolean viewingBugs = viewName.equals("BugTree");
        if (viewingBugs)
            checkBugDetailsVisibility();

        viewProjectItem.setSelected(!viewingBugs);
        if (viewingBugs)
            viewBugsItem.setEnabled(true);
        viewBugsItem.setSelected(viewingBugs);

        currentView = viewName;
        ableEditMenu();
    }

    /**
     * Get which view is displayed currently.
     */
    private String getView() {
        return currentView;
    }

    /**
     * Called to add the jar file in the jarNameTextField to the
     * Jar file list (and the project it represents).
     */
    private void addJarToList() {
        String dirs = jarNameTextField.getText();
        String[] jarDirs = parsePaths(dirs);
        for (String jarFile : jarDirs) {
            if (!jarFile.equals("")) {
                addJarToProject(jarFile);
            }
        }
        jarNameTextField.setText("");
    }

    /**
     * Add a Src file to the current project.
     *
     * @param srcFile the jar file to add to the project
     */
    private void addSrcToProject(String srcFile) {
        Project project = getCurrentProject();
        if (project.addSourceDir(srcFile)) {
            DefaultListModel listModel = (DefaultListModel) sourceDirList.getModel();
            listModel.addElement(srcFile);
        }
    }

    /**
     * Add a Jar file to the current project.
     *
     * @param jarFile the jar file to add to the project
     */
    private void addJarToProject(String jarFile) {
        Project project = getCurrentProject();
        if (project.addFile(jarFile)) {
            DefaultListModel listModel = (DefaultListModel) jarFileList.getModel();
            listModel.addElement(jarFile);
        }
    }

    /**
     * Parses a classpath into it's sub paths
     *
     * @param path the classpath
     * @return an array of paths
     */
    private String[] parsePaths(String paths) {
        return paths.split(SystemProperties.getProperty("path.separator"));
    }

    /**
     * Called to add the source directory in the sourceDirTextField
     * to the source directory list (and the project it represents).
     */
    private void addSourceDirToList() {
        String dirs = srcDirTextField.getText();
        String[] sourceDirs = parsePaths(dirs);
        for (String sourceDir : sourceDirs) {
            if (!sourceDir.equals("")) {
                Project project = getCurrentProject();
                if (project.addSourceDir(sourceDir)) {
                    DefaultListModel listModel = (DefaultListModel) sourceDirList.getModel();
                    listModel.addElement(sourceDir);
                }
            }
        }
        srcDirTextField.setText("");
    }

    /**
     * Called to add the classpath entry in the classpathEntryTextField
     * to the classpath entry list (and the project it represents).
     */
    private void addClasspathEntryToList() {
        String dirs = classpathEntryTextField.getText();
        String[] classDirs = parsePaths(dirs);
        for (String classpathEntry : classDirs) {
            if (!classpathEntry.equals("")) {
                addClasspathEntryToProject(classpathEntry);
            }
        }
        classpathEntryTextField.setText("");
    }

    /**
     * Add a classpath entry to the current project.
     *
     * @param classpathEntry the classpath entry to add
     */
    private void addClasspathEntryToProject(String classpathEntry) {
        Project project = getCurrentProject();
        if (project.addAuxClasspathEntry(classpathEntry)) {
            DefaultListModel listModel = (DefaultListModel) classpathEntryList.getModel();
            listModel.addElement(classpathEntry);
        }
    }

    /**
     * Synchronize current bug instance with the bug detail
     * window (source view, details window, etc.)
     */
    private void synchBugInstance() {
        // Get current bug instance
        BugInstance selected = getCurrentBugInstance();
        if (selected == null)
            return;

        // If the details window is minimized, then the user can't see
        // it and there is no point in updating it.
        if (!viewBugDetailsItem.isSelected())
            return;

        // Get the current source line annotation.
        // If the current leaf selected is not a source line annotation,
        // or a method annotation containing a source line annotation.
        // use the default source line annotation from the current bug instance
        // (if any).
        JTree bugTree = getCurrentBugTree();

        // if the summary window is shown then skip it
        if (bugTree == null) {
            return;
        }
        SourceLineAnnotation srcLine = null;
        TreePath selPath = bugTree.getSelectionPath();
        if (selPath != null) {
            Object leaf = ((DefaultMutableTreeNode) selPath.getLastPathComponent()).getUserObject();
            if (leaf instanceof SourceLineAnnotation)
                srcLine = (SourceLineAnnotation) leaf;
            else if (leaf instanceof BugAnnotationWithSourceLines)
                srcLine = ((BugAnnotationWithSourceLines) leaf).getSourceLines();

            if (srcLine == null)
                srcLine = selected.getPrimarySourceLineAnnotation();
        }

        // Show source code.
        if (srcLine == null || srcLine != currentSourceLineAnnotation) {
            Project project = getCurrentProject();
            AnalysisRun analysisRun = getCurrentAnalysisRun();
            if (project == null) throw new IllegalStateException("null project!");
            if (analysisRun == null) throw new IllegalStateException("null analysis run!");
            try {
                boolean success = viewSource(project, analysisRun, srcLine);
                sourceTextArea.setEnabled(success);
                if (!success)
                    sourceTextArea.setText("No source line information for this bug");
            } catch (IOException e) {
                sourceTextArea.setText("Could not find source: " + e.getMessage());
                logger.logMessage(Logger.WARNING, e.getMessage());
            }

            currentSourceLineAnnotation = srcLine;
        }

        // Show bug info.
        showBugInfo(selected);

        // Synch annotation text.
        synchBugAnnotation(selected);

        // Now the bug details are up to date.
        currentBugInstance = selected;
    }

    private static final int SELECTION_VOFFSET = 2;

    /**
     * Update the source view window.
     *
     * @param project     the project (containing the source directories to search)
     * @param analysisRun the analysis run (containing the mapping of classes to source files)
     * @param srcLine     the source line annotation (specifying source file to load and
     *                    which lines to highlight)
     * @return true if the source was shown successfully, false otherwise
     */
    private boolean viewSource(Project project, AnalysisRun analysisRun, final SourceLineAnnotation srcLine)
    throws IOException {
        // Get rid of old source code text
        sourceTextArea.setText("");

        // There is nothing to do without a source annotation
        // TODO: actually, might want to put a message in the source window
        // explaining that we don't have the source file, and that
        // they might want to recompile with debugging info turned on.
        if (srcLine == null)
            return false;

        SourceFinder sourceFinder = project.getSourceFinder();

        // Look up the source file for this class.
        String sourceFile;
        InputStream in;
        try {
            SourceFile source = sourceFinder.findSourceFile(srcLine);
            sourceFile = source.getFullFileName();
            in = source.getInputStream();
        } catch (IOException e) {
            logger.logMessage(Logger.WARNING, "No source file for class " + srcLine.getClassName());
            return false;
        }

        BufferedReader reader = null;

        try {
            reader = new BufferedReader(new InputStreamReader(in));
            sourceTextArea.read(reader, sourceFile); // 2nd arg is mostly ignored
        } finally {
            if (reader != null)
                reader.close();
        }

        if (srcLine.isUnknown()) {
            // No line number information, so can't highlight anything

            // There was code here to scroll to the top, but that isn't
            // needed because sourceTextArea.read() does that for us.
            return true;
        }

        // Highlight the annotation.
        // There seems to be some bug in Swing that sometimes prevents this code
        // from working when executed immediately after populating the
        // text in the text area.  My guess is that when a large amount of text
        // is added, Swing defers some UI update work until "later" that is needed
        // to compute the visibility of text in the text area.
        // So, post some code to do the update to the Swing event queue.
        // Not really an ideal solution, but it seems to work.
        // note: Could reimplement this to use sourceTextArea.scrollRectToVisible(),
        // but if it ain't broke...
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                // Highlight the lines from the source annotation.
                // Note that the source lines start at 1, while the line numbers
                // in the text area start at 0.
                try {
                    int startLine = srcLine.getStartLine() - 1;
                    int endLine = srcLine.getEndLine();

                    // Compute number of rows visible.
                    // What a colossal pain in the ass this is.
                    // You'd think there would be a convenient method to
                    // return this information, but no.
                    JViewport viewport = sourceTextAreaScrollPane.getViewport();
                    Rectangle viewportRect = viewport.getViewRect();
                    int topRow = sourceTextArea.getLineOfOffset(sourceTextArea.viewToModel(viewportRect.getLocation()));
                    int bottomRow = sourceTextArea.getLineOfOffset(sourceTextArea.viewToModel(new Point(viewportRect.x, (viewportRect.y + viewportRect.height) - 1)));
                    int numRowsVisible = bottomRow - topRow;

                    // Scroll the window so the beginning of the
                    // annotation text will be (approximately) centered.
                    int viewLine = Math.max(startLine - (numRowsVisible > 0 ? numRowsVisible / 2 : 0), 0);
                    int viewBegin = sourceTextArea.getLineStartOffset(viewLine);
                    Rectangle viewRect = sourceTextArea.modelToView(viewBegin);
                    viewport.setViewPosition(new Point(viewRect.x, viewRect.y));

                    // Select (and highlight) the annotation.
                    int selBegin = sourceTextArea.getLineStartOffset(startLine);
                    int selEnd = sourceTextArea.getLineStartOffset(endLine);
                    sourceTextArea.select(selBegin, selEnd);
                    sourceTextArea.getCaret().setSelectionVisible(true);
                } catch (javax.swing.text.BadLocationException e) {
                    logger.logMessage(Logger.ERROR, e.toString());
                }
            }
        });

        return true;
    }

    /**
     * Show descriptive text about the type of bug
     *
     * @param bugInstance the bug instance
     */
    private void showBugInfo(BugInstance bugInstance) {
        // Are we already showing details for this kind of bug?
        String bugDetailsKey = bugInstance.getType();
        if (bugDetailsKey.equals(currentBugDetailsKey))
            return;

        // Display the details
        String html = I18N.instance().getDetailHTML(bugDetailsKey);
        bugDescriptionEditorPane.setContentType("text/html");
        currentBugDetailsKey = bugDetailsKey;
        /*
        bugDescriptionEditorPane.setText(html);

        //      : unfortunately, using setText() on the editor pane
        // results in the contents being scrolled to the bottom of the pane.
        // An immediate inline call to set the scroll position does nothing.
        // So, use invokeLater(), even though this results in flashing.
        // [What we really need is a way to set the text WITHOUT changing
        // the caret position.    Need to investigate.]
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                bugDescriptionScrollPane.getViewport().setViewPosition(new Point(0, 0));
            }
        });
        */
        StringReader reader = new StringReader(html); // no need for BufferedReader
        try {
            bugDescriptionEditorPane.read(reader, "html bug description");
        } catch (IOException e) {
            bugDescriptionEditorPane.setText("Could not find bug description: " + e.getMessage());
            logger.logMessage(Logger.WARNING, e.getMessage());
        } finally {
            reader.close(); // polite, but doesn't do much in StringReader
        }
    }

    /**
     * Synchronize the bug annotation text with the current bug instance,
     * and update the annotation text with the new bug instance.
     *
     * @param selected the new BugInstance
     */
    private void synchBugAnnotation(BugInstance selected) {
        if (currentBugInstance != null) {
            String text = annotationTextArea.getText();
            try {
                currentBugInstance.setAnnotationText(text, null);
            } catch (SignInCancelledException e) {
                logger.logMessage(Logger.WARNING, e.getMessage());
            }
        }

        //annotationTextArea.setText(selected.getAnnotationText());
        String userAnnotation = selected.getAnnotationText();
        if (userAnnotation==null || userAnnotation.length()==0) {
            // this is the common case, so might as well optimize it
            annotationTextArea.setText("");
            return;
        }
        StringReader reader = new StringReader(userAnnotation); // no need for BufferedReader
        try {
            annotationTextArea.read(reader, "user annotation");
        } catch (IOException e) {
            annotationTextArea.setText("Could not find user annotation: " + e.getMessage());
            logger.logMessage(Logger.WARNING, e.getMessage());
        } finally {
            reader.close(); // polite, but doesn't do much in StringReader
        }
    }

    /**
     * Toggle a bug category checkbox.
     * Changes are reflected in the displayed bug trees (if any)
     * and also in the user preferences.
     *
     * @param checkBox the bug category checkbox
     */
    private void toggleBugCategory(JCheckBoxMenuItem checkBox) {
        int index = 0;

        while (index < bugCategoryCheckBoxList.length) {
            if (bugCategoryCheckBoxList[index] == checkBox)
                break;
            ++index;
        }

        if (index == bugCategoryCheckBoxList.length) {
            error("Could not find bug category checkbox");
            return;
        }

        boolean selected = checkBox.isSelected();
        String bugCategory = bugCategoryList[index];

        if (selected) {
            getFilterSettings().addCategory(bugCategory);
        } else {
            getFilterSettings().removeCategory(bugCategory);
        }

        if (currentAnalysisRun != null) {
            synchAnalysisRun(currentAnalysisRun);
        }
    }

    /* ----------------------------------------------------------------------
     * Misc. helpers
     * ---------------------------------------------------------------------- */

    /**
     * Show About
     */
    void about() {
        AboutDialog dialog = new AboutDialog(this, logger, true);
        dialog.setSize(600, 554);
        dialog.setLocationRelativeTo(null); // center the dialog
        dialog.setVisible(true);
    }

    /**
     * Exit the application.
     */
    @SuppressWarnings("DM_EXIT")
    void exitFindBugs() {
        // TODO: offer to save work, etc.
//        UserPreferences.getUserPreferences().storeUserDetectorPreferences();
        UserPreferences.getUserPreferences().write();
        System.exit(0);
    }

    /**
     * Create a file chooser dialog.
     * Ensures that the dialog will start in the current directory.
     *
     * @return the file chooser
     */
    private JFileChooser createFileChooser() {
        return new JFileChooser(currentDirectory);
    }

    /**
     * Create a file chooser dialog.
     * Ensures that the dialog will start in the current directory.
     *
     * @param extraComp The extra component to append to the dialog
     * @return the file chooser
     */
    private JFileChooser createFileChooser(final JComponent extraComp) {
        return new JFileChooser(currentDirectory) {
            private static final long serialVersionUID = 1L;

            @Override
            protected JDialog createDialog(Component parent) throws HeadlessException {
                JDialog dialog = super.createDialog(parent);
                dialog.getContentPane().add(extraComp, BorderLayout.SOUTH);
                dialog.setLocation(300, 200);
                dialog.setResizable(false);
                return dialog;
            }
        };
    }

    /**
     * Run a file chooser dialog.
     * If a file is chosen, then the current directory is updated.
     *
     * @param dialog      the file chooser dialog
     * @param dialogTitle the dialog title
     * @return the outcome
     */
    private int chooseFile(JFileChooser dialog, String dialogTitle) {
        int outcome = dialog.showDialog(this, dialogTitle);
        return updateCurrentDirectoryFromDialog(dialog, outcome);
    }

    /**
     * Run a file chooser dialog to choose a file to open.
     * If a file is chosen, then the current directory is updated.
     *
     * @param dialog the file chooser dialog
     * @return the outcome
     */
    private int chooseFileToOpen(JFileChooser dialog) {
        int outcome = dialog.showOpenDialog(this);
        return updateCurrentDirectoryFromDialog(dialog, outcome);
    }

    private int updateCurrentDirectoryFromDialog(JFileChooser dialog, int outcome) {
        if (outcome != JFileChooser.CANCEL_OPTION) {
            File selectedFile = dialog.getSelectedFile();
            currentDirectory = selectedFile.getParentFile();
        }
        return outcome;
    }

    /**
     * Get the Logger.
     */
    public Logger getLogger() {
        return logger;
    }

    /**
     * Show an error dialog.
     */
    public void error(String message) {
        JOptionPane.showMessageDialog(this, message, "Error", JOptionPane.ERROR_MESSAGE);
    }

    /**
     * Write a message to the console window.
     *
     * @param message the message to write
     */
    public void writeToLog(String message) {
//        consoleMessageArea.append(message);
//        consoleMessageArea.append("\n");
    }

    /**
     * Fix up the path that is received from JFileChooser, if necessary
     * Double clicking a directory causes a repeated name, for some reason
     * such as a:\b\c\c when a:\b\c was chosen
     */
    public File verifyFileSelection(File pickedFile) {
        if (pickedFile.exists())
            return pickedFile;

        File parent = pickedFile.getParentFile();
        if ((parent != null) && parent.getName().equals(pickedFile.getName()))
            return parent;

        //Something bad has happened
        return pickedFile;
    }

    /**
     * Get the current ProjectFilterSettings.
     */
    public ProjectFilterSettings getFilterSettings() {
        return UserPreferences.getUserPreferences().getFilterSettings();
    }

    /**
     * Get the current priority threshold.
     */
    public int getPriorityThreshold() {
        return getFilterSettings().getMinPriorityAsInt();
    }

    /**
     * Get list of AnalysisFeatureSettings.
     *
     * @return list of AnalysisFeatureSettings
     */
    public AnalysisFeatureSetting[] getSettingList() {
        return settingList;
    }

    /* ----------------------------------------------------------------------
     * main() method
     * ---------------------------------------------------------------------- */

    private static class SwingCommandLine extends FindBugsCommandLine {
        public SwingCommandLine() {
            addSwitch("-debug", "enable debug output");
            addSwitchWithOptionalExtraPart("-look", "plastic|gtk|native", "set look and feel");
            addOption("-project", "project file", "load given project");
            addOption("-priority", "thread priority",
                      "set analysis thread's priority level (default is " +
                      (Thread.NORM_PRIORITY-1) + ")");
            addOption("-loadbugs", "bugs xml filename", "load given bugs xml file");
        }

        String bugsFilename = "";

        public String getBugsFilename() {
            return bugsFilename;
        }

        // Thread priority for the analysis thread.  The default is
        // just below the priority of the GUI
        private int priority = Thread.NORM_PRIORITY-1;

        /**
         * Retrieve thread priority for the analysis thread.
         * @return thread priority for the analysis thread
         */
        public int getPriority() {
            return priority;
        }

        @Override
        protected void handleOption(String option, String optionExtraPart) {
            if (option.equals("-debug")) {
                System.out.println("Setting findbugs.debug=true");
                System.setProperty("findbugs.debug", "true");
            } else if (option.equals("-look")) {
                String arg = optionExtraPart;

                String theme = null;
                if (arg.equals("plastic")) {
                    // You can get the Plastic look and feel from jgoodies.com:
                    //    http://www.jgoodies.com/downloads/libraries.html
                    // Just put "plastic.jar" in the lib directory, right next
                    // to the other jar files.
                    theme = "com.jgoodies.plaf.plastic.PlasticXPLookAndFeel";
                } else if (arg.equals("gtk")) {
                    theme = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel";
                } else if (arg.equals("native")) {
                    theme = UIManager.getSystemLookAndFeelClassName();
                } else {
                    System.err.println("Style '" + arg + "' not supported");
                }

                if (theme != null) {
                    try {
                        UIManager.setLookAndFeel(theme);
                    } catch (Exception e) {
                        System.err.println("Couldn't load " + arg +
                                " look and feel: " + e.toString());
                    }
                }
            } else {
                super.handleOption(option, optionExtraPart);
            }
        }


        @Override
        protected void handleOptionWithArgument(String option, String argument) throws IOException {
            if (option.equals("-loadbugs")) {
                bugsFilename = argument;
            } else if (option.equals("-priority")) {
                int num;
                try {
                    num = Integer.parseInt(argument);
                } catch(NumberFormatException e) {
                    num = Thread.NORM_PRIORITY-1;
                }
                priority = num;
            } else {
                super.handleOptionWithArgument(option, argument);
            }
        }
    }

    /**
     * Invoke from the command line.
     *
     * @param args the command line arguments
     * @throws IOException
     */
    public static void main(String args[]) throws IOException {
        Project project = null;

        SwingCommandLine commandLine = new SwingCommandLine();
        try {
            commandLine.parse(args);
        } catch (IllegalArgumentException e) {
            System.err.println("Error: " + e.getMessage());
            showSynopsis();
            ShowHelp.showGeneralOptions();
            showCommandLineOptions();
            System.exit(1);
        } catch (HelpRequestedException e) {
            showSynopsis();
            ShowHelp.showGeneralOptions();
            showCommandLineOptions();
            System.exit(1);
        }

        if (commandLine.getProject().getFileCount() > 0) {
            project = commandLine.getProject();
        }

        //      Uncomment one of these to test I18N
        //        Locale.setDefault( Locale.FRENCH );
        //        Locale.setDefault( Locale.GERMAN );
        //        Locale.setDefault( Locale.JAPANESE );
        //        Locale.setDefault( new Locale( "et" ));
        //        Locale.setDefault( new Locale( "fi" ));
        //        Locale.setDefault( new Locale( "es" ));
        //        Locale.setDefault( new Locale( "pl" ));

        // Load plugins!
        DetectorFactoryCollection.instance();

        FindBugsFrame frame = new FindBugsFrame();

        if (project != null) {
            frame.setProject(project);
        } else if (commandLine.getBugsFilename().length() > 0) {
            try {
                File bugsFile = new File(commandLine.getBugsFilename());
                frame.loadBugsFromFile(bugsFile);
            } catch (Exception e) {
                System.err.println("Error: " + e.getMessage());
            }
        } else if (SystemProperties.getProperty("findbugs.loadBugsFromURL") != null) {
            // Allow JNLP launch to specify the URL of a report to load
            try {
                String urlspec = SystemProperties.getProperty("findbugs.loadBugsFromURL");
                frame.loadBugsFromURL(urlspec);
            } catch (Exception e) {
                System.err.println("Error: " + e.getMessage());
            }
        }

        frame.setPriority(commandLine.getPriority());

        if (commandLine.getSettingList() != null) {
            frame.settingList = commandLine.getSettingList();
            if (Arrays.equals(frame.settingList,FindBugs.MIN_EFFORT))
                frame.minEffortItem.setSelected(true);
            else if (Arrays.equals(frame.settingList, FindBugs.MAX_EFFORT))
                frame.maxEffortItem.setSelected(true);
        }

        frame.setSize(800, 600);
        frame.setLocationRelativeTo(null); // center the frame
        frame.setVisible(true);
    }

    private int analysisPriority = Thread.NORM_PRIORITY-1;

    public void setPriority(int priority) {
        this.analysisPriority = priority;
    }

    public static void showCommandLineOptions() {
        System.out.println("GUI options:");
        new SwingCommandLine().printUsage(System.out);
    }

    public static void showSynopsis() {
        System.out.println("Usage: findbugs [general options] [gui options]");
    }

    /* ----------------------------------------------------------------------
     * Instance variables
     * ---------------------------------------------------------------------- */

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JMenuItem aboutItem;
    private javax.swing.JButton addClasspathEntryButton;
    private javax.swing.JButton addJarButton;
    private javax.swing.JButton addSourceDirButton;
    private javax.swing.JTextArea annotationTextArea;
    private javax.swing.JScrollPane annotationTextAreaScrollPane;
    private javax.swing.JButton browseClasspathEntryButton;
    private javax.swing.JButton browseJarButton;
    private javax.swing.JButton browseSrcDirButton;
    private javax.swing.JEditorPane bugDescriptionEditorPane;
    private javax.swing.JScrollPane bugDescriptionScrollPane;
    private javax.swing.JTabbedPane bugDetailsTabbedPane;
    private javax.swing.JEditorPane bugSummaryEditorPane;
    private javax.swing.JSplitPane bugTreeBugDetailsSplitter;
    private javax.swing.JPanel bugTreePanel;
    private javax.swing.JTree byBugCategoryBugTree;
    private javax.swing.JScrollPane byBugCategoryScrollPane;
    private javax.swing.JTree byBugTypeBugTree;
    private javax.swing.JScrollPane byBugTypeScrollPane;
    private javax.swing.JTree byClassBugTree;
    private javax.swing.JScrollPane byClassScrollPane;
    private javax.swing.JTree byPackageBugTree;
    private javax.swing.JScrollPane byPackageScrollPane;
    private javax.swing.JScrollPane bySummary;
    private javax.swing.JButton classpathDownButton;
    private javax.swing.JLabel classpathEntryLabel;
    private javax.swing.JList classpathEntryList;
    private javax.swing.JLabel classpathEntryListLabel;
    private javax.swing.JScrollPane classpathEntryListScrollPane;
    private javax.swing.JTextField classpathEntryTextField;
    private javax.swing.JButton classpathUpButton;
    private javax.swing.JMenuItem closeProjectItem;
    private javax.swing.JMenuItem configureDetectorsItem;
    private javax.swing.JMenuItem copyItem;
    private javax.swing.JMenuItem cutItem;
    private javax.swing.JMenu editMenu;
    private javax.swing.JLabel editProjectLabel;
    private javax.swing.JPanel editProjectPanel;
    private javax.swing.ButtonGroup effortButtonGroup;
    private javax.swing.JMenu effortMenu;
    private javax.swing.JPanel emptyPanel;
    private javax.swing.JMenuItem exitItem;
    private javax.swing.JRadioButtonMenuItem expPriorityButton;
    private javax.swing.JMenu fileMenu;
    private javax.swing.JMenu filterWarningsMenu;
    private javax.swing.JButton findBugsButton;
    private javax.swing.JCheckBoxMenuItem fullDescriptionsItem;
    private javax.swing.JTabbedPane groupByTabbedPane;
    private javax.swing.JLabel growBoxSpacer;
    private javax.swing.JMenu helpMenu;
    private javax.swing.JRadioButtonMenuItem highPriorityButton;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JSeparator jSeparator1;
    private javax.swing.JSeparator jSeparator10;
    private javax.swing.JSeparator jSeparator11;
    private javax.swing.JSeparator jSeparator2;
    private javax.swing.JSeparator jSeparator3;
    private javax.swing.JSeparator jSeparator4;
    private javax.swing.JSeparator jSeparator5;
    private javax.swing.JSeparator jSeparator6;
    private javax.swing.JSeparator jSeparator7;
    private javax.swing.JSeparator jSeparator8;
    private javax.swing.JSeparator jSeparator9;
    private javax.swing.JLabel jarFileLabel;
    private javax.swing.JList jarFileList;
    private javax.swing.JLabel jarFileListLabel;
    private javax.swing.JScrollPane jarFileListScrollPane;
    private javax.swing.JTextField jarNameTextField;
    private javax.swing.JMenuItem loadBugsItem;
    private javax.swing.JLabel logoLabel;
    private javax.swing.JRadioButtonMenuItem lowPriorityButton;
    private javax.swing.JCheckBoxMenuItem maxEffortItem;
    private javax.swing.JRadioButtonMenuItem mediumPriorityButton;
    private javax.swing.JCheckBoxMenuItem minEffortItem;
    private javax.swing.JMenuItem newProjectItem;
    private javax.swing.JCheckBoxMenuItem normalEffortItem;
    private javax.swing.JMenuItem openProjectItem;
    private javax.swing.JMenuItem pasteItem;
    private javax.swing.ButtonGroup priorityButtonGroup;
    private javax.swing.JMenu recentProjectsMenu;
    private javax.swing.JMenuItem reloadProjectItem;
    private javax.swing.JButton removeClasspathEntryButton;
    private javax.swing.JButton removeJarButton;
    private javax.swing.JButton removeSrcDirButton;
    private javax.swing.JPanel reportPanel;
    private javax.swing.JMenuItem saveBugsItem;
    private javax.swing.JMenuItem saveProjectAsItem;
    private javax.swing.JMenuItem saveProjectItem;
    private javax.swing.JMenuItem selectAllItem;
    private javax.swing.JMenu settingsMenu;
    private javax.swing.JLabel sourceDirLabel;
    private javax.swing.JList sourceDirList;
    private javax.swing.JLabel sourceDirListLabel;
    private javax.swing.JScrollPane sourceDirListScrollPane;
    private javax.swing.JButton sourceDownButton;
    private javax.swing.JTextArea sourceTextArea;
    private LineNumberer sourceLineNumberer;
    private javax.swing.JScrollPane sourceTextAreaScrollPane;
    private javax.swing.JButton sourceUpButton;
    private javax.swing.JTextField srcDirTextField;
    private javax.swing.JMenuBar theMenuBar;
    private javax.swing.JLabel urlLabel;
    private javax.swing.JCheckBoxMenuItem viewBugDetailsItem;
    private javax.swing.JRadioButtonMenuItem viewBugsItem;
    private javax.swing.JMenu viewMenu;
    private javax.swing.JPanel viewPanel;
    private javax.swing.JRadioButtonMenuItem viewProjectItem;
    // End of variables declaration//GEN-END:variables

    // My variable declarations
    private Logger logger;
    private CardLayout viewPanelLayout;
    private String currentView;
    private File currentDirectory;
    private Project currentProject;
    private JTree[] bugTreeList;
    private AnalysisRun currentAnalysisRun;
    private BugInstance currentBugInstance; // be lazy in switching bug instance details
    private SourceLineAnnotation currentSourceLineAnnotation; // as above
    private String currentBugDetailsKey;
    private JCheckBoxMenuItem[] bugCategoryCheckBoxList;
    private String[] bugCategoryList;
    private AnalysisFeatureSetting[] settingList = FindBugs.DEFAULT_EFFORT;

    // My constant declarations
    private final static boolean MAC_OS_X = SystemProperties.getProperty("os.name").toLowerCase().startsWith("mac os x");

    private static int getMenuMask() {
        return Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
    }

}
