<commit_before>/*  Copyright (C) 2003-2015 JabRef contributors.
    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 2 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, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package net.sf.jabref.gui.maintable;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Comparator;
import java.util.List;
import java.util.ArrayList;

import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.TransferHandler;
import javax.swing.plaf.TableUI;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;

import net.sf.jabref.groups.GroupMatcher;
import net.sf.jabref.gui.BasePanel;
import net.sf.jabref.gui.EntryMarker;
import net.sf.jabref.gui.GUIGlobals;
import net.sf.jabref.gui.JabRefFrame;
import net.sf.jabref.gui.renderer.CompleteRenderer;
import net.sf.jabref.gui.renderer.GeneralRenderer;
import net.sf.jabref.gui.renderer.IncompleteRenderer;
import net.sf.jabref.gui.util.FirstColumnComparator;
import net.sf.jabref.gui.util.IconComparator;
import net.sf.jabref.gui.util.IsMarkedComparator;
import net.sf.jabref.gui.util.RankingFieldComparator;
import net.sf.jabref.bibtex.comparator.FieldComparator;
import net.sf.jabref.logic.search.matchers.SearchMatcher;
import net.sf.jabref.model.entry.BibtexEntry;
import net.sf.jabref.model.entry.EntryType;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import net.sf.jabref.*;
import net.sf.jabref.groups.EntryTableTransferHandler;
import net.sf.jabref.logic.search.HitOrMissComparator;
import net.sf.jabref.specialfields.SpecialFieldsUtils;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.SortedList;
import ca.odell.glazedlists.event.ListEventListener;
import ca.odell.glazedlists.matchers.Matcher;
import ca.odell.glazedlists.swing.DefaultEventSelectionModel;
import ca.odell.glazedlists.swing.DefaultEventTableModel;
import ca.odell.glazedlists.swing.GlazedListsSwing;
import ca.odell.glazedlists.swing.TableComparatorChooser;

/**
 * The central table which displays the bibtex entries.
 *
 * User: alver
 * Date: Oct 12, 2005
 * Time: 10:29:39 PM
 *
 */
public class MainTable extends JTable {
    private static final Log LOGGER = LogFactory.getLog(MainTable.class);

    private final MainTableFormat tableFormat;
    private final BasePanel panel;
    private final SortedList<BibtexEntry> sortedForMarking;
    private final SortedList<BibtexEntry> sortedForTable;
    private final SortedList<BibtexEntry> sortedForSearch;
    private final SortedList<BibtexEntry> sortedForGrouping;
    private final boolean tableColorCodes;
    private boolean isFloatSearchActive;
    private boolean isFloatGroupingActive;
    private final DefaultEventSelectionModel<BibtexEntry> localSelectionModel;
    private final TableComparatorChooser<BibtexEntry> comparatorChooser;
    private final JScrollPane pane;
    private Comparator<BibtexEntry> searchComparator;
    private Comparator<BibtexEntry> groupComparator;
    private final Comparator<BibtexEntry> markingComparator = new IsMarkedComparator();
    private Matcher<BibtexEntry> searchMatcher;
    private Matcher<BibtexEntry> groupMatcher;

    // needed to activate/deactivate the listener
    private final PersistenceTableColumnListener tableColumnListener;

    // Constants used to define how a cell should be rendered.
    private static final int REQUIRED = 1;
    private static final int OPTIONAL = 2;
    public static final int REQ_STRING = 1;
    public static final int REQ_NUMBER = 2;
    public static final int OPT_STRING = 3;
    private static final int OTHER = 3;
    private static final int BOOLEAN = 4;
    public static final int ICON_COL = 8; // Constant to indicate that an icon cell renderer should be used.

    static {
        MainTable.updateRenderers();
    }


