ca.phon.app.query.ResultSetEditor.java Source code

Java tutorial

Introduction

Here is the source code for ca.phon.app.query.ResultSetEditor.java

Source

/*
 * Phon - An open source tool for research in phonology.
 * Copyright (C) 2005 - 2015, Gregory Hedlund <ghedlund@mun.ca> and Yvan Rose <yrose@mun.ca>
 * Dept of Linguistics, Memorial University <https://phon.ca>
 * 
 * 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 ca.phon.app.query;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.RowFilter;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.MouseInputAdapter;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

import org.jdesktop.swingx.JXStatusBar;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.decorator.ColorHighlighter;
import org.jdesktop.swingx.decorator.ComponentAdapter;
import org.jdesktop.swingx.decorator.HighlightPredicate;
import org.jdesktop.swingx.decorator.Highlighter;
import org.jdesktop.swingx.decorator.HighlighterFactory;

import ca.phon.app.project.ProjectFrame;
import ca.phon.app.query.report.InventorySectionPanel;
import ca.phon.app.query.report.ResultListingSectionPanel;
import ca.phon.app.session.editor.DelegateEditorAction;
import ca.phon.app.session.editor.EditorAction;
import ca.phon.app.session.editor.EditorEvent;
import ca.phon.app.session.editor.EditorEventType;
import ca.phon.app.session.editor.EditorSelectionModel;
import ca.phon.app.session.editor.SessionEditor;
import ca.phon.app.session.editor.SessionEditorSelection;
import ca.phon.project.Project;
import ca.phon.query.db.Query;
import ca.phon.query.db.QueryManager;
import ca.phon.query.db.Result;
import ca.phon.query.db.ResultSet;
import ca.phon.query.db.ResultSetManager;
import ca.phon.query.db.ResultValue;
import ca.phon.query.report.ResultSetListingManager;
import ca.phon.query.report.csv.CSVTableDataWriter;
import ca.phon.query.report.io.InventorySection;
import ca.phon.query.report.io.ObjectFactory;
import ca.phon.query.report.io.ResultListing;
import ca.phon.query.report.io.ResultListingField;
import ca.phon.query.report.util.ResultListingFieldBuilder;
import ca.phon.session.Session;
import ca.phon.ui.CommonModuleFrame;
import ca.phon.ui.action.PhonUIAction;
import ca.phon.ui.decorations.DialogHeader;
import ca.phon.ui.fonts.FontPreferences;
import ca.phon.ui.layout.ButtonBarBuilder;
import ca.phon.ui.nativedialogs.FileFilter;
import ca.phon.ui.nativedialogs.NativeDialogs;
import ca.phon.ui.nativedialogs.SaveDialogProperties;
import ca.phon.ui.text.TableSearchField;
import ca.phon.ui.toast.Toast;
import ca.phon.ui.toast.ToastFactory;
import ca.phon.util.Range;
import ca.phon.util.icons.IconManager;
import ca.phon.util.icons.IconSize;

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

/**
 * An viewer/editor window for result sets.  For
 * displaying a table of results, use ResultSetTable.
 *
 */
public class ResultSetEditor extends ProjectFrame {

    private static final long serialVersionUID = -4309831950609525140L;

    private final static Logger LOGGER = Logger.getLogger(ResultSetEditor.class.getName());

    /* 
     * Actions buttons
     */
    private Action saveAction;
    private Action saveTableAction;
    private Action inventoryAction;
    private Action editColumnsAction;
    private Action toggleExcludedAction;
    private Action toggleShowExcludedAction;

    private JToolBar toolbar;
    private JButton saveButton;
    private JButton saveTableButton;
    private JButton inventoryButton;
    private JButton editColumnsButton;

    private JXStatusBar statusBar;
    private JLabel statusLabel;

    /*
     * result table
     */
    private JCheckBox showExcludedBox;
    private TableSearchField tableSearchField;
    private JXTable resultTable;

    /*
     * Query info
     */
    private Query query;
    private ResultSet resultSet;
    private Session session;

    /*
     * Temp project.  When set, the results are saved to this
     * project location.
     */
    private Project tempProject;

    private boolean modified = false;

