musite.ui.MusiteResultPanel.java Source code

Java tutorial

Introduction

Here is the source code for musite.ui.MusiteResultPanel.java

Source

/**
 * Musite
 * Copyright (C) 2010 Digital Biology Laboratory, University Of Missouri
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package musite.ui;

import java.awt.Color;
import java.awt.Font;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Hashtable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.JTextPane;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import javax.swing.text.SimpleAttributeSet;

import org.apache.commons.lang.StringUtils;

import musite.MusiteInit;
import musite.Proteins;
import musite.Protein;

import musite.prediction.SpecificityEstimator;
import musite.prediction.PredictionModel;
import musite.prediction.PredictionResult;
import musite.prediction.PredictionResultImpl;

import musite.io.fasta.DefaultProteinsFastaWriter;
import musite.io.fasta.ResidueAnnotator;
import musite.io.xml.PredictionResultXMLWriter;

import musite.ui.task.AbstractTask;
import musite.ui.task.WriteTask;
import musite.ui.task.TaskUtil;

import musite.ui.util.CMap;
import musite.ui.util.CTable;
import musite.ui.util.GradientSliderUI;

import musite.util.FileExtensionsFilter;
import musite.util.FilePathParser;
import musite.util.IOUtil;
import musite.util.StringUtil;

/**
 *
 * @author Jianjiong Gao
 */
public class MusiteResultPanel extends javax.swing.JPanel {

    public MusiteResultPanel(String panelName, PredictionResult result) {
        this(panelName, result, false);
    }

    /** Creates new form MusiteResultPanel */
    public MusiteResultPanel(String panelName, PredictionResult result, boolean saved) {
        this.panelName = panelName;
        this.result = result;
        this.saved = saved;

        initComponents();

        specificity = 0.95;
        setResult();

        runResetProteinList();
    }