    public MainTable(MainTableFormat tableFormat, EventList<BibtexEntry> list, JabRefFrame frame,
            BasePanel panel) {
        super();

        addFocusListener(Globals.focusListener);
        setAutoResizeMode(Globals.prefs.getInt(JabRefPreferences.AUTO_RESIZE_MODE));

        this.tableFormat = tableFormat;
        this.panel = panel;
        // This SortedList has a Comparator controlled by the TableComparatorChooser
        // we are going to install, which responds to user sorting selections:
        sortedForTable = new SortedList<>(list, null);
        // This SortedList applies afterwards, and floats marked entries:
        sortedForMarking = new SortedList<>(sortedForTable, null);
        // This SortedList applies afterwards, and can float search hits:
        sortedForSearch = new SortedList<>(sortedForMarking, null);
        // This SortedList applies afterwards, and can float grouping hits:
        sortedForGrouping = new SortedList<>(sortedForSearch, null);

        searchMatcher = null;
        groupMatcher = null;
        searchComparator = null;
        groupComparator = null;

        DefaultEventTableModel<BibtexEntry> tableModel = (DefaultEventTableModel<BibtexEntry>) GlazedListsSwing
                .eventTableModelWithThreadProxyList(sortedForGrouping, tableFormat);
        setModel(tableModel);

        tableColorCodes = Globals.prefs.getBoolean(JabRefPreferences.TABLE_COLOR_CODES_ON);
        localSelectionModel = (DefaultEventSelectionModel<BibtexEntry>) GlazedListsSwing
                .eventSelectionModelWithThreadProxyList(sortedForGrouping);
        setSelectionModel(localSelectionModel);
        pane = new JScrollPane(this);
        pane.setBorder(BorderFactory.createEmptyBorder());
        pane.getViewport().setBackground(Globals.prefs.getColor(JabRefPreferences.TABLE_BACKGROUND));
        setGridColor(Globals.prefs.getColor(JabRefPreferences.GRID_COLOR));
        if (Globals.prefs.getBoolean(JabRefPreferences.TABLE_SHOW_GRID)) {
            setShowGrid(true);
        } else
        {
            setShowGrid(false);
            setIntercellSpacing(new Dimension(0, 0));
        }

        this.setTableHeader(new PreventDraggingJTableHeader(this.getColumnModel()));

        comparatorChooser = this.createTableComparatorChooser(this, sortedForTable,
                TableComparatorChooser.MULTIPLE_COLUMN_KEYBOARD);

        this.tableColumnListener = new PersistenceTableColumnListener(this);

        // TODO: Figure out, whether this call is needed.
        getSelected();

        // enable DnD
        setDragEnabled(true);
        TransferHandler xfer = new EntryTableTransferHandler(this, frame, panel);
        setTransferHandler(xfer);
        pane.setTransferHandler(xfer);

        setupComparatorChooser();
        refreshSorting();
        setWidths();

    }

    public void refreshSorting() {
        sortedForMarking.getReadWriteLock().writeLock().lock();
        try {
            if (Globals.prefs.getBoolean(JabRefPreferences.FLOAT_MARKED_ENTRIES)) {
                sortedForMarking.setComparator(markingComparator);
            } else {
                sortedForMarking.setComparator(null);
            }
        } finally {
            sortedForMarking.getReadWriteLock().writeLock().unlock();
        }

        sortedForSearch.getReadWriteLock().writeLock().lock();
        try {
            sortedForSearch.setComparator(searchComparator);
        } finally {
            sortedForSearch.getReadWriteLock().writeLock().unlock();
        }

        sortedForGrouping.getReadWriteLock().writeLock().lock();
        try {
            sortedForGrouping.setComparator(groupComparator);
        } finally {
            sortedForGrouping.getReadWriteLock().writeLock().unlock();
        }
    }

    /**
     * Adds a sorting rule that floats hits to the top, and causes non-hits to be grayed out:
     */
    public void showFloatSearch() {
        if(!isFloatSearchActive) {
            isFloatSearchActive = true;

            searchMatcher = SearchMatcher.INSTANCE;
            searchComparator = new HitOrMissComparator(searchMatcher);
            refreshSorting();

            scrollTo(0);
        }
    }

    /**
     * Removes sorting by search results, and graying out of non-hits.
     */
    public void stopShowingFloatSearch() {
        if(isFloatSearchActive) {
            isFloatSearchActive = false;

            searchMatcher = null;
            searchComparator = null;
            refreshSorting();
        }
    }

    public boolean isFloatSearchActive() {
        return isFloatSearchActive;
    }