    private EditorAction recordChangedAct;

    /**
     * Constructor
     * @param project
     * @param query
     * @param session
     * @param rs
     */
    public ResultSetEditor(Project project, Query query, ResultSet rs) {
        super();
        super.setProject(project);
        this.query = query;
        this.resultSet = rs;

        try {
            this.session = project.openSession(rs.getCorpus(), rs.getSession());
            putExtension(Session.class, this.session);
        } catch (IOException e) {
            LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
        }

        // setup title
        super.setWindowName(query.getName() + "/" + rs.getSessionPath());

        init();
    }

    /**
     * Try to attach to a parent frame.  Does nothing
     * if already attached to an editor.
     */
    public boolean attachToEditor() {
        if (getParentFrame() == null) {
            // look for parent frames
            for (CommonModuleFrame cmf : CommonModuleFrame.getOpenWindows()) {
                if (cmf instanceof SessionEditor) {
                    final Session transcript = ((SessionEditor) cmf).getSession();
                    if (transcript.getCorpus().equals(resultSet.getCorpus())
                            && transcript.getName().equals(resultSet.getSession())) {
                        super.setParentFrame(cmf);
                        final SessionEditor editor = getEditor();

                        recordChangedAct = new DelegateEditorAction(this, "onRecordChange");
                        editor.getEventManager().registerActionForEvent(EditorEventType.RECORD_CHANGED_EVT,
                                recordChangedAct);

                        break;
                    }
                }
            }
        }
        return getParentFrame() != null;
    }

    /*
     * Editor event for record changes
     */
    public void onRecordChange(EditorEvent ee) {
        resultTable.repaint();
    }

    public SessionEditor getEditor() {
        return (attachToEditor() ? (SessionEditor) getParentFrame() : null);
    }

    private void init() {
        setupToolbar();
        setupResultTable();

        final JPanel resultsPanel = new JPanel();
        resultsPanel.setLayout(new BorderLayout());

        final FormLayout topLayout = new FormLayout("left:pref, right:pref:grow", "pref");
        final CellConstraints cc = new CellConstraints();
        final JPanel topPanel = new JPanel(topLayout);
        topPanel.add(showExcludedBox, cc.xy(1, 1));
        topPanel.add(tableSearchField, cc.xy(2, 1));

        final JScrollPane tblScroller = new JScrollPane(resultTable, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);

        resultsPanel.add(topPanel, BorderLayout.NORTH);
        resultsPanel.add(tblScroller, BorderLayout.CENTER);

        setLayout(new BorderLayout());
        add(toolbar, BorderLayout.NORTH);
        add(resultsPanel, BorderLayout.CENTER);
        add(statusBar, BorderLayout.SOUTH);

        updateStatus();
    }

    public ResultListing getListing() {
        final ResultSetListingManager manager = new ResultSetListingManager();
        ResultListing retVal = null;
        try {
            final ResultListing listing = manager.getResultListing(getProject(), query, resultSet);
            retVal = listing;
        } catch (IOException e) {
            // don't do anything, just create a new
            // listing 
        }
        if (retVal == null) {
            // create a new, default result listing
            final ObjectFactory factory = new ObjectFactory();
            retVal = factory.createResultListing();
            for (ResultListingField field : ResultListingFieldBuilder.getDefaultFields(resultSet)) {
                retVal.getField().add(field);
            }
        }
        return retVal;
    }

    private void setupToolbar() {
        setupButtons();

        toolbar = new JToolBar();
        toolbar.setFloatable(false);

        toolbar.add(saveButton);
        toolbar.add(saveTableButton);
        toolbar.addSeparator();
        //      toolbar.add(inventoryButton);
        toolbar.add(editColumnsButton);
    }

    private void setupButtons() {
        // save button
        saveButton = new JButton(getSaveAction());
        saveButton.setEnabled(false);
        saveTableButton = new JButton(getSaveTableAction());
        //      inventoryButton = new JButton(getInventoryAction());
        editColumnsButton = new JButton(getEditColumnsAction());
    }