    /** 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.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        resDispPanel = new javax.swing.JPanel();
        javax.swing.JPanel modelPanel = new javax.swing.JPanel();
        modelComboBox = new javax.swing.JComboBox();
        javax.swing.JPanel accPanel = new javax.swing.JPanel();
        proteinListComboBox = new javax.swing.JComboBox();
        javax.swing.JPanel optionPanel = new javax.swing.JPanel();
        phosphoCheckBox = new javax.swing.JCheckBox();
        alphabetCheckBox = new javax.swing.JCheckBox();
        displayTabbedPane = new javax.swing.JTabbedPane();
        sequenceScrollPane = new javax.swing.JScrollPane();
        sitesScrollPane = new javax.swing.JScrollPane();
        javax.swing.JPanel adjustPanel = new javax.swing.JPanel();
        javax.swing.JPanel threholdPanel = new javax.swing.JPanel();
        javax.swing.JPanel sliderBasePanel = new javax.swing.JPanel();
        sliderTitleComboBox = new javax.swing.JComboBox();
        javax.swing.JPanel specificityPanel = new javax.swing.JPanel();
        javax.swing.JLabel specificityLabel = new javax.swing.JLabel();
        specificityTextField = new javax.swing.JTextField();
        javax.swing.JLabel percLabel = new javax.swing.JLabel();
        javax.swing.JLabel scoreCutoffLabel = new javax.swing.JLabel();
        scoreCutoffTextField = new javax.swing.JTextField();
        specificitySlider = new javax.swing.JSlider();
        javax.swing.JPanel exportPanel = new javax.swing.JPanel();
        exportComboBox = new javax.swing.JComboBox();
        javax.swing.JPanel closePanel = new javax.swing.JPanel();
        javax.swing.JButton saveButton = new javax.swing.JButton();

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

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

        modelPanel.setVisible(result.getModels().size() > 1);
        modelPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Select model"));
        modelPanel.setLayout(new javax.swing.BoxLayout(modelPanel, javax.swing.BoxLayout.LINE_AXIS));

        PredictionModel[] models = result.getModels().toArray(new PredictionModel[0]);
        java.util.Arrays.sort(models, new java.util.Comparator<PredictionModel>() {
            public int compare(PredictionModel model1, PredictionModel model2) {
                String name1 = model1.getName();
                String name2 = model2.getName();
                if (name1 == null)
                    return name2 == null ? 0 : -1;
                if (name2 == null)
                    return 1;
                return name1.compareTo(name2);
            }
        });
        modelComboBox.setModel(new javax.swing.DefaultComboBoxModel(models));
        modelComboBox.setSelectedIndex(0);
        modelComboBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                modelComboBoxActionPerformed(evt);
            }
        });
        modelPanel.add(modelComboBox);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
        resDispPanel.add(modelPanel, gridBagConstraints);

        accPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Select protein(s)"));
        accPanel.setLayout(new java.awt.GridBagLayout());

        Vector pros = new Vector(result.proteins());
        //pros.add("Display all listed proteins");
        proteinListComboBox.setModel(new javax.swing.DefaultComboBoxModel(pros));
        proteinListComboBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                proteinListComboBoxActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
        accPanel.add(proteinListComboBox, gridBagConstraints);

        optionPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 3, 3));

        phosphoCheckBox.setText("List predicted phosphoproteins only");
        phosphoCheckBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                phosphoCheckBoxActionPerformed(evt);
            }
        });
        optionPanel.add(phosphoCheckBox);

        alphabetCheckBox.setText("List alphabetically (according to accessions)");
        alphabetCheckBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                alphabetCheckBoxActionPerformed(evt);
            }
        });
        optionPanel.add(alphabetCheckBox);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        accPanel.add(optionPanel, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        resDispPanel.add(accPanel, gridBagConstraints);

        displayTextPane = new ViewResultTextPane();
        sequenceScrollPane.setViewportView(displayTextPane);

        displayTabbedPane.addTab("Sequence(s)", sequenceScrollPane);

        displayTable = new ViewResultTable();
        sitesScrollPane.setViewportView(displayTable);

        displayTabbedPane.addTab("Predicted Sites", sitesScrollPane);

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

        adjustPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Stringency setting"));
        adjustPanel.setLayout(new java.awt.GridBagLayout());

        threholdPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 5, 0));

        sliderBasePanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Sliding according to"));
        sliderBasePanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 5, 0));

        sliderTitleComboBox.setMinimumSize(new java.awt.Dimension(150, 18));
        sliderTitleComboBox.setPreferredSize(new java.awt.Dimension(150, 22));
        sliderTitleComboBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                sliderTitleComboBoxActionPerformed(evt);
            }
        });
        sliderBasePanel.add(sliderTitleComboBox);

        threholdPanel.add(sliderBasePanel);

        specificityPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Cutoff"));
        specificityPanel.setLayout(new java.awt.GridBagLayout());

        specificityLabel.setText("Specificity:");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridy = 0;
        gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 0);
        specificityPanel.add(specificityLabel, gridBagConstraints);

        specificityTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
        specificityTextField.setMinimumSize(new java.awt.Dimension(60, 19));
        specificityTextField.setPreferredSize(new java.awt.Dimension(60, 19));
        specificityTextField.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                specificityTextFieldActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridy = 0;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 1);
        specificityPanel.add(specificityTextField, gridBagConstraints);

        percLabel.setText("%");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridy = 0;
        gridBagConstraints.insets = new java.awt.Insets(0, 1, 0, 20);
        specificityPanel.add(percLabel, gridBagConstraints);

        scoreCutoffLabel.setText("Score:");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridy = 0;
        specificityPanel.add(scoreCutoffLabel, gridBagConstraints);

        scoreCutoffTextField.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
        scoreCutoffTextField.setMinimumSize(new java.awt.Dimension(60, 19));
        scoreCutoffTextField.setPreferredSize(new java.awt.Dimension(60, 19));
        scoreCutoffTextField.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                scoreCutoffTextFieldActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridy = 0;
        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 20);
        specificityPanel.add(scoreCutoffTextField, gridBagConstraints);

        threholdPanel.add(specificityPanel);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        adjustPanel.add(threholdPanel, gridBagConstraints);

        specificitySlider.setPaintLabels(true);
        specificitySlider.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                specificitySliderStateChanged(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weightx = 1.0;
        adjustPanel.add(specificitySlider, gridBagConstraints);

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

        exportPanel.setLayout(new javax.swing.BoxLayout(exportPanel, javax.swing.BoxLayout.LINE_AXIS));

        exportComboBox.setModel(new javax.swing.DefaultComboBoxModel(
                new String[] { "Export this result as...", "Tab-delimited text file" }));
        exportComboBox.setMinimumSize(new java.awt.Dimension(200, 18));
        exportComboBox.setPreferredSize(new java.awt.Dimension(200, 22));
        exportComboBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                exportComboBoxActionPerformed(evt);
            }
        });
        exportPanel.add(exportComboBox);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 4;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START;
        gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
        resDispPanel.add(exportPanel, gridBagConstraints);

        closePanel.setLayout(new javax.swing.BoxLayout(closePanel, javax.swing.BoxLayout.LINE_AXIS));

        saveButton.setText("Save As...");
        saveButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                saveButtonActionPerformed(evt);
            }
        });
        closePanel.add(saveButton);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 4;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_END;
        gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
        resDispPanel.add(closePanel, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        add(resDispPanel, gridBagConstraints);
    }// </editor-fold>//GEN-END:initComponents

    private void specificityTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_specificityTextFieldActionPerformed
        double value;
        try {
            value = Double.valueOf(specificityTextField.getText());
        } catch (Exception e) {
            JOptionPane.showMessageDialog(this, "Please specify a number between 0 and 100.");
            return;
        }

        if (value < 0 || value > 100) {
            JOptionPane.showMessageDialog(this, "Please specify a number between 0 and 100.");
            return;
        }

        if (value / 100 == specificity)
            return;

        specificity = value / 100;
        threshold = selectedModel().getSpecEstimator().threshold(specificity);
        scoreCutoffTextField.setText(String.format("%.2f", threshold));

        lockScore = true;
        lockSpec = true;

        specificitySlider.setValue((int) Math.round(maxTick * (threshold - minScore) / (maxScore - minScore)));

        lockScore = false;
        lockSpec = false;
    }//GEN-LAST:event_specificityTextFieldActionPerformed

    private void specificitySliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_specificitySliderStateChanged
        if (ignoreSlideChangeEvent)
            return;

        javax.swing.JSlider source = (javax.swing.JSlider) evt.getSource();

        if (!lockScore) {
            int value = (int) source.getValue();
            threshold = (maxScore - minScore) * value / maxTick + minScore;
            scoreCutoffTextField.setText(String.format("%.2f", threshold));
        }

        if (!lockSpec) {
            if (selectedModel().getSpecEstimator() != null) {
                setSpecificity(threshold);
                specificityTextField.setText(String.format("%.2f", specificity * 100));
            }
        }

        if (!source.getValueIsAdjusting()) { // does not work
            result.setThreshold(selectedModel(), threshold);
            if (phosphoCheckBox.isSelected()) {
                runResetProteinList();
            } else {
                resetDisplay();
            }
        }
    }//GEN-LAST:event_specificitySliderStateChanged

    private void proteinListComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_proteinListComboBoxActionPerformed
        resetDisplay();
    }//GEN-LAST:event_proteinListComboBoxActionPerformed

    private void exportComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportComboBoxActionPerformed
        String name = panelName.replaceAll("[\\\\/:\\*\\?\"<>\\|]+", "-");
        String defaultFile = MusiteInit.defaultPath + File.separator + name;
        if (sliderTitleComboBox.getSelectedItem().equals(SPECIFICITY)) {
            defaultFile += String.format("-Sp_%.2f", specificity);
        } else {
            defaultFile += String.format("-Score_%.3f", threshold);
        }
        defaultFile += ".result";

        switch (exportComboBox.getSelectedIndex()) {
        case 1: // tab-delimited text file
        {
            JFileChooser fc = new JFileChooser(MusiteInit.defaultPath);
            fc.setSelectedFile(new File(defaultFile));

            ArrayList<String> exts = new ArrayList<String>(1);
            String fasta = "txt";
            exts.add(fasta);
            FileExtensionsFilter fastaFilter = new FileExtensionsFilter(exts, "Tab-delimited file (.txt)");
            fc.addChoosableFileFilter(fastaFilter);

            //fc.setAcceptAllFileFilterUsed(true);
            fc.setDialogTitle("Save the the result to...");
            int returnVal = fc.showSaveDialog(this);
            if (returnVal == JFileChooser.APPROVE_OPTION) {
                File file = fc.getSelectedFile();
                MusiteInit.defaultPath = file.getParent();

                String filePath = MusiteInit.defaultPath + File.separator + file.getName();

                String ext = FilePathParser.getExt(filePath);
                if (ext == null || !ext.equalsIgnoreCase("txt")) {
                    filePath += ".txt";
                }

                if (IOUtil.fileExist(filePath)) {
                    int ret = JOptionPane.showConfirmDialog(this, "Are you sure to replace the existing file?",
                            "Relace the existing file?", JOptionPane.YES_NO_OPTION);
                    if (ret == JOptionPane.NO_OPTION)
                        break;
                }

                Vector<Vector> data = formatData(proteinList, false, true);
                int n = data.size();
                ArrayList<String> dataOut = new ArrayList(n + 1);
                dataOut.add(StringUtils.join(header.iterator(), '\t'));

                for (Vector vec : data) {
                    dataOut.add(StringUtils.join(vec.iterator(), '\t'));
                }

                try {
                    IOUtil.writeCollectionAscii(dataOut, filePath);
                } catch (IOException e) {
                    e.printStackTrace();
                    JOptionPane.showMessageDialog(this, "Error: failed to write the file.");
                    break;
                }

                JOptionPane.showMessageDialog(this, "Successfully exported.");
            }

            break;
        }
        //            case 2: // fasta
        //            {
        //                JFileChooser fc = new JFileChooser(MusiteInit.defaultPath);
        //                fc.setSelectedFile(new File(defaultFile));
        //
        //                ArrayList<String> exts = new ArrayList<String>(1);
        //                String fasta = "fasta";
        //                exts.add(fasta);
        //                FileExtensionsFilter fastaFilter = new FileExtensionsFilter(exts,"Fasta file (.fasta)");
        //                fc.addChoosableFileFilter(fastaFilter);
        //
        //                //fc.setAcceptAllFileFilterUsed(true);
        //                fc.setDialogTitle("Save the the result to...");
        //                int returnVal = fc.showSaveDialog(this);
        //                if (returnVal == JFileChooser.APPROVE_OPTION) {
        //                    File file = fc.getSelectedFile();
        //                    MusiteInit.defaultPath = file.getParent();
        //
        //                    String filePath = MusiteInit.defaultPath + File.separator + file.getName();
        //
        //                    String ext = FilePathParser.getExt(filePath);
        //                    if (ext==null||!ext.equalsIgnoreCase("fasta")) {
        //                        filePath += ".fasta";
        //                    }
        //
        //                    if (IOUtil.fileExist(filePath)) {
        //                        int ret = JOptionPane.showConfirmDialog(this, "Are you sure to replace the existing file?", "Relace the existing file?", JOptionPane.YES_NO_OPTION);
        //                        if (ret==JOptionPane.NO_OPTION)
        //                            break;
        //                    }
        //
        //                    ProteinsWriter writer = new ModifiedProteinsFastaWriter();
        //                    WriteTask writeTask = new WriteTask(resultDisplay, writer, filePath);
        //                    TaskUtil.execute(writeTask);
        //                    if (!writeTask.success()) {
        //                        JOptionPane.showMessageDialog(this, "Failed to export.");
        //                        break;
        //                    }
        //
        //                    JOptionPane.showMessageDialog(this, "Successfully exported.");
        //                }
        //
        //                break;
        //            }
        //            case 3: // xml
        //            {
        //                JFileChooser fc = new JFileChooser(MusiteInit.defaultPath);
        //                fc.setSelectedFile(new File(defaultFile));
        //
        //                ArrayList<String> exts = new ArrayList<String>(1);
        //                String xml = "xml";
        //                exts.add(xml);
        //                FileExtensionsFilter xmlFilter = new FileExtensionsFilter(exts,"XML file (.xml)");
        //                fc.addChoosableFileFilter(xmlFilter);
        //
        //                //fc.setAcceptAllFileFilterUsed(true);
        //                fc.setDialogTitle("Save the the result to...");
        //                int returnVal = fc.showSaveDialog(this);
        //                if (returnVal == JFileChooser.APPROVE_OPTION) {
        //                    File file = fc.getSelectedFile();
        //                    MusiteInit.defaultPath = file.getParent();
        //
        //                    String filePath = MusiteInit.defaultPath + File.separator + file.getName();
        //
        //                    String ext = FilePathParser.getExt(filePath);
        //                    if (ext==null||!ext.equalsIgnoreCase("xml")) {
        //                        filePath += ".xml";
        //                    }
        //
        //                    if (IOUtil.fileExist(filePath)) {
        //                        int ret = JOptionPane.showConfirmDialog(this, "Are you sure to replace the existing file?", "Relace the existing file?", JOptionPane.YES_NO_OPTION);
        //                        if (ret==JOptionPane.NO_OPTION)
        //                            break;
        //                    }
        //
        //                    ProteinsXMLWriter writer = ProteinsXMLWriter.createWriter();
        //                    WriteTask xmlWriteTask = new WriteTask(resultDisplay, writer, filePath);
        //                    TaskUtil.execute(xmlWriteTask);
        //                    if (!xmlWriteTask.success()) {
        //                        JOptionPane.showMessageDialog(this, "Failed to export.");
        //                        break;
        //                    }
        //
        //                    JOptionPane.showMessageDialog(this, "Successfully exported.");
        //                }
        //
        //                break;
        //            }
        }
        exportComboBox.setSelectedIndex(0);
    }//GEN-LAST:event_exportComboBoxActionPerformed

    private void saveButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveButtonActionPerformed
        saveResult(true);
    }//GEN-LAST:event_saveButtonActionPerformed

    private void phosphoCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_phosphoCheckBoxActionPerformed
        runResetProteinList();
    }//GEN-LAST:event_phosphoCheckBoxActionPerformed

    private void alphabetCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_alphabetCheckBoxActionPerformed
        runResetProteinList();
    }//GEN-LAST:event_alphabetCheckBoxActionPerformed

    private void sliderTitleComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sliderTitleComboBoxActionPerformed
        setSliderLabels();
    }//GEN-LAST:event_sliderTitleComboBoxActionPerformed

    private void scoreCutoffTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_scoreCutoffTextFieldActionPerformed
        double value;
        try {
            value = Double.valueOf(scoreCutoffTextField.getText());
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "Please input a number.");
            return;
        }

        lockScore = true;

        this.threshold = value;
        specificitySlider.setValue((int) Math.round(maxTick * (threshold - minScore) / (maxScore - minScore)));
        //        if (phosphoCheckBox.isSelected()) {
        //            resetProteinList();
        //        } else {
        //            resetDisplay();
        //        }

        lockScore = false;
    }//GEN-LAST:event_scoreCutoffTextFieldActionPerformed

    private void modelComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_modelComboBoxActionPerformed
        setResult();
        runResetProteinList();
    }//GEN-LAST:event_modelComboBoxActionPerformed

    private PredictionModel selectedModel() {
        return (PredictionModel) modelComboBox.getSelectedItem();
    }

    private void setResult() {
        PredictionModel model = selectedModel();
        maxScore = result.getMaxPredictionScore(model);
        minScore = result.getMinPredictionScore(model);

        SpecificityEstimator est = model.getSpecEstimator();
        if (est != null) {
            double spec1 = est.threshold(1.0);
            double spec0 = est.threshold(0.0);
            if (maxScore < spec1) {
                maxScore = spec1;
            }
            if (minScore > spec0) {
                minScore = spec0;
            }

            threshold = result.getThreshold(model);

            if (Double.isInfinite(threshold)) {
                //specificity = 0.95;
                threshold = est.threshold(0.95);
            } else {
                specificity = est.specificity(threshold);
            }

        } else {
            this.threshold = (maxScore + minScore) / 2;
        }

        result.setThreshold(model, threshold);

        double eps = (maxScore - minScore) / maxTick; // one more tic each side
        maxScore += eps;
        minScore -= eps;

        // set ui
        ignoreSlideChangeEvent = true;

        setSliderTitleModel();

        String textSpec;
        if (est == null) {
            textSpec = "N/A";
            specificityTextField.setEditable(false);
        } else {
            textSpec = String.format("%.2f", specificity * 100);
        }
        specificityTextField.setText(textSpec);

        scoreCutoffTextField.setText(String.format("%.2f", threshold));

        setSliderLabels();
        setMapScoreColor();

        specificitySlider.setValue((int) Math.round(maxTick * (threshold - minScore) / (maxScore - minScore)));

        ignoreSlideChangeEvent = false;
    }

    //    public void setGroundTruth(Proteins groundTruth) {
    //        boolean succ;
    //        try {
    //            succ = ResultUtil.resetResultInterpreter(result, groundTruth);
    //        } catch (Exception e) {
    //            e.printStackTrace();
    //            succ = false;
    //        }
    //        if (!succ) {
    //            JOptionPane.showMessageDialog(this, "Failed to integrate the ground truth data.");
    //            return;
    //        }
    //
    //        specificityTextField.setEditable(true);
    //
    //        //setThreshold(threshold);
    //        setSpecificity(threshold);
    //        specificityTextField.setText(String.format("%.2f", specificity*100));
    //
    //        sliderTitleComboBox.setModel(new javax.swing.DefaultComboBoxModel(
    //                new String[] {SPECIFICITY, SCORE}));
    //
    //        setSliderLabels();
    //        setMapScoreColor();
    //
    //        this.groundTruth = groundTruth;
    //        resetDisplay();
    //
    //        JOptionPane.showMessageDialog(this, "Done.");
    //    }

    private static final int maxTick = 10000;
    private static final String SPECIFICITY = "Specificity";
    private static final String SCORE = "Score";

    private void setSliderTitleModel() {
        PredictionModel model = selectedModel();
        SpecificityEstimator est = model.getSpecEstimator();
        String[] ops;
        if (est == null) {
            ops = new String[] { SCORE };
        } else {
            ops = new String[] { SPECIFICITY, SCORE };
        }
        sliderTitleComboBox.setModel(new javax.swing.DefaultComboBoxModel(ops));
    }

    private void setSliderLabels() {
        specificitySlider.setMaximum(maxTick);
        specificitySlider.setMinorTickSpacing(maxTick / 20);

        Hashtable labelTable = new Hashtable();

        if (sliderTitleComboBox.getSelectedItem().equals(SPECIFICITY)) {
            SpecificityEstimator est = selectedModel().getSpecEstimator();
            double scoreAtSpec0 = est.threshold(0.0);
            double scoreAtSpec50 = est.threshold(0.5);
            double scoreAtSpec80 = est.threshold(0.8);
            double scoreAtSpec90 = est.threshold(0.9);
            double scoreAtSpec95 = est.threshold(0.95);
            double scoreAtSpec100 = est.threshold(1.0);

            //Create the label table
            labelTable.put((int) (maxTick * (scoreAtSpec0 - minScore) / (maxScore - minScore)), new JLabel("0"));
            labelTable.put((int) (maxTick * (scoreAtSpec50 - minScore) / (maxScore - minScore)), new JLabel("50"));
            labelTable.put((int) (maxTick * (scoreAtSpec80 - minScore) / (maxScore - minScore)), new JLabel("80"));
            labelTable.put((int) (maxTick * (scoreAtSpec90 - minScore) / (maxScore - minScore)), new JLabel("90"));
            labelTable.put((int) (maxTick * (scoreAtSpec95 - minScore) / (maxScore - minScore)), new JLabel("95"));
            labelTable.put((int) (maxTick * (scoreAtSpec100 - minScore) / (maxScore - minScore)),
                    new JLabel("100%"));

        } else {
            double step = Math.round(2 * (Math.floor(maxScore) - Math.ceil(minScore)) / 10) / 4;
            if (step == 0)
                step = 0.5;

            double score = Math.ceil(2 * minScore) / 2;
            while (score <= maxScore) {
                labelTable.put((int) (maxTick * (score - minScore) / (maxScore - minScore)),
                        new JLabel(String.format("%.1f", score)));
                score += step;
            }
        }

        specificitySlider.setLabelTable(labelTable);
    }

    private void setMapScoreColor() {
        SpecificityEstimator est = selectedModel().getSpecEstimator();
        mapScoreColor = new TreeMap();
        if (est != null) {
            double scoreAtSpec0 = est.threshold(0.0);
            double scoreAtSpec50 = est.threshold(0.5);
            double scoreAtSpec80 = est.threshold(0.8);
            double scoreAtSpec90 = est.threshold(0.9);
            double scoreAtSpec95 = est.threshold(0.95);
            double scoreAtSpec100 = est.threshold(1.0);
            mapScoreColor.put(scoreAtSpec0, new Color(255, 255, 0));
            mapScoreColor.put(scoreAtSpec50, new Color(0, 255, 0));
            mapScoreColor.put(scoreAtSpec80, new Color(0, 255, 200));
            mapScoreColor.put(scoreAtSpec90, new Color(50, 100, 200));
            mapScoreColor.put(scoreAtSpec95, new Color(150, 50, 200));
            mapScoreColor.put(scoreAtSpec100, new Color(250, 0, 0));
            if (scoreAtSpec0 > minScore) {
                mapScoreColor.put(minScore, new Color(255, 255, 0));
            }
            if (scoreAtSpec100 < maxScore) {
                mapScoreColor.put(maxScore, new Color(250, 0, 0));
            }
        } else {
            mapScoreColor.put(minScore, Color.BLUE);
            mapScoreColor.put(maxScore, Color.RED);
        }

        specificitySlider.setUI(new GradientSliderUI(specificitySlider, mapScoreColor, minScore, maxScore));
    }

    private void runResetProteinList() {
        AbstractTask task = new AbstractTask("Preparing protein list") {
            public void run() {
                try {
                    taskMonitor.setPercentCompleted(-1);
                    taskMonitor.setStatus("Preparing...");

                    resetProteinList();

                    taskMonitor.setPercentCompleted(100);
                    taskMonitor.setStatus("Done.");
                    this.success = true;
                } catch (Exception e) {
                    taskMonitor.setPercentCompleted(100);
                    taskMonitor.setStatus("Failed.\n" + e.getMessage());
                    e.printStackTrace();
                    return;
                }
            }
        };
        TaskUtil.execute(task);
        if (!task.success()) {
            JOptionPane.showMessageDialog(this, "Failed to preparing the data");
            return;
        }
    }

    private void resetProteinList() {
        boolean phospho = phosphoCheckBox.isSelected();
        boolean alphabetical = alphabetCheckBox.isSelected();

        if (!phospho && !alphabetical) {
            proteinList = result.proteins();
        } else {
            PredictionModel model = selectedModel();
            List<Protein> list = new ArrayList(result.proteins());
            if (phospho) {
                double th = result.getThreshold(model);
                int n = list.size();
                for (int i = n - 1; i >= 0; i--) {
                    Protein protein = list.get(i);
                    String acc = protein.getAccession();
                    Map<Integer, Double> preds = result.getPredictedSites(model, acc);
                    if (preds == null) {
                        list.remove(i);
                        continue;
                    }

                    boolean rmv = true;
                    for (double pred : preds.values()) {
                        if (pred >= th) {
                            rmv = false;
                            break;
                        }
                    }

                    if (rmv)
                        list.remove(i);
                }
            }

            if (alphabetical) {
                Collections.sort(list, new java.util.Comparator<Protein>() {
                    public int compare(Protein pro1, Protein pro2) {
                        return pro1.getAccession().compareTo(pro2.getAccession().toString());
                    }
                });
            }

            proteinList = list;
        }

        Vector vec = new Vector(proteinList);
        //vec.add("Display all listed proteins");

        lockDisplay = true;
        Object obj = proteinListComboBox.getSelectedItem();
        proteinListComboBox.setModel(new javax.swing.DefaultComboBoxModel(vec));
        if (obj != null)
            proteinListComboBox.setSelectedItem(obj);
        //        else
        //            proteinListComboBox.setSelectedIndex(0);

        lockDisplay = false;
        resetDisplay(); // this will be called after accComboBox.selectedItem
    }

    public boolean saveResult(boolean showConfirm) {
        JFileChooser fc = new JFileChooser(MusiteInit.defaultPath);

        // TODO: windows reserverd
        String fileName = panelName.replaceAll("[" + StringUtil.toOct("|\\?*<\":>+[]/") + "]+", "_");
        fc.setSelectedFile(new File(MusiteInit.defaultPath + File.separator + fileName + ".pred.xml.gz"));

        ArrayList<String> exts = new ArrayList<String>(2);
        exts.add("xml");
        exts.add("xml.gz");
        fc.addChoosableFileFilter(new FileExtensionsFilter(exts, "Prediction XML file (.xml, .xml.gz)"));

        exts = new ArrayList<String>(2);
        exts.add("pred.xml");
        exts.add("pred.xml.gz");
        fc.addChoosableFileFilter(new FileExtensionsFilter(exts, "Prediction result file (pred.xml, pred.xml.gz)"));

        //fc.setAcceptAllFileFilterUsed(true);
        fc.setDialogTitle("Save the the result to...");
        int returnVal = fc.showSaveDialog(this);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();
            MusiteInit.defaultPath = file.getParent();

            String filePath = MusiteInit.defaultPath + File.separator + file.getName();

            //            if (!filePath.toLowerCase().endsWith(".xml")&&!filePath.toLowerCase().endsWith(".xml.gz")) {
            //                filePath += ".pred.xml.gz";
            //            }

            if (IOUtil.fileExist(filePath)) {
                int ret = JOptionPane.showConfirmDialog(this, "Are you sure to replace the existing file?",
                        "Relace the existing file?", JOptionPane.YES_NO_OPTION);
                if (ret == JOptionPane.NO_OPTION)
                    return false;
            }

            PredictionResultXMLWriter writer = PredictionResultXMLWriter.createWriter();
            WriteTask task = new WriteTask(result, writer, filePath);

            TaskUtil.execute(task);
            if (task.success()) {
                if (showConfirm)
                    JOptionPane.showMessageDialog(this, "Result saved!");
                saved = true;
                return true;
            } else {
                JOptionPane.showMessageDialog(this, "Failed to save the result.");
                return false;
            }
        } else {
            return false;
        }
    }

    public void dispose() {
        // TODO: is this enough for garbage collection
        result = null;
        proteinList = null;
    }

    public String getPanelName() {
        return panelName;
    }

    public void setPanelName(String panelName) {
        this.panelName = panelName;
    }

    //    private void setThreshold(double threshold) {
    //        this.threshold = threshold;
    //        result.setThreshold(threshold);
    //        ResultInterpreter ri = result.getResultInterpreter();
    //        if (ri!=null) {
    //            specificity = 1-ri.getFPR(threshold);
    //        }
    //
    //        resetNumberOfSites();
    //    }

    private void setSpecificity(double threshold) {
        SpecificityEstimator est = selectedModel().getSpecEstimator();
        if (est != null) {
            specificity = est.specificity(threshold);
        }
    }

    private void resetDisplay() {
        if (lockDisplay)
            return;

        final PredictionResult proteins = new PredictionResultImpl();
        ;
        //        int idx = proteinListComboBox.getSelectedIndex();
        //        int n = accComboBox.getItemCount();
        //        if (idx==n-1) {
        //            proteins = resultDisplay;
        //        } else {
        Object obj = proteinListComboBox.getSelectedItem();
        if (obj != null && obj instanceof Protein) {
            Protein protein = (Protein) obj;
            PredictionModel model = selectedModel();
            proteins.addModel(model);
            proteins.addProtein(protein);
            proteins.setThreshold(model, result.getThreshold(model));
        }
        //*
        displayTable.setResult(proteins);
        displayTextPane.setResult(proteins);
        /*/
                AbstractTask task = new AbstractTask("Preparing data for displaying") {
        public void run() {
            try {
                    taskMonitor.setPercentCompleted(-1);
                    taskMonitor.setStatus("Preparing...");
                    displayTable.setResult(proteins);
                    displayTextPane.setResult(proteins);
                    taskMonitor.setPercentCompleted(100);
                    taskMonitor.setStatus("Done.");
                    this.success = true;
            } catch (Exception e) {
                    taskMonitor.setPercentCompleted(100);
                    taskMonitor.setStatus("Failed.\n"+e.getMessage());
                    e.printStackTrace();
                    return;
            }
        }
                };
                TaskUtil.execute(task);
                if (!task.success()) {
        JOptionPane.showMessageDialog(this, "Failed to preparing the data");
        return;
                }
        //*/
    }

    private static Vector header;
    private static final String headerPosition = "Position";
    private static final String headerAA = "Amino Acid";
    private static final String headerSeq = "Surr. Sequence";
    private static final String headerScore = "Score";
    private static final String headerSpecificity = "Specificity";

    static {
        header = new Vector();
        header.add(headerPosition);
        header.add(headerAA);
        header.add(headerSeq);
        header.add(headerScore);
        header.add(headerSpecificity);
    }

    private Vector formatData(Collection<Protein> proteinsList, boolean displayScoreInSequence,
            boolean displayHeader) {
        if (proteinsList == null) {
            return null;
        }

        PredictionModel model = selectedModel();

        Vector data = new Vector();

        for (Protein protein : proteinsList) {
            String acc = protein.getAccession();
            Map<Integer, Double> preds = result.getPredictedSites(model, acc);
            if (preds == null || preds.isEmpty())
                continue;

            if (displayHeader) {
                Vector vec = new Vector(1);
                vec.add('>' + protein.toString());
                data.add(vec);
            }

            String proteinSeq = protein.getSequence();
            int len = proteinSeq.length();

            for (Map.Entry<Integer, Double> entry : preds.entrySet()) {
                int site = entry.getKey();

                char aa = proteinSeq.charAt(site);

                double score = preds.get(site);

                String seq;
                if (site < offset) {
                    seq = StringUtils.repeat("*", offset - site) + proteinSeq.substring(0, site);
                } else {
                    seq = proteinSeq.substring(site - offset, site);
                }

                if (displayScoreInSequence)
                    seq += "[" + score + aa + "]";
                else
                    seq += aa + "#";

                int end = site + offset + 1;
                if (end > len) {
                    seq += proteinSeq.substring(site + 1, len) + StringUtils.repeat("*", end - len);
                } else {
                    seq += proteinSeq.substring(site + 1, end);
                }

                Vector vec = new Vector(header.size());
                vec.add(site + 1);
                vec.add(aa);
                vec.add(seq);

                vec.add(String.format("%.2f", score));

                SpecificityEstimator est = selectedModel().getSpecEstimator();
                String strSpec;
                if (est != null) {
                    double spec = est.specificity(score);
                    strSpec = String.format("%.2f%%", 100 * spec);
                } else {
                    strSpec = "";
                }
                vec.add(strSpec);
                //                vec.add(String.format("%.2f%%", 100*sens));
                data.add(vec);
            }
        }

        return data;
    }

    public boolean isSaved() {
        return saved;
    }

    private String panelName;
    private PredictionResult result;
    private Collection<Protein> proteinList;
    private Proteins groundTruth = null;
    private double specificity;
    private double threshold;
    private double maxScore;
    private double minScore;

    private boolean saved;

    private final int offset = 12;

    private ViewResultTextPane displayTextPane;
    private ViewResultTable displayTable;

    private TreeMap<Double, Color> mapScoreColor;

    private boolean lockSpec = false, lockScore = false, lockDisplay = false, ignoreSlideChangeEvent = false;

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JCheckBox alphabetCheckBox;
    private javax.swing.JTabbedPane displayTabbedPane;
    private javax.swing.JComboBox exportComboBox;
    private javax.swing.JComboBox modelComboBox;
    private javax.swing.JCheckBox phosphoCheckBox;
    private javax.swing.JComboBox proteinListComboBox;
    private javax.swing.JPanel resDispPanel;
    private javax.swing.JTextField scoreCutoffTextField;
    private javax.swing.JScrollPane sequenceScrollPane;
    private javax.swing.JScrollPane sitesScrollPane;
    private javax.swing.JComboBox sliderTitleComboBox;
    private javax.swing.JSlider specificitySlider;
    private javax.swing.JTextField specificityTextField;
    // End of variables declaration//GEN-END:variables

    private Color getColorOfSite(double score) {
        double leftScore = 0;
        double rightScore = 0;

        for (double tick : mapScoreColor.keySet()) {
            if (Math.abs(tick - score) < (maxScore - minScore) / maxTick / 2)
                return mapScoreColor.get(tick);

            if (tick < score) {
                leftScore = tick;
            } else {
                rightScore = tick;
                break;
            }
        }

        Color leftColor = mapScoreColor.get(leftScore);
        Color rightColor = mapScoreColor.get(rightScore);

        float[] leftRGB = leftColor.getRGBColorComponents(null);
        float[] rightRGB = rightColor.getRGBColorComponents(null);

        float perc = (float) ((score - leftScore) / (rightScore - leftScore));

        float red = (rightRGB[0] - leftRGB[0]) * perc + leftRGB[0];
        float green = (rightRGB[1] - leftRGB[1]) * perc + leftRGB[1];
        float blue = (rightRGB[2] - leftRGB[2]) * perc + leftRGB[2];

        return new Color(red, green, blue);
    }

    private Color invertColor(Color color) {
        float[] rgb = color.getRGBColorComponents(null);
        return new Color(1 - rgb[0], 1 - rgb[1], 1 - rgb[2]);
    }

    class ViewResultTable extends CTable {
        public ViewResultTable() {
            setEnabled(false);
            this.setRowHeight(20);
            setHeaderRowMerged();
        }

        private void setHeaderRowMerged() {
            final int columns = header.size();
            CMap map = new CMap() {
                public int span(int row, int column) {
                    if (isHeaderRow(row))
                        return columns;
                    return 1;
                }

                public int visibleCell(int row, int column) {
                    if (isHeaderRow(row))
                        return 0;
                    return column;
                }
            };

            setCMap(map);
        }

        protected void setCellRenderer() {
            final TableCellRenderer headAlignRender = new TableCellRenderer() {
                private DefaultTableCellRenderer cellRenderer = new DefaultTableCellRenderer();

                public java.awt.Component getTableCellRendererComponent(JTable table, Object value,
                        boolean isSelected, boolean hasFocus, int row, int column) {
                    java.awt.Component comp = table.getTableHeader().getDefaultRenderer()
                            .getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
                    if (comp instanceof JLabel) {
                        ((JLabel) comp).setHorizontalAlignment(JLabel.CENTER);
                        return comp;
                    } else {
                        JLabel label = (JLabel) cellRenderer.getTableCellRendererComponent(table, value, isSelected,
                                hasFocus, row, column);
                        label.setHorizontalAlignment(JLabel.CENTER);
                        label.setBackground(comp.getBackground());
                        label.setForeground(comp.getForeground());
                        label.setFont(comp.getFont());
                        return label;
                    }

                }
            };

            final TableCellRenderer midAlignRender = new TableCellRenderer() {
                private DefaultTableCellRenderer cellRenderer = new DefaultTableCellRenderer();

                public java.awt.Component getTableCellRendererComponent(JTable table, Object value,
                        boolean isSelected, boolean hasFocus, int row, int column) {
                    JLabel label;
                    java.awt.Component comp = table.getDefaultRenderer(String.class)
                            .getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
                    if (comp instanceof JLabel) {
                        label = ((JLabel) comp);
                    } else {
                        label = (JLabel) cellRenderer.getTableCellRendererComponent(table, value, isSelected,
                                hasFocus, row, column);
                        label.setHorizontalAlignment(JLabel.CENTER);
                        label.setForeground(comp.getForeground());
                        label.setFont(comp.getFont());
                        //label.setFont(new Font("Serif", table.getFont().getStyle(), 14));
                    }

                    if (isHeaderRow(row)) {
                        label.setHorizontalAlignment(JLabel.LEFT);
                        label.setBackground(Color.LIGHT_GRAY);
                    } else {
                        label.setHorizontalAlignment(JLabel.CENTER);
                        label.setBackground(Color.WHITE);
                    }

                    return label;
                }
            };

            final TableCellRenderer seqAlignRender = new TableCellRenderer() {
                public java.awt.Component getTableCellRendererComponent(JTable table, Object value,
                        boolean isSelected, boolean hasFocus, int row, int column) {
                    if (isHeaderRow(row)) {
                        return midAlignRender.getTableCellRendererComponent(table, value, isSelected, hasFocus, row,
                                column);
                    }

                    JTextPane textPane = new JTextPane();
                    textPane.setForeground(table.getForeground());
                    textPane.setBackground(table.getBackground());
                    textPane.setFont(new Font("Monospaced", table.getFont().getStyle(), 14));

                    if (value == null) {
                        return textPane;
                    }

                    String str = value.toString();
                    Pattern p = Pattern.compile("\\[(.+)(.)\\]");
                    Matcher m = p.matcher(str);
                    if (m.find()) {
                        int site = m.start();
                        double score = Double.parseDouble(m.group(1));

                        textPane.setText(m.replaceFirst("$2"));

                        StyledDocument doc = textPane.getStyledDocument();
                        SimpleAttributeSet attrSet = new SimpleAttributeSet();
                        Color color = getColorOfSite(score);
                        StyleConstants.setBackground(attrSet, color);
                        StyleConstants.setForeground(attrSet, invertColor(color));
                        doc.setCharacterAttributes(site, 1, attrSet, true);

                        attrSet = new SimpleAttributeSet();
                        StyleConstants.setAlignment(attrSet, StyleConstants.ALIGN_CENTER);
                        doc.setParagraphAttributes(0, 0, attrSet, true);
                    }
                    return textPane;
                }
            };

            TableColumnModel colModel = getColumnModel();

            // header column
            int colSeq = colModel.getColumnIndex(headerSeq);
            TableColumn column = colModel.getColumn(colSeq);
            column.setCellRenderer(seqAlignRender);
            //column.setHeaderRenderer(headAlignRender);

            int nCol = colModel.getColumnCount();
            for (int i = 0; i < nCol; i++) {
                if (i != colSeq) {
                    column = colModel.getColumn(i);
                    column.setCellRenderer(midAlignRender);
                    column.setHeaderRenderer(headAlignRender);
                }
            }
        }

        private void setPreferredColumnWidths(double[] percentages) {
            java.awt.Dimension tableDim = this.getPreferredSize();

            double total = 0;
            for (int i = 0; i < getColumnModel().getColumnCount(); i++)
                total += percentages[i];

            for (int i = 0; i < getColumnModel().getColumnCount(); i++) {
                TableColumn column = getColumnModel().getColumn(i);
                column.setPreferredWidth((int) (tableDim.width * (percentages[i] / total)));
            }
        }

        private boolean isHeaderRow(int row) {
            if (row < 0 || row >= getRowCount())
                return false;

            Object value = getModel().getValueAt(row, 0);
            if (value == null)
                return false;

            return value.toString().startsWith(">");
        }

        public void setResult(final PredictionResult result) {
            if (result == null) {
                setModel(new DefaultTableModel());
                return;
            }

            Vector data = formatData(result.proteins(), true,
                    proteinListComboBox.getSelectedIndex() >= proteinListComboBox.getItemCount() - 1);

            setModel(new DefaultTableModel(data, header));
            setCellRenderer();
            setPreferredColumnWidths(new double[] { 1.0, 1.0, 4.0, 1.0, 1.0 });
        }
    }

    class ViewResultTextPane extends JTextPane {
        private final int scorePrecision = 5;
        private boolean suppressRepaint = false;

        public ViewResultTextPane() {
            this.setEditable(false);
            setFont(new Font("Monospaced", getFont().getStyle(), 14));
            //this.setForeground(Color.DARK_GRAY);
        }

        public void repaint(long tm, int x, int y, int width, int height) {
            if (!suppressRepaint)
                super.repaint(tm, x, y, width, height);
        }

        //        public void paint(java.awt.Graphics g) {
        //            if (!suppressRepaint)
        //                super.paint(g);
        //        }

        public void setResult(final PredictionResult result) {
            // formart sequence
            DefaultProteinsFastaWriter proteinsWriter = new DefaultProteinsFastaWriter();
            proteinsWriter.setDisplayNumber(true);
            proteinsWriter.setAAPerWord(10);

            proteinsWriter.setResidueAnnotator(new ResidueAnnotator() {
                public String annotate(Protein protein, int index) {
                    PredictionModel model = selectedModel();
                    String acc = protein.getAccession();
                    Map<Integer, Double> preds = result.getPredictedSites(model, acc);
                    String score = String.format("%." + scorePrecision + "f", preds.get(index));
                    String res = "[" + score + protein.getSequence().charAt(index) + "]";
                    if (groundTruth == null) {
                        return res;
                    }

                    Set<Integer> trueSites = new TreeSet();
                    Protein pro = groundTruth.getProtein(protein.getAccession());
                    if (pro != null) {
                        trueSites = musite.PTMAnnotationUtil.getSites(pro, model.getSupportedPTM(),
                                model.getSupportedAminoAcid());
                    }

                    if (trueSites.isEmpty()) {
                        return res;
                    }

                    if (!preds.containsKey(index)) {
                        return res = "";
                    }

                    if (trueSites.contains(index)) {
                        res += "#";
                    }

                    return res;
                }

                public Set<Integer> indicesOfResidues(Protein protein) {
                    PredictionModel model = selectedModel();
                    Map<Integer, Double> preds = result.getPredictedSites(model, protein.getAccession());
                    if (preds == null)
                        return new HashSet();
                    Set<Integer> sites = preds.keySet();

                    if (groundTruth != null) {
                        Protein pro = groundTruth.getProtein(protein.getAccession());
                        if (pro != null) {
                            sites.addAll(musite.PTMAnnotationUtil.getSites(pro, model.getSupportedPTM(),
                                    model.getSupportedAminoAcid()));
                        }
                    }

                    return sites;
                }
            });

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            try {
                proteinsWriter.write(baos, result);
            } catch (java.io.IOException e) {
                e.printStackTrace();
            }

            String str = baos.toString();
            str = str.replaceAll("\r\n", "\n"); // convert to unix style

            // replace sites and return location
            HashMap<Integer, Double> mapLocScore = new HashMap();
            HashSet<Integer> trueSites = new HashSet();
            int nRemoved = 0;
            Pattern p = Pattern.compile("\\[(-?[0-9]*\\.[0-9]{" + scorePrecision + "})?(p?).\\]");
            Matcher m = p.matcher(str);
            while (m.find()) {
                int start = m.start();
                int site = start - nRemoved;

                String s = m.group(1);
                if (s != null && s.length() > 0) {
                    nRemoved += s.length();//[]

                    Double score = Double.valueOf(s);
                    mapLocScore.put(site, score);
                }

                s = m.group(2);
                if (s != null && s.length() == 1) {
                    nRemoved++; //p
                    trueSites.add(site);
                }

                nRemoved += 2;//[]
            }
            str = str.replaceAll("\\[(-?[0-9]*\\.[0-9]{" + scorePrecision + "})?p?(.)\\]", "$2");

            // header
            List<int[]> headerLocs = new ArrayList();
            p = Pattern.compile(">[^\n]+");
            m = p.matcher(str);
            while (m.find()) {
                int[] locs = new int[2];
                locs[0] = m.start();
                locs[1] = m.end();
                headerLocs.add(locs);
            }

            // line number
            List<int[]> numberLocs = new ArrayList();
            p = Pattern.compile("\n([0-9]+ *)");
            m = p.matcher(str);
            while (m.find()) {
                int[] locs = new int[2];
                locs[0] = m.start(1);
                locs[1] = m.end(1);
                numberLocs.add(locs);
            }

            suppressRepaint = true;

            // set text
            setText(str);

            // set style
            StyledDocument doc = getStyledDocument();

            {
                // render header
                SimpleAttributeSet attrSet = new SimpleAttributeSet();
                StyleConstants.setBackground(attrSet, Color.LIGHT_GRAY);
                for (int[] header : headerLocs) {
                    doc.setCharacterAttributes(header[0], header[1] - header[0], attrSet, false);
                }
            }

            {
                // render number
                SimpleAttributeSet attrSet = new SimpleAttributeSet();
                StyleConstants.setForeground(attrSet, Color.GRAY);
                for (int[] num : numberLocs) {
                    doc.setCharacterAttributes(num[0], num[1] - num[0], attrSet, false);
                }
            }

            {
                // render predictions by highlighting
                for (Map.Entry<Integer, Double> entry : mapLocScore.entrySet()) {
                    int site = entry.getKey();
                    double score = entry.getValue();
                    Color color = getColorOfSite(score);
                    SimpleAttributeSet attrSet = new SimpleAttributeSet();
                    StyleConstants.setBackground(attrSet, color);
                    StyleConstants.setForeground(attrSet, invertColor(color));
                    //StyleConstants.setUnderline(attrSet, true);
                    doc.setCharacterAttributes(site, 1, attrSet, false);
                }

                // render true sites by underlining
                for (int site : trueSites) {
                    SimpleAttributeSet attrSet = new SimpleAttributeSet();
                    StyleConstants.setUnderline(attrSet, true);
                    doc.setCharacterAttributes(site, 1, attrSet, false);
                }
            }

            suppressRepaint = false;
            repaint();
        }
    }
}