    /**
     * Adds a sorting rule that floats group hits to the top, and causes non-hits to be grayed out:
     */
    public void showFloatGrouping() {
        if(!isFloatGroupingActive) {
            isFloatGroupingActive = true;

            groupMatcher = GroupMatcher.INSTANCE;
            groupComparator = new HitOrMissComparator(groupMatcher);
            refreshSorting();
        }
    }

    /**
     * Removes sorting by group, and graying out of non-hits.
     */
    public void stopShowingFloatGrouping() {
        if(isFloatGroupingActive) {
            isFloatGroupingActive = false;

            groupMatcher = null;
            groupComparator = null;
            refreshSorting();
        }
    }

    public boolean isFloatGroupingActive() {
        return isFloatGroupingActive;
    }


    public EventList<BibtexEntry> getTableRows() {
        return sortedForGrouping;
    }

    public void addSelectionListener(ListEventListener<BibtexEntry> listener) {
        getSelected().addListEventListener(listener);
    }

    public JScrollPane getPane() {
        return pane;
    }

    @Override
    public TableCellRenderer getCellRenderer(int row, int column) {

        int score = -3;
        DefaultTableCellRenderer renderer = MainTable.defRenderer;

        int status = getCellStatus(row, column);

        if (!isFloatSearchActive || matches(row, searchMatcher)) {
            score++;
        }
        if (!isFloatGroupingActive || matches(row, groupMatcher)) {
            score += 2;
        }

        // Now, a grayed out renderer is for entries with -1, and
        // a very grayed out one for entries with -2
        if (score < -1) {
            if (column == 0) {
                MainTable.veryGrayedOutNumberRenderer.setNumber(row);
                renderer = MainTable.veryGrayedOutNumberRenderer;
            } else {
                renderer = MainTable.veryGrayedOutRenderer;
            }
        }
        else if (score == -1) {
            if (column == 0) {
                MainTable.grayedOutNumberRenderer.setNumber(row);
                renderer = MainTable.grayedOutNumberRenderer;
            } else {
                renderer = MainTable.grayedOutRenderer;
            }
        }

        else if (column == 0) {
            // Return a renderer with red background if the entry is incomplete.
            if (!isComplete(row)) {
                MainTable.incRenderer.setNumber(row);
                renderer = MainTable.incRenderer;
            } else {
                MainTable.compRenderer.setNumber(row);
                int marking = isMarked(row);
                if (marking > 0) {
                    marking = Math.min(marking, EntryMarker.MARK_COLOR_LEVELS);
                    renderer = MainTable.markedNumberRenderers[marking - 1];
                    MainTable.markedNumberRenderers[marking - 1].setNumber(row);
                } else {
                    renderer = MainTable.compRenderer;
                }
            }
            renderer.setHorizontalAlignment(JLabel.CENTER);
        }
        else if (tableColorCodes) {
            if (status == MainTable.REQUIRED) {
                renderer = MainTable.reqRenderer;
            } else if (status == MainTable.OPTIONAL) {
                renderer = MainTable.optRenderer;
            } else if (status == MainTable.BOOLEAN) {
                renderer = (DefaultTableCellRenderer) getDefaultRenderer(Boolean.class);
            }
        }

        // For MARKED feature:
        int marking = isMarked(row);
        if ((column != 0) && (marking > 0)) {
            marking = Math.min(marking, EntryMarker.MARK_COLOR_LEVELS);
            renderer = MainTable.markedRenderers[marking - 1];
        }

        return renderer;

    }