    /* Save action */
    private Action getSaveAction() {
        if (this.saveAction == null) {
            final ImageIcon icon = IconManager.getInstance().getIcon("actions/document-save", IconSize.SMALL);

            saveAction = new PhonUIAction(this, "saveData");
            saveAction.putValue(PhonUIAction.NAME, "Save");
            final KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_S,
                    Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
            saveAction.putValue(PhonUIAction.ACCELERATOR_KEY, ks);
            saveAction.putValue(PhonUIAction.SHORT_DESCRIPTION, "Save result set");
            saveAction.putValue(PhonUIAction.SMALL_ICON, icon);
        }
        return this.saveAction;
    }

    /*
     * Save table as csv action
     */
    private Action getSaveTableAction() {
        if (this.saveTableAction == null) {
            final ImageIcon icon = IconManager.getInstance().getIcon("actions/document-save-as", IconSize.SMALL);

            saveTableAction = new PhonUIAction(this, "saveTable");
            saveTableAction.putValue(PhonUIAction.NAME, "Save table as CSV...");
            saveTableAction.putValue(PhonUIAction.SHORT_DESCRIPTION, "Save result table as CSV.");
            final KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_S,
                    KeyEvent.SHIFT_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
            saveTableAction.putValue(PhonUIAction.ACCELERATOR_KEY, ks);
            saveTableAction.putValue(PhonUIAction.SMALL_ICON, icon);
        }
        return saveTableAction;
    }