    private void setWidths() {
        // Setting column widths:
        int ncWidth = Globals.prefs.getInt(JabRefPreferences.NUMBER_COL_WIDTH);
        String[] widths = Globals.prefs.getStringArray(JabRefPreferences.COLUMN_WIDTHS);
        TableColumnModel cm = getColumnModel();
        cm.getColumn(0).setPreferredWidth(ncWidth);
        // FIXME set correct width for each field
//        for (int i = 1; i < tableFormat.padleft; i++) {
//
//            // Check if the Column is an extended RankingColumn (and not a compact-ranking column)
//            // If this is the case, set a certain Column-width,
//            // because the RankingIconColumn needs some more width
//            if (tableFormat.isRankingColumn(i)) {
//                // Lock the width of ranking icon column.
//                cm.getColumn(i).setPreferredWidth(GUIGlobals.WIDTH_ICON_COL_RANKING);
//                cm.getColumn(i).setMinWidth(GUIGlobals.WIDTH_ICON_COL_RANKING);
//                cm.getColumn(i).setMaxWidth(GUIGlobals.WIDTH_ICON_COL_RANKING);
//            } else {
//                // Lock the width of icon columns.
//                cm.getColumn(i).setPreferredWidth(GUIGlobals.WIDTH_ICON_COL);
//                cm.getColumn(i).setMinWidth(GUIGlobals.WIDTH_ICON_COL);
//                cm.getColumn(i).setMaxWidth(GUIGlobals.WIDTH_ICON_COL);
//            }
//
//        }
//        for (int i = tableFormat.padleft; i < getModel().getColumnCount(); i++) {
//            try {
//                cm.getColumn(i).setPreferredWidth(Integer.parseInt(widths[i - tableFormat.padleft]));
//            } catch (Throwable ex) {
//                LOGGER.info("Exception while setting column widths. Choosing default.", ex);
//                cm.getColumn(i).setPreferredWidth(GUIGlobals.DEFAULT_FIELD_LENGTH);
//            }
//
//        }
    }

    public BibtexEntry getEntryAt(int row) {
        return sortedForGrouping.get(row);
    }

    /**
     * @return the return value is never null
     */
    public BibtexEntry[] getSelectedEntries() {
        final BibtexEntry[] BE_ARRAY = new BibtexEntry[0];
        return getSelected().toArray(BE_ARRAY);
    }