    public void saveTable() {
        final SaveDialogProperties props = new SaveDialogProperties();
        props.setParentWindow(this);
        props.setRunAsync(false);
        props.setTitle("Save as CSV");
        props.setCanCreateDirectories(true);
        props.setFileFilter(FileFilter.csvFilter);
        final String saveAs = NativeDialogs.showSaveDialog(props);
        if (saveAs != null) {
            // create a new CSVTableWriter
            final CSVTableDataWriter writer = new CSVTableDataWriter();
            try {
                writer.writeTableToFile(resultTable, new File(saveAs));
            } catch (IOException e) {
                LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);

                final Toast toast = ToastFactory.makeToast("Unable to save table: " + e.getLocalizedMessage());
                toast.start(saveTableButton);
            }
        }
    }

    public JTable getTable() {
        return this.resultTable;
    }

    /*
     * Inventory action
     */
    private Action getInventoryAction() {
        if (this.inventoryAction == null) {
            final ImageIcon icon = IconManager.getInstance().getIcon("actions/inventory", IconSize.SMALL);

            inventoryAction = new PhonUIAction(this, "generateInventory");
            inventoryAction.putValue(PhonUIAction.SHORT_DESCRIPTION, "Generate inventory...");
            inventoryAction.putValue(PhonUIAction.SMALL_ICON, icon);
        }
        return inventoryAction;
    }

    public void generateInventory() {
        // setup the inventory dialog
        final ObjectFactory factory = new ObjectFactory();
        final InventorySection invSection = factory.createInventorySection();
        final InventorySectionPanel panel = new InventorySectionPanel(invSection);
        final JPanel invPanel = panel.getOptionsPanel();

        final JDialog inventoryDialog = new JDialog(this, true);

    }

    /*
     * Edit columns
     */
    private Action getEditColumnsAction() {
        if (this.editColumnsAction == null) {
            final ImageIcon icon = IconManager.getInstance().getIcon("actions/edit", IconSize.SMALL);

            editColumnsAction = new PhonUIAction(this, "editColumns");
            editColumnsAction.putValue(PhonUIAction.NAME, "Edit table columns...");
            editColumnsAction.putValue(PhonUIAction.SHORT_DESCRIPTION, "Set up result table columns.");
            editColumnsAction.putValue(PhonUIAction.SMALL_ICON, icon);
        }
        return this.editColumnsAction;
    }

    public void editColumns() {
        final DialogHeader header = new DialogHeader("Edit Columns", "Set up result table columns.");
        final JPanel panel = new JPanel(new BorderLayout());

        final ResultListing listing = getListing();
        final ResultListingSectionPanel p = new ResultListingSectionPanel(listing);
        final JPanel resultListingEditor = p.getFieldPanel();

        panel.add(header, BorderLayout.NORTH);
        panel.add(resultListingEditor, BorderLayout.CENTER);

        final JDialog editColumnsDialog = new JDialog(this, true);
        editColumnsDialog.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        final JButton okButton = new JButton("Ok");
        okButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent arg0) {
                editColumnsDialog.dispose();

                final ResultListingTableModel tblModel = (ResultListingTableModel) resultTable.getModel();
                tblModel.setListing(listing);

                // save listing
                final ResultSetListingManager manager = new ResultSetListingManager();
                try {
                    manager.saveResultListing(getProject(), query, resultSet, listing);
                } catch (IOException e) {
                    // not critical, but should report the 'why'
                    LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
                }
            }
        });

        final JButton cancelButton = new JButton("Cancel");
        cancelButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                editColumnsDialog.dispose();
            }
        });

        final JComponent buttonBar = ButtonBarBuilder.buildOkCancelBar(okButton, cancelButton);

        panel.add(buttonBar, BorderLayout.SOUTH);

        editColumnsDialog.add(panel);
        editColumnsDialog.pack();
        editColumnsDialog.setSize(new Dimension(500, 400));
        editColumnsDialog.setLocationRelativeTo(this);
        editColumnsDialog.setVisible(true);
    }

    /*
     * toggle current result exclusion
     */
    private Action getToggleExcludedAction() {
        if (this.toggleExcludedAction == null) {
            toggleExcludedAction = new PhonUIAction(this, "toggleExcluded");
            toggleExcludedAction.putValue(PhonUIAction.NAME, "Toggle result excluded");
            toggleExcludedAction.putValue(PhonUIAction.SHORT_DESCRIPTION, "Toggle current result exclusion");
            final KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0);
            toggleExcludedAction.putValue(PhonUIAction.ACCELERATOR_KEY, ks);
        }
        return toggleExcludedAction;
    }

    public void toggleExcluded() {
        final int selectedRow = resultTable.getSelectedRow();
        final ResultListingTableModel tblModel = (ResultListingTableModel) resultTable.getModel();
        for (int rowIdx : resultTable.getSelectedRows()) {
            final int resultIdx = resultTable.convertRowIndexToModel(rowIdx);
            if (resultIdx >= 0) {
                final Result r = resultSet.getResult(resultIdx);
                r.setExcluded(!r.isExcluded());

                setModified(true);
                tblModel.fireTableRowsUpdated(resultIdx, resultIdx);
            }
        }

        if (selectedRow >= 0 && selectedRow < resultTable.getRowCount()) {
            resultTable.getSelectionModel().setSelectionInterval(selectedRow, selectedRow);
        }
    }

    /*
     * Toggle show excluded results
     */
    private Action getToggleShowExcludedAction() {
        if (this.toggleShowExcludedAction == null) {
            toggleShowExcludedAction = new PhonUIAction(this, "toggleShowExcluded");
            toggleShowExcludedAction.putValue(PhonUIAction.NAME, "Show excluded results");
        }
        return this.toggleShowExcludedAction;
    }

    public void toggleShowExcluded() {
        updateRowFilter();
    }

    @Override
    public void setJMenuBar(JMenuBar menuBar) {
        super.setJMenuBar(menuBar);

        // add a 'Results' menu
        final JMenu resultsMenu = new JMenu("Results");

        resultsMenu.add(getToggleExcludedAction());
        resultsMenu.addSeparator();
        //      resultsMenu.add(getInventoryAction());
        resultsMenu.add(getEditColumnsAction());

        menuBar.add(resultsMenu, 2);

        // get the file menu
        JMenu fileMenu = null;
        for (int i = 0; i < menuBar.getMenuCount(); i++) {
            if (menuBar.getMenu(i).getText().equals("File")) {
                fileMenu = menuBar.getMenu(i);
                break;
            }
        }

        if (fileMenu != null) {
            fileMenu.add(new JMenuItem(getSaveTableAction()), 0);
            fileMenu.add(new JMenuItem(getSaveAction()), 0);
        }
    }

    private void updateStatus() {
        final int row = resultTable.getSelectedRow();
        final String rowText = (row < 0 ? "?" : (row + 1) + "");
        final String numRows = resultTable.getRowCount() + "";

        String retVal = rowText + "/" + numRows;

        final int numResults = resultSet.numberOfResults(true);
        if (numResults != resultTable.getRowCount()) {
            final String numFiltered = (numResults - resultTable.getRowCount()) + "";
            retVal += " (" + numFiltered + " hidden)";
        }

        statusLabel.setText(retVal);
    }

    private void setupResultTable() {
        final ResultListingTableModel model = new ResultListingTableModel(session, resultSet, getListing());
        resultTable = new JXTable(model);
        resultTable.setColumnControlVisible(true);
        resultTable.setFont(FontPreferences.getUIIpaFont());
        resultTable.addMouseListener(new MouseInputAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                resultTable.requestFocusInWindow();
                resultTable.removeMouseListener(this);
            }
        });

        final Highlighter stripeHighlighter = HighlighterFactory.createSimpleStriping();
        resultTable.addHighlighter(stripeHighlighter);
        resultTable.addHighlighter(currentRecordHighlighter);
        resultTable.addHighlighter(excludedHighlighter);

        final String toggleId = "_toggle_result_excluded_";
        final Action toggleAct = getToggleExcludedAction();
        final InputMap inputMap = resultTable.getInputMap(JComponent.WHEN_FOCUSED);
        final ActionMap actionMap = resultTable.getActionMap();
        actionMap.put(toggleId, toggleAct);
        final KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0);
        inputMap.put(ks, toggleId);

        resultTable.setActionMap(actionMap);
        resultTable.setInputMap(JComponent.WHEN_FOCUSED, inputMap);
        resultTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        resultTable.getSelectionModel().addListSelectionListener(tableSelectionListener);

        // search field
        tableSearchField = new TableSearchField(resultTable, false) {

            @Override
            public void setTableFilter(String expr) {
                updateRowFilter();
            }

        };
        tableSearchField.setColumns(20);
        tableSearchField.setPrompt("Filter results");
        tableSearchField.getTextField().setFont(FontPreferences.getUIIpaFont());
        tableSearchField.getTextField().addKeyListener(new KeyListener() {

            @Override
            public void keyTyped(KeyEvent e) {
            }

            @Override
            public void keyReleased(KeyEvent e) {
                updateRowFilter();
            }

            @Override
            public void keyPressed(KeyEvent e) {
            }
        });

        // toggle excluded results
        showExcludedBox = new JCheckBox(getToggleShowExcludedAction());

        statusLabel = new JLabel();
        statusBar = new JXStatusBar();
        statusBar.add(statusLabel);

        updateRowFilter();
    }

    @Override
    public boolean hasUnsavedChanges() {
        return this.modified;
    }

    private void setModified(boolean modified) {
        this.modified = modified;
        saveButton.setEnabled(modified);
        super.getRootPane().putClientProperty("Window.documentModified", modified);
    }

    public Project getTempProject() {
        return this.tempProject;
    }

    public void setTempProject(Project project) {
        this.tempProject = project;
    }

    public Session getSession() {
        return this.session;
    }

    public Query getQuery() {
        return this.query;
    }

    public ResultSet getResultSet() {
        return this.resultSet;
    }

    @Override
    public boolean saveData() throws IOException {
        final QueryManager qm = QueryManager.getSharedInstance();
        final ResultSetManager rsManager = qm.createResultSetManager();
        rsManager.saveResultSet((getTempProject() != null ? getTempProject() : getProject()), query, resultSet);
        setModified(false);

        return true;
    }

    @Override
    public void close() {
        if (getEditor() != null) {
            if (getEditor().getSelectionModel() != null)
                getEditor().getSelectionModel().clear();
            if (getEditor().getEventManager() != null)
                getEditor().getEventManager().removeActionForEvent(EditorEventType.RECORD_CHANGED_EVT,
                        recordChangedAct);
        }
        super.close();
    }

    private void updateRowFilter() {
        // get the row filter (if any) from the search field
        final RowFilter<TableModel, Integer> searchFieldFilter = tableSearchField
                .getRowFilter(tableSearchField.getText());
        final List<RowFilter<TableModel, Integer>> filters = new ArrayList<RowFilter<TableModel, Integer>>();
        filters.add(excludedFilter);
        if (searchFieldFilter != null) {
            filters.add(searchFieldFilter);
        }
        final TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(resultTable.getModel());
        sorter.setSortsOnUpdates(true);
        final RowFilter<TableModel, Integer> filter = RowFilter.andFilter(filters);
        sorter.setRowFilter(filter);
        resultTable.setRowSorter(sorter);

        updateStatus();
    }

    /*
     * Table highlighter for excluded results (when shown) 
     */
    private final HighlightPredicate excludedPredicate = new HighlightPredicate() {

        @Override
        public boolean isHighlighted(Component arg0, ComponentAdapter arg1) {
            if (showExcludedBox.isSelected()) {
                final int rowIdx = arg1.row;
                final int resultIdx = resultTable.convertRowIndexToModel(rowIdx);
                final Result result = resultSet.getResult(resultIdx);
                return result.isExcluded();
            } else {
                return false;
            }
        }

    };

    private final Highlighter excludedHighlighter = new ColorHighlighter(excludedPredicate, new Color(0, 0, 0, 0),
            Color.gray) {

        @Override
        protected void applyForeground(Component renderer, ComponentAdapter adapter) {
            super.applyForeground(renderer, adapter);

            if (renderer instanceof JLabel) {
                JLabel lbl = (JLabel) renderer;
                lbl.setFont(lbl.getFont().deriveFont(Font.ITALIC));
            }
        }

        @Override
        public void applyBackground(Component renderer, ComponentAdapter adapter) {
        }

    };

    /*
     * Row highlighter for current record results
     *  
     */
    private final HighlightPredicate currentRecordPredicate = new HighlightPredicate() {

        @Override
        public boolean isHighlighted(Component arg0, ComponentAdapter arg1) {
            boolean retVal = false;

            // TODO 
            //         if(getEditor() != null) {
            //            final int rowIdx = arg1.row;
            //            final int resultIdx = resultTable.convertRowIndexToModel(rowIdx);
            //            final Result r = resultSet.getResult(resultIdx);
            //            
            //            final int currentRecord = getEditor().getModel().getCurrentIndex();
            //            retVal = (currentRecord == r.getRecordIndex());
            //         }

            return retVal;
        }

    };

    private final Highlighter currentRecordHighlighter = new ColorHighlighter(currentRecordPredicate, Color.yellow,
            Color.black);

    /*
     * Table selection listener
     */
    private final ListSelectionListener tableSelectionListener = new ListSelectionListener() {

        @Override
        public void valueChanged(ListSelectionEvent e) {
            if (!e.getValueIsAdjusting() && getEditor() != null) {
                final int rowIdx = resultTable.getSelectedRow();
                if (rowIdx < 0)
                    return;

                final int resultIdx = resultTable.convertRowIndexToModel(rowIdx);
                if (resultIdx >= 0) {
                    final Result r = resultSet.getResult(resultIdx);
                    final EditorSelectionModel selectionModel = getEditor().getSelectionModel();
                    selectionModel.clear();
                    for (ResultValue rv : r) {
                        final Range range = new Range(rv.getRange().getFirst(), rv.getRange().getLast(), false);
                        final SessionEditorSelection selection = new SessionEditorSelection(r.getRecordIndex(),
                                rv.getTierName(), rv.getGroupIndex(), range);
                        selectionModel.addSelection(selection);
                    }
                    if (getEditor().getCurrentRecordIndex() != r.getRecordIndex())
                        getEditor().setCurrentRecordIndex(r.getRecordIndex());
                } else {
                    getEditor().getSelectionModel().clear();
                }

                // update status label
                updateStatus();
            }
        }
    };

    /**
     * Excluded table filter
     */
    private final RowFilter<TableModel, Integer> excludedFilter = new RowFilter<TableModel, Integer>() {

        @Override
        public boolean include(javax.swing.RowFilter.Entry<? extends TableModel, ? extends Integer> entry) {
            final int resultIdx = entry.getIdentifier();
            final Result result = resultSet.getResult(resultIdx);

            if (showExcludedBox.isSelected() && result.isExcluded())
                return true;
            else {
                return !result.isExcluded();
            }
        }

    };

}