    private List<Boolean> getCurrentSortOrder() {
        List<Boolean> order = new ArrayList<>();
        List<Integer> sortCols = comparatorChooser.getSortingColumns();
        for (Integer i : sortCols) {
            order.add(comparatorChooser.isColumnReverse(i));
        }
        return order;<commit_msg>reenable setting of width<commit_after>/*  Copyright (C) 2003-2015 JabRef contributors.
    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 2 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, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package net.sf.jabref.gui.maintable;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Comparator;
import java.util.List;
import java.util.ArrayList;

import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.TransferHandler;
import javax.swing.plaf.TableUI;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;

import net.sf.jabref.groups.GroupMatcher;
import net.sf.jabref.gui.BasePanel;
import net.sf.jabref.gui.EntryMarker;
import net.sf.jabref.gui.GUIGlobals;
import net.sf.jabref.gui.JabRefFrame;
import net.sf.jabref.gui.renderer.CompleteRenderer;
import net.sf.jabref.gui.renderer.GeneralRenderer;
import net.sf.jabref.gui.renderer.IncompleteRenderer;
import net.sf.jabref.gui.util.FirstColumnComparator;
import net.sf.jabref.gui.util.IconComparator;
import net.sf.jabref.gui.util.IsMarkedComparator;
import net.sf.jabref.gui.util.RankingFieldComparator;
import net.sf.jabref.bibtex.comparator.FieldComparator;
import net.sf.jabref.logic.search.matchers.SearchMatcher;
import net.sf.jabref.model.entry.BibtexEntry;
import net.sf.jabref.model.entry.EntryType;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import net.sf.jabref.*;
import net.sf.jabref.groups.EntryTableTransferHandler;
import net.sf.jabref.logic.search.HitOrMissComparator;
import net.sf.jabref.specialfields.SpecialFieldsUtils;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.SortedList;
import ca.odell.glazedlists.event.ListEventListener;
import ca.odell.glazedlists.matchers.Matcher;
import ca.odell.glazedlists.swing.DefaultEventSelectionModel;
import ca.odell.glazedlists.swing.DefaultEventTableModel;
import ca.odell.glazedlists.swing.GlazedListsSwing;
import ca.odell.glazedlists.swing.TableComparatorChooser;

/**
 * The central table which displays the bibtex entries.
 *
 * User: alver
 * Date: Oct 12, 2005
 * Time: 10:29:39 PM
 *
 */
public class MainTable extends JTable {
    private static final Log LOGGER = LogFactory.getLog(MainTable.class);

    private final MainTableFormat tableFormat;
    private final BasePanel panel;
    private final SortedList<BibtexEntry> sortedForMarking;
    private final SortedList<BibtexEntry> sortedForTable;
    private final SortedList<BibtexEntry> sortedForSearch;
    private final SortedList<BibtexEntry> sortedForGrouping;
    private final boolean tableColorCodes;
    private boolean isFloatSearchActive;
    private boolean isFloatGroupingActive;
    private final DefaultEventSelectionModel<BibtexEntry> localSelectionModel;
    private final TableComparatorChooser<BibtexEntry> comparatorChooser;
    private final JScrollPane pane;
    private Comparator<BibtexEntry> searchComparator;
    private Comparator<BibtexEntry> groupComparator;
    private final Comparator<BibtexEntry> markingComparator = new IsMarkedComparator();
    private Matcher<BibtexEntry> searchMatcher;
    private Matcher<BibtexEntry> groupMatcher;

    // needed to activate/deactivate the listener
    private final PersistenceTableColumnListener tableColumnListener;

    // Constants used to define how a cell should be rendered.
    private static final int REQUIRED = 1;
    private static final int OPTIONAL = 2;
    public static final int REQ_STRING = 1;
    public static final int REQ_NUMBER = 2;
    public static final int OPT_STRING = 3;
    private static final int OTHER = 3;
    private static final int BOOLEAN = 4;
    public static final int ICON_COL = 8; // Constant to indicate that an icon cell renderer should be used.

    static {
        MainTable.updateRenderers();
    }


    public MainTable(MainTableFormat tableFormat, EventList<BibtexEntry> list, JabRefFrame frame,
            BasePanel panel) {
        super();

        addFocusListener(Globals.focusListener);
        setAutoResizeMode(Globals.prefs.getInt(JabRefPreferences.AUTO_RESIZE_MODE));

        this.tableFormat = tableFormat;
        this.panel = panel;
        // This SortedList has a Comparator controlled by the TableComparatorChooser
        // we are going to install, which responds to user sorting selections:
        sortedForTable = new SortedList<>(list, null);
        // This SortedList applies afterwards, and floats marked entries:
        sortedForMarking = new SortedList<>(sortedForTable, null);
        // This SortedList applies afterwards, and can float search hits:
        sortedForSearch = new SortedList<>(sortedForMarking, null);
        // This SortedList applies afterwards, and can float grouping hits:
        sortedForGrouping = new SortedList<>(sortedForSearch, null);

        searchMatcher = null;
        groupMatcher = null;
        searchComparator = null;
        groupComparator = null;

        DefaultEventTableModel<BibtexEntry> tableModel = (DefaultEventTableModel<BibtexEntry>) GlazedListsSwing
                .eventTableModelWithThreadProxyList(sortedForGrouping, tableFormat);
        setModel(tableModel);

        tableColorCodes = Globals.prefs.getBoolean(JabRefPreferences.TABLE_COLOR_CODES_ON);
        localSelectionModel = (DefaultEventSelectionModel<BibtexEntry>) GlazedListsSwing
                .eventSelectionModelWithThreadProxyList(sortedForGrouping);
        setSelectionModel(localSelectionModel);
        pane = new JScrollPane(this);
        pane.setBorder(BorderFactory.createEmptyBorder());
        pane.getViewport().setBackground(Globals.prefs.getColor(JabRefPreferences.TABLE_BACKGROUND));
        setGridColor(Globals.prefs.getColor(JabRefPreferences.GRID_COLOR));
        if (Globals.prefs.getBoolean(JabRefPreferences.TABLE_SHOW_GRID)) {
            setShowGrid(true);
        } else
        {
            setShowGrid(false);
            setIntercellSpacing(new Dimension(0, 0));
        }

        this.setTableHeader(new PreventDraggingJTableHeader(this.getColumnModel()));

        comparatorChooser = this.createTableComparatorChooser(this, sortedForTable,
                TableComparatorChooser.MULTIPLE_COLUMN_KEYBOARD);

        this.tableColumnListener = new PersistenceTableColumnListener(this);

        // TODO: Figure out, whether this call is needed.
        getSelected();

        // enable DnD
        setDragEnabled(true);
        TransferHandler xfer = new EntryTableTransferHandler(this, frame, panel);
        setTransferHandler(xfer);
        pane.setTransferHandler(xfer);

        setupComparatorChooser();
        refreshSorting();
        setWidths();

    }

    public void refreshSorting() {
        sortedForMarking.getReadWriteLock().writeLock().lock();
        try {
            if (Globals.prefs.getBoolean(JabRefPreferences.FLOAT_MARKED_ENTRIES)) {
                sortedForMarking.setComparator(markingComparator);
            } else {
                sortedForMarking.setComparator(null);
            }
        } finally {
            sortedForMarking.getReadWriteLock().writeLock().unlock();
        }

        sortedForSearch.getReadWriteLock().writeLock().lock();
        try {
            sortedForSearch.setComparator(searchComparator);
        } finally {
            sortedForSearch.getReadWriteLock().writeLock().unlock();
        }

        sortedForGrouping.getReadWriteLock().writeLock().lock();
        try {
            sortedForGrouping.setComparator(groupComparator);
        } finally {
            sortedForGrouping.getReadWriteLock().writeLock().unlock();
        }
    }

    /**
     * Adds a sorting rule that floats hits to the top, and causes non-hits to be grayed out:
     */
    public void showFloatSearch() {
        if(!isFloatSearchActive) {
            isFloatSearchActive = true;

            searchMatcher = SearchMatcher.INSTANCE;
            searchComparator = new HitOrMissComparator(searchMatcher);
            refreshSorting();

            scrollTo(0);
        }
    }

    /**
     * Removes sorting by search results, and graying out of non-hits.
     */
    public void stopShowingFloatSearch() {
        if(isFloatSearchActive) {
            isFloatSearchActive = false;

            searchMatcher = null;
            searchComparator = null;
            refreshSorting();
        }
    }

    public boolean isFloatSearchActive() {
        return isFloatSearchActive;
    }

    /**
     * Adds a sorting rule that floats group hits to the top, and causes non-hits to be grayed out:
     */
    public void showFloatGrouping() {
        if(!isFloatGroupingActive) {
            isFloatGroupingActive = true;

            groupMatcher = GroupMatcher.INSTANCE;
            groupComparator = new HitOrMissComparator(groupMatcher);
            refreshSorting();
        }
    }

    /**
     * Removes sorting by group, and graying out of non-hits.
     */
    public void stopShowingFloatGrouping() {
        if(isFloatGroupingActive) {
            isFloatGroupingActive = false;

            groupMatcher = null;
            groupComparator = null;
            refreshSorting();
        }
    }

    public boolean isFloatGroupingActive() {
        return isFloatGroupingActive;
    }


    public EventList<BibtexEntry> getTableRows() {
        return sortedForGrouping;
    }

    public void addSelectionListener(ListEventListener<BibtexEntry> listener) {
        getSelected().addListEventListener(listener);
    }

    public JScrollPane getPane() {
        return pane;
    }

    @Override
    public TableCellRenderer getCellRenderer(int row, int column) {

        int score = -3;
        DefaultTableCellRenderer renderer = MainTable.defRenderer;

        int status = getCellStatus(row, column);

        if (!isFloatSearchActive || matches(row, searchMatcher)) {
            score++;
        }
        if (!isFloatGroupingActive || matches(row, groupMatcher)) {
            score += 2;
        }

        // Now, a grayed out renderer is for entries with -1, and
        // a very grayed out one for entries with -2
        if (score < -1) {
            if (column == 0) {
                MainTable.veryGrayedOutNumberRenderer.setNumber(row);
                renderer = MainTable.veryGrayedOutNumberRenderer;
            } else {
                renderer = MainTable.veryGrayedOutRenderer;
            }
        }
        else if (score == -1) {
            if (column == 0) {
                MainTable.grayedOutNumberRenderer.setNumber(row);
                renderer = MainTable.grayedOutNumberRenderer;
            } else {
                renderer = MainTable.grayedOutRenderer;
            }
        }

        else if (column == 0) {
            // Return a renderer with red background if the entry is incomplete.
            if (!isComplete(row)) {
                MainTable.incRenderer.setNumber(row);
                renderer = MainTable.incRenderer;
            } else {
                MainTable.compRenderer.setNumber(row);
                int marking = isMarked(row);
                if (marking > 0) {
                    marking = Math.min(marking, EntryMarker.MARK_COLOR_LEVELS);
                    renderer = MainTable.markedNumberRenderers[marking - 1];
                    MainTable.markedNumberRenderers[marking - 1].setNumber(row);
                } else {
                    renderer = MainTable.compRenderer;
                }
            }
            renderer.setHorizontalAlignment(JLabel.CENTER);
        }
        else if (tableColorCodes) {
            if (status == MainTable.REQUIRED) {
                renderer = MainTable.reqRenderer;
            } else if (status == MainTable.OPTIONAL) {
                renderer = MainTable.optRenderer;
            } else if (status == MainTable.BOOLEAN) {
                renderer = (DefaultTableCellRenderer) getDefaultRenderer(Boolean.class);
            }
        }

        // For MARKED feature:
        int marking = isMarked(row);
        if ((column != 0) && (marking > 0)) {
            marking = Math.min(marking, EntryMarker.MARK_COLOR_LEVELS);
            renderer = MainTable.markedRenderers[marking - 1];
        }

        return renderer;

    }

    private void setWidths() {
        // Setting column widths:
        int ncWidth = Globals.prefs.getInt(JabRefPreferences.NUMBER_COL_WIDTH);
        String[] widthsFromPreferences = Globals.prefs.getStringArray(JabRefPreferences.COLUMN_WIDTHS);
        TableColumnModel cm = getColumnModel();
        cm.getColumn(0).setPreferredWidth(ncWidth);
        for(int i=1; i<cm.getColumnCount(); i++) {
            MainTableColumn mainTableColumn = tableFormat.getTableColumns().get(cm.getColumn(i).getModelIndex());
            if(SpecialFieldsUtils.FIELDNAME_RANKING.equals(mainTableColumn.getColumnName())) {
                cm.getColumn(i).setPreferredWidth(GUIGlobals.WIDTH_ICON_COL_RANKING);
                cm.getColumn(i).setMinWidth(GUIGlobals.WIDTH_ICON_COL_RANKING);
                cm.getColumn(i).setMaxWidth(GUIGlobals.WIDTH_ICON_COL_RANKING);
            } else if(mainTableColumn.isIconColumn()) {
                cm.getColumn(i).setPreferredWidth(GUIGlobals.WIDTH_ICON_COL);
                cm.getColumn(i).setMinWidth(GUIGlobals.WIDTH_ICON_COL);
                cm.getColumn(i).setMaxWidth(GUIGlobals.WIDTH_ICON_COL);
            } else {
                String[] allColumns = Globals.prefs.getStringArray(JabRefPreferences.COLUMN_NAMES);
                // find index of current mainTableColumn in allColumns
                for(int j=0; j<allColumns.length; j++) {
                    if(allColumns[j].equalsIgnoreCase(mainTableColumn.getDisplayName())) {
                        try {
                            // set preferred width by using found index j in the width array
                            cm.getColumn(i).setPreferredWidth(Integer.parseInt(widthsFromPreferences[j]));
                        } catch (NumberFormatException e) {
                            LOGGER.info("Exception while setting column widths. Choosing default.", e);
                            cm.getColumn(i).setPreferredWidth(GUIGlobals.DEFAULT_FIELD_LENGTH);
                        }
                        break;
                    }
                }
            }
        }
    }

    public BibtexEntry getEntryAt(int row) {
        return sortedForGrouping.get(row);
    }

    /**
     * @return the return value is never null
     */
    public BibtexEntry[] getSelectedEntries() {
        final BibtexEntry[] BE_ARRAY = new BibtexEntry[0];
        return getSelected().toArray(BE_ARRAY);
    }

    private List<Boolean> getCurrentSortOrder() {
        List<Boolean> order = new ArrayList<>();
        List<Integer> sortCols = comparatorChooser.getSortingColumns();
        for (Integer i : sortCols) {
            order.add(comparatorChooser.isColumnReverse(i));
        }
        return order;
{'fraction_non_alphanumeric': 0.08702848957504376, 'fraction_numerical': 0.003915327073054273, 'mean_word_length': 2.6348490107601528, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6717149', 'n_tokens_mistral': 8889, 'n_tokens_neox': 8598, 'n_words': 1936}